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. 4 5 #include "chrome/browser/profiles/profile_metrics.h" 6 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/common/chrome_constants.h" 16 #include "chrome/installer/util/google_update_settings.h" 17 #include "content/public/browser/browser_thread.h" 18 #include "content/public/browser/user_metrics.h" 19 20 namespace { 21 22 const int kMaximumReportedProfileCount = 5; 23 const int kMaximumDaysOfDisuse = 4 * 7; // Should be integral number of weeks. 24 25 ProfileMetrics::ProfileType GetProfileType( 26 const base::FilePath& profile_path) { 27 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 28 ProfileMetrics::ProfileType metric = ProfileMetrics::SECONDARY; 29 ProfileManager* manager = g_browser_process->profile_manager(); 30 base::FilePath user_data_dir; 31 // In unittests, we do not always have a profile_manager so check. 32 if (manager) { 33 user_data_dir = manager->user_data_dir(); 34 } 35 if (profile_path == user_data_dir.AppendASCII(chrome::kInitialProfile)) { 36 metric = ProfileMetrics::ORIGINAL; 37 } 38 return metric; 39 } 40 41 void UpdateReportedOSProfileStatistics(int active, int signedin) { 42 #if defined(OS_WIN) 43 GoogleUpdateSettings::UpdateProfileCounts(active, signedin); 44 #endif 45 } 46 47 void LogLockedProfileInformation(ProfileManager* manager) { 48 const ProfileInfoCache& info_cache = manager->GetProfileInfoCache(); 49 size_t number_of_profiles = info_cache.GetNumberOfProfiles(); 50 51 base::Time now = base::Time::Now(); 52 const int kMinutesInProfileValidDuration = 53 base::TimeDelta::FromDays(28).InMinutes(); 54 for (size_t i = 0; i < number_of_profiles; ++i) { 55 // Find when locked profiles were locked 56 if (info_cache.ProfileIsSigninRequiredAtIndex(i)) { 57 base::TimeDelta time_since_lock = now - 58 info_cache.GetProfileActiveTimeAtIndex(i); 59 // Specifying 100 buckets for the histogram to get a higher level of 60 // granularity in the reported data, given the large number of possible 61 // values (kMinutesInProfileValidDuration > 40,000). 62 UMA_HISTOGRAM_CUSTOM_COUNTS("Profile.LockedProfilesDuration", 63 time_since_lock.InMinutes(), 64 1, 65 kMinutesInProfileValidDuration, 66 100); 67 } 68 } 69 } 70 71 bool HasProfileAtIndexBeenActiveSince(const ProfileInfoCache& info_cache, 72 int index, 73 const base::Time& active_limit) { 74 #if !defined(OS_ANDROID) && !defined(OS_IOS) 75 // TODO(mlerman): iOS and Android should set an ActiveTime in the 76 // ProfileInfoCache. (see ProfileManager::OnBrowserSetLastActive) 77 if (info_cache.GetProfileActiveTimeAtIndex(index) < active_limit) 78 return false; 79 #endif 80 return true; 81 } 82 83 } // namespace 84 85 enum ProfileAvatar { 86 AVATAR_GENERIC = 0, // The names for avatar icons 87 AVATAR_GENERIC_AQUA, 88 AVATAR_GENERIC_BLUE, 89 AVATAR_GENERIC_GREEN, 90 AVATAR_GENERIC_ORANGE, 91 AVATAR_GENERIC_PURPLE, 92 AVATAR_GENERIC_RED, 93 AVATAR_GENERIC_YELLOW, 94 AVATAR_SECRET_AGENT, 95 AVATAR_SUPERHERO, 96 AVATAR_VOLLEYBALL, // 10 97 AVATAR_BUSINESSMAN, 98 AVATAR_NINJA, 99 AVATAR_ALIEN, 100 AVATAR_AWESOME, 101 AVATAR_FLOWER, 102 AVATAR_PIZZA, 103 AVATAR_SOCCER, 104 AVATAR_BURGER, 105 AVATAR_CAT, 106 AVATAR_CUPCAKE, // 20 107 AVATAR_DOG, 108 AVATAR_HORSE, 109 AVATAR_MARGARITA, 110 AVATAR_NOTE, 111 AVATAR_SUN_CLOUD, 112 AVATAR_PLACEHOLDER, 113 AVATAR_UNKNOWN, // 27 114 AVATAR_GAIA, // 28 115 NUM_PROFILE_AVATAR_METRICS 116 }; 117 118 bool ProfileMetrics::CountProfileInformation(ProfileManager* manager, 119 ProfileCounts* counts) { 120 const ProfileInfoCache& info_cache = manager->GetProfileInfoCache(); 121 size_t number_of_profiles = info_cache.GetNumberOfProfiles(); 122 counts->total = number_of_profiles; 123 124 // Ignore other metrics if we have no profiles, e.g. in Chrome Frame tests. 125 if (!number_of_profiles) 126 return false; 127 128 // Maximum age for "active" profile is 4 weeks. 129 base::Time oldest = base::Time::Now() - 130 base::TimeDelta::FromDays(kMaximumDaysOfDisuse); 131 132 for (size_t i = 0; i < number_of_profiles; ++i) { 133 if (!HasProfileAtIndexBeenActiveSince(info_cache, i, oldest)) { 134 counts->unused++; 135 } else { 136 if (info_cache.ProfileIsSupervisedAtIndex(i)) 137 counts->supervised++; 138 if (!info_cache.GetUserNameOfProfileAtIndex(i).empty()) { 139 counts->signedin++; 140 if (info_cache.IsUsingGAIAPictureOfProfileAtIndex(i)) 141 counts->gaia_icon++; 142 } 143 } 144 } 145 return true; 146 } 147 148 149 void ProfileMetrics::UpdateReportedProfilesStatistics(ProfileManager* manager) { 150 ProfileCounts counts; 151 if (CountProfileInformation(manager, &counts)) { 152 int limited_total = counts.total; 153 int limited_signedin = counts.signedin; 154 if (limited_total > kMaximumReportedProfileCount) { 155 limited_total = kMaximumReportedProfileCount + 1; 156 limited_signedin = 157 (int)((float)(counts.signedin * limited_total) 158 / counts.total + 0.5); 159 } 160 UpdateReportedOSProfileStatistics(limited_total, limited_signedin); 161 } 162 } 163 164 void ProfileMetrics::LogNumberOfProfiles(ProfileManager* manager) { 165 ProfileCounts counts; 166 bool success = CountProfileInformation(manager, &counts); 167 UMA_HISTOGRAM_COUNTS_100("Profile.NumberOfProfiles", counts.total); 168 169 // Ignore other metrics if we have no profiles, e.g. in Chrome Frame tests. 170 if (success) { 171 UMA_HISTOGRAM_COUNTS_100("Profile.NumberOfManagedProfiles", 172 counts.supervised); 173 UMA_HISTOGRAM_COUNTS_100("Profile.PercentageOfManagedProfiles", 174 100 * counts.supervised / counts.total); 175 UMA_HISTOGRAM_COUNTS_100("Profile.NumberOfSignedInProfiles", 176 counts.signedin); 177 UMA_HISTOGRAM_COUNTS_100("Profile.NumberOfUnusedProfiles", 178 counts.unused); 179 UMA_HISTOGRAM_COUNTS_100("Profile.NumberOfSignedInProfilesWithGAIAIcons", 180 counts.gaia_icon); 181 182 LogLockedProfileInformation(manager); 183 UpdateReportedOSProfileStatistics(counts.total, counts.signedin); 184 } 185 } 186 187 void ProfileMetrics::LogProfileAddNewUser(ProfileAdd metric) { 188 DCHECK(metric < NUM_PROFILE_ADD_METRICS); 189 UMA_HISTOGRAM_ENUMERATION("Profile.AddNewUser", metric, 190 NUM_PROFILE_ADD_METRICS); 191 UMA_HISTOGRAM_ENUMERATION("Profile.NetUserCount", ADD_NEW_USER, 192 NUM_PROFILE_NET_METRICS); 193 } 194 195 void ProfileMetrics::LogProfileAvatarSelection(size_t icon_index) { 196 DCHECK(icon_index < NUM_PROFILE_AVATAR_METRICS); 197 ProfileAvatar icon_name = AVATAR_UNKNOWN; 198 switch (icon_index) { 199 case 0: 200 icon_name = AVATAR_GENERIC; 201 break; 202 case 1: 203 icon_name = AVATAR_GENERIC_AQUA; 204 break; 205 case 2: 206 icon_name = AVATAR_GENERIC_BLUE; 207 break; 208 case 3: 209 icon_name = AVATAR_GENERIC_GREEN; 210 break; 211 case 4: 212 icon_name = AVATAR_GENERIC_ORANGE; 213 break; 214 case 5: 215 icon_name = AVATAR_GENERIC_PURPLE; 216 break; 217 case 6: 218 icon_name = AVATAR_GENERIC_RED; 219 break; 220 case 7: 221 icon_name = AVATAR_GENERIC_YELLOW; 222 break; 223 case 8: 224 icon_name = AVATAR_SECRET_AGENT; 225 break; 226 case 9: 227 icon_name = AVATAR_SUPERHERO; 228 break; 229 case 10: 230 icon_name = AVATAR_VOLLEYBALL; 231 break; 232 case 11: 233 icon_name = AVATAR_BUSINESSMAN; 234 break; 235 case 12: 236 icon_name = AVATAR_NINJA; 237 break; 238 case 13: 239 icon_name = AVATAR_ALIEN; 240 break; 241 case 14: 242 icon_name = AVATAR_AWESOME; 243 break; 244 case 15: 245 icon_name = AVATAR_FLOWER; 246 break; 247 case 16: 248 icon_name = AVATAR_PIZZA; 249 break; 250 case 17: 251 icon_name = AVATAR_SOCCER; 252 break; 253 case 18: 254 icon_name = AVATAR_BURGER; 255 break; 256 case 19: 257 icon_name = AVATAR_CAT; 258 break; 259 case 20: 260 icon_name = AVATAR_CUPCAKE; 261 break; 262 case 21: 263 icon_name = AVATAR_DOG; 264 break; 265 case 22: 266 icon_name = AVATAR_HORSE; 267 break; 268 case 23: 269 icon_name = AVATAR_MARGARITA; 270 break; 271 case 24: 272 icon_name = AVATAR_NOTE; 273 break; 274 case 25: 275 icon_name = AVATAR_SUN_CLOUD; 276 break; 277 case 26: 278 icon_name = AVATAR_PLACEHOLDER; 279 break; 280 case 28: 281 icon_name = AVATAR_GAIA; 282 break; 283 default: // We should never actually get here. 284 NOTREACHED(); 285 break; 286 } 287 UMA_HISTOGRAM_ENUMERATION("Profile.Avatar", icon_name, 288 NUM_PROFILE_AVATAR_METRICS); 289 } 290 291 void ProfileMetrics::LogProfileDeleteUser(ProfileDelete metric) { 292 DCHECK(metric < NUM_DELETE_PROFILE_METRICS); 293 UMA_HISTOGRAM_ENUMERATION("Profile.DeleteProfileAction", metric, 294 NUM_DELETE_PROFILE_METRICS); 295 UMA_HISTOGRAM_ENUMERATION("Profile.NetUserCount", PROFILE_DELETED, 296 NUM_PROFILE_NET_METRICS); 297 } 298 299 void ProfileMetrics::LogProfileOpenMethod(ProfileOpen metric) { 300 DCHECK(metric < NUM_PROFILE_OPEN_METRICS); 301 UMA_HISTOGRAM_ENUMERATION("Profile.OpenMethod", metric, 302 NUM_PROFILE_OPEN_METRICS); 303 } 304 305 void ProfileMetrics::LogProfileSwitchGaia(ProfileGaia metric) { 306 if (metric == GAIA_OPT_IN) 307 LogProfileAvatarSelection(AVATAR_GAIA); 308 UMA_HISTOGRAM_ENUMERATION("Profile.SwitchGaiaPhotoSettings", 309 metric, 310 NUM_PROFILE_GAIA_METRICS); 311 } 312 313 void ProfileMetrics::LogProfileSwitchUser(ProfileOpen metric) { 314 DCHECK(metric < NUM_PROFILE_OPEN_METRICS); 315 UMA_HISTOGRAM_ENUMERATION("Profile.OpenMethod", metric, 316 NUM_PROFILE_OPEN_METRICS); 317 } 318 319 void ProfileMetrics::LogProfileSyncInfo(ProfileSync metric) { 320 DCHECK(metric < NUM_PROFILE_SYNC_METRICS); 321 UMA_HISTOGRAM_ENUMERATION("Profile.SyncCustomize", metric, 322 NUM_PROFILE_SYNC_METRICS); 323 } 324 325 void ProfileMetrics::LogProfileAuthResult(ProfileAuth metric) { 326 UMA_HISTOGRAM_ENUMERATION("Profile.AuthResult", metric, 327 NUM_PROFILE_AUTH_METRICS); 328 } 329 330 void ProfileMetrics::LogProfileDesktopMenu( 331 ProfileDesktopMenu metric, 332 signin::GAIAServiceType gaia_service) { 333 // The first parameter to the histogram needs to be literal, because of the 334 // optimized implementation of |UMA_HISTOGRAM_ENUMERATION|. Do not attempt 335 // to refactor. 336 switch (gaia_service) { 337 case signin::GAIA_SERVICE_TYPE_NONE: 338 UMA_HISTOGRAM_ENUMERATION("Profile.DesktopMenu.NonGAIA", metric, 339 NUM_PROFILE_DESKTOP_MENU_METRICS); 340 break; 341 case signin::GAIA_SERVICE_TYPE_SIGNOUT: 342 UMA_HISTOGRAM_ENUMERATION("Profile.DesktopMenu.GAIASignout", metric, 343 NUM_PROFILE_DESKTOP_MENU_METRICS); 344 break; 345 case signin::GAIA_SERVICE_TYPE_INCOGNITO: 346 UMA_HISTOGRAM_ENUMERATION("Profile.DesktopMenu.GAIAIncognito", 347 metric, NUM_PROFILE_DESKTOP_MENU_METRICS); 348 break; 349 case signin::GAIA_SERVICE_TYPE_ADDSESSION: 350 UMA_HISTOGRAM_ENUMERATION("Profile.DesktopMenu.GAIAAddSession", metric, 351 NUM_PROFILE_DESKTOP_MENU_METRICS); 352 break; 353 case signin::GAIA_SERVICE_TYPE_REAUTH: 354 UMA_HISTOGRAM_ENUMERATION("Profile.DesktopMenu.GAIAReAuth", metric, 355 NUM_PROFILE_DESKTOP_MENU_METRICS); 356 break; 357 case signin::GAIA_SERVICE_TYPE_SIGNUP: 358 UMA_HISTOGRAM_ENUMERATION("Profile.DesktopMenu.GAIASignup", metric, 359 NUM_PROFILE_DESKTOP_MENU_METRICS); 360 break; 361 case signin::GAIA_SERVICE_TYPE_DEFAULT: 362 UMA_HISTOGRAM_ENUMERATION("Profile.DesktopMenu.GAIADefault", metric, 363 NUM_PROFILE_DESKTOP_MENU_METRICS); 364 break; 365 } 366 } 367 368 void ProfileMetrics::LogProfileDelete(bool profile_was_signed_in) { 369 UMA_HISTOGRAM_BOOLEAN("Profile.Delete", profile_was_signed_in); 370 } 371 372 void ProfileMetrics::LogProfileNewAvatarMenuNotYou( 373 ProfileNewAvatarMenuNotYou metric) { 374 DCHECK_LT(metric, NUM_PROFILE_AVATAR_MENU_NOT_YOU_METRICS); 375 UMA_HISTOGRAM_ENUMERATION("Profile.NewAvatarMenu.NotYou", metric, 376 NUM_PROFILE_AVATAR_MENU_NOT_YOU_METRICS); 377 } 378 379 void ProfileMetrics::LogProfileNewAvatarMenuSignin( 380 ProfileNewAvatarMenuSignin metric) { 381 DCHECK_LT(metric, NUM_PROFILE_AVATAR_MENU_SIGNIN_METRICS); 382 UMA_HISTOGRAM_ENUMERATION("Profile.NewAvatarMenu.Signin", metric, 383 NUM_PROFILE_AVATAR_MENU_SIGNIN_METRICS); 384 } 385 386 void ProfileMetrics::LogProfileNewAvatarMenuUpgrade( 387 ProfileNewAvatarMenuUpgrade metric) { 388 DCHECK_LT(metric, NUM_PROFILE_AVATAR_MENU_UPGRADE_METRICS); 389 UMA_HISTOGRAM_ENUMERATION("Profile.NewAvatarMenu.Upgrade", metric, 390 NUM_PROFILE_AVATAR_MENU_UPGRADE_METRICS); 391 } 392 393 #if defined(OS_ANDROID) 394 void ProfileMetrics::LogProfileAndroidAccountManagementMenu( 395 ProfileAndroidAccountManagementMenu metric, 396 signin::GAIAServiceType gaia_service) { 397 // The first parameter to the histogram needs to be literal, because of the 398 // optimized implementation of |UMA_HISTOGRAM_ENUMERATION|. Do not attempt 399 // to refactor. 400 switch (gaia_service) { 401 case signin::GAIA_SERVICE_TYPE_NONE: 402 UMA_HISTOGRAM_ENUMERATION( 403 "Profile.AndroidAccountManagementMenu.NonGAIA", 404 metric, 405 NUM_PROFILE_ANDROID_ACCOUNT_MANAGEMENT_MENU_METRICS); 406 break; 407 case signin::GAIA_SERVICE_TYPE_SIGNOUT: 408 UMA_HISTOGRAM_ENUMERATION( 409 "Profile.AndroidAccountManagementMenu.GAIASignout", 410 metric, 411 NUM_PROFILE_ANDROID_ACCOUNT_MANAGEMENT_MENU_METRICS); 412 break; 413 case signin::GAIA_SERVICE_TYPE_INCOGNITO: 414 UMA_HISTOGRAM_ENUMERATION( 415 "Profile.AndroidAccountManagementMenu.GAIASignoutIncognito", 416 metric, 417 NUM_PROFILE_ANDROID_ACCOUNT_MANAGEMENT_MENU_METRICS); 418 break; 419 case signin::GAIA_SERVICE_TYPE_ADDSESSION: 420 UMA_HISTOGRAM_ENUMERATION( 421 "Profile.AndroidAccountManagementMenu.GAIAAddSession", 422 metric, 423 NUM_PROFILE_ANDROID_ACCOUNT_MANAGEMENT_MENU_METRICS); 424 break; 425 case signin::GAIA_SERVICE_TYPE_REAUTH: 426 UMA_HISTOGRAM_ENUMERATION( 427 "Profile.AndroidAccountManagementMenu.GAIAReAuth", 428 metric, 429 NUM_PROFILE_ANDROID_ACCOUNT_MANAGEMENT_MENU_METRICS); 430 break; 431 case signin::GAIA_SERVICE_TYPE_SIGNUP: 432 UMA_HISTOGRAM_ENUMERATION( 433 "Profile.AndroidAccountManagementMenu.GAIASignup", 434 metric, 435 NUM_PROFILE_ANDROID_ACCOUNT_MANAGEMENT_MENU_METRICS); 436 break; 437 case signin::GAIA_SERVICE_TYPE_DEFAULT: 438 UMA_HISTOGRAM_ENUMERATION( 439 "Profile.AndroidAccountManagementMenu.GAIADefault", 440 metric, 441 NUM_PROFILE_ANDROID_ACCOUNT_MANAGEMENT_MENU_METRICS); 442 break; 443 } 444 } 445 #endif // defined(OS_ANDROID) 446 447 void ProfileMetrics::LogProfileLaunch(Profile* profile) { 448 base::FilePath profile_path = profile->GetPath(); 449 UMA_HISTOGRAM_ENUMERATION("Profile.LaunchBrowser", 450 GetProfileType(profile_path), 451 NUM_PROFILE_TYPE_METRICS); 452 453 if (profile->IsSupervised()) { 454 content::RecordAction( 455 base::UserMetricsAction("ManagedMode_NewManagedUserWindow")); 456 } 457 } 458 459 void ProfileMetrics::LogProfileSyncSignIn(const base::FilePath& profile_path) { 460 UMA_HISTOGRAM_ENUMERATION("Profile.SyncSignIn", 461 GetProfileType(profile_path), 462 NUM_PROFILE_TYPE_METRICS); 463 } 464 465 void ProfileMetrics::LogProfileUpdate(const base::FilePath& profile_path) { 466 UMA_HISTOGRAM_ENUMERATION("Profile.Update", 467 GetProfileType(profile_path), 468 NUM_PROFILE_TYPE_METRICS); 469 } 470