blob: 0804fd88b8de1a4f9306faa0cec4e890e09404e1 [file] [log] [blame]
// Copyright (c) 2011 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/profiles/profile_metrics.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/metrics/histogram.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_info_cache.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/signin/signin_header_helper.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/installer/util/google_update_settings.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/user_metrics.h"
namespace {
const int kMaximumReportedProfileCount = 5;
const int kMaximumDaysOfDisuse = 4 * 7; // Should be integral number of weeks.
ProfileMetrics::ProfileType GetProfileType(
const base::FilePath& profile_path) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
ProfileMetrics::ProfileType metric = ProfileMetrics::SECONDARY;
ProfileManager* manager = g_browser_process->profile_manager();
base::FilePath user_data_dir;
// In unittests, we do not always have a profile_manager so check.
if (manager) {
user_data_dir = manager->user_data_dir();
}
if (profile_path == user_data_dir.AppendASCII(chrome::kInitialProfile)) {
metric = ProfileMetrics::ORIGINAL;
}
return metric;
}
void UpdateReportedOSProfileStatistics(int active, int signedin) {
#if defined(OS_WIN)
GoogleUpdateSettings::UpdateProfileCounts(active, signedin);
#endif
}
void LogLockedProfileInformation(ProfileManager* manager) {
const ProfileInfoCache& info_cache = manager->GetProfileInfoCache();
size_t number_of_profiles = info_cache.GetNumberOfProfiles();
base::Time now = base::Time::Now();
const int kMinutesInProfileValidDuration =
base::TimeDelta::FromDays(28).InMinutes();
for (size_t i = 0; i < number_of_profiles; ++i) {
// Find when locked profiles were locked
if (info_cache.ProfileIsSigninRequiredAtIndex(i)) {
base::TimeDelta time_since_lock = now -
info_cache.GetProfileActiveTimeAtIndex(i);
// Specifying 100 buckets for the histogram to get a higher level of
// granularity in the reported data, given the large number of possible
// values (kMinutesInProfileValidDuration > 40,000).
UMA_HISTOGRAM_CUSTOM_COUNTS("Profile.LockedProfilesDuration",
time_since_lock.InMinutes(),
1,
kMinutesInProfileValidDuration,
100);
}
}
}
bool HasProfileAtIndexBeenActiveSince(const ProfileInfoCache& info_cache,
int index,
const base::Time& active_limit) {
#if !defined(OS_ANDROID) && !defined(OS_IOS)
// TODO(mlerman): iOS and Android should set an ActiveTime in the
// ProfileInfoCache. (see ProfileManager::OnBrowserSetLastActive)
if (info_cache.GetProfileActiveTimeAtIndex(index) < active_limit)
return false;
#endif
return true;
}
} // namespace
enum ProfileAvatar {
AVATAR_GENERIC = 0, // The names for avatar icons
AVATAR_GENERIC_AQUA,
AVATAR_GENERIC_BLUE,
AVATAR_GENERIC_GREEN,
AVATAR_GENERIC_ORANGE,
AVATAR_GENERIC_PURPLE,
AVATAR_GENERIC_RED,
AVATAR_GENERIC_YELLOW,
AVATAR_SECRET_AGENT,
AVATAR_SUPERHERO,
AVATAR_VOLLEYBALL, // 10
AVATAR_BUSINESSMAN,
AVATAR_NINJA,
AVATAR_ALIEN,
AVATAR_AWESOME,
AVATAR_FLOWER,
AVATAR_PIZZA,
AVATAR_SOCCER,
AVATAR_BURGER,
AVATAR_CAT,
AVATAR_CUPCAKE, // 20
AVATAR_DOG,
AVATAR_HORSE,
AVATAR_MARGARITA,
AVATAR_NOTE,
AVATAR_SUN_CLOUD,
AVATAR_PLACEHOLDER,
AVATAR_UNKNOWN, // 27
AVATAR_GAIA, // 28
NUM_PROFILE_AVATAR_METRICS
};
bool ProfileMetrics::CountProfileInformation(ProfileManager* manager,
ProfileCounts* counts) {
const ProfileInfoCache& info_cache = manager->GetProfileInfoCache();
size_t number_of_profiles = info_cache.GetNumberOfProfiles();
counts->total = number_of_profiles;
// Ignore other metrics if we have no profiles, e.g. in Chrome Frame tests.
if (!number_of_profiles)
return false;
// Maximum age for "active" profile is 4 weeks.
base::Time oldest = base::Time::Now() -
base::TimeDelta::FromDays(kMaximumDaysOfDisuse);
for (size_t i = 0; i < number_of_profiles; ++i) {
if (!HasProfileAtIndexBeenActiveSince(info_cache, i, oldest)) {
counts->unused++;
} else {
if (info_cache.ProfileIsSupervisedAtIndex(i))
counts->supervised++;
if (!info_cache.GetUserNameOfProfileAtIndex(i).empty()) {
counts->signedin++;
if (info_cache.IsUsingGAIAPictureOfProfileAtIndex(i))
counts->gaia_icon++;
}
}
}
return true;
}
void ProfileMetrics::UpdateReportedProfilesStatistics(ProfileManager* manager) {
ProfileCounts counts;
if (CountProfileInformation(manager, &counts)) {
int limited_total = counts.total;
int limited_signedin = counts.signedin;
if (limited_total > kMaximumReportedProfileCount) {
limited_total = kMaximumReportedProfileCount + 1;
limited_signedin =
(int)((float)(counts.signedin * limited_total)
/ counts.total + 0.5);
}
UpdateReportedOSProfileStatistics(limited_total, limited_signedin);
}
}
void ProfileMetrics::LogNumberOfProfiles(ProfileManager* manager) {
ProfileCounts counts;
bool success = CountProfileInformation(manager, &counts);
UMA_HISTOGRAM_COUNTS_100("Profile.NumberOfProfiles", counts.total);
// Ignore other metrics if we have no profiles, e.g. in Chrome Frame tests.
if (success) {
UMA_HISTOGRAM_COUNTS_100("Profile.NumberOfManagedProfiles",
counts.supervised);
UMA_HISTOGRAM_COUNTS_100("Profile.PercentageOfManagedProfiles",
100 * counts.supervised / counts.total);
UMA_HISTOGRAM_COUNTS_100("Profile.NumberOfSignedInProfiles",
counts.signedin);
UMA_HISTOGRAM_COUNTS_100("Profile.NumberOfUnusedProfiles",
counts.unused);
UMA_HISTOGRAM_COUNTS_100("Profile.NumberOfSignedInProfilesWithGAIAIcons",
counts.gaia_icon);
LogLockedProfileInformation(manager);
UpdateReportedOSProfileStatistics(counts.total, counts.signedin);
}
}
void ProfileMetrics::LogProfileAddNewUser(ProfileAdd metric) {
DCHECK(metric < NUM_PROFILE_ADD_METRICS);
UMA_HISTOGRAM_ENUMERATION("Profile.AddNewUser", metric,
NUM_PROFILE_ADD_METRICS);
UMA_HISTOGRAM_ENUMERATION("Profile.NetUserCount", ADD_NEW_USER,
NUM_PROFILE_NET_METRICS);
}
void ProfileMetrics::LogProfileAvatarSelection(size_t icon_index) {
DCHECK(icon_index < NUM_PROFILE_AVATAR_METRICS);
ProfileAvatar icon_name = AVATAR_UNKNOWN;
switch (icon_index) {
case 0:
icon_name = AVATAR_GENERIC;
break;
case 1:
icon_name = AVATAR_GENERIC_AQUA;
break;
case 2:
icon_name = AVATAR_GENERIC_BLUE;
break;
case 3:
icon_name = AVATAR_GENERIC_GREEN;
break;
case 4:
icon_name = AVATAR_GENERIC_ORANGE;
break;
case 5:
icon_name = AVATAR_GENERIC_PURPLE;
break;
case 6:
icon_name = AVATAR_GENERIC_RED;
break;
case 7:
icon_name = AVATAR_GENERIC_YELLOW;
break;
case 8:
icon_name = AVATAR_SECRET_AGENT;
break;
case 9:
icon_name = AVATAR_SUPERHERO;
break;
case 10:
icon_name = AVATAR_VOLLEYBALL;
break;
case 11:
icon_name = AVATAR_BUSINESSMAN;
break;
case 12:
icon_name = AVATAR_NINJA;
break;
case 13:
icon_name = AVATAR_ALIEN;
break;
case 14:
icon_name = AVATAR_AWESOME;
break;
case 15:
icon_name = AVATAR_FLOWER;
break;
case 16:
icon_name = AVATAR_PIZZA;
break;
case 17:
icon_name = AVATAR_SOCCER;
break;
case 18:
icon_name = AVATAR_BURGER;
break;
case 19:
icon_name = AVATAR_CAT;
break;
case 20:
icon_name = AVATAR_CUPCAKE;
break;
case 21:
icon_name = AVATAR_DOG;
break;
case 22:
icon_name = AVATAR_HORSE;
break;
case 23:
icon_name = AVATAR_MARGARITA;
break;
case 24:
icon_name = AVATAR_NOTE;
break;
case 25:
icon_name = AVATAR_SUN_CLOUD;
break;
case 26:
icon_name = AVATAR_PLACEHOLDER;
break;
case 28:
icon_name = AVATAR_GAIA;
break;
default: // We should never actually get here.
NOTREACHED();
break;
}
UMA_HISTOGRAM_ENUMERATION("Profile.Avatar", icon_name,
NUM_PROFILE_AVATAR_METRICS);
}
void ProfileMetrics::LogProfileDeleteUser(ProfileDelete metric) {
DCHECK(metric < NUM_DELETE_PROFILE_METRICS);
UMA_HISTOGRAM_ENUMERATION("Profile.DeleteProfileAction", metric,
NUM_DELETE_PROFILE_METRICS);
UMA_HISTOGRAM_ENUMERATION("Profile.NetUserCount", PROFILE_DELETED,
NUM_PROFILE_NET_METRICS);
}
void ProfileMetrics::LogProfileOpenMethod(ProfileOpen metric) {
DCHECK(metric < NUM_PROFILE_OPEN_METRICS);
UMA_HISTOGRAM_ENUMERATION("Profile.OpenMethod", metric,
NUM_PROFILE_OPEN_METRICS);
}
void ProfileMetrics::LogProfileSwitchGaia(ProfileGaia metric) {
if (metric == GAIA_OPT_IN)
LogProfileAvatarSelection(AVATAR_GAIA);
UMA_HISTOGRAM_ENUMERATION("Profile.SwitchGaiaPhotoSettings",
metric,
NUM_PROFILE_GAIA_METRICS);
}
void ProfileMetrics::LogProfileSwitchUser(ProfileOpen metric) {
DCHECK(metric < NUM_PROFILE_OPEN_METRICS);
UMA_HISTOGRAM_ENUMERATION("Profile.OpenMethod", metric,
NUM_PROFILE_OPEN_METRICS);
}
void ProfileMetrics::LogProfileSyncInfo(ProfileSync metric) {
DCHECK(metric < NUM_PROFILE_SYNC_METRICS);
UMA_HISTOGRAM_ENUMERATION("Profile.SyncCustomize", metric,
NUM_PROFILE_SYNC_METRICS);
}
void ProfileMetrics::LogProfileAuthResult(ProfileAuth metric) {
UMA_HISTOGRAM_ENUMERATION("Profile.AuthResult", metric,
NUM_PROFILE_AUTH_METRICS);
}
void ProfileMetrics::LogProfileDesktopMenu(
ProfileDesktopMenu metric,
signin::GAIAServiceType gaia_service) {
// The first parameter to the histogram needs to be literal, because of the
// optimized implementation of |UMA_HISTOGRAM_ENUMERATION|. Do not attempt
// to refactor.
switch (gaia_service) {
case signin::GAIA_SERVICE_TYPE_NONE:
UMA_HISTOGRAM_ENUMERATION("Profile.DesktopMenu.NonGAIA", metric,
NUM_PROFILE_DESKTOP_MENU_METRICS);
break;
case signin::GAIA_SERVICE_TYPE_SIGNOUT:
UMA_HISTOGRAM_ENUMERATION("Profile.DesktopMenu.GAIASignout", metric,
NUM_PROFILE_DESKTOP_MENU_METRICS);
break;
case signin::GAIA_SERVICE_TYPE_INCOGNITO:
UMA_HISTOGRAM_ENUMERATION("Profile.DesktopMenu.GAIAIncognito",
metric, NUM_PROFILE_DESKTOP_MENU_METRICS);
break;
case signin::GAIA_SERVICE_TYPE_ADDSESSION:
UMA_HISTOGRAM_ENUMERATION("Profile.DesktopMenu.GAIAAddSession", metric,
NUM_PROFILE_DESKTOP_MENU_METRICS);
break;
case signin::GAIA_SERVICE_TYPE_REAUTH:
UMA_HISTOGRAM_ENUMERATION("Profile.DesktopMenu.GAIAReAuth", metric,
NUM_PROFILE_DESKTOP_MENU_METRICS);
break;
case signin::GAIA_SERVICE_TYPE_SIGNUP:
UMA_HISTOGRAM_ENUMERATION("Profile.DesktopMenu.GAIASignup", metric,
NUM_PROFILE_DESKTOP_MENU_METRICS);
break;
case signin::GAIA_SERVICE_TYPE_DEFAULT:
UMA_HISTOGRAM_ENUMERATION("Profile.DesktopMenu.GAIADefault", metric,
NUM_PROFILE_DESKTOP_MENU_METRICS);
break;
}
}
void ProfileMetrics::LogProfileDelete(bool profile_was_signed_in) {
UMA_HISTOGRAM_BOOLEAN("Profile.Delete", profile_was_signed_in);
}
void ProfileMetrics::LogProfileNewAvatarMenuNotYou(
ProfileNewAvatarMenuNotYou metric) {
DCHECK_LT(metric, NUM_PROFILE_AVATAR_MENU_NOT_YOU_METRICS);
UMA_HISTOGRAM_ENUMERATION("Profile.NewAvatarMenu.NotYou", metric,
NUM_PROFILE_AVATAR_MENU_NOT_YOU_METRICS);
}
void ProfileMetrics::LogProfileNewAvatarMenuSignin(
ProfileNewAvatarMenuSignin metric) {
DCHECK_LT(metric, NUM_PROFILE_AVATAR_MENU_SIGNIN_METRICS);
UMA_HISTOGRAM_ENUMERATION("Profile.NewAvatarMenu.Signin", metric,
NUM_PROFILE_AVATAR_MENU_SIGNIN_METRICS);
}
void ProfileMetrics::LogProfileNewAvatarMenuUpgrade(
ProfileNewAvatarMenuUpgrade metric) {
DCHECK_LT(metric, NUM_PROFILE_AVATAR_MENU_UPGRADE_METRICS);
UMA_HISTOGRAM_ENUMERATION("Profile.NewAvatarMenu.Upgrade", metric,
NUM_PROFILE_AVATAR_MENU_UPGRADE_METRICS);
}
#if defined(OS_ANDROID)
void ProfileMetrics::LogProfileAndroidAccountManagementMenu(
ProfileAndroidAccountManagementMenu metric,
signin::GAIAServiceType gaia_service) {
// The first parameter to the histogram needs to be literal, because of the
// optimized implementation of |UMA_HISTOGRAM_ENUMERATION|. Do not attempt
// to refactor.
switch (gaia_service) {
case signin::GAIA_SERVICE_TYPE_NONE:
UMA_HISTOGRAM_ENUMERATION(
"Profile.AndroidAccountManagementMenu.NonGAIA",
metric,
NUM_PROFILE_ANDROID_ACCOUNT_MANAGEMENT_MENU_METRICS);
break;
case signin::GAIA_SERVICE_TYPE_SIGNOUT:
UMA_HISTOGRAM_ENUMERATION(
"Profile.AndroidAccountManagementMenu.GAIASignout",
metric,
NUM_PROFILE_ANDROID_ACCOUNT_MANAGEMENT_MENU_METRICS);
break;
case signin::GAIA_SERVICE_TYPE_INCOGNITO:
UMA_HISTOGRAM_ENUMERATION(
"Profile.AndroidAccountManagementMenu.GAIASignoutIncognito",
metric,
NUM_PROFILE_ANDROID_ACCOUNT_MANAGEMENT_MENU_METRICS);
break;
case signin::GAIA_SERVICE_TYPE_ADDSESSION:
UMA_HISTOGRAM_ENUMERATION(
"Profile.AndroidAccountManagementMenu.GAIAAddSession",
metric,
NUM_PROFILE_ANDROID_ACCOUNT_MANAGEMENT_MENU_METRICS);
break;
case signin::GAIA_SERVICE_TYPE_REAUTH:
UMA_HISTOGRAM_ENUMERATION(
"Profile.AndroidAccountManagementMenu.GAIAReAuth",
metric,
NUM_PROFILE_ANDROID_ACCOUNT_MANAGEMENT_MENU_METRICS);
break;
case signin::GAIA_SERVICE_TYPE_SIGNUP:
UMA_HISTOGRAM_ENUMERATION(
"Profile.AndroidAccountManagementMenu.GAIASignup",
metric,
NUM_PROFILE_ANDROID_ACCOUNT_MANAGEMENT_MENU_METRICS);
break;
case signin::GAIA_SERVICE_TYPE_DEFAULT:
UMA_HISTOGRAM_ENUMERATION(
"Profile.AndroidAccountManagementMenu.GAIADefault",
metric,
NUM_PROFILE_ANDROID_ACCOUNT_MANAGEMENT_MENU_METRICS);
break;
}
}
#endif // defined(OS_ANDROID)
void ProfileMetrics::LogProfileLaunch(Profile* profile) {
base::FilePath profile_path = profile->GetPath();
UMA_HISTOGRAM_ENUMERATION("Profile.LaunchBrowser",
GetProfileType(profile_path),
NUM_PROFILE_TYPE_METRICS);
if (profile->IsSupervised()) {
content::RecordAction(
base::UserMetricsAction("ManagedMode_NewManagedUserWindow"));
}
}
void ProfileMetrics::LogProfileSyncSignIn(const base::FilePath& profile_path) {
UMA_HISTOGRAM_ENUMERATION("Profile.SyncSignIn",
GetProfileType(profile_path),
NUM_PROFILE_TYPE_METRICS);
}
void ProfileMetrics::LogProfileUpdate(const base::FilePath& profile_path) {
UMA_HISTOGRAM_ENUMERATION("Profile.Update",
GetProfileType(profile_path),
NUM_PROFILE_TYPE_METRICS);
}