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

Unified Diff: android_webview/java/src/org/chromium/android_webview/AwVariationsSeedFetchService.java

Issue 2975693002: Add AwVariationsSeedFetchService and refactory VariationsSeedFetcher (Closed)
Patch Set: Fix typo in comments 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « android_webview/java/DEPS ('k') | android_webview/javatests/DEPS » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: android_webview/java/src/org/chromium/android_webview/AwVariationsSeedFetchService.java
diff --git a/android_webview/java/src/org/chromium/android_webview/AwVariationsSeedFetchService.java b/android_webview/java/src/org/chromium/android_webview/AwVariationsSeedFetchService.java
new file mode 100644
index 0000000000000000000000000000000000000000..27bdaad23a4ac3b79617592a1e0ad5e4cb095063
--- /dev/null
+++ b/android_webview/java/src/org/chromium/android_webview/AwVariationsSeedFetchService.java
@@ -0,0 +1,237 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.android_webview;
+
+import android.annotation.TargetApi;
+import android.app.job.JobParameters;
+import android.app.job.JobService;
+import android.os.AsyncTask;
+import android.os.Build;
+
+import org.chromium.base.ContextUtils;
+import org.chromium.base.Log;
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.VisibleForTesting;
+import org.chromium.base.annotations.SuppressFBWarnings;
+import org.chromium.components.variations.firstrun.VariationsSeedFetcher;
+import org.chromium.components.variations.firstrun.VariationsSeedFetcher.SeedInfo;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * AwVariationsSeedFetchService is a Job Service to fetch test seed data which is used by Finch
+ * to enable AB testing experiments in the native code. The fetched data is stored in the local
+ * directory which belongs to the Service process. This is a prototype of the Variations Seed Fetch
+ * Service which is one part of the work of adding Finch to Android WebView.
+ */
+@TargetApi(Build.VERSION_CODES.LOLLIPOP) // JobService requires API level 21.
+public class AwVariationsSeedFetchService extends JobService {
+ private static final String TAG = "AwVartnsSeedFetchSvc";
+
+ public static final String WEBVIEW_VARIATIONS_DIR = "WebView_Variations/";
+ public static final String SEED_DATA_FILENAME = "variations_seed_data";
+ public static final String SEED_PREF_FILENAME = "variations_seed_pref";
+
+ // Synchronization lock to prevent simultaneous local seed file writing.
+ private static final Lock sLock = new ReentrantLock();
+
+ @Override
+ public boolean onStartJob(JobParameters params) {
+ // Ensure we can use ContextUtils later on.
+ ContextUtils.initApplicationContext(this.getApplicationContext());
+ new FetchFinchSeedDataTask(params).execute();
+ return true;
+ }
+
+ @Override
+ public boolean onStopJob(JobParameters params) {
+ // This method is called by the JobScheduler to stop a job before it has finished.
+ // Return true here to reschedule the job.
+ return true;
+ }
+
+ private class FetchFinchSeedDataTask extends AsyncTask<Void, Void, Void> {
+ private JobParameters mJobParams;
+
+ FetchFinchSeedDataTask(JobParameters params) {
+ mJobParams = params;
+ }
+
+ @Override
+ protected Void doInBackground(Void... params) {
+ fetchVariationsSeed();
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void success) {
+ jobFinished(mJobParams, false /* false -> don't reschedule */);
+ }
+ }
+
+ private static void fetchVariationsSeed() {
+ assert !ThreadUtils.runningOnUiThread();
+ // TryLock will drop calls from other threads when there is a thread executing the function.
+ // TODO(yiyuny): Add explicitly control to ensure there's only one threading fetching at a
+ // time and that the seed doesn't get fetched too frequently.
+ if (sLock.tryLock()) {
+ try {
+ SeedInfo seedInfo = VariationsSeedFetcher.get().downloadContent(
+ VariationsSeedFetcher.VariationsPlatform.ANDROID_WEBVIEW, "");
+ storeVariationsSeed(seedInfo);
+ } 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.
+ } finally {
+ sLock.unlock();
+ }
+ }
+ }
+
+ /**
+ * SeedPreference is used to serialize/deserialize related fields of seed data when reading or
+ * writing them to the internal storage.
+ */
+ public static class SeedPreference implements Serializable {
+ /**
+ * Let the program deserialize the data when the fields are changed.
+ */
+ private static final long serialVersionUID = 0L;
+
+ public final String signature;
+ public final String country;
+ public final String date;
+ public final boolean isGzipCompressed;
+
+ public SeedPreference(SeedInfo seedInfo) {
+ signature = seedInfo.signature;
+ country = seedInfo.country;
+ date = seedInfo.date;
+ isGzipCompressed = seedInfo.isGzipCompressed;
+ }
+ }
+
+ private static File getOrCreateWebViewVariationsDir() throws IOException {
+ File webViewFileDir = ContextUtils.getApplicationContext().getFilesDir();
+ File dir = new File(webViewFileDir, WEBVIEW_VARIATIONS_DIR);
+ if (dir.mkdir() || dir.isDirectory()) {
+ return dir;
+ }
+ throw new IOException("Failed to get or create the WebView variations directory.");
+ }
+
+ /**
+ * Store the variations seed data and its related header fields into two separate files in the
+ * directory of Android WebView.
+ * @param seedInfo The seed data and its related header fields fetched from the finch server.
+ */
+ @VisibleForTesting
+ public static void storeVariationsSeed(SeedInfo seedInfo) {
+ FileOutputStream fosSeedData = null;
+ ObjectOutputStream oosSeedPref = null;
+ try {
+ File webViewVariationsDir = getOrCreateWebViewVariationsDir();
+ File seedDataFile = File.createTempFile(SEED_DATA_FILENAME, null, webViewVariationsDir);
+ fosSeedData = new FileOutputStream(seedDataFile);
+ fosSeedData.write(seedInfo.seedData, 0, seedInfo.seedData.length);
+ renameTempFile(seedDataFile, new File(webViewVariationsDir, SEED_DATA_FILENAME));
+ // Store separately so that reading large seed data (which is expensive) can be
+ // prevented when checking the last seed fetch time.
+ File seedPrefFile = File.createTempFile(SEED_PREF_FILENAME, null, webViewVariationsDir);
+ oosSeedPref = new ObjectOutputStream(new FileOutputStream(seedPrefFile));
+ oosSeedPref.writeObject(new SeedPreference(seedInfo));
+ renameTempFile(seedPrefFile, new File(webViewVariationsDir, SEED_PREF_FILENAME));
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to write variations seed data or preference to a file." + e);
+ } finally {
+ closeStream(fosSeedData);
+ closeStream(oosSeedPref);
+ }
+ }
+
+ /**
+ * Get the variations seed data from the file in the internal storage.
+ * @return The byte array which contains the seed data.
+ * @throws IOException if fail to get or create the WebView Variations directory.
+ */
+ @VisibleForTesting
+ public static byte[] getVariationsSeedData() throws IOException {
+ FileInputStream fisSeedData = null;
+ try {
+ File webViewVariationsDir = getOrCreateWebViewVariationsDir();
+ fisSeedData = new FileInputStream(new File(webViewVariationsDir, SEED_DATA_FILENAME));
+ return VariationsSeedFetcher.convertInputStreamToByteArray(fisSeedData);
+ } finally {
+ if (fisSeedData != null) {
+ fisSeedData.close();
+ }
+ }
+ }
+
+ /**
+ * Get the variations seed preference from the file in the internal storage.
+ * @return The seed preference which holds related header fields of the seed.
+ * @throws IOException if fail to get or create the WebView Variations directory.
+ * @throws ClassNotFoundException if fail to load the class of the serialized object.
+ */
+ @VisibleForTesting
+ public static SeedPreference getVariationsSeedPreference()
+ throws IOException, ClassNotFoundException {
+ ObjectInputStream oisSeedPref = null;
+ try {
+ File webViewVariationsDir = getOrCreateWebViewVariationsDir();
+ oisSeedPref = new ObjectInputStream(
+ new FileInputStream(new File(webViewVariationsDir, SEED_PREF_FILENAME)));
+ return (SeedPreference) oisSeedPref.readObject();
+ } finally {
+ if (oisSeedPref != null) {
+ oisSeedPref.close();
+ }
+ }
+ }
+
+ /**
+ * Clear the test data.
+ * @throws IOException if fail to get or create the WebView Variations directory.
+ */
+ @SuppressFBWarnings("RV_RETURN_VALUE_IGNORED_BAD_PRACTICE") // ignoring File.delete() return
+ @VisibleForTesting
+ public static void clearDataForTesting() throws IOException {
+ File webViewVariationsDir = getOrCreateWebViewVariationsDir();
+ File seedDataFile = new File(webViewVariationsDir, SEED_DATA_FILENAME);
+ File seedPrefFile = new File(webViewVariationsDir, SEED_PREF_FILENAME);
+ seedDataFile.delete();
+ seedPrefFile.delete();
+ webViewVariationsDir.delete();
+ }
+
+ @SuppressFBWarnings("RV_RETURN_VALUE_IGNORED_BAD_PRACTICE")
+ // ignoring File.delete() and File.renameTo() return
+ private static void renameTempFile(File tempFile, File newFile) {
+ newFile.delete();
+ tempFile.renameTo(newFile);
+ }
+
+ private static void closeStream(Closeable stream) {
+ if (stream != null) {
+ try {
+ stream.close();
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to close stream. " + e);
+ }
+ }
+ }
+}
« no previous file with comments | « android_webview/java/DEPS ('k') | android_webview/javatests/DEPS » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698