Commit d571deba authored by Robbie McElrath's avatar Robbie McElrath Committed by Commit Bot

[AW] Add flags controlling the finch seed download behavior

This CL adds the following flags which will allow developers to change
the behavior of WebView's finch seed download implementation without
needing to modify any code.

 * finch-seed-expiration-age
 * finch-seed-ignore-pending-download
 * finch-seed-min-download-period
 * finch-seed-min-update-period

These flags will allow us to more easily test that seeds are being
downloaded correctly.

This also moves AwSwitches to a common package so it can be read from
the service as well.

Test: Set the flags and verify they work as expected.
Bug: 1035235
Change-Id: Ia0f179a6370218f7284e89634a637544ceb247c6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2002897
Commit-Queue: Robbie McElrath <rmcelrath@chromium.org>
Reviewed-by: default avatarChangwan Ryu <changwan@chromium.org>
Reviewed-by: default avatarNate Fischer <ntfschr@chromium.org>
Cr-Commit-Position: refs/heads/master@{#733734}
parent 2aa3dd1a
......@@ -93,6 +93,7 @@ Some interesting flags and Features:
useful for identifying which content in the app is rendered by a WebView.
* `--force-enable-metrics-reporting`: enable UMA metrics reporting (does not
override app opt-out)
* `--finch-seed-expiration-age=0 --finch-seed-min-update-period=0 --finch-seed-min-download-period=0 --finch-seed-ignore-pending-download`: always request a new finch seed when an app starts
WebView also defines its own flags and Features:
......
......@@ -17,6 +17,7 @@ import android.os.SystemClock;
import androidx.annotation.IntDef;
import androidx.annotation.VisibleForTesting;
import org.chromium.android_webview.common.AwSwitches;
import org.chromium.android_webview.common.services.IVariationsSeedServer;
import org.chromium.android_webview.common.services.ServiceNames;
import org.chromium.android_webview.common.variations.VariationsUtils;
......@@ -144,12 +145,15 @@ public class VariationsSeedLoader {
if (lastRequestTime == 0) {
return false;
}
return now < lastRequestTime + MAX_REQUEST_PERIOD_MILLIS;
long maxRequestPeriodMillis = VariationsUtils.getDurationSwitchValueInMillis(
AwSwitches.FINCH_SEED_MIN_UPDATE_PERIOD, MAX_REQUEST_PERIOD_MILLIS);
return now < lastRequestTime + maxRequestPeriodMillis;
}
private boolean isSeedExpired(long seedFileTime) {
long expirationTime = seedFileTime + SEED_EXPIRATION_MILLIS;
return getCurrentTimeMillis() > expirationTime;
long expirationDuration = VariationsUtils.getDurationSwitchValueInMillis(
AwSwitches.FINCH_SEED_EXPIRATION_AGE, SEED_EXPIRATION_MILLIS);
return getCurrentTimeMillis() > seedFileTime + expirationDuration;
}
// Loads our local copy of the seed, if any, and then renames our local copy and/or requests a
......
......@@ -33,6 +33,28 @@ public final class AwSwitches {
public static final String WEBVIEW_SAFEBROWSING_BLOCK_ALL_RESOURCES =
"webview-safebrowsing-block-all-resources";
// The length of time in seconds that an app's copy of the variations seed should be considered
// fresh. If an app's seed is older than this, a new seed will be requested from WebView's
// IVariationsSeedServer.
// No native switch.
public static final String FINCH_SEED_EXPIRATION_AGE = "finch-seed-expiration-age";
// Forces WebView's service to always schedule a new variations seed download job, even if one
// is already pending.
// No native switch.
public static final String FINCH_SEED_IGNORE_PENDING_DOWNLOAD =
"finch-seed-ignore-pending-download";
// The minimum amount of time in seconds that WebView's service will wait between two
// variations seed downloads from the variations server.
// No native switch.
public static final String FINCH_SEED_MIN_DOWNLOAD_PERIOD = "finch-seed-min-download-period";
// The minimum amount of time in seconds that the embedded WebView implementation will wait
// between two requests to WebView's service for a new variations seed.
// No native switch.
public static final String FINCH_SEED_MIN_UPDATE_PERIOD = "finch-seed-min-update-period";
// Do not instantiate this class.
private AwSwitches() {}
}
......@@ -26,6 +26,15 @@ public final class ProductionSupportedFlagList {
Flag.commandLine("show-composited-layer-borders",
"Renders a border around compositor layers to help debug and study layer "
+ "compositing."),
Flag.commandLine(AwSwitches.FINCH_SEED_EXPIRATION_AGE + "=0",
"Forces all variations seeds to be considered stale."),
Flag.commandLine(AwSwitches.FINCH_SEED_IGNORE_PENDING_DOWNLOAD,
"Forces the WebView service to reschedule a variations seed download job even "
+ "if one is already pending."),
Flag.commandLine(AwSwitches.FINCH_SEED_MIN_DOWNLOAD_PERIOD + "=0",
"Disables throttling of variations seed download jobs."),
Flag.commandLine(AwSwitches.FINCH_SEED_MIN_UPDATE_PERIOD + "=0",
"Disables throttling of new variations seed requests to the WebView service."),
Flag.commandLine("webview-log-js-console-messages",
"Mirrors JavaScript console messages to system logs."),
Flag.commandLine(AwSwitches.CRASH_UPLOADS_ENABLED_FOR_TESTING_SWITCH,
......
......@@ -5,12 +5,14 @@
package org.chromium.android_webview.common.variations;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import org.chromium.android_webview.proto.AwVariationsSeedOuterClass.AwVariationsSeed;
import org.chromium.base.BuildInfo;
import org.chromium.base.CommandLine;
import org.chromium.base.Log;
import org.chromium.base.PathUtils;
import org.chromium.components.variations.firstrun.VariationsSeedFetcher.SeedInfo;
......@@ -22,6 +24,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.text.ParseException;
import java.util.Date;
import java.util.concurrent.TimeUnit;
/**
* Utilities for manipulating variations seeds, used by both WebView and WebView's services.
......@@ -77,10 +80,15 @@ public class VariationsUtils {
// Creates/updates the timestamp with the current time.
public static void updateStampTime() {
updateStampTime(new Date().getTime());
}
// Creates/updates the timestamp with the specified time.
@VisibleForTesting
public static void updateStampTime(long now) {
File file = getStampFile();
try {
if (!file.createNewFile()) {
long now = (new Date()).getTime();
file.setLastModified(now);
}
} catch (IOException e) {
......@@ -157,6 +165,22 @@ public class VariationsUtils {
}
}
// Returns the value of the |switchName| flag converted from seconds to milliseconds. If the
// |switchName| flag isn't present, or contains an invalid value, |defaultValueMillis| will be
// returned.
public static long getDurationSwitchValueInMillis(String switchName, long defaultValueMillis) {
CommandLine cli = CommandLine.getInstance();
if (!cli.hasSwitch(switchName)) {
return defaultValueMillis;
}
try {
return TimeUnit.SECONDS.toMillis(Long.parseLong(cli.getSwitchValue(switchName)));
} catch (NumberFormatException e) {
Log.e(TAG, "Invalid value for flag " + switchName, e);
return defaultValueMillis;
}
}
// Logs an INFO message if running in a debug build of Android.
public static void debugLog(String message) {
if (BuildInfo.isDebugAndroid()) {
......
......@@ -21,11 +21,13 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.android_webview.common.AwSwitches;
import org.chromium.android_webview.common.variations.VariationsUtils;
import org.chromium.android_webview.services.AwVariationsSeedFetcher;
import org.chromium.android_webview.test.util.VariationsTestUtils;
import org.chromium.base.ContextUtils;
import org.chromium.base.test.util.CallbackHelper;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.components.background_task_scheduler.TaskIds;
import org.chromium.components.variations.firstrun.VariationsSeedFetcher;
......@@ -200,6 +202,50 @@ public class AwVariationsSeedFetcherTest {
}
}
// Tests that the --finch-seed-min-download-period flag can override the job throttling.
@Test
@SmallTest
@CommandLineFlags.Add(AwSwitches.FINCH_SEED_MIN_DOWNLOAD_PERIOD + "=0")
public void testFinchSeedMinDownloadPeriodFlag() throws IOException {
File stamp = VariationsUtils.getStampFile();
try {
// Create a recent stamp file that would usually prevent job scheduling.
Assert.assertFalse("Stamp file already exists", stamp.exists());
Assert.assertTrue("Failed to create stamp file", stamp.createNewFile());
AwVariationsSeedFetcher.scheduleIfNeeded();
mScheduler.assertScheduled();
} finally {
mScheduler.clear();
VariationsTestUtils.deleteSeeds(); // Remove the stamp file.
}
}
// Tests that the --finch-seed-ignore-pending-download flag results in jobs being rescheduled.
@Test
@SmallTest
@CommandLineFlags.Add(AwSwitches.FINCH_SEED_IGNORE_PENDING_DOWNLOAD)
public void testFinchSeedIgnorePendingDownloadFlag() {
File stamp = VariationsUtils.getStampFile();
try {
AwVariationsSeedFetcher.scheduleIfNeeded();
JobInfo originalJob = mScheduler.getPendingJob(JOB_ID);
Assert.assertNotNull("Job should have been scheduled", originalJob);
AwVariationsSeedFetcher.scheduleIfNeeded();
// Check that the job got rescheduled.
JobInfo rescheduledJob = mScheduler.getPendingJob(JOB_ID);
Assert.assertNotNull("Job should have been rescheduled", rescheduledJob);
Assert.assertNotSame(
"Rescheduled job should not be equal to the originally scheduled job",
originalJob, rescheduledJob);
} finally {
mScheduler.clear();
}
}
@Test
@SmallTest
public void testFetch() throws IOException, TimeoutException {
......
......@@ -19,12 +19,14 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.android_webview.VariationsSeedLoader;
import org.chromium.android_webview.common.AwSwitches;
import org.chromium.android_webview.common.variations.VariationsUtils;
import org.chromium.android_webview.test.services.MockVariationsSeedServer;
import org.chromium.android_webview.test.util.VariationsTestUtils;
import org.chromium.base.ContextUtils;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.test.util.CallbackHelper;
import org.chromium.base.test.util.CommandLineFlags;
import java.io.File;
import java.io.IOException;
......@@ -401,4 +403,42 @@ public class VariationsSeedLoaderTest {
VariationsSeedLoader.APP_SEED_FRESHNESS_HISTOGRAM_NAME,
(int) TimeUnit.HOURS.toMinutes(seedAgeHours)));
}
// Tests that the finch-seed-expiration-age flag works.
@Test
@MediumTest
@CommandLineFlags.Add(AwSwitches.FINCH_SEED_EXPIRATION_AGE + "=0")
public void testFinchSeedExpirationAgeFlag() throws Exception {
try {
// Create a new seed file with a recent timestamp.
File oldFile = VariationsUtils.getSeedFile();
VariationsTestUtils.writeMockSeed(oldFile);
oldFile.setLastModified(CURRENT_TIME_MILLIS);
boolean seedRequested = runTestLoaderBlocking();
Assert.assertTrue("Seed file should be requested", seedRequested);
} finally {
VariationsTestUtils.deleteSeeds();
}
}
// Tests that the finch-seed-min-update-period flag overrides the seed request throttling.
@Test
@MediumTest
@CommandLineFlags.Add(AwSwitches.FINCH_SEED_MIN_UPDATE_PERIOD + "=0")
public void testFinchSeedMinUpdatePeriodFlag() throws Exception {
try {
// Update the last modified time of the stamp file to simulate having just requested a
// new seed from the service.
VariationsUtils.getStampFile().createNewFile();
VariationsUtils.updateStampTime(CURRENT_TIME_MILLIS);
boolean seedRequested = runTestLoaderBlocking();
Assert.assertTrue("Seed file should be requested", seedRequested);
} finally {
VariationsTestUtils.deleteSeeds();
}
}
}
......@@ -13,7 +13,9 @@ import android.content.ComponentName;
import android.content.Context;
import android.os.Build;
import org.chromium.android_webview.common.AwSwitches;
import org.chromium.android_webview.common.variations.VariationsUtils;
import org.chromium.base.CommandLine;
import org.chromium.base.ContextUtils;
import org.chromium.base.Log;
import org.chromium.base.compat.ApiHelperForN;
......@@ -86,7 +88,8 @@ public class AwVariationsSeedFetcher extends JobService {
if (scheduler == null) return;
// Check if it's already scheduled.
if (getPendingJob(scheduler, JOB_ID) != null) {
if (!CommandLine.getInstance().hasSwitch(AwSwitches.FINCH_SEED_IGNORE_PENDING_DOWNLOAD)
&& getPendingJob(scheduler, JOB_ID) != null) {
VariationsUtils.debugLog("Seed download job already scheduled");
return;
}
......@@ -95,7 +98,9 @@ public class AwVariationsSeedFetcher extends JobService {
long lastRequestTime = VariationsUtils.getStampTime();
if (lastRequestTime != 0) {
long now = (new Date()).getTime();
if (now < lastRequestTime + MIN_JOB_PERIOD_MILLIS) {
long minJobPeriodMillis = VariationsUtils.getDurationSwitchValueInMillis(
AwSwitches.FINCH_SEED_MIN_DOWNLOAD_PERIOD, MIN_JOB_PERIOD_MILLIS);
if (now < lastRequestTime + minJobPeriodMillis) {
VariationsUtils.debugLog("Throttling seed download job");
return;
}
......
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