Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(230)

Side by Side Diff: components/proximity_auth/unlock_manager.cc

Issue 1239193005: [Proximity Auth] Port the UnlockManager class. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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.
4
5 #include "components/proximity_auth/unlock_manager.h"
6
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"
18
19 #if defined(OS_CHROMEOS)
20 #include "chromeos/dbus/dbus_thread_manager.h"
21 #endif // defined(OS_CHROMEOS)
22
23 namespace proximity_auth {
24 namespace {
25
26 // The maximum amount of time, in seconds, that the unlock manager can stay in
27 // the 'waking up' state after resuming from sleep.
28 const int kWakingUpDurationSecs = 5;
29
30 // The limit, in seconds, on the elapsed time for an auth attempt. If an auth
31 // attempt exceeds this limit, it will time out and be rejected. This is
32 // provided as a failsafe, in case something goes wrong.
33 const int kAuthAttemptTimeoutSecs = 5;
34
35 // Returns the remote device's security settings state, for metrics,
36 // corresponding to a remote status update.
37 metrics::RemoteSecuritySettingsState GetRemoteSecuritySettingsState(
38 const RemoteStatusUpdate& status_update) {
39 switch (status_update.secure_screen_lock_state) {
40 case SECURE_SCREEN_LOCK_STATE_UNKNOWN:
41 return metrics::RemoteSecuritySettingsState::UNKNOWN;
42
43 case SECURE_SCREEN_LOCK_DISABLED:
44 switch (status_update.trust_agent_state) {
45 case TRUST_AGENT_UNSUPPORTED:
46 return metrics::RemoteSecuritySettingsState::
47 SCREEN_LOCK_DISABLED_TRUST_AGENT_UNSUPPORTED;
48 case TRUST_AGENT_DISABLED:
49 return metrics::RemoteSecuritySettingsState::
50 SCREEN_LOCK_DISABLED_TRUST_AGENT_DISABLED;
51 case TRUST_AGENT_ENABLED:
52 return metrics::RemoteSecuritySettingsState::
53 SCREEN_LOCK_DISABLED_TRUST_AGENT_ENABLED;
54 }
55
56 case SECURE_SCREEN_LOCK_ENABLED:
57 switch (status_update.trust_agent_state) {
58 case TRUST_AGENT_UNSUPPORTED:
59 return metrics::RemoteSecuritySettingsState::
60 SCREEN_LOCK_ENABLED_TRUST_AGENT_UNSUPPORTED;
61 case TRUST_AGENT_DISABLED:
62 return metrics::RemoteSecuritySettingsState::
63 SCREEN_LOCK_ENABLED_TRUST_AGENT_DISABLED;
64 case TRUST_AGENT_ENABLED:
65 return metrics::RemoteSecuritySettingsState::
66 SCREEN_LOCK_ENABLED_TRUST_AGENT_ENABLED;
67 }
68 }
69
70 NOTREACHED();
71 return metrics::RemoteSecuritySettingsState::UNKNOWN;
72 }
73
74 } // namespace
75
76 UnlockManager::UnlockManager(ScreenlockType screenlock_type,
77 scoped_ptr<ProximityMonitor> proximity_monitor,
78 ProximityAuthClient* proximity_auth_client)
79 : screenlock_type_(screenlock_type),
80 controller_(nullptr),
81 client_(nullptr),
82 proximity_monitor_(proximity_monitor.Pass()),
83 proximity_auth_client_(proximity_auth_client),
84 is_locked_(false),
85 is_attempting_auth_(false),
86 is_waking_up_(false),
87 screenlock_state_(ScreenlockState::INACTIVE),
88 clear_waking_up_state_weak_ptr_factory_(this),
89 reject_auth_attempt_weak_ptr_factory_(this),
90 weak_ptr_factory_(this) {
91 // TODO(isherman): Register for auth attempt notifications, equivalent to the
92 // JavaScript lines:
93 //
94 // chrome.screenlockPrivate.onAuthAttempted.addListener(
95 // this.onAuthAttempted_.bind(this));
96
97 ScreenlockBridge* screenlock_bridge = ScreenlockBridge::Get();
98 screenlock_bridge->AddObserver(this);
99 OnScreenLockStateChanged(screenlock_bridge->IsLocked());
100
101 #if defined(OS_CHROMEOS)
102 DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver(this);
103 #endif // defined(OS_CHROMEOS)
104 SetWakingUpState(true);
105
106 if (device::BluetoothAdapterFactory::IsBluetoothAdapterAvailable()) {
107 device::BluetoothAdapterFactory::GetAdapter(
108 base::Bind(&UnlockManager::OnBluetoothAdapterInitialized,
109 weak_ptr_factory_.GetWeakPtr()));
110 }
111 }
112
113 UnlockManager::~UnlockManager() {
114 if (client_)
115 client_->RemoveObserver(this);
116
117 ScreenlockBridge::Get()->RemoveObserver(this);
118
119 #if defined(OS_CHROMEOS)
120 DBusThreadManager::Get()->GetPowerManagerClient()->RemoveObserver(this);
121 #endif // defined(OS_CHROMEOS)
122
123 if (bluetooth_adapter_)
124 bluetooth_adapter_->RemoveObserver(this);
125 }
126
127 bool UnlockManager::IsUnlockAllowed() {
128 return (remote_screenlock_state_ &&
129 *remote_screenlock_state_ == RemoteScreenlockState::UNLOCKED &&
130 controller_ &&
131 controller_->GetState() ==
132 Controller::State::SECURE_CHANNEL_ESTABLISHED &&
133 proximity_monitor_->IsUnlockAllowed() &&
134 (screenlock_type_ != ScreenlockType::SIGN_IN ||
135 (client_ && client_->SupportsSignIn())));
136 }
137
138 void UnlockManager::SetController(Controller* controller) {
139 if (client_) {
140 client_->RemoveObserver(this);
141 client_ = nullptr;
142 }
143
144 controller_ = controller;
145 if (controller_)
146 SetWakingUpState(true);
147
148 UpdateLockScreen();
149 }
150
151 void UnlockManager::OnControllerStateChanged() {
152 Controller::State state = controller_->GetState();
153 PA_LOG(INFO) << "[Unlock] Controller state changed: "
154 << static_cast<int>(state);
155
156 remote_screenlock_state_.reset();
157 if (state == Controller::State::SECURE_CHANNEL_ESTABLISHED) {
158 client_ = controller_->GetClient();
159 client_->AddObserver(this);
160 }
161
162 if (state == Controller::State::AUTHENTICATION_FAILED)
163 SetWakingUpState(false);
164
165 UpdateLockScreen();
166 }
167
168 void UnlockManager::OnUnlockEventSent(bool success) {
169 if (!is_attempting_auth_) {
170 PA_LOG(ERROR) << "[Unlock] Sent easy_unlock event, but no auth attempted.";
171 return;
172 }
173
174 if (sign_in_secret_ && success)
175 proximity_auth_client_->FinalizeSignin(*sign_in_secret_);
Tim Song 2015/07/23 21:32:15 Shouldn't you move these two lines inside AcceptAu
Ilya Sherman 2015/08/11 23:37:15 The current flow matches what the app is doing (se
176
177 AcceptAuthAttempt(success);
178 }
179
180 void UnlockManager::OnRemoteStatusUpdate(
181 const RemoteStatusUpdate& status_update) {
182 PA_LOG(INFO) << "[Unlock] Status Update: ("
183 << "user_present=" << status_update.user_presence << ", "
184 << "secure_screen_lock="
185 << status_update.secure_screen_lock_state << ", "
186 << "trust_agent=" << status_update.trust_agent_state << ")";
187 metrics::RecordRemoteSecuritySettingsState(
188 GetRemoteSecuritySettingsState(status_update));
189
190 remote_screenlock_state_.reset(new RemoteScreenlockState(
191 GetScreenlockStateFromRemoteUpdate(status_update)));
192
193 // This also calls |UpdateLockScreen()|
194 SetWakingUpState(false);
195 }
196
197 void UnlockManager::OnDecryptResponse(scoped_ptr<std::string> decrypted_bytes) {
198 if (!is_attempting_auth_) {
199 PA_LOG(ERROR) << "[Unlock] Decrypt response received but not attempting "
200 << "auth.";
201 return;
202 }
203
204 if (!decrypted_bytes) {
205 PA_LOG(INFO) << "[Unlock] Failed to decrypt sign-in challenge.";
206 AcceptAuthAttempt(false);
207 } else {
208 sign_in_secret_ = decrypted_bytes.Pass();
209 client_->DispatchUnlockEvent();
210 }
211 }
212
213 void UnlockManager::OnUnlockResponse(bool success) {
214 if (!is_attempting_auth_) {
215 PA_LOG(ERROR) << "[Unlock] Unlock response received but not attempting "
216 << "auth.";
217 return;
218 }
219
220 PA_LOG(INFO) << "[Unlock] Unlock response from remote device: "
221 << (success ? "success" : "failure");
222 if (success)
223 client_->DispatchUnlockEvent();
224 else
225 AcceptAuthAttempt(false);
226 }
227
228 void UnlockManager::OnDisconnected() {
229 client_->RemoveObserver(this);
230 client_ = nullptr;
231 }
232
233 void UnlockManager::OnScreenDidLock(
234 ScreenlockBridge::LockHandler::ScreenType screen_type) {
235 OnScreenLockStateChanged(true);
236 }
237
238 void UnlockManager::OnScreenDidUnlock(
239 ScreenlockBridge::LockHandler::ScreenType screen_type) {
240 OnScreenLockStateChanged(false);
241 }
242
243 void UnlockManager::OnFocusedUserChanged(const std::string& user_id) {}
244
245 void UnlockManager::OnScreenLockStateChanged(bool is_locked) {
Tim Song 2015/07/23 21:32:15 There is already a |ScreenlockState| enum that has
Ilya Sherman 2015/08/11 23:37:15 Done.
246 // TODO(tengs): Chrome will only start connecting to the phone when
247 // the screen is locked, for privacy reasons. We should reinvestigate
248 // this behaviour if we want automatic locking.
249 if (is_locked && bluetooth_adapter_ && bluetooth_adapter_->IsPowered() &&
250 controller_ &&
251 controller_->GetState() == Controller::State::FINDING_CONNECTION) {
252 SetWakingUpState(true);
253 }
254
255 is_locked_ = is_locked;
256 UpdateProximityMonitorState();
257 }
258
259 void UnlockManager::OnBluetoothAdapterInitialized(
260 scoped_refptr<device::BluetoothAdapter> adapter) {
261 bluetooth_adapter_ = adapter;
262 bluetooth_adapter_->AddObserver(this);
263 }
264
265 void UnlockManager::AdapterPresentChanged(device::BluetoothAdapter* adapter,
266 bool present) {
267 UpdateLockScreen();
268 }
269
270 void UnlockManager::AdapterPoweredChanged(device::BluetoothAdapter* adapter,
271 bool powered) {
272 UpdateLockScreen();
273 }
274
275 #if defined(OS_CHROMEOS)
276 void UnlockManager::SuspendDone(const base::TimeDelta& sleep_duration) {
277 SetWakingUpState(true);
278 }
279 #endif // defined(OS_CHROMEOS)
280
281 void UnlockManager::OnAuthAttempted(
282 ScreenlockBridge::LockHandler::AuthType auth_type) {
283 if (is_attempting_auth_) {
284 PA_LOG(INFO) << "[Unlock] Already attempting auth.";
285 return;
286 }
287
288 if (auth_type != ScreenlockBridge::LockHandler::USER_CLICK)
289 return;
290
291 is_attempting_auth_ = true;
292
293 if (!controller_) {
294 PA_LOG(ERROR) << "[Unlock] No controller active when auth is attempted";
295 AcceptAuthAttempt(false);
296 UpdateLockScreen();
297 return;
298 }
299
300 if (!IsUnlockAllowed()) {
301 AcceptAuthAttempt(false);
302 UpdateLockScreen();
303 return;
304 }
305
306 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
307 FROM_HERE,
308 base::Bind(&UnlockManager::AcceptAuthAttempt,
309 reject_auth_attempt_weak_ptr_factory_.GetWeakPtr(), false),
310 base::TimeDelta::FromSeconds(kAuthAttemptTimeoutSecs));
311
312 if (screenlock_type_ == ScreenlockType::SIGN_IN) {
313 SendSignInChallenge();
314 } else {
315 if (client_->SupportsSignIn()) {
316 client_->RequestUnlock();
317 } else {
318 PA_LOG(INFO) << "[Unlock] Protocol v3.1 not supported, skipping "
319 << "request_unlock.";
320 client_->DispatchUnlockEvent();
321 }
322 }
323 }
324
325 void UnlockManager::SendSignInChallenge() {
326 // TODO(isherman): Implement.
327 NOTIMPLEMENTED();
328 }
329
330 ScreenlockState UnlockManager::GetScreenlockState() {
331 if (!controller_ || controller_->GetState() == Controller::State::STOPPED)
332 return ScreenlockState::INACTIVE;
333
334 if (IsUnlockAllowed())
335 return ScreenlockState::AUTHENTICATED;
336
337 if (controller_->GetState() == Controller::State::AUTHENTICATION_FAILED)
338 return ScreenlockState::PHONE_NOT_AUTHENTICATED;
339
340 if (is_waking_up_)
341 return ScreenlockState::BLUETOOTH_CONNECTING;
342
343 if (!bluetooth_adapter_ || !bluetooth_adapter_->IsPowered())
344 return ScreenlockState::NO_BLUETOOTH;
345
346 if (screenlock_type_ == ScreenlockType::SIGN_IN && client_ &&
347 !client_->SupportsSignIn())
348 return ScreenlockState::PHONE_UNSUPPORTED;
349
350 // If the RSSI is too low, then the remote device is nowhere near the local
351 // device. This message should take priority over messages about screen lock
352 // states.
353 if (!proximity_monitor_->IsUnlockAllowed() &&
354 !proximity_monitor_->IsInRssiRange())
355 return ScreenlockState::RSSI_TOO_LOW;
356
357 if (remote_screenlock_state_) {
358 switch (*remote_screenlock_state_) {
359 case RemoteScreenlockState::DISABLED:
360 return ScreenlockState::PHONE_NOT_LOCKABLE;
361
362 case RemoteScreenlockState::LOCKED:
363 if (proximity_monitor_->GetStrategy() ==
364 ProximityMonitor::Strategy::CHECK_TRANSMIT_POWER &&
365 !proximity_monitor_->IsUnlockAllowed()) {
366 return ScreenlockState::PHONE_LOCKED_AND_TX_POWER_TOO_HIGH;
367 }
368 return ScreenlockState::PHONE_LOCKED;
369
370 case RemoteScreenlockState::UNKNOWN:
371 return ScreenlockState::PHONE_UNSUPPORTED;
372
373 case RemoteScreenlockState::UNLOCKED:
374 // Handled by the code below.
375 break;
376 }
377 }
378
379 if (!proximity_monitor_->IsUnlockAllowed()) {
380 ProximityMonitor::Strategy strategy = proximity_monitor_->GetStrategy();
381 if (strategy != ProximityMonitor::Strategy::CHECK_TRANSMIT_POWER) {
382 // CHECK_RSSI should have been handled above, and no other states should
383 // prevent unlocking.
384 PA_LOG(ERROR) << "[Unlock] Invalid ProximityMonitor strategy: "
385 << static_cast<int>(strategy);
386 return ScreenlockState::NO_PHONE;
387 }
388 return ScreenlockState::TX_POWER_TOO_HIGH;
389 }
390
391 return ScreenlockState::NO_PHONE;
392 }
393
394 void UnlockManager::UpdateLockScreen() {
395 UpdateProximityMonitorState();
396
397 ScreenlockState new_state = GetScreenlockState();
398 if (screenlock_state_ == new_state)
399 return;
400
401 proximity_auth_client_->UpdateScreenlockState(new_state);
402 screenlock_state_ = new_state;
403 }
404
405 void UnlockManager::UpdateProximityMonitorState() {
406 if (is_locked_ && controller_ &&
407 controller_->GetState() ==
408 Controller::State::SECURE_CHANNEL_ESTABLISHED) {
409 proximity_monitor_->Start();
410 } else {
411 proximity_monitor_->Stop();
412 }
413 }
414
415 void UnlockManager::SetWakingUpState(bool is_waking_up) {
416 is_waking_up_ = is_waking_up;
417
418 // Clear the waking up state after a timeout.
419 clear_waking_up_state_weak_ptr_factory_.InvalidateWeakPtrs();
420 if (is_waking_up_) {
421 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
422 FROM_HERE,
423 base::Bind(&UnlockManager::SetWakingUpState,
424 clear_waking_up_state_weak_ptr_factory_.GetWeakPtr(), false),
425 base::TimeDelta::FromSeconds(kWakingUpDurationSecs));
426 }
427
428 UpdateLockScreen();
429 }
430
431 void UnlockManager::AcceptAuthAttempt(bool should_accept) {
432 if (!is_attempting_auth_)
433 return;
434
435 // Cancel the pending task to time out the auth attempt.
436 reject_auth_attempt_weak_ptr_factory_.InvalidateWeakPtrs();
437
438 if (should_accept)
439 proximity_monitor_->RecordProximityMetricsOnAuthSuccess();
440
441 is_attempting_auth_ = false;
442 proximity_auth_client_->FinalizeUnlock(should_accept);
443 }
444
445 UnlockManager::RemoteScreenlockState
446 UnlockManager::GetScreenlockStateFromRemoteUpdate(RemoteStatusUpdate update) {
447 switch (update.secure_screen_lock_state) {
448 case SECURE_SCREEN_LOCK_DISABLED:
449 return RemoteScreenlockState::DISABLED;
450
451 case SECURE_SCREEN_LOCK_ENABLED:
452 if (update.user_presence == USER_PRESENT)
453 return RemoteScreenlockState::UNLOCKED;
454
455 return RemoteScreenlockState::LOCKED;
456
457 case SECURE_SCREEN_LOCK_STATE_UNKNOWN:
458 return RemoteScreenlockState::UNKNOWN;
459 }
460
461 NOTREACHED();
462 return RemoteScreenlockState::UNKNOWN;
463 }
464
465 } // namespace proximity_auth
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698