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. 4 5#include "chrome/browser/signin/easy_unlock_service_signin_chromeos.h" 6 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/thread_task_runner_handle.h" 13#include "base/time/time.h" 14#include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_key_manager.h" 15#include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_metrics.h" 16#include "chrome/browser/chromeos/login/session/user_session_manager.h" 17#include "chromeos/login/auth/user_context.h" 18 19namespace { 20 21// The maximum allowed backoff interval when waiting for cryptohome to start. 22uint32 kMaxCryptohomeBackoffIntervalMs = 10000u; 23 24// If the data load fails, the initial interval after which the load will be 25// retried. Further intervals will exponentially increas by factor 2. 26uint32 kInitialCryptohomeBackoffIntervalMs = 200u; 27 28// Calculates the backoff interval that should be used next. 29// |backoff| The last backoff interval used. 30uint32 GetNextBackoffInterval(uint32 backoff) { 31 if (backoff == 0u) 32 return kInitialCryptohomeBackoffIntervalMs; 33 return backoff * 2; 34} 35 36void LoadDataForUser( 37 const std::string& user_id, 38 uint32 backoff_ms, 39 const chromeos::EasyUnlockKeyManager::GetDeviceDataListCallback& callback); 40 41// Callback passed to |LoadDataForUser()|. 42// If |LoadDataForUser| function succeeded, it invokes |callback| with the 43// results. 44// If |LoadDataForUser| failed and further retries are allowed, schedules new 45// |LoadDataForUser| call with some backoff. If no further retires are allowed, 46// it invokes |callback| with the |LoadDataForUser| results. 47void RetryDataLoadOnError( 48 const std::string& user_id, 49 uint32 backoff_ms, 50 const chromeos::EasyUnlockKeyManager::GetDeviceDataListCallback& callback, 51 bool success, 52 const chromeos::EasyUnlockDeviceKeyDataList& data_list) { 53 if (success) { 54 callback.Run(success, data_list); 55 return; 56 } 57 58 uint32 next_backoff_ms = GetNextBackoffInterval(backoff_ms); 59 if (next_backoff_ms > kMaxCryptohomeBackoffIntervalMs) { 60 callback.Run(false, data_list); 61 return; 62 } 63 64 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( 65 FROM_HERE, 66 base::Bind(&LoadDataForUser, user_id, next_backoff_ms, callback), 67 base::TimeDelta::FromMilliseconds(next_backoff_ms)); 68} 69 70// Loads device data list associated with the user's Easy unlock keys. 71void LoadDataForUser( 72 const std::string& user_id, 73 uint32 backoff_ms, 74 const chromeos::EasyUnlockKeyManager::GetDeviceDataListCallback& callback) { 75 chromeos::EasyUnlockKeyManager* key_manager = 76 chromeos::UserSessionManager::GetInstance()->GetEasyUnlockKeyManager(); 77 DCHECK(key_manager); 78 79 key_manager->GetDeviceDataList( 80 chromeos::UserContext(user_id), 81 base::Bind(&RetryDataLoadOnError, user_id, backoff_ms, callback)); 82} 83 84} // namespace 85 86EasyUnlockServiceSignin::UserData::UserData() 87 : state(EasyUnlockServiceSignin::USER_DATA_STATE_INITIAL) { 88} 89 90EasyUnlockServiceSignin::UserData::~UserData() {} 91 92EasyUnlockServiceSignin::EasyUnlockServiceSignin(Profile* profile) 93 : EasyUnlockService(profile), 94 allow_cryptohome_backoff_(true), 95 service_active_(false), 96 weak_ptr_factory_(this) { 97} 98 99EasyUnlockServiceSignin::~EasyUnlockServiceSignin() { 100} 101 102EasyUnlockService::Type EasyUnlockServiceSignin::GetType() const { 103 return EasyUnlockService::TYPE_SIGNIN; 104} 105 106std::string EasyUnlockServiceSignin::GetUserEmail() const { 107 return user_id_; 108} 109 110void EasyUnlockServiceSignin::LaunchSetup() { 111 NOTREACHED(); 112} 113 114const base::DictionaryValue* EasyUnlockServiceSignin::GetPermitAccess() const { 115 return NULL; 116} 117 118void EasyUnlockServiceSignin::SetPermitAccess( 119 const base::DictionaryValue& permit) { 120 NOTREACHED(); 121} 122 123void EasyUnlockServiceSignin::ClearPermitAccess() { 124 NOTREACHED(); 125} 126 127const base::ListValue* EasyUnlockServiceSignin::GetRemoteDevices() const { 128 const UserData* data = FindLoadedDataForCurrentUser(); 129 if (!data) 130 return NULL; 131 return &data->remote_devices_value; 132} 133 134void EasyUnlockServiceSignin::SetRemoteDevices( 135 const base::ListValue& devices) { 136 NOTREACHED(); 137} 138 139void EasyUnlockServiceSignin::ClearRemoteDevices() { 140 NOTREACHED(); 141} 142 143void EasyUnlockServiceSignin::RunTurnOffFlow() { 144 NOTREACHED(); 145} 146 147void EasyUnlockServiceSignin::ResetTurnOffFlow() { 148 NOTREACHED(); 149} 150 151EasyUnlockService::TurnOffFlowStatus 152 EasyUnlockServiceSignin::GetTurnOffFlowStatus() const { 153 return EasyUnlockService::IDLE; 154} 155 156std::string EasyUnlockServiceSignin::GetChallenge() const { 157 const UserData* data = FindLoadedDataForCurrentUser(); 158 // TODO(xiyuan): Use correct remote device instead of hard coded first one. 159 uint32 device_index = 0; 160 if (!data || data->devices.size() <= device_index) 161 return std::string(); 162 return data->devices[device_index].challenge; 163} 164 165std::string EasyUnlockServiceSignin::GetWrappedSecret() const { 166 const UserData* data = FindLoadedDataForCurrentUser(); 167 // TODO(xiyuan): Use correct remote device instead of hard coded first one. 168 uint32 device_index = 0; 169 if (!data || data->devices.size() <= device_index) 170 return std::string(); 171 return data->devices[device_index].wrapped_secret; 172} 173 174void EasyUnlockServiceSignin::RecordEasySignInOutcome( 175 const std::string& user_id, 176 bool success) const { 177 DCHECK_EQ(GetUserEmail(), user_id); 178 179 chromeos::RecordEasyUnlockLoginEvent(success 180 ? chromeos::EASY_SIGN_IN_SUCCESS 181 : chromeos::EASY_SIGN_IN_FAILURE); 182 VLOG(1) << "Easy sign-in " << (success ? "success" : "failure"); 183} 184 185void EasyUnlockServiceSignin::RecordPasswordLoginEvent( 186 const std::string& user_id) const { 187 DCHECK_EQ(GetUserEmail(), user_id); 188 189 chromeos::EasyUnlockLoginEvent event = 190 chromeos::EASY_SIGN_IN_LOGIN_EVENT_COUNT; 191 if (!GetRemoteDevices() || 192 GetHardlockState() == EasyUnlockScreenlockStateHandler::NO_PAIRING) { 193 event = chromeos::PASSWORD_SIGN_IN_NO_PAIRING; 194 } else if (GetHardlockState() == 195 EasyUnlockScreenlockStateHandler::PAIRING_CHANGED) { 196 event = chromeos::PASSWORD_SIGN_IN_PAIRING_CHANGED; 197 } else if (GetHardlockState() == 198 EasyUnlockScreenlockStateHandler::USER_HARDLOCK) { 199 event = chromeos::PASSWORD_SIGN_IN_USER_HARDLOCK; 200 } else if (!screenlock_state_handler()) { 201 event = chromeos::PASSWORD_SIGN_IN_SERVICE_NOT_ACTIVE; 202 } else { 203 switch (screenlock_state_handler()->state()) { 204 case EasyUnlockScreenlockStateHandler::STATE_INACTIVE: 205 event = chromeos::PASSWORD_SIGN_IN_SERVICE_NOT_ACTIVE; 206 break; 207 case EasyUnlockScreenlockStateHandler::STATE_NO_BLUETOOTH: 208 event = chromeos::PASSWORD_SIGN_IN_NO_BLUETOOTH; 209 break; 210 case EasyUnlockScreenlockStateHandler::STATE_BLUETOOTH_CONNECTING: 211 event = chromeos::PASSWORD_SIGN_IN_BLUETOOTH_CONNECTING; 212 break; 213 case EasyUnlockScreenlockStateHandler::STATE_NO_PHONE: 214 event = chromeos::PASSWORD_SIGN_IN_NO_PHONE; 215 break; 216 case EasyUnlockScreenlockStateHandler::STATE_PHONE_NOT_AUTHENTICATED: 217 event = chromeos::PASSWORD_SIGN_IN_PHONE_NOT_AUTHENTICATED; 218 break; 219 case EasyUnlockScreenlockStateHandler::STATE_PHONE_LOCKED: 220 event = chromeos::PASSWORD_SIGN_IN_PHONE_LOCKED; 221 break; 222 case EasyUnlockScreenlockStateHandler::STATE_PHONE_UNLOCKABLE: 223 event = chromeos::PASSWORD_SIGN_IN_PHONE_NOT_LOCKABLE; 224 break; 225 case EasyUnlockScreenlockStateHandler::STATE_PHONE_NOT_NEARBY: 226 event = chromeos::PASSWORD_SIGN_IN_PHONE_NOT_NEARBY; 227 break; 228 case EasyUnlockScreenlockStateHandler::STATE_PHONE_UNSUPPORTED: 229 event = chromeos::PASSWORD_SIGN_IN_PHONE_UNSUPPORTED; 230 break; 231 case EasyUnlockScreenlockStateHandler::STATE_AUTHENTICATED: 232 event = chromeos::PASSWORD_SIGN_IN_WITH_AUTHENTICATED_PHONE; 233 break; 234 } 235 } 236 237 chromeos::RecordEasyUnlockLoginEvent(event); 238 VLOG(1) << "EasySignIn password login event, event=" << event; 239} 240 241void EasyUnlockServiceSignin::InitializeInternal() { 242 if (chromeos::LoginState::Get()->IsUserLoggedIn()) 243 return; 244 245 service_active_ = true; 246 247 chromeos::LoginState::Get()->AddObserver(this); 248 ScreenlockBridge* screenlock_bridge = ScreenlockBridge::Get(); 249 screenlock_bridge->AddObserver(this); 250 if (!screenlock_bridge->focused_user_id().empty()) 251 OnFocusedUserChanged(screenlock_bridge->focused_user_id()); 252} 253 254void EasyUnlockServiceSignin::ShutdownInternal() { 255 if (!service_active_) 256 return; 257 service_active_ = false; 258 259 weak_ptr_factory_.InvalidateWeakPtrs(); 260 ScreenlockBridge::Get()->RemoveObserver(this); 261 chromeos::LoginState::Get()->RemoveObserver(this); 262 STLDeleteContainerPairSecondPointers(user_data_.begin(), user_data_.end()); 263 user_data_.clear(); 264} 265 266bool EasyUnlockServiceSignin::IsAllowedInternal() { 267 return service_active_ && 268 !user_id_.empty() && 269 !chromeos::LoginState::Get()->IsUserLoggedIn(); 270} 271 272void EasyUnlockServiceSignin::OnScreenDidLock() { 273 // Update initial UI is when the account picker on login screen is ready. 274 ShowInitialUserState(); 275} 276 277void EasyUnlockServiceSignin::OnScreenDidUnlock() { 278 Shutdown(); 279} 280 281void EasyUnlockServiceSignin::OnFocusedUserChanged(const std::string& user_id) { 282 if (user_id_ == user_id) 283 return; 284 285 // Setting or clearing the user_id may changed |IsAllowed| value, so in these 286 // cases update the app state. Otherwise, it's enough to notify the app the 287 // user data has been updated. 288 bool should_update_app_state = user_id_.empty() != user_id.empty(); 289 user_id_ = user_id; 290 291 ResetScreenlockState(); 292 ShowInitialUserState(); 293 294 if (should_update_app_state) { 295 UpdateAppState(); 296 } else { 297 NotifyUserUpdated(); 298 } 299 300 LoadCurrentUserDataIfNeeded(); 301} 302 303void EasyUnlockServiceSignin::LoggedInStateChanged() { 304 if (!chromeos::LoginState::Get()->IsUserLoggedIn()) 305 return; 306 UnloadApp(); 307} 308 309void EasyUnlockServiceSignin::LoadCurrentUserDataIfNeeded() { 310 if (user_id_.empty() || !service_active_) 311 return; 312 313 std::map<std::string, UserData*>::iterator it = user_data_.find(user_id_); 314 if (it == user_data_.end()) 315 user_data_.insert(std::make_pair(user_id_, new UserData())); 316 317 UserData* data = user_data_[user_id_]; 318 319 if (data->state != USER_DATA_STATE_INITIAL) 320 return; 321 data->state = USER_DATA_STATE_LOADING; 322 323 LoadDataForUser( 324 user_id_, 325 allow_cryptohome_backoff_ ? 0u : kMaxCryptohomeBackoffIntervalMs, 326 base::Bind(&EasyUnlockServiceSignin::OnUserDataLoaded, 327 weak_ptr_factory_.GetWeakPtr(), 328 user_id_)); 329} 330 331void EasyUnlockServiceSignin::OnUserDataLoaded( 332 const std::string& user_id, 333 bool success, 334 const chromeos::EasyUnlockDeviceKeyDataList& devices) { 335 allow_cryptohome_backoff_ = false; 336 337 UserData* data = user_data_[user_id_]; 338 data->state = USER_DATA_STATE_LOADED; 339 if (success) { 340 data->devices = devices; 341 chromeos::EasyUnlockKeyManager::DeviceDataListToRemoteDeviceList( 342 user_id, devices, &data->remote_devices_value); 343 } 344 345 // If the fetched data belongs to the currently focused user, notify the app 346 // that it has to refresh it's user data. 347 if (user_id == user_id_) 348 NotifyUserUpdated(); 349} 350 351const EasyUnlockServiceSignin::UserData* 352 EasyUnlockServiceSignin::FindLoadedDataForCurrentUser() const { 353 if (user_id_.empty()) 354 return NULL; 355 356 std::map<std::string, UserData*>::const_iterator it = 357 user_data_.find(user_id_); 358 if (it == user_data_.end()) 359 return NULL; 360 if (it->second->state != USER_DATA_STATE_LOADED) 361 return NULL; 362 return it->second; 363} 364