Skip to content

Commit

Permalink
[Android] Adding UMA for events in Background Task Scheduler
Browse files Browse the repository at this point in the history
Adds UMA for the following:
* Scheduling
* Canceling
* Starting a task
* Stopping a task
* Introduces the class to report uma: BackgroundTaskSchedulerUma

BUG=710630

Review-Url: https://codereview.chromium.org/2911503002
Cr-Commit-Position: refs/heads/master@{#476474}
  • Loading branch information
fgorski authored and Commit Bot committed Jun 1, 2017
1 parent adb3fe9 commit 3f7a5c7
Show file tree
Hide file tree
Showing 13 changed files with 529 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
import org.chromium.chrome.browser.webapps.ChromeWebApkHost;
import org.chromium.chrome.browser.webapps.WebApkVersionManager;
import org.chromium.chrome.browser.webapps.WebappRegistry;
import org.chromium.components.background_task_scheduler.BackgroundTaskSchedulerFactory;
import org.chromium.components.minidump_uploader.CrashFileManager;
import org.chromium.components.signin.AccountManagerHelper;
import org.chromium.content.browser.ChildProcessLauncher;
Expand Down Expand Up @@ -400,6 +401,13 @@ public void run() {
}
}
});

deferredStartupHandler.addDeferredTask(new Runnable() {
@Override
public void run() {
BackgroundTaskSchedulerFactory.getScheduler().checkForOSUpgrade(application);
}
});
}

private void initChannelsAsync() {
Expand Down
2 changes: 2 additions & 0 deletions components/background_task_scheduler/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ if (is_android) {
"android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerGcmNetworkManager.java",
"android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerJobService.java",
"android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerPrefs.java",
"android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerUma.java",
"android/java/src/org/chromium/components/background_task_scheduler/BundleToPersistableBundleConverter.java",
"android/java/src/org/chromium/components/background_task_scheduler/TaskIds.java",
"android/java/src/org/chromium/components/background_task_scheduler/TaskInfo.java",
Expand Down Expand Up @@ -68,6 +69,7 @@ if (is_android) {
"android/junit/src/org/chromium/components/background_task_scheduler/BackgroundTaskGcmTaskServiceTest.java",
"android/junit/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerPrefsTest.java",
"android/junit/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerTest.java",
"android/junit/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerUmaTest.java",
"android/junit/src/org/chromium/components/background_task_scheduler/ShadowGcmNetworkManager.java",
"android/junit/src/org/chromium/components/background_task_scheduler/TestBackgroundTask.java",
]
Expand Down
1 change: 1 addition & 0 deletions components/background_task_scheduler/OWNERS
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
awdf@chromium.org
dtrainor@chromium.org
fgorski@chromium.org
nyquist@chromium.org

Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ public int onRunTask(TaskParams params) {
ThreadUtils.runOnUiThreadBlocking(new Runnable() {
@Override
public void run() {
BackgroundTaskSchedulerUma.getInstance().reportTaskStarted(taskParams.getTaskId());
taskNeedsBackgroundProcessing.set(
backgroundTask.onStartTask(ContextUtils.getApplicationContext(), taskParams,
new TaskFinishedCallbackGcmTaskService(waiter)));
Expand All @@ -115,6 +116,7 @@ public void run() {
ThreadUtils.runOnUiThreadBlocking(new Runnable() {
@Override
public void run() {
BackgroundTaskSchedulerUma.getInstance().reportTaskStopped(taskParams.getTaskId());
taskNeedsRescheduling.set(backgroundTask.onStopTask(
ContextUtils.getApplicationContext(), taskParams));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ public boolean onStartJob(JobParameters params) {

TaskParameters taskParams =
BackgroundTaskSchedulerJobService.getTaskParametersFromJobParameters(params);

BackgroundTaskSchedulerUma.getInstance().reportTaskStarted(taskParams.getTaskId());
boolean taskNeedsBackgroundProcessing = backgroundTask.onStartTask(getApplicationContext(),
taskParams, new TaskFinishedCallbackJobService(this, backgroundTask, params));

Expand All @@ -96,6 +98,7 @@ public boolean onStopJob(JobParameters params) {

TaskParameters taskParams =
BackgroundTaskSchedulerJobService.getTaskParametersFromJobParameters(params);
BackgroundTaskSchedulerUma.getInstance().reportTaskStopped(taskParams.getTaskId());
boolean taskNeedsReschedule =
backgroundTask.onStopTask(getApplicationContext(), taskParams);
mCurrentTasks.remove(params.getJobId());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ static boolean hasParameterlessPublicConstructor(Class<? extends BackgroundTask>
public boolean schedule(Context context, TaskInfo taskInfo) {
ThreadUtils.assertOnUiThread();
boolean success = mSchedulerDelegate.schedule(context, taskInfo);
BackgroundTaskSchedulerUma.getInstance().reportTaskScheduled(taskInfo.getTaskId(), success);
if (success) {
BackgroundTaskSchedulerPrefs.addScheduledTask(taskInfo);
}
Expand All @@ -91,6 +92,7 @@ public boolean schedule(Context context, TaskInfo taskInfo) {
*/
public void cancel(Context context, int taskId) {
ThreadUtils.assertOnUiThread();
BackgroundTaskSchedulerUma.getInstance().reportTaskCanceled(taskId);
BackgroundTaskSchedulerPrefs.removeScheduledTask(taskId);
mSchedulerDelegate.cancel(context, taskId);
}
Expand All @@ -105,14 +107,19 @@ public void cancel(Context context, int taskId) {
public void checkForOSUpgrade(Context context) {
int oldSdkInt = BackgroundTaskSchedulerPrefs.getLastSdkVersion();
int newSdkInt = Build.VERSION.SDK_INT;
// No OS upgrade detected.
if (oldSdkInt == newSdkInt) return;

// Save the current SDK version to preferences.
BackgroundTaskSchedulerPrefs.setLastSdkVersion(newSdkInt);
if (oldSdkInt != newSdkInt) {
// Save the current SDK version to preferences.
BackgroundTaskSchedulerPrefs.setLastSdkVersion(newSdkInt);
}

// No OS upgrade detected or OS upgrade does not change delegate.
if (oldSdkInt == newSdkInt || !osUpgradeChangesDelegateType(oldSdkInt, newSdkInt)) {
BackgroundTaskSchedulerUma.getInstance().flushStats();
return;
}

// Check for OS upgrades forcing delegate change or "just in case" rescheduling.
if (!osUpgradeChangesDelegateType(oldSdkInt, newSdkInt)) return;
BackgroundTaskSchedulerUma.getInstance().removeCachedStats();

// Explicitly create and invoke old delegate type to cancel all scheduled tasks.
// All preference entries are kept until reschedule call, which removes then then.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
// 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.components.background_task_scheduler;

import android.content.SharedPreferences;

import org.chromium.base.ContextUtils;
import org.chromium.base.ThreadUtils;
import org.chromium.base.VisibleForTesting;
import org.chromium.base.library_loader.LibraryLoader;
import org.chromium.base.metrics.RecordHistogram;

import java.util.HashSet;
import java.util.Set;

class BackgroundTaskSchedulerUma {
// BackgroundTaskId defined in tools/metrics/histograms/enums.xml
static final int BACKGROUND_TASK_TEST = 0;
static final int BACKGROUND_TASK_OMAHA = 1;
static final int BACKGROUND_TASK_GCM = 2;
static final int BACKGROUND_TASK_NOTIFICATIONS = 3;
static final int BACKGROUND_TASK_WEBVIEW_MINIDUMP = 4;
static final int BACKGROUND_TASK_CHROME_MINIDUMP = 5;
static final int BACKGROUND_TASK_OFFLINE_PAGES = 6;
static final int BACKGROUND_TASK_OFFLINE_PREFETCH = 7;
// Keep this one at the end and increment appropriately when adding new tasks.
static final int BACKGROUND_TASK_COUNT = 8;

static final String KEY_CACHED_UMA = "bts_cached_uma";

private static BackgroundTaskSchedulerUma sInstance;

private static class CachedUmaEntry {
private static final String SEPARATOR = ":";
private String mEvent;
private int mValue;
private int mCount;

/**
* Parses a cached UMA entry from a string.
*
* @param entry A serialized entry from preferences store.
* @return A parsed CachedUmaEntry object, or <c>null</c> if parsing failed.
*/
public static CachedUmaEntry parseEntry(String entry) {
if (entry == null) return null;

String[] entryParts = entry.split(SEPARATOR);
if (entryParts.length != 3 || entryParts[0].isEmpty() || entryParts[1].isEmpty()
|| entryParts[2].isEmpty()) {
return null;
}
int value = -1;
int count = -1;
try {
value = Integer.parseInt(entryParts[1]);
count = Integer.parseInt(entryParts[2]);
} catch (NumberFormatException e) {
return null;
}
return new CachedUmaEntry(entryParts[0], value, count);
}

/** Returns a string for partial matching of the prefs entry. */
public static String getStringForPartialMatching(String event, int value) {
return event + SEPARATOR + value + SEPARATOR;
}

public CachedUmaEntry(String event, int value, int count) {
mEvent = event;
mValue = value;
mCount = count;
}

/** Converts cached UMA entry to a string in format: EVENT:VALUE:COUNT. */
public String toString() {
return mEvent + SEPARATOR + mValue + SEPARATOR + mCount;
}

/** Gets the name of the event (UMA). */
public String getEvent() {
return mEvent;
}

/** Gets the value of the event (concrete value of the enum). */
public int getValue() {
return mValue;
}

/** Gets the count of events that happened. */
public int getCount() {
return mCount;
}

/** Increments the count of the event. */
public void increment() {
mCount++;
}
}

public static BackgroundTaskSchedulerUma getInstance() {
if (sInstance == null) {
sInstance = new BackgroundTaskSchedulerUma();
}
return sInstance;
}

@VisibleForTesting
public static void setInstanceForTesting(BackgroundTaskSchedulerUma instance) {
sInstance = instance;
}

/** Reports metrics for task scheduling and whether it was successful. */
public void reportTaskScheduled(int taskId, boolean success) {
if (success) {
cacheEvent("Android.BackgroundTaskScheduler.TaskScheduled.Success",
toUmaEnumValueFromTaskId(taskId));
} else {
cacheEvent("Android.BackgroundTaskScheduler.TaskScheduled.Failure",
toUmaEnumValueFromTaskId(taskId));
}
}

/** Reports metrics for task canceling. */
public void reportTaskCanceled(int taskId) {
cacheEvent(
"Android.BackgroundTaskScheduler.TaskCanceled", toUmaEnumValueFromTaskId(taskId));
}

/** Reports metrics for starting a task. */
public void reportTaskStarted(int taskId) {
cacheEvent("Android.BackgroundTaskScheduler.TaskStarted", toUmaEnumValueFromTaskId(taskId));
}

/** Reports metrics for stopping a task. */
public void reportTaskStopped(int taskId) {
cacheEvent("Android.BackgroundTaskScheduler.TaskStopped", toUmaEnumValueFromTaskId(taskId));
}

/** Method that actually invokes histogram recording. Extracted for testing. */
@VisibleForTesting
void recordEnumeratedHistogram(String histogram, int value, int maxCount) {
RecordHistogram.recordEnumeratedHistogram(histogram, value, maxCount);
}

/** Records histograms for cached stats. Should only be called when native is initialized. */
public void flushStats() {
assertNativeIsLoaded();
ThreadUtils.assertOnUiThread();

Set<String> cachedUmaStrings = getCachedUmaEntries(ContextUtils.getAppSharedPreferences());

for (String cachedUmaString : cachedUmaStrings) {
CachedUmaEntry entry = CachedUmaEntry.parseEntry(cachedUmaString);
if (entry == null) continue;
for (int i = 0; i < entry.getCount(); i++) {
recordEnumeratedHistogram(
entry.getEvent(), entry.getValue(), BACKGROUND_TASK_COUNT);
}
}

// Once all metrics are reported, we can simply remove the shared preference key.
removeCachedStats();
}

/** Removes all of the cached stats without reporting. */
public void removeCachedStats() {
ThreadUtils.assertOnUiThread();
ContextUtils.getAppSharedPreferences().edit().remove(KEY_CACHED_UMA).apply();
}

/** Caches the event to be reported through UMA in shared preferences. */
@VisibleForTesting
void cacheEvent(String event, int value) {
SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
Set<String> cachedUmaStrings = getCachedUmaEntries(prefs);
String partialMatch = CachedUmaEntry.getStringForPartialMatching(event, value);

String existingEntry = null;
for (String cachedUmaString : cachedUmaStrings) {
if (cachedUmaString.startsWith(partialMatch)) {
existingEntry = cachedUmaString;
break;
}
}

Set<String> setToWriteBack = new HashSet<>(cachedUmaStrings);
CachedUmaEntry entry = null;
if (existingEntry != null) {
entry = CachedUmaEntry.parseEntry(existingEntry);
if (entry == null) {
entry = new CachedUmaEntry(event, value, 1);
}
setToWriteBack.remove(existingEntry);
entry.increment();
} else {
entry = new CachedUmaEntry(event, value, 1);
}

setToWriteBack.add(entry.toString());
updateCachedUma(prefs, setToWriteBack);
}

@VisibleForTesting
static int toUmaEnumValueFromTaskId(int taskId) {
switch (taskId) {
case TaskIds.TEST:
return BACKGROUND_TASK_TEST;
case TaskIds.OMAHA_JOB_ID:
return BACKGROUND_TASK_OMAHA;
case TaskIds.GCM_BACKGROUND_TASK_JOB_ID:
return BACKGROUND_TASK_GCM;
case TaskIds.NOTIFICATION_SERVICE_JOB_ID:
return BACKGROUND_TASK_NOTIFICATIONS;
case TaskIds.WEBVIEW_MINIDUMP_UPLOADING_JOB_ID:
return BACKGROUND_TASK_WEBVIEW_MINIDUMP;
case TaskIds.CHROME_MINIDUMP_UPLOADING_JOB_ID:
return BACKGROUND_TASK_CHROME_MINIDUMP;
case TaskIds.OFFLINE_PAGES_BACKGROUND_JOB_ID:
return BACKGROUND_TASK_OFFLINE_PAGES;
case TaskIds.OFFLINE_PAGES_PREFETCH_JOB_ID:
return BACKGROUND_TASK_OFFLINE_PREFETCH;
default:
assert false;
}
// Returning a value that is not expected to ever be reported.
return BACKGROUND_TASK_TEST;
}

@VisibleForTesting
static Set<String> getCachedUmaEntries(SharedPreferences prefs) {
return prefs.getStringSet(KEY_CACHED_UMA, new HashSet<String>(1));
}

@VisibleForTesting
static void updateCachedUma(SharedPreferences prefs, Set<String> cachedUma) {
ThreadUtils.assertOnUiThread();
SharedPreferences.Editor editor = prefs.edit();
editor.putStringSet(KEY_CACHED_UMA, cachedUma);
editor.apply();
}

void assertNativeIsLoaded() {
assert LibraryLoader.isInitialized();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
* that there is no overlap of task IDs between different users of the BackgroundTaskScheduler.
*/
public final class TaskIds {
// When adding your job id to the list below, remember to make a corresponding update to the
// BackgroundTaskSchedulerUma#toUmaEnumValueFromTaskId(int) method.
public static final int TEST = 0x00008378;
public static final int OMAHA_JOB_ID = 0x00011684;

Expand Down
Loading

0 comments on commit 3f7a5c7

Please sign in to comment.