Commit b4cd3def authored by Paul Miller's avatar Paul Miller Committed by Commit Bot

WebView: Serve and load dummy variations seeds

Implement VariationsSeedService in the WebView services process to serve
variations seeds to apps. Implement VariationsSeedLoader to load seeds stored
in the app, and periodically request new seeds from the service, on
WebView startup.

VariationsSeedService replaces AwVariationsConfigurationService. The
remaining AwVariations* functionality (downloading seeds from the remote
variations server into the local WebView service, and serializing the seeds
to and from flash) will be replaced in a separate commit.

Downloading and serializing variations seeds is not yet implemented for
WebView, so use dummy seeds for now (3 KB of zeros, simulating the size
of an actual WebView variations seed).

Enable the new code via either the existing "enable-webview-variations"
flag or the "finch-exp" file. This is an empty file created by AGSA to
indicate we are in the variations experiment.

Run the new tests like so:
$ out/*/bin/run_webview_instrumentation_test_apk -f org.chromium.android_webview.\*.Variations\*

Design document:
https://docs.google.com/document/d/1vLLDfaJb6DQz1RQFthse4i8N_r1wLiVOcLE05i_9KAI/edit?usp=sharing

BUG=733857

Change-Id: I1171f7b82276c32ac53ebc1b2d4bec9ed1cfb31d
Reviewed-on: https://chromium-review.googlesource.com/954058Reviewed-by: default avatarMustafa Emre Acer <meacer@chromium.org>
Reviewed-by: default avatarAlexei Svitkine <asvitkine@chromium.org>
Reviewed-by: default avatarBo <boliu@chromium.org>
Commit-Queue: Paul Miller <paulmiller@chromium.org>
Cr-Commit-Position: refs/heads/master@{#546205}
parent f7ba0408
...@@ -865,6 +865,7 @@ android_library("android_webview_java") { ...@@ -865,6 +865,7 @@ android_library("android_webview_java") {
"java/src/org/chromium/android_webview/ResourcesContextWrapperFactory.java", "java/src/org/chromium/android_webview/ResourcesContextWrapperFactory.java",
"java/src/org/chromium/android_webview/ScrollAccessibilityHelper.java", "java/src/org/chromium/android_webview/ScrollAccessibilityHelper.java",
"java/src/org/chromium/android_webview/SslUtil.java", "java/src/org/chromium/android_webview/SslUtil.java",
"java/src/org/chromium/android_webview/VariationsSeedLoader.java",
"java/src/org/chromium/android_webview/WebViewChromiumRunQueue.java", "java/src/org/chromium/android_webview/WebViewChromiumRunQueue.java",
"java/src/org/chromium/android_webview/permission/AwGeolocationCallback.java", "java/src/org/chromium/android_webview/permission/AwGeolocationCallback.java",
"java/src/org/chromium/android_webview/permission/AwPermissionRequest.java", "java/src/org/chromium/android_webview/permission/AwPermissionRequest.java",
...@@ -874,6 +875,7 @@ android_library("android_webview_java") { ...@@ -874,6 +875,7 @@ android_library("android_webview_java") {
":android_webview_commandline_java", ":android_webview_commandline_java",
":android_webview_platform_services_java", ":android_webview_platform_services_java",
":android_webview_services_java", ":android_webview_services_java",
":android_webview_variations_utils_java",
":resources", ":resources",
":strings_grd", ":strings_grd",
":system_webview_manifest", ":system_webview_manifest",
...@@ -886,6 +888,7 @@ android_library("android_webview_java") { ...@@ -886,6 +888,7 @@ android_library("android_webview_java") {
"//components/navigation_interception/android:navigation_interception_java", "//components/navigation_interception/android:navigation_interception_java",
"//components/policy/android:policy_java", "//components/policy/android:policy_java",
"//components/safe_browsing/android:safe_browsing_java", "//components/safe_browsing/android:safe_browsing_java",
"//components/variations/android:variations_java",
"//components/version_info/android:version_constants_java", "//components/version_info/android:version_constants_java",
"//components/web_contents_delegate_android:web_contents_delegate_android_java", "//components/web_contents_delegate_android:web_contents_delegate_android_java",
"//components/web_restrictions:client_java", "//components/web_restrictions:client_java",
...@@ -913,6 +916,13 @@ android_library("android_webview_java") { ...@@ -913,6 +916,13 @@ android_library("android_webview_java") {
android_manifest_for_lint = system_webview_android_manifest android_manifest_for_lint = system_webview_android_manifest
} }
android_library("android_webview_variations_utils_java") {
java_files = [ "java/src/org/chromium/android_webview/VariationsUtils.java" ]
deps = [
"//components/variations/android:variations_java",
]
}
java_strings_grd("strings_grd") { java_strings_grd("strings_grd") {
grd_file = "java/strings/android_webview_strings.grd" grd_file = "java/strings/android_webview_strings.grd"
outputs = [ outputs = [
...@@ -1010,17 +1020,21 @@ android_library("android_webview_services_java") { ...@@ -1010,17 +1020,21 @@ android_library("android_webview_services_java") {
"java/src/org/chromium/android_webview/services/AwMinidumpUploaderDelegate.java", "java/src/org/chromium/android_webview/services/AwMinidumpUploaderDelegate.java",
"java/src/org/chromium/android_webview/services/CrashReceiverService.java", "java/src/org/chromium/android_webview/services/CrashReceiverService.java",
"java/src/org/chromium/android_webview/services/ServiceInit.java", "java/src/org/chromium/android_webview/services/ServiceInit.java",
"java/src/org/chromium/android_webview/services/VariationsSeedServer.java",
] ]
deps = [ deps = [
":android_webview_commandline_java", ":android_webview_commandline_java",
":android_webview_platform_services_java", ":android_webview_platform_services_java",
":android_webview_variations_utils_java",
":system_webview_manifest", ":system_webview_manifest",
"//base:base_java", "//base:base_java",
"//components/background_task_scheduler:background_task_scheduler_java", "//components/background_task_scheduler:background_task_scheduler_java",
"//components/minidump_uploader:minidump_uploader_java", "//components/minidump_uploader:minidump_uploader_java",
] ]
srcjar_deps = [
srcjar_deps = [ ":crash_receiver_aidl" ] ":crash_receiver_aidl",
":aw_variations_seed_server_aidl",
]
android_manifest_for_lint = system_webview_android_manifest android_manifest_for_lint = system_webview_android_manifest
} }
...@@ -1031,6 +1045,13 @@ android_aidl("crash_receiver_aidl") { ...@@ -1031,6 +1045,13 @@ android_aidl("crash_receiver_aidl") {
] ]
} }
android_aidl("aw_variations_seed_server_aidl") {
import_include = [ "java/src" ]
sources = [
"java/src/org/chromium/android_webview/services/IVariationsSeedServer.aidl",
]
}
if (public_android_sdk) { if (public_android_sdk) {
system_webview_apk_tmpl("system_webview_apk") { system_webview_apk_tmpl("system_webview_apk") {
android_manifest = system_webview_android_manifest android_manifest = system_webview_android_manifest
......
...@@ -40,6 +40,11 @@ ...@@ -40,6 +40,11 @@
android:exported="true" android:exported="true"
android:authorities="{{ manifest_package }}.LicenseContentProvider" /> android:authorities="{{ manifest_package }}.LicenseContentProvider" />
{% if donor_package is not defined %} {% if donor_package is not defined %}
<!-- If you change the variations services, also see
android_webview/test/shell/AndroidManifest.xml. -->
<service android:name="org.chromium.android_webview.services.VariationsSeedServer"
android:exported="true"
android:process=":webview_service" />
<service android:name="org.chromium.android_webview.services.CrashReceiverService" <service android:name="org.chromium.android_webview.services.CrashReceiverService"
android:exported="true" android:exported="true"
android:process=":webview_service" /> android:process=":webview_service" />
......
...@@ -32,6 +32,7 @@ import org.chromium.android_webview.AwResource; ...@@ -32,6 +32,7 @@ import org.chromium.android_webview.AwResource;
import org.chromium.android_webview.AwServiceWorkerController; import org.chromium.android_webview.AwServiceWorkerController;
import org.chromium.android_webview.AwTracingController; import org.chromium.android_webview.AwTracingController;
import org.chromium.android_webview.HttpAuthDatabase; import org.chromium.android_webview.HttpAuthDatabase;
import org.chromium.android_webview.VariationsSeedLoader;
import org.chromium.android_webview.command_line.CommandLineUtil; import org.chromium.android_webview.command_line.CommandLineUtil;
import org.chromium.base.BuildConfig; import org.chromium.base.BuildConfig;
import org.chromium.base.ContextUtils; import org.chromium.base.ContextUtils;
...@@ -65,6 +66,7 @@ public class WebViewChromiumAwInit { ...@@ -65,6 +66,7 @@ public class WebViewChromiumAwInit {
private WebViewDatabaseAdapter mWebViewDatabase; private WebViewDatabaseAdapter mWebViewDatabase;
private AwServiceWorkerController mServiceWorkerController; private AwServiceWorkerController mServiceWorkerController;
private AwTracingController mAwTracingController; private AwTracingController mAwTracingController;
private VariationsSeedLoader mSeedLoader;
// Guards accees to the other members, and is notifyAll() signalled on the UI thread // Guards accees to the other members, and is notifyAll() signalled on the UI thread
// when the chromium process has been started. // when the chromium process has been started.
...@@ -145,6 +147,11 @@ public class WebViewChromiumAwInit { ...@@ -145,6 +147,11 @@ public class WebViewChromiumAwInit {
// NOTE: Finished writing Java resources. From this point on, it's safe to use them. // NOTE: Finished writing Java resources. From this point on, it's safe to use them.
AwBrowserProcess.configureChildProcessLauncher(); AwBrowserProcess.configureChildProcessLauncher();
// finishVariationsInitLocked() must precede native initialization so the seed is available
// when AwFieldTrialCreator::SetUpFieldTrials() runs.
finishVariationsInitLocked();
AwBrowserProcess.start(); AwBrowserProcess.start();
AwBrowserProcess.handleMinidumpsAndSetMetricsConsent(true /* updateMetricsConsent */); AwBrowserProcess.handleMinidumpsAndSetMetricsConsent(true /* updateMetricsConsent */);
...@@ -358,4 +365,24 @@ public class WebViewChromiumAwInit { ...@@ -358,4 +365,24 @@ public class WebViewChromiumAwInit {
} }
return mWebViewDatabase; return mWebViewDatabase;
} }
// See comments in VariationsSeedLoader.java on when it's safe to call this.
public void startVariationsInit() {
synchronized (mLock) {
if (mSeedLoader == null) {
mSeedLoader = new VariationsSeedLoader();
mSeedLoader.startVariationsInit();
}
}
}
private void finishVariationsInitLocked() {
assert Thread.holdsLock(mLock);
if (mSeedLoader == null) {
Log.e(TAG, "finishVariationsInitLocked() called before startVariationsInit()");
startVariationsInit();
}
mSeedLoader.finishVariationsInit();
mSeedLoader = null; // Allow this to be GC'd after its background thread finishes.
}
} }
...@@ -163,7 +163,7 @@ public class WebViewChromiumFactoryProvider implements WebViewFactoryProvider { ...@@ -163,7 +163,7 @@ public class WebViewChromiumFactoryProvider implements WebViewFactoryProvider {
@TargetApi(Build.VERSION_CODES.N) // For getSystemService() and isUserUnlocked(). @TargetApi(Build.VERSION_CODES.N) // For getSystemService() and isUserUnlocked().
private void initialize(WebViewDelegate webViewDelegate) { private void initialize(WebViewDelegate webViewDelegate) {
// The package is used to locate the services for copying crash minidumps and requesting // The package is used to locate the services for copying crash minidumps and requesting
// variatinos seeds. So it must be set before initializing variations and before a renderer // variations seeds. So it must be set before initializing variations and before a renderer
// has a chance to crash. // has a chance to crash.
PackageInfo packageInfo = WebViewFactory.getLoadedPackageInfo(); PackageInfo packageInfo = WebViewFactory.getLoadedPackageInfo();
AwBrowserProcess.setWebViewPackageName(packageInfo.packageName); AwBrowserProcess.setWebViewPackageName(packageInfo.packageName);
...@@ -237,6 +237,8 @@ public class WebViewChromiumFactoryProvider implements WebViewFactoryProvider { ...@@ -237,6 +237,8 @@ public class WebViewChromiumFactoryProvider implements WebViewFactoryProvider {
} }
// Now safe to use WebView data directory. // Now safe to use WebView data directory.
mAwInit.startVariationsInit();
mShouldDisableThreadChecking = mShouldDisableThreadChecking =
shouldDisableThreadChecking(ContextUtils.getApplicationContext()); shouldDisableThreadChecking(ContextUtils.getApplicationContext());
......
...@@ -47,9 +47,9 @@ import java.nio.channels.FileLock; ...@@ -47,9 +47,9 @@ import java.nio.channels.FileLock;
*/ */
@JNINamespace("android_webview") @JNINamespace("android_webview")
public final class AwBrowserProcess { public final class AwBrowserProcess {
public static final String WEBVIEW_DIR_BASENAME = "webview";
private static final String TAG = "AwBrowserProcess"; private static final String TAG = "AwBrowserProcess";
private static final String WEBVIEW_DIR_BASENAME = "webview";
private static final String EXCLUSIVE_LOCK_FILE = "webview_data.lock"; private static final String EXCLUSIVE_LOCK_FILE = "webview_data.lock";
private static RandomAccessFile sLockFile; private static RandomAccessFile sLockFile;
private static FileLock sExclusiveFileLock; private static FileLock sExclusiveFileLock;
......
// Copyright 2018 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.support.annotation.Nullable;
import org.chromium.base.Log;
import org.chromium.base.PathUtils;
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.util.Date;
/**
* Utilities for manipulating variations seeds, used by both WebView and WebView's services.
*/
public class VariationsUtils {
private static final String TAG = "VariationsUtils";
private static final String SEED_FILE_NAME = "variations_seed";
private static final String NEW_SEED_FILE_NAME = "variations_seed_new";
private static final String STAMP_FILE_NAME = "variations_stamp";
private static void closeSafely(Closeable c) {
if (c != null) {
try {
c.close();
} catch (IOException e) {
Log.e(TAG, "Failed to close " + c);
}
}
}
// Both the WebView variations service and apps using WebView keep a pair of seed files in their
// data directory. New seeds are written to the new seed file, and then the old file is replaced
// with the new file.
public static File getSeedFile() {
return new File(PathUtils.getDataDirectory(), SEED_FILE_NAME);
}
public static File getNewSeedFile() {
return new File(PathUtils.getDataDirectory(), NEW_SEED_FILE_NAME);
}
public static void replaceOldWithNewSeed() {
File oldSeedFile = getSeedFile();
File newSeedFile = getNewSeedFile();
if (!newSeedFile.renameTo(oldSeedFile)) {
Log.e(TAG, "Failed to replace old seed " + oldSeedFile +
" with new seed " + newSeedFile);
}
}
// There's a 3rd timestamp file whose modification time is the time of the last seed request. In
// the app, this is used to rate-limit seed requests. In the service, this is used to cancel the
// periodic seed fetch if no app requests the seed for a long time.
public static File getStampFile() {
return new File(PathUtils.getDataDirectory(), STAMP_FILE_NAME);
}
// Get the timestamp, in milliseconds since epoch, or 0 if the file doesn't exist.
public static long getStampTime() {
return getStampFile().lastModified();
}
// Creates/updates the timestamp with the current time.
public static void updateStampTime() {
File file = getStampFile();
try {
if (!file.createNewFile()) {
long now = (new Date()).getTime();
file.setLastModified(now);
}
} catch (IOException e) {
Log.e(TAG, "Failed to write " + file);
}
}
// Returns null in case of incomplete/corrupt/missing seed.
@Nullable
public static SeedInfo readSeedFile(File inFile) {
// Read and discard the seed file, then return a mock seed. TODO(paulmiller): Return the
// actual seed, once seed downloading and serialization are implemented.
FileInputStream in = null;
try {
in = new FileInputStream(inFile);
byte[] data = new byte[1024];
while (in.read(data) > 0) {}
SeedInfo seed = new SeedInfo();
// Fill in a mock date so that seed.parseDate() doesn't crash.
seed.date = "Thu, 01 Jan 1970 12:34:56 GMT";
return seed;
} catch (IOException e) {
Log.e(TAG, "Failed reading seed file \"" + inFile + "\": " + e.getMessage());
return null;
} finally {
closeSafely(in);
}
}
// Returns true on success. "out" will always be closed, regardless of success.
public static boolean writeSeed(FileOutputStream out, SeedInfo info) {
// Write 3 KB of zeros (the current size of an actual WebView seed). TODO(paulmiller): Write
// the actual seed, once seed downloading and serialization are implemented.
byte[] zeros = new byte[3 * 1024];
try {
out.write(zeros);
return true;
} catch (IOException e) {
Log.e(TAG, "Failed writing seed file: " + e.getMessage());
return false;
} finally {
closeSafely(out);
}
}
}
// Copyright 2018 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.services;
oneway interface IVariationsSeedServer {
// Apps request variations seeds from WebView's service by calling this interface. They should
// pass the "date" field of their current seed as oldSeedDate (in milliseconds since epoch), or
// Long.MIN_VALUE to indicate they have no seed. If the service's latest seed is newer than
// oldSeedDate, the service will write it to newSeedFile.
void getSeed(in ParcelFileDescriptor newSeedFile, in long oldSeedDate);
}
...@@ -17,10 +17,15 @@ import org.chromium.base.VisibleForTesting; ...@@ -17,10 +17,15 @@ import org.chromium.base.VisibleForTesting;
*/ */
@VisibleForTesting @VisibleForTesting
public class ServiceInit { public class ServiceInit {
private static final String WEBVIEW_SERVICE_DIR_BASENAME = "webview";
private static boolean sInitDone; private static boolean sInitDone;
@VisibleForTesting
public static void setPrivateDataDirectorySuffix() {
// This is unrelated to the PathUtils directory set in WebView proper, because this code
// runs only in the service process.
PathUtils.setPrivateDataDirectorySuffix("webview");
}
public static void init(Context appContext) { public static void init(Context appContext) {
ThreadUtils.assertOnUiThread(); ThreadUtils.assertOnUiThread();
if (sInitDone) return; if (sInitDone) return;
...@@ -28,9 +33,7 @@ public class ServiceInit { ...@@ -28,9 +33,7 @@ public class ServiceInit {
// In Monochrome, ChromeApplication.attachBaseContext() will set Chrome's command line. // In Monochrome, ChromeApplication.attachBaseContext() will set Chrome's command line.
// initCommandLine() overwrites this with WebView's command line. // initCommandLine() overwrites this with WebView's command line.
CommandLineUtil.initCommandLine(); CommandLineUtil.initCommandLine();
// This is unrelated to the PathUtils directory set in WebView proper, because this code setPrivateDataDirectorySuffix();
// runs only in the service process.
PathUtils.setPrivateDataDirectorySuffix(WEBVIEW_SERVICE_DIR_BASENAME);
sInitDone = true; sInitDone = true;
} }
} }
// Copyright 2018 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.services;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.ParcelFileDescriptor.AutoCloseOutputStream;
import org.chromium.android_webview.VariationsUtils;
/**
* VariationsSeedServer is a bound service that shares the Variations seed with all the WebViews
* on the system. A WebView will bind and call getSeed, passing a file descriptor to which the
* service should write the seed.
*/
public class VariationsSeedServer extends Service {
private final IVariationsSeedServer.Stub mBinder = new IVariationsSeedServer.Stub() {
@Override
public void getSeed(ParcelFileDescriptor newSeedFile, long oldSeedDate) {
VariationsUtils.writeSeed(new AutoCloseOutputStream(newSeedFile), null);
}
};
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
@Override
public void onCreate() {
super.onCreate();
ServiceInit.init(getApplicationContext());
}
}
// Copyright 2018 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.test.services;
import android.content.Intent;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import org.chromium.android_webview.services.IVariationsSeedServer;
import org.chromium.android_webview.services.VariationsSeedServer;
import org.chromium.base.test.util.CallbackHelper;
/**
* VariationsSeedServer is a bound service that shares the Variations seed with all the WebViews
* on the system. A WebView will bind and call getSeed, passing a file descriptor to which the
* service should write the seed.
*/
public class MockVariationsSeedServer extends VariationsSeedServer {
private static CallbackHelper sOnSeedRequested = new CallbackHelper();
public static CallbackHelper getRequestHelper() {
return sOnSeedRequested;
}
private final IVariationsSeedServer.Stub mMockBinder = new IVariationsSeedServer.Stub() {
@Override
public void getSeed(ParcelFileDescriptor newSeedFile, long oldSeedDate) {
sOnSeedRequested.notifyCalled();
}
};
@Override
public IBinder onBind(Intent intent) {
return mMockBinder;
}
}
// Copyright 2018 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.test.services;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.ConditionVariable;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.MediumTest;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.android_webview.services.IVariationsSeedServer;
import org.chromium.android_webview.services.VariationsSeedServer;
import org.chromium.base.ContextUtils;
import org.chromium.base.test.BaseJUnit4ClassRunner;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
* Test VariationsSeedServer.
*/
@RunWith(BaseJUnit4ClassRunner.class)
public class VariationsSeedServerTest {
private static final long BINDER_TIMEOUT_MILLIS = 10000;
private File mTempFile;
@Before
public void setUp() throws IOException {
ContextUtils.initApplicationContextForTests(
InstrumentationRegistry.getInstrumentation().getTargetContext()
.getApplicationContext());
mTempFile = File.createTempFile("test_variations_seed", null);
}
@After
public void tearDown() throws IOException {
Assert.assertTrue("Failed to delete \"" + mTempFile + "\"", mTempFile.delete());
}
@Test
@MediumTest
public void testGetSeed() throws FileNotFoundException {
final ConditionVariable getSeedCalled = new ConditionVariable();
final ParcelFileDescriptor file =
ParcelFileDescriptor.open(mTempFile, ParcelFileDescriptor.MODE_WRITE_ONLY);
ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
try {
// TODO(paulmiller): Test with various oldSeedDate values, after
// VariationsSeedServer can write actual seeds (with actual date values).
IVariationsSeedServer.Stub.asInterface(service)
.getSeed(file, /*oldSeedDate=*/ 0);
} catch (RemoteException e) {
Assert.fail("Faild requesting seed: " + e.getMessage());
} finally {
ContextUtils.getApplicationContext().unbindService(this);
getSeedCalled.open();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {}
};
Intent intent = new Intent(
ContextUtils.getApplicationContext(), VariationsSeedServer.class);
Assert.assertTrue("Failed to bind to VariationsSeedServer",
ContextUtils.getApplicationContext()
.bindService(intent, connection, Context.BIND_AUTO_CREATE));
Assert.assertTrue("Timed out waiting for getSeed() to return",
getSeedCalled.block(BINDER_TIMEOUT_MILLIS));
}
}
...@@ -121,6 +121,7 @@ instrumentation_test_apk("webview_instrumentation_test_apk") { ...@@ -121,6 +121,7 @@ instrumentation_test_apk("webview_instrumentation_test_apk") {
"//android_webview:android_webview_java", "//android_webview:android_webview_java",
"//android_webview:android_webview_platform_services_java", "//android_webview:android_webview_platform_services_java",
"//android_webview:android_webview_services_java", "//android_webview:android_webview_services_java",
"//android_webview:aw_variations_seed_server_aidl",
"//android_webview/test/embedded_test_server:aw_net_java_test_support", "//android_webview/test/embedded_test_server:aw_net_java_test_support",
"//base:base_java", "//base:base_java",
"//base:base_java_test_support", "//base:base_java_test_support",
...@@ -220,6 +221,7 @@ instrumentation_test_apk("webview_instrumentation_test_apk") { ...@@ -220,6 +221,7 @@ instrumentation_test_apk("webview_instrumentation_test_apk") {
"../javatests/src/org/chromium/android_webview/test/TestAwContentsClient.java", "../javatests/src/org/chromium/android_webview/test/TestAwContentsClient.java",
"../javatests/src/org/chromium/android_webview/test/TestAwServiceWorkerClient.java", "../javatests/src/org/chromium/android_webview/test/TestAwServiceWorkerClient.java",
"../javatests/src/org/chromium/android_webview/test/UserAgentTest.java", "../javatests/src/org/chromium/android_webview/test/UserAgentTest.java",
"../javatests/src/org/chromium/android_webview/test/VariationsSeedLoaderTest.java",
"../javatests/src/org/chromium/android_webview/test/VisualStateTest.java", "../javatests/src/org/chromium/android_webview/test/VisualStateTest.java",
"../javatests/src/org/chromium/android_webview/test/WebKitHitTestTest.java", "../javatests/src/org/chromium/android_webview/test/WebKitHitTestTest.java",
"../javatests/src/org/chromium/android_webview/test/WebViewAsynchronousFindApisTest.java", "../javatests/src/org/chromium/android_webview/test/WebViewAsynchronousFindApisTest.java",
...@@ -228,6 +230,8 @@ instrumentation_test_apk("webview_instrumentation_test_apk") { ...@@ -228,6 +230,8 @@ instrumentation_test_apk("webview_instrumentation_test_apk") {
"../javatests/src/org/chromium/android_webview/test/WebViewWebVrTest.java", "../javatests/src/org/chromium/android_webview/test/WebViewWebVrTest.java",
"../javatests/src/org/chromium/android_webview/test/services/CrashReceiverServiceTest.java", "../javatests/src/org/chromium/android_webview/test/services/CrashReceiverServiceTest.java",
"../javatests/src/org/chromium/android_webview/test/services/MinidumpUploaderTest.java", "../javatests/src/org/chromium/android_webview/test/services/MinidumpUploaderTest.java",
"../javatests/src/org/chromium/android_webview/test/services/MockVariationsSeedServer.java",
"../javatests/src/org/chromium/android_webview/test/services/VariationsSeedServerTest.java",
"../javatests/src/org/chromium/android_webview/test/services/VisualStateCallbackTest.java", "../javatests/src/org/chromium/android_webview/test/services/VisualStateCallbackTest.java",
"../javatests/src/org/chromium/android_webview/test/util/AwQuotaManagerBridgeTestUtil.java", "../javatests/src/org/chromium/android_webview/test/util/AwQuotaManagerBridgeTestUtil.java",
"../javatests/src/org/chromium/android_webview/test/util/AwTestTouchUtils.java", "../javatests/src/org/chromium/android_webview/test/util/AwTestTouchUtils.java",
......
...@@ -64,5 +64,12 @@ ...@@ -64,5 +64,12 @@
android:process=":second_browser_process" android:process=":second_browser_process"
android:isolatedProcess="false" android:isolatedProcess="false"
android:exported="false" /> android:exported="false" />
<!-- If you change the variations services, also see
android_webview/apk/java/AndroidManifest.xml. -->
<service android:name="org.chromium.android_webview.services.VariationsSeedServer"
android:exported="true"
android:process=":webview_service" />
<service android:name="org.chromium.android_webview.test.services.MockVariationsSeedServer"
android:exported="true" />
</application> </application>
</manifest> </manifest>
...@@ -22,6 +22,10 @@ import java.net.MalformedURLException; ...@@ -22,6 +22,10 @@ import java.net.MalformedURLException;
import java.net.SocketTimeoutException; import java.net.SocketTimeoutException;
import java.net.URL; import java.net.URL;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/** /**
...@@ -123,6 +127,13 @@ public class VariationsSeedFetcher { ...@@ -123,6 +127,13 @@ public class VariationsSeedFetcher {
public String date; public String date;
public boolean isGzipCompressed; public boolean isGzipCompressed;
public byte[] seedData; public byte[] seedData;
public Date parseDate() throws ParseException {
// The date field comes from the HTTP "Date" header, which has this format. (See RFC
// 2616, sections 3.3.1 and 14.18.) SimpleDateFormat is weirdly not thread-safe, so
// instantiate a new one for each call.
return new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US).parse(date);
}
} }
/** /**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment