Commit 027fd170 authored by Charlie Ma's avatar Charlie Ma Committed by Commit Bot

Add histogram for Chrome mobile daily use count.

The limit of this implementation is unintentional launch, e.g user click
some notification and go to external activity and then go back, it will
also be counted. Solution is create a histogram of launch type, if majority
if launches come from hitting Chrome icon, then the noise have less impact
to the accuracy of daily launch count histogram.

Bug: 806240
Change-Id: Iec367758267bba806b4ed2c34c9e030e0ec65909
Reviewed-on: https://chromium-review.googlesource.com/1103838Reviewed-by: default avatarTed Choc <tedchoc@chromium.org>
Reviewed-by: default avatarGayane Petrosyan <gayane@chromium.org>
Commit-Queue: Charlie Ma <charliema@chromium.org>
Cr-Commit-Position: refs/heads/master@{#576108}
parent 8ed544d2
......@@ -736,6 +736,7 @@ public class ChromeTabbedActivity
@Override
public void onStartWithNative() {
mMainIntentMetrics.logLaunchBehavior();
super.onStartWithNative();
setInitialOverviewState();
......
......@@ -5,11 +5,16 @@
package org.chromium.chrome.browser.metrics;
import android.app.Activity;
import android.content.SharedPreferences;
import android.os.Handler;
import android.support.annotation.IntDef;
import org.chromium.base.ActivityState;
import org.chromium.base.ApplicationState;
import org.chromium.base.ApplicationStatus;
import org.chromium.base.ContextUtils;
import org.chromium.base.ThreadUtils;
import org.chromium.base.VisibleForTesting;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.metrics.RecordUserAction;
import org.chromium.chrome.browser.ChromeActivity;
......@@ -53,10 +58,29 @@ public class MainIntentBehaviorMetrics implements ApplicationStatus.ActivityStat
private static final int DURATION_HISTOGRAM_BUCKET_COUNT = 50;
private static long sTimeoutDurationMs = TIMEOUT_DURATION_MS;
private static boolean sLoggedLaunchBehavior;
static {
ApplicationStatus.registerApplicationStateListener(newState -> {
if (newState == ApplicationState.HAS_STOPPED_ACTIVITIES) {
sLoggedLaunchBehavior = false;
}
});
}
@VisibleForTesting
static final String LAUNCH_TIMESTAMP_PREF = "MainIntent.LaunchTimestamp";
@VisibleForTesting
static final String LAUNCH_COUNT_PREF = "MainIntent.LaunchCount";
// Constants used to log UMA "enum" histogram about launch type.
private static final int LAUNCH_FROM_ICON = 0;
private static final int LAUNCH_NOT_FROM_ICON = 1;
private static final int LAUNCH_BOUNDARY = 2;
private final ChromeActivity mActivity;
private final Handler mHandler;
private final Runnable mTimeoutRunnable;
private final Runnable mLogLaunchRunnable;
private boolean mPendingActionRecordForMainIntent;
private long mBackgroundDurationMs;
......@@ -78,6 +102,7 @@ public class MainIntentBehaviorMetrics implements ApplicationStatus.ActivityStat
recordUserBehavior(MainIntentActionType.CONTINUATION);
}
};
mLogLaunchRunnable = () -> logLaunchBehavior(false);
}
/**
......@@ -121,6 +146,8 @@ public class MainIntentBehaviorMetrics implements ApplicationStatus.ActivityStat
recordUserBehavior(MainIntentActionType.SWITCH_TABS);
}
};
logLaunchBehavior(true);
}
/**
......@@ -169,6 +196,15 @@ public class MainIntentBehaviorMetrics implements ApplicationStatus.ActivityStat
return mPendingActionRecordForMainIntent;
}
/**
* Log how many times user intentionally (from launcher or recents) launch Chrome per day,
* and the type of each launch.
*/
public void logLaunchBehavior() {
if (sLoggedLaunchBehavior) return;
ThreadUtils.getUiThreadHandler().postDelayed(mLogLaunchRunnable, sTimeoutDurationMs);
}
private String getHistogramNameForBehavior(@MainIntentActionType int behavior) {
switch (behavior) {
case MainIntentActionType.CONTINUATION:
......@@ -210,4 +246,31 @@ public class MainIntentBehaviorMetrics implements ApplicationStatus.ActivityStat
mTabModelObserver.destroy();
mTabModelObserver = null;
}
private void logLaunchBehavior(boolean isLaunchFromIcon) {
if (sLoggedLaunchBehavior) return;
sLoggedLaunchBehavior = true;
SharedPreferences pref = ContextUtils.getAppSharedPreferences();
SharedPreferences.Editor editor = pref.edit();
long current = System.currentTimeMillis();
long timestamp = pref.getLong(LAUNCH_TIMESTAMP_PREF, 0);
int count = pref.getInt(LAUNCH_COUNT_PREF, 0);
if (current - timestamp > BACKGROUND_TIME_24_HOUR_MS) {
// Log count if it's not first launch of Chrome.
if (timestamp != 0) {
RecordHistogram.recordCountHistogram("MobileStartup.DailyLaunchCount", count);
}
count = 0;
editor.putLong(LAUNCH_TIMESTAMP_PREF, current);
}
count++;
editor.putInt(LAUNCH_COUNT_PREF, count).apply();
RecordHistogram.recordEnumeratedHistogram("MobileStartup.LaunchType",
isLaunchFromIcon ? LAUNCH_FROM_ICON : LAUNCH_NOT_FROM_ICON, LAUNCH_BOUNDARY);
ThreadUtils.getUiThreadHandler().removeCallbacks(mLogLaunchRunnable);
}
}
\ No newline at end of file
......@@ -4,6 +4,8 @@
package org.chromium.chrome.browser.metrics;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertThat;
import android.annotation.SuppressLint;
......@@ -21,16 +23,23 @@ import org.junit.runner.RunWith;
import org.chromium.base.ContextUtils;
import org.chromium.base.ThreadUtils;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.UserActionTester;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.ChromeSwitches;
import org.chromium.chrome.browser.ChromeTabbedActivity;
import org.chromium.chrome.browser.bookmarks.BookmarkActivity;
import org.chromium.chrome.browser.download.DownloadActivity;
import org.chromium.chrome.browser.history.HistoryActivity;
import org.chromium.chrome.browser.omnibox.UrlBar;
import org.chromium.chrome.browser.preferences.Preferences;
import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType;
import org.chromium.chrome.browser.tabmodel.TabModelUtils;
import org.chromium.chrome.test.ChromeActivityTestRule;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.util.ActivityUtils;
import org.chromium.chrome.test.util.MenuUtils;
import org.chromium.chrome.test.util.OmniboxTestUtils;
import org.chromium.content.browser.test.util.Criteria;
import org.chromium.content.browser.test.util.CriteriaHelper;
......@@ -192,6 +201,110 @@ public class MainIntentBehaviorMetricsIntegrationTest {
}
}
@MediumTest
@Test
public void testLaunch_Duration_MoreThan_1Day() throws Exception {
long timestamp = System.currentTimeMillis() - 25 * HOURS_IN_MS;
ContextUtils.getAppSharedPreferences()
.edit()
.putLong(MainIntentBehaviorMetrics.LAUNCH_TIMESTAMP_PREF, timestamp)
.commit();
ContextUtils.getAppSharedPreferences()
.edit()
.putInt(MainIntentBehaviorMetrics.LAUNCH_COUNT_PREF, 10)
.commit();
mActivityTestRule.startMainActivityFromLauncher();
assertEquals(1,
RecordHistogram.getHistogramValueCountForTesting(
"MobileStartup.DailyLaunchCount", 10));
assertEquals(1,
ContextUtils.getAppSharedPreferences().getInt(
MainIntentBehaviorMetrics.LAUNCH_COUNT_PREF, 0));
long newTimestamp = ContextUtils.getAppSharedPreferences().getLong(
MainIntentBehaviorMetrics.LAUNCH_TIMESTAMP_PREF, 0);
assertNotEquals(timestamp, newTimestamp);
assertNotEquals(0, newTimestamp);
}
@MediumTest
@Test
public void testLaunch_Duration_LessThan_1Day() throws Exception {
long timestamp = System.currentTimeMillis() - 12 * HOURS_IN_MS;
ContextUtils.getAppSharedPreferences()
.edit()
.putLong(MainIntentBehaviorMetrics.LAUNCH_TIMESTAMP_PREF, timestamp)
.commit();
ContextUtils.getAppSharedPreferences()
.edit()
.putInt(MainIntentBehaviorMetrics.LAUNCH_COUNT_PREF, 1)
.commit();
mActivityTestRule.startMainActivityFromLauncher();
assertEquals(0,
RecordHistogram.getHistogramValueCountForTesting(
"MobileStartup.DailyLaunchCount", 1));
assertEquals(2,
ContextUtils.getAppSharedPreferences().getInt(
MainIntentBehaviorMetrics.LAUNCH_COUNT_PREF, 0));
assertEquals(timestamp,
ContextUtils.getAppSharedPreferences().getLong(
MainIntentBehaviorMetrics.LAUNCH_TIMESTAMP_PREF, 0));
}
@MediumTest
@Test
public void testLaunch_From_InAppActivities() throws Exception {
try {
MainIntentBehaviorMetrics.setTimeoutDurationMsForTesting(0);
long timestamp = System.currentTimeMillis() - 12 * HOURS_IN_MS;
ContextUtils.getAppSharedPreferences()
.edit()
.putLong(MainIntentBehaviorMetrics.LAUNCH_TIMESTAMP_PREF, timestamp)
.commit();
mActivityTestRule.startMainActivityFromLauncher();
Preferences preferences = mActivityTestRule.startPreferences(null);
preferences.finish();
ChromeActivityTestRule.waitForActivityNativeInitializationComplete(
ChromeActivityTestRule.waitFor(ChromeTabbedActivity.class));
BookmarkActivity bookmarkActivity = ActivityUtils.waitForActivity(
InstrumentationRegistry.getInstrumentation(), BookmarkActivity.class,
new MenuUtils.MenuActivityTrigger(InstrumentationRegistry.getInstrumentation(),
mActivityTestRule.getActivity(), R.id.all_bookmarks_menu_id));
bookmarkActivity.finish();
ChromeActivityTestRule.waitForActivityNativeInitializationComplete(
ChromeActivityTestRule.waitFor(ChromeTabbedActivity.class));
DownloadActivity downloadActivity = ActivityUtils.waitForActivity(
InstrumentationRegistry.getInstrumentation(), DownloadActivity.class,
new MenuUtils.MenuActivityTrigger(InstrumentationRegistry.getInstrumentation(),
mActivityTestRule.getActivity(), R.id.downloads_menu_id));
downloadActivity.finish();
ChromeActivityTestRule.waitForActivityNativeInitializationComplete(
ChromeActivityTestRule.waitFor(ChromeTabbedActivity.class));
HistoryActivity historyActivity = ActivityUtils.waitForActivity(
InstrumentationRegistry.getInstrumentation(), HistoryActivity.class,
new MenuUtils.MenuActivityTrigger(InstrumentationRegistry.getInstrumentation(),
mActivityTestRule.getActivity(), R.id.open_history_menu_id));
historyActivity.finish();
assertEquals(1,
ContextUtils.getAppSharedPreferences().getInt(
MainIntentBehaviorMetrics.LAUNCH_COUNT_PREF, 0));
} finally {
MainIntentBehaviorMetrics.setTimeoutDurationMsForTesting(
MainIntentBehaviorMetrics.TIMEOUT_DURATION_MS);
}
}
private void assertBackgroundDurationLogged(long duration, String expectedMetric) {
startActivity(false);
mActionTester = new UserActionTester();
......
......@@ -26787,6 +26787,11 @@ Called by update_gpu_driver_bug_workaround_entries.py.-->
</int>
</enum>
<enum name="LaunchType">
<int value="0" label="From icon"/>
<int value="1" label="Not from icon"/>
</enum>
<enum name="LazyCSSParseUsage">
<int value="0" label="&gt;= 0%"/>
<int value="1" label="&gt; 10%"/>
......@@ -45480,6 +45480,11 @@ uploading your change for review.
</summary>
</histogram>
<histogram name="MobileStartup.DailyLaunchCount">
<owner>charliema@chromium.org</owner>
<summary>The count of launching Chrome mobile app within a day.</summary>
</histogram>
<histogram name="MobileStartup.IntentToCreationTime" units="ms">
<owner>twellington@chromium.org</owner>
<summary>
......@@ -45489,6 +45494,13 @@ uploading your change for review.
</summary>
</histogram>
<histogram name="MobileStartup.LaunchType" enum="LaunchType">
<owner>charliema@chromium.org</owner>
<summary>
The type of launching Chrome mobile app, e.g launch by hitting icon.
</summary>
</histogram>
<histogram name="MobileStartup.LoadedHomepageOnColdStart" enum="BooleanIsNtp"
expires_after="M72">
<owner>tedchoc@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