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