chromium/ash/detachable_base/detachable_base_handler.h

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

#ifndef ASH_DETACHABLE_BASE_DETACHABLE_BASE_HANDLER_H_
#define ASH_DETACHABLE_BASE_DETACHABLE_BASE_HANDLER_H_

#include <map>
#include <optional>
#include <string>
#include <vector>

#include "ash/ash_export.h"
#include "ash/detachable_base/detachable_base_pairing_status.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/scoped_observation.h"
#include "chromeos/ash/components/dbus/hammerd/hammerd_client.h"
#include "chromeos/dbus/power/power_manager_client.h"
#include "components/account_id/account_id.h"

class PrefRegistrySimple;
class PrefService;

namespace ash {

class DetachableBaseObserver;
struct UserInfo;

// Keeps track of the state of Chrome OS device's detachable base. It tracks
// whether:
//   * a detachable base is paired, and authenticated
//       * Detachable base is considered paired when hammerd connects Chrome OS
//         with the base
//       * During pairing, hammerd will issue a challenge to the detachable base
//         to verify its identity. The base is authenticated if the challenge
//         succeeds. If the pairing challenge fails, the base might still work,
//         but it will not be identifiable.
//   * the paired base requires a firmware update
// This information is used to detect when a detachable base related
// notification should be shown to the user.
// Note that this does not track active/existing users, but it provides methods
// to set and retrive per user state to and from local state - the
// DetachableBaseHandler clients are expected to determine for which users the
// detachable base state should be set or retrieved.
class ASH_EXPORT DetachableBaseHandler
    : public HammerdClient::Observer,
      public chromeos::PowerManagerClient::Observer {
 public:
  // |local_state| - PrefService of Local state. May be null in tests.
  explicit DetachableBaseHandler(PrefService* local_state);

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

  ~DetachableBaseHandler() override;

  // Registers the local state prefs for detachable base devices.
  static void RegisterPrefs(PrefRegistrySimple* registry);

  void AddObserver(DetachableBaseObserver* observer);
  void RemoveObserver(DetachableBaseObserver* observer);

  // Removes the detachable base data associated with a user from local state.
  void RemoveUserData(const UserInfo& user);

  // Gets the detachable base pairing state.
  DetachableBasePairingStatus GetPairingStatus() const;

  // Whether the paired base matches the last base used by the user.
  // Returns true only if the base has kAuthenticated pairing state.
  // If this the user has not previously used any detachable bases (i.e. the
  // last used detachable base is empty), this will return true.
  bool PairedBaseMatchesLastUsedByUser(const UserInfo& user) const;

  // Sets the currently paired base as the last used base for the user.
  // If the user is not ephemeral, the last used base information will be
  // persisted in the local state.
  // No-op if a detachable base is not currently paired and authenticated.
  // Returns whether the base state was successfully updated.
  //
  // Note that this will fail for non-ephemeral users if |local_state_| is not
  // yet initialized. Observers will be notified that pairing status
  // changed when local state is initialized (provided a base is still
  // paired) - setting the last used base can be retried at that point.
  bool SetPairedBaseAsLastUsedByUser(const UserInfo& user);

  // HammerdClient::Observer:
  void BaseFirmwareUpdateNeeded() override;
  void BaseFirmwareUpdateStarted() override;
  void BaseFirmwareUpdateSucceeded() override;
  void BaseFirmwareUpdateFailed() override;
  void PairChallengeSucceeded(const std::vector<uint8_t>& base_id) override;
  void PairChallengeFailed() override;
  void InvalidBaseConnected() override;

  // chromeos::PowerManagerClient::Observer:
  void TabletModeEventReceived(chromeos::PowerManagerClient::TabletMode mode,
                               base::TimeTicks timestamp) override;

 private:
  // Identifier for a detachable base device - HEX encoded string created from
  // data passed to PairChallengeSucceeded. It's known only if the base was
  // successfully authenticated.
  using DetachableBaseId = std::string;

  // Callback for getting initial power manager switches - used to determine
  // whether the tablet mode is on when the DetachableBaseHandler is created.
  void OnGotPowerManagerSwitchStates(
      std::optional<chromeos::PowerManagerClient::SwitchStates> switch_states);

  // Updates the tracked tablet mode state, and notifies observers about pairing
  // status change if required.
  void UpdateTabletMode(chromeos::PowerManagerClient::TabletMode mode);

  // Retrieves the last known base used by a user. Returns an empty string if
  // a detachable base usage was not previously recorded for the user.
  DetachableBaseId GetLastUsedDeviceForUser(const UserInfo& user) const;

  // Notifies observers that the detachable base pairing state has changed.
  void NotifyPairingStatusChanged();

  // Notifies observers about whether the detachable base requires a firmware
  // update.
  void NotifyBaseRequiresFirmwareUpdate(bool requires_update);

  raw_ptr<PrefService> local_state_ = nullptr;

  // Tablet mode state currently reported by power manager - tablet mode getting
  // turned on is used as a signal that the base is detached.
  std::optional<chromeos::PowerManagerClient::TabletMode> tablet_mode_;

  // The HEX encoded ID of the authenticated paired base device. This will
  // be non empty iff pairing_status_ is kAuthenticated.
  DetachableBaseId authenticated_base_id_;

  // The paired base device pairing status.
  DetachableBasePairingStatus pairing_status_ =
      DetachableBasePairingStatus::kNone;

  base::ScopedObservation<HammerdClient, HammerdClient::Observer>
      hammerd_observation_;
  base::ScopedObservation<chromeos::PowerManagerClient,
                          chromeos::PowerManagerClient::Observer>
      power_manager_observation_;

  // In-memory map from a user account ID to last used device set for user using
  // SetPairedBaseAsLastUsedByUser().
  // Used for ephemeral users.
  std::map<AccountId, DetachableBaseId> last_used_devices_;

  base::ObserverList<DetachableBaseObserver>::Unchecked observers_;

  base::WeakPtrFactory<DetachableBaseHandler> weak_ptr_factory_{this};
};

}  // namespace ash

#endif  // ASH_DETACHABLE_BASE_DETACHABLE_BASE_HANDLER_H_