chromium/chrome/services/sharing/nearby/platform/ble_v2_gatt_server.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_SERVER_H_
#define CHROME_SERVICES_SHARING_NEARBY_PLATFORM_BLE_V2_GATT_SERVER_H_

#include "base/containers/flat_map.h"
#include "base/memory/weak_ptr.h"
#include "chrome/services/sharing/nearby/platform/bluetooth_adapter.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/remote.h"
#include "mojo/public/cpp/bindings/shared_remote.h"
#include "third_party/nearby/src/internal/platform/implementation/ble_v2.h"

namespace nearby::chrome {

class BleV2GattServer : public ::nearby::api::ble_v2::GattServer,
                        public bluetooth::mojom::GattServiceObserver {
 public:
  // Representation of the local GattServices hosted in the BleV2GattServer.
  struct GattService {
    class Factory {
     public:
      virtual std::unique_ptr<GattService> Create();

      virtual ~Factory();
    };

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

    mojo::SharedRemote<bluetooth::mojom::GattService> gatt_service_remote;
    base::flat_map<Uuid, api::ble_v2::GattCharacteristic>
        characteristic_uuid_to_characteristic_map;

    // Characteristic UUID to value map. The value is set
    // in `UpdateCharacteristic()`, and this class is responsible for storing
    // the value of a GATT characteristic. See documentation in
    // `UpdateCharacteristic()`.
    base::flat_map<Uuid, nearby::ByteArray> characteristic_uuid_to_value_map;
  };

  BleV2GattServer(const mojo::SharedRemote<bluetooth::mojom::Adapter>& adapter,
                  std::unique_ptr<GattService::Factory> gatt_service_factory =
                      std::make_unique<GattService::Factory>());
  ~BleV2GattServer() override;

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

  // nearby::api::ble_v2::GattServer:
  BluetoothAdapter& GetBlePeripheral() override;
  std::optional<api::ble_v2::GattCharacteristic> CreateCharacteristic(
      const Uuid& service_uuid,
      const Uuid& characteristic_uuid,
      api::ble_v2::GattCharacteristic::Permission permission,
      api::ble_v2::GattCharacteristic::Property property) override;
  bool UpdateCharacteristic(
      const api::ble_v2::GattCharacteristic& characteristic,
      const nearby::ByteArray& value) override;
  absl::Status NotifyCharacteristicChanged(
      const api::ble_v2::GattCharacteristic& characteristic,
      bool confirm,
      const nearby::ByteArray& new_value) override;
  void Stop() override;

  // Triggers registration of all `GattService`s known to this
  // `BleV2GattServer`, which will make each `GattService` and all of its
  // associated attributes available on the local adapters GATT database.
  // This should be called once all the requested characteristics have been
  // created via `CreateCharacteristic()`, before advertising begins.
  void RegisterGattServices(
      base::OnceCallback<void(bool)> on_registration_complete_callback);

  base::WeakPtr<BleV2GattServer> GetWeakPtr() {
    return weak_ptr_factory_.GetWeakPtr();
  }

 private:
  // bluetooth::mojom::GattServiceObserver:
  void OnLocalCharacteristicRead(
      bluetooth::mojom::DeviceInfoPtr remote_device,
      const device::BluetoothUUID& characteristic_uuid,
      const device::BluetoothUUID& service_uuid,
      uint32_t offset,
      OnLocalCharacteristicReadCallback callback) override;

  void RegisterGattService(GattService* gatt_service);
  void DoRegisterGattService(GattService* gatt_service);
  void OnRegisterGattService(
      std::optional<device::BluetoothGattService::GattErrorCode> error_code);

  void OnGattServiceDisconnected(const Uuid& gatt_service_id);

  // Indicates whether registration of this `BleV2GattServer` via `Register` has
  // been initiated. If so, this boolean helps enforce that calls to create
  // characteristics or add new `GattService`s are not supported, since they
  // will not show up on the platform layer to clients.
  bool has_registration_started_ = false;

  // Once registration has started (indicated by `has_registration_started_`),
  // represents the overall registration success of triggering registration on
  // the `GattService`s. If any `GattService` fails to be registered, a failure
  // is returned back on the callback in `Register()`. For the `BleV2GattServer`
  // MVP, only one `GattService` exists at a single time, since there is only
  // a single usecase for `GattService`, however, this allows this code to be
  // future-proofed and support multiple `GattService`s created at a time.
  bool did_any_gatt_services_fail_to_register_ = false;

  // `task_runner_` is required to prevent blocking in calls over the Mojo
  // remote during GATT service registration.
  scoped_refptr<base::SequencedTaskRunner> task_runner_;

  // `registration_barrier_` is initialized during `RegisterGattServices()`
  // with the number of `GattService`s that need to be registered via
  // `RegisterGattService()`. This is needed because `RegisterGattServices()`
  // kicks off multiple asynchronous tasks at once, and waits until they are
  // all completed. When a `GattService` successfully completes its
  // registration, it decrements the `registration_barrier_` count;
  // when all tasks are completed successfully, `BleV2GattServer` triggers the
  // passed in `on_registration_complete_callback_` to indicate to callers
  // success. When a `GattService` unsuccessfully completes its registration,
  // it immediately triggers the `on_registration_complete_callback_` with
  // failure, and resets the `registration_barrier_` since there is no need to
  // continue the registration of all other `GattService`s.
  std::unique_ptr<base::AtomicRefCount> registration_barrier_;
  base::OnceCallback<void(bool)> on_registration_complete_callback_;

  std::unique_ptr<GattService::Factory> gatt_service_factory_;
  std::unique_ptr<BluetoothAdapter> bluetooth_adapter_;
  base::flat_map<Uuid, std::unique_ptr<GattService>> uuid_to_gatt_service_map_;
  mojo::SharedRemote<bluetooth::mojom::Adapter> adapter_remote_;
  mojo::Receiver<bluetooth::mojom::GattServiceObserver> gatt_service_observer_{
      this};
  base::WeakPtrFactory<BleV2GattServer> weak_ptr_factory_{this};
};

}  // namespace nearby::chrome

#endif  // CHROME_SERVICES_SHARING_NEARBY_PLATFORM_BLE_V2_GATT_SERVER_H_