Index: components/variations/android/java/src/org/chromium/components/variations/firstrun/VariationsSeedFetcher.java |
diff --git a/components/variations/android/java/src/org/chromium/components/variations/firstrun/VariationsSeedFetcher.java b/components/variations/android/java/src/org/chromium/components/variations/firstrun/VariationsSeedFetcher.java |
index b02c6f1adf8d27000ab71e6a0838fbf1c3162402..a147f1a06c1ef020b22dc82f1356f7d064287164 100644 |
--- a/components/variations/android/java/src/org/chromium/components/variations/firstrun/VariationsSeedFetcher.java |
+++ b/components/variations/android/java/src/org/chromium/components/variations/firstrun/VariationsSeedFetcher.java |
@@ -4,7 +4,6 @@ |
package org.chromium.components.variations.firstrun; |
-import android.content.Context; |
import android.content.SharedPreferences; |
import android.os.SystemClock; |
@@ -30,8 +29,11 @@ import java.util.concurrent.TimeUnit; |
*/ |
public class VariationsSeedFetcher { |
private static final String TAG = "VariationsSeedFetch"; |
+ |
+ public enum VariationsPlatform { ANDROID, ANDROID_WEBVIEW } |
+ |
private static final String VARIATIONS_SERVER_URL = |
- "https://clientservices.googleapis.com/chrome-variations/seed?osname=android"; |
+ "https://clientservices.googleapis.com/chrome-variations/seed?osname="; |
private static final int BUFFER_SIZE = 4096; |
private static final int READ_TIMEOUT = 3000; // time in ms |
@@ -47,7 +49,7 @@ public class VariationsSeedFetcher { |
@VisibleForTesting |
static final String VARIATIONS_INITIALIZED_PREF = "variations_initialized"; |
- // Synchronization lock |
+ // Synchronization lock to make singleton thread-safe. |
private static final Object sLock = new Object(); |
private static VariationsSeedFetcher sInstance; |
@@ -77,9 +79,19 @@ public class VariationsSeedFetcher { |
} |
@VisibleForTesting |
- protected HttpURLConnection getServerConnection(String restrictMode) |
- throws MalformedURLException, IOException { |
+ protected HttpURLConnection getServerConnection(VariationsPlatform platform, |
+ String restrictMode) throws MalformedURLException, IOException { |
String urlString = VARIATIONS_SERVER_URL; |
+ switch (platform) { |
+ case ANDROID: |
+ urlString += "android"; |
+ break; |
+ case ANDROID_WEBVIEW: |
+ urlString += "android_webview"; |
+ break; |
+ default: |
+ assert false; |
+ } |
if (restrictMode != null && !restrictMode.isEmpty()) { |
urlString += "&restrict=" + restrictMode; |
} |
@@ -87,6 +99,17 @@ public class VariationsSeedFetcher { |
return (HttpURLConnection) url.openConnection(); |
} |
+ /** |
+ * Object holding the seed data and related fields retrieved from HTTP headers. |
+ */ |
+ public static class SeedInfo { |
+ public String signature; |
+ public String country; |
+ public String date; |
+ public boolean isGzipCompressed; |
+ public byte[] seedData; |
+ } |
+ |
/** |
* Fetch the first run variations seed. |
* @param restrictMode The restrict mode parameter to pass to the server via a URL param. |
@@ -95,7 +118,6 @@ public class VariationsSeedFetcher { |
assert !ThreadUtils.runningOnUiThread(); |
// Prevent multiple simultaneous fetches |
synchronized (sLock) { |
- Context context = ContextUtils.getApplicationContext(); |
SharedPreferences prefs = ContextUtils.getAppSharedPreferences(); |
// Early return if an attempt has already been made to fetch the seed, even if it |
// failed. Only attempt to get the initial Java seed once, since a failure probably |
@@ -106,7 +128,18 @@ public class VariationsSeedFetcher { |
|| VariationsSeedBridge.hasNativePref()) { |
return; |
} |
- downloadContent(context, restrictMode); |
+ |
+ try { |
+ SeedInfo info = downloadContent(VariationsPlatform.ANDROID, restrictMode); |
+ VariationsSeedBridge.setVariationsFirstRunSeed(info.seedData, info.signature, |
+ info.country, info.date, info.isGzipCompressed); |
+ } catch (IOException e) { |
+ // Exceptions are handled and logged in the downloadContent method, so we don't |
+ // need any exception handling here. The only reason we need a catch-statement here |
+ // is because those exceptions are re-thrown from downloadContent to skip the |
+ // normal logic flow within that method. |
+ } |
+ // VARIATIONS_INITIALIZED_PREF should still be set to true when exceptions occur |
prefs.edit().putBoolean(VARIATIONS_INITIALIZED_PREF, true).apply(); |
} |
} |
@@ -130,11 +163,23 @@ public class VariationsSeedFetcher { |
histogram.record(timeDeltaMillis); |
} |
- private void downloadContent(Context context, String restrictMode) { |
+ /** |
+ * Download the variations seed data with platform and retrictMode. |
+ * @param platform the platform parameter to let server only return experiments which can be |
+ * run on that platform. |
+ * @param restrictMode the restrict mode parameter to pass to the server via a URL param. |
+ * @return the object holds the seed data and its related header fields. |
+ * @throws SocketTimeoutException when fetching seed connection times out. |
+ * @throws UnknownHostException when fetching seed connection has an unknown host. |
+ * @throws IOException when response code is not HTTP_OK or transmission fails on the open |
+ * connection. |
+ */ |
+ public SeedInfo downloadContent(VariationsPlatform platform, String restrictMode) |
+ throws SocketTimeoutException, UnknownHostException, IOException { |
HttpURLConnection connection = null; |
try { |
long startTimeMillis = SystemClock.elapsedRealtime(); |
- connection = getServerConnection(restrictMode); |
+ connection = getServerConnection(platform, restrictMode); |
connection.setReadTimeout(READ_TIMEOUT); |
connection.setConnectTimeout(REQUEST_TIMEOUT); |
connection.setDoInput(true); |
@@ -143,29 +188,33 @@ public class VariationsSeedFetcher { |
int responseCode = connection.getResponseCode(); |
recordFetchResultOrCode(responseCode); |
if (responseCode != HttpURLConnection.HTTP_OK) { |
- Log.w(TAG, "Non-OK response code = %d", responseCode); |
- return; |
+ String errorMsg = "Non-OK response code = " + responseCode; |
+ Log.w(TAG, errorMsg); |
+ throw new IOException(errorMsg); |
} |
recordSeedConnectTime(SystemClock.elapsedRealtime() - startTimeMillis); |
- // Convert the InputStream into a byte array. |
- byte[] rawSeed = getRawSeed(connection); |
- String signature = getHeaderFieldOrEmpty(connection, "X-Seed-Signature"); |
- String country = getHeaderFieldOrEmpty(connection, "X-Country"); |
- String date = getHeaderFieldOrEmpty(connection, "Date"); |
- boolean isGzipCompressed = getHeaderFieldOrEmpty(connection, "IM").equals("gzip"); |
- VariationsSeedBridge.setVariationsFirstRunSeed( |
- rawSeed, signature, country, date, isGzipCompressed); |
+ |
+ SeedInfo info = new SeedInfo(); |
+ info.seedData = getRawSeed(connection); |
+ info.signature = getHeaderFieldOrEmpty(connection, "X-Seed-Signature"); |
+ info.country = getHeaderFieldOrEmpty(connection, "X-Country"); |
+ info.date = getHeaderFieldOrEmpty(connection, "Date"); |
+ info.isGzipCompressed = getHeaderFieldOrEmpty(connection, "IM").equals("gzip"); |
recordSeedFetchTime(SystemClock.elapsedRealtime() - startTimeMillis); |
+ return info; |
} catch (SocketTimeoutException e) { |
recordFetchResultOrCode(SEED_FETCH_RESULT_TIMEOUT); |
- Log.w(TAG, "SocketTimeoutException fetching first run seed: ", e); |
+ Log.w(TAG, "SocketTimeoutException timeout when fetching variations seed.", e); |
+ throw e; |
} catch (UnknownHostException e) { |
recordFetchResultOrCode(SEED_FETCH_RESULT_UNKNOWN_HOST_EXCEPTION); |
- Log.w(TAG, "UnknownHostException fetching first run seed: ", e); |
+ Log.w(TAG, "UnknownHostException unknown host when fetching variations seed.", e); |
+ throw e; |
} catch (IOException e) { |
recordFetchResultOrCode(SEED_FETCH_RESULT_IOEXCEPTION); |
- Log.w(TAG, "IOException fetching first run seed: ", e); |
+ Log.w(TAG, "IOException when fetching variations seed.", e); |
+ throw e; |
} finally { |
if (connection != null) { |
connection.disconnect(); |
@@ -173,6 +222,22 @@ public class VariationsSeedFetcher { |
} |
} |
+ /** |
+ * Convert a input stream into a byte array. |
+ * @param inputStream the input stream |
+ * @return the byte array which holds the data from the input stream |
+ * @throws IOException if I/O error occurs when reading data from the input stream |
+ */ |
+ public static byte[] convertInputStreamToByteArray(InputStream inputStream) throws IOException { |
+ ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream(); |
+ byte[] buffer = new byte[BUFFER_SIZE]; |
+ int charactersReadCount = 0; |
+ while ((charactersReadCount = inputStream.read(buffer)) != -1) { |
+ byteBuffer.write(buffer, 0, charactersReadCount); |
+ } |
+ return byteBuffer.toByteArray(); |
+ } |
+ |
private String getHeaderFieldOrEmpty(HttpURLConnection connection, String name) { |
String headerField = connection.getHeaderField(name); |
if (headerField == null) { |
@@ -192,14 +257,4 @@ public class VariationsSeedFetcher { |
} |
} |
} |
- |
- private byte[] convertInputStreamToByteArray(InputStream inputStream) throws IOException { |
- ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream(); |
- byte[] buffer = new byte[BUFFER_SIZE]; |
- int charactersReadCount = 0; |
- while ((charactersReadCount = inputStream.read(buffer)) != -1) { |
- byteBuffer.write(buffer, 0, charactersReadCount); |
- } |
- return byteBuffer.toByteArray(); |
- } |
} |