text: // Copyright 2014 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 "chromeos/components/proximity_auth/proximity_auth_system.h"
  
  #include "chromeos/components/multidevice/logging/logging.h"
  #include "chromeos/components/proximity_auth/proximity_auth_client.h"
  #include "chromeos/components/proximity_auth/remote_device_life_cycle_impl.h"
  #include "chromeos/components/proximity_auth/unlock_manager_impl.h"
  #include "chromeos/services/secure_channel/public/cpp/client/secure_channel_client.h"
  
  namespace proximity_auth {
  
  ProximityAuthSystem::ProximityAuthSystem(
      ScreenlockType screenlock_type,
      ProximityAuthClient* proximity_auth_client,
      chromeos::secure_channel::SecureChannelClient* secure_channel_client)
      : secure_channel_client_(secure_channel_client),
        unlock_manager_(
            std::make_unique<UnlockManagerImpl>(screenlock_type,
                                                proximity_auth_client)),
        suspended_(false),
        started_(false) {}
  
  ProximityAuthSystem::ProximityAuthSystem(
      chromeos::secure_channel::SecureChannelClient* secure_channel_client,
      std::unique_ptr<UnlockManager> unlock_manager)
      : secure_channel_client_(secure_channel_client),
        unlock_manager_(std::move(unlock_manager)),
        suspended_(false),
        started_(false) {}
  
  ProximityAuthSystem::~ProximityAuthSystem() {
    ScreenlockBridge::Get()->RemoveObserver(this);
    unlock_manager_->SetRemoteDeviceLifeCycle(nullptr);
  }
  
  void ProximityAuthSystem::Start() {
    if (started_)
      return;
    started_ = true;
    ScreenlockBridge::Get()->AddObserver(this);
    const AccountId& focused_account_id =
        ScreenlockBridge::Get()->focused_account_id();
    if (focused_account_id.is_valid())
      OnFocusedUserChanged(focused_account_id);
  }
  
  void ProximityAuthSystem::Stop() {
    if (!started_)
      return;
    started_ = false;
    ScreenlockBridge::Get()->RemoveObserver(this);
    OnFocusedUserChanged(EmptyAccountId());
  }
  
  void ProximityAuthSystem::SetRemoteDevicesForUser(
      const AccountId& account_id,
      const chromeos::multidevice::RemoteDeviceRefList& remote_devices,
      base::Optional<chromeos::multidevice::RemoteDeviceRef> local_device) {
    PA_LOG(VERBOSE) << "Setting devices for user " << account_id.Serialize()
                    << ". Remote device count: " << remote_devices.size()
                    << ", Local device: ["
                    << (local_device.has_value() ? "present" : "absent") << "].";
  
    remote_devices_map_[account_id] = remote_devices;
    local_device_map_.emplace(account_id, *local_device);
  
    if (started_) {
      const AccountId& focused_account_id =
          ScreenlockBridge::Get()->focused_account_id();
      if (focused_account_id.is_valid())
        OnFocusedUserChanged(focused_account_id);
    }
  }
  
  chromeos::multidevice::RemoteDeviceRefList
  ProximityAuthSystem::GetRemoteDevicesForUser(
      const AccountId& account_id) const {
    if (remote_devices_map_.find(account_id) == remote_devices_map_.end())
      return chromeos::multidevice::RemoteDeviceRefList();
    return remote_devices_map_.at(account_id);
  }
  
  void ProximityAuthSystem::OnAuthAttempted() {
    unlock_manager_->OnAuthAttempted(mojom::AuthType::USER_CLICK);
  }
  
  void ProximityAuthSystem::OnSuspend() {
    PA_LOG(INFO) << "Preparing for device suspension.";
    DCHECK(!suspended_);
    suspended_ = true;
    unlock_manager_->SetRemoteDeviceLifeCycle(nullptr);
    remote_device_life_cycle_.reset();
  }
  
  void ProximityAuthSystem::OnSuspendDone() {
    PA_LOG(INFO) << "Device resumed from suspension.";
    DCHECK(suspended_);
    suspended_ = false;
  
    if (!ScreenlockBridge::Get()->IsLocked()) {
      PA_LOG(INFO) << "Suspend done, but no lock screen.";
    } else if (!started_) {
      PA_LOG(INFO) << "Suspend done, but not system started.";
    } else {
      OnFocusedUserChanged(ScreenlockBridge::Get()->focused_account_id());
    }
  }
  
  void ProximityAuthSystem::CancelConnectionAttempt() {
    unlock_manager_->CancelConnectionAttempt();
  }
  
  std::unique_ptr<RemoteDeviceLifeCycle>
  ProximityAuthSystem::CreateRemoteDeviceLifeCycle(
      chromeos::multidevice::RemoteDeviceRef remote_device,
      base::Optional<chromeos::multidevice::RemoteDeviceRef> local_device) {
    return std::make_unique<RemoteDeviceLifeCycleImpl>(
        remote_device, local_device, secure_channel_client_);
  }
  
  void ProximityAuthSystem::OnScreenDidLock(
      ScreenlockBridge::LockHandler::ScreenType screen_type) {
    const AccountId& focused_account_id =
        ScreenlockBridge::Get()->focused_account_id();
    if (focused_account_id.is_valid())
      OnFocusedUserChanged(focused_account_id);
  }
  
  void ProximityAuthSystem::OnScreenDidUnlock(
      ScreenlockBridge::LockHandler::ScreenType screen_type) {
    unlock_manager_->SetRemoteDeviceLifeCycle(nullptr);
    remote_device_life_cycle_.reset();
  }
  
  void ProximityAuthSystem::OnFocusedUserChanged(const AccountId& account_id) {
    // Update the current RemoteDeviceLifeCycle to the focused user.
    if (remote_device_life_cycle_) {
      if (remote_device_life_cycle_->GetRemoteDevice().user_email() !=
          account_id.GetUserEmail()) {
        PA_LOG(INFO) << "Focused user changed, destroying life cycle for "
                     << account_id.Serialize() << ".";
        unlock_manager_->SetRemoteDeviceLifeCycle(nullptr);
        remote_device_life_cycle_.reset();
      } else {
        PA_LOG(INFO) << "Refocused on a user who is already focused.";
        return;
      }
    }
  
    if (remote_devices_map_.find(account_id) == remote_devices_map_.end() ||
        remote_devices_map_[account_id].size() == 0) {
      PA_LOG(INFO) << "User " << account_id.Serialize()
                   << " does not have a Smart Lock host device.";
      return;
    }
    if (local_device_map_.find(account_id) == local_device_map_.end()) {
      PA_LOG(INFO) << "User " << account_id.Serialize()
                   << " does not have a local device.";
      return;
    }
  
    // TODO(tengs): We currently assume each user has only one RemoteDevice, so we
    // can simply take the first item in the list.
    chromeos::multidevice::RemoteDeviceRef remote_device =
        remote_devices_map_[account_id][0];
  
    base::Optional<chromeos::multidevice::RemoteDeviceRef> local_device;
    local_device = local_device_map_.at(account_id);
  
    if (!suspended_) {
      PA_LOG(INFO) << "Creating RemoteDeviceLifeCycle for focused user: "
                   << account_id.Serialize();
      remote_device_life_cycle_ =
          CreateRemoteDeviceLifeCycle(remote_device, local_device);
  
      // UnlockManager listens for Bluetooth power change events, and is therefore
      // responsible for starting RemoteDeviceLifeCycle when Bluetooth becomes
      // powered.
      unlock_manager_->SetRemoteDeviceLifeCycle(remote_device_life_cycle_.get());
    }
  }
  
  }  // namespace proximity_auth
  