910e62b5创建于 1月15日历史提交
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/devtools/protocol/bluetooth_emulation_handler.h"

#include <map>
#include <string>
#include <string_view>
#include <utility>
#include <vector>

#include "base/containers/span.h"
#include "base/containers/to_vector.h"
#include "base/notreached.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "content/browser/bluetooth/bluetooth_adapter_factory_wrapper.h"
#include "content/browser/devtools/protocol/bluetooth_emulation.h"
#include "device/bluetooth/emulation/fake_central.h"

using device::BluetoothAdapterFactory;

namespace content::protocol {

namespace {

base::flat_map<uint16_t, std::vector<uint8_t>> ToManufacturerData(
    protocol::Array<protocol::BluetoothEmulation::ManufacturerData>*
        in_manufacturer_data) {
  base::flat_map<uint16_t, std::vector<uint8_t>> out_manufacturer_data;
  for (auto& data : *in_manufacturer_data) {
    out_manufacturer_data[data->GetKey()] = base::ToVector(data->GetData());
  }
  return out_manufacturer_data;
}

std::vector<device::BluetoothUUID> ToBluetoothUUIDs(
    protocol::Array<protocol::String>* in_bluetooth_ids) {
  std::vector<device::BluetoothUUID> out_bluetooth_ids;
  for (auto& uuid : *in_bluetooth_ids) {
    out_bluetooth_ids.emplace_back(uuid);
  }
  return out_bluetooth_ids;
}

mojo::StructPtr<bluetooth::mojom::ScanRecord> ToScanRecord(
    BluetoothEmulation::ScanRecord* in_record) {
  mojo::StructPtr<bluetooth::mojom::ScanRecord> out_record =
      bluetooth::mojom::ScanRecord::New();

  if (in_record->HasUuids()) {
    out_record->uuids = ToBluetoothUUIDs(in_record->GetUuids(nullptr));
  }

  // Convert Appearance
  if (in_record->HasAppearance()) {
    out_record->appearance = bluetooth::mojom::Appearance::New(
        in_record->HasAppearance(), in_record->GetAppearance(0));
  }

  // Convert TX Power
  if (in_record->HasTxPower()) {
    out_record->tx_power = bluetooth::mojom::Power::New(
        in_record->HasTxPower(), in_record->GetTxPower(0));
  }

  if (in_record->HasManufacturerData()) {
    out_record->manufacturer_data =
        ToManufacturerData(in_record->GetManufacturerData(nullptr));
  }

  return out_record;
}

mojo::StructPtr<bluetooth::mojom::CharacteristicProperties>
ToCharacteristicProperties(
    BluetoothEmulation::CharacteristicProperties* in_properties) {
  mojo::StructPtr<bluetooth::mojom::CharacteristicProperties> out_properties =
      bluetooth::mojom::CharacteristicProperties::New();
  out_properties->broadcast = in_properties->GetBroadcast().value_or(false);
  out_properties->read = in_properties->GetRead().value_or(false);
  out_properties->write_without_response =
      in_properties->GetWriteWithoutResponse().value_or(false);
  out_properties->write = in_properties->GetWrite().value_or(false);
  out_properties->notify = in_properties->GetNotify().value_or(false);
  out_properties->indicate = in_properties->GetIndicate().value_or(false);
  out_properties->authenticated_signed_writes =
      in_properties->GetAuthenticatedSignedWrites().value_or(false);
  out_properties->extended_properties =
      in_properties->GetExtendedProperties().value_or(false);
  return out_properties;
}

bluetooth::mojom::CentralState ToCentralState(const std::string& state_string) {
  if (state_string ==
      protocol::BluetoothEmulation::CentralStateEnum::PoweredOff) {
    return bluetooth::mojom::CentralState::POWERED_OFF;
  } else if (state_string ==
             protocol::BluetoothEmulation::CentralStateEnum::PoweredOn) {
    return bluetooth::mojom::CentralState::POWERED_ON;
  }
  return bluetooth::mojom::CentralState::ABSENT;
}

constexpr std::string_view ToGATTOperation(
    bluetooth::mojom::GATTOperationType type) {
  switch (type) {
    case bluetooth::mojom::GATTOperationType::kConnect:
      return BluetoothEmulation::GATTOperationTypeEnum::Connection;
    case bluetooth::mojom::GATTOperationType::kDiscovery:
      return BluetoothEmulation::GATTOperationTypeEnum::Discovery;
  }
}

std::optional<bluetooth::mojom::GATTOperationType> ToGATTOperation(
    std::string_view type) {
  if (type == BluetoothEmulation::GATTOperationTypeEnum::Connection) {
    return bluetooth::mojom::GATTOperationType::kConnect;
  } else if (type == BluetoothEmulation::GATTOperationTypeEnum::Discovery) {
    return bluetooth::mojom::GATTOperationType::kDiscovery;
  } else {
    return std::nullopt;
  }
}

constexpr std::string_view ToCharacteristicOperation(
    bluetooth::mojom::CharacteristicOperationType type) {
  switch (type) {
    case bluetooth::mojom::CharacteristicOperationType::kRead:
      return BluetoothEmulation::CharacteristicOperationTypeEnum::Read;
    case bluetooth::mojom::CharacteristicOperationType::kWrite:
      return BluetoothEmulation::CharacteristicOperationTypeEnum::Write;
    case bluetooth::mojom::CharacteristicOperationType::
        kSubscribeToNotifications:
      return BluetoothEmulation::CharacteristicOperationTypeEnum::
          SubscribeToNotifications;
    case bluetooth::mojom::CharacteristicOperationType::
        kUnsubscribeFromNotifications:
      return BluetoothEmulation::CharacteristicOperationTypeEnum::
          UnsubscribeFromNotifications;
  }
}

std::optional<bluetooth::mojom::CharacteristicOperationType>
ToCharacteristicOperation(std::string_view type) {
  if (type == BluetoothEmulation::CharacteristicOperationTypeEnum::Read) {
    return bluetooth::mojom::CharacteristicOperationType::kRead;
  } else if (type ==
             BluetoothEmulation::CharacteristicOperationTypeEnum::Write) {
    return bluetooth::mojom::CharacteristicOperationType::kWrite;
  } else if (type == BluetoothEmulation::CharacteristicOperationTypeEnum::
                         SubscribeToNotifications) {
    return bluetooth::mojom::CharacteristicOperationType::
        kSubscribeToNotifications;
  } else if (type == BluetoothEmulation::CharacteristicOperationTypeEnum::
                         UnsubscribeFromNotifications) {
    return bluetooth::mojom::CharacteristicOperationType::
        kUnsubscribeFromNotifications;
  } else {
    return std::nullopt;
  }
}

constexpr std::string_view ToCharacteristicWriteType(
    bluetooth::mojom::WriteType type) {
  switch (type) {
    case bluetooth::mojom::WriteType::kNone:
      NOTREACHED();
    case bluetooth::mojom::WriteType::kWriteDefaultDeprecated:
      return BluetoothEmulation::CharacteristicWriteTypeEnum::
          WriteDefaultDeprecated;
    case bluetooth::mojom::WriteType::kWriteWithResponse:
      return BluetoothEmulation::CharacteristicWriteTypeEnum::WriteWithResponse;
    case bluetooth::mojom::WriteType::kWriteWithoutResponse:
      return BluetoothEmulation::CharacteristicWriteTypeEnum::
          WriteWithoutResponse;
  }
}

constexpr std::string_view ToDescriptorOperation(
    bluetooth::mojom::DescriptorOperationType type) {
  switch (type) {
    case bluetooth::mojom::DescriptorOperationType::kRead:
      return BluetoothEmulation::CharacteristicOperationTypeEnum::Read;
    case bluetooth::mojom::DescriptorOperationType::kWrite:
      return BluetoothEmulation::DescriptorOperationTypeEnum::Write;
  }
}

std::optional<bluetooth::mojom::DescriptorOperationType> ToDescriptorOperation(
    std::string_view type) {
  if (type == BluetoothEmulation::DescriptorOperationTypeEnum::Read) {
    return bluetooth::mojom::DescriptorOperationType::kRead;
  } else if (type == BluetoothEmulation::DescriptorOperationTypeEnum::Write) {
    return bluetooth::mojom::DescriptorOperationType::kWrite;
  } else {
    return std::nullopt;
  }
}

std::string getParentId(const std::string& id) {
  // This decoding mechanism aligns with the identifier formatting mechanism in
  // FakePeripheral::AddFakeService,
  // FakeRemoteGattService::AddFakeCharacteristic, and
  // FakeRemoteGattCharacteristic::AddFakeDescriptor.
  size_t lastUnderscorePos = id.rfind('_');
  if (lastUnderscorePos != std::string::npos) {
    return id.substr(0, lastUnderscorePos);
  }
  return "";
}

bool IsValidIdString(const std::string& str) {
  if (str.empty()) {
    return false;
  }
  // Id is expected to start from 1.
  if (str[0] < '1' || str[0] > '9') {
    return false;
  }
  return std::ranges::all_of(str.begin() + 1, str.end(), ::isdigit);
}

bool IsValidServiceId(const std::string& id) {
  size_t lastUnderscorePos = id.rfind('_');
  return IsValidIdString(id.substr(lastUnderscorePos + 1));
}

bool IsValidCharacteristicId(const std::string& id) {
  size_t lastUnderscorePos = id.rfind('_');
  return IsValidIdString(id.substr(lastUnderscorePos + 1)) &&
         IsValidServiceId(id.substr(0, lastUnderscorePos));
}

bool IsValidDescriptorId(const std::string& id) {
  size_t lastUnderscorePos = id.rfind('_');
  return IsValidIdString(id.substr(lastUnderscorePos + 1)) &&
         IsValidCharacteristicId(id.substr(0, lastUnderscorePos));
}

}  // namespace

// static
std::vector<BluetoothEmulationHandler*> BluetoothEmulationHandler::ForAgentHost(
    DevToolsAgentHostImpl* host) {
  return host->HandlersByName<BluetoothEmulationHandler>(
      BluetoothEmulation::Metainfo::domainName);
}

BluetoothEmulationHandler::BluetoothEmulationHandler()
    : DevToolsDomainHandler(BluetoothEmulation::Metainfo::domainName) {}

BluetoothEmulationHandler::~BluetoothEmulationHandler() = default;

void BluetoothEmulationHandler::Wire(UberDispatcher* dispatcher) {
  frontend_.emplace(dispatcher->channel());
  BluetoothEmulation::Dispatcher::wire(dispatcher, this);
}

Response BluetoothEmulationHandler::Enable(const std::string& in_state,
                                           bool in_le_supported) {
  if (emulation_enabled_) {
    return Response::ServerError("BluetoothEmulation already enabled");
  }

  CHECK(!is_enabled());
  CHECK(!client_receiver_.is_bound());
  emulation_enabled_ = true;
  global_factory_values_ =
      BluetoothAdapterFactory::Get()->InitGlobalOverrideValues();
  global_factory_values_->SetLESupported(in_le_supported);
  if (in_le_supported) {
    content::BluetoothAdapterFactoryWrapper::Get().SetBluetoothAdapterOverride(
        base::MakeRefCounted<bluetooth::FakeCentral>(
            ToCentralState(in_state),
            fake_central_.BindNewPipeAndPassReceiver()));
    fake_central_.reset_on_disconnect();
    // While there's a possibility the client might not be fully settled on the
    // fake central side upon return, this is acceptable. Client events are
    // expected to be delivered only after at least one peripheral has been
    // simulated. By that time, the client should be properly settled on the
    // fake central side.
    fake_central_->SetClient(client_receiver_.BindNewEndpointAndPassRemote());
    client_receiver_.reset_on_disconnect();
  }
  return Response::Success();
}

Response BluetoothEmulationHandler::Disable() {
  if (is_enabled()) {
    CHECK(emulation_enabled_);
    client_receiver_.reset();
    fake_central_.reset();
    // reset this only if this is the instance holding the bound central.
    content::BluetoothAdapterFactoryWrapper::Get().SetBluetoothAdapterOverride(
        nullptr);
  }
  // The instance that has a valid `global_factory_values_` is the one that sets
  // `emulation_enabled_`. Therefore, only that instance can reset
  // `emulation_enabled_`.
  if (global_factory_values_) {
    global_factory_values_.reset();
    emulation_enabled_ = false;
  }
  return Response::Success();
}

void BluetoothEmulationHandler::SetSimulatedCentralState(
    const std::string& in_state,
    std::unique_ptr<SetSimulatedCentralStateCallback> callback) {
  if (!is_enabled()) {
    std::move(callback)->sendFailure(
        Response::ServerError("BluetoothEmulation not enabled"));
    return;
  }
  fake_central_->SetState(
      ToCentralState(in_state),
      base::BindOnce(&SetSimulatedCentralStateCallback::sendSuccess,
                     std::move(callback)));
}

void BluetoothEmulationHandler::SimulatePreconnectedPeripheral(
    const std::string& in_address,
    const std::string& in_name,
    std::unique_ptr<
        protocol::Array<protocol::BluetoothEmulation::ManufacturerData>>
        in_manufacturer_data,
    std::unique_ptr<protocol::Array<String>> in_known_service_uuids,
    std::unique_ptr<SimulatePreconnectedPeripheralCallback> callback) {
  if (!is_enabled()) {
    std::move(callback)->sendFailure(
        Response::ServerError("BluetoothEmulation not enabled"));
    return;
  }
  fake_central_->SimulatePreconnectedPeripheral(
      in_address, in_name, ToManufacturerData(in_manufacturer_data.get()),
      ToBluetoothUUIDs(in_known_service_uuids.get()),
      base::BindOnce(&SimulatePreconnectedPeripheralCallback::sendSuccess,
                     std::move(callback)));
}

void BluetoothEmulationHandler::SimulateAdvertisement(
    std::unique_ptr<protocol::BluetoothEmulation::ScanEntry> in_entry,
    std::unique_ptr<SimulateAdvertisementCallback> callback) {
  if (!is_enabled()) {
    std::move(callback)->sendFailure(
        Response::ServerError("BluetoothEmulation not enabled"));
    return;
  }
  bluetooth::mojom::ScanResultPtr payload = bluetooth::mojom::ScanResult::New(
      in_entry->GetDeviceAddress(), in_entry->GetRssi(),
      ToScanRecord(in_entry->GetScanRecord()));
  fake_central_->SimulateAdvertisementReceived(
      std::move(payload),
      base::BindOnce(&SimulateAdvertisementCallback::sendSuccess,
                     std::move(callback)));
}

void BluetoothEmulationHandler::SimulateGATTOperationResponse(
    const std::string& in_address,
    const std::string& in_type,
    int in_code,
    std::unique_ptr<SimulateGATTOperationResponseCallback> callback) {
  if (!is_enabled()) {
    std::move(callback)->sendFailure(
        Response::ServerError("BluetoothEmulation not enabled"));
    return;
  }
  auto gatt_operation_type = ToGATTOperation(in_type);
  if (!gatt_operation_type) {
    std::move(callback)->sendFailure(Response::InvalidParams(
        base::StrCat({"Unknown GATT operation type ", in_type})));
    return;
  }

  fake_central_->SimulateGATTOperationResponse(
      *gatt_operation_type, in_address, in_code,
      base::BindOnce(
          [](std::unique_ptr<SimulateGATTOperationResponseCallback> callback,
             const std::string& type, bool success) {
            if (!success) {
              std::move(callback)->sendFailure(
                  Response::ServerError(base::StrCat(
                      {"Failed to simulate GATT response for operation type ",
                       type})));
              return;
            }
            std::move(callback)->sendSuccess();
          },
          std::move(callback), in_type));
}

void BluetoothEmulationHandler::SimulateCharacteristicOperationResponse(
    const std::string& characteristic_id,
    const std::string& in_type,
    int in_code,
    std::optional<Binary> in_data,
    std::unique_ptr<SimulateCharacteristicOperationResponseCallback> callback) {
  if (!is_enabled()) {
    std::move(callback)->sendFailure(
        Response::ServerError("BluetoothEmulation not enabled"));
    return;
  }
  auto operation_type = ToCharacteristicOperation(in_type);
  if (!operation_type) {
    std::move(callback)->sendFailure(Response::InvalidParams(
        base::StrCat({"Unknown characteristic operation type ", in_type})));
    return;
  }
  if (operation_type == bluetooth::mojom::CharacteristicOperationType::kRead &&
      ((in_code == bluetooth::mojom::kGATTSuccess) != in_data.has_value())) {
    std::move(callback)->sendFailure(Response::InvalidParams(
        base::StrCat({"Characteristic operation type ", in_type, " with code ",
                      base::NumberToString(in_code),
                      in_data ? " does not expect" : " expects", " data"})));
    return;
  }

  std::string service_id = getParentId(characteristic_id);
  std::string address = getParentId(service_id);
  fake_central_->SimulateCharacteristicOperationResponse(
      *operation_type, characteristic_id, service_id, address, in_code,
      in_data ? std::optional(base::ToVector(*in_data)) : std::nullopt,
      base::BindOnce(
          [](std::unique_ptr<SimulateCharacteristicOperationResponseCallback>
                 callback,
             const std::string& error_message, bool success) {
            if (!success) {
              std::move(callback)->sendFailure(
                  Response::ServerError(error_message));
              return;
            }
            std::move(callback)->sendSuccess();
          },
          std::move(callback),
          base::StrCat(
              {"Failed to simulate characteristic response for operation type ",
               in_type})));
}

void BluetoothEmulationHandler::SimulateDescriptorOperationResponse(
    const std::string& descriptor_id,
    const std::string& in_type,
    int in_code,
    std::optional<Binary> in_data,
    std::unique_ptr<SimulateDescriptorOperationResponseCallback> callback) {
  if (!is_enabled()) {
    std::move(callback)->sendFailure(
        Response::ServerError("BluetoothEmulation not enabled"));
    return;
  }
  auto operation_type = ToDescriptorOperation(in_type);
  if (!operation_type) {
    std::move(callback)->sendFailure(Response::InvalidParams(
        base::StrCat({"Unknown descriptor operation type ", in_type})));
    return;
  }
  if (operation_type == bluetooth::mojom::DescriptorOperationType::kRead &&
      ((in_code == bluetooth::mojom::kGATTSuccess) != in_data.has_value())) {
    std::move(callback)->sendFailure(Response::InvalidParams(
        base::StrCat({"Descriptor operation type ", in_type, " with code ",
                      base::NumberToString(in_code),
                      in_data ? " does not expect" : " expects", " data"})));
    return;
  }

  std::string characteristic_id = getParentId(descriptor_id);
  std::string service_id = getParentId(characteristic_id);
  std::string address = getParentId(service_id);
  fake_central_->SimulateDescriptorOperationResponse(
      *operation_type, descriptor_id, characteristic_id, service_id, address,
      in_code, in_data ? std::optional(base::ToVector(*in_data)) : std::nullopt,
      base::BindOnce(
          [](std::unique_ptr<SimulateDescriptorOperationResponseCallback>
                 callback,
             const std::string& error_message, bool success) {
            if (!success) {
              std::move(callback)->sendFailure(
                  Response::ServerError(error_message));
              return;
            }
            std::move(callback)->sendSuccess();
          },
          std::move(callback),
          base::StrCat(
              {"Failed to simulate descriptor response for operation type ",
               in_type})));
}

void BluetoothEmulationHandler::AddService(
    const std::string& in_address,
    const std::string& service_uuid,
    std::unique_ptr<AddServiceCallback> callback) {
  if (!is_enabled()) {
    std::move(callback)->sendFailure(
        Response::ServerError("BluetoothEmulation not enabled"));
    return;
  }

  device::BluetoothUUID uuid(service_uuid);
  if (!uuid.IsValid()) {
    std::move(callback)->sendFailure(Response::InvalidParams(
        base::StrCat({service_uuid, " is not a valid UUID"})));
    return;
  }

  fake_central_->AddFakeService(
      in_address, uuid,
      base::BindOnce(
          [](std::unique_ptr<AddServiceCallback> callback,
             const std::string& error_message,
             const std::optional<std::string>& identifier) {
            if (!identifier) {
              std::move(callback)->sendFailure(
                  Response::ServerError(error_message));
              return;
            }
            DCHECK(IsValidServiceId(*identifier));
            std::move(callback)->sendSuccess(*identifier);
          },
          std::move(callback),
          base::StrCat({"Failed to add service ", service_uuid,
                        " to peripheral ", in_address})));
}

void BluetoothEmulationHandler::RemoveService(
    const std::string& service_id,
    std::unique_ptr<RemoveServiceCallback> callback) {
  if (!is_enabled()) {
    std::move(callback)->sendFailure(
        Response::ServerError("BluetoothEmulation not enabled"));
    return;
  }

  std::string address = getParentId(service_id);
  fake_central_->RemoveFakeService(
      service_id, address,
      base::BindOnce(
          [](std::unique_ptr<RemoveServiceCallback> callback,
             const std::string& error_message, bool success) {
            if (!success) {
              std::move(callback)->sendFailure(
                  Response::ServerError(error_message));
              return;
            }
            std::move(callback)->sendSuccess();
          },
          std::move(callback),
          base::StrCat(
              {"Failed to remove service represented by ", service_id})));
}

void BluetoothEmulationHandler::AddCharacteristic(
    const std::string& service_id,
    const std::string& characteristic_uuid,
    std::unique_ptr<protocol::BluetoothEmulation::CharacteristicProperties>
        in_properties,
    std::unique_ptr<AddCharacteristicCallback> callback) {
  if (!is_enabled()) {
    std::move(callback)->sendFailure(
        Response::ServerError("BluetoothEmulation not enabled"));
    return;
  }

  device::BluetoothUUID uuid(characteristic_uuid);
  if (!uuid.IsValid()) {
    std::move(callback)->sendFailure(Response::InvalidParams(
        base::StrCat({characteristic_uuid, " is not a valid UUID"})));
    return;
  }

  std::string address = getParentId(service_id);
  fake_central_->AddFakeCharacteristic(
      uuid, ToCharacteristicProperties(in_properties.get()), service_id,
      address,
      base::BindOnce(
          [](std::unique_ptr<AddCharacteristicCallback> callback,
             const std::string& error_message,
             const std::optional<std::string>& identifier) {
            if (!identifier) {
              std::move(callback)->sendFailure(
                  Response::ServerError(error_message));
              return;
            }
            DCHECK(IsValidCharacteristicId(*identifier));
            std::move(callback)->sendSuccess(*identifier);
          },
          std::move(callback),
          base::StrCat({"Failed to add characteristic ", characteristic_uuid,
                        " to service ", service_id})));
}

void BluetoothEmulationHandler::RemoveCharacteristic(
    const std::string& characteristic_id,
    std::unique_ptr<RemoveCharacteristicCallback> callback) {
  if (!is_enabled()) {
    std::move(callback)->sendFailure(
        Response::ServerError("BluetoothEmulation not enabled"));
    return;
  }

  std::string service_id = getParentId(characteristic_id);
  std::string address = getParentId(service_id);
  fake_central_->RemoveFakeCharacteristic(
      characteristic_id, service_id, address,
      base::BindOnce(
          [](std::unique_ptr<RemoveCharacteristicCallback> callback,
             const std::string& error_message, bool success) {
            if (!success) {
              std::move(callback)->sendFailure(
                  Response::ServerError(error_message));
              return;
            }
            std::move(callback)->sendSuccess();
          },
          std::move(callback),
          base::StrCat({"Failed to remove characteristic represented by ",
                        characteristic_id})));
}

void BluetoothEmulationHandler::AddDescriptor(
    const std::string& characteristic_id,
    const std::string& descriptor_uuid,
    std::unique_ptr<AddDescriptorCallback> callback) {
  if (!is_enabled()) {
    std::move(callback)->sendFailure(
        Response::ServerError("BluetoothEmulation not enabled"));
    return;
  }

  device::BluetoothUUID uuid(descriptor_uuid);
  if (!uuid.IsValid()) {
    std::move(callback)->sendFailure(Response::InvalidParams(
        base::StrCat({descriptor_uuid, " is not a valid UUID"})));
    return;
  }

  std::string service_id = getParentId(characteristic_id);
  std::string address = getParentId(service_id);
  fake_central_->AddFakeDescriptor(
      uuid, characteristic_id, service_id, address,
      base::BindOnce(
          [](std::unique_ptr<AddDescriptorCallback> callback,
             const std::string& error_message,
             const std::optional<std::string>& identifier) {
            if (!identifier) {
              std::move(callback)->sendFailure(
                  Response::ServerError(error_message));
              return;
            }
            DCHECK(IsValidDescriptorId(*identifier));
            std::move(callback)->sendSuccess(*identifier);
          },
          std::move(callback),
          base::StrCat({"Failed to add descriptor ", descriptor_uuid,
                        " to characteristic ", characteristic_id})));
}

void BluetoothEmulationHandler::RemoveDescriptor(
    const std::string& descriptor_id,
    std::unique_ptr<RemoveDescriptorCallback> callback) {
  if (!is_enabled()) {
    std::move(callback)->sendFailure(
        Response::ServerError("BluetoothEmulation not enabled"));
    return;
  }

  std::string characteristic_id = getParentId(descriptor_id);
  std::string service_id = getParentId(characteristic_id);
  std::string address = getParentId(service_id);
  fake_central_->RemoveFakeDescriptor(
      descriptor_id, characteristic_id, service_id, address,
      base::BindOnce(
          [](std::unique_ptr<RemoveDescriptorCallback> callback,
             const std::string& error_message, bool success) {
            if (!success) {
              std::move(callback)->sendFailure(
                  Response::ServerError(error_message));
              return;
            }
            std::move(callback)->sendSuccess();
          },
          std::move(callback),
          base::StrCat(
              {"Failed to remove descriptor represented by ", descriptor_id})));
}

void BluetoothEmulationHandler::SimulateGATTDisconnection(
    const std::string& address,
    std::unique_ptr<SimulateGATTDisconnectionCallback> callback) {
  if (!is_enabled()) {
    std::move(callback)->sendFailure(
        Response::ServerError("BluetoothEmulation not enabled"));
    return;
  }

  fake_central_->SimulateGATTDisconnection(
      address,
      base::BindOnce(
          [](std::unique_ptr<SimulateGATTDisconnectionCallback> callback,
             bool success) {
            if (!success) {
              std::move(callback)->sendFailure(Response::ServerError(
                  "Failed to simulate GATT disconnection"));
              return;
            }
            std::move(callback)->sendSuccess();
          },
          std::move(callback)));
}

void BluetoothEmulationHandler::DispatchGATTOperationEvent(
    bluetooth::mojom::GATTOperationType type,
    const std::string& peripheral_address) {
  frontend_->GattOperationReceived(peripheral_address,
                                   std::string(ToGATTOperation(type)));
}

void BluetoothEmulationHandler::DispatchCharacteristicOperationEvent(
    bluetooth::mojom::CharacteristicOperationType type,
    const std::optional<std::vector<uint8_t>>& data,
    const std::optional<bluetooth::mojom::WriteType> write_type,
    const std::string& characteristic_id) {
  frontend_->CharacteristicOperationReceived(
      characteristic_id, std::string(ToCharacteristicOperation(type)),
      data ? std::optional(Binary::fromVector(*data)) : std::nullopt,
      write_type
          ? std::optional(std::string(ToCharacteristicWriteType(*write_type)))
          : std::nullopt);
}

void BluetoothEmulationHandler::DispatchDescriptorOperationEvent(
    bluetooth::mojom::DescriptorOperationType type,
    const std::optional<std::vector<uint8_t>>& data,
    const std::string& descriptor_id) {
  frontend_->DescriptorOperationReceived(
      descriptor_id, std::string(ToDescriptorOperation(type)),
      data ? std::optional(Binary::fromVector(*data)) : std::nullopt);
}

}  // namespace content::protocol