• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 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/metrics/signin_status_metrics_provider.h"
6 
7 #include <string>
8 #include <vector>
9 
10 #include "base/bind.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/metrics/histogram.h"
13 #include "chrome/browser/browser_process.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/profiles/profile_info_cache.h"
16 #include "chrome/browser/profiles/profile_manager.h"
17 #include "chrome/browser/ui/browser.h"
18 #include "chrome/browser/ui/browser_list.h"
19 #include "components/signin/core/browser/signin_manager.h"
20 
21 #if !defined(OS_ANDROID)
22 #include "chrome/browser/ui/browser_finder.h"
23 #endif
24 
25 namespace {
26 
27 // The event of calling function ComputeCurrentSigninStatus and the errors
28 // occurred during the function execution.
29 enum ComputeSigninStatus {
30   ENTERED_COMPUTE_SIGNIN_STATUS,
31   ERROR_NO_PROFILE_FOUND,
32   NO_BROWSER_OPENED,
33   USER_SIGNIN_WHEN_STATUS_UNKNOWN,
34   USER_SIGNOUT_WHEN_STATUS_UNKNOWN,
35   TRY_TO_OVERRIDE_ERROR_STATUS,
36   COMPUTE_SIGNIN_STATUS_MAX,
37 };
38 
RecordComputeSigninStatusHistogram(ComputeSigninStatus status)39 void RecordComputeSigninStatusHistogram(ComputeSigninStatus status) {
40   UMA_HISTOGRAM_ENUMERATION("UMA.ComputeCurrentSigninStatus", status,
41                             COMPUTE_SIGNIN_STATUS_MAX);
42 }
43 
44 }  // namespace
45 
SigninStatusMetricsProvider(bool is_test)46 SigninStatusMetricsProvider::SigninStatusMetricsProvider(bool is_test)
47     : signin_status_(UNKNOWN_SIGNIN_STATUS),
48       scoped_observer_(this),
49       is_test_(is_test),
50       weak_ptr_factory_(this) {
51   if (is_test_)
52     return;
53 
54   // Postpone the initialization until all threads are created.
55   base::MessageLoop::current()->PostTask(
56       FROM_HERE,
57       base::Bind(&SigninStatusMetricsProvider::Initialize,
58                  weak_ptr_factory_.GetWeakPtr()));
59 }
60 
~SigninStatusMetricsProvider()61 SigninStatusMetricsProvider::~SigninStatusMetricsProvider() {
62   if (is_test_)
63     return;
64 
65 #if !defined(OS_ANDROID)
66   BrowserList::RemoveObserver(this);
67 #endif
68 
69   SigninManagerFactory* factory = SigninManagerFactory::GetInstance();
70   if (factory)
71     factory->RemoveObserver(this);
72 }
73 
ProvideGeneralMetrics(metrics::ChromeUserMetricsExtension * uma_proto)74 void SigninStatusMetricsProvider::ProvideGeneralMetrics(
75     metrics::ChromeUserMetricsExtension* uma_proto) {
76   UMA_HISTOGRAM_ENUMERATION(
77       "UMA.ProfileSignInStatus", signin_status_, SIGNIN_STATUS_MAX);
78   // After a histogram value is recorded, a new UMA session will be started, so
79   // we need to re-check the current sign-in status regardless of the previous
80   // recorded |signin_status_| value.
81   signin_status_ = UNKNOWN_SIGNIN_STATUS;
82   ComputeCurrentSigninStatus();
83 }
84 
85 // static
CreateInstance()86 SigninStatusMetricsProvider* SigninStatusMetricsProvider::CreateInstance() {
87   return new SigninStatusMetricsProvider(false);
88 }
89 
OnBrowserAdded(Browser * browser)90 void SigninStatusMetricsProvider::OnBrowserAdded(Browser* browser) {
91   if (signin_status_ == MIXED_SIGNIN_STATUS)
92     return;
93 
94   SigninManager* manager = SigninManagerFactory::GetForProfile(
95       browser->profile());
96 
97   // Nothing will change if the opened browser is in incognito mode.
98   if (!manager)
99     return;
100 
101   const bool signed_in = manager->IsAuthenticated();
102   UpdateStatusWhenBrowserAdded(signed_in);
103 }
104 
SigninManagerCreated(SigninManagerBase * manager)105 void SigninStatusMetricsProvider::SigninManagerCreated(
106     SigninManagerBase* manager) {
107   // Whenever a new profile is created, a new SigninManagerBase will be created
108   // for it. This ensures that all sign-in or sign-out actions of all opened
109   // profiles are being monitored.
110   scoped_observer_.Add(manager);
111 
112   // If the status is unknown, it means this is the first created
113   // SigninManagerBase and the corresponding profile should be the only opened
114   // profile.
115   if (signin_status_ == UNKNOWN_SIGNIN_STATUS) {
116     size_t signed_in_count =
117         manager->IsAuthenticated() ? 1 : 0;
118     UpdateInitialSigninStatus(1, signed_in_count);
119   }
120 }
121 
SigninManagerShutdown(SigninManagerBase * manager)122 void SigninStatusMetricsProvider::SigninManagerShutdown(
123     SigninManagerBase* manager) {
124   if (scoped_observer_.IsObserving(manager))
125     scoped_observer_.Remove(manager);
126 }
127 
GoogleSigninSucceeded(const std::string & account_id,const std::string & username,const std::string & password)128 void SigninStatusMetricsProvider::GoogleSigninSucceeded(
129     const std::string& account_id,
130     const std::string& username,
131     const std::string& password) {
132   if (signin_status_ == ALL_PROFILES_NOT_SIGNED_IN) {
133     SetSigninStatus(MIXED_SIGNIN_STATUS);
134   } else if (signin_status_ == UNKNOWN_SIGNIN_STATUS) {
135     // There should have at least one browser opened if the user can sign in, so
136     // signin_status_ value should not be unknown.
137     SetSigninStatus(ERROR_GETTING_SIGNIN_STATUS);
138     RecordComputeSigninStatusHistogram(USER_SIGNIN_WHEN_STATUS_UNKNOWN);
139   }
140 }
141 
GoogleSignedOut(const std::string & account_id,const std::string & username)142 void SigninStatusMetricsProvider::GoogleSignedOut(const std::string& account_id,
143                                                   const std::string& username) {
144   if (signin_status_ == ALL_PROFILES_SIGNED_IN) {
145     SetSigninStatus(MIXED_SIGNIN_STATUS);
146   } else if (signin_status_ == UNKNOWN_SIGNIN_STATUS) {
147     // There should have at least one browser opened if the user can sign out,
148     // so signin_status_ value should not be unknown.
149     SetSigninStatus(ERROR_GETTING_SIGNIN_STATUS);
150     RecordComputeSigninStatusHistogram(USER_SIGNOUT_WHEN_STATUS_UNKNOWN);
151   }
152 }
153 
Initialize()154 void SigninStatusMetricsProvider::Initialize() {
155   // Add observers.
156 #if !defined(OS_ANDROID)
157   // On Android, there is always only one profile in any situation, opening new
158   // windows (which is possible with only some Android devices) will not change
159   // the opened profiles signin status.
160   BrowserList::AddObserver(this);
161 #endif
162   SigninManagerFactory::GetInstance()->AddObserver(this);
163 
164   // Start observing all already-created SigninManagers.
165   ProfileManager* profile_manager = g_browser_process->profile_manager();
166   std::vector<Profile*> profiles = profile_manager->GetLoadedProfiles();
167   for (size_t i = 0; i < profiles.size(); ++i) {
168     SigninManager* manager = SigninManagerFactory::GetForProfileIfExists(
169         profiles[i]);
170     if (manager) {
171       DCHECK(!scoped_observer_.IsObserving(manager));
172       scoped_observer_.Add(manager);
173     }
174   }
175 
176   // It is possible that when this object is created, no SigninManager is
177   // created yet, for example, when Chrome is opened for the first time after
178   // installation on desktop, or when Chrome on Android is loaded into memory.
179   if (profiles.empty()) {
180     SetSigninStatus(UNKNOWN_SIGNIN_STATUS);
181   } else {
182     ComputeCurrentSigninStatus();
183   }
184 }
185 
UpdateInitialSigninStatus(size_t total_count,size_t signed_in_profiles_count)186 void SigninStatusMetricsProvider::UpdateInitialSigninStatus(
187     size_t total_count,
188     size_t signed_in_profiles_count) {
189   // total_count is known to be bigger than 0.
190   if (signed_in_profiles_count == 0) {
191     SetSigninStatus(ALL_PROFILES_NOT_SIGNED_IN);
192   } else if (total_count == signed_in_profiles_count) {
193     SetSigninStatus(ALL_PROFILES_SIGNED_IN);
194   } else {
195     SetSigninStatus(MIXED_SIGNIN_STATUS);
196   }
197 }
198 
UpdateStatusWhenBrowserAdded(bool signed_in)199 void SigninStatusMetricsProvider::UpdateStatusWhenBrowserAdded(bool signed_in) {
200 #if !defined(OS_ANDROID)
201   if ((signin_status_ == ALL_PROFILES_NOT_SIGNED_IN && signed_in) ||
202       (signin_status_ == ALL_PROFILES_SIGNED_IN && !signed_in)) {
203     SetSigninStatus(MIXED_SIGNIN_STATUS);
204   } else if (signin_status_ == UNKNOWN_SIGNIN_STATUS) {
205     // If when function ProvideGeneralMetrics() is called, Chrome is
206     // running in the background with no browser window opened, |signin_status_|
207     // will be reset to |UNKNOWN_SIGNIN_STATUS|. Then this newly added browser
208     // is the only opened browser/profile and its signin status represents
209     // the whole status.
210     SetSigninStatus(signed_in ? ALL_PROFILES_SIGNED_IN :
211                                 ALL_PROFILES_NOT_SIGNED_IN);
212   }
213 #endif
214 }
215 
ComputeCurrentSigninStatus()216 void SigninStatusMetricsProvider::ComputeCurrentSigninStatus() {
217   ProfileManager* profile_manager = g_browser_process->profile_manager();
218   std::vector<Profile*> profile_list = profile_manager->GetLoadedProfiles();
219 
220   size_t opened_profiles_count = 0;
221   size_t signed_in_profiles_count = 0;
222 
223   for (size_t i = 0; i < profile_list.size(); ++i) {
224 #if !defined(OS_ANDROID)
225     if (chrome::GetTotalBrowserCountForProfile(profile_list[i]) == 0) {
226       // The profile is loaded, but there's no opened browser for this profile.
227       continue;
228     }
229 #endif
230     opened_profiles_count++;
231     SigninManager* manager = SigninManagerFactory::GetForProfile(
232         profile_list[i]->GetOriginalProfile());
233     if (manager && manager->IsAuthenticated())
234       signed_in_profiles_count++;
235   }
236 
237   RecordComputeSigninStatusHistogram(ENTERED_COMPUTE_SIGNIN_STATUS);
238   if (profile_list.empty()) {
239     // This should not happen. If it does, record it in histogram.
240     RecordComputeSigninStatusHistogram(ERROR_NO_PROFILE_FOUND);
241     SetSigninStatus(ERROR_GETTING_SIGNIN_STATUS);
242   } else if (opened_profiles_count == 0) {
243     // The code indicates that Chrome is running in the background but no
244     // browser window is opened.
245     RecordComputeSigninStatusHistogram(NO_BROWSER_OPENED);
246     SetSigninStatus(UNKNOWN_SIGNIN_STATUS);
247   } else {
248     UpdateInitialSigninStatus(opened_profiles_count, signed_in_profiles_count);
249   }
250 }
251 
SetSigninStatus(SigninStatusMetricsProvider::ProfilesSigninStatus new_status)252 void SigninStatusMetricsProvider::SetSigninStatus(
253     SigninStatusMetricsProvider::ProfilesSigninStatus new_status) {
254   if (signin_status_ == ERROR_GETTING_SIGNIN_STATUS) {
255     RecordComputeSigninStatusHistogram(TRY_TO_OVERRIDE_ERROR_STATUS);
256     return;
257   }
258   signin_status_ = new_status;
259 }
260 
261 SigninStatusMetricsProvider::ProfilesSigninStatus
GetSigninStatusForTesting()262 SigninStatusMetricsProvider::GetSigninStatusForTesting() {
263   return signin_status_;
264 }
265