chromium/chromeos/ash/services/secure_channel/ble_scanner.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 CHROMEOS_ASH_SERVICES_SECURE_CHANNEL_BLE_SCANNER_H_
#define CHROMEOS_ASH_SERVICES_SECURE_CHANNEL_BLE_SCANNER_H_

#include <optional>
#include <ostream>

#include "base/containers/flat_map.h"
#include "base/containers/flat_set.h"
#include "base/observer_list.h"
#include "base/observer_list_types.h"
#include "chromeos/ash/components/multidevice/remote_device_ref.h"
#include "chromeos/ash/services/secure_channel/connection_attempt_details.h"
#include "chromeos/ash/services/secure_channel/connection_role.h"
#include "chromeos/ash/services/secure_channel/device_id_pair.h"
#include "chromeos/ash/services/secure_channel/public/cpp/shared/connection_medium.h"
#include "chromeos/ash/services/secure_channel/public/mojom/secure_channel.mojom-shared.h"

namespace device {
class BluetoothDevice;
}

namespace ash::secure_channel {

// Performs BLE scans and notifies its delegate when an advertisement has been
// received from a remote device. This class allows clients to specify what type
// of connection they are scanning for and filters results accordingly.
class BleScanner {
 public:
  class Observer : public base::CheckedObserver {
   public:
    ~Observer() override = default;
    virtual void OnReceivedAdvertisement(
        multidevice::RemoteDeviceRef remote_device,
        device::BluetoothDevice* bluetooth_device,
        ConnectionMedium connection_medium,
        ConnectionRole connection_role,
        const std::vector<uint8_t>& eid) = 0;
    virtual void OnDiscoveryFailed(
        const DeviceIdPair& device_id_pair,
        mojom::DiscoveryResult discovery_result,
        std::optional<mojom::DiscoveryErrorCode> error_code) {}
  };

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

  virtual ~BleScanner();

  // Adds a scan request for the provided ConnectionAttemptDetails. If no scan
  // requests were previously present, adding a scan request will start a BLE
  // discovery session.
  void AddScanRequest(const ConnectionAttemptDetails& scan_request);

  // Removes a scan request for the provided ConnectionAttemptDetails. If this
  // function removes the only remaining request, the ongoing BLE discovery
  // session will stop.
  void RemoveScanRequest(const ConnectionAttemptDetails& scan_request);

  bool HasScanRequest(const ConnectionAttemptDetails& scan_request);

  // Retrieves the timestamp of the last successful discovery for the given
  // |remote_device_id|, or nullopt if we haven't seen this remote device during
  // the current Chrome session.
  std::optional<base::Time> GetLastSeenTimestamp(
      const std::string& remote_device_id);

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

 protected:
  BleScanner();

  virtual void HandleScanRequestChange() = 0;

  bool should_discovery_session_be_active() { return !scan_requests_.empty(); }
  const base::flat_set<ConnectionAttemptDetails>& scan_requests() {
    return scan_requests_;
  }
  DeviceIdPairSet GetAllDeviceIdPairs();

  void NotifyReceivedAdvertisementFromDevice(
      const multidevice::RemoteDeviceRef& remote_device,
      device::BluetoothDevice* bluetooth_device,
      ConnectionMedium connection_medium,
      ConnectionRole connection_role,
      const std::vector<uint8_t>& eid);

  void NotifyBleDiscoverySessionFailed(
      const DeviceIdPair& device_id_pair,
      mojom::DiscoveryResult discovery_state,
      std::optional<mojom::DiscoveryErrorCode> error_code);

 private:
  base::ObserverList<Observer> observer_list_;

  base::flat_set<ConnectionAttemptDetails> scan_requests_;
  base::flat_map<std::string, base::Time>
      remote_device_id_to_last_seen_timestamp_;
};

}  // namespace ash::secure_channel

#endif  // CHROMEOS_ASH_SERVICES_SECURE_CHANNEL_BLE_SCANNER_H_