MD Downloads: UI review feedback
[chromium-blink-merge.git] / chrome / browser / signin / easy_unlock_service_signin_chromeos.cc
blobe30ee9298818ddfda639a3aeb4211d0dcbf57893
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/signin/easy_unlock_service_signin_chromeos.h"
7 #include "base/basictypes.h"
8 #include "base/bind.h"
9 #include "base/location.h"
10 #include "base/logging.h"
11 #include "base/stl_util.h"
12 #include "base/sys_info.h"
13 #include "base/thread_task_runner_handle.h"
14 #include "base/time/time.h"
15 #include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_key_manager.h"
16 #include "chrome/browser/chromeos/login/session/user_session_manager.h"
17 #include "chrome/browser/signin/easy_unlock_app_manager.h"
18 #include "chrome/browser/signin/easy_unlock_metrics.h"
19 #include "chromeos/login/auth/user_context.h"
20 #include "chromeos/tpm/tpm_token_loader.h"
22 namespace {
24 // The maximum allowed backoff interval when waiting for cryptohome to start.
25 uint32 kMaxCryptohomeBackoffIntervalMs = 10000u;
27 // If the data load fails, the initial interval after which the load will be
28 // retried. Further intervals will exponentially increas by factor 2.
29 uint32 kInitialCryptohomeBackoffIntervalMs = 200u;
31 // Calculates the backoff interval that should be used next.
32 // |backoff| The last backoff interval used.
33 uint32 GetNextBackoffInterval(uint32 backoff) {
34 if (backoff == 0u)
35 return kInitialCryptohomeBackoffIntervalMs;
36 return backoff * 2;
39 void LoadDataForUser(
40 const std::string& user_id,
41 uint32 backoff_ms,
42 const chromeos::EasyUnlockKeyManager::GetDeviceDataListCallback& callback);
44 // Callback passed to |LoadDataForUser()|.
45 // If |LoadDataForUser| function succeeded, it invokes |callback| with the
46 // results.
47 // If |LoadDataForUser| failed and further retries are allowed, schedules new
48 // |LoadDataForUser| call with some backoff. If no further retires are allowed,
49 // it invokes |callback| with the |LoadDataForUser| results.
50 void RetryDataLoadOnError(
51 const std::string& user_id,
52 uint32 backoff_ms,
53 const chromeos::EasyUnlockKeyManager::GetDeviceDataListCallback& callback,
54 bool success,
55 const chromeos::EasyUnlockDeviceKeyDataList& data_list) {
56 if (success) {
57 callback.Run(success, data_list);
58 return;
61 uint32 next_backoff_ms = GetNextBackoffInterval(backoff_ms);
62 if (next_backoff_ms > kMaxCryptohomeBackoffIntervalMs) {
63 callback.Run(false, data_list);
64 return;
67 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
68 FROM_HERE,
69 base::Bind(&LoadDataForUser, user_id, next_backoff_ms, callback),
70 base::TimeDelta::FromMilliseconds(next_backoff_ms));
73 // Loads device data list associated with the user's Easy unlock keys.
74 void LoadDataForUser(
75 const std::string& user_id,
76 uint32 backoff_ms,
77 const chromeos::EasyUnlockKeyManager::GetDeviceDataListCallback& callback) {
78 chromeos::EasyUnlockKeyManager* key_manager =
79 chromeos::UserSessionManager::GetInstance()->GetEasyUnlockKeyManager();
80 DCHECK(key_manager);
82 key_manager->GetDeviceDataList(
83 chromeos::UserContext(user_id),
84 base::Bind(&RetryDataLoadOnError, user_id, backoff_ms, callback));
87 } // namespace
89 EasyUnlockServiceSignin::UserData::UserData()
90 : state(EasyUnlockServiceSignin::USER_DATA_STATE_INITIAL) {
93 EasyUnlockServiceSignin::UserData::~UserData() {}
95 EasyUnlockServiceSignin::EasyUnlockServiceSignin(Profile* profile)
96 : EasyUnlockService(profile),
97 allow_cryptohome_backoff_(true),
98 service_active_(false),
99 user_pod_last_focused_timestamp_(base::TimeTicks::Now()),
100 weak_ptr_factory_(this) {
103 EasyUnlockServiceSignin::~EasyUnlockServiceSignin() {
106 void EasyUnlockServiceSignin::SetCurrentUser(const std::string& user_id) {
107 OnFocusedUserChanged(user_id);
110 EasyUnlockService::Type EasyUnlockServiceSignin::GetType() const {
111 return EasyUnlockService::TYPE_SIGNIN;
114 std::string EasyUnlockServiceSignin::GetUserEmail() const {
115 return user_id_;
118 void EasyUnlockServiceSignin::LaunchSetup() {
119 NOTREACHED();
122 const base::DictionaryValue* EasyUnlockServiceSignin::GetPermitAccess() const {
123 return NULL;
126 void EasyUnlockServiceSignin::SetPermitAccess(
127 const base::DictionaryValue& permit) {
128 NOTREACHED();
131 void EasyUnlockServiceSignin::ClearPermitAccess() {
132 NOTREACHED();
135 const base::ListValue* EasyUnlockServiceSignin::GetRemoteDevices() const {
136 const UserData* data = FindLoadedDataForCurrentUser();
137 if (!data)
138 return NULL;
139 return &data->remote_devices_value;
142 void EasyUnlockServiceSignin::SetRemoteDevices(
143 const base::ListValue& devices) {
144 NOTREACHED();
147 void EasyUnlockServiceSignin::RunTurnOffFlow() {
148 NOTREACHED();
151 void EasyUnlockServiceSignin::ResetTurnOffFlow() {
152 NOTREACHED();
155 EasyUnlockService::TurnOffFlowStatus
156 EasyUnlockServiceSignin::GetTurnOffFlowStatus() const {
157 return EasyUnlockService::IDLE;
160 std::string EasyUnlockServiceSignin::GetChallenge() const {
161 const UserData* data = FindLoadedDataForCurrentUser();
162 // TODO(xiyuan): Use correct remote device instead of hard coded first one.
163 uint32 device_index = 0;
164 if (!data || data->devices.size() <= device_index)
165 return std::string();
166 return data->devices[device_index].challenge;
169 std::string EasyUnlockServiceSignin::GetWrappedSecret() const {
170 const UserData* data = FindLoadedDataForCurrentUser();
171 // TODO(xiyuan): Use correct remote device instead of hard coded first one.
172 uint32 device_index = 0;
173 if (!data || data->devices.size() <= device_index)
174 return std::string();
175 return data->devices[device_index].wrapped_secret;
178 void EasyUnlockServiceSignin::RecordEasySignInOutcome(
179 const std::string& user_id,
180 bool success) const {
181 DCHECK_EQ(GetUserEmail(), user_id);
183 RecordEasyUnlockSigninEvent(
184 success ? EASY_UNLOCK_SUCCESS : EASY_UNLOCK_FAILURE);
185 if (success) {
186 RecordEasyUnlockSigninDuration(
187 base::TimeTicks::Now() - user_pod_last_focused_timestamp_);
189 DVLOG(1) << "Easy sign-in " << (success ? "success" : "failure");
192 void EasyUnlockServiceSignin::RecordPasswordLoginEvent(
193 const std::string& user_id) const {
194 // This happens during tests, where a user could log in without the user pod
195 // being focused.
196 if (GetUserEmail() != user_id)
197 return;
199 if (!IsEnabled())
200 return;
202 EasyUnlockAuthEvent event = GetPasswordAuthEvent();
203 RecordEasyUnlockSigninEvent(event);
204 DVLOG(1) << "Easy Sign-in password login event, event=" << event;
207 void EasyUnlockServiceSignin::StartAutoPairing(
208 const AutoPairingResultCallback& callback) {
209 NOTREACHED();
212 void EasyUnlockServiceSignin::SetAutoPairingResult(
213 bool success,
214 const std::string& error) {
215 NOTREACHED();
218 void EasyUnlockServiceSignin::InitializeInternal() {
219 if (chromeos::LoginState::Get()->IsUserLoggedIn())
220 return;
222 service_active_ = true;
224 chromeos::LoginState::Get()->AddObserver(this);
225 proximity_auth::ScreenlockBridge* screenlock_bridge =
226 proximity_auth::ScreenlockBridge::Get();
227 screenlock_bridge->AddObserver(this);
228 if (!screenlock_bridge->focused_user_id().empty())
229 OnFocusedUserChanged(screenlock_bridge->focused_user_id());
232 void EasyUnlockServiceSignin::ShutdownInternal() {
233 if (!service_active_)
234 return;
235 service_active_ = false;
237 weak_ptr_factory_.InvalidateWeakPtrs();
238 proximity_auth::ScreenlockBridge::Get()->RemoveObserver(this);
239 chromeos::LoginState::Get()->RemoveObserver(this);
240 STLDeleteContainerPairSecondPointers(user_data_.begin(), user_data_.end());
241 user_data_.clear();
244 bool EasyUnlockServiceSignin::IsAllowedInternal() const {
245 return service_active_ &&
246 !user_id_.empty() &&
247 !chromeos::LoginState::Get()->IsUserLoggedIn();
250 void EasyUnlockServiceSignin::OnWillFinalizeUnlock(bool success) {
251 // This code path should only be exercised for the lock screen, not for the
252 // sign-in screen.
253 NOTREACHED();
256 void EasyUnlockServiceSignin::OnSuspendDone() {
257 // Ignored.
260 void EasyUnlockServiceSignin::OnScreenDidLock(
261 proximity_auth::ScreenlockBridge::LockHandler::ScreenType screen_type) {
262 // In production code, the screen type should always be the signin screen; but
263 // in tests, the screen type might be different.
264 if (screen_type !=
265 proximity_auth::ScreenlockBridge::LockHandler::SIGNIN_SCREEN)
266 return;
268 // Update initial UI is when the account picker on login screen is ready.
269 ShowInitialUserState();
270 user_pod_last_focused_timestamp_ = base::TimeTicks::Now();
273 void EasyUnlockServiceSignin::OnScreenDidUnlock(
274 proximity_auth::ScreenlockBridge::LockHandler::ScreenType screen_type) {
275 // In production code, the screen type should always be the signin screen; but
276 // in tests, the screen type might be different.
277 if (screen_type !=
278 proximity_auth::ScreenlockBridge::LockHandler::SIGNIN_SCREEN)
279 return;
281 DisableAppWithoutResettingScreenlockState();
283 Shutdown();
286 void EasyUnlockServiceSignin::OnFocusedUserChanged(const std::string& user_id) {
287 if (user_id_ == user_id)
288 return;
290 // Setting or clearing the user_id may changed |IsAllowed| value, so in these
291 // cases update the app state. Otherwise, it's enough to notify the app the
292 // user data has been updated.
293 bool should_update_app_state = user_id_.empty() != user_id.empty();
294 user_id_ = user_id;
295 user_pod_last_focused_timestamp_ = base::TimeTicks::Now();
297 ResetScreenlockState();
298 ShowInitialUserState();
300 if (should_update_app_state) {
301 UpdateAppState();
302 } else {
303 NotifyUserUpdated();
306 LoadCurrentUserDataIfNeeded();
308 // Start loading TPM system token.
309 // The system token will be needed to sign a nonce using TPM private key
310 // during the sign-in protocol.
311 EasyUnlockScreenlockStateHandler::HardlockState hardlock_state;
312 if (GetPersistedHardlockState(&hardlock_state) &&
313 hardlock_state != EasyUnlockScreenlockStateHandler::NO_PAIRING) {
314 chromeos::TPMTokenLoader::Get()->EnsureStarted();
318 void EasyUnlockServiceSignin::LoggedInStateChanged() {
319 if (!chromeos::LoginState::Get()->IsUserLoggedIn())
320 return;
321 DisableAppWithoutResettingScreenlockState();
324 void EasyUnlockServiceSignin::LoadCurrentUserDataIfNeeded() {
325 // TODO(xiyuan): Revisit this when adding tests.
326 if (!base::SysInfo::IsRunningOnChromeOS())
327 return;
329 if (user_id_.empty() || !service_active_)
330 return;
332 std::map<std::string, UserData*>::iterator it = user_data_.find(user_id_);
333 if (it == user_data_.end())
334 user_data_.insert(std::make_pair(user_id_, new UserData()));
336 UserData* data = user_data_[user_id_];
338 if (data->state != USER_DATA_STATE_INITIAL)
339 return;
340 data->state = USER_DATA_STATE_LOADING;
342 LoadDataForUser(
343 user_id_,
344 allow_cryptohome_backoff_ ? 0u : kMaxCryptohomeBackoffIntervalMs,
345 base::Bind(&EasyUnlockServiceSignin::OnUserDataLoaded,
346 weak_ptr_factory_.GetWeakPtr(),
347 user_id_));
350 void EasyUnlockServiceSignin::OnUserDataLoaded(
351 const std::string& user_id,
352 bool success,
353 const chromeos::EasyUnlockDeviceKeyDataList& devices) {
354 allow_cryptohome_backoff_ = false;
356 UserData* data = user_data_[user_id];
357 data->state = USER_DATA_STATE_LOADED;
358 if (success) {
359 data->devices = devices;
360 chromeos::EasyUnlockKeyManager::DeviceDataListToRemoteDeviceList(
361 user_id, devices, &data->remote_devices_value);
363 // User could have a NO_HARDLOCK state but has no remote devices if
364 // previous user session shuts down before
365 // CheckCryptohomeKeysAndMaybeHardlock finishes. Set NO_PAIRING state
366 // and update UI to remove the confusing spinner in this case.
367 EasyUnlockScreenlockStateHandler::HardlockState hardlock_state;
368 if (devices.empty() &&
369 GetPersistedHardlockState(&hardlock_state) &&
370 hardlock_state == EasyUnlockScreenlockStateHandler::NO_HARDLOCK) {
371 SetHardlockStateForUser(user_id,
372 EasyUnlockScreenlockStateHandler::NO_PAIRING);
376 // If the fetched data belongs to the currently focused user, notify the app
377 // that it has to refresh it's user data.
378 if (user_id == user_id_)
379 NotifyUserUpdated();
382 const EasyUnlockServiceSignin::UserData*
383 EasyUnlockServiceSignin::FindLoadedDataForCurrentUser() const {
384 if (user_id_.empty())
385 return NULL;
387 std::map<std::string, UserData*>::const_iterator it =
388 user_data_.find(user_id_);
389 if (it == user_data_.end())
390 return NULL;
391 if (it->second->state != USER_DATA_STATE_LOADED)
392 return NULL;
393 return it->second;