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"
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
{
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
;
73 return metrics::RemoteSecuritySettingsState::UNKNOWN
;
78 UnlockManager::UnlockManager(ScreenlockType screenlock_type
,
79 scoped_ptr
<ProximityMonitor
> proximity_monitor
,
80 ProximityAuthClient
* proximity_auth_client
)
81 : screenlock_type_(screenlock_type
),
84 proximity_monitor_(proximity_monitor
.Pass()),
85 proximity_auth_client_(proximity_auth_client
),
87 is_attempting_auth_(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
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() {
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
&&
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
) {
142 client_
->RemoveObserver(this);
146 controller_
= controller
;
148 SetWakingUpState(true);
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);
170 void UnlockManager::OnUnlockEventSent(bool success
) {
171 if (!is_attempting_auth_
) {
172 PA_LOG(ERROR
) << "[Unlock] Sent easy_unlock event, but no auth attempted.";
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 "
206 if (!decrypted_bytes
) {
207 PA_LOG(INFO
) << "[Unlock] Failed to decrypt sign-in challenge.";
208 AcceptAuthAttempt(false);
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 "
222 PA_LOG(INFO
) << "[Unlock] Unlock response from remote device: "
223 << (success
? "success" : "failure");
225 client_
->DispatchUnlockEvent();
227 AcceptAuthAttempt(false);
230 void UnlockManager::OnDisconnected() {
231 client_
->RemoveObserver(this);
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() &&
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
,
272 void UnlockManager::AdapterPoweredChanged(device::BluetoothAdapter
* adapter
,
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.";
290 if (auth_type
!= ScreenlockBridge::LockHandler::USER_CLICK
)
293 is_attempting_auth_
= true;
296 PA_LOG(ERROR
) << "[Unlock] No controller active when auth is attempted";
297 AcceptAuthAttempt(false);
302 if (!IsUnlockAllowed()) {
303 AcceptAuthAttempt(false);
308 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
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();
317 if (client_
->SupportsSignIn()) {
318 client_
->RequestUnlock();
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.
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
;
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
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.
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
)
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();
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();
423 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
425 base::Bind(&UnlockManager::SetWakingUpState
,
426 clear_waking_up_state_weak_ptr_factory_
.GetWeakPtr(), false),
427 base::TimeDelta::FromSeconds(kWakingUpDurationSecs
));
433 void UnlockManager::AcceptAuthAttempt(bool should_accept
) {
434 if (!is_attempting_auth_
)
437 // Cancel the pending task to time out the auth attempt.
438 reject_auth_attempt_weak_ptr_factory_
.InvalidateWeakPtrs();
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
;
464 return RemoteScreenlockState::UNKNOWN
;
467 } // namespace proximity_auth