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
20namespace {
21
22const int kMaximumReportedProfileCount = 5;
23const int kMaximumDaysOfDisuse = 4 * 7;  // Should be integral number of weeks.
24
25ProfileMetrics::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
41void UpdateReportedOSProfileStatistics(int active, int signedin) {
42#if defined(OS_WIN)
43  GoogleUpdateSettings::UpdateProfileCounts(active, signedin);
44#endif
45}
46
47void 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
71bool 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
85enum 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
118bool 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
149void 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
164void 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
187void 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
195void 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
291void 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
299void 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
305void 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
313void 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
319void 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
325void ProfileMetrics::LogProfileAuthResult(ProfileAuth metric) {
326  UMA_HISTOGRAM_ENUMERATION("Profile.AuthResult", metric,
327                            NUM_PROFILE_AUTH_METRICS);
328}
329
330void 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
368void ProfileMetrics::LogProfileDelete(bool profile_was_signed_in) {
369  UMA_HISTOGRAM_BOOLEAN("Profile.Delete", profile_was_signed_in);
370}
371
372void 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
379void 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
386void 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)
394void 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
447void 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
459void ProfileMetrics::LogProfileSyncSignIn(const base::FilePath& profile_path) {
460  UMA_HISTOGRAM_ENUMERATION("Profile.SyncSignIn",
461                            GetProfileType(profile_path),
462                            NUM_PROFILE_TYPE_METRICS);
463}
464
465void ProfileMetrics::LogProfileUpdate(const base::FilePath& profile_path) {
466  UMA_HISTOGRAM_ENUMERATION("Profile.Update",
467                            GetProfileType(profile_path),
468                            NUM_PROFILE_TYPE_METRICS);
469}
470