Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / metrics / signin_status_metrics_provider.cc
blob741684f6094258b8218c6bbb3074a29c3d4d1eed
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"
7 #include <string>
8 #include <vector>
10 #include "base/bind.h"
11 #include "base/location.h"
12 #include "base/metrics/histogram.h"
13 #include "base/single_thread_task_runner.h"
14 #include "base/thread_task_runner_handle.h"
15 #include "chrome/browser/browser_process.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/profiles/profile_info_cache.h"
18 #include "chrome/browser/profiles/profile_manager.h"
19 #include "chrome/browser/ui/browser.h"
20 #include "chrome/browser/ui/browser_list.h"
21 #include "components/signin/core/browser/signin_manager.h"
23 #if !defined(OS_ANDROID) && !defined(OS_IOS)
24 #include "chrome/browser/ui/browser_finder.h"
25 #endif
27 namespace {
29 // The event of calling function ComputeCurrentSigninStatus and the errors
30 // occurred during the function execution.
31 enum ComputeSigninStatus {
32 ENTERED_COMPUTE_SIGNIN_STATUS,
33 ERROR_NO_PROFILE_FOUND,
34 NO_BROWSER_OPENED,
35 USER_SIGNIN_WHEN_STATUS_UNKNOWN,
36 USER_SIGNOUT_WHEN_STATUS_UNKNOWN,
37 TRY_TO_OVERRIDE_ERROR_STATUS,
38 COMPUTE_SIGNIN_STATUS_MAX,
41 void RecordComputeSigninStatusHistogram(ComputeSigninStatus status) {
42 UMA_HISTOGRAM_ENUMERATION("UMA.ComputeCurrentSigninStatus", status,
43 COMPUTE_SIGNIN_STATUS_MAX);
46 } // namespace
48 SigninStatusMetricsProvider::SigninStatusMetricsProvider(bool is_test)
49 : scoped_observer_(this),
50 is_test_(is_test),
51 weak_ptr_factory_(this) {
52 if (is_test_)
53 return;
55 // Postpone the initialization until all threads are created.
56 base::ThreadTaskRunnerHandle::Get()->PostTask(
57 FROM_HERE, base::Bind(&SigninStatusMetricsProvider::Initialize,
58 weak_ptr_factory_.GetWeakPtr()));
61 SigninStatusMetricsProvider::~SigninStatusMetricsProvider() {
62 if (is_test_)
63 return;
64 // TODO(ios): should we provide an implementation of BrowserListObserver
65 // that works on iOS, http://crbug.com/403371
66 #if !defined(OS_ANDROID) && !defined(OS_IOS)
67 BrowserList::RemoveObserver(this);
68 #endif
70 SigninManagerFactory* factory = SigninManagerFactory::GetInstance();
71 if (factory)
72 factory->RemoveObserver(this);
75 void SigninStatusMetricsProvider::ProvideGeneralMetrics(
76 metrics::ChromeUserMetricsExtension* uma_proto) {
77 RecordSigninStatusHistogram(signin_status());
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 ResetSigninStatus();
82 ComputeCurrentSigninStatus();
85 // static
86 SigninStatusMetricsProvider* SigninStatusMetricsProvider::CreateInstance() {
87 return new SigninStatusMetricsProvider(false);
90 void SigninStatusMetricsProvider::OnBrowserAdded(Browser* browser) {
91 if (signin_status() == MIXED_SIGNIN_STATUS)
92 return;
94 SigninManager* manager = SigninManagerFactory::GetForProfile(
95 browser->profile());
97 // Nothing will change if the opened browser is in incognito mode.
98 if (!manager)
99 return;
101 const bool signed_in = manager->IsAuthenticated();
102 UpdateStatusWhenBrowserAdded(signed_in);
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);
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);
122 void SigninStatusMetricsProvider::SigninManagerShutdown(
123 SigninManagerBase* manager) {
124 if (scoped_observer_.IsObserving(manager))
125 scoped_observer_.Remove(manager);
128 void SigninStatusMetricsProvider::GoogleSigninSucceeded(
129 const std::string& account_id,
130 const std::string& username,
131 const std::string& password) {
132 SigninStatus recorded_signin_status = signin_status();
133 if (recorded_signin_status == ALL_PROFILES_NOT_SIGNED_IN) {
134 SetSigninStatus(MIXED_SIGNIN_STATUS);
135 } else if (recorded_signin_status == UNKNOWN_SIGNIN_STATUS) {
136 // There should have at least one browser opened if the user can sign in, so
137 // signin_status_ value should not be unknown.
138 SetSigninStatus(ERROR_GETTING_SIGNIN_STATUS);
139 RecordComputeSigninStatusHistogram(USER_SIGNIN_WHEN_STATUS_UNKNOWN);
143 void SigninStatusMetricsProvider::GoogleSignedOut(const std::string& account_id,
144 const std::string& username) {
145 SigninStatus recorded_signin_status = signin_status();
146 if (recorded_signin_status == ALL_PROFILES_SIGNED_IN) {
147 SetSigninStatus(MIXED_SIGNIN_STATUS);
148 } else if (recorded_signin_status == UNKNOWN_SIGNIN_STATUS) {
149 // There should have at least one browser opened if the user can sign out,
150 // so signin_status_ value should not be unknown.
151 SetSigninStatus(ERROR_GETTING_SIGNIN_STATUS);
152 RecordComputeSigninStatusHistogram(USER_SIGNOUT_WHEN_STATUS_UNKNOWN);
156 void SigninStatusMetricsProvider::Initialize() {
157 // Add observers.
158 // TODO(ios): should we provide an implementation of BrowserListObserver
159 // that works on iOS, http://crbug.com/403371
160 #if !defined(OS_ANDROID) && !defined(OS_IOS)
161 // On Android, there is always only one profile in any situation, opening new
162 // windows (which is possible with only some Android devices) will not change
163 // the opened profiles signin status.
164 BrowserList::AddObserver(this);
165 #endif
166 SigninManagerFactory::GetInstance()->AddObserver(this);
168 // Start observing all already-created SigninManagers.
169 ProfileManager* profile_manager = g_browser_process->profile_manager();
170 std::vector<Profile*> profiles = profile_manager->GetLoadedProfiles();
171 for (size_t i = 0; i < profiles.size(); ++i) {
172 SigninManager* manager = SigninManagerFactory::GetForProfileIfExists(
173 profiles[i]);
174 if (manager) {
175 DCHECK(!scoped_observer_.IsObserving(manager));
176 scoped_observer_.Add(manager);
180 // It is possible that when this object is created, no SigninManager is
181 // created yet, for example, when Chrome is opened for the first time after
182 // installation on desktop, or when Chrome on Android is loaded into memory.
183 if (profiles.empty()) {
184 SetSigninStatus(UNKNOWN_SIGNIN_STATUS);
185 } else {
186 ComputeCurrentSigninStatus();
190 void SigninStatusMetricsProvider::UpdateInitialSigninStatus(
191 size_t total_count,
192 size_t signed_in_profiles_count) {
193 // total_count is known to be bigger than 0.
194 if (signed_in_profiles_count == 0) {
195 SetSigninStatus(ALL_PROFILES_NOT_SIGNED_IN);
196 } else if (total_count == signed_in_profiles_count) {
197 SetSigninStatus(ALL_PROFILES_SIGNED_IN);
198 } else {
199 SetSigninStatus(MIXED_SIGNIN_STATUS);
203 void SigninStatusMetricsProvider::UpdateStatusWhenBrowserAdded(bool signed_in) {
204 #if !defined(OS_ANDROID) && !defined(OS_IOS)
205 SigninStatus recorded_signin_status = signin_status();
206 if ((recorded_signin_status == ALL_PROFILES_NOT_SIGNED_IN && signed_in) ||
207 (recorded_signin_status == ALL_PROFILES_SIGNED_IN && !signed_in)) {
208 SetSigninStatus(MIXED_SIGNIN_STATUS);
209 } else if (recorded_signin_status == UNKNOWN_SIGNIN_STATUS) {
210 // If when function ProvideGeneralMetrics() is called, Chrome is
211 // running in the background with no browser window opened, |signin_status_|
212 // will be reset to |UNKNOWN_SIGNIN_STATUS|. Then this newly added browser
213 // is the only opened browser/profile and its signin status represents
214 // the whole status.
215 SetSigninStatus(signed_in ? ALL_PROFILES_SIGNED_IN :
216 ALL_PROFILES_NOT_SIGNED_IN);
218 #endif
221 void SigninStatusMetricsProvider::ComputeCurrentSigninStatus() {
222 ProfileManager* profile_manager = g_browser_process->profile_manager();
223 std::vector<Profile*> profile_list = profile_manager->GetLoadedProfiles();
225 size_t opened_profiles_count = 0;
226 size_t signed_in_profiles_count = 0;
228 for (size_t i = 0; i < profile_list.size(); ++i) {
229 #if !defined(OS_ANDROID) && !defined(OS_IOS)
230 if (chrome::GetTotalBrowserCountForProfile(profile_list[i]) == 0) {
231 // The profile is loaded, but there's no opened browser for this profile.
232 continue;
234 #endif
235 opened_profiles_count++;
236 SigninManager* manager = SigninManagerFactory::GetForProfile(
237 profile_list[i]->GetOriginalProfile());
238 if (manager && manager->IsAuthenticated())
239 signed_in_profiles_count++;
242 RecordComputeSigninStatusHistogram(ENTERED_COMPUTE_SIGNIN_STATUS);
243 if (profile_list.empty()) {
244 // This should not happen. If it does, record it in histogram.
245 RecordComputeSigninStatusHistogram(ERROR_NO_PROFILE_FOUND);
246 SetSigninStatus(ERROR_GETTING_SIGNIN_STATUS);
247 } else if (opened_profiles_count == 0) {
248 // The code indicates that Chrome is running in the background but no
249 // browser window is opened.
250 RecordComputeSigninStatusHistogram(NO_BROWSER_OPENED);
251 SetSigninStatus(UNKNOWN_SIGNIN_STATUS);
252 } else {
253 UpdateInitialSigninStatus(opened_profiles_count, signed_in_profiles_count);
257 SigninStatusMetricsProvider::SigninStatus
258 SigninStatusMetricsProvider::GetSigninStatusForTesting() {
259 return signin_status();