chromium/chromeos/ash/components/proximity_auth/unlock_manager_impl_unittest.cc

// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chromeos/ash/components/proximity_auth/unlock_manager_impl.h"

#include <memory>
#include <utility>

#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/test_simple_task_runner.h"
#include "base/timer/mock_timer.h"
#include "build/build_config.h"
#include "chromeos/ash/components/multidevice/logging/logging.h"
#include "chromeos/ash/components/multidevice/remote_device_test_util.h"
#include "chromeos/ash/components/proximity_auth/fake_lock_handler.h"
#include "chromeos/ash/components/proximity_auth/fake_remote_device_life_cycle.h"
#include "chromeos/ash/components/proximity_auth/messenger.h"
#include "chromeos/ash/components/proximity_auth/mock_proximity_auth_client.h"
#include "chromeos/ash/components/proximity_auth/proximity_monitor.h"
#include "chromeos/ash/components/proximity_auth/remote_device_life_cycle.h"
#include "chromeos/ash/components/proximity_auth/remote_status_update.h"
#include "chromeos/ash/services/secure_channel/connection.h"
#include "chromeos/ash/services/secure_channel/public/cpp/client/fake_client_channel.h"
#include "chromeos/dbus/power/fake_power_manager_client.h"
#include "chromeos/dbus/power/power_manager_client.h"
#include "chromeos/dbus/power_manager/suspend.pb.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "device/bluetooth/test/mock_bluetooth_adapter.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

using testing::_;
using testing::AtLeast;
using testing::Invoke;
using testing::NiceMock;
using testing::Return;

namespace proximity_auth {
namespace {

using SmartLockState = ash::SmartLockState;

// Note that the trust agent state is currently ignored by the UnlockManager
// implementation.
RemoteStatusUpdate kRemoteScreenUnlocked = {
    USER_PRESENT, SECURE_SCREEN_LOCK_ENABLED, TRUST_AGENT_UNSUPPORTED};
RemoteStatusUpdate kRemoteScreenLocked = {
    USER_ABSENT, SECURE_SCREEN_LOCK_ENABLED, TRUST_AGENT_UNSUPPORTED};
RemoteStatusUpdate kRemoteScreenlockDisabled = {
    USER_PRESENT, SECURE_SCREEN_LOCK_DISABLED, TRUST_AGENT_UNSUPPORTED};
RemoteStatusUpdate kRemoteScreenlockStateUnknown = {
    USER_PRESENCE_UNKNOWN, SECURE_SCREEN_LOCK_STATE_UNKNOWN,
    TRUST_AGENT_UNSUPPORTED};

class MockMessenger : public Messenger {
 public:
  MockMessenger() {}

  MockMessenger(const MockMessenger&) = delete;
  MockMessenger& operator=(const MockMessenger&) = delete;

  ~MockMessenger() override {}

  MOCK_METHOD1(AddObserver, void(MessengerObserver* observer));
  MOCK_METHOD1(RemoveObserver, void(MessengerObserver* observer));
  MOCK_METHOD0(DispatchUnlockEvent, void());
  MOCK_METHOD1(RequestDecryption, void(const std::string& challenge));
  MOCK_METHOD0(RequestUnlock, void());
  MOCK_CONST_METHOD0(GetConnection, ash::secure_channel::Connection*());
  MOCK_CONST_METHOD0(GetChannel, ash::secure_channel::ClientChannel*());
};

class MockProximityMonitor : public ProximityMonitor {
 public:
  explicit MockProximityMonitor(base::OnceClosure destroy_callback)
      : destroy_callback_(std::move(destroy_callback)), started_(false) {
    ON_CALL(*this, IsUnlockAllowed()).WillByDefault(Return(true));
  }

  MockProximityMonitor(const MockProximityMonitor&) = delete;
  MockProximityMonitor& operator=(const MockProximityMonitor&) = delete;

  ~MockProximityMonitor() override { std::move(destroy_callback_).Run(); }

  void Start() override { started_ = true; }
  void Stop() override {}
  MOCK_CONST_METHOD0(IsUnlockAllowed, bool());
  MOCK_METHOD0(RecordProximityMetricsOnAuthSuccess, void());

  bool started() { return started_; }

 private:
  base::OnceClosure destroy_callback_;
  bool started_;
};

class TestUnlockManager : public UnlockManagerImpl {
 public:
  explicit TestUnlockManager(ProximityAuthClient* proximity_auth_client)
      : UnlockManagerImpl(proximity_auth_client) {}

  TestUnlockManager(const TestUnlockManager&) = delete;
  TestUnlockManager& operator=(const TestUnlockManager&) = delete;

  ~TestUnlockManager() override {}

  using MessengerObserver::OnDisconnected;
  using MessengerObserver::OnRemoteStatusUpdate;
  using MessengerObserver::OnUnlockEventSent;
  using MessengerObserver::OnUnlockResponse;
  using UnlockManager::OnAuthAttempted;

  MockProximityMonitor* proximity_monitor() { return proximity_monitor_; }
  bool proximity_monitor_destroyed() { return proximity_monitor_destroyed_; }

 private:
  std::unique_ptr<ProximityMonitor> CreateProximityMonitor(
      RemoteDeviceLifeCycle* life_cycle) override {
    std::unique_ptr<MockProximityMonitor> proximity_monitor(
        new NiceMock<MockProximityMonitor>(
            base::BindOnce(&TestUnlockManager::OnProximityMonitorDestroyed,
                           base::Unretained(this))));
    proximity_monitor_destroyed_ = false;

    proximity_monitor_ = proximity_monitor.get();
    return proximity_monitor;
  }

  void OnProximityMonitorDestroyed() { proximity_monitor_destroyed_ = true; }

  // Owned by the super class.
  raw_ptr<MockProximityMonitor, DanglingUntriaged> proximity_monitor_ = nullptr;
  bool proximity_monitor_destroyed_ = false;
};

// Creates a mock Bluetooth adapter and sets it as the global adapter for
// testing.
scoped_refptr<device::MockBluetoothAdapter>
CreateAndRegisterMockBluetoothAdapter() {
  scoped_refptr<device::MockBluetoothAdapter> adapter =
      new NiceMock<device::MockBluetoothAdapter>();
  device::BluetoothAdapterFactory::SetAdapterForTesting(adapter);
  return adapter;
}

}  // namespace

class ProximityAuthUnlockManagerImplTest : public testing::Test {
 public:
  ProximityAuthUnlockManagerImplTest()
      : remote_device_(ash::multidevice::CreateRemoteDeviceRefForTest()),
        local_device_(ash::multidevice::CreateRemoteDeviceRefForTest()),
        life_cycle_(remote_device_, local_device_),
        fake_client_channel_(
            std::make_unique<ash::secure_channel::FakeClientChannel>()),
        bluetooth_adapter_(CreateAndRegisterMockBluetoothAdapter()),
        task_runner_(new base::TestSimpleTaskRunner()),
        thread_task_runner_current_default_handle_(task_runner_) {}

  ~ProximityAuthUnlockManagerImplTest() override = default;

  void SetUp() override {
    chromeos::PowerManagerClient::InitializeFake();

    ON_CALL(*bluetooth_adapter_, IsPresent()).WillByDefault(Return(true));
    ON_CALL(*bluetooth_adapter_, IsPowered()).WillByDefault(Return(true));

    ON_CALL(messenger_, GetChannel())
        .WillByDefault(Return(fake_client_channel_.get()));
    life_cycle_.set_messenger(&messenger_);
    life_cycle_.set_channel(fake_client_channel_.get());
  }

  void TearDown() override {
    // Make sure to verify the mock prior to the destruction of the unlock
    // manager, as otherwise it's impossible to tell whether calls to Stop()
    // occur as a side-effect of the destruction or from the code intended to be
    // under test.
    if (proximity_monitor())
      testing::Mock::VerifyAndClearExpectations(proximity_monitor());

    unlock_manager_.reset();

    chromeos::PowerManagerClient::Shutdown();
  }

  void CreateUnlockManager() {
    unlock_manager_ =
        std::make_unique<TestUnlockManager>(&proximity_auth_client_);

    auto mock_timer = std::make_unique<base::MockOneShotTimer>();
    mock_bluetooth_suspension_recovery_timer_ = mock_timer.get();
    unlock_manager_->SetBluetoothSuspensionRecoveryTimerForTesting(
        std::move(mock_timer));
  }

  void SimulateUserPresentState() {
    unlock_manager_->SetRemoteDeviceLifeCycle(&life_cycle_);
    life_cycle_.ChangeState(
        RemoteDeviceLifeCycle::State::SECURE_CHANNEL_ESTABLISHED);
    unlock_manager_->OnRemoteStatusUpdate(kRemoteScreenUnlocked);
  }

  void RunPendingTasks() { task_runner_->RunPendingTasks(); }

  MockProximityMonitor* proximity_monitor() {
    return unlock_manager_ ? unlock_manager_->proximity_monitor() : nullptr;
  }

  bool proximity_monitor_destroyed() {
    return unlock_manager_ ? unlock_manager_->proximity_monitor_destroyed()
                           : false;
  }

 protected:
  ash::multidevice::RemoteDeviceRef remote_device_;
  ash::multidevice::RemoteDeviceRef local_device_;
  FakeRemoteDeviceLifeCycle life_cycle_;
  std::unique_ptr<ash::secure_channel::FakeClientChannel> fake_client_channel_;

  // Mock used for verifying interactions with the Bluetooth subsystem.
  scoped_refptr<device::MockBluetoothAdapter> bluetooth_adapter_;

  NiceMock<MockProximityAuthClient> proximity_auth_client_;
  NiceMock<MockMessenger> messenger_;
  std::unique_ptr<TestUnlockManager> unlock_manager_;
  raw_ptr<base::MockOneShotTimer, DanglingUntriaged>
      mock_bluetooth_suspension_recovery_timer_ = nullptr;

 private:
  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
  base::SingleThreadTaskRunner::CurrentDefaultHandle
      thread_task_runner_current_default_handle_;
  FakeLockHandler lock_handler_;
  ash::multidevice::ScopedDisableLoggingForTesting disable_logging_;
};

TEST_F(ProximityAuthUnlockManagerImplTest, IsUnlockAllowed_InitialState) {
  CreateUnlockManager();
  EXPECT_FALSE(unlock_manager_->IsUnlockAllowed());
}

TEST_F(ProximityAuthUnlockManagerImplTest,
       IsUnlockAllowed_SessionLock_AllGood) {
  CreateUnlockManager();

  unlock_manager_->SetRemoteDeviceLifeCycle(&life_cycle_);
  life_cycle_.ChangeState(
      RemoteDeviceLifeCycle::State::SECURE_CHANNEL_ESTABLISHED);
  unlock_manager_->OnRemoteStatusUpdate(kRemoteScreenUnlocked);

  EXPECT_TRUE(unlock_manager_->IsUnlockAllowed());
}

TEST_F(ProximityAuthUnlockManagerImplTest,
       IsUnlockAllowed_DisallowedByProximityMonitor) {
  CreateUnlockManager();

  unlock_manager_->SetRemoteDeviceLifeCycle(&life_cycle_);
  life_cycle_.ChangeState(
      RemoteDeviceLifeCycle::State::SECURE_CHANNEL_ESTABLISHED);

  ON_CALL(*proximity_monitor(), IsUnlockAllowed()).WillByDefault(Return(false));
  unlock_manager_->OnRemoteStatusUpdate(kRemoteScreenUnlocked);
  EXPECT_FALSE(unlock_manager_->IsUnlockAllowed());
}

TEST_F(ProximityAuthUnlockManagerImplTest,
       IsUnlockAllowed_RemoteDeviceLifeCycleIsNull) {
  CreateUnlockManager();

  unlock_manager_->SetRemoteDeviceLifeCycle(nullptr);
  unlock_manager_->OnRemoteStatusUpdate(kRemoteScreenUnlocked);

  EXPECT_FALSE(unlock_manager_->IsUnlockAllowed());
}

TEST_F(ProximityAuthUnlockManagerImplTest,
       IsUnlockAllowed_RemoteScreenlockStateLocked) {
  CreateUnlockManager();

  unlock_manager_->SetRemoteDeviceLifeCycle(&life_cycle_);
  life_cycle_.ChangeState(
      RemoteDeviceLifeCycle::State::SECURE_CHANNEL_ESTABLISHED);
  unlock_manager_->OnRemoteStatusUpdate(kRemoteScreenLocked);

  EXPECT_FALSE(unlock_manager_->IsUnlockAllowed());
}

TEST_F(ProximityAuthUnlockManagerImplTest, IsUnlockAllowed_UserIsSecondary) {
  CreateUnlockManager();

  unlock_manager_->SetRemoteDeviceLifeCycle(&life_cycle_);
  life_cycle_.ChangeState(
      RemoteDeviceLifeCycle::State::SECURE_CHANNEL_ESTABLISHED);
  unlock_manager_->OnRemoteStatusUpdate({USER_PRESENCE_SECONDARY,
                                         SECURE_SCREEN_LOCK_ENABLED,
                                         TRUST_AGENT_UNSUPPORTED});

  EXPECT_FALSE(unlock_manager_->IsUnlockAllowed());
}

TEST_F(ProximityAuthUnlockManagerImplTest,
       IsUnlockAllowed_PrimaryUserInBackground) {
  CreateUnlockManager();

  unlock_manager_->SetRemoteDeviceLifeCycle(&life_cycle_);
  life_cycle_.ChangeState(
      RemoteDeviceLifeCycle::State::SECURE_CHANNEL_ESTABLISHED);
  unlock_manager_->OnRemoteStatusUpdate({USER_PRESENCE_BACKGROUND,
                                         SECURE_SCREEN_LOCK_ENABLED,
                                         TRUST_AGENT_UNSUPPORTED});

  EXPECT_FALSE(unlock_manager_->IsUnlockAllowed());
}

TEST_F(ProximityAuthUnlockManagerImplTest,
       IsUnlockAllowed_RemoteScreenlockStateUnknown) {
  CreateUnlockManager();

  unlock_manager_->SetRemoteDeviceLifeCycle(&life_cycle_);
  life_cycle_.ChangeState(
      RemoteDeviceLifeCycle::State::SECURE_CHANNEL_ESTABLISHED);
  unlock_manager_->OnRemoteStatusUpdate(kRemoteScreenlockStateUnknown);

  EXPECT_FALSE(unlock_manager_->IsUnlockAllowed());
}

TEST_F(ProximityAuthUnlockManagerImplTest,
       IsUnlockAllowed_RemoteScreenlockStateDisabled) {
  CreateUnlockManager();

  unlock_manager_->SetRemoteDeviceLifeCycle(&life_cycle_);
  life_cycle_.ChangeState(
      RemoteDeviceLifeCycle::State::SECURE_CHANNEL_ESTABLISHED);
  unlock_manager_->OnRemoteStatusUpdate(kRemoteScreenlockDisabled);

  EXPECT_FALSE(unlock_manager_->IsUnlockAllowed());
}

TEST_F(ProximityAuthUnlockManagerImplTest,
       IsUnlockAllowed_RemoteScreenlockStateNotYetReceived) {
  CreateUnlockManager();

  unlock_manager_->SetRemoteDeviceLifeCycle(&life_cycle_);
  life_cycle_.ChangeState(
      RemoteDeviceLifeCycle::State::SECURE_CHANNEL_ESTABLISHED);

  EXPECT_FALSE(unlock_manager_->IsUnlockAllowed());
}

TEST_F(ProximityAuthUnlockManagerImplTest, SetRemoteDeviceLifeCycle_SetToNull) {
  CreateUnlockManager();
  SimulateUserPresentState();

  EXPECT_CALL(proximity_auth_client_,
              UpdateSmartLockState(SmartLockState::kInactive));
  unlock_manager_->SetRemoteDeviceLifeCycle(nullptr);
}

TEST_F(ProximityAuthUnlockManagerImplTest,
       SetRemoteDeviceLifeCycle_ExistingRemoteDeviceLifeCycle) {
  CreateUnlockManager();
  SimulateUserPresentState();

  EXPECT_CALL(proximity_auth_client_,
              UpdateSmartLockState(SmartLockState::kConnectingToPhone));
  unlock_manager_->SetRemoteDeviceLifeCycle(&life_cycle_);
}

TEST_F(ProximityAuthUnlockManagerImplTest,
       SetRemoteDeviceLifeCycle_AuthenticationFailed) {
  CreateUnlockManager();
  SimulateUserPresentState();

  unlock_manager_->SetRemoteDeviceLifeCycle(nullptr);

  life_cycle_.ChangeState(RemoteDeviceLifeCycle::State::AUTHENTICATION_FAILED);

  EXPECT_CALL(proximity_auth_client_,
              UpdateSmartLockState(SmartLockState::kPhoneNotAuthenticated));
  unlock_manager_->SetRemoteDeviceLifeCycle(&life_cycle_);
}

TEST_F(ProximityAuthUnlockManagerImplTest, SetRemoteDeviceLifeCycle_WakingUp) {
  CreateUnlockManager();
  SimulateUserPresentState();

  unlock_manager_->SetRemoteDeviceLifeCycle(nullptr);

  life_cycle_.ChangeState(RemoteDeviceLifeCycle::State::FINDING_CONNECTION);

  EXPECT_CALL(proximity_auth_client_,
              UpdateSmartLockState(SmartLockState::kConnectingToPhone));
  unlock_manager_->SetRemoteDeviceLifeCycle(&life_cycle_);
}

TEST_F(ProximityAuthUnlockManagerImplTest,
       SetRemoteDeviceLifeCycle_TimesOutBeforeConnection) {
  CreateUnlockManager();

  life_cycle_.set_messenger(nullptr);
  life_cycle_.ChangeState(RemoteDeviceLifeCycle::State::FINDING_CONNECTION);

  EXPECT_CALL(proximity_auth_client_,
              UpdateSmartLockState(SmartLockState::kConnectingToPhone));
  unlock_manager_->SetRemoteDeviceLifeCycle(&life_cycle_);

  EXPECT_CALL(proximity_auth_client_,
              UpdateSmartLockState(SmartLockState::kPhoneNotFound));
  // Simulate timing out before a connection is established.
  RunPendingTasks();
}

TEST_F(ProximityAuthUnlockManagerImplTest,
       SetRemoteDeviceLifeCycle_NullRemoteDeviceLifeCycle_NoProximityMonitor) {
  CreateUnlockManager();
  SimulateUserPresentState();
  unlock_manager_->SetRemoteDeviceLifeCycle(nullptr);
}

TEST_F(
    ProximityAuthUnlockManagerImplTest,
    SetRemoteDeviceLifeCycle_ConnectingRemoteDeviceLifeCycle_StopsProximityMonitor) {
  CreateUnlockManager();
  SimulateUserPresentState();

  life_cycle_.ChangeState(RemoteDeviceLifeCycle::State::FINDING_CONNECTION);
  EXPECT_TRUE(proximity_monitor_destroyed());
}

TEST_F(
    ProximityAuthUnlockManagerImplTest,
    SetRemoteDeviceLifeCycle_ConnectedRemoteDeviceLifeCycle_StartsProximityMonitor) {
  CreateUnlockManager();
  unlock_manager_->SetRemoteDeviceLifeCycle(&life_cycle_);

  life_cycle_.ChangeState(
      RemoteDeviceLifeCycle::State::SECURE_CHANNEL_ESTABLISHED);
  EXPECT_TRUE(proximity_monitor()->started());
}

// Regression test for crbug.com/931929. Capture the case where the phone is
// connected to, connection is lost, and then a new connection is made shortly
// after.
TEST_F(ProximityAuthUnlockManagerImplTest,
       SetRemoteDeviceLifeCycle_TwiceConnectedRemoteDeviceLifeCycle) {
  CreateUnlockManager();

  unlock_manager_->SetRemoteDeviceLifeCycle(&life_cycle_);

  life_cycle_.ChangeState(
      RemoteDeviceLifeCycle::State::SECURE_CHANNEL_ESTABLISHED);
  EXPECT_TRUE(proximity_monitor()->started());

  // Simulate the phone connection being lost. The ProximityMonitor is stale
  // and should have been destroyed.
  life_cycle_.ChangeState(RemoteDeviceLifeCycle::State::FINDING_CONNECTION);
  EXPECT_TRUE(proximity_monitor_destroyed());

  life_cycle_.ChangeState(
      RemoteDeviceLifeCycle::State::SECURE_CHANNEL_ESTABLISHED);
  EXPECT_FALSE(proximity_monitor_destroyed());
  EXPECT_TRUE(proximity_monitor()->started());
}

TEST_F(ProximityAuthUnlockManagerImplTest, BluetoothAdapterNotPresent) {
  ON_CALL(*bluetooth_adapter_, IsPresent()).WillByDefault(Return(false));

  CreateUnlockManager();

  EXPECT_CALL(proximity_auth_client_,
              UpdateSmartLockState(SmartLockState::kBluetoothDisabled));

  unlock_manager_->SetRemoteDeviceLifeCycle(&life_cycle_);
  EXPECT_FALSE(life_cycle_.started());
}

TEST_F(ProximityAuthUnlockManagerImplTest, BluetoothAdapterPowerChanges) {
  ON_CALL(*bluetooth_adapter_, IsPowered()).WillByDefault(Return(false));

  CreateUnlockManager();

  EXPECT_CALL(proximity_auth_client_,
              UpdateSmartLockState(SmartLockState::kBluetoothDisabled));

  unlock_manager_->SetRemoteDeviceLifeCycle(&life_cycle_);
  EXPECT_FALSE(life_cycle_.started());

  EXPECT_CALL(proximity_auth_client_,
              UpdateSmartLockState(SmartLockState::kConnectingToPhone));
  ON_CALL(*bluetooth_adapter_, IsPowered()).WillByDefault(Return(true));
  bluetooth_adapter_->NotifyAdapterPoweredChanged(true);
  EXPECT_TRUE(life_cycle_.started());
}

TEST_F(
    ProximityAuthUnlockManagerImplTest,
    CacheBluetoothAdapterStateAfterSuspendAndResume_AttemptConnectionWhileBluetoothAdapterIsStillRecovering) {
  CreateUnlockManager();

  ASSERT_FALSE(mock_bluetooth_suspension_recovery_timer_->IsRunning());

  chromeos::FakePowerManagerClient::Get()->SendSuspendImminent(
      power_manager::SuspendImminent_Reason_LID_CLOSED);

  // Simulate https://crbug.com/986896 by returning false for presence and power
  // directly after resuming, but do not fire
  // |mock_bluetooth_suspension_recovery_timer_|, simulating that not enough
  // time has passed for the BluetoothAdapter to recover. It's expected under
  // these conditions that:
  // * ProximityAuthClient::UpdateSmartLockState() never be called with
  //   SmartLockState::kBluetoothDisabled.
  // * ProximityAuthClient::UpdateSmartLockState() only be called once with
  //   SmartLockState::BLUETOOTH_CONNECTING, because it should only be called
  //   on when the SmartLockState value changes.
  EXPECT_CALL(proximity_auth_client_,
              UpdateSmartLockState(SmartLockState::kBluetoothDisabled))
      .Times(0);
  EXPECT_CALL(proximity_auth_client_,
              UpdateSmartLockState(SmartLockState::kConnectingToPhone));

  ON_CALL(*bluetooth_adapter_, IsPresent()).WillByDefault(Return(false));
  ON_CALL(*bluetooth_adapter_, IsPowered()).WillByDefault(Return(false));

  chromeos::FakePowerManagerClient::Get()->SendSuspendDone();
  EXPECT_TRUE(mock_bluetooth_suspension_recovery_timer_->IsRunning());

  // Simulate how ProximityAuthSystem, the owner of UnlockManager, reacts to
  // resume: providing a new RemoteDeviceLifeCycle. This shouldn't trigger a new
  // call to ProximityAuthClient::UpdateSmartLockState().
  unlock_manager_->SetRemoteDeviceLifeCycle(&life_cycle_);
  EXPECT_TRUE(life_cycle_.started());

  EXPECT_TRUE(mock_bluetooth_suspension_recovery_timer_->IsRunning());
}

TEST_F(
    ProximityAuthUnlockManagerImplTest,
    CacheBluetoothAdapterStateAfterSuspendAndResume_AttemptConnectionOnceBluetoothAdapterHasHadTimeToRecover) {
  CreateUnlockManager();

  ASSERT_FALSE(mock_bluetooth_suspension_recovery_timer_->IsRunning());

  chromeos::FakePowerManagerClient::Get()->SendSuspendImminent(
      power_manager::SuspendImminent_Reason_LID_CLOSED);

  // Simulate https://crbug.com/986896 by returning false for presence and power
  // directly after resuming, and then fire
  // |mock_bluetooth_suspension_recovery_timer_|, simulating that enough time
  // has passed for the BluetoothAdapter to recover - this means that Bluetooth
  // is truly off after resume and the user should be visually informed as such.
  // It's expected under these conditions that:
  // * ProximityAuthClient::UpdateSmartLockState() only be called once with
  //   SmartLockState::kBluetoothDisabled, but after the timer fires (this is
  //   impossible to explicitly do in code with mocks, unfortunately).
  // * ProximityAuthClient::UpdateSmartLockState() only be called once with
  //   SmartLockState::BLUETOOTH_CONNECTING, directly after SuspendDone.
  EXPECT_CALL(proximity_auth_client_,
              UpdateSmartLockState(SmartLockState::kBluetoothDisabled));
  EXPECT_CALL(proximity_auth_client_,
              UpdateSmartLockState(SmartLockState::kConnectingToPhone));

  ON_CALL(*bluetooth_adapter_, IsPresent()).WillByDefault(Return(false));
  ON_CALL(*bluetooth_adapter_, IsPowered()).WillByDefault(Return(false));

  chromeos::FakePowerManagerClient::Get()->SendSuspendDone();
  EXPECT_TRUE(mock_bluetooth_suspension_recovery_timer_->IsRunning());

  // Simulate how ProximityAuthSystem, the owner of UnlockManager, reacts to
  // resume: providing a new RemoteDeviceLifeCycle. This shouldn't trigger a new
  // call to ProximityAuthClient::UpdateSmartLockState().
  unlock_manager_->SetRemoteDeviceLifeCycle(&life_cycle_);
  EXPECT_TRUE(life_cycle_.started());

  ON_CALL(*bluetooth_adapter_, IsPresent()).WillByDefault(Return(false));
  ON_CALL(*bluetooth_adapter_, IsPowered()).WillByDefault(Return(false));

  EXPECT_TRUE(mock_bluetooth_suspension_recovery_timer_->IsRunning());

  // This leads to ProximityAuthClient::UpdateSmartLockState() being called
  // with SmartLockState::NO_BLUETOOTH.
  mock_bluetooth_suspension_recovery_timer_->Fire();
}

TEST_F(
    ProximityAuthUnlockManagerImplTest,
    InitialScanAfterSuspendResume_DontPerformInitialScanIfConnectionEstablished) {
  CreateUnlockManager();

  ASSERT_FALSE(mock_bluetooth_suspension_recovery_timer_->IsRunning());

  // Simulates the lid of chromebook closing resulting in suspension.
  chromeos::FakePowerManagerClient::Get()->SendSuspendImminent(
      power_manager::SuspendImminent_Reason_LID_CLOSED);

  EXPECT_CALL(
      proximity_auth_client_,
      UpdateSmartLockState(SmartLockState::kPhoneFoundLockedAndProximate))
      .Times(1);
  EXPECT_CALL(proximity_auth_client_,
              UpdateSmartLockState(SmartLockState::kConnectingToPhone))
      .Times(1);

  // We want to emulate a bluetooth adapter that is present and powered
  // upon lid reopen and resume, because we are testing the code path
  // within OnBluetoothAdapterPresentAndPowerChanged() after the
  // suspension timer concludes.
  ON_CALL(*bluetooth_adapter_, IsPresent()).WillByDefault(Return(true));
  ON_CALL(*bluetooth_adapter_, IsPowered()).WillByDefault(Return(true));

  // This event simulates reopen of lid resulting in resume.
  chromeos::FakePowerManagerClient::Get()->SendSuspendDone();
  EXPECT_TRUE(mock_bluetooth_suspension_recovery_timer_->IsRunning());

  // Start the life cycle for unlock manager.
  unlock_manager_->SetRemoteDeviceLifeCycle(&life_cycle_);
  EXPECT_TRUE(life_cycle_.started());

  // Simulate a secure channel connection established with phone.
  life_cycle_.ChangeState(
      RemoteDeviceLifeCycle::State::SECURE_CHANNEL_ESTABLISHED);

  // Simulate the phone responding with locked and proximate.
  unlock_manager_->OnRemoteStatusUpdate(kRemoteScreenLocked);

  EXPECT_TRUE(mock_bluetooth_suspension_recovery_timer_->IsRunning());

  // Time out the suspension recovery timer so we run
  // OnBluetoothAdapterPresentAndPowerChanged().
  mock_bluetooth_suspension_recovery_timer_->Fire();
}

TEST_F(ProximityAuthUnlockManagerImplTest,
       BluetoothOffMessageShownImmediatelyIfBluetoothWasOffBeforeSuspend) {
  CreateUnlockManager();

  ON_CALL(*bluetooth_adapter_, IsPresent()).WillByDefault(Return(false));
  ON_CALL(*bluetooth_adapter_, IsPowered()).WillByDefault(Return(false));

  chromeos::FakePowerManagerClient::Get()->SendSuspendImminent(
      power_manager::SuspendImminent_Reason_LID_CLOSED);

  EXPECT_CALL(proximity_auth_client_,
              UpdateSmartLockState(SmartLockState::kBluetoothDisabled));
  EXPECT_CALL(proximity_auth_client_,
              UpdateSmartLockState(SmartLockState::kConnectingToPhone))
      .Times(0);

  chromeos::FakePowerManagerClient::Get()->SendSuspendDone();

  // Simulate how ProximityAuthSystem, the owner of UnlockManager, reacts to
  // resume: providing a new RemoteDeviceLifeCycle.
  unlock_manager_->SetRemoteDeviceLifeCycle(&life_cycle_);
  EXPECT_FALSE(life_cycle_.started());
}

TEST_F(ProximityAuthUnlockManagerImplTest, StartsProximityMonitor) {
  CreateUnlockManager();
  SimulateUserPresentState();
  EXPECT_TRUE(proximity_monitor()->started());
}

TEST_F(ProximityAuthUnlockManagerImplTest,
       OnAuthenticationFailed_StopsProximityMonitor) {
  CreateUnlockManager();
  SimulateUserPresentState();

  life_cycle_.ChangeState(RemoteDeviceLifeCycle::State::AUTHENTICATION_FAILED);
  EXPECT_TRUE(proximity_monitor_destroyed());
}

TEST_F(ProximityAuthUnlockManagerImplTest,
       AuthenticationFailed_UpdatesSmartLockState) {
  CreateUnlockManager();
  SimulateUserPresentState();

  EXPECT_CALL(proximity_auth_client_,
              UpdateSmartLockState(SmartLockState::kPhoneNotAuthenticated));
  life_cycle_.ChangeState(RemoteDeviceLifeCycle::State::AUTHENTICATION_FAILED);
}

TEST_F(ProximityAuthUnlockManagerImplTest,
       FindingConnection_UpdatesSmartLockState) {
  CreateUnlockManager();

  // Regression test for https://crbug.com/890047, ensuring that the NO_PHONE
  // status doesn't incorrectly appear for a brief moment before the
  // BLUETOOTH_CONNECTING spinner.
  EXPECT_CALL(proximity_auth_client_,
              UpdateSmartLockState(SmartLockState::kPhoneNotFound))
      .Times(0);

  EXPECT_CALL(proximity_auth_client_,
              UpdateSmartLockState(SmartLockState::kConnectingToPhone));
  unlock_manager_->SetRemoteDeviceLifeCycle(&life_cycle_);
  EXPECT_TRUE(life_cycle_.started());
}

TEST_F(ProximityAuthUnlockManagerImplTest,
       Authenticating_UpdatesSmartLockState) {
  CreateUnlockManager();

  // Regression test for https://crbug.com/890047, ensuring that the NO_PHONE
  // status doesn't incorrectly appear for a brief moment before the
  // BLUETOOTH_CONNECTING spinner.
  EXPECT_CALL(proximity_auth_client_,
              UpdateSmartLockState(SmartLockState::kPhoneNotFound))
      .Times(0);

  EXPECT_CALL(proximity_auth_client_,
              UpdateSmartLockState(SmartLockState::kConnectingToPhone));
  unlock_manager_->SetRemoteDeviceLifeCycle(&life_cycle_);
  EXPECT_TRUE(life_cycle_.started());
  life_cycle_.ChangeState(RemoteDeviceLifeCycle::State::AUTHENTICATING);
}

TEST_F(ProximityAuthUnlockManagerImplTest,
       OnDecryptResponse_NoAuthAttemptInProgress) {
  CreateUnlockManager();
  SimulateUserPresentState();

  EXPECT_CALL(proximity_auth_client_, FinalizeUnlock(_)).Times(0);
}

TEST_F(ProximityAuthUnlockManagerImplTest,
       OnUnlockEventSent_NoAuthAttemptInProgress) {
  CreateUnlockManager();
  SimulateUserPresentState();

  EXPECT_CALL(proximity_auth_client_, FinalizeUnlock(_)).Times(0);
  unlock_manager_.get()->OnUnlockEventSent(true);
}

TEST_F(ProximityAuthUnlockManagerImplTest,
       OnUnlockResponse_NoAuthAttemptInProgress) {
  CreateUnlockManager();
  SimulateUserPresentState();

  EXPECT_CALL(proximity_auth_client_, FinalizeUnlock(_)).Times(0);
  unlock_manager_.get()->OnUnlockResponse(true);
}

TEST_F(ProximityAuthUnlockManagerImplTest,
       OnAuthAttempted_NoRemoteDeviceLifeCycle) {
  CreateUnlockManager();
  SimulateUserPresentState();

  unlock_manager_->SetRemoteDeviceLifeCycle(nullptr);

  EXPECT_CALL(proximity_auth_client_, FinalizeUnlock(false));
  unlock_manager_->OnAuthAttempted(mojom::AuthType::USER_CLICK);
}

TEST_F(ProximityAuthUnlockManagerImplTest, OnAuthAttempted_UnlockNotAllowed) {
  CreateUnlockManager();
  SimulateUserPresentState();

  ON_CALL(*proximity_monitor(), IsUnlockAllowed()).WillByDefault(Return(false));

  EXPECT_CALL(proximity_auth_client_, FinalizeUnlock(false));
  unlock_manager_->OnAuthAttempted(mojom::AuthType::USER_CLICK);
}

TEST_F(ProximityAuthUnlockManagerImplTest, OnAuthAttempted_NotUserClick) {
  CreateUnlockManager();
  SimulateUserPresentState();

  EXPECT_CALL(proximity_auth_client_, FinalizeUnlock(_)).Times(0);
  unlock_manager_->OnAuthAttempted(mojom::AuthType::EXPAND_THEN_USER_CLICK);
}

TEST_F(ProximityAuthUnlockManagerImplTest, OnAuthAttempted_DuplicateCall) {
  CreateUnlockManager();
  SimulateUserPresentState();

  EXPECT_CALL(messenger_, RequestUnlock());
  unlock_manager_->OnAuthAttempted(mojom::AuthType::USER_CLICK);

  EXPECT_CALL(messenger_, RequestUnlock()).Times(0);
  unlock_manager_->OnAuthAttempted(mojom::AuthType::USER_CLICK);
}

TEST_F(ProximityAuthUnlockManagerImplTest, OnAuthAttempted_TimesOut) {
  CreateUnlockManager();
  SimulateUserPresentState();

  unlock_manager_->OnAuthAttempted(mojom::AuthType::USER_CLICK);

  // Simulate the timeout period elapsing.
  EXPECT_CALL(proximity_auth_client_, FinalizeUnlock(false));
  RunPendingTasks();
}

TEST_F(ProximityAuthUnlockManagerImplTest,
       OnAuthAttempted_DoesntTimeOutFollowingResponse) {
  CreateUnlockManager();
  SimulateUserPresentState();

  unlock_manager_->OnAuthAttempted(mojom::AuthType::USER_CLICK);

  EXPECT_CALL(proximity_auth_client_, FinalizeUnlock(_));
  unlock_manager_->OnUnlockResponse(false);

  // Simulate the timeout period elapsing.
  EXPECT_CALL(proximity_auth_client_, FinalizeUnlock(_)).Times(0);
  RunPendingTasks();
}

TEST_F(ProximityAuthUnlockManagerImplTest,
       OnAuthAttempted_Unlock_UnlockRequestFails) {
  CreateUnlockManager();
  SimulateUserPresentState();

  EXPECT_CALL(messenger_, RequestUnlock());
  unlock_manager_->OnAuthAttempted(mojom::AuthType::USER_CLICK);

  EXPECT_CALL(proximity_auth_client_, FinalizeUnlock(false));
  unlock_manager_->OnUnlockResponse(false);
}

TEST_F(ProximityAuthUnlockManagerImplTest,
       OnAuthAttempted_Unlock_WithSignIn_RequestSucceeds_EventSendFails) {
  CreateUnlockManager();
  SimulateUserPresentState();

  EXPECT_CALL(messenger_, RequestUnlock());
  unlock_manager_->OnAuthAttempted(mojom::AuthType::USER_CLICK);

  EXPECT_CALL(messenger_, DispatchUnlockEvent());
  unlock_manager_->OnUnlockResponse(true);

  EXPECT_CALL(proximity_auth_client_, FinalizeUnlock(false));
  unlock_manager_->OnUnlockEventSent(false);
}

TEST_F(ProximityAuthUnlockManagerImplTest,
       OnAuthAttempted_Unlock_RequestSucceeds_EventSendSucceeds) {
  CreateUnlockManager();
  SimulateUserPresentState();

  EXPECT_CALL(messenger_, RequestUnlock());
  unlock_manager_->OnAuthAttempted(mojom::AuthType::USER_CLICK);

  EXPECT_CALL(messenger_, DispatchUnlockEvent());
  unlock_manager_->OnUnlockResponse(true);

  EXPECT_CALL(proximity_auth_client_, FinalizeUnlock(true));
  unlock_manager_->OnUnlockEventSent(true);
}

}  // namespace proximity_auth