1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/profiles/profile_metrics.h"
7 #include "base/files/file_path.h"
8 #include "base/logging.h"
9 #include "base/metrics/histogram.h"
10 #include "chrome/browser/browser_process.h"
11 #include "chrome/browser/profiles/profile.h"
12 #include "chrome/browser/profiles/profile_info_cache.h"
13 #include "chrome/browser/profiles/profile_manager.h"
14 #include "chrome/browser/signin/signin_header_helper.h"
15 #include "chrome/browser/ui/browser_finder.h"
16 #include "chrome/common/chrome_constants.h"
17 #include "chrome/installer/util/google_update_settings.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "content/public/browser/user_metrics.h"
23 #if defined(OS_WIN) || defined(OS_MACOSX)
24 const int kMaximumReportedProfileCount
= 5;
27 const int kMaximumDaysOfDisuse
= 4 * 7; // Should be integral number of weeks.
29 size_t number_of_profile_switches_
= 0;
31 // Enum for tracking the state of profiles being switched to.
32 enum ProfileOpenState
{
33 // Profile being switched to is already opened and has browsers opened.
35 // Profile being switched to is already opened but has no browsers opened.
36 PROFILE_OPENED_NO_BROWSER
,
37 // Profile being switched to is not opened.
41 ProfileOpenState
GetProfileOpenState(
42 ProfileManager
* manager
,
43 const base::FilePath
& path
) {
44 Profile
* profile_switched_to
= manager
->GetProfileByPath(path
);
46 if (!profile_switched_to
) {
47 return PROFILE_UNOPENED
;
50 if (chrome::GetTotalBrowserCountForProfile(profile_switched_to
) > 0) {
51 return PROFILE_OPENED
;
54 return PROFILE_OPENED_NO_BROWSER
;
57 ProfileMetrics::ProfileType
GetProfileType(
58 const base::FilePath
& profile_path
) {
59 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
60 ProfileMetrics::ProfileType metric
= ProfileMetrics::SECONDARY
;
61 ProfileManager
* manager
= g_browser_process
->profile_manager();
62 base::FilePath user_data_dir
;
63 // In unittests, we do not always have a profile_manager so check.
65 user_data_dir
= manager
->user_data_dir();
67 if (profile_path
== user_data_dir
.AppendASCII(chrome::kInitialProfile
)) {
68 metric
= ProfileMetrics::ORIGINAL
;
73 void LogLockedProfileInformation(ProfileManager
* manager
) {
74 const ProfileInfoCache
& info_cache
= manager
->GetProfileInfoCache();
75 size_t number_of_profiles
= info_cache
.GetNumberOfProfiles();
77 base::Time now
= base::Time::Now();
78 const int kMinutesInProfileValidDuration
=
79 base::TimeDelta::FromDays(28).InMinutes();
80 for (size_t i
= 0; i
< number_of_profiles
; ++i
) {
81 // Find when locked profiles were locked
82 if (info_cache
.ProfileIsSigninRequiredAtIndex(i
)) {
83 base::TimeDelta time_since_lock
= now
-
84 info_cache
.GetProfileActiveTimeAtIndex(i
);
85 // Specifying 100 buckets for the histogram to get a higher level of
86 // granularity in the reported data, given the large number of possible
87 // values (kMinutesInProfileValidDuration > 40,000).
88 UMA_HISTOGRAM_CUSTOM_COUNTS("Profile.LockedProfilesDuration",
89 time_since_lock
.InMinutes(),
91 kMinutesInProfileValidDuration
,
97 bool HasProfileAtIndexBeenActiveSince(const ProfileInfoCache
& info_cache
,
99 const base::Time
& active_limit
) {
100 #if !defined(OS_ANDROID) && !defined(OS_IOS)
101 // TODO(mlerman): iOS and Android should set an ActiveTime in the
102 // ProfileInfoCache. (see ProfileManager::OnBrowserSetLastActive)
103 if (info_cache
.GetProfileActiveTimeAtIndex(index
) < active_limit
)
112 AVATAR_GENERIC
= 0, // The names for avatar icons
115 AVATAR_GENERIC_GREEN
,
116 AVATAR_GENERIC_ORANGE
,
117 AVATAR_GENERIC_PURPLE
,
119 AVATAR_GENERIC_YELLOW
,
122 AVATAR_VOLLEYBALL
, // 10
132 AVATAR_CUPCAKE
, // 20
139 AVATAR_UNKNOWN
, // 27
141 NUM_PROFILE_AVATAR_METRICS
144 bool ProfileMetrics::CountProfileInformation(ProfileManager
* manager
,
145 ProfileCounts
* counts
) {
146 const ProfileInfoCache
& info_cache
= manager
->GetProfileInfoCache();
147 size_t number_of_profiles
= info_cache
.GetNumberOfProfiles();
148 counts
->total
= number_of_profiles
;
150 // Ignore other metrics if we have no profiles, e.g. in Chrome Frame tests.
151 if (!number_of_profiles
)
154 // Maximum age for "active" profile is 4 weeks.
155 base::Time oldest
= base::Time::Now() -
156 base::TimeDelta::FromDays(kMaximumDaysOfDisuse
);
158 for (size_t i
= 0; i
< number_of_profiles
; ++i
) {
159 if (!HasProfileAtIndexBeenActiveSince(info_cache
, i
, oldest
)) {
162 if (info_cache
.ProfileIsSupervisedAtIndex(i
))
163 counts
->supervised
++;
164 if (info_cache
.ProfileIsAuthenticatedAtIndex(i
)) {
166 if (info_cache
.IsUsingGAIAPictureOfProfileAtIndex(i
))
168 if (info_cache
.ProfileIsAuthErrorAtIndex(i
))
169 counts
->auth_errors
++;
176 void ProfileMetrics::UpdateReportedProfilesStatistics(ProfileManager
* manager
) {
177 #if defined(OS_WIN) || defined(OS_MACOSX)
178 ProfileCounts counts
;
179 if (CountProfileInformation(manager
, &counts
)) {
180 size_t limited_total
= counts
.total
;
181 size_t limited_signedin
= counts
.signedin
;
182 if (limited_total
> kMaximumReportedProfileCount
) {
183 limited_total
= kMaximumReportedProfileCount
+ 1;
185 (int)((float)(counts
.signedin
* limited_total
)
186 / counts
.total
+ 0.5);
188 UpdateReportedOSProfileStatistics(limited_total
, limited_signedin
);
193 void ProfileMetrics::LogNumberOfProfileSwitches() {
194 UMA_HISTOGRAM_COUNTS_100("Profile.NumberOfSwitches",
195 number_of_profile_switches_
);
198 // The OS_MACOSX implementation of this function is in profile_metrics_mac.mm.
200 void ProfileMetrics::UpdateReportedOSProfileStatistics(
201 size_t active
, size_t signedin
) {
202 GoogleUpdateSettings::UpdateProfileCounts(active
, signedin
);
206 void ProfileMetrics::LogNumberOfProfiles(ProfileManager
* manager
) {
207 ProfileCounts counts
;
208 bool success
= CountProfileInformation(manager
, &counts
);
209 UMA_HISTOGRAM_COUNTS_100("Profile.NumberOfProfiles", counts
.total
);
211 // Ignore other metrics if we have no profiles, e.g. in Chrome Frame tests.
213 UMA_HISTOGRAM_COUNTS_100("Profile.NumberOfManagedProfiles",
215 UMA_HISTOGRAM_COUNTS_100("Profile.PercentageOfManagedProfiles",
216 100 * counts
.supervised
/ counts
.total
);
217 UMA_HISTOGRAM_COUNTS_100("Profile.NumberOfSignedInProfiles",
219 UMA_HISTOGRAM_COUNTS_100("Profile.NumberOfUnusedProfiles",
221 UMA_HISTOGRAM_COUNTS_100("Profile.NumberOfSignedInProfilesWithGAIAIcons",
223 UMA_HISTOGRAM_COUNTS_100("Profile.NumberOfProfilesWithAuthErrors",
226 LogLockedProfileInformation(manager
);
228 #if defined(OS_WIN) || defined(OS_MACOSX)
229 UpdateReportedOSProfileStatistics(counts
.total
, counts
.signedin
);
234 void ProfileMetrics::LogProfileAddNewUser(ProfileAdd metric
) {
235 DCHECK(metric
< NUM_PROFILE_ADD_METRICS
);
236 UMA_HISTOGRAM_ENUMERATION("Profile.AddNewUser", metric
,
237 NUM_PROFILE_ADD_METRICS
);
238 UMA_HISTOGRAM_ENUMERATION("Profile.NetUserCount", ADD_NEW_USER
,
239 NUM_PROFILE_NET_METRICS
);
242 void ProfileMetrics::LogProfileAvatarSelection(size_t icon_index
) {
243 DCHECK(icon_index
< NUM_PROFILE_AVATAR_METRICS
);
244 ProfileAvatar icon_name
= AVATAR_UNKNOWN
;
245 switch (icon_index
) {
247 icon_name
= AVATAR_GENERIC
;
250 icon_name
= AVATAR_GENERIC_AQUA
;
253 icon_name
= AVATAR_GENERIC_BLUE
;
256 icon_name
= AVATAR_GENERIC_GREEN
;
259 icon_name
= AVATAR_GENERIC_ORANGE
;
262 icon_name
= AVATAR_GENERIC_PURPLE
;
265 icon_name
= AVATAR_GENERIC_RED
;
268 icon_name
= AVATAR_GENERIC_YELLOW
;
271 icon_name
= AVATAR_SECRET_AGENT
;
274 icon_name
= AVATAR_SUPERHERO
;
277 icon_name
= AVATAR_VOLLEYBALL
;
280 icon_name
= AVATAR_BUSINESSMAN
;
283 icon_name
= AVATAR_NINJA
;
286 icon_name
= AVATAR_ALIEN
;
289 icon_name
= AVATAR_AWESOME
;
292 icon_name
= AVATAR_FLOWER
;
295 icon_name
= AVATAR_PIZZA
;
298 icon_name
= AVATAR_SOCCER
;
301 icon_name
= AVATAR_BURGER
;
304 icon_name
= AVATAR_CAT
;
307 icon_name
= AVATAR_CUPCAKE
;
310 icon_name
= AVATAR_DOG
;
313 icon_name
= AVATAR_HORSE
;
316 icon_name
= AVATAR_MARGARITA
;
319 icon_name
= AVATAR_NOTE
;
322 icon_name
= AVATAR_SUN_CLOUD
;
325 icon_name
= AVATAR_PLACEHOLDER
;
328 icon_name
= AVATAR_GAIA
;
330 default: // We should never actually get here.
334 UMA_HISTOGRAM_ENUMERATION("Profile.Avatar", icon_name
,
335 NUM_PROFILE_AVATAR_METRICS
);
338 void ProfileMetrics::LogProfileDeleteUser(ProfileDelete metric
) {
339 DCHECK(metric
< NUM_DELETE_PROFILE_METRICS
);
340 UMA_HISTOGRAM_ENUMERATION("Profile.DeleteProfileAction", metric
,
341 NUM_DELETE_PROFILE_METRICS
);
342 if (metric
!= DELETE_PROFILE_USER_MANAGER_SHOW_WARNING
&&
343 metric
!= DELETE_PROFILE_SETTINGS_SHOW_WARNING
) {
344 // If a user was actually deleted, update the net user count.
345 UMA_HISTOGRAM_ENUMERATION("Profile.NetUserCount", PROFILE_DELETED
,
346 NUM_PROFILE_NET_METRICS
);
350 void ProfileMetrics::LogProfileOpenMethod(ProfileOpen metric
) {
351 DCHECK(metric
< NUM_PROFILE_OPEN_METRICS
);
352 UMA_HISTOGRAM_ENUMERATION("Profile.OpenMethod", metric
,
353 NUM_PROFILE_OPEN_METRICS
);
356 void ProfileMetrics::LogProfileSwitch(
358 ProfileManager
* manager
,
359 const base::FilePath
& profile_path
) {
360 DCHECK(metric
< NUM_PROFILE_OPEN_METRICS
);
361 ProfileOpenState open_state
= GetProfileOpenState(manager
, profile_path
);
362 switch (open_state
) {
364 UMA_HISTOGRAM_ENUMERATION(
365 "Profile.OpenMethod.ToOpenedProfile",
367 NUM_PROFILE_OPEN_METRICS
);
369 case PROFILE_OPENED_NO_BROWSER
:
370 UMA_HISTOGRAM_ENUMERATION(
371 "Profile.OpenMethod.ToOpenedProfileWithoutBrowser",
373 NUM_PROFILE_OPEN_METRICS
);
375 case PROFILE_UNOPENED
:
376 UMA_HISTOGRAM_ENUMERATION(
377 "Profile.OpenMethod.ToUnopenedProfile",
379 NUM_PROFILE_OPEN_METRICS
);
382 // There are no other possible values.
387 ++number_of_profile_switches_
;
388 // The LogOpenMethod histogram aggregates data from profile switches as well
389 // as opening of profile related UI elements.
390 LogProfileOpenMethod(metric
);
393 void ProfileMetrics::LogProfileSwitchGaia(ProfileGaia metric
) {
394 if (metric
== GAIA_OPT_IN
)
395 LogProfileAvatarSelection(AVATAR_GAIA
);
396 UMA_HISTOGRAM_ENUMERATION("Profile.SwitchGaiaPhotoSettings",
398 NUM_PROFILE_GAIA_METRICS
);
401 void ProfileMetrics::LogProfileSyncInfo(ProfileSync metric
) {
402 DCHECK(metric
< NUM_PROFILE_SYNC_METRICS
);
403 UMA_HISTOGRAM_ENUMERATION("Profile.SyncCustomize", metric
,
404 NUM_PROFILE_SYNC_METRICS
);
407 void ProfileMetrics::LogProfileAuthResult(ProfileAuth metric
) {
408 UMA_HISTOGRAM_ENUMERATION("Profile.AuthResult", metric
,
409 NUM_PROFILE_AUTH_METRICS
);
412 void ProfileMetrics::LogProfileDesktopMenu(
413 ProfileDesktopMenu metric
,
414 signin::GAIAServiceType gaia_service
) {
415 // The first parameter to the histogram needs to be literal, because of the
416 // optimized implementation of |UMA_HISTOGRAM_ENUMERATION|. Do not attempt
418 switch (gaia_service
) {
419 case signin::GAIA_SERVICE_TYPE_NONE
:
420 UMA_HISTOGRAM_ENUMERATION("Profile.DesktopMenu.NonGAIA", metric
,
421 NUM_PROFILE_DESKTOP_MENU_METRICS
);
423 case signin::GAIA_SERVICE_TYPE_SIGNOUT
:
424 UMA_HISTOGRAM_ENUMERATION("Profile.DesktopMenu.GAIASignout", metric
,
425 NUM_PROFILE_DESKTOP_MENU_METRICS
);
427 case signin::GAIA_SERVICE_TYPE_INCOGNITO
:
428 UMA_HISTOGRAM_ENUMERATION("Profile.DesktopMenu.GAIAIncognito",
429 metric
, NUM_PROFILE_DESKTOP_MENU_METRICS
);
431 case signin::GAIA_SERVICE_TYPE_ADDSESSION
:
432 UMA_HISTOGRAM_ENUMERATION("Profile.DesktopMenu.GAIAAddSession", metric
,
433 NUM_PROFILE_DESKTOP_MENU_METRICS
);
435 case signin::GAIA_SERVICE_TYPE_REAUTH
:
436 UMA_HISTOGRAM_ENUMERATION("Profile.DesktopMenu.GAIAReAuth", metric
,
437 NUM_PROFILE_DESKTOP_MENU_METRICS
);
439 case signin::GAIA_SERVICE_TYPE_SIGNUP
:
440 UMA_HISTOGRAM_ENUMERATION("Profile.DesktopMenu.GAIASignup", metric
,
441 NUM_PROFILE_DESKTOP_MENU_METRICS
);
443 case signin::GAIA_SERVICE_TYPE_DEFAULT
:
444 UMA_HISTOGRAM_ENUMERATION("Profile.DesktopMenu.GAIADefault", metric
,
445 NUM_PROFILE_DESKTOP_MENU_METRICS
);
450 void ProfileMetrics::LogProfileDelete(bool profile_was_signed_in
) {
451 UMA_HISTOGRAM_BOOLEAN("Profile.Delete", profile_was_signed_in
);
454 void ProfileMetrics::LogProfileNewAvatarMenuNotYou(
455 ProfileNewAvatarMenuNotYou metric
) {
456 DCHECK_LT(metric
, NUM_PROFILE_AVATAR_MENU_NOT_YOU_METRICS
);
457 UMA_HISTOGRAM_ENUMERATION("Profile.NewAvatarMenu.NotYou", metric
,
458 NUM_PROFILE_AVATAR_MENU_NOT_YOU_METRICS
);
461 void ProfileMetrics::LogProfileNewAvatarMenuSignin(
462 ProfileNewAvatarMenuSignin metric
) {
463 DCHECK_LT(metric
, NUM_PROFILE_AVATAR_MENU_SIGNIN_METRICS
);
464 UMA_HISTOGRAM_ENUMERATION("Profile.NewAvatarMenu.Signin", metric
,
465 NUM_PROFILE_AVATAR_MENU_SIGNIN_METRICS
);
468 void ProfileMetrics::LogProfileNewAvatarMenuUpgrade(
469 ProfileNewAvatarMenuUpgrade metric
) {
470 DCHECK_LT(metric
, NUM_PROFILE_AVATAR_MENU_UPGRADE_METRICS
);
471 UMA_HISTOGRAM_ENUMERATION("Profile.NewAvatarMenu.Upgrade", metric
,
472 NUM_PROFILE_AVATAR_MENU_UPGRADE_METRICS
);
475 void ProfileMetrics::LogTimeToOpenUserManager(
476 const base::TimeDelta
& time_to_open
) {
477 UMA_HISTOGRAM_TIMES("Profile.TimeToOpenUserManager", time_to_open
);
480 #if defined(OS_ANDROID)
481 void ProfileMetrics::LogProfileAndroidAccountManagementMenu(
482 ProfileAndroidAccountManagementMenu metric
,
483 signin::GAIAServiceType gaia_service
) {
484 // The first parameter to the histogram needs to be literal, because of the
485 // optimized implementation of |UMA_HISTOGRAM_ENUMERATION|. Do not attempt
487 switch (gaia_service
) {
488 case signin::GAIA_SERVICE_TYPE_NONE
:
489 UMA_HISTOGRAM_ENUMERATION(
490 "Profile.AndroidAccountManagementMenu.NonGAIA",
492 NUM_PROFILE_ANDROID_ACCOUNT_MANAGEMENT_MENU_METRICS
);
494 case signin::GAIA_SERVICE_TYPE_SIGNOUT
:
495 UMA_HISTOGRAM_ENUMERATION(
496 "Profile.AndroidAccountManagementMenu.GAIASignout",
498 NUM_PROFILE_ANDROID_ACCOUNT_MANAGEMENT_MENU_METRICS
);
500 case signin::GAIA_SERVICE_TYPE_INCOGNITO
:
501 UMA_HISTOGRAM_ENUMERATION(
502 "Profile.AndroidAccountManagementMenu.GAIASignoutIncognito",
504 NUM_PROFILE_ANDROID_ACCOUNT_MANAGEMENT_MENU_METRICS
);
506 case signin::GAIA_SERVICE_TYPE_ADDSESSION
:
507 UMA_HISTOGRAM_ENUMERATION(
508 "Profile.AndroidAccountManagementMenu.GAIAAddSession",
510 NUM_PROFILE_ANDROID_ACCOUNT_MANAGEMENT_MENU_METRICS
);
512 case signin::GAIA_SERVICE_TYPE_REAUTH
:
513 UMA_HISTOGRAM_ENUMERATION(
514 "Profile.AndroidAccountManagementMenu.GAIAReAuth",
516 NUM_PROFILE_ANDROID_ACCOUNT_MANAGEMENT_MENU_METRICS
);
518 case signin::GAIA_SERVICE_TYPE_SIGNUP
:
519 UMA_HISTOGRAM_ENUMERATION(
520 "Profile.AndroidAccountManagementMenu.GAIASignup",
522 NUM_PROFILE_ANDROID_ACCOUNT_MANAGEMENT_MENU_METRICS
);
524 case signin::GAIA_SERVICE_TYPE_DEFAULT
:
525 UMA_HISTOGRAM_ENUMERATION(
526 "Profile.AndroidAccountManagementMenu.GAIADefault",
528 NUM_PROFILE_ANDROID_ACCOUNT_MANAGEMENT_MENU_METRICS
);
532 #endif // defined(OS_ANDROID)
534 void ProfileMetrics::LogProfileLaunch(Profile
* profile
) {
535 base::FilePath profile_path
= profile
->GetPath();
536 UMA_HISTOGRAM_ENUMERATION("Profile.LaunchBrowser",
537 GetProfileType(profile_path
),
538 NUM_PROFILE_TYPE_METRICS
);
540 if (profile
->IsSupervised()) {
541 content::RecordAction(
542 base::UserMetricsAction("ManagedMode_NewManagedUserWindow"));
546 void ProfileMetrics::LogProfileSyncSignIn(const base::FilePath
& profile_path
) {
547 UMA_HISTOGRAM_ENUMERATION("Profile.SyncSignIn",
548 GetProfileType(profile_path
),
549 NUM_PROFILE_TYPE_METRICS
);
552 void ProfileMetrics::LogProfileUpdate(const base::FilePath
& profile_path
) {
553 UMA_HISTOGRAM_ENUMERATION("Profile.Update",
554 GetProfileType(profile_path
),
555 NUM_PROFILE_TYPE_METRICS
);