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

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

Issue 2970993002: Add AwVariationsSeedFetcher and refactory VariationsSeedFetcher (Closed)
Patch Set: Update unittest 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;
25 import java.util.HashMap;
26 import java.util.Map;
26 import java.util.concurrent.TimeUnit; 27 import java.util.concurrent.TimeUnit;
27 28
28 /** 29 /**
29 * Fetches the variations seed before the actual first run of Chrome. 30 * Fetches the variations seed before the actual first run of Chrome.
30 */ 31 */
31 public class VariationsSeedFetcher { 32 public class VariationsSeedFetcher {
32 private static final String TAG = "VariationsSeedFetch"; 33 private static final String TAG = "VariationsSeedFetch";
33 private static final String VARIATIONS_SERVER_URL = 34
34 "https://clientservices.googleapis.com/chrome-variations/seed?osname =android"; 35 public static final String VARIATIONS_PLATFORM = "android";
Alexei Svitkine (slow) 2017/07/06 22:01:10 Nit: VARIATIONS_PLATFORM_ANDROID Also, add a blan
36 public static final String VARIATIONS_SERVER_URL =
37 "https://clientservices.googleapis.com/chrome-variations/seed?osname =";
38
39 public static final String SIGNATURE_HEADERFIELD = "X-Seed-Signature";
40 public static final String COUNTRY_HEADERFIELD = "X-Country";
41 public static final String DATE_HEADERFIELD = "Date";
42 public static final String IM_HEADERFIELD = "IM";
35 43
36 private static final int BUFFER_SIZE = 4096; 44 private static final int BUFFER_SIZE = 4096;
37 private static final int READ_TIMEOUT = 3000; // time in ms 45 private static final int READ_TIMEOUT = 3000; // time in ms
38 private static final int REQUEST_TIMEOUT = 1000; // time in ms 46 private static final int REQUEST_TIMEOUT = 1000; // time in ms
39 47
40 // Values for the "Variations.FirstRun.SeedFetchResult" sparse histogram, wh ich also logs 48 // 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. 49 // 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. 50 // 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; 51 private static final int SEED_FETCH_RESULT_UNKNOWN_HOST_EXCEPTION = -3;
44 private static final int SEED_FETCH_RESULT_TIMEOUT = -2; 52 private static final int SEED_FETCH_RESULT_TIMEOUT = -2;
(...skipping 25 matching lines...) Expand all
70 * Override the VariationsSeedFetcher, typically with a mock, for testing cl asses that depend on 78 * Override the VariationsSeedFetcher, typically with a mock, for testing cl asses that depend on
71 * this one. 79 * this one.
72 * @param fetcher the mock. 80 * @param fetcher the mock.
73 */ 81 */
74 @VisibleForTesting 82 @VisibleForTesting
75 public static void setVariationsSeedFetcherForTesting(VariationsSeedFetcher fetcher) { 83 public static void setVariationsSeedFetcherForTesting(VariationsSeedFetcher fetcher) {
76 sInstance = fetcher; 84 sInstance = fetcher;
77 } 85 }
78 86
79 @VisibleForTesting 87 @VisibleForTesting
80 protected HttpURLConnection getServerConnection(String restrictMode) 88 protected HttpURLConnection getServerConnection(String urlString, String res trictMode)
81 throws MalformedURLException, IOException { 89 throws MalformedURLException, IOException {
82 String urlString = VARIATIONS_SERVER_URL;
83 if (restrictMode != null && !restrictMode.isEmpty()) { 90 if (restrictMode != null && !restrictMode.isEmpty()) {
84 urlString += "&restrict=" + restrictMode; 91 urlString += "&restrict=" + restrictMode;
85 } 92 }
86 URL url = new URL(urlString); 93 URL url = new URL(urlString);
87 return (HttpURLConnection) url.openConnection(); 94 return (HttpURLConnection) url.openConnection();
88 } 95 }
89 96
90 /** 97 /**
91 * Fetch the first run variations seed. 98 * Fetch the first run variations seed.
92 * @param restrictMode The restrict mode parameter to pass to the server via a URL param. 99 * @param restrictMode The restrict mode parameter to pass to the server via a URL param.
93 */ 100 */
94 public void fetchSeed(String restrictMode) { 101 public void fetchSeed(String restrictMode) {
95 assert !ThreadUtils.runningOnUiThread(); 102 assert !ThreadUtils.runningOnUiThread();
96 // Prevent multiple simultaneous fetches 103 // Prevent multiple simultaneous fetches
97 synchronized (sLock) { 104 synchronized (sLock) {
98 Context context = ContextUtils.getApplicationContext();
99 SharedPreferences prefs = ContextUtils.getAppSharedPreferences(); 105 SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
100 // Early return if an attempt has already been made to fetch the see d, even if it 106 // 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 107 // 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. 108 // 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 109 // 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. 110 // Android preference that is set when the seed is fetched by the na tive code.
105 if (prefs.getBoolean(VARIATIONS_INITIALIZED_PREF, false) 111 if (prefs.getBoolean(VARIATIONS_INITIALIZED_PREF, false)
106 || VariationsSeedBridge.hasNativePref()) { 112 || VariationsSeedBridge.hasNativePref()) {
107 return; 113 return;
108 } 114 }
109 downloadContent(context, restrictMode); 115
116 Map<String, String> headerFields = new HashMap<String, String>();
117 byte[] rawSeed = downloadContent(
118 VARIATIONS_SERVER_URL + VARIATIONS_PLATFORM, restrictMode, h eaderFields);
119 if (rawSeed != null) {
120 String signature = headerFields.get(SIGNATURE_HEADERFIELD);
121 String country = headerFields.get(COUNTRY_HEADERFIELD);
122 String date = headerFields.get(DATE_HEADERFIELD);
123 boolean isGzipCompressed = headerFields.get(IM_HEADERFIELD).equa ls("gzip");
124 VariationsSeedBridge.setVariationsFirstRunSeed(
125 rawSeed, signature, country, date, isGzipCompressed);
126 }
110 prefs.edit().putBoolean(VARIATIONS_INITIALIZED_PREF, true).apply(); 127 prefs.edit().putBoolean(VARIATIONS_INITIALIZED_PREF, true).apply();
111 } 128 }
112 } 129 }
113 130
114 private void recordFetchResultOrCode(int resultOrCode) { 131 private void recordFetchResultOrCode(int resultOrCode) {
115 SparseHistogramSample histogram = 132 SparseHistogramSample histogram =
116 new SparseHistogramSample("Variations.FirstRun.SeedFetchResult") ; 133 new SparseHistogramSample("Variations.FirstRun.SeedFetchResult") ;
117 histogram.record(resultOrCode); 134 histogram.record(resultOrCode);
118 } 135 }
119 136
120 private void recordSeedFetchTime(long timeDeltaMillis) { 137 private void recordSeedFetchTime(long timeDeltaMillis) {
121 Log.i(TAG, "Fetched first run seed in " + timeDeltaMillis + " ms"); 138 Log.i(TAG, "Fetched first run seed in " + timeDeltaMillis + " ms");
122 TimesHistogramSample histogram = new TimesHistogramSample( 139 TimesHistogramSample histogram = new TimesHistogramSample(
123 "Variations.FirstRun.SeedFetchTime", TimeUnit.MILLISECONDS); 140 "Variations.FirstRun.SeedFetchTime", TimeUnit.MILLISECONDS);
124 histogram.record(timeDeltaMillis); 141 histogram.record(timeDeltaMillis);
125 } 142 }
126 143
127 private void recordSeedConnectTime(long timeDeltaMillis) { 144 private void recordSeedConnectTime(long timeDeltaMillis) {
128 TimesHistogramSample histogram = new TimesHistogramSample( 145 TimesHistogramSample histogram = new TimesHistogramSample(
129 "Variations.FirstRun.SeedConnectTime", TimeUnit.MILLISECONDS); 146 "Variations.FirstRun.SeedConnectTime", TimeUnit.MILLISECONDS);
130 histogram.record(timeDeltaMillis); 147 histogram.record(timeDeltaMillis);
131 } 148 }
132 149
133 private void downloadContent(Context context, String restrictMode) { 150 public byte[] downloadContent(
151 String urlString, String restrictMode, Map<String, String> headField s) {
134 HttpURLConnection connection = null; 152 HttpURLConnection connection = null;
153 byte[] rawSeed = null;
135 try { 154 try {
136 long startTimeMillis = SystemClock.elapsedRealtime(); 155 long startTimeMillis = SystemClock.elapsedRealtime();
137 connection = getServerConnection(restrictMode); 156 connection = getServerConnection(urlString, restrictMode);
138 connection.setReadTimeout(READ_TIMEOUT); 157 connection.setReadTimeout(READ_TIMEOUT);
139 connection.setConnectTimeout(REQUEST_TIMEOUT); 158 connection.setConnectTimeout(REQUEST_TIMEOUT);
140 connection.setDoInput(true); 159 connection.setDoInput(true);
141 connection.setRequestProperty("A-IM", "gzip"); 160 connection.setRequestProperty("A-IM", "gzip");
142 connection.connect(); 161 connection.connect();
143 int responseCode = connection.getResponseCode(); 162 int responseCode = connection.getResponseCode();
144 recordFetchResultOrCode(responseCode); 163 recordFetchResultOrCode(responseCode);
145 if (responseCode != HttpURLConnection.HTTP_OK) { 164 if (responseCode != HttpURLConnection.HTTP_OK) {
146 Log.w(TAG, "Non-OK response code = %d", responseCode); 165 Log.w(TAG, "Non-OK response code = %d", responseCode);
147 return; 166 return null;
148 } 167 }
149 168
150 recordSeedConnectTime(SystemClock.elapsedRealtime() - startTimeMilli s); 169 recordSeedConnectTime(SystemClock.elapsedRealtime() - startTimeMilli s);
151 // Convert the InputStream into a byte array. 170 // Convert the InputStream into a byte array.
152 byte[] rawSeed = getRawSeed(connection); 171 rawSeed = getRawSeed(connection);
153 String signature = getHeaderFieldOrEmpty(connection, "X-Seed-Signatu re"); 172 headFields.put(SIGNATURE_HEADERFIELD,
154 String country = getHeaderFieldOrEmpty(connection, "X-Country"); 173 getHeaderFieldOrEmpty(connection, SIGNATURE_HEADERFIELD));
155 String date = getHeaderFieldOrEmpty(connection, "Date"); 174 headFields.put(
156 boolean isGzipCompressed = getHeaderFieldOrEmpty(connection, "IM").e quals("gzip"); 175 COUNTRY_HEADERFIELD, getHeaderFieldOrEmpty(connection, COUNT RY_HEADERFIELD));
157 VariationsSeedBridge.setVariationsFirstRunSeed( 176 headFields.put(DATE_HEADERFIELD, getHeaderFieldOrEmpty(connection, D ATE_HEADERFIELD));
158 rawSeed, signature, country, date, isGzipCompressed); 177 headFields.put(IM_HEADERFIELD, getHeaderFieldOrEmpty(connection, IM_ HEADERFIELD));
Alexei Svitkine (slow) 2017/07/06 22:01:10 Suggest defining a simple class with these fields
159 recordSeedFetchTime(SystemClock.elapsedRealtime() - startTimeMillis) ; 178 recordSeedFetchTime(SystemClock.elapsedRealtime() - startTimeMillis) ;
160 } catch (SocketTimeoutException e) { 179 } catch (SocketTimeoutException e) {
161 recordFetchResultOrCode(SEED_FETCH_RESULT_TIMEOUT); 180 recordFetchResultOrCode(SEED_FETCH_RESULT_TIMEOUT);
162 Log.w(TAG, "SocketTimeoutException fetching first run seed: ", e); 181 Log.w(TAG, "SocketTimeoutException fetching first run seed: ", e);
163 } catch (UnknownHostException e) { 182 } catch (UnknownHostException e) {
164 recordFetchResultOrCode(SEED_FETCH_RESULT_UNKNOWN_HOST_EXCEPTION); 183 recordFetchResultOrCode(SEED_FETCH_RESULT_UNKNOWN_HOST_EXCEPTION);
165 Log.w(TAG, "UnknownHostException fetching first run seed: ", e); 184 Log.w(TAG, "UnknownHostException fetching first run seed: ", e);
166 } catch (IOException e) { 185 } catch (IOException e) {
167 recordFetchResultOrCode(SEED_FETCH_RESULT_IOEXCEPTION); 186 recordFetchResultOrCode(SEED_FETCH_RESULT_IOEXCEPTION);
168 Log.w(TAG, "IOException fetching first run seed: ", e); 187 Log.w(TAG, "IOException fetching first run seed: ", e);
169 } finally { 188 } finally {
170 if (connection != null) { 189 if (connection != null) {
171 connection.disconnect(); 190 connection.disconnect();
172 } 191 }
173 } 192 }
193 return rawSeed;
paulmiller 2017/07/06 23:09:05 It looks like this can be null, but AwVariationsSe
174 } 194 }
175 195
176 private String getHeaderFieldOrEmpty(HttpURLConnection connection, String na me) { 196 private String getHeaderFieldOrEmpty(HttpURLConnection connection, String na me) {
177 String headerField = connection.getHeaderField(name); 197 String headerField = connection.getHeaderField(name);
178 if (headerField == null) { 198 if (headerField == null) {
179 return ""; 199 return "";
180 } 200 }
181 return headerField.trim(); 201 return headerField.trim();
182 } 202 }
183 203
(...skipping 12 matching lines...) Expand all
196 private byte[] convertInputStreamToByteArray(InputStream inputStream) throws IOException { 216 private byte[] convertInputStreamToByteArray(InputStream inputStream) throws IOException {
197 ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream(); 217 ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream();
198 byte[] buffer = new byte[BUFFER_SIZE]; 218 byte[] buffer = new byte[BUFFER_SIZE];
199 int charactersReadCount = 0; 219 int charactersReadCount = 0;
200 while ((charactersReadCount = inputStream.read(buffer)) != -1) { 220 while ((charactersReadCount = inputStream.read(buffer)) != -1) {
201 byteBuffer.write(buffer, 0, charactersReadCount); 221 byteBuffer.write(buffer, 0, charactersReadCount);
202 } 222 }
203 return byteBuffer.toByteArray(); 223 return byteBuffer.toByteArray();
204 } 224 }
205 } 225 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698