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.
5 #include "chrome/browser/metrics/signin_status_metrics_provider.h"
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"
21 #if !defined(OS_ANDROID)
22 #include "chrome/browser/ui/browser_finder.h"
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
,
33 USER_SIGNIN_WHEN_STATUS_UNKNOWN
,
34 USER_SIGNOUT_WHEN_STATUS_UNKNOWN
,
35 TRY_TO_OVERRIDE_ERROR_STATUS
,
36 COMPUTE_SIGNIN_STATUS_MAX
,
39 void RecordComputeSigninStatusHistogram(ComputeSigninStatus status
) {
40 UMA_HISTOGRAM_ENUMERATION("UMA.ComputeCurrentSigninStatus", status
,
41 COMPUTE_SIGNIN_STATUS_MAX
);
46 SigninStatusMetricsProvider::SigninStatusMetricsProvider(bool is_test
)
47 : signin_status_(UNKNOWN_SIGNIN_STATUS
),
48 scoped_observer_(this),
50 weak_ptr_factory_(this) {
54 // Postpone the initialization until all threads are created.
55 base::MessageLoop::current()->PostTask(
57 base::Bind(&SigninStatusMetricsProvider::Initialize
,
58 weak_ptr_factory_
.GetWeakPtr()));
61 SigninStatusMetricsProvider::~SigninStatusMetricsProvider() {
65 #if !defined(OS_ANDROID)
66 BrowserList::RemoveObserver(this);
69 SigninManagerFactory
* factory
= SigninManagerFactory::GetInstance();
71 factory
->RemoveObserver(this);
74 void SigninStatusMetricsProvider::RecordSigninStatusHistogram() {
75 UMA_HISTOGRAM_ENUMERATION(
76 "UMA.ProfileSignInStatus", signin_status_
, SIGNIN_STATUS_MAX
);
77 // After a histogram value is recorded, a new UMA session will be started, so
78 // we need to re-check the current sign-in status regardless of the previous
79 // recorded |signin_status_| value.
80 signin_status_
= UNKNOWN_SIGNIN_STATUS
;
81 ComputeCurrentSigninStatus();
85 SigninStatusMetricsProvider
* SigninStatusMetricsProvider::CreateInstance() {
86 return new SigninStatusMetricsProvider(false);
89 void SigninStatusMetricsProvider::OnBrowserAdded(Browser
* browser
) {
90 if (signin_status_
== MIXED_SIGNIN_STATUS
)
93 SigninManager
* manager
= SigninManagerFactory::GetForProfile(
96 // Nothing will change if the opened browser is in incognito mode.
100 const bool signed_in
= manager
->IsAuthenticated();
101 UpdateStatusWhenBrowserAdded(signed_in
);
104 void SigninStatusMetricsProvider::SigninManagerCreated(
105 SigninManagerBase
* manager
) {
106 // Whenever a new profile is created, a new SigninManagerBase will be created
107 // for it. This ensures that all sign-in or sign-out actions of all opened
108 // profiles are being monitored.
109 scoped_observer_
.Add(manager
);
111 // If the status is unknown, it means this is the first created
112 // SigninManagerBase and the corresponding profile should be the only opened
114 if (signin_status_
== UNKNOWN_SIGNIN_STATUS
) {
115 size_t signed_in_count
=
116 manager
->IsAuthenticated() ? 1 : 0;
117 UpdateInitialSigninStatus(1, signed_in_count
);
121 void SigninStatusMetricsProvider::SigninManagerShutdown(
122 SigninManagerBase
* manager
) {
123 if (scoped_observer_
.IsObserving(manager
))
124 scoped_observer_
.Remove(manager
);
127 void SigninStatusMetricsProvider::GoogleSigninSucceeded(
128 const std::string
& account_id
,
129 const std::string
& username
,
130 const std::string
& password
) {
131 if (signin_status_
== ALL_PROFILES_NOT_SIGNED_IN
) {
132 SetSigninStatus(MIXED_SIGNIN_STATUS
);
133 } else if (signin_status_
== UNKNOWN_SIGNIN_STATUS
) {
134 // There should have at least one browser opened if the user can sign in, so
135 // signin_status_ value should not be unknown.
136 SetSigninStatus(ERROR_GETTING_SIGNIN_STATUS
);
137 RecordComputeSigninStatusHistogram(USER_SIGNIN_WHEN_STATUS_UNKNOWN
);
141 void SigninStatusMetricsProvider::GoogleSignedOut(const std::string
& account_id
,
142 const std::string
& username
) {
143 if (signin_status_
== ALL_PROFILES_SIGNED_IN
) {
144 SetSigninStatus(MIXED_SIGNIN_STATUS
);
145 } else if (signin_status_
== UNKNOWN_SIGNIN_STATUS
) {
146 // There should have at least one browser opened if the user can sign out,
147 // so signin_status_ value should not be unknown.
148 SetSigninStatus(ERROR_GETTING_SIGNIN_STATUS
);
149 RecordComputeSigninStatusHistogram(USER_SIGNOUT_WHEN_STATUS_UNKNOWN
);
153 void SigninStatusMetricsProvider::Initialize() {
155 #if !defined(OS_ANDROID)
156 // On Android, there is always only one profile in any situation, opening new
157 // windows (which is possible with only some Android devices) will not change
158 // the opened profiles signin status.
159 BrowserList::AddObserver(this);
161 SigninManagerFactory::GetInstance()->AddObserver(this);
163 // Start observing all already-created SigninManagers.
164 ProfileManager
* profile_manager
= g_browser_process
->profile_manager();
165 std::vector
<Profile
*> profiles
= profile_manager
->GetLoadedProfiles();
166 for (size_t i
= 0; i
< profiles
.size(); ++i
) {
167 SigninManager
* manager
= SigninManagerFactory::GetForProfileIfExists(
170 DCHECK(!scoped_observer_
.IsObserving(manager
));
171 scoped_observer_
.Add(manager
);
175 // It is possible that when this object is created, no SigninManager is
176 // created yet, for example, when Chrome is opened for the first time after
177 // installation on desktop, or when Chrome on Android is loaded into memory.
178 if (profiles
.empty()) {
179 SetSigninStatus(UNKNOWN_SIGNIN_STATUS
);
181 ComputeCurrentSigninStatus();
185 void SigninStatusMetricsProvider::UpdateInitialSigninStatus(
187 size_t signed_in_profiles_count
) {
188 // total_count is known to be bigger than 0.
189 if (signed_in_profiles_count
== 0) {
190 SetSigninStatus(ALL_PROFILES_NOT_SIGNED_IN
);
191 } else if (total_count
== signed_in_profiles_count
) {
192 SetSigninStatus(ALL_PROFILES_SIGNED_IN
);
194 SetSigninStatus(MIXED_SIGNIN_STATUS
);
198 void SigninStatusMetricsProvider::UpdateStatusWhenBrowserAdded(bool signed_in
) {
199 #if !defined(OS_ANDROID)
200 if ((signin_status_
== ALL_PROFILES_NOT_SIGNED_IN
&& signed_in
) ||
201 (signin_status_
== ALL_PROFILES_SIGNED_IN
&& !signed_in
)) {
202 SetSigninStatus(MIXED_SIGNIN_STATUS
);
203 } else if (signin_status_
== UNKNOWN_SIGNIN_STATUS
) {
204 // If when function RecordSigninStatusHistogram() is called, Chrome is
205 // running in the background with no browser window opened, |signin_status_|
206 // will be reset to |UNKNOWN_SIGNIN_STATUS|. Then this newly added browser
207 // is the only opened browser/profile and its signin status represents
209 SetSigninStatus(signed_in
? ALL_PROFILES_SIGNED_IN
:
210 ALL_PROFILES_NOT_SIGNED_IN
);
215 void SigninStatusMetricsProvider::ComputeCurrentSigninStatus() {
216 ProfileManager
* profile_manager
= g_browser_process
->profile_manager();
217 std::vector
<Profile
*> profile_list
= profile_manager
->GetLoadedProfiles();
219 size_t opened_profiles_count
= 0;
220 size_t signed_in_profiles_count
= 0;
222 for (size_t i
= 0; i
< profile_list
.size(); ++i
) {
223 #if !defined(OS_ANDROID)
224 if (chrome::GetTotalBrowserCountForProfile(profile_list
[i
]) == 0) {
225 // The profile is loaded, but there's no opened browser for this profile.
229 opened_profiles_count
++;
230 SigninManager
* manager
= SigninManagerFactory::GetForProfile(
231 profile_list
[i
]->GetOriginalProfile());
232 if (manager
&& manager
->IsAuthenticated())
233 signed_in_profiles_count
++;
236 RecordComputeSigninStatusHistogram(ENTERED_COMPUTE_SIGNIN_STATUS
);
237 if (profile_list
.empty()) {
238 // This should not happen. If it does, record it in histogram.
239 RecordComputeSigninStatusHistogram(ERROR_NO_PROFILE_FOUND
);
240 SetSigninStatus(ERROR_GETTING_SIGNIN_STATUS
);
241 } else if (opened_profiles_count
== 0) {
242 // The code indicates that Chrome is running in the background but no
243 // browser window is opened.
244 RecordComputeSigninStatusHistogram(NO_BROWSER_OPENED
);
245 SetSigninStatus(UNKNOWN_SIGNIN_STATUS
);
247 UpdateInitialSigninStatus(opened_profiles_count
, signed_in_profiles_count
);
251 void SigninStatusMetricsProvider::SetSigninStatus(
252 SigninStatusMetricsProvider::ProfilesSigninStatus new_status
) {
253 if (signin_status_
== ERROR_GETTING_SIGNIN_STATUS
) {
254 RecordComputeSigninStatusHistogram(TRY_TO_OVERRIDE_ERROR_STATUS
);
257 signin_status_
= new_status
;
260 SigninStatusMetricsProvider::ProfilesSigninStatus
261 SigninStatusMetricsProvider::GetSigninStatusForTesting() {
262 return signin_status_
;