Chromium Code Reviews| 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..666227440af90c6a19b3b92c4d9442e52abe07df |
| --- /dev/null |
| +++ b/android_webview/java/src/org/chromium/android_webview/AwVariationsSeedFetchService.java |
| @@ -0,0 +1,157 @@ |
| +// 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.SuppressLint; |
| +import android.app.job.JobParameters; |
| +import android.app.job.JobService; |
| +import android.os.AsyncTask; |
| + |
| +import org.chromium.base.ContextUtils; |
| +import org.chromium.base.Log; |
| +import org.chromium.base.ThreadUtils; |
| +import org.chromium.components.variations.firstrun.VariationsSeedFetcher; |
| + |
| +import java.io.Closeable; |
| +import java.io.File; |
| +import java.io.FileNotFoundException; |
| +import java.io.FileOutputStream; |
| +import java.io.IOException; |
| +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 tesing experiments in the native code. The fetched data is stored in the local |
| + * directory which belongs to the Service process. |
| + */ |
| +@SuppressLint("NewApi") // 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(final JobParameters params) { |
| + new FetchFinchSeedDataTask(params).execute(); |
| + return true; |
| + } |
| + |
| + @Override |
| + public boolean onStopJob(JobParameters params) { |
| + // When job parameter is not satisfied any more, the job should be rescheduled. |
| + return true; |
| + } |
| + |
| + private class FetchFinchSeedDataTask extends AsyncTask<Void, Void, Void> { |
|
Alexei Svitkine (slow)
2017/07/12 15:52:50
Can this class be static?
yiyuny
2017/07/12 18:31:30
I prefer to make it non-static because function jo
Alexei Svitkine (slow)
2017/07/12 19:01:21
That makes sense.
|
| + private JobParameters mJobParams; |
| + |
| + FetchFinchSeedDataTask(JobParameters params) { |
| + mJobParams = params; |
| + } |
| + |
| + @Override |
| + protected Void doInBackground(Void... params) { |
| + AwVariationsSeedFetchService.fetchSeed(); |
| + return null; |
| + } |
| + |
| + @Override |
| + protected void onPostExecute(Void success) { |
| + jobFinished(mJobParams, false); |
| + } |
| + } |
| + |
| + private static void fetchSeed() { |
| + assert !ThreadUtils.runningOnUiThread(); |
| + // TryLock will drop calls from other threads when one threads are executing the function. |
| + // TODO(yiyuny): Add explicitly control to make sure there is only one thread fetching the |
| + // seed in a while before the seed is expired. |
|
Alexei Svitkine (slow)
2017/07/12 15:52:50
Nit: "before the seed is expired" isn't too meanin
yiyuny
2017/07/12 18:31:30
Done.
|
| + if (sLock.tryLock()) { |
| + try { |
| + VariationsSeedFetcher.SeedInfo si = VariationsSeedFetcher.get().downloadContent( |
|
Alexei Svitkine (slow)
2017/07/12 15:52:50
Nit: seedInfo
yiyuny
2017/07/12 18:31:30
Done.
|
| + VariationsSeedFetcher.VariationsPlatform.ANDROID_WEBVIEW, ""); |
| + storeVariationsSeed(si); |
| + } catch (IOException e) { |
| + // Exceptions are handled in the downloadContent function and rethrowing the |
| + // exception is to stop the normal logic flow after it, so no error-handling here. |
| + // Not explicitly handing SocketTimeoutException and UnknownHostException for they |
| + // are both subclasses of IOException. |
| + } finally { |
| + sLock.unlock(); |
| + } |
| + } |
| + } |
| + |
| + /** |
| + * Store seed preference independently from Seed Info. |
|
Alexei Svitkine (slow)
2017/07/12 15:52:49
This comment reads like a method comment, since it
yiyuny
2017/07/12 18:31:30
Done.
|
| + */ |
| + public static class SeedPreference implements Serializable { |
| + public String signature; |
| + public String country; |
| + public String date; |
| + public boolean isGzipCompressed; |
|
Alexei Svitkine (slow)
2017/07/12 15:52:49
Can these be final - or is that something that's n
yiyuny
2017/07/12 18:31:30
Done.
|
| + |
| + public SeedPreference(final VariationsSeedFetcher.SeedInfo si) { |
|
Alexei Svitkine (slow)
2017/07/12 15:52:49
Nit: seedInfo. Acronyms are discouraged, especiall
yiyuny
2017/07/12 18:31:30
Done.
|
| + signature = si.signature; |
| + country = si.country; |
| + date = si.date; |
| + isGzipCompressed = si.isGzipCompressed; |
| + } |
| + } |
| + |
| + private static File getOrCreateWebViewVariationsDir() { |
| + File dir = new File( |
| + ContextUtils.getApplicationContext().getFilesDir(), WEBVIEW_VARIATIONS_DIR); |
|
Alexei Svitkine (slow)
2017/07/12 15:52:50
Nit: If this is wrapping already, suggest extracti
yiyuny
2017/07/12 18:31:30
Done.
|
| + if (dir.mkdir() || dir.isDirectory()) { |
| + return dir; |
| + } |
| + return null; |
| + } |
| + |
| + private static void storeVariationsSeed(final VariationsSeedFetcher.SeedInfo si) { |
|
Alexei Svitkine (slow)
2017/07/12 15:52:49
seedInfo
yiyuny
2017/07/12 18:31:30
Done.
|
| + FileOutputStream fosSeed = null; |
| + ObjectOutputStream oosSeedPref = null; |
|
Alexei Svitkine (slow)
2017/07/12 15:52:49
Nit: Move these to right above the try.
yiyuny
2017/07/12 18:31:30
Done.
|
| + File webViewVariationsDir = getOrCreateWebViewVariationsDir(); |
| + if (webViewVariationsDir == null) { |
| + return; |
| + } |
| + try { |
| + fosSeed = new FileOutputStream(new File(webViewVariationsDir, SEED_DATA_FILENAME)); |
| + fosSeed.write(si.rawSeed, 0, si.rawSeed.length); |
| + // Store separately so that reading large seed data (which is expensive) can be |
| + // prevented when checking the last seed fetch time. |
| + oosSeedPref = new ObjectOutputStream( |
| + new FileOutputStream(new File(webViewVariationsDir, SEED_PREF_FILENAME))); |
| + oosSeedPref.writeObject(new SeedPreference(si)); |
| + } catch (FileNotFoundException e) { |
| + Log.e(TAG, |
| + "FileNotFoundException failed to open file to write seed data or preference."); |
| + } catch (IOException e) { |
| + Log.e(TAG, |
| + "IOException failed to write variations seed data or preference to the file."); |
| + } finally { |
| + closeStream(fosSeed, SEED_DATA_FILENAME); |
| + closeStream(oosSeedPref, SEED_PREF_FILENAME); |
| + } |
| + } |
| + |
| + private static void closeStream(Closeable stream, String filename) { |
| + if (stream != null) { |
| + try { |
| + stream.close(); |
| + } catch (IOException e) { |
| + Log.e(TAG, "IOException failed to close " + filename + " file."); |
| + } |
| + } |
| + } |
| +} |