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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: components/proximity_auth/unlock_manager.cc
diff --git a/components/proximity_auth/unlock_manager.cc b/components/proximity_auth/unlock_manager.cc
new file mode 100644
index 0000000000000000000000000000000000000000..e778042e3ea4ef15692600459b4530fba8d7114d
--- /dev/null
+++ b/components/proximity_auth/unlock_manager.cc
@@ -0,0 +1,465 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/proximity_auth/unlock_manager.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "components/proximity_auth/client.h"
+#include "components/proximity_auth/logging/logging.h"
+#include "components/proximity_auth/metrics.h"
+#include "components/proximity_auth/proximity_auth_client.h"
+#include "components/proximity_auth/proximity_monitor.h"
+#include "device/bluetooth/bluetooth_adapter_factory.h"
+
+#if defined(OS_CHROMEOS)
+#include "chromeos/dbus/dbus_thread_manager.h"
+#endif // defined(OS_CHROMEOS)
+
+namespace proximity_auth {
+namespace {
+
+// The maximum amount of time, in seconds, that the unlock manager can stay in
+// the 'waking up' state after resuming from sleep.
+const int kWakingUpDurationSecs = 5;
+
+// The limit, in seconds, on the elapsed time for an auth attempt. If an auth
+// attempt exceeds this limit, it will time out and be rejected. This is
+// provided as a failsafe, in case something goes wrong.
+const int kAuthAttemptTimeoutSecs = 5;
+
+// Returns the remote device's security settings state, for metrics,
+// corresponding to a remote status update.
+metrics::RemoteSecuritySettingsState GetRemoteSecuritySettingsState(
+ const RemoteStatusUpdate& status_update) {
+ switch (status_update.secure_screen_lock_state) {
+ case SECURE_SCREEN_LOCK_STATE_UNKNOWN:
+ return metrics::RemoteSecuritySettingsState::UNKNOWN;
+
+ case SECURE_SCREEN_LOCK_DISABLED:
+ switch (status_update.trust_agent_state) {
+ case TRUST_AGENT_UNSUPPORTED:
+ return metrics::RemoteSecuritySettingsState::
+ SCREEN_LOCK_DISABLED_TRUST_AGENT_UNSUPPORTED;
+ case TRUST_AGENT_DISABLED:
+ return metrics::RemoteSecuritySettingsState::
+ SCREEN_LOCK_DISABLED_TRUST_AGENT_DISABLED;
+ case TRUST_AGENT_ENABLED:
+ return metrics::RemoteSecuritySettingsState::
+ SCREEN_LOCK_DISABLED_TRUST_AGENT_ENABLED;
+ }
+
+ case SECURE_SCREEN_LOCK_ENABLED:
+ switch (status_update.trust_agent_state) {
+ case TRUST_AGENT_UNSUPPORTED:
+ return metrics::RemoteSecuritySettingsState::
+ SCREEN_LOCK_ENABLED_TRUST_AGENT_UNSUPPORTED;
+ case TRUST_AGENT_DISABLED:
+ return metrics::RemoteSecuritySettingsState::
+ SCREEN_LOCK_ENABLED_TRUST_AGENT_DISABLED;
+ case TRUST_AGENT_ENABLED:
+ return metrics::RemoteSecuritySettingsState::
+ SCREEN_LOCK_ENABLED_TRUST_AGENT_ENABLED;
+ }
+ }
+
+ NOTREACHED();
+ return metrics::RemoteSecuritySettingsState::UNKNOWN;
+}
+
+} // namespace
+
+UnlockManager::UnlockManager(ScreenlockType screenlock_type,
+ scoped_ptr<ProximityMonitor> proximity_monitor,
+ ProximityAuthClient* proximity_auth_client)
+ : screenlock_type_(screenlock_type),
+ controller_(nullptr),
+ client_(nullptr),
+ proximity_monitor_(proximity_monitor.Pass()),
+ proximity_auth_client_(proximity_auth_client),
+ is_locked_(false),
+ is_attempting_auth_(false),
+ is_waking_up_(false),
+ screenlock_state_(ScreenlockState::INACTIVE),
+ clear_waking_up_state_weak_ptr_factory_(this),
+ reject_auth_attempt_weak_ptr_factory_(this),
+ weak_ptr_factory_(this) {
+ // TODO(isherman): Register for auth attempt notifications, equivalent to the
+ // JavaScript lines:
+ //
+ // chrome.screenlockPrivate.onAuthAttempted.addListener(
+ // this.onAuthAttempted_.bind(this));
+
+ ScreenlockBridge* screenlock_bridge = ScreenlockBridge::Get();
+ screenlock_bridge->AddObserver(this);
+ OnScreenLockStateChanged(screenlock_bridge->IsLocked());
+
+#if defined(OS_CHROMEOS)
+ DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver(this);
+#endif // defined(OS_CHROMEOS)
+ SetWakingUpState(true);
+
+ if (device::BluetoothAdapterFactory::IsBluetoothAdapterAvailable()) {
+ device::BluetoothAdapterFactory::GetAdapter(
+ base::Bind(&UnlockManager::OnBluetoothAdapterInitialized,
+ weak_ptr_factory_.GetWeakPtr()));
+ }
+}
+
+UnlockManager::~UnlockManager() {
+ if (client_)
+ client_->RemoveObserver(this);
+
+ ScreenlockBridge::Get()->RemoveObserver(this);
+
+#if defined(OS_CHROMEOS)
+ DBusThreadManager::Get()->GetPowerManagerClient()->RemoveObserver(this);
+#endif // defined(OS_CHROMEOS)
+
+ if (bluetooth_adapter_)
+ bluetooth_adapter_->RemoveObserver(this);
+}
+
+bool UnlockManager::IsUnlockAllowed() {
+ return (remote_screenlock_state_ &&
+ *remote_screenlock_state_ == RemoteScreenlockState::UNLOCKED &&
+ controller_ &&
+ controller_->GetState() ==
+ Controller::State::SECURE_CHANNEL_ESTABLISHED &&
+ proximity_monitor_->IsUnlockAllowed() &&
+ (screenlock_type_ != ScreenlockType::SIGN_IN ||
+ (client_ && client_->SupportsSignIn())));
+}
+
+void UnlockManager::SetController(Controller* controller) {
+ if (client_) {
+ client_->RemoveObserver(this);
+ client_ = nullptr;
+ }
+
+ controller_ = controller;
+ if (controller_)
+ SetWakingUpState(true);
+
+ UpdateLockScreen();
+}
+
+void UnlockManager::OnControllerStateChanged() {
+ Controller::State state = controller_->GetState();
+ PA_LOG(INFO) << "[Unlock] Controller state changed: "
+ << static_cast<int>(state);
+
+ remote_screenlock_state_.reset();
+ if (state == Controller::State::SECURE_CHANNEL_ESTABLISHED) {
+ client_ = controller_->GetClient();
+ client_->AddObserver(this);
+ }
+
+ if (state == Controller::State::AUTHENTICATION_FAILED)
+ SetWakingUpState(false);
+
+ UpdateLockScreen();
+}
+
+void UnlockManager::OnUnlockEventSent(bool success) {
+ if (!is_attempting_auth_) {
+ PA_LOG(ERROR) << "[Unlock] Sent easy_unlock event, but no auth attempted.";
+ return;
+ }
+
+ if (sign_in_secret_ && success)
+ 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
+
+ AcceptAuthAttempt(success);
+}
+
+void UnlockManager::OnRemoteStatusUpdate(
+ const RemoteStatusUpdate& status_update) {
+ PA_LOG(INFO) << "[Unlock] Status Update: ("
+ << "user_present=" << status_update.user_presence << ", "
+ << "secure_screen_lock="
+ << status_update.secure_screen_lock_state << ", "
+ << "trust_agent=" << status_update.trust_agent_state << ")";
+ metrics::RecordRemoteSecuritySettingsState(
+ GetRemoteSecuritySettingsState(status_update));
+
+ remote_screenlock_state_.reset(new RemoteScreenlockState(
+ GetScreenlockStateFromRemoteUpdate(status_update)));
+
+ // This also calls |UpdateLockScreen()|
+ SetWakingUpState(false);
+}
+
+void UnlockManager::OnDecryptResponse(scoped_ptr<std::string> decrypted_bytes) {
+ if (!is_attempting_auth_) {
+ PA_LOG(ERROR) << "[Unlock] Decrypt response received but not attempting "
+ << "auth.";
+ return;
+ }
+
+ if (!decrypted_bytes) {
+ PA_LOG(INFO) << "[Unlock] Failed to decrypt sign-in challenge.";
+ AcceptAuthAttempt(false);
+ } else {
+ sign_in_secret_ = decrypted_bytes.Pass();
+ client_->DispatchUnlockEvent();
+ }
+}
+
+void UnlockManager::OnUnlockResponse(bool success) {
+ if (!is_attempting_auth_) {
+ PA_LOG(ERROR) << "[Unlock] Unlock response received but not attempting "
+ << "auth.";
+ return;
+ }
+
+ PA_LOG(INFO) << "[Unlock] Unlock response from remote device: "
+ << (success ? "success" : "failure");
+ if (success)
+ client_->DispatchUnlockEvent();
+ else
+ AcceptAuthAttempt(false);
+}
+
+void UnlockManager::OnDisconnected() {
+ client_->RemoveObserver(this);
+ client_ = nullptr;
+}
+
+void UnlockManager::OnScreenDidLock(
+ ScreenlockBridge::LockHandler::ScreenType screen_type) {
+ OnScreenLockStateChanged(true);
+}
+
+void UnlockManager::OnScreenDidUnlock(
+ ScreenlockBridge::LockHandler::ScreenType screen_type) {
+ OnScreenLockStateChanged(false);
+}
+
+void UnlockManager::OnFocusedUserChanged(const std::string& user_id) {}
+
+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.
+ // TODO(tengs): Chrome will only start connecting to the phone when
+ // the screen is locked, for privacy reasons. We should reinvestigate
+ // this behaviour if we want automatic locking.
+ if (is_locked && bluetooth_adapter_ && bluetooth_adapter_->IsPowered() &&
+ controller_ &&
+ controller_->GetState() == Controller::State::FINDING_CONNECTION) {
+ SetWakingUpState(true);
+ }
+
+ is_locked_ = is_locked;
+ UpdateProximityMonitorState();
+}
+
+void UnlockManager::OnBluetoothAdapterInitialized(
+ scoped_refptr<device::BluetoothAdapter> adapter) {
+ bluetooth_adapter_ = adapter;
+ bluetooth_adapter_->AddObserver(this);
+}
+
+void UnlockManager::AdapterPresentChanged(device::BluetoothAdapter* adapter,
+ bool present) {
+ UpdateLockScreen();
+}
+
+void UnlockManager::AdapterPoweredChanged(device::BluetoothAdapter* adapter,
+ bool powered) {
+ UpdateLockScreen();
+}
+
+#if defined(OS_CHROMEOS)
+void UnlockManager::SuspendDone(const base::TimeDelta& sleep_duration) {
+ SetWakingUpState(true);
+}
+#endif // defined(OS_CHROMEOS)
+
+void UnlockManager::OnAuthAttempted(
+ ScreenlockBridge::LockHandler::AuthType auth_type) {
+ if (is_attempting_auth_) {
+ PA_LOG(INFO) << "[Unlock] Already attempting auth.";
+ return;
+ }
+
+ if (auth_type != ScreenlockBridge::LockHandler::USER_CLICK)
+ return;
+
+ is_attempting_auth_ = true;
+
+ if (!controller_) {
+ PA_LOG(ERROR) << "[Unlock] No controller active when auth is attempted";
+ AcceptAuthAttempt(false);
+ UpdateLockScreen();
+ return;
+ }
+
+ if (!IsUnlockAllowed()) {
+ AcceptAuthAttempt(false);
+ UpdateLockScreen();
+ return;
+ }
+
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&UnlockManager::AcceptAuthAttempt,
+ reject_auth_attempt_weak_ptr_factory_.GetWeakPtr(), false),
+ base::TimeDelta::FromSeconds(kAuthAttemptTimeoutSecs));
+
+ if (screenlock_type_ == ScreenlockType::SIGN_IN) {
+ SendSignInChallenge();
+ } else {
+ if (client_->SupportsSignIn()) {
+ client_->RequestUnlock();
+ } else {
+ PA_LOG(INFO) << "[Unlock] Protocol v3.1 not supported, skipping "
+ << "request_unlock.";
+ client_->DispatchUnlockEvent();
+ }
+ }
+}
+
+void UnlockManager::SendSignInChallenge() {
+ // TODO(isherman): Implement.
+ NOTIMPLEMENTED();
+}
+
+ScreenlockState UnlockManager::GetScreenlockState() {
+ if (!controller_ || controller_->GetState() == Controller::State::STOPPED)
+ return ScreenlockState::INACTIVE;
+
+ if (IsUnlockAllowed())
+ return ScreenlockState::AUTHENTICATED;
+
+ if (controller_->GetState() == Controller::State::AUTHENTICATION_FAILED)
+ return ScreenlockState::PHONE_NOT_AUTHENTICATED;
+
+ if (is_waking_up_)
+ return ScreenlockState::BLUETOOTH_CONNECTING;
+
+ if (!bluetooth_adapter_ || !bluetooth_adapter_->IsPowered())
+ return ScreenlockState::NO_BLUETOOTH;
+
+ if (screenlock_type_ == ScreenlockType::SIGN_IN && client_ &&
+ !client_->SupportsSignIn())
+ return ScreenlockState::PHONE_UNSUPPORTED;
+
+ // If the RSSI is too low, then the remote device is nowhere near the local
+ // device. This message should take priority over messages about screen lock
+ // states.
+ if (!proximity_monitor_->IsUnlockAllowed() &&
+ !proximity_monitor_->IsInRssiRange())
+ return ScreenlockState::RSSI_TOO_LOW;
+
+ if (remote_screenlock_state_) {
+ switch (*remote_screenlock_state_) {
+ case RemoteScreenlockState::DISABLED:
+ return ScreenlockState::PHONE_NOT_LOCKABLE;
+
+ case RemoteScreenlockState::LOCKED:
+ if (proximity_monitor_->GetStrategy() ==
+ ProximityMonitor::Strategy::CHECK_TRANSMIT_POWER &&
+ !proximity_monitor_->IsUnlockAllowed()) {
+ return ScreenlockState::PHONE_LOCKED_AND_TX_POWER_TOO_HIGH;
+ }
+ return ScreenlockState::PHONE_LOCKED;
+
+ case RemoteScreenlockState::UNKNOWN:
+ return ScreenlockState::PHONE_UNSUPPORTED;
+
+ case RemoteScreenlockState::UNLOCKED:
+ // Handled by the code below.
+ break;
+ }
+ }
+
+ if (!proximity_monitor_->IsUnlockAllowed()) {
+ ProximityMonitor::Strategy strategy = proximity_monitor_->GetStrategy();
+ if (strategy != ProximityMonitor::Strategy::CHECK_TRANSMIT_POWER) {
+ // CHECK_RSSI should have been handled above, and no other states should
+ // prevent unlocking.
+ PA_LOG(ERROR) << "[Unlock] Invalid ProximityMonitor strategy: "
+ << static_cast<int>(strategy);
+ return ScreenlockState::NO_PHONE;
+ }
+ return ScreenlockState::TX_POWER_TOO_HIGH;
+ }
+
+ return ScreenlockState::NO_PHONE;
+}
+
+void UnlockManager::UpdateLockScreen() {
+ UpdateProximityMonitorState();
+
+ ScreenlockState new_state = GetScreenlockState();
+ if (screenlock_state_ == new_state)
+ return;
+
+ proximity_auth_client_->UpdateScreenlockState(new_state);
+ screenlock_state_ = new_state;
+}
+
+void UnlockManager::UpdateProximityMonitorState() {
+ if (is_locked_ && controller_ &&
+ controller_->GetState() ==
+ Controller::State::SECURE_CHANNEL_ESTABLISHED) {
+ proximity_monitor_->Start();
+ } else {
+ proximity_monitor_->Stop();
+ }
+}
+
+void UnlockManager::SetWakingUpState(bool is_waking_up) {
+ is_waking_up_ = is_waking_up;
+
+ // Clear the waking up state after a timeout.
+ clear_waking_up_state_weak_ptr_factory_.InvalidateWeakPtrs();
+ if (is_waking_up_) {
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&UnlockManager::SetWakingUpState,
+ clear_waking_up_state_weak_ptr_factory_.GetWeakPtr(), false),
+ base::TimeDelta::FromSeconds(kWakingUpDurationSecs));
+ }
+
+ UpdateLockScreen();
+}
+
+void UnlockManager::AcceptAuthAttempt(bool should_accept) {
+ if (!is_attempting_auth_)
+ return;
+
+ // Cancel the pending task to time out the auth attempt.
+ reject_auth_attempt_weak_ptr_factory_.InvalidateWeakPtrs();
+
+ if (should_accept)
+ proximity_monitor_->RecordProximityMetricsOnAuthSuccess();
+
+ is_attempting_auth_ = false;
+ proximity_auth_client_->FinalizeUnlock(should_accept);
+}
+
+UnlockManager::RemoteScreenlockState
+UnlockManager::GetScreenlockStateFromRemoteUpdate(RemoteStatusUpdate update) {
+ switch (update.secure_screen_lock_state) {
+ case SECURE_SCREEN_LOCK_DISABLED:
+ return RemoteScreenlockState::DISABLED;
+
+ case SECURE_SCREEN_LOCK_ENABLED:
+ if (update.user_presence == USER_PRESENT)
+ return RemoteScreenlockState::UNLOCKED;
+
+ return RemoteScreenlockState::LOCKED;
+
+ case SECURE_SCREEN_LOCK_STATE_UNKNOWN:
+ return RemoteScreenlockState::UNKNOWN;
+ }
+
+ NOTREACHED();
+ return RemoteScreenlockState::UNKNOWN;
+}
+
+} // namespace proximity_auth

Powered by Google App Engine
This is Rietveld 408576698