Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / components / proximity_auth / unlock_manager.cc
blobe55ad50814ff678267ed6b4f73b051e385a6f75f
1 // Copyright 2015 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 "components/proximity_auth/unlock_manager.h"
7 #include "base/bind.h"
8 #include "base/location.h"
9 #include "base/logging.h"
10 #include "base/thread_task_runner_handle.h"
11 #include "base/time/time.h"
12 #include "components/proximity_auth/logging/logging.h"
13 #include "components/proximity_auth/messenger.h"
14 #include "components/proximity_auth/metrics.h"
15 #include "components/proximity_auth/proximity_auth_client.h"
16 #include "components/proximity_auth/proximity_monitor.h"
17 #include "device/bluetooth/bluetooth_adapter_factory.h"
19 #if defined(OS_CHROMEOS)
20 #include "chromeos/dbus/dbus_thread_manager.h"
22 using chromeos::DBusThreadManager;
23 #endif // defined(OS_CHROMEOS)
25 namespace proximity_auth {
26 namespace {
28 // The maximum amount of time, in seconds, that the unlock manager can stay in
29 // the 'waking up' state after resuming from sleep.
30 const int kWakingUpDurationSecs = 5;
32 // The limit, in seconds, on the elapsed time for an auth attempt. If an auth
33 // attempt exceeds this limit, it will time out and be rejected. This is
34 // provided as a failsafe, in case something goes wrong.
35 const int kAuthAttemptTimeoutSecs = 5;
37 // Returns the remote device's security settings state, for metrics,
38 // corresponding to a remote status update.
39 metrics::RemoteSecuritySettingsState GetRemoteSecuritySettingsState(
40 const RemoteStatusUpdate& status_update) {
41 switch (status_update.secure_screen_lock_state) {
42 case SECURE_SCREEN_LOCK_STATE_UNKNOWN:
43 return metrics::RemoteSecuritySettingsState::UNKNOWN;
45 case SECURE_SCREEN_LOCK_DISABLED:
46 switch (status_update.trust_agent_state) {
47 case TRUST_AGENT_UNSUPPORTED:
48 return metrics::RemoteSecuritySettingsState::
49 SCREEN_LOCK_DISABLED_TRUST_AGENT_UNSUPPORTED;
50 case TRUST_AGENT_DISABLED:
51 return metrics::RemoteSecuritySettingsState::
52 SCREEN_LOCK_DISABLED_TRUST_AGENT_DISABLED;
53 case TRUST_AGENT_ENABLED:
54 return metrics::RemoteSecuritySettingsState::
55 SCREEN_LOCK_DISABLED_TRUST_AGENT_ENABLED;
58 case SECURE_SCREEN_LOCK_ENABLED:
59 switch (status_update.trust_agent_state) {
60 case TRUST_AGENT_UNSUPPORTED:
61 return metrics::RemoteSecuritySettingsState::
62 SCREEN_LOCK_ENABLED_TRUST_AGENT_UNSUPPORTED;
63 case TRUST_AGENT_DISABLED:
64 return metrics::RemoteSecuritySettingsState::
65 SCREEN_LOCK_ENABLED_TRUST_AGENT_DISABLED;
66 case TRUST_AGENT_ENABLED:
67 return metrics::RemoteSecuritySettingsState::
68 SCREEN_LOCK_ENABLED_TRUST_AGENT_ENABLED;
72 NOTREACHED();
73 return metrics::RemoteSecuritySettingsState::UNKNOWN;
76 } // namespace
78 UnlockManager::UnlockManager(ScreenlockType screenlock_type,
79 scoped_ptr<ProximityMonitor> proximity_monitor,
80 ProximityAuthClient* proximity_auth_client)
81 : screenlock_type_(screenlock_type),
82 life_cycle_(nullptr),
83 messenger_(nullptr),
84 proximity_monitor_(proximity_monitor.Pass()),
85 proximity_auth_client_(proximity_auth_client),
86 is_locked_(false),
87 is_attempting_auth_(false),
88 is_waking_up_(false),
89 screenlock_state_(ScreenlockState::INACTIVE),
90 clear_waking_up_state_weak_ptr_factory_(this),
91 reject_auth_attempt_weak_ptr_factory_(this),
92 weak_ptr_factory_(this) {
93 // TODO(isherman): Register for auth attempt notifications, equivalent to the
94 // JavaScript lines:
96 // chrome.screenlockPrivate.onAuthAttempted.addListener(
97 // this.onAuthAttempted_.bind(this));
99 ScreenlockBridge* screenlock_bridge = ScreenlockBridge::Get();
100 screenlock_bridge->AddObserver(this);
101 OnScreenLockedOrUnlocked(screenlock_bridge->IsLocked());
103 #if defined(OS_CHROMEOS)
104 DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver(this);
105 #endif // defined(OS_CHROMEOS)
106 SetWakingUpState(true);
108 if (device::BluetoothAdapterFactory::IsBluetoothAdapterAvailable()) {
109 device::BluetoothAdapterFactory::GetAdapter(
110 base::Bind(&UnlockManager::OnBluetoothAdapterInitialized,
111 weak_ptr_factory_.GetWeakPtr()));
115 UnlockManager::~UnlockManager() {
116 if (messenger_)
117 messenger_->RemoveObserver(this);
119 ScreenlockBridge::Get()->RemoveObserver(this);
121 #if defined(OS_CHROMEOS)
122 DBusThreadManager::Get()->GetPowerManagerClient()->RemoveObserver(this);
123 #endif // defined(OS_CHROMEOS)
125 if (bluetooth_adapter_)
126 bluetooth_adapter_->RemoveObserver(this);
129 bool UnlockManager::IsUnlockAllowed() {
130 return (remote_screenlock_state_ &&
131 *remote_screenlock_state_ == RemoteScreenlockState::UNLOCKED &&
132 life_cycle_ &&
133 life_cycle_->GetState() ==
134 RemoteDeviceLifeCycle::State::SECURE_CHANNEL_ESTABLISHED &&
135 proximity_monitor_->IsUnlockAllowed() &&
136 (screenlock_type_ != ScreenlockType::SIGN_IN ||
137 (messenger_ && messenger_->SupportsSignIn())));
140 void UnlockManager::SetRemoteDeviceLifeCycle(
141 RemoteDeviceLifeCycle* life_cycle) {
142 if (messenger_) {
143 messenger_->RemoveObserver(this);
144 messenger_ = nullptr;
147 life_cycle_ = life_cycle;
148 if (life_cycle_)
149 SetWakingUpState(true);
151 UpdateLockScreen();
154 void UnlockManager::OnLifeCycleStateChanged() {
155 RemoteDeviceLifeCycle::State state = life_cycle_->GetState();
156 PA_LOG(INFO) << "[Unlock] RemoteDeviceLifeCycle state changed: "
157 << static_cast<int>(state);
159 remote_screenlock_state_.reset();
160 if (state == RemoteDeviceLifeCycle::State::SECURE_CHANNEL_ESTABLISHED) {
161 messenger_ = life_cycle_->GetMessenger();
162 messenger_->AddObserver(this);
165 if (state == RemoteDeviceLifeCycle::State::AUTHENTICATION_FAILED)
166 SetWakingUpState(false);
168 UpdateLockScreen();
171 void UnlockManager::OnUnlockEventSent(bool success) {
172 if (!is_attempting_auth_) {
173 PA_LOG(ERROR) << "[Unlock] Sent easy_unlock event, but no auth attempted.";
174 return;
177 if (sign_in_secret_ && success)
178 proximity_auth_client_->FinalizeSignin(*sign_in_secret_);
180 AcceptAuthAttempt(success);
183 void UnlockManager::OnRemoteStatusUpdate(
184 const RemoteStatusUpdate& status_update) {
185 PA_LOG(INFO) << "[Unlock] Status Update: ("
186 << "user_present=" << status_update.user_presence << ", "
187 << "secure_screen_lock="
188 << status_update.secure_screen_lock_state << ", "
189 << "trust_agent=" << status_update.trust_agent_state << ")";
190 metrics::RecordRemoteSecuritySettingsState(
191 GetRemoteSecuritySettingsState(status_update));
193 remote_screenlock_state_.reset(new RemoteScreenlockState(
194 GetScreenlockStateFromRemoteUpdate(status_update)));
196 // This also calls |UpdateLockScreen()|
197 SetWakingUpState(false);
200 void UnlockManager::OnDecryptResponse(scoped_ptr<std::string> decrypted_bytes) {
201 if (!is_attempting_auth_) {
202 PA_LOG(ERROR) << "[Unlock] Decrypt response received but not attempting "
203 << "auth.";
204 return;
207 if (!decrypted_bytes) {
208 PA_LOG(INFO) << "[Unlock] Failed to decrypt sign-in challenge.";
209 AcceptAuthAttempt(false);
210 } else {
211 sign_in_secret_ = decrypted_bytes.Pass();
212 messenger_->DispatchUnlockEvent();
216 void UnlockManager::OnUnlockResponse(bool success) {
217 if (!is_attempting_auth_) {
218 PA_LOG(ERROR) << "[Unlock] Unlock response received but not attempting "
219 << "auth.";
220 return;
223 PA_LOG(INFO) << "[Unlock] Unlock response from remote device: "
224 << (success ? "success" : "failure");
225 if (success)
226 messenger_->DispatchUnlockEvent();
227 else
228 AcceptAuthAttempt(false);
231 void UnlockManager::OnDisconnected() {
232 messenger_->RemoveObserver(this);
233 messenger_ = nullptr;
236 void UnlockManager::OnScreenDidLock(
237 ScreenlockBridge::LockHandler::ScreenType screen_type) {
238 OnScreenLockedOrUnlocked(true);
241 void UnlockManager::OnScreenDidUnlock(
242 ScreenlockBridge::LockHandler::ScreenType screen_type) {
243 OnScreenLockedOrUnlocked(false);
246 void UnlockManager::OnFocusedUserChanged(const std::string& user_id) {}
248 void UnlockManager::OnScreenLockedOrUnlocked(bool is_locked) {
249 // TODO(tengs): Chrome will only start connecting to the phone when
250 // the screen is locked, for privacy reasons. We should reinvestigate
251 // this behaviour if we want automatic locking.
252 if (is_locked && bluetooth_adapter_ && bluetooth_adapter_->IsPowered() &&
253 life_cycle_ &&
254 life_cycle_->GetState() ==
255 RemoteDeviceLifeCycle::State::FINDING_CONNECTION) {
256 SetWakingUpState(true);
259 is_locked_ = is_locked;
260 UpdateProximityMonitorState();
263 void UnlockManager::OnBluetoothAdapterInitialized(
264 scoped_refptr<device::BluetoothAdapter> adapter) {
265 bluetooth_adapter_ = adapter;
266 bluetooth_adapter_->AddObserver(this);
269 void UnlockManager::AdapterPresentChanged(device::BluetoothAdapter* adapter,
270 bool present) {
271 UpdateLockScreen();
274 void UnlockManager::AdapterPoweredChanged(device::BluetoothAdapter* adapter,
275 bool powered) {
276 UpdateLockScreen();
279 #if defined(OS_CHROMEOS)
280 void UnlockManager::SuspendDone(const base::TimeDelta& sleep_duration) {
281 SetWakingUpState(true);
283 #endif // defined(OS_CHROMEOS)
285 void UnlockManager::OnAuthAttempted(
286 ScreenlockBridge::LockHandler::AuthType auth_type) {
287 if (is_attempting_auth_) {
288 PA_LOG(INFO) << "[Unlock] Already attempting auth.";
289 return;
292 if (auth_type != ScreenlockBridge::LockHandler::USER_CLICK)
293 return;
295 is_attempting_auth_ = true;
297 if (!life_cycle_) {
298 PA_LOG(ERROR) << "[Unlock] No life_cycle active when auth is attempted";
299 AcceptAuthAttempt(false);
300 UpdateLockScreen();
301 return;
304 if (!IsUnlockAllowed()) {
305 AcceptAuthAttempt(false);
306 UpdateLockScreen();
307 return;
310 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
311 FROM_HERE,
312 base::Bind(&UnlockManager::AcceptAuthAttempt,
313 reject_auth_attempt_weak_ptr_factory_.GetWeakPtr(), false),
314 base::TimeDelta::FromSeconds(kAuthAttemptTimeoutSecs));
316 if (screenlock_type_ == ScreenlockType::SIGN_IN) {
317 SendSignInChallenge();
318 } else {
319 if (messenger_->SupportsSignIn()) {
320 messenger_->RequestUnlock();
321 } else {
322 PA_LOG(INFO) << "[Unlock] Protocol v3.1 not supported, skipping "
323 << "request_unlock.";
324 messenger_->DispatchUnlockEvent();
329 void UnlockManager::SendSignInChallenge() {
330 // TODO(isherman): Implement.
331 NOTIMPLEMENTED();
334 ScreenlockState UnlockManager::GetScreenlockState() {
335 if (!life_cycle_ ||
336 life_cycle_->GetState() == RemoteDeviceLifeCycle::State::STOPPED)
337 return ScreenlockState::INACTIVE;
339 if (IsUnlockAllowed())
340 return ScreenlockState::AUTHENTICATED;
342 if (life_cycle_->GetState() ==
343 RemoteDeviceLifeCycle::State::AUTHENTICATION_FAILED)
344 return ScreenlockState::PHONE_NOT_AUTHENTICATED;
346 if (is_waking_up_)
347 return ScreenlockState::BLUETOOTH_CONNECTING;
349 if (!bluetooth_adapter_ || !bluetooth_adapter_->IsPowered())
350 return ScreenlockState::NO_BLUETOOTH;
352 if (screenlock_type_ == ScreenlockType::SIGN_IN && messenger_ &&
353 !messenger_->SupportsSignIn())
354 return ScreenlockState::PHONE_UNSUPPORTED;
356 // If the RSSI is too low, then the remote device is nowhere near the local
357 // device. This message should take priority over messages about screen lock
358 // states.
359 if (!proximity_monitor_->IsUnlockAllowed() &&
360 !proximity_monitor_->IsInRssiRange())
361 return ScreenlockState::RSSI_TOO_LOW;
363 if (remote_screenlock_state_) {
364 switch (*remote_screenlock_state_) {
365 case RemoteScreenlockState::DISABLED:
366 return ScreenlockState::PHONE_NOT_LOCKABLE;
368 case RemoteScreenlockState::LOCKED:
369 if (proximity_monitor_->GetStrategy() ==
370 ProximityMonitor::Strategy::CHECK_TRANSMIT_POWER &&
371 !proximity_monitor_->IsUnlockAllowed()) {
372 return ScreenlockState::PHONE_LOCKED_AND_TX_POWER_TOO_HIGH;
374 return ScreenlockState::PHONE_LOCKED;
376 case RemoteScreenlockState::UNKNOWN:
377 return ScreenlockState::PHONE_UNSUPPORTED;
379 case RemoteScreenlockState::UNLOCKED:
380 // Handled by the code below.
381 break;
385 if (!proximity_monitor_->IsUnlockAllowed()) {
386 ProximityMonitor::Strategy strategy = proximity_monitor_->GetStrategy();
387 if (strategy != ProximityMonitor::Strategy::CHECK_TRANSMIT_POWER) {
388 // CHECK_RSSI should have been handled above, and no other states should
389 // prevent unlocking.
390 PA_LOG(ERROR) << "[Unlock] Invalid ProximityMonitor strategy: "
391 << static_cast<int>(strategy);
392 return ScreenlockState::NO_PHONE;
394 return ScreenlockState::TX_POWER_TOO_HIGH;
397 return ScreenlockState::NO_PHONE;
400 void UnlockManager::UpdateLockScreen() {
401 UpdateProximityMonitorState();
403 ScreenlockState new_state = GetScreenlockState();
404 if (screenlock_state_ == new_state)
405 return;
407 proximity_auth_client_->UpdateScreenlockState(new_state);
408 screenlock_state_ = new_state;
411 void UnlockManager::UpdateProximityMonitorState() {
412 if (is_locked_ && life_cycle_ &&
413 life_cycle_->GetState() ==
414 RemoteDeviceLifeCycle::State::SECURE_CHANNEL_ESTABLISHED) {
415 proximity_monitor_->Start();
416 } else {
417 proximity_monitor_->Stop();
421 void UnlockManager::SetWakingUpState(bool is_waking_up) {
422 is_waking_up_ = is_waking_up;
424 // Clear the waking up state after a timeout.
425 clear_waking_up_state_weak_ptr_factory_.InvalidateWeakPtrs();
426 if (is_waking_up_) {
427 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
428 FROM_HERE,
429 base::Bind(&UnlockManager::SetWakingUpState,
430 clear_waking_up_state_weak_ptr_factory_.GetWeakPtr(), false),
431 base::TimeDelta::FromSeconds(kWakingUpDurationSecs));
434 UpdateLockScreen();
437 void UnlockManager::AcceptAuthAttempt(bool should_accept) {
438 if (!is_attempting_auth_)
439 return;
441 // Cancel the pending task to time out the auth attempt.
442 reject_auth_attempt_weak_ptr_factory_.InvalidateWeakPtrs();
444 if (should_accept)
445 proximity_monitor_->RecordProximityMetricsOnAuthSuccess();
447 is_attempting_auth_ = false;
448 proximity_auth_client_->FinalizeUnlock(should_accept);
451 UnlockManager::RemoteScreenlockState
452 UnlockManager::GetScreenlockStateFromRemoteUpdate(RemoteStatusUpdate update) {
453 switch (update.secure_screen_lock_state) {
454 case SECURE_SCREEN_LOCK_DISABLED:
455 return RemoteScreenlockState::DISABLED;
457 case SECURE_SCREEN_LOCK_ENABLED:
458 if (update.user_presence == USER_PRESENT)
459 return RemoteScreenlockState::UNLOCKED;
461 return RemoteScreenlockState::LOCKED;
463 case SECURE_SCREEN_LOCK_STATE_UNKNOWN:
464 return RemoteScreenlockState::UNKNOWN;
467 NOTREACHED();
468 return RemoteScreenlockState::UNKNOWN;
471 } // namespace proximity_auth