I've found cases where `MediaType::toString` generates erroneous results.

One case is when the `MediaType` instance has a parameter of an empty value. This can happen when calling `withParameter("foo", "")`, or parsing `text/plain; foo=""`. When the parameters are later joined, the value is erroneously matched as a token instead of being quoted (as tokens cannot be empty). This will lead the following to throw `IllegalArgumentException`: 
```java
MediaType type1 = MediaType.parse("text/plain; foo=\"\"");
MediaType type2 = MediaType.parse(type1.toString()); // Trying to parse 'text/plain; foo=' will fail
```
 I think [this line](https://github.com/google/guava/blob/5a8f19bd3556012ed9e65cd4268a85ddde95733f/guava/src/com/google/common/net/MediaType.java#L1091) should first check if the string is empty before matching against `TOKEN_MATCHER`, or maybe just return the literal `"\"\""` on that case to avoid the `StringBuilder` allocation.

Another case is when parameters are set explicitly using `withParameter[s](...)`, where parameter value[s] contain non-ASCII characters. For example this will also throw `IllegalArgumentException`:
```java
MediaType type1 = MediaType.create("text", "plain").withParameter("let_me_in!", "\"جوافة\"");
MediaType type2 = MediaType.parse(type1.toString()); // The tokenizer will fail to consume QUOTED_TEXT_MATCHER
```
I don't know whether this is the intended behaviour or not but `MediaType::toString` can be used in HTTP header values which shouldn't contain non-ASCII octets. IMHO `normalizeParameterValue(...)` should validate the value against the `ascii()` matcher.
if (!parameters.isEmpty()) {
      builder.append("; ");
      Multimap<String, String> quotedParameters =
          Multimaps.transformValues(
              parameters,
              new Function<String, String>() {
                @Override
                public String apply(String value) {
                  return (TOKEN_MATCHER.matchesAllOf(value) && !value.isEmpty())
                      ? value
                      : escapeAndQuote(value);
                }
              });
      PARAMETER_JOINER.appendTo(builder, quotedParameters.entries());
    }
    return builder.toString();
  }
  private static String escapeAndQuote(String value) {
    StringBuilder escaped = new StringBuilder(value.length() + 16).append('"');
    for (int i = 0; i < value.length(); i++) {
      char ch = value.charAt(i);
      if (ch == '\r' || ch == '\\' || ch == '"') {
        escaped.append('\\');
      }
      escaped.append(ch);
    }
    return escaped.append('"').toString();
  }
}