Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(28)

Unified Diff: chrome/android/java/src/org/chromium/chrome/browser/physicalweb/PwsClientImpl.java

Issue 1989963003: Set User-Agent and Accept-Language request headers for PWS requests (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: rebase Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/android/java/src/org/chromium/chrome/browser/physicalweb/PwsClientImpl.java
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/PwsClientImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/PwsClientImpl.java
index 20f0a611a287ed7e51c1a3c3d7a6c8507f0b204c..51a76f543bc3e808aa13d5031c501fdb2f6c2618 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/PwsClientImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/PwsClientImpl.java
@@ -4,12 +4,16 @@
package org.chromium.chrome.browser.physicalweb;
+import android.content.Context;
import android.graphics.Bitmap;
import android.os.AsyncTask;
+import android.os.Build;
import org.chromium.base.Log;
import org.chromium.base.ThreadUtils;
+import org.chromium.base.VisibleForTesting;
import org.chromium.chrome.GoogleAPIKeys;
+import org.chromium.chrome.R;
import org.chromium.chrome.browser.ChromeVersionInfo;
import org.chromium.chrome.browser.physicalweb.PwsClient.FetchIconCallback;
import org.chromium.chrome.browser.physicalweb.PwsClient.ResolveScanCallback;
@@ -21,6 +25,8 @@ import org.json.JSONObject;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Formatter;
+import java.util.Locale;
/**
* This class sends requests to the Physical Web Service.
@@ -30,6 +36,30 @@ class PwsClientImpl implements PwsClient {
private static final String ENDPOINT_URL =
"https://physicalweb.googleapis.com/v1alpha1/urls:resolve";
+ // Format strings for creating the User-Agent string. It should somewhat resemble the Chrome for
+ // Android User-Agent but doesn't need to match perfectly as this value will only be seen by the
+ // Physical Web metadata service and favicon fetcher.
+ // The WebKit version is not accessible from here so it is reported as 0.0.
+ private static final String USER_AGENT_FORMAT =
+ "Mozilla/5.0 (%s) AppleWebKit/0.0 (KHTML, like Gecko) %s Safari/0.0";
+ private static final String OS_INFO_FORMAT = "Linux; Android %s; %s Build/%s";
+ private static final String PRODUCT_FORMAT = "Chrome/%s Mobile";
+
+ // HTTP request header strings, lazily initialized.
+ private static String sUserAgent;
+ private static String sAcceptLanguage;
+
+ // Cached locale string. When the default locale changes, recreate the Accept-Language header.
+ private static String sDefaultLocale;
+
+ // The context must be valid for as long as this client is in use, since it is used to recreate
+ // the Accept-Language header when the locale changes.
+ private final Context mContext;
+
+ public PwsClientImpl(Context context) {
+ mContext = context;
+ }
+
private String getApiKey() {
if (ChromeVersionInfo.isStableBuild()) {
return GoogleAPIKeys.GOOGLE_API_KEY;
@@ -117,7 +147,8 @@ class PwsClientImpl implements PwsClient {
try {
JSONObject payload = createResolveScanPayload(broadcastUrls);
String url = ENDPOINT_URL + "?key=" + getApiKey();
- request = new JsonObjectHttpRequest(url, payload, requestCallback);
+ request = new JsonObjectHttpRequest(url, getUserAgent(), getAcceptLanguage(), payload,
+ requestCallback);
} catch (MalformedURLException e) {
Log.e(TAG, "Error creating PWS HTTP request", e);
return;
@@ -159,7 +190,8 @@ class PwsClientImpl implements PwsClient {
// Create the request.
BitmapHttpRequest request = null;
try {
- request = new BitmapHttpRequest(iconUrl, requestCallback);
+ request = new BitmapHttpRequest(iconUrl, getUserAgent(), getAcceptLanguage(),
+ requestCallback);
} catch (MalformedURLException e) {
Log.e(TAG, "Error creating icon request", e);
return;
@@ -167,4 +199,135 @@ class PwsClientImpl implements PwsClient {
// The callback will be called on the main thread.
AsyncTask.THREAD_POOL_EXECUTOR.execute(request);
}
+
+ /**
+ * Recreate the Chrome for Android User-Agent string as closely as possible without calling any
+ * native code.
+ * @return A User-Agent string
+ */
+ @VisibleForTesting
+ String getUserAgent() {
+ if (sUserAgent == null) {
+ // Build the OS info string.
+ // eg: Linux; Android 5.1.1; Nexus 4 Build/LMY48T
+ String osInfo = String.format(OS_INFO_FORMAT, Build.VERSION.RELEASE, Build.MODEL,
+ Build.ID);
+
+ // Build the product string.
+ // eg: Chrome/50.0.2661.89 Mobile
+ String product = String.format(PRODUCT_FORMAT, ChromeVersionInfo.getProductVersion());
+
+ // Build the User-Agent string.
+ // eg: Mozilla/5.0 (Linux; Android 5.1.1; Nexus 4 Build/LMY48T) AppleWebKit/0.0 (KHTML,
+ // like Gecko) Chrome/50.0.2661.89 Mobile Safari/0.0
+ sUserAgent = String.format(USER_AGENT_FORMAT, osInfo, product);
+ }
+ return sUserAgent;
+ }
+
+ /**
+ * Construct the Accept-Language string based on the current locale.
+ * @return An Accept-Language string.
+ */
+ @VisibleForTesting
+ String getAcceptLanguage() {
+ String defaultLocale = Locale.getDefault().toString();
+ if (sDefaultLocale == null || !sDefaultLocale.equals(defaultLocale)) {
+ String acceptLanguages = mContext.getResources().getString(R.string.accept_languages);
+ acceptLanguages = prependToAcceptLanguagesIfNecessary(defaultLocale, acceptLanguages);
+ sAcceptLanguage = generateAcceptLanguageHeader(acceptLanguages);
+ sDefaultLocale = defaultLocale;
+ }
+ return sAcceptLanguage;
+ }
+
+ /**
+ * Handle the special cases in converting a language code/region code pair into an ISO-639-1
+ * language tag.
+ * @param language The 2-character language code
+ * @param region The 2-character country code
+ * @return A language tag.
+ */
+ @VisibleForTesting
+ static String makeLanguageTag(String language, String region) {
+ // Java mostly follows ISO-639-1 and ICU, except for the following three.
+ // See documentation on java.util.Locale constructor for more.
+ String isoLanguage;
+ if ("iw".equals(language)) {
+ isoLanguage = "he";
+ } else if ("ji".equals(language)) {
+ isoLanguage = "yi";
+ } else if ("in".equals(language)) {
+ isoLanguage = "id";
+ } else {
+ isoLanguage = language;
+ }
+
+ return isoLanguage + "-" + region;
+ }
+
+ /**
+ * Get the language code for the default locale and prepend it to the Accept-Language string if
+ * it isn't already present. The logic should match PrependToAcceptLanguagesIfNecessary in
+ * chrome/browser/android/preferences/pref_service_bridge.cc
+ * @param locale A string representing the default locale.
+ * @param acceptLanguages The default language list for the language of the user's locale.
+ * @return An updated language list.
+ */
+ @VisibleForTesting
+ static String prependToAcceptLanguagesIfNecessary(String locale, String acceptLanguages)
+ {
+ if (locale.length() != 5 || locale.charAt(2) != '_') {
+ return acceptLanguages;
+ }
+
+ String language = locale.substring(0, 2);
+ String region = locale.substring(3);
+ String languageTag = makeLanguageTag(language, region);
+
+ if (acceptLanguages.contains(languageTag)) {
+ return acceptLanguages;
+ }
+
+ Formatter parts = new Formatter();
+ parts.format("%s,", languageTag);
+ // If language is not in the accept languages list, also add language code.
+ // This will work with the IDS_ACCEPT_LANGUAGES localized strings bundled with Chrome but
+ // may fail on arbitrary lists of language tags due to differences in case and whitespace.
+ if (!acceptLanguages.contains(language + ",") && !acceptLanguages.endsWith(language)) {
+ parts.format("%s,", language);
+ }
+ parts.format("%s", acceptLanguages);
+ return parts.toString();
+ }
+
+ /**
+ * Given a list of comma-delimited language codes in decreasing order of preference, insert
+ * q-values to represent the relative quality/precedence of each language. The logic should
+ * match GenerateAcceptLanguageHeader in net/http/http_util.cc.
+ * @param languageList A comma-delimited list of language codes containing no whitespace.
+ * @return An Accept-Language header with q-values.
+ */
+ @VisibleForTesting
+ static String generateAcceptLanguageHeader(String languageList) {
+ // We use integers for qvalue and qvalue decrement that are 10 times larger than actual
+ // values to avoid a problem with comparing two floating point numbers.
+ int kQvalueDecrement10 = 2;
+ int qvalue10 = 10;
+ String[] parts = languageList.split(",");
+ Formatter langListWithQ = new Formatter();
+ for (String language : parts) {
+ if (qvalue10 == 10) {
+ // q=1.0 is implicit
+ langListWithQ.format("%s", language);
+ } else {
+ langListWithQ.format(",%s;q=0.%d", language, qvalue10);
+ }
+ // It does not make sense to have 'q=0'.
+ if (qvalue10 > kQvalueDecrement10) {
+ qvalue10 -= kQvalueDecrement10;
+ }
+ }
+ return langListWithQ.toString();
+ }
}

Powered by Google App Engine
This is Rietveld 408576698