// 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.

#include "chrome/browser/ash/arc/bluetooth/arc_floss_bridge.h"

#include <bluetooth/bluetooth.h>
#include <bluetooth/l2cap.h>
#include <bluetooth/rfcomm.h>

#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "chromeos/ash/experiences/arc/bluetooth/bluetooth_type_converters.h"
#include "chromeos/ash/experiences/arc/session/arc_bridge_service.h"
#include "device/bluetooth/bluetooth_socket.h"
#include "device/bluetooth/floss/bluetooth_device_floss.h"
#include "device/bluetooth/floss/floss_dbus_manager.h"
#include "device/bluetooth/floss/floss_sdp_types.h"
#include "device/bluetooth/floss/floss_socket_manager.h"

using device::BluetoothUUID;
using floss::BluetoothDeviceFloss;

namespace arc {

ArcFlossBridge::ArcFlossBridge(content::BrowserContext* context,
                               ArcBridgeService* bridge_service)
    : ArcBluetoothBridge(context, bridge_service) {}

ArcFlossBridge::~ArcFlossBridge() {
  if (GetAdapter() && GetAdapter()->IsPowered() &&
      floss::FlossDBusManager::Get()->GetAdapterClient()->HasObserver(this)) {
    floss::FlossDBusManager::Get()->GetAdapterClient()->RemoveObserver(this);
  }
}

floss::BluetoothAdapterFloss* ArcFlossBridge::GetAdapter() const {
  return static_cast<floss::BluetoothAdapterFloss*>(bluetooth_adapter_.get());
}

void ArcFlossBridge::HandlePoweredOn() {
  if (!floss::FlossDBusManager::Get()->GetAdapterClient()->HasObserver(this)) {
    floss::FlossDBusManager::Get()->GetAdapterClient()->AddObserver(this);
  }
}

void ArcFlossBridge::OnSdpSearchResult(mojom::BluetoothAddressPtr remote_addr,
                                       const device::BluetoothUUID& target_uuid,
                                       floss::DBusResult<bool> result) {
  if (!result.has_value()) {
    OnGetServiceRecordsError(
        std::move(remote_addr), target_uuid,
        bluez::BluetoothServiceRecordBlueZ::ErrorCode::UNKNOWN);
    return;
  }

  if (!*result) {
    OnGetServiceRecordsError(
        std::move(remote_addr), target_uuid,
        bluez::BluetoothServiceRecordBlueZ::ErrorCode::UNKNOWN);
    return;
  }
}

void ArcFlossBridge::GetSdpRecords(mojom::BluetoothAddressPtr remote_addr,
                                   const BluetoothUUID& target_uuid) {
  if (!AdapterReadyAndRegistered()) {
    OnGetServiceRecordsError(
        std::move(remote_addr), target_uuid,
        bluez::BluetoothServiceRecordBlueZ::ErrorCode::ERROR_ADAPTER_NOT_READY);
    return;
  }

  const floss::FlossDeviceId remote_device = floss::FlossDeviceId(
      {.address = remote_addr->To<std::string>(), .name = ""});
  floss::ResponseCallback<bool> response_callback = base::BindOnce(
      &ArcFlossBridge::OnSdpSearchResult, weak_factory_.GetWeakPtr(),
      std::move(remote_addr), target_uuid);

  floss::FlossDBusManager::Get()->GetAdapterClient()->SdpSearch(
      std::move(response_callback), remote_device, target_uuid);
}

void ArcFlossBridge::CreateSdpRecordComplete(device::BluetoothUUID uuid,
                                             floss::DBusResult<bool> result) {
  if (!result.has_value()) {
    arc::mojom::BluetoothCreateSdpRecordResultPtr callback_result =
        arc::mojom::BluetoothCreateSdpRecordResult::New();
    callback_result->status = mojom::BluetoothStatus::FAIL;
    CompleteCreateSdpRecord(uuid, std::move(callback_result));
    return;
  }

  if (!*result) {
    arc::mojom::BluetoothCreateSdpRecordResultPtr callback_result =
        arc::mojom::BluetoothCreateSdpRecordResult::New();
    callback_result->status = mojom::BluetoothStatus::FAIL;
    CompleteCreateSdpRecord(uuid, std::move(callback_result));
    return;
  }
}

void ArcFlossBridge::CreateSdpRecord(mojom::BluetoothSdpRecordPtr record_mojo,
                                     CreateSdpRecordCallback callback) {
  if (!AdapterReadyAndRegistered()) {
    arc::mojom::BluetoothCreateSdpRecordResultPtr result =
        arc::mojom::BluetoothCreateSdpRecordResult::New();
    result->status = mojom::BluetoothStatus::NOT_READY;
    std::move(callback).Run(std::move(result));
    return;
  }
  const floss::BtSdpRecord sdp_record =
      mojo::TypeConverter<floss::BtSdpRecord,
                          bluez::BluetoothServiceRecordBlueZ>::
          Convert(mojo::TypeConverter<
                  bluez::BluetoothServiceRecordBlueZ,
                  mojom::BluetoothSdpRecordPtr>::Convert(record_mojo));
  const device::BluetoothUUID uuid = floss::GetUUIDFromSdpRecord(sdp_record);
  if (!uuid.IsValid()) {
    arc::mojom::BluetoothCreateSdpRecordResultPtr result =
        arc::mojom::BluetoothCreateSdpRecordResult::New();
    result->status = mojom::BluetoothStatus::PARM_INVALID;
    std::move(callback).Run(std::move(result));
    return;
  }
  create_sdp_record_callbacks_.insert_or_assign(uuid, std::move(callback));

  floss::ResponseCallback<bool> response_callback =
      base::BindOnce(&ArcFlossBridge::CreateSdpRecordComplete,
                     weak_factory_.GetWeakPtr(), uuid);
  floss::FlossDBusManager::Get()->GetAdapterClient()->CreateSdpRecord(
      std::move(response_callback), sdp_record);
}

void ArcFlossBridge::RemoveSdpRecordComplete(RemoveSdpRecordCallback callback,
                                             floss::DBusResult<bool> result) {
  if (!result.has_value()) {
    std::move(callback).Run(arc::mojom::BluetoothStatus::FAIL);
    return;
  }

  if (!*result) {
    std::move(callback).Run(arc::mojom::BluetoothStatus::FAIL);
    return;
  }

  std::move(callback).Run(arc::mojom::BluetoothStatus::SUCCESS);
}

void ArcFlossBridge::RemoveSdpRecord(uint32_t service_handle,
                                     RemoveSdpRecordCallback callback) {
  if (!AdapterReadyAndRegistered()) {
    std::move(callback).Run(arc::mojom::BluetoothStatus::NOT_READY);
    return;
  }

  floss::ResponseCallback<bool> response_callback =
      base::BindOnce(&ArcFlossBridge::RemoveSdpRecordComplete,
                     weak_factory_.GetWeakPtr(), std::move(callback));
  floss::FlossDBusManager::Get()->GetAdapterClient()->RemoveSdpRecord(
      std::move(response_callback), service_handle);
}

namespace {

void OnNoOpBtifResult(
    floss::DBusResult<floss::FlossDBusClient::BtifStatus> result) {}

floss::FlossSocketManager::Security GetSecureFromFlags(
    const mojom::BluetoothSocketFlagsPtr flags) {
  // From Floss's masking logic secure = encrypt+auth
  return (flags->encrypt && flags->auth)
             ? floss::FlossSocketManager::Security::kSecure
             : floss::FlossSocketManager::Security::kInsecure;
}

}  // namespace

void ArcFlossBridge::OnCloseBluetoothListeningSocketComplete(
    BluetoothListeningSocket* socket,
    floss::DBusResult<floss::FlossDBusClient::BtifStatus> result) {
  // We are not going to keep the socket around regardless of whether Floss
  // succeeded, but we give it a chance.
  listening_sockets_.erase(socket);
}

void ArcFlossBridge::CloseBluetoothListeningSocket(
    BluetoothListeningSocket* ptr) {
  if (!listening_sockets_.contains(ptr)) {
    return;
  }

  floss::ResponseCallback<floss::FlossAdapterClient::BtifStatus>
      response_callback = base::BindOnce(
          &ArcFlossBridge::OnCloseBluetoothListeningSocketComplete,
          weak_factory_.GetWeakPtr(), ptr);
  floss::FlossDBusManager::Get()->GetSocketManager()->Close(
      listening_sockets_[ptr].second, std::move(response_callback));
}

void ArcFlossBridge::CloseBluetoothConnectingSocket(
    BluetoothConnectingSocket* ptr) {
  auto found = connecting_sockets_.find(ptr);
  connecting_sockets_.erase(found);
}

void ArcFlossBridge::SdpSearchComplete(
    const floss::FlossDeviceId device,
    const device::BluetoothUUID uuid,
    const std::vector<floss::BtSdpRecord>& records) {
  mojom::BluetoothAddressPtr address =
      mojom::BluetoothAddress::From(device.address);
  std::vector<bluez::BluetoothServiceRecordBlueZ> records_bluez;
  for (const auto& record : records) {
    records_bluez.push_back(
        mojo::TypeConverter<bluez::BluetoothServiceRecordBlueZ,
                            floss::BtSdpRecord>::Convert(record));
    floss::BtSdpHeaderOverlay header =
        floss::GetHeaderOverlayFromSdpRecord(record);
    // This record may be for an L2CAP service, but callers have to specify what
    // protocol they want to use anyway.
    uuid_lookups_.insert_or_assign(
        std::make_pair(device.address, header.rfcomm_channel_number),
        header.uuid);
  }
  OnGetServiceRecordsFinished(std::move(address), uuid, records_bluez);
}

void ArcFlossBridge::SdpRecordCreated(const floss::BtSdpRecord record,
                                      const int32_t handle) {
  const device::BluetoothUUID uuid = floss::GetUUIDFromSdpRecord(record);
  if (!uuid.IsValid()) {
    return;
  }
  arc::mojom::BluetoothCreateSdpRecordResultPtr callback_result =
      arc::mojom::BluetoothCreateSdpRecordResult::New();
  callback_result->status = mojom::BluetoothStatus::SUCCESS;
  callback_result->service_handle = handle;
  CompleteCreateSdpRecord(uuid, std::move(callback_result));
}

void ArcFlossBridge::SendCachedDevices() const {
  auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
      arc_bridge_service_->bluetooth(), OnDevicePropertiesChanged);
  if (!bluetooth_instance) {
    return;
  }

  for (const auto* device : bluetooth_adapter_->GetDevices()) {
    const BluetoothDeviceFloss* floss_device =
        static_cast<const BluetoothDeviceFloss*>(device);
    if (!floss_device->HasReadProperties()) {
      VLOG(1) << "Skipping device that hasn't read properties: "
              << floss_device->GetAddress();
      continue;
    }

    // Since a cached device may not be a currently available device, we use
    // OnDevicePropertiesChanged() instead of OnDeviceFound() to avoid trigger
    // the logic of device found in Android.
    bluetooth_instance->OnDevicePropertiesChanged(
        mojom::BluetoothAddress::From(device->GetAddress()),
        GetDeviceProperties(mojom::BluetoothPropertyType::ALL, device));
  }
}

void ArcFlossBridge::StartLEScanImpl() {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  if (!bluetooth_adapter_) {
    LOG(DFATAL) << "Bluetooth adapter does not exist.";
    return;
  }

  if (ble_scan_session_) {
    LOG(ERROR) << "LE scan already running.";
    StartLEScanOffTimer();
    scanned_devices_.clear();
    discovery_queue_.Pop();
    return;
  }

  ble_scan_session_ = bluetooth_adapter_->StartLowEnergyScanSession(
      nullptr, weak_factory_.GetWeakPtr());
}

void ArcFlossBridge::ResetLEScanSession() {
  if (ble_scan_session_) {
    ble_scan_session_.reset();
  }
}

bool ArcFlossBridge::IsDiscoveringOrScanning() {
  return discovery_session_ || ble_scan_session_;
}

void ArcFlossBridge::CreateBluetoothListenSocket(
    mojom::BluetoothSocketType type,
    mojom::BluetoothSocketFlagsPtr flags,
    int port,
    ArcFlossBridge::BluetoothSocketListenCallback callback) {
  if (!AdapterReadyAndRegistered()) {
    return;
  }
  if (type != mojom::BluetoothSocketType::TYPE_RFCOMM &&
      type != mojom::BluetoothSocketType::TYPE_L2CAP_LE) {
    std::move(callback).Run(
        mojom::BluetoothStatus::FAIL, /*port=*/0,
        mojo::PendingReceiver<mojom::BluetoothListenSocketClient>());
    return;
  }
  auto sock_wrapper = std::make_unique<BluetoothListeningSocket>();
  sock_wrapper->sock_type = type;
  auto connection_accepted_callback =
      base::BindRepeating(&ArcFlossBridge::OnConnectionAccepted,
                          weak_factory_.GetWeakPtr(), sock_wrapper.get());
  int socket_ready_callback_id = next_socket_ready_callback_id_++;
  socket_ready_callbacks_.insert_or_assign(socket_ready_callback_id,
                                           std::move(callback));
  auto connection_state_changed_callback = base::BindRepeating(
      &ArcFlossBridge::OnConnectionStateChanged, weak_factory_.GetWeakPtr(),
      // TODO(crbug.com/40061562): Remove `UnsafeDanglingUntriaged`
      base::UnsafeDanglingUntriaged(sock_wrapper.get()),
      socket_ready_callback_id);
  floss::ResponseCallback<floss::FlossDBusClient::BtifStatus>
      response_callback =
          base::BindOnce(&ArcFlossBridge::OnCreateListenSocketCallback,
                         weak_factory_.GetWeakPtr(), std::move(sock_wrapper),
                         socket_ready_callback_id);
  switch (type) {
    case mojom::BluetoothSocketType::TYPE_RFCOMM: {
      floss::FlossDBusManager::Get()->GetSocketManager()->ListenUsingRfcommAlt(
          std::nullopt, std::nullopt, port,
          floss::FlossSocketManager::GetRawFlossFlagsFromBluetoothFlags(
              flags->encrypt, flags->auth, flags->auth_mitm,
              flags->auth_16_digit, /*no_sdp=*/true),
          std::move(response_callback),
          std::move(connection_state_changed_callback),
          std::move(connection_accepted_callback));
      break;
    }
    case mojom::BluetoothSocketType::TYPE_L2CAP_LE: {
      floss::FlossDBusManager::Get()->GetSocketManager()->ListenUsingL2capLe(
          GetSecureFromFlags(std::move(flags)), std::move(response_callback),
          std::move(connection_state_changed_callback),
          std::move(connection_accepted_callback));
      break;
    }
    default: {
      return;
    }
  }
}

void ArcFlossBridge::CreateBluetoothConnectSocket(
    mojom::BluetoothSocketType type,
    mojom::BluetoothSocketFlagsPtr flags,
    mojom::BluetoothAddressPtr addr,
    int port,
    ArcFlossBridge::BluetoothSocketConnectCallback callback) {
  if (!AdapterReadyAndRegistered()) {
    return;
  }
  const floss::FlossDeviceId remote_device =
      floss::FlossDeviceId({.address = addr->To<std::string>(), .name = ""});
  auto sock_wrapper = std::make_unique<BluetoothConnectingSocket>();
  sock_wrapper->sock_type = type;
  switch (type) {
    case mojom::BluetoothSocketType::TYPE_RFCOMM: {
      const std::pair<std::string, int32_t> uuid_lookup_key =
          std::make_pair(remote_device.address, port);
      if (!uuid_lookups_.contains(uuid_lookup_key)) {
        std::move(callback).Run(
            mojom::BluetoothStatus::FAIL,
            mojo::PendingReceiver<mojom::BluetoothConnectSocketClient>());
        return;
      }
      floss::FlossDBusManager::Get()->GetSocketManager()->ConnectUsingRfcomm(
          remote_device, uuid_lookups_.at(uuid_lookup_key),
          GetSecureFromFlags(std::move(flags)),
          base::BindOnce(&ArcFlossBridge::OnCreateConnectSocketCallback,
                         weak_factory_.GetWeakPtr(), std::move(sock_wrapper),
                         std::move(callback)));
      break;
    }
    case mojom::BluetoothSocketType::TYPE_L2CAP_LE: {
      floss::FlossDBusManager::Get()->GetSocketManager()->ConnectUsingL2capLe(
          remote_device, port, GetSecureFromFlags(std::move(flags)),
          base::BindOnce(&ArcFlossBridge::OnCreateConnectSocketCallback,
                         weak_factory_.GetWeakPtr(), std::move(sock_wrapper),
                         std::move(callback)));
      break;
    }
    default: {
      std::move(callback).Run(
          mojom::BluetoothStatus::FAIL,
          mojo::PendingReceiver<mojom::BluetoothConnectSocketClient>());
      return;
    }
  }
}

void ArcFlossBridge::OnCreateListenSocketCallback(
    std::unique_ptr<ArcBluetoothBridge::BluetoothListeningSocket> sock_wrapper,
    int socket_ready_callback_id,
    floss::DBusResult<floss::FlossDBusClient::BtifStatus> result) {
  ArcBluetoothBridge::BluetoothListeningSocket* sock_wrapper_to_pass =
      sock_wrapper.get();
  listening_sockets_.insert_or_assign(
      sock_wrapper.get(),
      std::make_pair(std::move(sock_wrapper), empty_socket_id_));
  if (!result.has_value()) {
    CompleteListenSocketReady(socket_ready_callback_id,
                              mojom::BluetoothStatus::FAIL,
                              sock_wrapper_to_pass);
    return;
  }

  if (*result != floss::FlossDBusClient::BtifStatus::kSuccess) {
    CompleteListenSocketReady(socket_ready_callback_id,
                              mojom::BluetoothStatus::FAIL,
                              sock_wrapper_to_pass);
    return;
  }
}

void ArcFlossBridge::OnCreateConnectSocketCallback(
    std::unique_ptr<ArcBluetoothBridge::BluetoothConnectingSocket> sock_wrapper,
    ArcFlossBridge::BluetoothSocketConnectCallback callback,
    floss::FlossDBusClient::BtifStatus status,
    std::optional<floss::FlossSocketManager::FlossSocket>&& socket) {
  if (status != floss::FlossDBusClient::BtifStatus::kSuccess) {
    std::move(callback).Run(
        mojom::BluetoothStatus::FAIL,
        mojo::PendingReceiver<mojom::BluetoothConnectSocketClient>());
    return;
  }

  if (!socket || !socket->fd) {
    std::move(callback).Run(
        mojom::BluetoothStatus::FAIL,
        mojo::PendingReceiver<mojom::BluetoothConnectSocketClient>());
    return;
  }

  sock_wrapper->file = std::move(socket->fd.value());
  std::move(callback).Run(mojom::BluetoothStatus::SUCCESS,
                          sock_wrapper->remote.BindNewPipeAndPassReceiver());
  auto connection = mojom::BluetoothSocketConnection::New();
  mojo::ScopedHandle handle = mojo::WrapPlatformHandle(
      mojo::PlatformHandle(std::move(sock_wrapper->file)));
  connection->sock = std::move(handle);
  switch (sock_wrapper->sock_type) {
    case mojom::BluetoothSocketType::TYPE_RFCOMM:
    case mojom::BluetoothSocketType::TYPE_L2CAP_LE:
      connection->addr = mojom::BluetoothAddress::From<std::string>(
          socket->remote_device.address);
      connection->port = socket->port;
      break;
    default:
      LOG(ERROR) << "Unknown socket type " << sock_wrapper->sock_type;
      return;
  }
  sock_wrapper->remote->OnConnected(std::move(connection));
  connecting_sockets_.insert(std::move(sock_wrapper));
}

void ArcFlossBridge::OnConnectionStateChanged(
    ArcBluetoothBridge::BluetoothListeningSocket* sock_wrapper,
    int socket_ready_callback_id,
    floss::FlossSocketManager::ServerSocketState state,
    floss::FlossSocketManager::FlossListeningSocket socket,
    floss::FlossSocketManager::BtifStatus status) {
  if (!sock_wrapper) {
    // sock_wrapper has been disposed (or worse), nothing to do, but resolve
    // callback with failure if it is still around.
    CompleteListenSocketReady(socket_ready_callback_id,
                              mojom::BluetoothStatus::FAIL, sock_wrapper,
                              socket);
    return;
  }

  if (status != floss::FlossSocketManager::BtifStatus::kSuccess) {
    LOG(ERROR) << "Received OnConnectionStateChanged callback with "
                  "non-Success status: "
               << static_cast<int>(status);
    CompleteListenSocketReady(socket_ready_callback_id,
                              mojom::BluetoothStatus::FAIL, sock_wrapper,
                              socket);
    return;
  }

  if (state != floss::FlossSocketManager::ServerSocketState::kReady) {
    // Socket isn't ready for accepting connections, nothing to do
    return;
  }

  // At this point we may assume that listening socket creation was a
  // success. Let CompleteListenSocketReady know if it doesn't already.
  CompleteListenSocketReady(socket_ready_callback_id,
                            mojom::BluetoothStatus::SUCCESS, sock_wrapper,
                            socket);
  if (!AdapterReadyAndRegistered()) {
    return;
  }

  floss::ResponseCallback<floss::FlossDBusClient::BtifStatus>
      response_callback = base::BindOnce(&OnNoOpBtifResult);
  // TODO: figure out the correct timeout here
  floss::FlossDBusManager::Get()->GetSocketManager()->Accept(
      socket.id, /*timeout_ms=*/std::nullopt, std::move(response_callback));
}

void ArcFlossBridge::OnConnectionAccepted(
    const ArcBluetoothBridge::BluetoothListeningSocket* sock_wrapper,
    floss::FlossSocketManager::FlossSocket&& socket) {
  if (!sock_wrapper) {
    // sock_wrapper has been disposed (or worse), nothing to do
    return;
  }

  if (!socket.is_valid() || !socket.fd.has_value()) {
    LOG(ERROR) << "New socket connection was accepted with invalid socket";
    return;
  }

  mojo::ScopedHandle handle =
      mojo::WrapPlatformHandle(mojo::PlatformHandle(std::move(*socket.fd)));
  auto connection = mojom::BluetoothSocketConnection::New();
  connection->sock = std::move(handle);
  connection->addr =
      mojom::BluetoothAddress::From(socket.remote_device.address);
  connection->port = socket.port;
  switch (socket.type) {
    case floss::FlossSocketManager::SocketType::kRfcomm:
    case floss::FlossSocketManager::SocketType::kL2cap:
      sock_wrapper->remote->OnAccepted(std::move(connection));
      break;
    default:
      return;
  }
}

int ArcFlossBridge::GetPortOrChannel(
    ArcBluetoothBridge::BluetoothListeningSocket* sock_wrapper,
    floss::FlossSocketManager::FlossListeningSocket socket) {
  switch (sock_wrapper->sock_type) {
    case mojom::BluetoothSocketType::TYPE_RFCOMM:
      if (!socket.channel) {
        return 0;
      }
      return *socket.channel;
    case mojom::BluetoothSocketType::TYPE_L2CAP_LE:
      if (!socket.psm) {
        return 0;
      }
      return *socket.psm;
    default:
      return 0;
  }
}

void ArcFlossBridge::CompleteListenSocketReady(
    int socket_ready_callback_id,
    mojom::BluetoothStatus status,
    ArcBluetoothBridge::BluetoothListeningSocket* sock_wrapper) {
  CompleteListenSocketReady(socket_ready_callback_id, status,
                            /*port_or_channel*/ 0, sock_wrapper);
}

void ArcFlossBridge::CompleteListenSocketReady(
    int socket_ready_callback_id,
    mojom::BluetoothStatus status,
    int port_or_channel,
    ArcBluetoothBridge::BluetoothListeningSocket* sock_wrapper) {
  if (!socket_ready_callbacks_.contains(socket_ready_callback_id)) {
    return;
  }

  BluetoothSocketListenCallback callback =
      std::move(socket_ready_callbacks_[socket_ready_callback_id]);
  socket_ready_callbacks_.erase(socket_ready_callback_id);
  if (status != mojom::BluetoothStatus::SUCCESS) {
    std::move(callback).Run(
        mojom::BluetoothStatus::FAIL, /*port=*/0,
        mojo::PendingReceiver<mojom::BluetoothListenSocketClient>());
  } else {
    std::move(callback).Run(mojom::BluetoothStatus::SUCCESS, port_or_channel,
                            sock_wrapper->remote.BindNewPipeAndPassReceiver());
    sock_wrapper->remote.set_disconnect_handler(
        base::BindOnce(&ArcFlossBridge::CloseBluetoothListeningSocket,
                       weak_factory_.GetWeakPtr(), sock_wrapper));
  }
}

void ArcFlossBridge::CompleteListenSocketReady(
    int socket_ready_callback_id,
    mojom::BluetoothStatus status,
    ArcBluetoothBridge::BluetoothListeningSocket* sock_wrapper,
    floss::FlossSocketManager::FlossListeningSocket socket) {
  if (!socket_ready_callbacks_.contains(socket_ready_callback_id)) {
    return;
  }
  if (!listening_sockets_.contains(sock_wrapper)) {
    return;
  }
  listening_sockets_[sock_wrapper].second = socket.id;
  CompleteListenSocketReady(socket_ready_callback_id, status,
                            GetPortOrChannel(sock_wrapper, socket),
                            sock_wrapper);
}

void ArcFlossBridge::CompleteCreateSdpRecord(
    device::BluetoothUUID uuid,
    arc::mojom::BluetoothCreateSdpRecordResultPtr result) {
  if (!create_sdp_record_callbacks_.contains(uuid)) {
    return;
  }

  CreateSdpRecordCallback callback =
      std::move(create_sdp_record_callbacks_[uuid]);
  create_sdp_record_callbacks_.erase(uuid);
  std::move(callback).Run(std::move(result));
}

bool ArcFlossBridge::AdapterReadyAndRegistered() {
  if (!GetAdapter() || !GetAdapter()->IsPresent()) {
    return false;
  }

  if (!floss::FlossDBusManager::Get()->GetAdapterClient()->HasObserver(this)) {
    floss::FlossDBusManager::Get()->GetAdapterClient()->AddObserver(this);
  }

  return true;
}

}  // namespace arc
