// Copyright 2015 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.metrics;
import android.util.Pair;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.library_loader.LibraryLoader;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.metrics.RecordUserAction;
import org.chromium.content_public.browser.WebContents;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* Used for recording metrics about Chrome launches that need to be recorded before the native
* library may have been loaded. Metrics are cached until the library is known to be loaded, then
* committed to the MetricsService all at once.
*/
@JNINamespace("metrics")
public class LaunchMetrics {
/**
* Creating an instance of a subclass of this class automatically adds it to a list of objects
* that are committed when the native library is available.
*/
private abstract static class CachedHistogram {
private static final List<CachedHistogram> sEvents = new ArrayList<CachedHistogram>();
protected final String mHistogramName;
/**
* @param histogramName Name of the histogram to record.
*/
protected CachedHistogram(String histogramName) {
mHistogramName = histogramName;
sEvents.add(this);
}
/** Commits the histogram. Expects the native library to be loaded. */
protected abstract void commitAndClear();
}
/**
* Caches an action that will be recorded after native side is loaded.
*/
public static class ActionEvent extends CachedHistogram {
private int mCount;
public ActionEvent(String actionName) {
super(actionName);
}
public void record() {
if (LibraryLoader.isInitialized()) {
recordWithNative();
} else {
mCount++;
}
}
private void recordWithNative() {
RecordUserAction.record(mHistogramName);
}
@Override
protected void commitAndClear() {
while (mCount > 0) {
recordWithNative();
mCount--;
}
}
}
/** Caches a set of integer histogram samples. */
public static class SparseHistogramSample extends CachedHistogram {
private final List<Integer> mSamples = new ArrayList<Integer>();
public SparseHistogramSample(String histogramName) {
super(histogramName);
}
public void record(int sample) {
if (LibraryLoader.isInitialized()) {
recordWithNative(sample);
} else {
mSamples.add(sample);
}
}
private void recordWithNative(int sample) {
RecordHistogram.recordSparseSlowlyHistogram(mHistogramName, sample);
}
@Override
protected void commitAndClear() {
for (Integer sample : mSamples) {
recordWithNative(sample);
}
mSamples.clear();
}
}
/** Caches a set of enumerated histogram samples. */
public static class EnumeratedHistogramSample extends CachedHistogram {
private final List<Integer> mSamples = new ArrayList<Integer>();
private final int mMaxValue;
public EnumeratedHistogramSample(String histogramName, int maxValue) {
super(histogramName);
mMaxValue = maxValue;
}
public void record(int sample) {
if (LibraryLoader.isInitialized()) {
recordWithNative(sample);
} else {
mSamples.add(sample);
}
}
private void recordWithNative(int sample) {
RecordHistogram.recordEnumeratedHistogram(mHistogramName, sample, mMaxValue);
}
@Override
protected void commitAndClear() {
for (Integer sample : mSamples) {
recordWithNative(sample);
}
mSamples.clear();
}
}
/** Caches a set of times histogram samples. */
public static class TimesHistogramSample extends CachedHistogram {
private final List<Long> mSamples = new ArrayList<Long>();
private final TimeUnit mTimeUnit;
public TimesHistogramSample(String histogramName, TimeUnit timeUnit) {
super(histogramName);
mTimeUnit = timeUnit;
}
public void record(long sample) {
if (LibraryLoader.isInitialized()) {
recordWithNative(sample);
} else {
mSamples.add(sample);
}
}
private void recordWithNative(long sample) {
RecordHistogram.recordTimesHistogram(mHistogramName, sample, mTimeUnit);
}
@Override
protected void commitAndClear() {
for (Long sample : mSamples) {
recordWithNative(sample);
}
mSamples.clear();
}
}
// Each list item is a pair of the url and where it was added from e.g. from the add to
// homescreen menu item, an app banner, or unknown. The mapping of int source values to
// their string names is found in the C++ ShortcutInfo struct.
private static final List<Pair<String, Integer>> sActivityUrls =
new ArrayList<Pair<String, Integer>>();
private static final List<Pair<String, Integer>> sTabUrls =
new ArrayList<Pair<String, Integer>>();
private static final List<Long> sWebappHistogramTimes = new ArrayList<Long>();
/**
* Records the launch of a standalone Activity for a URL (i.e. a WebappActivity)
* added from a specific source.
* @param url URL that kicked off the Activity's creation.
* @param source integer id of the source from where the URL was added.
*/
public static void recordHomeScreenLaunchIntoStandaloneActivity(String url, int source) {
sActivityUrls.add(new Pair<String, Integer>(url, source));
}
/**
* Records the launch of a Tab for a URL (i.e. a Home screen shortcut).
* @param url URL that kicked off the Tab's creation.
* @param source integer id of the source from where the URL was added.
*/
public static void recordHomeScreenLaunchIntoTab(String url, int source) {
sTabUrls.add(new Pair<String, Integer>(url, source));
}
/**
* Records the time it took to look up from disk whether a MAC is valid during webapp startup.
* @param time the number of milliseconds it took to finish.
*/
public static void recordWebappHistogramTimes(long time) {
sWebappHistogramTimes.add(time);
}
/**
* Calls out to native code to record URLs that have been launched via the Home screen.
* This intermediate step is necessary because Activity.onCreate() may be called when
* the native library has not yet been loaded.
* @param webContents WebContents for the current Tab.
*/
public static void commitLaunchMetrics(WebContents webContents) {
for (Pair<String, Integer> item : sActivityUrls) {
nativeRecordLaunch(true, item.first, item.second, webContents);
}
sActivityUrls.clear();
for (Pair<String, Integer> item : sTabUrls) {
nativeRecordLaunch(false, item.first, item.second, webContents);
}
sTabUrls.clear();
// Record generic cached events.
for (CachedHistogram event : CachedHistogram.sEvents) event.commitAndClear();
}
/**
* Records metrics about the state of the homepage on launch.
* @param showHomeButton Whether the home button is shown.
* @param homepageIsNtp Whether the homepage is set to the NTP.
* @param homepageUrl The value of the homepage URL.
*/
public static void recordHomePageLaunchMetrics(
boolean showHomeButton, boolean homepageIsNtp, String homepageUrl) {
if (homepageUrl == null) {
homepageUrl = "";
assert !showHomeButton : "Homepage should be disabled for a null URL";
}
nativeRecordHomePageLaunchMetrics(showHomeButton, homepageIsNtp, homepageUrl);
}
private static native void nativeRecordLaunch(
boolean standalone, String url, int source, WebContents webContents);
private static native void nativeRecordHomePageLaunchMetrics(
boolean showHomeButton, boolean homepageIsNtp, String homepageUrl);
}