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"
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"
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
) {
35 return kInitialCryptohomeBackoffIntervalMs
;
40 const std::string
& user_id
,
42 const chromeos::EasyUnlockKeyManager::GetDeviceDataListCallback
& callback
);
44 // Callback passed to |LoadDataForUser()|.
45 // If |LoadDataForUser| function succeeded, it invokes |callback| with the
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
,
53 const chromeos::EasyUnlockKeyManager::GetDeviceDataListCallback
& callback
,
55 const chromeos::EasyUnlockDeviceKeyDataList
& data_list
) {
57 callback
.Run(success
, data_list
);
61 uint32 next_backoff_ms
= GetNextBackoffInterval(backoff_ms
);
62 if (next_backoff_ms
> kMaxCryptohomeBackoffIntervalMs
) {
63 callback
.Run(false, data_list
);
67 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
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.
75 const std::string
& user_id
,
77 const chromeos::EasyUnlockKeyManager::GetDeviceDataListCallback
& callback
) {
78 chromeos::EasyUnlockKeyManager
* key_manager
=
79 chromeos::UserSessionManager::GetInstance()->GetEasyUnlockKeyManager();
82 key_manager
->GetDeviceDataList(
83 chromeos::UserContext(user_id
),
84 base::Bind(&RetryDataLoadOnError
, user_id
, backoff_ms
, callback
));
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 {
118 void EasyUnlockServiceSignin::LaunchSetup() {
122 const base::DictionaryValue
* EasyUnlockServiceSignin::GetPermitAccess() const {
126 void EasyUnlockServiceSignin::SetPermitAccess(
127 const base::DictionaryValue
& permit
) {
131 void EasyUnlockServiceSignin::ClearPermitAccess() {
135 const base::ListValue
* EasyUnlockServiceSignin::GetRemoteDevices() const {
136 const UserData
* data
= FindLoadedDataForCurrentUser();
139 return &data
->remote_devices_value
;
142 void EasyUnlockServiceSignin::SetRemoteDevices(
143 const base::ListValue
& devices
) {
147 void EasyUnlockServiceSignin::RunTurnOffFlow() {
151 void EasyUnlockServiceSignin::ResetTurnOffFlow() {
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
);
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
196 if (GetUserEmail() != user_id
)
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
) {
212 void EasyUnlockServiceSignin::SetAutoPairingResult(
214 const std::string
& error
) {
218 void EasyUnlockServiceSignin::InitializeInternal() {
219 if (chromeos::LoginState::Get()->IsUserLoggedIn())
222 service_active_
= true;
224 chromeos::LoginState::Get()->AddObserver(this);
225 ScreenlockBridge
* screenlock_bridge
= ScreenlockBridge::Get();
226 screenlock_bridge
->AddObserver(this);
227 if (!screenlock_bridge
->focused_user_id().empty())
228 OnFocusedUserChanged(screenlock_bridge
->focused_user_id());
231 void EasyUnlockServiceSignin::ShutdownInternal() {
232 if (!service_active_
)
234 service_active_
= false;
236 weak_ptr_factory_
.InvalidateWeakPtrs();
237 ScreenlockBridge::Get()->RemoveObserver(this);
238 chromeos::LoginState::Get()->RemoveObserver(this);
239 STLDeleteContainerPairSecondPointers(user_data_
.begin(), user_data_
.end());
243 bool EasyUnlockServiceSignin::IsAllowedInternal() const {
244 return service_active_
&&
246 !chromeos::LoginState::Get()->IsUserLoggedIn();
249 void EasyUnlockServiceSignin::OnWillFinalizeUnlock(bool success
) {
250 // This code path should only be exercised for the lock screen, not for the
255 void EasyUnlockServiceSignin::OnSuspendDone() {
259 void EasyUnlockServiceSignin::OnScreenDidLock(
260 ScreenlockBridge::LockHandler::ScreenType screen_type
) {
261 // In production code, the screen type should always be the signin screen; but
262 // in tests, the screen type might be different.
263 if (screen_type
!= ScreenlockBridge::LockHandler::SIGNIN_SCREEN
)
266 // Update initial UI is when the account picker on login screen is ready.
267 ShowInitialUserState();
268 user_pod_last_focused_timestamp_
= base::TimeTicks::Now();
271 void EasyUnlockServiceSignin::OnScreenDidUnlock(
272 ScreenlockBridge::LockHandler::ScreenType screen_type
) {
273 // In production code, the screen type should always be the signin screen; but
274 // in tests, the screen type might be different.
275 if (screen_type
!= ScreenlockBridge::LockHandler::SIGNIN_SCREEN
)
278 DisableAppWithoutResettingScreenlockState();
283 void EasyUnlockServiceSignin::OnFocusedUserChanged(const std::string
& user_id
) {
284 if (user_id_
== user_id
)
287 // Setting or clearing the user_id may changed |IsAllowed| value, so in these
288 // cases update the app state. Otherwise, it's enough to notify the app the
289 // user data has been updated.
290 bool should_update_app_state
= user_id_
.empty() != user_id
.empty();
292 user_pod_last_focused_timestamp_
= base::TimeTicks::Now();
294 ResetScreenlockState();
295 ShowInitialUserState();
297 if (should_update_app_state
) {
303 LoadCurrentUserDataIfNeeded();
305 // Start loading TPM system token.
306 // The system token will be needed to sign a nonce using TPM private key
307 // during the sign-in protocol.
308 EasyUnlockScreenlockStateHandler::HardlockState hardlock_state
;
309 if (GetPersistedHardlockState(&hardlock_state
) &&
310 hardlock_state
!= EasyUnlockScreenlockStateHandler::NO_PAIRING
) {
311 chromeos::TPMTokenLoader::Get()->EnsureStarted();
315 void EasyUnlockServiceSignin::LoggedInStateChanged() {
316 if (!chromeos::LoginState::Get()->IsUserLoggedIn())
318 DisableAppWithoutResettingScreenlockState();
321 void EasyUnlockServiceSignin::LoadCurrentUserDataIfNeeded() {
322 // TODO(xiyuan): Revisit this when adding tests.
323 if (!base::SysInfo::IsRunningOnChromeOS())
326 if (user_id_
.empty() || !service_active_
)
329 std::map
<std::string
, UserData
*>::iterator it
= user_data_
.find(user_id_
);
330 if (it
== user_data_
.end())
331 user_data_
.insert(std::make_pair(user_id_
, new UserData()));
333 UserData
* data
= user_data_
[user_id_
];
335 if (data
->state
!= USER_DATA_STATE_INITIAL
)
337 data
->state
= USER_DATA_STATE_LOADING
;
341 allow_cryptohome_backoff_
? 0u : kMaxCryptohomeBackoffIntervalMs
,
342 base::Bind(&EasyUnlockServiceSignin::OnUserDataLoaded
,
343 weak_ptr_factory_
.GetWeakPtr(),
347 void EasyUnlockServiceSignin::OnUserDataLoaded(
348 const std::string
& user_id
,
350 const chromeos::EasyUnlockDeviceKeyDataList
& devices
) {
351 allow_cryptohome_backoff_
= false;
353 UserData
* data
= user_data_
[user_id
];
354 data
->state
= USER_DATA_STATE_LOADED
;
356 data
->devices
= devices
;
357 chromeos::EasyUnlockKeyManager::DeviceDataListToRemoteDeviceList(
358 user_id
, devices
, &data
->remote_devices_value
);
360 // User could have a NO_HARDLOCK state but has no remote devices if
361 // previous user session shuts down before
362 // CheckCryptohomeKeysAndMaybeHardlock finishes. Set NO_PAIRING state
363 // and update UI to remove the confusing spinner in this case.
364 EasyUnlockScreenlockStateHandler::HardlockState hardlock_state
;
365 if (devices
.empty() &&
366 GetPersistedHardlockState(&hardlock_state
) &&
367 hardlock_state
== EasyUnlockScreenlockStateHandler::NO_HARDLOCK
) {
368 SetHardlockStateForUser(user_id
,
369 EasyUnlockScreenlockStateHandler::NO_PAIRING
);
373 // If the fetched data belongs to the currently focused user, notify the app
374 // that it has to refresh it's user data.
375 if (user_id
== user_id_
)
379 const EasyUnlockServiceSignin::UserData
*
380 EasyUnlockServiceSignin::FindLoadedDataForCurrentUser() const {
381 if (user_id_
.empty())
384 std::map
<std::string
, UserData
*>::const_iterator it
=
385 user_data_
.find(user_id_
);
386 if (it
== user_data_
.end())
388 if (it
->second
->state
!= USER_DATA_STATE_LOADED
)