summaryrefslogtreecommitdiffstats
path: root/chrome/browser/metrics/signin_status_metrics_provider.cc
blob: a207615a27a1760e8b8b650196c017a70df3c71f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/metrics/signin_status_metrics_provider.h"

#include <string>
#include <vector>

#include "base/bind.h"
#include "base/message_loop/message_loop.h"
#include "base/metrics/histogram.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_info_cache.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_list.h"
#include "components/signin/core/browser/signin_manager.h"

#if !defined(OS_ANDROID)
#include "chrome/browser/ui/browser_finder.h"
#endif

namespace {

// The event of calling function ComputeCurrentSigninStatus and the errors
// occurred during the function execution.
enum ComputeSigninStatus {
  ENTERED_COMPUTE_SIGNIN_STATUS,
  ERROR_NO_PROFILE_FOUND,
  NO_BROWSER_OPENED,
  USER_SIGNIN_WHEN_STATUS_UNKNOWN,
  USER_SIGNOUT_WHEN_STATUS_UNKNOWN,
  TRY_TO_OVERRIDE_ERROR_STATUS,
  COMPUTE_SIGNIN_STATUS_MAX,
};

void RecordComputeSigninStatusHistogram(ComputeSigninStatus status) {
  UMA_HISTOGRAM_ENUMERATION("UMA.ComputeCurrentSigninStatus", status,
                            COMPUTE_SIGNIN_STATUS_MAX);
}

}  // namespace

SigninStatusMetricsProvider::SigninStatusMetricsProvider(bool is_test)
    : signin_status_(UNKNOWN_SIGNIN_STATUS),
      scoped_observer_(this),
      is_test_(is_test),
      weak_ptr_factory_(this) {
  if (is_test_)
    return;

  // Postpone the initialization until all threads are created.
  base::MessageLoop::current()->PostTask(
      FROM_HERE,
      base::Bind(&SigninStatusMetricsProvider::Initialize,
                 weak_ptr_factory_.GetWeakPtr()));
}

SigninStatusMetricsProvider::~SigninStatusMetricsProvider() {
  if (is_test_)
    return;

#if !defined(OS_ANDROID)
  BrowserList::RemoveObserver(this);
#endif

  SigninManagerFactory* factory = SigninManagerFactory::GetInstance();
  if (factory)
    factory->RemoveObserver(this);
}

void SigninStatusMetricsProvider::RecordSigninStatusHistogram() {
  UMA_HISTOGRAM_ENUMERATION(
      "UMA.ProfileSignInStatus", signin_status_, SIGNIN_STATUS_MAX);
  // After a histogram value is recorded, a new UMA session will be started, so
  // we need to re-check the current sign-in status regardless of the previous
  // recorded |signin_status_| value.
  signin_status_ = UNKNOWN_SIGNIN_STATUS;
  ComputeCurrentSigninStatus();
}

// static
SigninStatusMetricsProvider* SigninStatusMetricsProvider::CreateInstance() {
  return new SigninStatusMetricsProvider(false);
}

void SigninStatusMetricsProvider::OnBrowserAdded(Browser* browser) {
  if (signin_status_ == MIXED_SIGNIN_STATUS)
    return;

  SigninManager* manager = SigninManagerFactory::GetForProfile(
      browser->profile());

  // Nothing will change if the opened browser is in incognito mode.
  if (!manager)
    return;

  const bool signed_in = manager->IsAuthenticated();
  UpdateStatusWhenBrowserAdded(signed_in);
}

void SigninStatusMetricsProvider::SigninManagerCreated(
    SigninManagerBase* manager) {
  // Whenever a new profile is created, a new SigninManagerBase will be created
  // for it. This ensures that all sign-in or sign-out actions of all opened
  // profiles are being monitored.
  scoped_observer_.Add(manager);

  // If the status is unknown, it means this is the first created
  // SigninManagerBase and the corresponding profile should be the only opened
  // profile.
  if (signin_status_ == UNKNOWN_SIGNIN_STATUS) {
    size_t signed_in_count =
        manager->IsAuthenticated() ? 1 : 0;
    UpdateInitialSigninStatus(1, signed_in_count);
  }
}

void SigninStatusMetricsProvider::SigninManagerShutdown(
    SigninManagerBase* manager) {
  if (scoped_observer_.IsObserving(manager))
    scoped_observer_.Remove(manager);
}

void SigninStatusMetricsProvider::GoogleSigninSucceeded(
    const std::string& account_id,
    const std::string& username,
    const std::string& password) {
  if (signin_status_ == ALL_PROFILES_NOT_SIGNED_IN) {
    SetSigninStatus(MIXED_SIGNIN_STATUS);
  } else if (signin_status_ == UNKNOWN_SIGNIN_STATUS) {
    // There should have at least one browser opened if the user can sign in, so
    // signin_status_ value should not be unknown.
    SetSigninStatus(ERROR_GETTING_SIGNIN_STATUS);
    RecordComputeSigninStatusHistogram(USER_SIGNIN_WHEN_STATUS_UNKNOWN);
  }
}

void SigninStatusMetricsProvider::GoogleSignedOut(const std::string& account_id,
                                                  const std::string& username) {
  if (signin_status_ == ALL_PROFILES_SIGNED_IN) {
    SetSigninStatus(MIXED_SIGNIN_STATUS);
  } else if (signin_status_ == UNKNOWN_SIGNIN_STATUS) {
    // There should have at least one browser opened if the user can sign out,
    // so signin_status_ value should not be unknown.
    SetSigninStatus(ERROR_GETTING_SIGNIN_STATUS);
    RecordComputeSigninStatusHistogram(USER_SIGNOUT_WHEN_STATUS_UNKNOWN);
  }
}

void SigninStatusMetricsProvider::Initialize() {
  // Add observers.
#if !defined(OS_ANDROID)
  // On Android, there is always only one profile in any situation, opening new
  // windows (which is possible with only some Android devices) will not change
  // the opened profiles signin status.
  BrowserList::AddObserver(this);
#endif
  SigninManagerFactory::GetInstance()->AddObserver(this);

  // Start observing all already-created SigninManagers.
  ProfileManager* profile_manager = g_browser_process->profile_manager();
  std::vector<Profile*> profiles = profile_manager->GetLoadedProfiles();
  for (size_t i = 0; i < profiles.size(); ++i) {
    SigninManager* manager = SigninManagerFactory::GetForProfileIfExists(
        profiles[i]);
    if (manager) {
      DCHECK(!scoped_observer_.IsObserving(manager));
      scoped_observer_.Add(manager);
    }
  }

  // It is possible that when this object is created, no SigninManager is
  // created yet, for example, when Chrome is opened for the first time after
  // installation on desktop, or when Chrome on Android is loaded into memory.
  if (profiles.empty()) {
    SetSigninStatus(UNKNOWN_SIGNIN_STATUS);
  } else {
    ComputeCurrentSigninStatus();
  }
}

void SigninStatusMetricsProvider::UpdateInitialSigninStatus(
    size_t total_count,
    size_t signed_in_profiles_count) {
  // total_count is known to be bigger than 0.
  if (signed_in_profiles_count == 0) {
    SetSigninStatus(ALL_PROFILES_NOT_SIGNED_IN);
  } else if (total_count == signed_in_profiles_count) {
    SetSigninStatus(ALL_PROFILES_SIGNED_IN);
  } else {
    SetSigninStatus(MIXED_SIGNIN_STATUS);
  }
}

void SigninStatusMetricsProvider::UpdateStatusWhenBrowserAdded(bool signed_in) {
#if !defined(OS_ANDROID)
  if ((signin_status_ == ALL_PROFILES_NOT_SIGNED_IN && signed_in) ||
      (signin_status_ == ALL_PROFILES_SIGNED_IN && !signed_in)) {
    SetSigninStatus(MIXED_SIGNIN_STATUS);
  } else if (signin_status_ == UNKNOWN_SIGNIN_STATUS) {
    // If when function RecordSigninStatusHistogram() is called, Chrome is
    // running in the background with no browser window opened, |signin_status_|
    // will be reset to |UNKNOWN_SIGNIN_STATUS|. Then this newly added browser
    // is the only opened browser/profile and its signin status represents
    // the whole status.
    SetSigninStatus(signed_in ? ALL_PROFILES_SIGNED_IN :
                                ALL_PROFILES_NOT_SIGNED_IN);
  }
#endif
}

void SigninStatusMetricsProvider::ComputeCurrentSigninStatus() {
  ProfileManager* profile_manager = g_browser_process->profile_manager();
  std::vector<Profile*> profile_list = profile_manager->GetLoadedProfiles();

  size_t opened_profiles_count = 0;
  size_t signed_in_profiles_count = 0;

  for (size_t i = 0; i < profile_list.size(); ++i) {
#if !defined(OS_ANDROID)
    if (chrome::GetTotalBrowserCountForProfile(profile_list[i]) == 0) {
      // The profile is loaded, but there's no opened browser for this profile.
      continue;
    }
#endif
    opened_profiles_count++;
    SigninManager* manager = SigninManagerFactory::GetForProfile(
        profile_list[i]->GetOriginalProfile());
    if (manager && manager->IsAuthenticated())
      signed_in_profiles_count++;
  }

  RecordComputeSigninStatusHistogram(ENTERED_COMPUTE_SIGNIN_STATUS);
  if (profile_list.empty()) {
    // This should not happen. If it does, record it in histogram.
    RecordComputeSigninStatusHistogram(ERROR_NO_PROFILE_FOUND);
    SetSigninStatus(ERROR_GETTING_SIGNIN_STATUS);
  } else if (opened_profiles_count == 0) {
    // The code indicates that Chrome is running in the background but no
    // browser window is opened.
    RecordComputeSigninStatusHistogram(NO_BROWSER_OPENED);
    SetSigninStatus(UNKNOWN_SIGNIN_STATUS);
  } else {
    UpdateInitialSigninStatus(opened_profiles_count, signed_in_profiles_count);
  }
}

void SigninStatusMetricsProvider::SetSigninStatus(
    SigninStatusMetricsProvider::ProfilesSigninStatus new_status) {
  if (signin_status_ == ERROR_GETTING_SIGNIN_STATUS) {
    RecordComputeSigninStatusHistogram(TRY_TO_OVERRIDE_ERROR_STATUS);
    return;
  }
  signin_status_ = new_status;
}

SigninStatusMetricsProvider::ProfilesSigninStatus
SigninStatusMetricsProvider::GetSigninStatusForTesting() {
  return signin_status_;
}