Commit 184d43b2 authored by Max Curran's avatar Max Curran Committed by Chromium LUCI CQ

Initial implementation of the background task to measure how often user are...

Initial implementation of the background task to measure how often user are offline and what they do while offline.

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

Bug: 1131600
Change-Id: I89f67a6bbdaf71b550518b40c06788e3dfaccf11
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2459946
Commit-Queue: Max Curran <curranmax@chromium.org>
Reviewed-by: default avatarFilip Gorski <fgorski@chromium.org>
Reviewed-by: default avatarTarun Bansal <tbansal@chromium.org>
Reviewed-by: default avatarRobert Ogden <robertogden@chromium.org>
Cr-Commit-Position: refs/heads/master@{#843303}
parent 056d872a
......@@ -897,6 +897,7 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/ntp/search/SearchBoxViewBinder.java",
"java/src/org/chromium/chrome/browser/ntp/snippets/SectionHeader.java",
"java/src/org/chromium/chrome/browser/ntp/snippets/SectionHeaderView.java",
"java/src/org/chromium/chrome/browser/offline/measurements/OfflineMeasurementsBackgroundTask.java",
"java/src/org/chromium/chrome/browser/offlinepages/AutoFetchNotifier.java",
"java/src/org/chromium/chrome/browser/offlinepages/BackgroundScheduler.java",
"java/src/org/chromium/chrome/browser/offlinepages/BackgroundSchedulerBridge.java",
......
......@@ -137,6 +137,7 @@ chrome_junit_test_java_sources = [
"junit/src/org/chromium/chrome/browser/notifications/NotificationTriggerSchedulerTest.java",
"junit/src/org/chromium/chrome/browser/notifications/channels/ChromeChannelDefinitionsTest.java",
"junit/src/org/chromium/chrome/browser/ntp/cards/InnerNodeTest.java",
"junit/src/org/chromium/chrome/browser/offline/measurements/OfflineMeasurementsBackgroundTaskUnitTest.java",
"junit/src/org/chromium/chrome/browser/offlinepages/BackgroundSchedulerTest.java",
"junit/src/org/chromium/chrome/browser/offlinepages/CctOfflinePageModelObserverTest.java",
"junit/src/org/chromium/chrome/browser/offlinepages/ClientIdTest.java",
......
......@@ -78,7 +78,8 @@ public class ChromeCachedFlags {
ChromeFeatureList.TABBED_APP_OVERFLOW_MENU_REGROUP,
ChromeFeatureList.TABBED_APP_OVERFLOW_MENU_THREE_BUTTON_ACTIONBAR,
ChromeFeatureList.USE_CHIME_ANDROID_SDK,
ChromeFeatureList.READ_LATER);
ChromeFeatureList.READ_LATER,
ChromeFeatureList.OFFLINE_MEASUREMENTS_BACKGROUND_TASK);
// clang-format on
CachedFeatureFlags.cacheNativeFlags(featuresToCache);
CachedFeatureFlags.cacheAdditionalNativeFlags();
......
......@@ -15,6 +15,7 @@ import org.chromium.chrome.browser.explore_sites.ExploreSitesBackgroundTask;
import org.chromium.chrome.browser.feed.FeedV1;
import org.chromium.chrome.browser.notifications.NotificationTriggerBackgroundTask;
import org.chromium.chrome.browser.notifications.scheduler.NotificationSchedulerTask;
import org.chromium.chrome.browser.offline.measurements.OfflineMeasurementsBackgroundTask;
import org.chromium.chrome.browser.offlinepages.OfflineBackgroundTask;
import org.chromium.chrome.browser.offlinepages.prefetch.OfflineNotificationBackgroundTask;
import org.chromium.chrome.browser.offlinepages.prefetch.PrefetchBackgroundTask;
......@@ -91,6 +92,8 @@ public class ChromeBackgroundTaskFactory implements BackgroundTaskFactory {
return new NotificationTriggerBackgroundTask();
case TaskIds.PERIODIC_BACKGROUND_SYNC_CHROME_WAKEUP_TASK_JOB_ID:
return new PeriodicBackgroundSyncChromeWakeUpTask();
case TaskIds.OFFLINE_MEASUREMENT_JOB_ID:
return new OfflineMeasurementsBackgroundTask();
// End of Java tasks. All native tasks should be listed here.
case TaskIds.QUERY_TILE_JOB_ID:
case TaskIds.FEEDV2_REFRESH_JOB_ID:
......
......@@ -64,6 +64,7 @@ import org.chromium.chrome.browser.metrics.PackageMetrics;
import org.chromium.chrome.browser.metrics.WebApkUma;
import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings;
import org.chromium.chrome.browser.notifications.channels.ChannelsUpdater;
import org.chromium.chrome.browser.offline.measurements.OfflineMeasurementsBackgroundTask;
import org.chromium.chrome.browser.partnercustomizations.PartnerBrowserCustomizations;
import org.chromium.chrome.browser.photo_picker.DecoderService;
import org.chromium.chrome.browser.policy.EnterpriseInfo;
......@@ -444,6 +445,8 @@ public class ProcessInitializationHandler {
() -> VideoTutorialShareHelper.saveUrlsToSharedPrefs());
deferredStartupHandler.addDeferredTask(
() -> TosDialogBehaviorSharedPrefInvalidator.refreshSharedPreferenceIfTosSkipped());
deferredStartupHandler.addDeferredTask(
() -> OfflineMeasurementsBackgroundTask.maybeScheduleTaskAndReportMetrics());
}
private void initChannelsAsync() {
......
monorail {
component: "UI>Browser>Offline"
}
team_email: "offline-dev@chromium.org"
tbansal@chromium.org
robertogden@chromium.org
curranmax@chromium.org
// Copyright 2020 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.chrome.browser.offline.measurements;
import android.content.Context;
import androidx.annotation.VisibleForTesting;
import org.chromium.base.ContextUtils;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.chrome.browser.flags.CachedFeatureFlags;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
import org.chromium.components.background_task_scheduler.BackgroundTask;
import org.chromium.components.background_task_scheduler.BackgroundTaskSchedulerFactory;
import org.chromium.components.background_task_scheduler.TaskIds;
import org.chromium.components.background_task_scheduler.TaskInfo;
import org.chromium.components.background_task_scheduler.TaskParameters;
import java.util.concurrent.TimeUnit;
/**
* Collects data about how often the user is offline, and what they do while offline.
*/
public class OfflineMeasurementsBackgroundTask implements BackgroundTask {
// Finch parameters and default values.
public static final String MEASUREMENT_INTERVAL_IN_MINUTES = "measurement_interval_in_minutes";
public static final int DEFAULT_MEASUREMENT_INTERVAL_IN_MINUTES = 60;
private static int sNewMeasurementIntervalInMinutesTestingOverride;
// UMA histograms.
public static final String OFFLINE_MEASUREMENTS_MEASUREMENT_INTERVAL =
"Offline.Measurements.MeasurementInterval";
public static final String OFFLINE_MEASUREMENTS_TIME_BETWEEN_CHECKS =
"Offline.Measurements.TimeBetweenChecks";
/**
* Clock to use so we can mock the time in tests.
*/
public interface Clock {
long currentTimeMillis();
}
private static Clock sClock = System::currentTimeMillis;
@VisibleForTesting
static void setClockForTesting(Clock clock) {
sClock = clock;
}
public OfflineMeasurementsBackgroundTask() {}
public static void maybeScheduleTaskAndReportMetrics() {
reportMetrics();
if (ChromeFeatureList.isEnabled(ChromeFeatureList.OFFLINE_MEASUREMENTS_BACKGROUND_TASK)) {
scheduleTask();
} else {
cancelTaskAndClearPersistedMetrics();
}
}
private static void reportMetrics() {
// Log all stored values in Prefs, then clear prefs.
long[] timeBetweenChecksMillisList = getTimeBetweenChecksFromPrefs();
for (long timeBetweenChecksMillis : timeBetweenChecksMillisList) {
if (timeBetweenChecksMillis >= 0) {
RecordHistogram.recordCustomTimesHistogram(OFFLINE_MEASUREMENTS_TIME_BETWEEN_CHECKS,
timeBetweenChecksMillis, TimeUnit.MINUTES.toMillis(1),
TimeUnit.DAYS.toMillis(1), 50);
}
}
// After logging the data to UMA, clear the data from prefs so it isn't logged again.
clearTimeBetweenChecksFromPrefs();
}
private static void scheduleTask() {
int newMeasurementIntervalInMinutes = getNewMeasurementIntervalInMinutes();
int currentTaskMeasurementIntervalInMinutes = getCurrentTaskMeasurementIntervalInMinutes();
// If the current task has the same parameters as the parameter from Finch, then we don't
// have to do anything.
if (currentTaskMeasurementIntervalInMinutes == newMeasurementIntervalInMinutes) {
return;
}
// If the parameter stored in Prefs is non-zero, then the task is already running. In that
// case we need to cancel it first so we can schedule it with the new parameter.
if (currentTaskMeasurementIntervalInMinutes != 0) {
cancelTaskAndClearPersistedMetrics();
}
// Schedule the task with the new interval.
TaskInfo.TimingInfo timingInfo =
TaskInfo.PeriodicInfo.create()
.setIntervalMs(TimeUnit.MINUTES.toMillis(newMeasurementIntervalInMinutes))
.build();
TaskInfo taskInfo =
TaskInfo.createTask(TaskIds.OFFLINE_MEASUREMENT_JOB_ID, timingInfo).build();
BackgroundTaskSchedulerFactory.getScheduler().schedule(
ContextUtils.getApplicationContext(), taskInfo);
RecordHistogram.recordCustomTimesHistogram(OFFLINE_MEASUREMENTS_MEASUREMENT_INTERVAL,
TimeUnit.MINUTES.toMillis(newMeasurementIntervalInMinutes),
TimeUnit.MINUTES.toMillis(1), TimeUnit.DAYS.toMillis(1), 50);
setCurrentTaskMeasurementIntervalInMinutes(newMeasurementIntervalInMinutes);
setLastCheckMillis(-1);
}
private static void cancelTaskAndClearPersistedMetrics() {
// Cancels the task if is currently running.
long currentTaskMeasurementIntervalInMinutes = getCurrentTaskMeasurementIntervalInMinutes();
if (currentTaskMeasurementIntervalInMinutes != 0) {
BackgroundTaskSchedulerFactory.getScheduler().cancel(
ContextUtils.getApplicationContext(), TaskIds.OFFLINE_MEASUREMENT_JOB_ID);
}
// Clears the state stored in Prefs.
SharedPreferencesManager.getInstance().removeKey(
ChromePreferenceKeys.OFFLINE_MEASUREMENTS_LAST_CHECK_MILLIS);
SharedPreferencesManager.getInstance().removeKey(
ChromePreferenceKeys
.OFFLINE_MEASUREMENTS_CURRENT_TASK_MEASUREMENT_INTERVAL_IN_MINUTES);
}
private static int getNewMeasurementIntervalInMinutes() {
if (sNewMeasurementIntervalInMinutesTestingOverride > 0) {
return sNewMeasurementIntervalInMinutesTestingOverride;
}
return ChromeFeatureList.getFieldTrialParamByFeatureAsInt(
ChromeFeatureList.OFFLINE_MEASUREMENTS_BACKGROUND_TASK,
MEASUREMENT_INTERVAL_IN_MINUTES, DEFAULT_MEASUREMENT_INTERVAL_IN_MINUTES);
}
@VisibleForTesting
static void setNewMeasurementIntervalInMinutesForTesting(int measurementIntervalInMinutes) {
sNewMeasurementIntervalInMinutesTestingOverride = measurementIntervalInMinutes;
}
/** Writes the interval used to schedule the current task to Prefs. */
private static void setCurrentTaskMeasurementIntervalInMinutes(
int currentTaskMeasurementIntervalInMinutes) {
SharedPreferencesManager.getInstance().writeInt(
ChromePreferenceKeys
.OFFLINE_MEASUREMENTS_CURRENT_TASK_MEASUREMENT_INTERVAL_IN_MINUTES,
currentTaskMeasurementIntervalInMinutes);
}
/**
* Gets the value of the current measurement interval stored in Prefs. If the task is not
* running, this will be zero. If the task is running already, it will be the Finch parameter
* used to start it.
*/
private static int getCurrentTaskMeasurementIntervalInMinutes() {
return SharedPreferencesManager.getInstance().readInt(
ChromePreferenceKeys
.OFFLINE_MEASUREMENTS_CURRENT_TASK_MEASUREMENT_INTERVAL_IN_MINUTES);
}
/** Writes the time (in milliseconds) of the last background check to Prefs. */
private static void setLastCheckMillis(long lastCheckMillis) {
SharedPreferencesManager.getInstance().writeLong(
ChromePreferenceKeys.OFFLINE_MEASUREMENTS_LAST_CHECK_MILLIS, lastCheckMillis);
}
/** Gets the time (in milliseconds) of the last background check to Prefs. */
private static long getLastCheckMillis() {
return SharedPreferencesManager.getInstance().readLong(
ChromePreferenceKeys.OFFLINE_MEASUREMENTS_LAST_CHECK_MILLIS);
}
@Override
public void reschedule(Context context) {
scheduleTask();
}
@Override
public boolean onStartTask(
Context context, TaskParameters taskParameters, TaskFinishedCallback callback) {
// If feature is no longer enabled, cancels the periodic task so it doesn't run again.
if (!CachedFeatureFlags.isEnabled(ChromeFeatureList.OFFLINE_MEASUREMENTS_BACKGROUND_TASK)) {
cancelTaskAndClearPersistedMetrics();
return false;
}
// Calculates the time since the last time this task was run and record it to UMA.
long lastCheckMillis = getLastCheckMillis();
long currentCheckMillis = sClock.currentTimeMillis();
setLastCheckMillis(currentCheckMillis);
if (lastCheckMillis > 0) {
long timeBetweenChecksMillis = currentCheckMillis - lastCheckMillis;
addTimeBetweenChecksToPrefs(timeBetweenChecksMillis);
}
return false;
}
@Override
public boolean onStopTask(Context context, TaskParameters taskParameters) {
// Informs the scheduler that the task does not need to be rescheduled.
return false;
}
private static String getTimeBetweenChecksFromPrefsAsString() {
return SharedPreferencesManager.getInstance().readString(
ChromePreferenceKeys.OFFLINE_MEASUREMENTS_TIME_BETWEEN_CHECKS_MILLIS_LIST, "");
}
private static void addTimeBetweenChecksToPrefs(long newValue) {
String existingList = getTimeBetweenChecksFromPrefsAsString();
// Add newValue to the list.
StringBuilder strBuilder = new StringBuilder(existingList);
if (strBuilder.length() > 0) {
strBuilder.append(",");
}
strBuilder.append(newValue);
// Write the new list to Prefs.
SharedPreferencesManager.getInstance().writeString(
ChromePreferenceKeys.OFFLINE_MEASUREMENTS_TIME_BETWEEN_CHECKS_MILLIS_LIST,
strBuilder.toString());
}
private static long[] getTimeBetweenChecksFromPrefs() {
String rawList = getTimeBetweenChecksFromPrefsAsString();
if (rawList.equals("")) {
return new long[0];
}
// Split list by "," and then convert to long. Any values that cannot be converted will
// return a value of -1.
String[] longAsStrings = rawList.split(",");
long[] longAsLongs = new long[longAsStrings.length];
for (int i = 0; i < longAsStrings.length; i++) {
try {
longAsLongs[i] = Long.parseLong(longAsStrings[i]);
} catch (NumberFormatException e) {
longAsLongs[i] = -1;
}
}
return longAsLongs;
}
private static void clearTimeBetweenChecksFromPrefs() {
SharedPreferencesManager.getInstance().removeKey(
ChromePreferenceKeys.OFFLINE_MEASUREMENTS_TIME_BETWEEN_CHECKS_MILLIS_LIST);
}
}
// Copyright 2020 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.chrome.browser.offline.measurements;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.content.Context;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.annotation.Config;
import org.chromium.base.FeatureList;
import org.chromium.base.metrics.test.ShadowRecordHistogram;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.chrome.browser.flags.CachedFeatureFlags;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.components.background_task_scheduler.BackgroundTask;
import org.chromium.components.background_task_scheduler.BackgroundTaskScheduler;
import org.chromium.components.background_task_scheduler.BackgroundTaskSchedulerFactory;
import org.chromium.components.background_task_scheduler.TaskIds;
import org.chromium.components.background_task_scheduler.TaskInfo;
import org.chromium.components.background_task_scheduler.TaskParameters;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;
/** Unit tests for {@link OfflineMeasurementsBackgroundTask}. */
@RunWith(BaseRobolectricTestRunner.class)
@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class})
public class OfflineMeasurementsBackgroundTaskUnitTest {
/**
* Fake of BackgroundTaskScheduler system service.
*/
public static class FakeBackgroundTaskScheduler implements BackgroundTaskScheduler {
private HashMap<Integer, TaskInfo> mTaskInfos = new HashMap<>();
@Override
public boolean schedule(Context context, TaskInfo taskInfo) {
mTaskInfos.put(taskInfo.getTaskId(), taskInfo);
return true;
}
@Override
public void cancel(Context context, int taskId) {
mTaskInfos.remove(taskId);
}
@Override
public void checkForOSUpgrade(Context context) {}
@Override
public void reschedule(Context context) {}
public TaskInfo getTaskInfo(int taskId) {
return mTaskInfos.get(taskId);
}
public Boolean containsTaskId(int taskId) {
return mTaskInfos.containsKey(taskId);
}
}
private FakeBackgroundTaskScheduler mFakeBackgroundTaskScheduler;
/**
* Fake of OfflineMeasurementsBackgroundTask.Clock that can be used to test the timing parts of
* OfflineMeasurementsBackgroundTask.
*/
public static class FakeClock implements OfflineMeasurementsBackgroundTask.Clock {
private long mCurrentTimeMillis;
public FakeClock() {
mCurrentTimeMillis = 0;
}
@Override
public long currentTimeMillis() {
return mCurrentTimeMillis;
}
public void setCurrentTimeMillis(long currentTimeMillis) {
mCurrentTimeMillis = currentTimeMillis;
}
public void advanceCurrentTimeMillis(long millis) {
mCurrentTimeMillis += millis;
}
}
private FakeClock mFakeClock;
@Before
public void setUp() {
mFakeBackgroundTaskScheduler = new FakeBackgroundTaskScheduler();
BackgroundTaskSchedulerFactory.setSchedulerForTesting(mFakeBackgroundTaskScheduler);
mFakeClock = new FakeClock();
OfflineMeasurementsBackgroundTask.setClockForTesting(mFakeClock);
ShadowRecordHistogram.reset();
// Clears the testing override for the measurement interval.
OfflineMeasurementsBackgroundTask.setNewMeasurementIntervalInMinutesForTesting(
0); // IN-TEST
}
private void setFeatureStatusForTest(boolean isEnabled) {
HashMap<String, Boolean> testFeatures = new HashMap<String, Boolean>();
testFeatures.put(ChromeFeatureList.OFFLINE_MEASUREMENTS_BACKGROUND_TASK, isEnabled);
FeatureList.setTestFeatures(testFeatures);
CachedFeatureFlags.setForTesting(
ChromeFeatureList.OFFLINE_MEASUREMENTS_BACKGROUND_TASK, isEnabled);
}
private void assertUmaHistogramHasUniqueSample(
String messagePrefix, String umaHistogram, int sample, int numSamples) {
assertEquals(String.format("%s the UMA histogram %s should have %d total entries",
messagePrefix, umaHistogram, numSamples),
numSamples, ShadowRecordHistogram.getHistogramTotalCountForTesting(umaHistogram));
if (numSamples > 0) {
assertEquals(
String.format(
"%s the UMA histogram %s should have %d samples with a value of %d",
messagePrefix, umaHistogram, numSamples, sample),
numSamples,
ShadowRecordHistogram.getHistogramValueCountForTesting(umaHistogram, sample));
}
}
private void assertTaskScheduledWithCorrectInterval(int expectedIntervalInMinutes) {
// Check that the TimingInfo of the scheduled task is: 1) an instance of PeriodicInfo, and
// 2) the periodic interval is the default interval specified in
// OfflineMeasurementBackgroundTask.
TaskInfo thisTaskInfo =
mFakeBackgroundTaskScheduler.getTaskInfo(TaskIds.OFFLINE_MEASUREMENT_JOB_ID);
TaskInfo.TimingInfo thisTimingInfo = thisTaskInfo.getTimingInfo();
assertTrue("Task's TimingInfo should be a PeriodicInfo",
thisTimingInfo instanceof TaskInfo.PeriodicInfo);
TaskInfo.PeriodicInfo thisPeriodicInfo = (TaskInfo.PeriodicInfo) thisTimingInfo;
assertEquals("Task should be periodically scheduled with the given interval",
TimeUnit.MINUTES.toMillis(expectedIntervalInMinutes),
thisPeriodicInfo.getIntervalMs());
}
@Test
public void scheduleTaskWhenFeatureDisabled() {
// Disable the Offline Measurements feature for this test.
setFeatureStatusForTest(false);
// Tries to schedule task.
OfflineMeasurementsBackgroundTask.maybeScheduleTaskAndReportMetrics();
// Check that mFakeTaskScheduler doesn't have an entry for this task.
assertFalse("Task shouldn't be scheduled when feature is disabled",
mFakeBackgroundTaskScheduler.containsTaskId(TaskIds.OFFLINE_MEASUREMENT_JOB_ID));
// Check that there are no entries in Offline.Measurements.MeasurementInterval.
assertUmaHistogramHasUniqueSample("When the feature is disabled,",
OfflineMeasurementsBackgroundTask.OFFLINE_MEASUREMENTS_MEASUREMENT_INTERVAL, 0, 0);
}
@Test
public void scheduleTaskWhenFeatureEnabled() {
// Enable the Offline Measurements feature for this test.
setFeatureStatusForTest(true);
// Tries to schedule the task.
OfflineMeasurementsBackgroundTask.maybeScheduleTaskAndReportMetrics();
// Check that mFakeTaskScheduler has an entry for this task with the correct taskInfo.
assertTrue("Task should be scheduled when the feature is enabled",
mFakeBackgroundTaskScheduler.containsTaskId(TaskIds.OFFLINE_MEASUREMENT_JOB_ID));
// Check that the task is scheduled with the default measurement interval.
assertTaskScheduledWithCorrectInterval(
OfflineMeasurementsBackgroundTask.DEFAULT_MEASUREMENT_INTERVAL_IN_MINUTES);
// Check that Offline.Measurements.MeasurementInterval has one entry of the default
// interval.
assertUmaHistogramHasUniqueSample("When the feature is enabled,",
OfflineMeasurementsBackgroundTask.OFFLINE_MEASUREMENTS_MEASUREMENT_INTERVAL,
(int) TimeUnit.MINUTES.toMillis(
OfflineMeasurementsBackgroundTask.DEFAULT_MEASUREMENT_INTERVAL_IN_MINUTES),
1);
}
@Test
public void scheduleTaskWithDifferentInterval() {
// Enables the feature for this test.
setFeatureStatusForTest(true);
// Establish test constants.
final int measurementInterval1 = 15;
final int measurementInterval2 = 30;
// Schedule the task with the first measurement interval.
OfflineMeasurementsBackgroundTask.setNewMeasurementIntervalInMinutesForTesting(
measurementInterval1);
OfflineMeasurementsBackgroundTask.maybeScheduleTaskAndReportMetrics();
// Check that task was correctly scheduled with the first measurement interval.
assertTrue("Task should be scheduled when the feature is enabled",
mFakeBackgroundTaskScheduler.containsTaskId(TaskIds.OFFLINE_MEASUREMENT_JOB_ID));
assertTaskScheduledWithCorrectInterval(measurementInterval1);
assertUmaHistogramHasUniqueSample("When the feature is enabled,",
OfflineMeasurementsBackgroundTask.OFFLINE_MEASUREMENTS_MEASUREMENT_INTERVAL,
(int) TimeUnit.MINUTES.toMillis(measurementInterval1), 1);
// Try scheduling again with the same measurement interval.
OfflineMeasurementsBackgroundTask.maybeScheduleTaskAndReportMetrics();
// If we schedule again with the same measurement interval, nothing should change.
assertTrue("Task should be scheduled when the feature is enabled",
mFakeBackgroundTaskScheduler.containsTaskId(TaskIds.OFFLINE_MEASUREMENT_JOB_ID));
assertTaskScheduledWithCorrectInterval(measurementInterval1);
assertUmaHistogramHasUniqueSample("When the feature is enabled,",
OfflineMeasurementsBackgroundTask.OFFLINE_MEASUREMENTS_MEASUREMENT_INTERVAL,
(int) TimeUnit.MINUTES.toMillis(measurementInterval1), 1);
// Schedule the task with the second measurement interval.
OfflineMeasurementsBackgroundTask.setNewMeasurementIntervalInMinutesForTesting(
measurementInterval2);
OfflineMeasurementsBackgroundTask.maybeScheduleTaskAndReportMetrics();
// Check that the task is now scheduled with the second measurement interval
assertTrue("Task should be scheduled when the feature is enabled",
mFakeBackgroundTaskScheduler.containsTaskId(TaskIds.OFFLINE_MEASUREMENT_JOB_ID));
assertTaskScheduledWithCorrectInterval(measurementInterval2);
// Check that the UMA histogram has one entry for each measurement interval.
assertEquals("There should be one entry for each time task was scheduled", 2,
ShadowRecordHistogram.getHistogramTotalCountForTesting(
OfflineMeasurementsBackgroundTask
.OFFLINE_MEASUREMENTS_MEASUREMENT_INTERVAL));
assertEquals("There should be one entry for the first measurement interval", 1,
ShadowRecordHistogram.getHistogramValueCountForTesting(
OfflineMeasurementsBackgroundTask.OFFLINE_MEASUREMENTS_MEASUREMENT_INTERVAL,
(int) TimeUnit.MINUTES.toMillis(measurementInterval1)));
assertEquals("There should be one entry for the first measurement interval", 1,
ShadowRecordHistogram.getHistogramValueCountForTesting(
OfflineMeasurementsBackgroundTask.OFFLINE_MEASUREMENTS_MEASUREMENT_INTERVAL,
(int) TimeUnit.MINUTES.toMillis(measurementInterval2)));
// Disable the feature and try to reschedule.
setFeatureStatusForTest(false);
OfflineMeasurementsBackgroundTask.maybeScheduleTaskAndReportMetrics();
// Check that the task is no longer scheduled
assertFalse("Task shouldn't be scheduled when feature is disabled",
mFakeBackgroundTaskScheduler.containsTaskId(TaskIds.OFFLINE_MEASUREMENT_JOB_ID));
}
@Test
public void runTask() {
setFeatureStatusForTest(true);
final long[] intervals = {100, 200, 300, 400, 500};
TaskParameters testParameters =
TaskParameters.create(TaskIds.OFFLINE_MEASUREMENT_JOB_ID).build();
BackgroundTask.TaskFinishedCallback testCallback = needsReschedule -> {
fail("Task shouldn't need to use callback");
};
mFakeClock.setCurrentTimeMillis(1000);
OfflineMeasurementsBackgroundTask task = new OfflineMeasurementsBackgroundTask();
task.onStartTask(null, testParameters, testCallback);
for (long interval : intervals) {
// Increment clock and run task again.
mFakeClock.advanceCurrentTimeMillis(interval);
task = new OfflineMeasurementsBackgroundTask();
task.onStartTask(null, testParameters, testCallback);
}
// Report the persisted metrics.
OfflineMeasurementsBackgroundTask.maybeScheduleTaskAndReportMetrics();
// Check that the metrics were reported as expected.
assertEquals(
"The total number of entries should equal to the number of intervals between tasks",
intervals.length,
ShadowRecordHistogram.getHistogramTotalCountForTesting(
OfflineMeasurementsBackgroundTask
.OFFLINE_MEASUREMENTS_TIME_BETWEEN_CHECKS));
for (long interval : intervals) {
assertEquals("There should be one entry for each interval between tasks", 1,
ShadowRecordHistogram.getHistogramValueCountForTesting(
OfflineMeasurementsBackgroundTask
.OFFLINE_MEASUREMENTS_TIME_BETWEEN_CHECKS,
(int) interval));
}
}
}
......@@ -194,6 +194,7 @@ const base::Feature* kFeaturesExposedToJava[] = {
&kKitKatSupported,
&kNotificationSuspender,
&kOfflineIndicatorV2,
&kOfflineMeasurementsBackgroundTask,
&kOmniboxSpareRenderer,
&kProbabilisticCryptidRenderer,
&kReachedCodeProfiler,
......@@ -549,6 +550,9 @@ const base::Feature kNotificationSuspender{"NotificationSuspender",
const base::Feature kOfflineIndicatorV2{"OfflineIndicatorV2",
base::FEATURE_ENABLED_BY_DEFAULT};
const base::Feature kOfflineMeasurementsBackgroundTask{
"OfflineMeasurementsBackgroundTask", base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kOmniboxSpareRenderer{"OmniboxSpareRenderer",
base::FEATURE_DISABLED_BY_DEFAULT};
......
......@@ -92,6 +92,7 @@ extern const base::Feature kKitKatSupported;
extern const base::Feature kLanguagesPreference;
extern const base::Feature kNotificationSuspender;
extern const base::Feature kOfflineIndicatorV2;
extern const base::Feature kOfflineMeasurementsBackgroundTask;
extern const base::Feature kOmniboxSpareRenderer;
extern const base::Feature kProbabilisticCryptidRenderer;
extern const base::Feature kReachedCodeProfiler;
......
......@@ -84,6 +84,7 @@ public class CachedFeatureFlags {
put(ChromeFeatureList.CCT_INCOGNITO_AVAILABLE_TO_THIRD_PARTY, false);
put(ChromeFeatureList.READ_LATER, false);
put(ChromeFeatureList.CCT_REMOVE_REMOTE_VIEW_IDS, true);
put(ChromeFeatureList.OFFLINE_MEASUREMENTS_BACKGROUND_TASK, false);
}
};
......
......@@ -350,6 +350,8 @@ public abstract class ChromeFeatureList {
public static final String OFFLINE_INDICATOR_ALWAYS_HTTP_PROBE =
"OfflineIndicatorAlwaysHttpProbe";
public static final String OFFLINE_INDICATOR_V2 = "OfflineIndicatorV2";
public static final String OFFLINE_MEASUREMENTS_BACKGROUND_TASK =
"OfflineMeasurementsBackgroundTask";
public static final String OFFLINE_PAGES_DESCRIPTIVE_FAIL_STATUS =
"OfflinePagesDescriptiveFailStatus";
public static final String OFFLINE_PAGES_DESCRIPTIVE_PENDING_STATUS =
......
......@@ -514,6 +514,25 @@ public final class ChromePreferenceKeys {
*/
public static final String OFFLINE_INDICATOR_V2_ENABLED = "offline_indicator_v2_enabled";
/**
* The measurement interval (in minutes) used to schedule the currently running
* OfflineMeasureBackgroundTask. This value is zero if the OfflineMeasureBackgroundTask is not
* currently running.
*/
public static final String OFFLINE_MEASUREMENTS_CURRENT_TASK_MEASUREMENT_INTERVAL_IN_MINUTES =
"Chrome.OfflineMeasurements.CurrentTaskMeasurementIntervalInMinutes";
/** Time of the last OfflineMeasurementsBackgroundTask check. */
public static final String OFFLINE_MEASUREMENTS_LAST_CHECK_MILLIS =
"Chrome.OfflineMeasurements.LastCheckMillis";
/**
* Comma separated list of time between OfflineMeasurementsBackgroundTask checks. When possible
* these values will be recorded to UMA.
*/
public static final String OFFLINE_MEASUREMENTS_TIME_BETWEEN_CHECKS_MILLIS_LIST =
"Chrome.OfflineMeasurements.TimeBetweenChecksMillisList";
/** The shared preference for the 'save card to device' checkbox status. */
public static final String PAYMENTS_CHECK_SAVE_CARD_TO_DEVICE = "check_save_card_to_device";
......@@ -849,6 +868,9 @@ public final class ChromePreferenceKeys {
IMAGE_DESCRIPTIONS_JUST_ONCE_COUNT,
IMAGE_DESCRIPTIONS_DONT_ASK_AGAIN,
ISOLATED_SPLITS_DEX_COMPILE_VERSION,
OFFLINE_MEASUREMENTS_CURRENT_TASK_MEASUREMENT_INTERVAL_IN_MINUTES,
OFFLINE_MEASUREMENTS_LAST_CHECK_MILLIS,
OFFLINE_MEASUREMENTS_TIME_BETWEEN_CHECKS_MILLIS_LIST,
PERSISTENT_OFFLINE_CONTENT_AVAILABILITY_STATUS,
PRICE_TRACKING_PRICE_WELCOME_MESSAGE_CARD,
PRICE_TRACKING_PRICE_WELCOME_MESSAGE_CARD_SHOW_COUNT,
......
......@@ -38,8 +38,9 @@ public abstract class BackgroundTaskSchedulerExternalUma {
public static final int BACKGROUND_TASK_QUERY_TILE = 23;
public static final int BACKGROUND_TASK_FEEDV2_REFRESH = 24;
public static final int BACKGROUND_TASK_DOWNLOAD_LATER = 25;
public static final int BACKGROUND_TASK_OFFLINE_MEASUREMENTS = 26;
// Keep this one at the end and increment appropriately when adding new tasks.
public static final int BACKGROUND_TASK_COUNT = 26;
public static final int BACKGROUND_TASK_COUNT = 27;
protected BackgroundTaskSchedulerExternalUma() {}
......@@ -150,6 +151,8 @@ public abstract class BackgroundTaskSchedulerExternalUma {
return BACKGROUND_TASK_QUERY_TILE;
case TaskIds.FEEDV2_REFRESH_JOB_ID:
return BACKGROUND_TASK_FEEDV2_REFRESH;
case TaskIds.OFFLINE_MEASUREMENT_JOB_ID:
return BACKGROUND_TASK_OFFLINE_MEASUREMENTS;
default:
assert false;
}
......
......@@ -125,7 +125,10 @@ public class BackgroundTaskSchedulerUmaTest {
BackgroundTaskSchedulerUma.toUmaEnumValueFromTaskId(TaskIds.QUERY_TILE_JOB_ID));
assertEquals(BackgroundTaskSchedulerUma.BACKGROUND_TASK_FEEDV2_REFRESH,
BackgroundTaskSchedulerUma.toUmaEnumValueFromTaskId(TaskIds.FEEDV2_REFRESH_JOB_ID));
assertEquals(BackgroundTaskSchedulerUma.BACKGROUND_TASK_COUNT, 26);
assertEquals(BackgroundTaskSchedulerUma.BACKGROUND_TASK_OFFLINE_MEASUREMENTS,
BackgroundTaskSchedulerUma.toUmaEnumValueFromTaskId(
TaskIds.OFFLINE_MEASUREMENT_JOB_ID));
assertEquals(BackgroundTaskSchedulerUma.BACKGROUND_TASK_COUNT, 27);
}
@Test
......
......@@ -48,6 +48,7 @@ enum class TaskIds {
PERIODIC_BACKGROUND_SYNC_CHROME_WAKEUP_TASK_JOB_ID = 105,
QUERY_TILE_JOB_ID = 106,
FEEDV2_REFRESH_JOB_ID = 107,
OFFLINE_MEASUREMENT_JOB_ID = 108,
};
} // namespace background_task
......
......@@ -21,6 +21,28 @@ reviews. Googlers can read more about this at go/gwsq-gerrit.
<histograms>
<histogram name="Offline.Measurements.MeasurementInterval" units="minutes"
expires_after="2021-04-04">
<owner>curranmax@chromium.org</owner>
<owner>tbansal@chromium.org</owner>
<summary>
The specified interval (in minutes) which the offline measurements
experiment will periodically run. This value is logged when Chrome starts up
for users where this feature is enabled.
</summary>
</histogram>
<histogram name="Offline.Measurements.TimeBetweenChecks" units="minutes"
expires_after="2021-04-04">
<owner>curranmax@chromium.org</owner>
<owner>tbansal@chromium.org</owner>
<summary>
The time (in minutes) between the last check and current check for the
offline measurements experiment. This value is logged each time the offline
measurements background task runs.
</summary>
</histogram>
<histogram name="OfflineIndicator.ConnectivityChanged.DeviceState.Offline"
enum="OfflineIndicatorSurfaceState" expires_after="2021-07-11">
<owner>curranmax@chromium.org</owner>
......
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