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 <vector>
8
9 #include "base/check_op.h"
10 #include "base/files/file_path.h"
11 #include "base/metrics/histogram_functions.h"
12 #include "base/metrics/user_metrics.h"
13 #include "base/notreached.h"
14 #include "build/build_config.h"
15 #include "chrome/browser/browser_process.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/profiles/profile_attributes_entry.h"
18 #include "chrome/browser/profiles/profile_attributes_storage.h"
19 #include "chrome/browser/profiles/profile_avatar_icon_util.h"
20 #include "chrome/browser/profiles/profile_manager.h"
21 #include "chrome/browser/signin/chrome_signin_helper.h"
22 #include "chrome/browser/ui/signin/profile_colors_util.h"
23 #include "chrome/common/chrome_constants.h"
24 #include "chrome/installer/util/google_update_settings.h"
25 #include "components/profile_metrics/browser_profile_type.h"
26 #include "components/profile_metrics/counts.h"
27 #include "components/signin/core/browser/signin_header_helper.h"
28 #include "content/public/browser/browser_thread.h"
29
30 #if !defined(OS_ANDROID)
31 #include "chrome/browser/ui/browser_finder.h"
32 #endif
33
34 namespace {
35
36 #if !defined(OS_ANDROID)
37 constexpr base::TimeDelta kProfileActivityThreshold =
38 base::TimeDelta::FromDays(28); // Should be integral number of weeks.
39 #endif
40
41 enum class ProfileType {
42 ORIGINAL = 0, // Refers to the original/default profile
43 SECONDARY, // Refers to a user-created profile
44 kMaxValue = SECONDARY
45 };
46
47 // Enum for getting net counts for adding and deleting users.
48 enum class ProfileNetUserCounts {
49 ADD_NEW_USER = 0, // Total count of add new user
50 PROFILE_DELETED, // User deleted a profile
51 kMaxValue = PROFILE_DELETED
52 };
53
GetProfileType(const base::FilePath & profile_path)54 ProfileType GetProfileType(const base::FilePath& profile_path) {
55 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
56 ProfileType metric = ProfileType::SECONDARY;
57 ProfileManager* manager = g_browser_process->profile_manager();
58 base::FilePath user_data_dir;
59 // In unittests, we do not always have a profile_manager so check.
60 if (manager) {
61 user_data_dir = manager->user_data_dir();
62 }
63 if (profile_path == user_data_dir.AppendASCII(chrome::kInitialProfile)) {
64 metric = ProfileType::ORIGINAL;
65 }
66 return metric;
67 }
68
GetProfileColorsUniqueness(ProfileAttributesStorage * storage)69 profile_metrics::ProfileColorsUniqueness GetProfileColorsUniqueness(
70 ProfileAttributesStorage* storage) {
71 #if defined(OS_ANDROID)
72 return profile_metrics::ProfileColorsUniqueness::kSingleProfile;
73 #else
74 std::vector<ProfileAttributesEntry*> entries =
75 storage->GetAllProfilesAttributes();
76 DCHECK(!entries.empty());
77 if (entries.size() == 1u)
78 return profile_metrics::ProfileColorsUniqueness::kSingleProfile;
79
80 size_t default_colors_count = 0;
81 std::set<ProfileThemeColors> used_colors;
82 for (ProfileAttributesEntry* entry : entries) {
83 base::Optional<ProfileThemeColors> profile_colors =
84 entry->GetProfileThemeColorsIfSet();
85 if (!profile_colors) {
86 default_colors_count++;
87 } else if (!base::Contains(used_colors, *profile_colors)) {
88 used_colors.insert(*profile_colors);
89 } else {
90 return profile_metrics::ProfileColorsUniqueness::kRepeated;
91 }
92 }
93 return default_colors_count > 1u
94 ? profile_metrics::ProfileColorsUniqueness::
95 kUniqueExceptForRepeatedDefault
96 : profile_metrics::ProfileColorsUniqueness::kUnique;
97 #endif
98 }
99
100 } // namespace
101
102 // This enum is used for histograms. Do not change existing values. Append new
103 // values at the end.
104 enum ProfileAvatar {
105 AVATAR_GENERIC = 0, // The names for avatar icons
106 AVATAR_GENERIC_AQUA = 1,
107 AVATAR_GENERIC_BLUE = 2,
108 AVATAR_GENERIC_GREEN = 3,
109 AVATAR_GENERIC_ORANGE = 4,
110 AVATAR_GENERIC_PURPLE = 5,
111 AVATAR_GENERIC_RED = 6,
112 AVATAR_GENERIC_YELLOW = 7,
113 AVATAR_SECRET_AGENT = 8,
114 AVATAR_SUPERHERO = 9,
115 AVATAR_VOLLEYBALL = 10,
116 AVATAR_BUSINESSMAN = 11,
117 AVATAR_NINJA = 12,
118 AVATAR_ALIEN = 13,
119 AVATAR_AWESOME = 14,
120 AVATAR_FLOWER = 15,
121 AVATAR_PIZZA = 16,
122 AVATAR_SOCCER = 17,
123 AVATAR_BURGER = 18,
124 AVATAR_CAT = 19,
125 AVATAR_CUPCAKE = 20,
126 AVATAR_DOG = 21,
127 AVATAR_HORSE = 22,
128 AVATAR_MARGARITA = 23,
129 AVATAR_NOTE = 24,
130 AVATAR_SUN_CLOUD = 25,
131 AVATAR_PLACEHOLDER = 26,
132 AVATAR_UNKNOWN = 27,
133 AVATAR_GAIA = 28,
134 // Modern avatars:
135 AVATAR_ORIGAMI_CAT = 29,
136 AVATAR_ORIGAMI_CORGI = 30,
137 AVATAR_ORIGAMI_DRAGON = 31,
138 AVATAR_ORIGAMI_ELEPHANT = 32,
139 AVATAR_ORIGAMI_FOX = 33,
140 AVATAR_ORIGAMI_MONKEY = 34,
141 AVATAR_ORIGAMI_PANDA = 35,
142 AVATAR_ORIGAMI_PENGUIN = 36,
143 AVATAR_ORIGAMI_PINKBUTTERFLY = 37,
144 AVATAR_ORIGAMI_RABBIT = 38,
145 AVATAR_ORIGAMI_UNICORN = 39,
146 AVATAR_ILLUSTRATION_BASKETBALL = 40,
147 AVATAR_ILLUSTRATION_BIKE = 41,
148 AVATAR_ILLUSTRATION_BIRD = 42,
149 AVATAR_ILLUSTRATION_CHEESE = 43,
150 AVATAR_ILLUSTRATION_FOOTBALL = 44,
151 AVATAR_ILLUSTRATION_RAMEN = 45,
152 AVATAR_ILLUSTRATION_SUNGLASSES = 46,
153 AVATAR_ILLUSTRATION_SUSHI = 47,
154 AVATAR_ILLUSTRATION_TAMAGOTCHI = 48,
155 AVATAR_ILLUSTRATION_VINYL = 49,
156 AVATAR_ABSTRACT_AVOCADO = 50,
157 AVATAR_ABSTRACT_CAPPUCCINO = 51,
158 AVATAR_ABSTRACT_ICECREAM = 52,
159 AVATAR_ABSTRACT_ICEWATER = 53,
160 AVATAR_ABSTRACT_MELON = 54,
161 AVATAR_ABSTRACT_ONIGIRI = 55,
162 AVATAR_ABSTRACT_PIZZA = 56,
163 AVATAR_ABSTRACT_SANDWICH = 57,
164 NUM_PROFILE_AVATAR_METRICS
165 };
166
167 // static
IsProfileActive(const ProfileAttributesEntry * entry)168 bool ProfileMetrics::IsProfileActive(const ProfileAttributesEntry* entry) {
169 #if !defined(OS_ANDROID)
170 // TODO(mlerman): iOS and Android should set an ActiveTime in the
171 // ProfileAttributesStorage. (see ProfileManager::OnBrowserSetLastActive)
172 if (base::Time::Now() - entry->GetActiveTime() > kProfileActivityThreshold)
173 return false;
174 #endif
175 return true;
176 }
177
178 // static
CountProfileInformation(ProfileAttributesStorage * storage,profile_metrics::Counts * counts)179 void ProfileMetrics::CountProfileInformation(ProfileAttributesStorage* storage,
180 profile_metrics::Counts* counts) {
181 size_t number_of_profiles = storage->GetNumberOfProfiles();
182 counts->total = number_of_profiles;
183
184 // Ignore other metrics if we have no profiles.
185 if (!number_of_profiles)
186 return;
187
188 std::vector<ProfileAttributesEntry*> entries =
189 storage->GetAllProfilesAttributes();
190 for (ProfileAttributesEntry* entry : entries) {
191 if (!IsProfileActive(entry)) {
192 counts->unused++;
193 } else {
194 counts->active++;
195 if (!entry->IsUsingDefaultName())
196 counts->named++;
197 if (entry->IsSupervised())
198 counts->supervised++;
199 if (entry->IsAuthenticated()) {
200 counts->signedin++;
201 if (entry->IsUsingGAIAPicture())
202 counts->gaia_icon++;
203 if (entry->IsAuthError())
204 counts->auth_errors++;
205 }
206 }
207 }
208 counts->colors_uniqueness = GetProfileColorsUniqueness(storage);
209 }
210
GetBrowserProfileType(Profile * profile)211 profile_metrics::BrowserProfileType ProfileMetrics::GetBrowserProfileType(
212 Profile* profile) {
213 if (profile->IsSystemProfile())
214 return profile_metrics::BrowserProfileType::kSystem;
215 if (profile->IsGuestSession() || profile->IsEphemeralGuestProfile())
216 return profile_metrics::BrowserProfileType::kGuest;
217 // A regular profile can be in a guest session or a system profile. Hence it
218 // should be checked after them.
219 if (profile->IsRegularProfile())
220 return profile_metrics::BrowserProfileType::kRegular;
221
222 if (profile->IsIncognitoProfile())
223 return profile_metrics::BrowserProfileType::kIncognito;
224
225 if (profile->IsOffTheRecord() && !profile->IsPrimaryOTRProfile())
226 return profile_metrics::BrowserProfileType::kOtherOffTheRecordProfile;
227
228 NOTREACHED();
229 return profile_metrics::BrowserProfileType::kMaxValue;
230 }
231
LogNumberOfProfiles(ProfileAttributesStorage * storage)232 void ProfileMetrics::LogNumberOfProfiles(ProfileAttributesStorage* storage) {
233 profile_metrics::Counts counts;
234 CountProfileInformation(storage, &counts);
235 profile_metrics::LogProfileMetricsCounts(counts);
236 }
237
LogProfileAddNewUser(ProfileAdd metric)238 void ProfileMetrics::LogProfileAddNewUser(ProfileAdd metric) {
239 base::UmaHistogramEnumeration("Profile.AddNewUser", metric);
240 base::UmaHistogramEnumeration("Profile.NetUserCount",
241 ProfileNetUserCounts::ADD_NEW_USER);
242 }
243
LogProfileAvatarSelection(size_t icon_index)244 void ProfileMetrics::LogProfileAvatarSelection(size_t icon_index) {
245 ProfileAvatar icon_name = AVATAR_UNKNOWN;
246 switch (icon_index) {
247 case 0:
248 icon_name = AVATAR_GENERIC;
249 break;
250 case 1:
251 icon_name = AVATAR_GENERIC_AQUA;
252 break;
253 case 2:
254 icon_name = AVATAR_GENERIC_BLUE;
255 break;
256 case 3:
257 icon_name = AVATAR_GENERIC_GREEN;
258 break;
259 case 4:
260 icon_name = AVATAR_GENERIC_ORANGE;
261 break;
262 case 5:
263 icon_name = AVATAR_GENERIC_PURPLE;
264 break;
265 case 6:
266 icon_name = AVATAR_GENERIC_RED;
267 break;
268 case 7:
269 icon_name = AVATAR_GENERIC_YELLOW;
270 break;
271 case 8:
272 icon_name = AVATAR_SECRET_AGENT;
273 break;
274 case 9:
275 icon_name = AVATAR_SUPERHERO;
276 break;
277 case 10:
278 icon_name = AVATAR_VOLLEYBALL;
279 break;
280 case 11:
281 icon_name = AVATAR_BUSINESSMAN;
282 break;
283 case 12:
284 icon_name = AVATAR_NINJA;
285 break;
286 case 13:
287 icon_name = AVATAR_ALIEN;
288 break;
289 case 14:
290 icon_name = AVATAR_AWESOME;
291 break;
292 case 15:
293 icon_name = AVATAR_FLOWER;
294 break;
295 case 16:
296 icon_name = AVATAR_PIZZA;
297 break;
298 case 17:
299 icon_name = AVATAR_SOCCER;
300 break;
301 case 18:
302 icon_name = AVATAR_BURGER;
303 break;
304 case 19:
305 icon_name = AVATAR_CAT;
306 break;
307 case 20:
308 icon_name = AVATAR_CUPCAKE;
309 break;
310 case 21:
311 icon_name = AVATAR_DOG;
312 break;
313 case 22:
314 icon_name = AVATAR_HORSE;
315 break;
316 case 23:
317 icon_name = AVATAR_MARGARITA;
318 break;
319 case 24:
320 icon_name = AVATAR_NOTE;
321 break;
322 case 25:
323 icon_name = AVATAR_SUN_CLOUD;
324 break;
325 case 26:
326 icon_name = AVATAR_PLACEHOLDER;
327 break;
328 // Modern avatars:
329 case 27:
330 icon_name = AVATAR_ORIGAMI_CAT;
331 break;
332 case 28:
333 icon_name = AVATAR_ORIGAMI_CORGI;
334 break;
335 case 29:
336 icon_name = AVATAR_ORIGAMI_DRAGON;
337 break;
338 case 30:
339 icon_name = AVATAR_ORIGAMI_ELEPHANT;
340 break;
341 case 31:
342 icon_name = AVATAR_ORIGAMI_FOX;
343 break;
344 case 32:
345 icon_name = AVATAR_ORIGAMI_MONKEY;
346 break;
347 case 33:
348 icon_name = AVATAR_ORIGAMI_PANDA;
349 break;
350 case 34:
351 icon_name = AVATAR_ORIGAMI_PENGUIN;
352 break;
353 case 35:
354 icon_name = AVATAR_ORIGAMI_PINKBUTTERFLY;
355 break;
356 case 36:
357 icon_name = AVATAR_ORIGAMI_RABBIT;
358 break;
359 case 37:
360 icon_name = AVATAR_ORIGAMI_UNICORN;
361 break;
362 case 38:
363 icon_name = AVATAR_ILLUSTRATION_BASKETBALL;
364 break;
365 case 39:
366 icon_name = AVATAR_ILLUSTRATION_BIKE;
367 break;
368 case 40:
369 icon_name = AVATAR_ILLUSTRATION_BIRD;
370 break;
371 case 41:
372 icon_name = AVATAR_ILLUSTRATION_CHEESE;
373 break;
374 case 42:
375 icon_name = AVATAR_ILLUSTRATION_FOOTBALL;
376 break;
377 case 43:
378 icon_name = AVATAR_ILLUSTRATION_RAMEN;
379 break;
380 case 44:
381 icon_name = AVATAR_ILLUSTRATION_SUNGLASSES;
382 break;
383 case 45:
384 icon_name = AVATAR_ILLUSTRATION_SUSHI;
385 break;
386 case 46:
387 icon_name = AVATAR_ILLUSTRATION_TAMAGOTCHI;
388 break;
389 case 47:
390 icon_name = AVATAR_ILLUSTRATION_VINYL;
391 break;
392 case 48:
393 icon_name = AVATAR_ABSTRACT_AVOCADO;
394 break;
395 case 49:
396 icon_name = AVATAR_ABSTRACT_CAPPUCCINO;
397 break;
398 case 50:
399 icon_name = AVATAR_ABSTRACT_ICECREAM;
400 break;
401 case 51:
402 icon_name = AVATAR_ABSTRACT_ICEWATER;
403 break;
404 case 52:
405 icon_name = AVATAR_ABSTRACT_MELON;
406 break;
407 case 53:
408 icon_name = AVATAR_ABSTRACT_ONIGIRI;
409 break;
410 case 54:
411 icon_name = AVATAR_ABSTRACT_PIZZA;
412 break;
413 case 55:
414 icon_name = AVATAR_ABSTRACT_SANDWICH;
415 break;
416 case SIZE_MAX:
417 icon_name = AVATAR_GAIA;
418 break;
419 default:
420 NOTREACHED();
421 }
422 base::UmaHistogramEnumeration("Profile.Avatar", icon_name,
423 NUM_PROFILE_AVATAR_METRICS);
424 }
425
LogProfileDeleteUser(ProfileDelete metric)426 void ProfileMetrics::LogProfileDeleteUser(ProfileDelete metric) {
427 DCHECK(metric < NUM_DELETE_PROFILE_METRICS);
428 base::UmaHistogramEnumeration("Profile.DeleteProfileAction", metric,
429 NUM_DELETE_PROFILE_METRICS);
430 if (metric != DELETE_PROFILE_USER_MANAGER_SHOW_WARNING &&
431 metric != DELETE_PROFILE_SETTINGS_SHOW_WARNING &&
432 metric != DELETE_PROFILE_ABORTED) {
433 // If a user was actually deleted, update the net user count.
434 base::UmaHistogramEnumeration("Profile.NetUserCount",
435 ProfileNetUserCounts::PROFILE_DELETED);
436 }
437 }
438
LogProfileSwitchGaia(ProfileGaia metric)439 void ProfileMetrics::LogProfileSwitchGaia(ProfileGaia metric) {
440 if (metric == GAIA_OPT_IN)
441 LogProfileAvatarSelection(SIZE_MAX);
442 base::UmaHistogramEnumeration("Profile.SwitchGaiaPhotoSettings", metric,
443 NUM_PROFILE_GAIA_METRICS);
444 }
445
LogProfileSyncInfo(ProfileSync metric)446 void ProfileMetrics::LogProfileSyncInfo(ProfileSync metric) {
447 DCHECK(metric < NUM_PROFILE_SYNC_METRICS);
448 base::UmaHistogramEnumeration("Profile.SyncCustomize", metric,
449 NUM_PROFILE_SYNC_METRICS);
450 }
451
LogProfileDelete(bool profile_was_signed_in)452 void ProfileMetrics::LogProfileDelete(bool profile_was_signed_in) {
453 base::UmaHistogramBoolean("Profile.Delete", profile_was_signed_in);
454 }
455
LogTimeToOpenUserManager(const base::TimeDelta & time_to_open)456 void ProfileMetrics::LogTimeToOpenUserManager(
457 const base::TimeDelta& time_to_open) {
458 base::UmaHistogramCustomTimes("Profile.TimeToOpenUserManagerUpTo1min",
459 time_to_open,
460 base::TimeDelta::FromMilliseconds(1),
461 base::TimeDelta::FromMinutes(1), 50);
462 }
463
464 #if defined(OS_ANDROID)
LogProfileAndroidAccountManagementMenu(ProfileAndroidAccountManagementMenu metric,signin::GAIAServiceType gaia_service)465 void ProfileMetrics::LogProfileAndroidAccountManagementMenu(
466 ProfileAndroidAccountManagementMenu metric,
467 signin::GAIAServiceType gaia_service) {
468 // The first parameter to the histogram needs to be literal, because of the
469 // optimized implementation of |base::UmaHistogramEnumeration|. Do not attempt
470 // to refactor.
471 switch (gaia_service) {
472 case signin::GAIA_SERVICE_TYPE_NONE:
473 base::UmaHistogramEnumeration(
474 "Profile.AndroidAccountManagementMenu.NonGAIA", metric,
475 NUM_PROFILE_ANDROID_ACCOUNT_MANAGEMENT_MENU_METRICS);
476 break;
477 case signin::GAIA_SERVICE_TYPE_SIGNOUT:
478 base::UmaHistogramEnumeration(
479 "Profile.AndroidAccountManagementMenu.GAIASignout", metric,
480 NUM_PROFILE_ANDROID_ACCOUNT_MANAGEMENT_MENU_METRICS);
481 break;
482 case signin::GAIA_SERVICE_TYPE_INCOGNITO:
483 base::UmaHistogramEnumeration(
484 "Profile.AndroidAccountManagementMenu.GAIASignoutIncognito", metric,
485 NUM_PROFILE_ANDROID_ACCOUNT_MANAGEMENT_MENU_METRICS);
486 break;
487 case signin::GAIA_SERVICE_TYPE_ADDSESSION:
488 base::UmaHistogramEnumeration(
489 "Profile.AndroidAccountManagementMenu.GAIAAddSession", metric,
490 NUM_PROFILE_ANDROID_ACCOUNT_MANAGEMENT_MENU_METRICS);
491 break;
492 case signin::GAIA_SERVICE_TYPE_SIGNUP:
493 base::UmaHistogramEnumeration(
494 "Profile.AndroidAccountManagementMenu.GAIASignup", metric,
495 NUM_PROFILE_ANDROID_ACCOUNT_MANAGEMENT_MENU_METRICS);
496 break;
497 case signin::GAIA_SERVICE_TYPE_DEFAULT:
498 base::UmaHistogramEnumeration(
499 "Profile.AndroidAccountManagementMenu.GAIADefault", metric,
500 NUM_PROFILE_ANDROID_ACCOUNT_MANAGEMENT_MENU_METRICS);
501 break;
502 }
503 }
504 #endif // defined(OS_ANDROID)
505
LogProfileLaunch(Profile * profile)506 void ProfileMetrics::LogProfileLaunch(Profile* profile) {
507 if (profile->IsSupervised()) {
508 base::RecordAction(
509 base::UserMetricsAction("ManagedMode_NewManagedUserWindow"));
510 }
511 }
512
LogProfileUpdate(const base::FilePath & profile_path)513 void ProfileMetrics::LogProfileUpdate(const base::FilePath& profile_path) {
514 base::UmaHistogramEnumeration("Profile.Update", GetProfileType(profile_path));
515 }
516