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_COMPUTE_SIGNIN_STATUS,
32 COMPUTE_SIGNIN_STATUS_MAX,
35 void RecordComputeSigninStatusHistogram(ComputeSigninStatus status) {
36 UMA_HISTOGRAM_ENUMERATION("UMA.ComputeCurrentSigninStatus", status,
37 COMPUTE_SIGNIN_STATUS_MAX);
42 SigninStatusMetricsProvider::SigninStatusMetricsProvider(bool is_test)
43 : signin_status_(UNKNOWN_SIGNIN_STATUS),
44 scoped_observer_(this),
46 weak_ptr_factory_(this) {
50 // Postpone the initialization until all threads are created.
51 base::MessageLoop::current()->PostTask(
53 base::Bind(&SigninStatusMetricsProvider::Initialize,
54 weak_ptr_factory_.GetWeakPtr()));
57 SigninStatusMetricsProvider::~SigninStatusMetricsProvider() {
61 #if !defined(OS_ANDROID)
62 BrowserList::RemoveObserver(this);
65 SigninManagerFactory* factory = SigninManagerFactory::GetInstance();
67 factory->RemoveObserver(this);
70 void SigninStatusMetricsProvider::RecordSigninStatusHistogram() {
71 UMA_HISTOGRAM_ENUMERATION(
72 "UMA.ProfileSignInStatus", signin_status_, SIGNIN_STATUS_MAX);
73 // After a histogram value is recorded, a new UMA session will be started, so
74 // we need to re-check the current sign-in status regardless of the previous
75 // recorded |signin_status_| value.
76 ComputeCurrentSigninStatus();
80 SigninStatusMetricsProvider* SigninStatusMetricsProvider::CreateInstance() {
81 return new SigninStatusMetricsProvider(false);
84 void SigninStatusMetricsProvider::OnBrowserAdded(Browser* browser) {
85 if (signin_status_ == MIXED_SIGNIN_STATUS)
88 SigninManager* manager = SigninManagerFactory::GetForProfile(
91 // Nothing will change if the opened browser is in incognito mode.
95 const bool signed_in = !manager->GetAuthenticatedUsername().empty();
96 UpdateStatusWhenBrowserAdded(signed_in);
99 void SigninStatusMetricsProvider::SigninManagerCreated(
100 SigninManagerBase* manager) {
101 // Whenever a new profile is created, a new SigninManagerBase will be created
102 // for it. This ensures that all sign-in or sign-out actions of all opened
103 // profiles are being monitored.
104 scoped_observer_.Add(manager);
106 // If the status is unknown, it means this is the first created
107 // SigninManagerBase and the corresponding profile should be the only opened
109 if (signin_status_ == UNKNOWN_SIGNIN_STATUS) {
110 size_t signed_in_count =
111 manager->GetAuthenticatedUsername().empty() ? 0 : 1;
112 UpdateInitialSigninStatus(1, signed_in_count);
116 void SigninStatusMetricsProvider::SigninManagerShutdown(
117 SigninManagerBase* manager) {
118 if (scoped_observer_.IsObserving(manager))
119 scoped_observer_.Remove(manager);
122 void SigninStatusMetricsProvider::GoogleSigninSucceeded(
123 const std::string& username,
124 const std::string& password) {
125 if (signin_status_ == ALL_PROFILES_NOT_SIGNED_IN)
126 signin_status_ = MIXED_SIGNIN_STATUS;
129 void SigninStatusMetricsProvider::GoogleSignedOut(const std::string& username) {
130 if (signin_status_ == ALL_PROFILES_SIGNED_IN)
131 signin_status_ = MIXED_SIGNIN_STATUS;
134 void SigninStatusMetricsProvider::Initialize() {
136 #if !defined(OS_ANDROID)
137 // On Android, there is always only one profile in any situation, opening new
138 // windows (which is possible with only some Android devices) will not change
139 // the opened profiles signin status.
140 BrowserList::AddObserver(this);
142 SigninManagerFactory::GetInstance()->AddObserver(this);
144 // Start observing all already-created SigninManagers.
145 ProfileManager* profile_manager = g_browser_process->profile_manager();
146 std::vector<Profile*> profiles = profile_manager->GetLoadedProfiles();
147 for (size_t i = 0; i < profiles.size(); ++i) {
148 SigninManager* manager = SigninManagerFactory::GetForProfileIfExists(
151 DCHECK(!scoped_observer_.IsObserving(manager));
152 scoped_observer_.Add(manager);
156 // It is possible that when this object is created, no SigninManager is
157 // created yet, for example, when Chrome is opened for the first time after
158 // installation on desktop, or when Chrome on Android is loaded into memory.
159 if (profiles.empty()) {
160 signin_status_ = UNKNOWN_SIGNIN_STATUS;
162 ComputeCurrentSigninStatus();
166 void SigninStatusMetricsProvider::UpdateInitialSigninStatus(
168 size_t signed_in_profiles_count) {
169 RecordComputeSigninStatusHistogram(ENTERED_COMPUTE_SIGNIN_STATUS);
171 if (total_count == 0) {
172 // This should never happen. If it does, record it in histogram.
173 RecordComputeSigninStatusHistogram(ERROR_COMPUTE_SIGNIN_STATUS);
174 signin_status_ = UNKNOWN_SIGNIN_STATUS;
175 } else if (signed_in_profiles_count == 0) {
176 signin_status_ = ALL_PROFILES_NOT_SIGNED_IN;
177 } else if (total_count == signed_in_profiles_count) {
178 signin_status_ = ALL_PROFILES_SIGNED_IN;
180 signin_status_ = MIXED_SIGNIN_STATUS;
184 void SigninStatusMetricsProvider::UpdateStatusWhenBrowserAdded(bool signed_in) {
185 #if !defined(OS_ANDROID)
186 if ((signin_status_ == ALL_PROFILES_NOT_SIGNED_IN && signed_in) ||
187 (signin_status_ == ALL_PROFILES_SIGNED_IN && !signed_in)) {
188 signin_status_ = MIXED_SIGNIN_STATUS;
193 void SigninStatusMetricsProvider::ComputeCurrentSigninStatus() {
194 // Get the sign-in status of all currently open profiles. Sign-in status is
195 // indicated by its username. When username is not empty, the profile is
197 ProfileManager* profile_manager = g_browser_process->profile_manager();
198 std::vector<Profile*> profile_list = profile_manager->GetLoadedProfiles();
200 size_t opened_profiles_count = 0;
201 size_t signed_in_profiles_count = 0;
203 for (size_t i = 0; i < profile_list.size(); ++i) {
204 #if !defined(OS_ANDROID)
205 if (chrome::GetTotalBrowserCountForProfile(profile_list[i]) == 0) {
206 // The profile is loaded, but there's no opened browser for this profile.
210 opened_profiles_count++;
211 SigninManager* manager = SigninManagerFactory::GetForProfile(
212 profile_list[i]->GetOriginalProfile());
213 if (manager && !manager->GetAuthenticatedUsername().empty())
214 signed_in_profiles_count++;
216 UpdateInitialSigninStatus(opened_profiles_count, signed_in_profiles_count);
219 SigninStatusMetricsProvider::ProfilesSigninStatus
220 SigninStatusMetricsProvider::GetSigninStatusForTesting() {
221 return signin_status_;