Commit b71e1fa9 authored by yfriedman's avatar yfriedman Committed by Commit bot

Upstream UmaSessionStats.

UmaSessionStats is primarily for tracking metrics pertaining to the
length of a user-session in chrome (from start/resume to pause) but
also tracks some usage during the session.

BUG=428903

Review URL: https://codereview.chromium.org/962293002

Cr-Commit-Position: refs/heads/master@{#318700}
parent c2a1883b
// 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.app.Activity;
import android.content.ComponentCallbacks;
import android.content.Context;
import android.content.res.Configuration;
import org.chromium.base.ActivityState;
import org.chromium.base.ApplicationStatus;
import org.chromium.chrome.browser.Tab;
import org.chromium.chrome.browser.preferences.privacy.CrashReportingPermissionManager;
import org.chromium.chrome.browser.preferences.privacy.PrivacyPreferencesManager;
import org.chromium.chrome.browser.tabmodel.TabModel;
import org.chromium.chrome.browser.tabmodel.TabModelSelector;
import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabObserver;
import org.chromium.content_public.browser.WebContents;
import org.chromium.net.NetworkChangeNotifier;
/**
* Mainly sets up session stats for chrome. A session is defined as the duration when the
* application is in the foreground. Also used to communicate information between Chrome
* and the framework's MetricService.
*/
public class UmaSessionStats implements NetworkChangeNotifier.ConnectionTypeObserver {
private static final String SAMSUNG_MULTWINDOW_PACKAGE = "com.sec.feature.multiwindow";
private static long sNativeUmaSessionStats = 0;
// TabModelSelector is needed to get the count of open tabs. We want to log the number of open
// tabs on every page load.
private TabModelSelector mTabModelSelector;
private TabModelSelectorTabObserver mTabModelSelectorTabObserver;
private final Context mContext;
private final boolean mIsMultiWindowCapable;
private ComponentCallbacks mComponentCallbacks;
private boolean mKeyboardConnected = false;
private final CrashReportingPermissionManager mReportingPermissionManager;
public UmaSessionStats(Context context) {
mContext = context;
mIsMultiWindowCapable = context.getPackageManager().hasSystemFeature(
SAMSUNG_MULTWINDOW_PACKAGE);
mReportingPermissionManager = PrivacyPreferencesManager.getInstance(context);
}
private void recordPageLoadStats(int tabId) {
Tab tab = mTabModelSelector.getTabById(tabId);
if (tab == null) return;
WebContents webContents = tab.getWebContents();
boolean isDesktopUserAgent = webContents != null
&& webContents.getNavigationController().getUseDesktopUserAgent();
nativeRecordPageLoaded(isDesktopUserAgent);
if (mKeyboardConnected) {
nativeRecordPageLoadedWithKeyboard();
}
TabModel regularModel = mTabModelSelector.getModel(false);
nativeRecordTabCountPerLoad(getTabCountFromModel(regularModel));
}
private int getTabCountFromModel(TabModel model) {
return model == null ? 0 : model.getCount();
}
/**
* Starts a new session for logging.
* @param tabModelSelector A TabModelSelector instance for recording tab counts on page loads.
* If null, UmaSessionStats does not record page loads and tab counts.
*/
public void startNewSession(TabModelSelector tabModelSelector) {
ensureNativeInitialized();
mTabModelSelector = tabModelSelector;
if (mTabModelSelector != null) {
mComponentCallbacks = new ComponentCallbacks() {
@Override
public void onLowMemory() {
// Not required
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
mKeyboardConnected = newConfig.keyboard != Configuration.KEYBOARD_NOKEYS;
}
};
mContext.registerComponentCallbacks(mComponentCallbacks);
mKeyboardConnected = mContext.getResources().getConfiguration()
.keyboard != Configuration.KEYBOARD_NOKEYS;
mTabModelSelectorTabObserver = new TabModelSelectorTabObserver(mTabModelSelector) {
@Override
public void onPageLoadFinished(Tab tab) {
recordPageLoadStats(tab.getId());
}
};
}
nativeUmaResumeSession(sNativeUmaSessionStats);
NetworkChangeNotifier.addConnectionTypeObserver(this);
updateMetricsServiceState();
}
private static void ensureNativeInitialized() {
// Lazily create the native object and the notification handler. These objects are never
// destroyed.
if (sNativeUmaSessionStats == 0) {
sNativeUmaSessionStats = nativeInit();
}
}
/**
* Logs screen ratio on Samsung MultiWindow devices.
*/
public void logMultiWindowStats(int windowArea, int displayArea, int instanceCount) {
if (mIsMultiWindowCapable) {
if (displayArea == 0) return;
int areaPercent = (windowArea * 100) / displayArea;
int safePercent = areaPercent > 0 ? areaPercent : 0;
nativeRecordMultiWindowSession(safePercent, instanceCount);
}
}
/**
* Logs the current session.
*/
public void logAndEndSession() {
if (mTabModelSelector != null) {
mContext.unregisterComponentCallbacks(mComponentCallbacks);
mTabModelSelectorTabObserver.destroy();
mTabModelSelector = null;
}
nativeUmaEndSession(sNativeUmaSessionStats);
NetworkChangeNotifier.removeConnectionTypeObserver(this);
}
public static void logRendererCrash(Activity activity) {
int activityState = ApplicationStatus.getStateForActivity(activity);
nativeLogRendererCrash(
activityState == ActivityState.PAUSED
|| activityState == ActivityState.STOPPED
|| activityState == ActivityState.DESTROYED);
}
/**
* Updates the state of the MetricsService to account for the user's preferences.
*/
public void updateMetricsServiceState() {
boolean mayRecordStats = !PrivacyPreferencesManager.getInstance(mContext)
.isNeverUploadCrashDump();
boolean mayUploadStats = mReportingPermissionManager.isUploadPermitted();
// Re-start the MetricsService with the given parameters.
nativeUpdateMetricsServiceState(mayRecordStats, mayUploadStats);
}
@Override
public void onConnectionTypeChanged(int connectionType) {
updateMetricsServiceState();
}
public static void registerExternalExperiment(int studyId, int experimentId) {
nativeRegisterExternalExperiment(studyId, experimentId);
}
public static void registerSyntheticFieldTrial(String trialName, String groupName) {
nativeRegisterSyntheticFieldTrial(trialName, groupName);
}
private static native long nativeInit();
private native void nativeUpdateMetricsServiceState(boolean mayRecord, boolean mayUpload);
private native void nativeUmaResumeSession(long nativeUmaSessionStats);
private native void nativeUmaEndSession(long nativeUmaSessionStats);
private static native void nativeLogRendererCrash(boolean isPaused);
private static native void nativeRegisterExternalExperiment(int studyId,
int experimentId);
private static native void nativeRegisterSyntheticFieldTrial(
String trialName, String groupName);
private static native void nativeRecordMultiWindowSession(int areaPercent, int instanceCount);
private static native void nativeRecordTabCountPerLoad(int numTabsOpen);
private static native void nativeRecordPageLoaded(boolean isDesktopUserAgent);
private static native void nativeRecordPageLoadedWithKeyboard();
}
......@@ -32,6 +32,7 @@
#include "chrome/browser/android/intent_helper.h"
#include "chrome/browser/android/location_settings_impl.h"
#include "chrome/browser/android/logo_bridge.h"
#include "chrome/browser/android/metrics/uma_session_stats.h"
#include "chrome/browser/android/most_visited_sites.h"
#include "chrome/browser/android/new_tab_page_prefs.h"
#include "chrome/browser/android/omnibox/answers_image_bridge.h"
......@@ -229,6 +230,7 @@ static base::android::RegistrationMethod kChromeRegisteredMethods[] = {
{"TranslateInfoBarDelegate", RegisterTranslateInfoBarDelegate},
{"TtsPlatformImpl", TtsPlatformImplAndroid::Register},
{"UmaBridge", RegisterUmaBridge},
{"UmaSessionStats", RegisterUmaSessionStats},
{"UrlUtilities", RegisterUrlUtilities},
{"Variations", variations::android::RegisterVariations},
{"VoiceSearchTabHelper", RegisterVoiceSearchTabHelper},
......
asvitkine@chromium.org
dfalcantara@chromium.org
mariakhomenko@chromium.org
// 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.
#include "chrome/browser/android/metrics/uma_session_stats.h"
#include "base/android/jni_string.h"
#include "base/command_line.h"
#include "base/metrics/histogram.h"
#include "base/prefs/pref_service.h"
#include "base/strings/string_number_conversions.h"
#include "base/time/time.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
#include "chrome/browser/metrics/metrics_services_manager.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/installer/util/google_update_settings.h"
#include "components/metrics/metrics_service.h"
#include "components/variations/metrics_util.h"
#include "components/variations/variations_associated_data.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/user_metrics.h"
#include "jni/UmaSessionStats_jni.h"
using base::android::ConvertJavaStringToUTF8;
using base::UserMetricsAction;
namespace {
UmaSessionStats* g_uma_session_stats = NULL;
} // namespace
UmaSessionStats::UmaSessionStats()
: active_session_count_(0) {
}
UmaSessionStats::~UmaSessionStats() {
}
void UmaSessionStats::UmaResumeSession(JNIEnv* env, jobject obj) {
DCHECK(g_browser_process);
if (active_session_count_ == 0) {
session_start_time_ = base::TimeTicks::Now();
// Tell the metrics service that the application resumes.
metrics::MetricsService* metrics = g_browser_process->metrics_service();
if (metrics) {
metrics->OnAppEnterForeground();
}
}
++active_session_count_;
}
void UmaSessionStats::UmaEndSession(JNIEnv* env, jobject obj) {
--active_session_count_;
DCHECK(active_session_count_ >= 0);
if (active_session_count_ == 0) {
base::TimeDelta duration = base::TimeTicks::Now() - session_start_time_;
UMA_HISTOGRAM_LONG_TIMES("Session.TotalDuration", duration);
DCHECK(g_browser_process);
// Tell the metrics service it was cleanly shutdown.
metrics::MetricsService* metrics = g_browser_process->metrics_service();
if (metrics) {
metrics->OnAppEnterBackground();
}
}
}
// static
void UmaSessionStats::RegisterSyntheticFieldTrialWithNameHash(
uint32_t trial_name_hash,
const std::string& group_name) {
ChromeMetricsServiceAccessor::RegisterSyntheticFieldTrialWithNameHash(
trial_name_hash, group_name);
}
// static
void UmaSessionStats::RegisterSyntheticFieldTrial(
const std::string& trial_name,
const std::string& group_name) {
ChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial(trial_name,
group_name);
}
// Starts/stops the MetricsService when permissions have changed.
// There are three possible states:
// * Logs are being recorded and being uploaded to the server.
// * Logs are being recorded, but not being uploaded to the server.
// This happens when we've got permission to upload on Wi-Fi but we're on a
// mobile connection (for example).
// * Logs are neither being recorded or uploaded.
static void UpdateMetricsServiceState(JNIEnv* env, jobject obj,
jboolean may_record, jboolean may_upload) {
metrics::MetricsService* metrics = g_browser_process->metrics_service();
DCHECK(metrics);
if (metrics->recording_active() != may_record) {
// This function puts a consent file with the ClientID in the
// data directory. The ID is passed to the renderer for crash
// reporting when things go wrong.
content::BrowserThread::GetBlockingPool()->PostTask(FROM_HERE,
base::Bind(
base::IgnoreResult(GoogleUpdateSettings::SetCollectStatsConsent),
may_record));
}
g_browser_process->GetMetricsServicesManager()->UpdatePermissions(
may_record, may_upload);
}
// Renderer process crashed in the foreground.
static void LogRendererCrash(JNIEnv* env, jclass clazz, jboolean is_paused) {
DCHECK(g_browser_process);
if (!is_paused) {
// Increment the renderer crash count in stability metrics.
PrefService* pref = g_browser_process->local_state();
DCHECK(pref);
int value = pref->GetInteger(prefs::kStabilityRendererCrashCount);
pref->SetInteger(prefs::kStabilityRendererCrashCount, value + 1);
}
// Note: When we are paused, any UI metric we increment may not make it to
// the disk before we are killed. Treat the count below as a lower bound.
content::RecordAction(base::UserMetricsAction("MobileRendererCrashed"));
}
static void RegisterExternalExperiment(JNIEnv* env,
jclass clazz,
jint study_id,
jint experiment_id) {
const std::string group_name_utf8 = base::IntToString(experiment_id);
variations::ActiveGroupId active_group;
active_group.name = static_cast<uint32>(study_id);
active_group.group = metrics::HashName(group_name_utf8);
variations::AssociateGoogleVariationIDForceHashes(
variations::GOOGLE_WEB_PROPERTIES, active_group,
static_cast<variations::VariationID>(experiment_id));
UmaSessionStats::RegisterSyntheticFieldTrialWithNameHash(
static_cast<uint32_t>(study_id), group_name_utf8);
}
static void RegisterSyntheticFieldTrial(JNIEnv* env,
jclass clazz,
jstring jtrial_name,
jstring jgroup_name) {
std::string trial_name(ConvertJavaStringToUTF8(env, jtrial_name));
std::string group_name(ConvertJavaStringToUTF8(env, jgroup_name));
UmaSessionStats::RegisterSyntheticFieldTrial(trial_name, group_name);
}
static void RecordMultiWindowSession(JNIEnv*, jclass,
jint area_percent,
jint instance_count) {
UMA_HISTOGRAM_PERCENTAGE("MobileStartup.MobileMultiWindowSession",
area_percent);
// Make sure the bucket count is the same as the range. This currently
// expects no more than 10 simultaneous multi window instances.
UMA_HISTOGRAM_CUSTOM_COUNTS("MobileStartup.MobileMultiWindowInstances",
instance_count,
1 /* min */,
10 /* max */,
10 /* bucket count */);
}
static void RecordTabCountPerLoad(JNIEnv*, jclass, jint num_tabs) {
// Record how many tabs total are open.
UMA_HISTOGRAM_CUSTOM_COUNTS("Tabs.TabCountPerLoad", num_tabs, 1, 200, 50);
}
static void RecordPageLoaded(JNIEnv*, jclass, jboolean is_desktop_user_agent) {
// Should be called whenever a page has been loaded.
content::RecordAction(UserMetricsAction("MobilePageLoaded"));
if (is_desktop_user_agent) {
content::RecordAction(
UserMetricsAction("MobilePageLoadedDesktopUserAgent"));
}
}
static void RecordPageLoadedWithKeyboard(JNIEnv*, jclass) {
content::RecordAction(UserMetricsAction("MobilePageLoadedWithKeyboard"));
}
static jlong Init(JNIEnv* env, jclass obj) {
// We should have only one UmaSessionStats instance.
DCHECK(!g_uma_session_stats);
g_uma_session_stats = new UmaSessionStats();
return reinterpret_cast<intptr_t>(g_uma_session_stats);
}
// Register native methods
bool RegisterUmaSessionStats(JNIEnv* env) {
return RegisterNativesImpl(env);
}
// 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.
#ifndef CHROME_BROWSER_ANDROID_METRICS_UMA_SESSION_STATS_H_
#define CHROME_BROWSER_ANDROID_METRICS_UMA_SESSION_STATS_H_
#include <jni.h>
#include <string>
#include "base/memory/scoped_ptr.h"
#include "base/time/time.h"
class UserActionRateCounter;
// The native part of java UmaSessionStats class.
class UmaSessionStats {
public:
UmaSessionStats();
void UmaResumeSession(JNIEnv* env, jobject obj);
void UmaEndSession(JNIEnv* env, jobject obj);
static void RegisterSyntheticFieldTrialWithNameHash(
uint32_t trial_name_hash,
const std::string& group_name);
static void RegisterSyntheticFieldTrial(
const std::string& trial_name,
const std::string& group_name);
private:
~UmaSessionStats();
// Start of the current session, used for UMA.
base::TimeTicks session_start_time_;
int active_session_count_;
DISALLOW_COPY_AND_ASSIGN(UmaSessionStats);
};
// Registers the native methods through jni
bool RegisterUmaSessionStats(JNIEnv* env);
#endif // CHROME_BROWSER_ANDROID_METRICS_UMA_SESSION_STATS_H_
......@@ -101,6 +101,8 @@
'browser/android/logo_service.h',
'browser/android/manifest_icon_selector.cc',
'browser/android/manifest_icon_selector.h',
'browser/android/metrics/uma_session_stats.cc',
'browser/android/metrics/uma_session_stats.h',
'browser/android/most_visited_sites.cc',
'browser/android/most_visited_sites.h',
'browser/android/new_tab_page_prefs.cc',
......@@ -1599,6 +1601,7 @@
'android/java/src/org/chromium/chrome/browser/NewTabPagePrefs.java',
'android/java/src/org/chromium/chrome/browser/IntentHelper.java',
'android/java/src/org/chromium/chrome/browser/JavascriptAppModalDialog.java',
'android/java/src/org/chromium/chrome/browser/metrics/UmaSessionStats.java',
'android/java/src/org/chromium/chrome/browser/NavigationPopup.java',
'android/java/src/org/chromium/chrome/browser/net/spdyproxy/DataReductionProxySettings.java',
'android/java/src/org/chromium/chrome/browser/notifications/NotificationUIManager.java',
......
......@@ -33448,6 +33448,14 @@ Therefore, the affected-histogram name has to have at least one dot in it.
</summary>
</histogram>
<histogram name="Session.TotalDuration" units="milliseconds">
<owner>mariakhomenko@chromium.org</owner>
<owner>fqian@chromium.org</owner>
<summary>
The length of a session (launch/foregrounding to backgrounding) on mobile.
</summary>
</histogram>
<histogram name="SessionCrashed.Bubble" enum="SessionCrashedBubbleUserAction">
<owner>yiyaoliu@chromium.org</owner>
<summary>How did the user interact with the SessionCrashed Bubble?</summary>
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