chromium/chrome/services/sharing/nearby/platform/ble_v2_gatt_client.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_GATT_CLIENT_H_
#define CHROME_SERVICES_SHARING_NEARBY_PLATFORM_BLE_V2_GATT_CLIENT_H_

#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "device/bluetooth/public/mojom/device.mojom.h"
#include "mojo/public/cpp/bindings/pending_remote.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 BleV2GattClient : public ::nearby::api::ble_v2::GattClient {
 public:
  // Representation of the remote GattServices discovered by BleV2GattClient.
  class GattService {
   public:
    class Factory {
     public:
      virtual std::unique_ptr<GattService> Create();

      virtual ~Factory();
    };

    virtual ~GattService();
    GattService(GattService&) = delete;
    GattService& operator=(GattService&) = delete;

    bluetooth::mojom::ServiceInfoPtr service_info;
    std::optional<std::vector<bluetooth::mojom::CharacteristicInfoPtr>>
        characteristics;

   protected:
    GattService();
  };

  BleV2GattClient(mojo::PendingRemote<bluetooth::mojom::Device> device,
                  std::unique_ptr<GattService::Factory> gatt_service_factory =
                      std::make_unique<GattService::Factory>());
  ~BleV2GattClient() override;

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

  // nearby::api::ble_v2::GattClient:
  bool DiscoverServiceAndCharacteristics(
      const Uuid& service_uuid,
      const std::vector<Uuid>& characteristic_uuids) override;
  std::optional<api::ble_v2::GattCharacteristic> GetCharacteristic(
      const Uuid& service_uuid,
      const Uuid& characteristic_uuid) override;
  std::optional<std::string> ReadCharacteristic(
      const api::ble_v2::GattCharacteristic& characteristic) override;
  bool WriteCharacteristic(
      const api::ble_v2::GattCharacteristic& characteristic,
      std::string_view value,
      WriteType type) override;
  bool SetCharacteristicSubscription(
      const api::ble_v2::GattCharacteristic& characteristic,
      bool enable,
      absl::AnyInvocable<void(std::string_view value)>
          on_characteristic_changed_cb) override;
  void Disconnect() override;

 private:
  void DoDiscoverServices(
      base::WaitableEvent* discover_services_waitable_event);
  void OnGetGattServices(
      base::WaitableEvent* discover_services_waitable_event,
      std::vector<bluetooth::mojom::ServiceInfoPtr> services);

  void DoGetCharacteristics(
      GattService* gatt_service,
      base::WaitableEvent* get_characteristics_waitable_event);
  void OnGetCharacteristics(
      GattService* gatt_service,
      base::WaitableEvent* get_characteristics_waitable_event,
      std::optional<std::vector<bluetooth::mojom::CharacteristicInfoPtr>>
          characteristics);

  void DoReadCharacteristic(
      const std::string& service_id,
      const std::string& characteristic_id,
      std::optional<std::string>* read_characteristic_result,
      base::WaitableEvent* read_characteristic_waitable_event);
  void OnReadCharacteristic(
      base::TimeTicks gatt_read_characteristic_start_time,
      std::optional<std::string>* read_characteristic_result,
      base::WaitableEvent* read_characteristic_waitable_event,
      bluetooth::mojom::GattResult result,
      const std::optional<std::vector<uint8_t>>& value);

  // Returns the `CharacteristicInfoPtr` and it's containing GATT service id
  // if it exists.
  std::optional<std::pair<bluetooth::mojom::CharacteristicInfoPtr, std::string>>
  GetCharacteristicInfoMojom(const Uuid& service_uuid,
                             const Uuid& characteristic_uuid);

  void Shutdown(base::WaitableEvent* shutdown_waitable_event);
  void OnMojoDisconnect();

  bool have_gatt_services_been_discovered_ = false;

  // Map of service UUID to a vector of GATT Services on the remote device that
  // match the service UUID. `uuid_to_discovered_gatt_services_map_` supports
  // duplicate GATT services with the same UUID because Android's GATT server
  // contains duplicate GATT services with the same UUID that contain different
  // GATT characteristics. BleV2GattClient needs to check all of the GATT
  // services under the UUID for a match to the requested characteristics. This
  // is aligned with how Windows handles duplicate GATT services from Android
  // in their BLE V2 implementation.
  std::map<std::string, std::vector<std::unique_ptr<GattService>>>
      uuid_to_discovered_gatt_services_map_;

  // Track all pending tasks in case the object is invalidated while
  // waiting.
  scoped_refptr<base::SequencedTaskRunner> task_runner_;
  base::flat_set<raw_ptr<base::WaitableEvent>>
      pending_discover_services_waitable_events_;
  base::flat_set<raw_ptr<base::WaitableEvent>>
      pending_get_characteristics_waitable_events_;
  base::flat_set<raw_ptr<base::WaitableEvent>>
      pending_read_characteristic_waitable_events_;

  std::unique_ptr<GattService::Factory> gatt_service_factory_;

  mojo::SharedRemote<bluetooth::mojom::Device> remote_device_;

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

}  // namespace nearby::chrome

#endif  // CHROME_SERVICES_SHARING_NEARBY_PLATFORM_BLE_V2_GATT_CLIENT_H_