chromium/chrome/services/sharing/nearby/platform/ble_v2_medium.h

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

#ifndef CHROME_SERVICES_SHARING_NEARBY_PLATFORM_BLE_V2_MEDIUM_H_
#define CHROME_SERVICES_SHARING_NEARBY_PLATFORM_BLE_V2_MEDIUM_H_

#include <string>

#include "base/gtest_prod_util.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "chrome/services/sharing/nearby/platform/ble_v2_remote_peripheral.h"
#include "device/bluetooth/public/mojom/adapter.mojom.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/shared_remote.h"
#include "third_party/nearby/src/internal/platform/implementation/ble_v2.h"

namespace base {
class SequencedTaskRunner;
class WaitableEvent;
}  // namespace base

namespace nearby::chrome {

class BleV2GattServer;

// Concrete ble_v2::BleMedium implementation.
// Productionized impl will also consumes bluetooth::mojom::Adapter methods.
// Current skeleton has not added it yet.
class BleV2Medium : public ::nearby::api::ble_v2::BleMedium,
                    public bluetooth::mojom::AdapterObserver {
 public:
  BleV2Medium();

  explicit BleV2Medium(
      const mojo::SharedRemote<bluetooth::mojom::Adapter>& adapter);

  ~BleV2Medium() override;

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

  // This would deprecate soon. Will delete this after nearby codes delete it
  // first.
  // TODO(b/271305977): Delete this method.
  bool StartAdvertising(
      const api::ble_v2::BleAdvertisementData& advertising_data,
      api::ble_v2::AdvertiseParameters advertise_set_parameters) override;

  std::unique_ptr<AdvertisingSession> StartAdvertising(
      const api::ble_v2::BleAdvertisementData& advertising_data,
      api::ble_v2::AdvertiseParameters advertise_set_parameters,
      AdvertisingCallback callback) override;

  // This would deprecate soon. Will delete this after nearby codes delete it
  // first.
  // TODO(b/271305977): Delete this method.
  bool StopAdvertising() override;

  // This would deprecate soon. Will delete this after nearby codes delete it
  // first.
  // TODO(b/271305977): Delete this method.
  bool StartScanning(const Uuid& service_uuid,
                     api::ble_v2::TxPowerLevel tx_power_level,
                     ScanCallback callback) override;

  // This would deprecate soon. Will delete this after nearby codes delete it
  // first.
  // TODO(b/271305977): Delete this method.
  bool StopScanning() override;

  std::unique_ptr<ScanningSession> StartScanning(
      const Uuid& service_uuid,
      api::ble_v2::TxPowerLevel tx_power_level,
      ScanningCallback callback) override;

  std::unique_ptr<api::ble_v2::GattServer> StartGattServer(
      api::ble_v2::ServerGattConnectionCallback callback) override;

  std::unique_ptr<api::ble_v2::GattClient> ConnectToGattServer(
      api::ble_v2::BlePeripheral& peripheral,
      api::ble_v2::TxPowerLevel tx_power_level,
      api::ble_v2::ClientGattConnectionCallback callback) override;

  std::unique_ptr<api::ble_v2::BleServerSocket> OpenServerSocket(
      const std::string& service_id) override;

  std::unique_ptr<api::ble_v2::BleSocket> Connect(
      const std::string& service_id,
      api::ble_v2::TxPowerLevel tx_power_level,
      api::ble_v2::BlePeripheral& peripheral,
      CancellationFlag* cancellation_flag) override;

  bool IsExtendedAdvertisementsAvailable() override;

  bool GetRemotePeripheral(const std::string& mac_address,
                           GetRemotePeripheralCallback callback) override;
  bool GetRemotePeripheral(api::ble_v2::BlePeripheral::UniqueId id,
                           GetRemotePeripheralCallback callback) override;

 private:
  // TODO(b/330759317): Remove FRIEND call once unit tests can rely on
  // Fake classes instead of accessing private members.
  FRIEND_TEST_ALL_PREFIXES(BleV2MediumTest,
                           TestAdvertising_MultipleStartAdvertisingSuccess);
  // bluetooth::mojom::AdapterObserver:
  void PresentChanged(bool present) override;
  void PoweredChanged(bool powered) override;
  void DiscoverableChanged(bool discoverable) override;
  void DiscoveringChanged(bool discovering) override;
  void DeviceAdded(bluetooth::mojom::DeviceInfoPtr device) override;
  void DeviceChanged(bluetooth::mojom::DeviceInfoPtr device) override;
  void DeviceRemoved(bluetooth::mojom::DeviceInfoPtr device) override;

  void ProcessFoundDevice(bluetooth::mojom::DeviceInfoPtr device);
  bool IsScanning();
  uint64_t GenerateUniqueSessionId();
  chrome::BleV2RemotePeripheral* GetDiscoveredBlePeripheral(
      const std::string& address);

  void DoRegisterGattServices(
      BleV2GattServer* gatt_server,
      bool* registration_success,
      base::WaitableEvent* register_gatt_services_waitable_event);
  void OnRegisterGattServices(
      bool* out_registration_success,
      base::WaitableEvent* register_gatt_services_waitable_event,
      bool in_registration_success);

  void DoConnectToGattServer(
      mojo::PendingRemote<bluetooth::mojom::Device>* device,
      const std::string& address,
      base::WaitableEvent* connect_to_gatt_server_waitable_event);
  void OnConnectToGattServer(
      base::TimeTicks gatt_connection_start_time,
      mojo::PendingRemote<bluetooth::mojom::Device>* out_device,
      base::WaitableEvent* connect_to_gatt_server_waitable_event,
      bluetooth::mojom::ConnectResult result,
      mojo::PendingRemote<bluetooth::mojom::Device> in_device);

  void Shutdown(base::WaitableEvent* shutdown_waitable_event);

  // `task_runner_` is used in `StartAdvertising()` to post a task to register
  // `ble_v2_gatt_server_` if it exists (which indicates that the GATT server
  // will be advertised, thus all the GATT services associated with
  // `ble_v2_gatt_server_` need to be registered beforehand). The
  // `task_runner_` and posted tasks are shutdown in `Shutdown()` in the
  // destructor.
  scoped_refptr<base::SequencedTaskRunner> task_runner_;

  mojo::SharedRemote<bluetooth::mojom::Adapter> adapter_;

  // Only set in `StartGattServer()` if the LE Dual Scatternet role
  // is supported on the device. This is used to trigger asynchronous GATT
  // server registration in `StartAdvertising()`.
  base::WeakPtr<BleV2GattServer> gatt_server_;

  // Track all pending register GATT service waitable events.
  base::flat_set<raw_ptr<base::WaitableEvent>>
      pending_register_gatt_services_waitable_events_;

  // Track all pending connect to remote GATT servers waitable events.
  base::flat_set<raw_ptr<base::WaitableEvent>>
      pending_connect_to_gatt_server_waitable_events_;

  // Only set while discovery is active.
  mojo::Remote<bluetooth::mojom::DiscoverySession> discovery_session_;

  // Group scanning session ids with the same bluetooth service id in sets.
  std::map<device::BluetoothUUID, std::set<uint64_t>>
      service_uuid_to_session_ids_map_;

  // Saving scanning callbacks, keyed by session ids.
  std::map<uint64_t, ScanningCallback> session_id_to_scanning_callback_map_;

  // Keyed by address of advertising remote device. Note: References to these
  // BlePeripherals are passed to Nearby Connections. This is safe because, for
  // std::map, insert/emplace do not invalidate references, and the erase
  // operation only invalidates the reference to the erased element.
  std::map<std::string, chrome::BleV2RemotePeripheral>
      discovered_ble_peripherals_map_;

  // Group registered advertisements with the same bluetooth service uuid.
  std::map<device::BluetoothUUID,
           std::vector<mojo::SharedRemote<bluetooth::mojom::Advertisement>>>
      registered_advertisements_map_;

  // |adapter_observer_| is only set and bound during active discovery so that
  // events we don't care about outside of discovery don't pile up.
  mojo::Receiver<bluetooth::mojom::AdapterObserver> adapter_observer_{this};

  base::WeakPtrFactory<BleV2Medium> weak_ptr_factory_{this};
};
}  // namespace nearby::chrome

#endif  // CHROME_SERVICES_SHARING_NEARBY_PLATFORM_BLE_V2_MEDIUM_H_