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

Side by Side Diff: components/variations/android/java/src/org/chromium/components/variations/firstrun/VariationsSeedFetcher.java

Issue 2975693002: Add AwVariationsSeedFetchService and refactory VariationsSeedFetcher (Closed)
Patch Set: Update for comments of Patch 4 Created 3 years, 5 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 unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package org.chromium.components.variations.firstrun; 5 package org.chromium.components.variations.firstrun;
6 6
7 import android.content.Context;
8 import android.content.SharedPreferences; 7 import android.content.SharedPreferences;
9 import android.os.SystemClock; 8 import android.os.SystemClock;
10 9
11 import org.chromium.base.ContextUtils; 10 import org.chromium.base.ContextUtils;
12 import org.chromium.base.Log; 11 import org.chromium.base.Log;
13 import org.chromium.base.ThreadUtils; 12 import org.chromium.base.ThreadUtils;
14 import org.chromium.base.VisibleForTesting; 13 import org.chromium.base.VisibleForTesting;
15 import org.chromium.base.metrics.CachedMetrics.SparseHistogramSample; 14 import org.chromium.base.metrics.CachedMetrics.SparseHistogramSample;
16 import org.chromium.base.metrics.CachedMetrics.TimesHistogramSample; 15 import org.chromium.base.metrics.CachedMetrics.TimesHistogramSample;
17 16
18 import java.io.ByteArrayOutputStream; 17 import java.io.ByteArrayOutputStream;
19 import java.io.IOException; 18 import java.io.IOException;
20 import java.io.InputStream; 19 import java.io.InputStream;
21 import java.net.HttpURLConnection; 20 import java.net.HttpURLConnection;
22 import java.net.MalformedURLException; 21 import java.net.MalformedURLException;
23 import java.net.SocketTimeoutException; 22 import java.net.SocketTimeoutException;
24 import java.net.URL; 23 import java.net.URL;
25 import java.net.UnknownHostException; 24 import java.net.UnknownHostException;
26 import java.util.concurrent.TimeUnit; 25 import java.util.concurrent.TimeUnit;
27 26
28 /** 27 /**
29 * Fetches the variations seed before the actual first run of Chrome. 28 * Fetches the variations seed before the actual first run of Chrome.
30 */ 29 */
31 public class VariationsSeedFetcher { 30 public class VariationsSeedFetcher {
32 private static final String TAG = "VariationsSeedFetch"; 31 private static final String TAG = "VariationsSeedFetch";
32
33 public enum VariationsPlatform { ANDROID, ANDROID_WEBVIEW }
34
33 private static final String VARIATIONS_SERVER_URL = 35 private static final String VARIATIONS_SERVER_URL =
34 "https://clientservices.googleapis.com/chrome-variations/seed?osname =android"; 36 "https://clientservices.googleapis.com/chrome-variations/seed?osname =";
35 37
36 private static final int BUFFER_SIZE = 4096; 38 private static final int BUFFER_SIZE = 4096;
37 private static final int READ_TIMEOUT = 3000; // time in ms 39 private static final int READ_TIMEOUT = 3000; // time in ms
38 private static final int REQUEST_TIMEOUT = 1000; // time in ms 40 private static final int REQUEST_TIMEOUT = 1000; // time in ms
39 41
40 // Values for the "Variations.FirstRun.SeedFetchResult" sparse histogram, wh ich also logs 42 // Values for the "Variations.FirstRun.SeedFetchResult" sparse histogram, wh ich also logs
41 // HTTP result codes. These are negative so that they don't conflict with th e HTTP codes. 43 // HTTP result codes. These are negative so that they don't conflict with th e HTTP codes.
42 // These values should not be renumbered or re-used since they are logged to UMA. 44 // These values should not be renumbered or re-used since they are logged to UMA.
43 private static final int SEED_FETCH_RESULT_UNKNOWN_HOST_EXCEPTION = -3; 45 private static final int SEED_FETCH_RESULT_UNKNOWN_HOST_EXCEPTION = -3;
44 private static final int SEED_FETCH_RESULT_TIMEOUT = -2; 46 private static final int SEED_FETCH_RESULT_TIMEOUT = -2;
45 private static final int SEED_FETCH_RESULT_IOEXCEPTION = -1; 47 private static final int SEED_FETCH_RESULT_IOEXCEPTION = -1;
46 48
47 @VisibleForTesting 49 @VisibleForTesting
48 static final String VARIATIONS_INITIALIZED_PREF = "variations_initialized"; 50 static final String VARIATIONS_INITIALIZED_PREF = "variations_initialized";
49 51
50 // Synchronization lock 52 // Synchronization lock to make singleton thread-safe
51 private static final Object sLock = new Object(); 53 private static final Object sLock = new Object();
52 54
53 private static VariationsSeedFetcher sInstance; 55 private static VariationsSeedFetcher sInstance;
54 56
55 @VisibleForTesting 57 @VisibleForTesting
56 VariationsSeedFetcher() {} 58 VariationsSeedFetcher() {}
57 59
58 public static VariationsSeedFetcher get() { 60 public static VariationsSeedFetcher get() {
59 // TODO(aberent) Check not running on UI thread. Doing so however makes Robolectric testing 61 // TODO(aberent) Check not running on UI thread. Doing so however makes Robolectric testing
60 // of dependent classes difficult. 62 // of dependent classes difficult.
61 synchronized (sLock) { 63 synchronized (sLock) {
62 if (sInstance == null) { 64 if (sInstance == null) {
63 sInstance = new VariationsSeedFetcher(); 65 sInstance = new VariationsSeedFetcher();
64 } 66 }
65 return sInstance; 67 return sInstance;
66 } 68 }
67 } 69 }
68 70
69 /** 71 /**
70 * Override the VariationsSeedFetcher, typically with a mock, for testing cl asses that depend on 72 * Override the VariationsSeedFetcher, typically with a mock, for testing cl asses that depend on
71 * this one. 73 * this one.
72 * @param fetcher the mock. 74 * @param fetcher the mock.
73 */ 75 */
74 @VisibleForTesting 76 @VisibleForTesting
75 public static void setVariationsSeedFetcherForTesting(VariationsSeedFetcher fetcher) { 77 public static void setVariationsSeedFetcherForTesting(VariationsSeedFetcher fetcher) {
76 sInstance = fetcher; 78 sInstance = fetcher;
77 } 79 }
78 80
79 @VisibleForTesting 81 @VisibleForTesting
80 protected HttpURLConnection getServerConnection(String restrictMode) 82 protected HttpURLConnection getServerConnection(final VariationsPlatform pla tform,
81 throws MalformedURLException, IOException { 83 final String restrictMode) throws MalformedURLException, IOException {
82 String urlString = VARIATIONS_SERVER_URL; 84 String urlString = VARIATIONS_SERVER_URL;
85 switch (platform) {
86 case ANDROID:
87 urlString += "android";
88 break;
89 case ANDROID_WEBVIEW:
90 urlString += "android_webview";
91 break;
92 default:
93 assert false;
94 }
83 if (restrictMode != null && !restrictMode.isEmpty()) { 95 if (restrictMode != null && !restrictMode.isEmpty()) {
84 urlString += "&restrict=" + restrictMode; 96 urlString += "&restrict=" + restrictMode;
85 } 97 }
86 URL url = new URL(urlString); 98 URL url = new URL(urlString);
87 return (HttpURLConnection) url.openConnection(); 99 return (HttpURLConnection) url.openConnection();
88 } 100 }
89 101
90 /** 102 /**
103 * Store the seed and its related header fields
104 */
105 public static class SeedInfo {
106 public String signature;
107 public String country;
108 public String date;
109 public boolean isGzipCompressed;
110 public byte[] rawSeed;
111 }
112
113 /**
91 * Fetch the first run variations seed. 114 * Fetch the first run variations seed.
92 * @param restrictMode The restrict mode parameter to pass to the server via a URL param. 115 * @param restrictMode The restrict mode parameter to pass to the server via a URL param.
93 */ 116 */
94 public void fetchSeed(String restrictMode) { 117 public void fetchSeed(String restrictMode) {
95 assert !ThreadUtils.runningOnUiThread(); 118 assert !ThreadUtils.runningOnUiThread();
96 // Prevent multiple simultaneous fetches 119 // Prevent multiple simultaneous fetches
97 synchronized (sLock) { 120 synchronized (sLock) {
98 Context context = ContextUtils.getApplicationContext();
99 SharedPreferences prefs = ContextUtils.getAppSharedPreferences(); 121 SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
100 // Early return if an attempt has already been made to fetch the see d, even if it 122 // Early return if an attempt has already been made to fetch the see d, even if it
101 // failed. Only attempt to get the initial Java seed once, since a f ailure probably 123 // failed. Only attempt to get the initial Java seed once, since a f ailure probably
102 // indicates a network problem that is unlikely to be resolved by a second attempt. 124 // indicates a network problem that is unlikely to be resolved by a second attempt.
103 // Note that VariationsSeedBridge.hasNativePref() is a pure Java fun ction, reading an 125 // Note that VariationsSeedBridge.hasNativePref() is a pure Java fun ction, reading an
104 // Android preference that is set when the seed is fetched by the na tive code. 126 // Android preference that is set when the seed is fetched by the na tive code.
105 if (prefs.getBoolean(VARIATIONS_INITIALIZED_PREF, false) 127 if (prefs.getBoolean(VARIATIONS_INITIALIZED_PREF, false)
106 || VariationsSeedBridge.hasNativePref()) { 128 || VariationsSeedBridge.hasNativePref()) {
107 return; 129 return;
108 } 130 }
109 downloadContent(context, restrictMode); 131
132 try {
133 SeedInfo info = downloadContent(VariationsPlatform.ANDROID, rest rictMode);
134 VariationsSeedBridge.setVariationsFirstRunSeed(info.rawSeed, inf o.signature,
135 info.country, info.date, info.isGzipCompressed);
136 } catch (IOException e) {
137 // Exceptions are handled in the downloadContent function and re throwing the
138 // exception is to stop the normal logic flow after it, so no er ror-handling here.
139 // Not explicitly handing SocketTimeoutException and UnknownHost Exception for they
140 // are both subclasses of IOException.
141 }
110 prefs.edit().putBoolean(VARIATIONS_INITIALIZED_PREF, true).apply(); 142 prefs.edit().putBoolean(VARIATIONS_INITIALIZED_PREF, true).apply();
111 } 143 }
112 } 144 }
113 145
114 private void recordFetchResultOrCode(int resultOrCode) { 146 private void recordFetchResultOrCode(int resultOrCode) {
115 SparseHistogramSample histogram = 147 SparseHistogramSample histogram =
116 new SparseHistogramSample("Variations.FirstRun.SeedFetchResult") ; 148 new SparseHistogramSample("Variations.FirstRun.SeedFetchResult") ;
117 histogram.record(resultOrCode); 149 histogram.record(resultOrCode);
118 } 150 }
119 151
120 private void recordSeedFetchTime(long timeDeltaMillis) { 152 private void recordSeedFetchTime(long timeDeltaMillis) {
121 Log.i(TAG, "Fetched first run seed in " + timeDeltaMillis + " ms"); 153 Log.i(TAG, "Fetched first run seed in " + timeDeltaMillis + " ms");
122 TimesHistogramSample histogram = new TimesHistogramSample( 154 TimesHistogramSample histogram = new TimesHistogramSample(
123 "Variations.FirstRun.SeedFetchTime", TimeUnit.MILLISECONDS); 155 "Variations.FirstRun.SeedFetchTime", TimeUnit.MILLISECONDS);
124 histogram.record(timeDeltaMillis); 156 histogram.record(timeDeltaMillis);
125 } 157 }
126 158
127 private void recordSeedConnectTime(long timeDeltaMillis) { 159 private void recordSeedConnectTime(long timeDeltaMillis) {
128 TimesHistogramSample histogram = new TimesHistogramSample( 160 TimesHistogramSample histogram = new TimesHistogramSample(
129 "Variations.FirstRun.SeedConnectTime", TimeUnit.MILLISECONDS); 161 "Variations.FirstRun.SeedConnectTime", TimeUnit.MILLISECONDS);
130 histogram.record(timeDeltaMillis); 162 histogram.record(timeDeltaMillis);
131 } 163 }
132 164
133 private void downloadContent(Context context, String restrictMode) { 165 public SeedInfo downloadContent(final VariationsPlatform platform, final Str ing restrictMode)
Alexei Svitkine (slow) 2017/07/11 19:37:24 Please add javadoc documenting params, return valu
yiyuny 2017/07/11 22:43:37 Done.
166 throws SocketTimeoutException, UnknownHostException, IOException {
134 HttpURLConnection connection = null; 167 HttpURLConnection connection = null;
168 SeedInfo info = new SeedInfo();
Alexei Svitkine (slow) 2017/07/11 19:37:24 Can this be constructed right above line 187? Ide
135 try { 169 try {
136 long startTimeMillis = SystemClock.elapsedRealtime(); 170 long startTimeMillis = SystemClock.elapsedRealtime();
137 connection = getServerConnection(restrictMode); 171 connection = getServerConnection(platform, restrictMode);
138 connection.setReadTimeout(READ_TIMEOUT); 172 connection.setReadTimeout(READ_TIMEOUT);
139 connection.setConnectTimeout(REQUEST_TIMEOUT); 173 connection.setConnectTimeout(REQUEST_TIMEOUT);
140 connection.setDoInput(true); 174 connection.setDoInput(true);
141 connection.setRequestProperty("A-IM", "gzip"); 175 connection.setRequestProperty("A-IM", "gzip");
142 connection.connect(); 176 connection.connect();
143 int responseCode = connection.getResponseCode(); 177 int responseCode = connection.getResponseCode();
144 recordFetchResultOrCode(responseCode); 178 recordFetchResultOrCode(responseCode);
145 if (responseCode != HttpURLConnection.HTTP_OK) { 179 if (responseCode != HttpURLConnection.HTTP_OK) {
146 Log.w(TAG, "Non-OK response code = %d", responseCode); 180 Log.w(TAG, "Non-OK response code = %d", responseCode);
147 return; 181 throw new IOException(
182 "HTTP error code when fetching variations seed data: " + responseCode);
Alexei Svitkine (slow) 2017/07/11 19:37:24 Instead of crafting a whole new string, how about
yiyuny 2017/07/11 22:43:37 Done.
148 } 183 }
149 184
150 recordSeedConnectTime(SystemClock.elapsedRealtime() - startTimeMilli s); 185 recordSeedConnectTime(SystemClock.elapsedRealtime() - startTimeMilli s);
151 // Convert the InputStream into a byte array. 186 // Convert the InputStream into a byte array.
152 byte[] rawSeed = getRawSeed(connection); 187 info.rawSeed = getRawSeed(connection);
153 String signature = getHeaderFieldOrEmpty(connection, "X-Seed-Signatu re"); 188 info.signature = getHeaderFieldOrEmpty(connection, "X-Seed-Signature ");
154 String country = getHeaderFieldOrEmpty(connection, "X-Country"); 189 info.country = getHeaderFieldOrEmpty(connection, "X-Country");
155 String date = getHeaderFieldOrEmpty(connection, "Date"); 190 info.date = getHeaderFieldOrEmpty(connection, "Date");
156 boolean isGzipCompressed = getHeaderFieldOrEmpty(connection, "IM").e quals("gzip"); 191 info.isGzipCompressed = getHeaderFieldOrEmpty(connection, "IM").equa ls("gzip");
157 VariationsSeedBridge.setVariationsFirstRunSeed(
158 rawSeed, signature, country, date, isGzipCompressed);
159 recordSeedFetchTime(SystemClock.elapsedRealtime() - startTimeMillis) ; 192 recordSeedFetchTime(SystemClock.elapsedRealtime() - startTimeMillis) ;
160 } catch (SocketTimeoutException e) { 193 } catch (SocketTimeoutException e) {
161 recordFetchResultOrCode(SEED_FETCH_RESULT_TIMEOUT); 194 recordFetchResultOrCode(SEED_FETCH_RESULT_TIMEOUT);
162 Log.w(TAG, "SocketTimeoutException fetching first run seed: ", e); 195 Log.w(TAG, "SocketTimeoutException timeout when fetching variations seed.", e);
196 throw e;
163 } catch (UnknownHostException e) { 197 } catch (UnknownHostException e) {
164 recordFetchResultOrCode(SEED_FETCH_RESULT_UNKNOWN_HOST_EXCEPTION); 198 recordFetchResultOrCode(SEED_FETCH_RESULT_UNKNOWN_HOST_EXCEPTION);
165 Log.w(TAG, "UnknownHostException fetching first run seed: ", e); 199 Log.w(TAG, "UnknownHostException unknown host when fetching variatio ns seed.", e);
200 throw e;
166 } catch (IOException e) { 201 } catch (IOException e) {
167 recordFetchResultOrCode(SEED_FETCH_RESULT_IOEXCEPTION); 202 recordFetchResultOrCode(SEED_FETCH_RESULT_IOEXCEPTION);
168 Log.w(TAG, "IOException fetching first run seed: ", e); 203 Log.w(TAG,
204 "IOException I/O errors while opening the connection to fetc h variations seed.",
205 e);
206 throw e;
169 } finally { 207 } finally {
170 if (connection != null) { 208 if (connection != null) {
171 connection.disconnect(); 209 connection.disconnect();
172 } 210 }
173 } 211 }
212 return info;
174 } 213 }
175 214
176 private String getHeaderFieldOrEmpty(HttpURLConnection connection, String na me) { 215 private String getHeaderFieldOrEmpty(HttpURLConnection connection, String na me) {
177 String headerField = connection.getHeaderField(name); 216 String headerField = connection.getHeaderField(name);
178 if (headerField == null) { 217 if (headerField == null) {
179 return ""; 218 return "";
180 } 219 }
181 return headerField.trim(); 220 return headerField.trim();
182 } 221 }
183 222
(...skipping 12 matching lines...) Expand all
196 private byte[] convertInputStreamToByteArray(InputStream inputStream) throws IOException { 235 private byte[] convertInputStreamToByteArray(InputStream inputStream) throws IOException {
197 ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream(); 236 ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream();
198 byte[] buffer = new byte[BUFFER_SIZE]; 237 byte[] buffer = new byte[BUFFER_SIZE];
199 int charactersReadCount = 0; 238 int charactersReadCount = 0;
200 while ((charactersReadCount = inputStream.read(buffer)) != -1) { 239 while ((charactersReadCount = inputStream.read(buffer)) != -1) {
201 byteBuffer.write(buffer, 0, charactersReadCount); 240 byteBuffer.write(buffer, 0, charactersReadCount);
202 } 241 }
203 return byteBuffer.toByteArray(); 242 return byteBuffer.toByteArray();
204 } 243 }
205 } 244 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698