// Copyright 2016 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.precache;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import org.chromium.base.ContextUtils;
import org.chromium.base.VisibleForTesting;
import org.chromium.base.library_loader.LibraryLoader;
import org.chromium.base.metrics.RecordHistogram;
import java.util.Arrays;
/**
* Enumerates the various failure reasons and events of interest for precaching. When the library is
* loaded, the events are logged as UMA metrics. Otherwise the events are persisted in shared
* preferences until the library loads in future. When the library is not loaded, only single
* occurrence of an event is recorded, duplicate occurrence of the same event is not persisted.
*/
public class PrecacheUMA {
/**
* The events should not be renumbered or reused since these are used in a histogram.
* This must remain in sync with Precache.Events in tools/metrics/histograms/histograms.xml.
*/
public static class Event {
/**
* Indicates that the precache scheduled task has started. The task can be periodic or
* one-off completion task.
*/
public static final int PRECACHE_TASK_STARTED_PERIODIC = 0;
public static final int PRECACHE_TASK_STARTED_ONEOFF = 1;
// Duplicate GCM task was started while precache was running.
public static final int PRECACHE_TASK_STARTED_DUPLICATE = 2;
/**
* The native library failed to load, during the run of a precache scheduled task.
*/
public static final int PRECACHE_TASK_LOAD_LIBRARY_FAIL = 3;
/**
* Various failure reasons due to which precache task was cancelled.
*/
public static final int PRECACHE_CANCEL_NO_UNMETERED_NETWORK = 4;
public static final int PRECACHE_CANCEL_NO_POWER = 5;
public static final int PRECACHE_CANCEL_DISABLED_PREF = 6;
public static final int DISABLED_IN_PRECACHE_PREF = 7;
public static final int SYNC_SERVICE_TIMEOUT = 8;
public static final int PRECACHE_SESSION_TIMEOUT = 9;
/**
* Precache session started.
*/
public static final int PRECACHE_SESSION_STARTED = 10;
/**
* Precache task was scheduled. The task can be periodic or one-off completion task. The
* result of scheduling can be success or failure. The periodic task can be scheduled due to
* Chrome upgrade or at startup.
*/
public static final int PERIODIC_TASK_SCHEDULE_STARTUP = 11;
public static final int PERIODIC_TASK_SCHEDULE_STARTUP_FAIL = 12;
public static final int PERIODIC_TASK_SCHEDULE_UPGRADE = 13;
public static final int PERIODIC_TASK_SCHEDULE_UPGRADE_FAIL = 14;
public static final int ONEOFF_TASK_SCHEDULE = 15;
public static final int ONEOFF_TASK_SCHEDULE_FAIL = 16;
/**
* Precache session completed successfully or unsuccessfully.
*/
public static final int PRECACHE_SESSION_COMPLETE = 17;
public static final int PRECACHE_SESSION_INCOMPLETE = 18;
/**
* Limit of the events.
*/
public static final int EVENT_START = 0;
public static final int EVENT_END = 19;
@VisibleForTesting
static int getBitPosition(int event) {
assert (event >= EVENT_START) && (event < EVENT_END);
return event;
}
@VisibleForTesting
static long getBitMask(int event) {
assert (event >= EVENT_START) && (event < EVENT_END);
return 1L << event;
}
@VisibleForTesting
static int[] getEventsFromBitMask(long bitmask) {
int[] events = new int[EVENT_END];
int filledEvents = 0;
for (int event = EVENT_START; event < EVENT_END; ++event) {
if ((getBitMask(event) & bitmask) != 0L) {
events[filledEvents] = event;
++filledEvents;
}
}
return Arrays.copyOf(events, filledEvents);
}
@VisibleForTesting
static long addEventToBitMask(long bitmask, int event) {
return bitmask | getBitMask(event);
}
}
static final String PREF_PERSISTENCE_METRICS = "precache.persistent_metrics";
static final String EVENTS_HISTOGRAM = "Precache.Events";
/**
* Record the precache event. The event is persisted in shared preferences if the native library
* is not loaded. If library is loaded, the event will be recorded as UMA metric, and any prior
* persisted events are recorded to UMA as well.
* @param event the precache event.
*/
public static void record(int event) {
SharedPreferences sharedPreferences = ContextUtils.getAppSharedPreferences();
long persistent_metric = sharedPreferences.getLong(PREF_PERSISTENCE_METRICS, 0);
Editor preferencesEditor = sharedPreferences.edit();
if (LibraryLoader.isInitialized()) {
RecordHistogram.recordEnumeratedHistogram(
EVENTS_HISTOGRAM, Event.getBitPosition(event), Event.EVENT_END);
for (int e : Event.getEventsFromBitMask(persistent_metric)) {
RecordHistogram.recordEnumeratedHistogram(
EVENTS_HISTOGRAM, Event.getBitPosition(e), Event.EVENT_END);
}
preferencesEditor.remove(PREF_PERSISTENCE_METRICS);
} else {
// Save the metric in preferences.
persistent_metric = Event.addEventToBitMask(persistent_metric, event);
preferencesEditor.putLong(PREF_PERSISTENCE_METRICS, persistent_metric);
}
preferencesEditor.apply();
}
}