Update mojo sdk to rev c02a28868825edfa57ab77947b8cb15e741c5598
[chromium-blink-merge.git] / components / proximity_auth / unlock_manager.cc
blobd9156fcf5574fc55e505651b24cb1cbbfed92532
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/client.h"
13 #include "components/proximity_auth/logging/logging.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 controller_(nullptr),
83 client_(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 (client_)
117 client_->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 controller_ &&
133 controller_->GetState() ==
134 Controller::State::SECURE_CHANNEL_ESTABLISHED &&
135 proximity_monitor_->IsUnlockAllowed() &&
136 (screenlock_type_ != ScreenlockType::SIGN_IN ||
137 (client_ && client_->SupportsSignIn())));
140 void UnlockManager::SetController(Controller* controller) {
141 if (client_) {
142 client_->RemoveObserver(this);
143 client_ = nullptr;
146 controller_ = controller;
147 if (controller_)
148 SetWakingUpState(true);
150 UpdateLockScreen();
153 void UnlockManager::OnControllerStateChanged() {
154 Controller::State state = controller_->GetState();
155 PA_LOG(INFO) << "[Unlock] Controller state changed: "
156 << static_cast<int>(state);
158 remote_screenlock_state_.reset();
159 if (state == Controller::State::SECURE_CHANNEL_ESTABLISHED) {
160 client_ = controller_->GetClient();
161 client_->AddObserver(this);
164 if (state == Controller::State::AUTHENTICATION_FAILED)
165 SetWakingUpState(false);
167 UpdateLockScreen();
170 void UnlockManager::OnUnlockEventSent(bool success) {
171 if (!is_attempting_auth_) {
172 PA_LOG(ERROR) << "[Unlock] Sent easy_unlock event, but no auth attempted.";
173 return;
176 if (sign_in_secret_ && success)
177 proximity_auth_client_->FinalizeSignin(*sign_in_secret_);
179 AcceptAuthAttempt(success);
182 void UnlockManager::OnRemoteStatusUpdate(
183 const RemoteStatusUpdate& status_update) {
184 PA_LOG(INFO) << "[Unlock] Status Update: ("
185 << "user_present=" << status_update.user_presence << ", "
186 << "secure_screen_lock="
187 << status_update.secure_screen_lock_state << ", "
188 << "trust_agent=" << status_update.trust_agent_state << ")";
189 metrics::RecordRemoteSecuritySettingsState(
190 GetRemoteSecuritySettingsState(status_update));
192 remote_screenlock_state_.reset(new RemoteScreenlockState(
193 GetScreenlockStateFromRemoteUpdate(status_update)));
195 // This also calls |UpdateLockScreen()|
196 SetWakingUpState(false);
199 void UnlockManager::OnDecryptResponse(scoped_ptr<std::string> decrypted_bytes) {
200 if (!is_attempting_auth_) {
201 PA_LOG(ERROR) << "[Unlock] Decrypt response received but not attempting "
202 << "auth.";
203 return;
206 if (!decrypted_bytes) {
207 PA_LOG(INFO) << "[Unlock] Failed to decrypt sign-in challenge.";
208 AcceptAuthAttempt(false);
209 } else {
210 sign_in_secret_ = decrypted_bytes.Pass();
211 client_->DispatchUnlockEvent();
215 void UnlockManager::OnUnlockResponse(bool success) {
216 if (!is_attempting_auth_) {
217 PA_LOG(ERROR) << "[Unlock] Unlock response received but not attempting "
218 << "auth.";
219 return;
222 PA_LOG(INFO) << "[Unlock] Unlock response from remote device: "
223 << (success ? "success" : "failure");
224 if (success)
225 client_->DispatchUnlockEvent();
226 else
227 AcceptAuthAttempt(false);
230 void UnlockManager::OnDisconnected() {
231 client_->RemoveObserver(this);
232 client_ = nullptr;
235 void UnlockManager::OnScreenDidLock(
236 ScreenlockBridge::LockHandler::ScreenType screen_type) {
237 OnScreenLockedOrUnlocked(true);
240 void UnlockManager::OnScreenDidUnlock(
241 ScreenlockBridge::LockHandler::ScreenType screen_type) {
242 OnScreenLockedOrUnlocked(false);
245 void UnlockManager::OnFocusedUserChanged(const std::string& user_id) {}
247 void UnlockManager::OnScreenLockedOrUnlocked(bool is_locked) {
248 // TODO(tengs): Chrome will only start connecting to the phone when
249 // the screen is locked, for privacy reasons. We should reinvestigate
250 // this behaviour if we want automatic locking.
251 if (is_locked && bluetooth_adapter_ && bluetooth_adapter_->IsPowered() &&
252 controller_ &&
253 controller_->GetState() == Controller::State::FINDING_CONNECTION) {
254 SetWakingUpState(true);
257 is_locked_ = is_locked;
258 UpdateProximityMonitorState();
261 void UnlockManager::OnBluetoothAdapterInitialized(
262 scoped_refptr<device::BluetoothAdapter> adapter) {
263 bluetooth_adapter_ = adapter;
264 bluetooth_adapter_->AddObserver(this);
267 void UnlockManager::AdapterPresentChanged(device::BluetoothAdapter* adapter,
268 bool present) {
269 UpdateLockScreen();
272 void UnlockManager::AdapterPoweredChanged(device::BluetoothAdapter* adapter,
273 bool powered) {
274 UpdateLockScreen();
277 #if defined(OS_CHROMEOS)
278 void UnlockManager::SuspendDone(const base::TimeDelta& sleep_duration) {
279 SetWakingUpState(true);
281 #endif // defined(OS_CHROMEOS)
283 void UnlockManager::OnAuthAttempted(
284 ScreenlockBridge::LockHandler::AuthType auth_type) {
285 if (is_attempting_auth_) {
286 PA_LOG(INFO) << "[Unlock] Already attempting auth.";
287 return;
290 if (auth_type != ScreenlockBridge::LockHandler::USER_CLICK)
291 return;
293 is_attempting_auth_ = true;
295 if (!controller_) {
296 PA_LOG(ERROR) << "[Unlock] No controller active when auth is attempted";
297 AcceptAuthAttempt(false);
298 UpdateLockScreen();
299 return;
302 if (!IsUnlockAllowed()) {
303 AcceptAuthAttempt(false);
304 UpdateLockScreen();
305 return;
308 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
309 FROM_HERE,
310 base::Bind(&UnlockManager::AcceptAuthAttempt,
311 reject_auth_attempt_weak_ptr_factory_.GetWeakPtr(), false),
312 base::TimeDelta::FromSeconds(kAuthAttemptTimeoutSecs));
314 if (screenlock_type_ == ScreenlockType::SIGN_IN) {
315 SendSignInChallenge();
316 } else {
317 if (client_->SupportsSignIn()) {
318 client_->RequestUnlock();
319 } else {
320 PA_LOG(INFO) << "[Unlock] Protocol v3.1 not supported, skipping "
321 << "request_unlock.";
322 client_->DispatchUnlockEvent();
327 void UnlockManager::SendSignInChallenge() {
328 // TODO(isherman): Implement.
329 NOTIMPLEMENTED();
332 ScreenlockState UnlockManager::GetScreenlockState() {
333 if (!controller_ || controller_->GetState() == Controller::State::STOPPED)
334 return ScreenlockState::INACTIVE;
336 if (IsUnlockAllowed())
337 return ScreenlockState::AUTHENTICATED;
339 if (controller_->GetState() == Controller::State::AUTHENTICATION_FAILED)
340 return ScreenlockState::PHONE_NOT_AUTHENTICATED;
342 if (is_waking_up_)
343 return ScreenlockState::BLUETOOTH_CONNECTING;
345 if (!bluetooth_adapter_ || !bluetooth_adapter_->IsPowered())
346 return ScreenlockState::NO_BLUETOOTH;
348 if (screenlock_type_ == ScreenlockType::SIGN_IN && client_ &&
349 !client_->SupportsSignIn())
350 return ScreenlockState::PHONE_UNSUPPORTED;
352 // If the RSSI is too low, then the remote device is nowhere near the local
353 // device. This message should take priority over messages about screen lock
354 // states.
355 if (!proximity_monitor_->IsUnlockAllowed() &&
356 !proximity_monitor_->IsInRssiRange())
357 return ScreenlockState::RSSI_TOO_LOW;
359 if (remote_screenlock_state_) {
360 switch (*remote_screenlock_state_) {
361 case RemoteScreenlockState::DISABLED:
362 return ScreenlockState::PHONE_NOT_LOCKABLE;
364 case RemoteScreenlockState::LOCKED:
365 if (proximity_monitor_->GetStrategy() ==
366 ProximityMonitor::Strategy::CHECK_TRANSMIT_POWER &&
367 !proximity_monitor_->IsUnlockAllowed()) {
368 return ScreenlockState::PHONE_LOCKED_AND_TX_POWER_TOO_HIGH;
370 return ScreenlockState::PHONE_LOCKED;
372 case RemoteScreenlockState::UNKNOWN:
373 return ScreenlockState::PHONE_UNSUPPORTED;
375 case RemoteScreenlockState::UNLOCKED:
376 // Handled by the code below.
377 break;
381 if (!proximity_monitor_->IsUnlockAllowed()) {
382 ProximityMonitor::Strategy strategy = proximity_monitor_->GetStrategy();
383 if (strategy != ProximityMonitor::Strategy::CHECK_TRANSMIT_POWER) {
384 // CHECK_RSSI should have been handled above, and no other states should
385 // prevent unlocking.
386 PA_LOG(ERROR) << "[Unlock] Invalid ProximityMonitor strategy: "
387 << static_cast<int>(strategy);
388 return ScreenlockState::NO_PHONE;
390 return ScreenlockState::TX_POWER_TOO_HIGH;
393 return ScreenlockState::NO_PHONE;
396 void UnlockManager::UpdateLockScreen() {
397 UpdateProximityMonitorState();
399 ScreenlockState new_state = GetScreenlockState();
400 if (screenlock_state_ == new_state)
401 return;
403 proximity_auth_client_->UpdateScreenlockState(new_state);
404 screenlock_state_ = new_state;
407 void UnlockManager::UpdateProximityMonitorState() {
408 if (is_locked_ && controller_ &&
409 controller_->GetState() ==
410 Controller::State::SECURE_CHANNEL_ESTABLISHED) {
411 proximity_monitor_->Start();
412 } else {
413 proximity_monitor_->Stop();
417 void UnlockManager::SetWakingUpState(bool is_waking_up) {
418 is_waking_up_ = is_waking_up;
420 // Clear the waking up state after a timeout.
421 clear_waking_up_state_weak_ptr_factory_.InvalidateWeakPtrs();
422 if (is_waking_up_) {
423 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
424 FROM_HERE,
425 base::Bind(&UnlockManager::SetWakingUpState,
426 clear_waking_up_state_weak_ptr_factory_.GetWeakPtr(), false),
427 base::TimeDelta::FromSeconds(kWakingUpDurationSecs));
430 UpdateLockScreen();
433 void UnlockManager::AcceptAuthAttempt(bool should_accept) {
434 if (!is_attempting_auth_)
435 return;
437 // Cancel the pending task to time out the auth attempt.
438 reject_auth_attempt_weak_ptr_factory_.InvalidateWeakPtrs();
440 if (should_accept)
441 proximity_monitor_->RecordProximityMetricsOnAuthSuccess();
443 is_attempting_auth_ = false;
444 proximity_auth_client_->FinalizeUnlock(should_accept);
447 UnlockManager::RemoteScreenlockState
448 UnlockManager::GetScreenlockStateFromRemoteUpdate(RemoteStatusUpdate update) {
449 switch (update.secure_screen_lock_state) {
450 case SECURE_SCREEN_LOCK_DISABLED:
451 return RemoteScreenlockState::DISABLED;
453 case SECURE_SCREEN_LOCK_ENABLED:
454 if (update.user_presence == USER_PRESENT)
455 return RemoteScreenlockState::UNLOCKED;
457 return RemoteScreenlockState::LOCKED;
459 case SECURE_SCREEN_LOCK_STATE_UNKNOWN:
460 return RemoteScreenlockState::UNKNOWN;
463 NOTREACHED();
464 return RemoteScreenlockState::UNKNOWN;
467 } // namespace proximity_auth