// Copyright 2016 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_bluetooth_bridge.h"

#include <bluetooth/bluetooth.h>
#include <bluetooth/l2cap.h>
#include <bluetooth/rfcomm.h>
#include <fcntl.h>
#include <stdint.h>
#include <sys/socket.h>

#include <algorithm>
#include <iomanip>
#include <optional>
#include <string>
#include <string_view>
#include <utility>

#include "ash/constants/ash_pref_names.h"
#include "base/containers/queue.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/no_destructor.h"
#include "base/posix/eintr_wrapper.h"
#include "base/strings/string_number_conversions.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/time/time.h"
#include "chrome/browser/ash/arc/bluetooth/arc_bluez_bridge.h"
#include "chrome/browser/ash/arc/bluetooth/arc_floss_bridge.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/webui/ash/bluetooth/bluetooth_pairing_dialog.h"
#include "chromeos/ash/components/browser_context_helper/browser_context_helper.h"
#include "chromeos/ash/experiences/arc/arc_browser_context_keyed_service_factory_base.h"
#include "chromeos/ash/experiences/arc/bluetooth/bluetooth_type_converters.h"
#include "chromeos/ash/experiences/arc/intent_helper/arc_intent_helper_bridge.h"
#include "chromeos/ash/experiences/arc/intent_helper/arc_intent_helper_package.h"
#include "chromeos/ash/experiences/arc/session/arc_bridge_service.h"
#include "components/device_event_log/device_event_log.h"
#include "components/prefs/pref_service.h"
#include "components/session_manager/core/session.h"
#include "components/session_manager/core/session_manager.h"
#include "device/bluetooth/bluetooth_common.h"
#include "device/bluetooth/bluetooth_device.h"
#include "device/bluetooth/bluetooth_gatt_connection.h"
#include "device/bluetooth/bluetooth_gatt_notify_session.h"
#include "device/bluetooth/bluetooth_local_gatt_characteristic.h"
#include "device/bluetooth/bluetooth_local_gatt_descriptor.h"
#include "device/bluetooth/bluez/bluetooth_device_bluez.h"
#include "device/bluetooth/bluez/bluetooth_local_gatt_characteristic_bluez.h"
#include "device/bluetooth/bluez/bluetooth_remote_gatt_characteristic_bluez.h"
#include "device/bluetooth/floss/bluetooth_gatt_characteristic_floss.h"
#include "device/bluetooth/floss/floss_features.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/platform/platform_handle.h"
#include "mojo/public/cpp/system/platform_handle.h"

// Enable VLOG level 1.
#undef ENABLED_VLOG_LEVEL
#define ENABLED_VLOG_LEVEL 1

using device::BluetoothAdapter;
using device::BluetoothAdapterFactory;
using device::BluetoothAdvertisement;
using device::BluetoothDevice;
using device::BluetoothDiscoveryFilter;
using device::BluetoothDiscoverySession;
using device::BluetoothGattCharacteristic;
using device::BluetoothGattConnection;
using device::BluetoothGattDescriptor;
using device::BluetoothGattNotifySession;
using device::BluetoothGattService;
using device::BluetoothLocalGattCharacteristic;
using device::BluetoothLocalGattDescriptor;
using device::BluetoothLocalGattService;
using device::BluetoothRemoteGattCharacteristic;
using device::BluetoothRemoteGattDescriptor;
using device::BluetoothRemoteGattService;
using device::BluetoothTransport;
using device::BluetoothUUID;

namespace {
// Bluetooth Spec Vol 3, Part G, 3.3.3.3 Client Characteristic Configuration.
constexpr uint8_t DISABLE_NOTIFICATION_VALUE = 0;
constexpr uint8_t ENABLE_NOTIFICATION_VALUE = 1;
constexpr uint8_t ENABLE_INDICATION_VALUE = 2;
constexpr int32_t kInvalidGattAttributeHandle = -1;
constexpr int32_t kInvalidAdvertisementHandle = -1;
// Bluetooth Specification Version 4.2 Vol 3 Part F Section 3.2.2
// An attribute handle of value 0xFFFF is known as the maximum attribute handle.
constexpr int32_t kMaxGattAttributeHandle = 0xFFFF;
// Bluetooth Specification Version 4.2 Vol 3 Part F Section 3.2.9
// The maximum length of an attribute value shall be 512 octets.
constexpr int kMaxGattAttributeLength = 512;
// Copied from Android at system/bt/stack/btm/btm_ble_int.h
// https://android.googlesource.com/platform/system/bt/+/android-n-preview-5/stack/btm/btm_ble_int.h?pli=1#109
constexpr uint16_t kAndroidMBluetoothVersionNumber = 95;
// Timeout for Bluetooth Discovery (scan)
// 120 seconds is used here as the upper bound of the time need to do device
// discovery once, 20 seconds for inquiry scan and 100 seconds for page scan
// for 100 new devices.
constexpr base::TimeDelta kDiscoveryTimeout = base::Seconds(120);
// From https://www.bluetooth.com/specifications/assigned-numbers/baseband
// The Class of Device for generic computer.
constexpr uint32_t kBluetoothComputerClass = 0x100;
// Timeout for Android to complete a disabling op to adapter.
// In the case where an enabling op happens immediately after a disabling op,
// Android takes the following enabling op as a no-op and waits 3~4 seconds for
// the previous disabling op to finish, so the enabling op will never be
// fulfilled by Android, and the disabling op will later routed back to Chrome
// while Chrome's adapter is enabled. This results in the wrong power state
// which should be enabled. Since the signaling from Android to Chrome for
// Bluetooth is via Bluetooth HAL layer which run on the same process as
// Bluetooth Service in Java space, so the signaling to Chrome about the
// to-be-happen sleep cannot be done. This timeout tries to ensure the validity
// and the order of toggles on power state sent to Android.
// If Android takes more than 8 seconds to complete the intent initiated by
// Chrome, Chrome will take EnableAdapter/DisableAdapter calls as a request from
// Android to toggle the power state. The power state will be synced on both
// Chrome and Android, but as a result, Bluetooth will be off.
constexpr base::TimeDelta kPowerIntentTimeout = base::Seconds(8);

// Client name for logging in BLE scanning.
constexpr char kScanClientName[] = "ARC";

arc::mojom::BluetoothGattStatus ConvertGattErrorCodeToStatus(
    const device::BluetoothGattService::GattErrorCode& error_code,
    bool is_read_operation) {
  switch (error_code) {
    case device::BluetoothGattService::GattErrorCode::kInvalidLength:
      return arc::mojom::BluetoothGattStatus::GATT_INVALID_ATTRIBUTE_LENGTH;
    case device::BluetoothGattService::GattErrorCode::kNotPermitted:
      return is_read_operation
                 ? arc::mojom::BluetoothGattStatus::GATT_READ_NOT_PERMITTED
                 : arc::mojom::BluetoothGattStatus::GATT_WRITE_NOT_PERMITTED;
    case device::BluetoothGattService::GattErrorCode::kNotAuthorized:
      return arc::mojom::BluetoothGattStatus::GATT_INSUFFICIENT_AUTHENTICATION;
    case device::BluetoothGattService::GattErrorCode::kNotSupported:
      return arc::mojom::BluetoothGattStatus::GATT_REQUEST_NOT_SUPPORTED;
    case device::BluetoothGattService::GattErrorCode::kUnknown:
    case device::BluetoothGattService::GattErrorCode::kFailed:
    case device::BluetoothGattService::GattErrorCode::kInProgress:
    case device::BluetoothGattService::GattErrorCode::kNotPaired:
    default:
      return arc::mojom::BluetoothGattStatus::GATT_FAILURE;
  }
}

// Example of identifier: /org/bluez/hci0/dev_E0_CF_65_8C_86_1A/service001a
// Convert the last 4 characters of |identifier| to an
// int, by interpreting them as hexadecimal digits.
std::optional<uint16_t> ConvertGattIdentifierToId(std::string_view identifier) {
  uint32_t result;
  if (identifier.size() < 4 ||
      !base::HexStringToUInt(identifier.substr(identifier.size() - 4), &result))
    return std::nullopt;
  return result;
}

// Create GattDBElement and fill in common data for
// Gatt Service/Characteristic/Descriptor.
template <class RemoteGattAttribute>
arc::mojom::BluetoothGattDBElementPtr CreateGattDBElement(
    const arc::mojom::BluetoothGattDBAttributeType type,
    const RemoteGattAttribute* attribute) {
  std::optional<uint16_t> id =
      ConvertGattIdentifierToId(attribute->GetIdentifier());
  if (!id)
    return nullptr;

  arc::mojom::BluetoothGattDBElementPtr element =
      arc::mojom::BluetoothGattDBElement::New();
  element->type = type;
  element->uuid = attribute->GetUUID();
  element->element_id = element->attribute_handle = element->start_handle =
      element->end_handle = *id;
  // TODO(b/191129417) remove once ARC++ handles new field
  element->deprecated_id = *id;
  element->properties = 0;
  return element;
}

template <class RemoteGattAttribute>
RemoteGattAttribute* FindGattAttributeByUuid(
    const std::vector<RemoteGattAttribute*>& attributes,
    const BluetoothUUID& uuid) {
  auto it = std::ranges::find(attributes, uuid, &RemoteGattAttribute::GetUUID);
  return it != attributes.end() ? *it : nullptr;
}

// Common success callback for GATT operations that only need to report
// GattStatus back to Android.
void OnGattOperationDone(arc::ArcBluetoothBridge::GattStatusCallback callback) {
  std::move(callback).Run(arc::mojom::BluetoothGattStatus::GATT_SUCCESS);
}

// Common error callback for GATT operations that only need to report
// GattStatus back to Android.
void OnGattOperationError(arc::ArcBluetoothBridge::GattStatusCallback callback,
                          BluetoothGattService::GattErrorCode error_code) {
  std::move(callback).Run(ConvertGattErrorCodeToStatus(
      error_code, /* is_read_operation = */ false));
}

// Common callback (success and error) for ReadGattCharacteristic and
// ReadGattDescriptor.
void OnGattRead(
    arc::ArcBluetoothBridge::GattReadCallback callback,
    std::optional<device::BluetoothGattService::GattErrorCode> error_code,
    const std::vector<uint8_t>& result) {
  arc::mojom::BluetoothGattValuePtr gattValue =
      arc::mojom::BluetoothGattValue::New();

  if (error_code.has_value()) {
    gattValue->status = ConvertGattErrorCodeToStatus(
        error_code.value(), /*is_read_operation=*/true);
  } else {
    gattValue->status = arc::mojom::BluetoothGattStatus::GATT_SUCCESS;
  }
  gattValue->value = result;
  std::move(callback).Run(std::move(gattValue));
}

// Callback function for mojom::BluetoothInstance::RequestGattRead
void OnGattServerRead(
    BluetoothLocalGattService::Delegate::ValueCallback callback,
    arc::mojom::BluetoothGattStatus status,
    const std::vector<uint8_t>& value) {
  if (status == arc::mojom::BluetoothGattStatus::GATT_SUCCESS) {
    std::move(callback).Run(/*error_code=*/std::nullopt, value);
  } else {
    std::move(callback).Run(BluetoothGattService::GattErrorCode::kFailed,
                            /*value=*/std::vector<uint8_t>());
  }
}

// Callback function for mojom::BluetoothInstance::RequestGattWrite
void OnGattServerWrite(
    base::OnceClosure success_callback,
    BluetoothLocalGattService::Delegate::ErrorCallback error_callback,
    arc::mojom::BluetoothGattStatus status) {
  if (status == arc::mojom::BluetoothGattStatus::GATT_SUCCESS)
    std::move(success_callback).Run();
  else
    std::move(error_callback).Run();
}

bool IsGattOffsetValid(int offset) {
  return 0 <= offset && offset < kMaxGattAttributeLength;
}

// This is needed because Android only support UUID 16 bits in service data
// section in advertising data
std::optional<uint16_t> GetUUID16(const BluetoothUUID& uuid) {
  // Convert xxxxyyyy-xxxx-xxxx-xxxx-xxxxxxxxxxxx to int16 yyyy
  uint32_t result;
  if (uuid.canonical_value().size() < 8 ||
      !base::HexStringToUInt(uuid.canonical_value().substr(4, 4), &result))
    return std::nullopt;
  return result;
}

arc::mojom::BluetoothPropertyPtr GetDiscoveryTimeoutProperty(uint32_t timeout) {
  return arc::mojom::BluetoothProperty::NewDiscoveryTimeout(timeout);
}

const device::BluetoothLocalGattDescriptor* FindCCCD(
    const device::BluetoothLocalGattCharacteristic* characteristic) {
  for (auto descriptor : characteristic->GetDescriptors()) {
    if (descriptor->GetUUID() ==
        BluetoothGattDescriptor::ClientCharacteristicConfigurationUuid()) {
      return descriptor;
    }
  }
  return nullptr;
}

std::vector<uint8_t> MakeCCCDValue(uint8_t value) {
  return {value, 0};
}

void SendRssiOnGetConnectionInfoDone(
    arc::ArcBluetoothBridge::ReadRemoteRssiCallback callback,
    const device::BluetoothDevice::ConnectionInfo& conn_info) {
  std::move(callback).Run(conn_info.rssi);
}

}  // namespace

namespace arc {
namespace {

// Singleton factory for ArcAccessibilityHelperBridge.
class ArcBluezBridgeFactory
    : public internal::ArcBrowserContextKeyedServiceFactoryBase<
          ArcBluezBridge,
          ArcBluezBridgeFactory> {
 public:
  // Factory name used by ArcBrowserContextKeyedServiceFactoryBase.
  static constexpr const char* kName = "ArcBluezBridgeFactory";

  static ArcBluezBridgeFactory* GetInstance() {
    static base::NoDestructor<ArcBluezBridgeFactory> instance;
    return instance.get();
  }

 private:
  friend base::NoDestructor<ArcBluezBridgeFactory>;
  ArcBluezBridgeFactory() = default;
  ~ArcBluezBridgeFactory() override = default;
};

// Singleton factory for ArcAccessibilityHelperBridge.
class ArcFlossBridgeFactory
    : public internal::ArcBrowserContextKeyedServiceFactoryBase<
          ArcFlossBridge,
          ArcFlossBridgeFactory> {
 public:
  // Factory name used by ArcBrowserContextKeyedServiceFactoryBase.
  static constexpr const char* kName = "ArcFlossBridgeFactory";

  static ArcFlossBridgeFactory* GetInstance() {
    static base::NoDestructor<ArcFlossBridgeFactory> instance;
    return instance.get();
  }

 private:
  friend base::NoDestructor<ArcFlossBridgeFactory>;
  ArcFlossBridgeFactory() = default;
  ~ArcFlossBridgeFactory() override = default;
};

}  // namespace

// static
ArcBluetoothBridge* ArcBluetoothBridge::GetForBrowserContext(
    content::BrowserContext* context) {
  if (floss::features::IsFlossEnabled()) {
    return ArcFlossBridgeFactory::GetForBrowserContext(context);
  } else {
    return ArcBluezBridgeFactory::GetForBrowserContext(context);
  }
}

ArcBluetoothBridge::ArcBluetoothBridge(content::BrowserContext* context,
                                       ArcBridgeService* bridge_service)
    : arc_bridge_service_(bridge_service),
      bluetooth_arc_connection_observer_(this) {
  arc_bridge_service_->app()->AddObserver(this);
  arc_bridge_service_->intent_helper()->AddObserver(this);

  if (BluetoothAdapterFactory::IsBluetoothSupported()) {
    VLOG(1) << "Registering bluetooth adapter.";
    BluetoothAdapterFactory::Get()->GetAdapter(base::BindOnce(
        &ArcBluetoothBridge::OnAdapterInitialized, weak_factory_.GetWeakPtr()));
  } else {
    VLOG(1) << "Bluetooth not supported.";
  }
}

ArcBluetoothBridge::~ArcBluetoothBridge() {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);

  if (bluetooth_adapter_)
    bluetooth_adapter_->RemoveObserver(this);

  arc_bridge_service_->app()->RemoveObserver(this);
  arc_bridge_service_->intent_helper()->RemoveObserver(this);
  arc_bridge_service_->bluetooth()->SetHost(nullptr);
}

void ArcBluetoothBridge::OnAdapterInitialized(
    scoped_refptr<BluetoothAdapter> adapter) {
  DCHECK(adapter);
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);

  bluetooth_adapter_ = adapter;

  if (!bluetooth_adapter_->HasObserver(this))
    bluetooth_adapter_->AddObserver(this);

  // Once the bluetooth adapter is ready, we can now signal the container that
  // the interface is ready to be interacted with. This avoids races in most
  // methods, since it's undesirable to implement a retry mechanism for the
  // cases when an inbound method is called and the adapter is not ready yet.
  arc_bridge_service_->bluetooth()->SetHost(this);
}

void ArcBluetoothBridge::AdapterPoweredChanged(BluetoothAdapter* adapter,
                                               bool powered) {
  AdapterPowerState power_change =
      powered ? AdapterPowerState::TURN_ON : AdapterPowerState::TURN_OFF;
  if (IsPowerChangeInitiatedByRemote(power_change))
    DequeueRemotePowerChange(power_change);
  else
    EnqueueLocalPowerChange(power_change);
}

void ArcBluetoothBridge::DeviceAdded(BluetoothAdapter* adapter,
                                     BluetoothDevice* device) {
  DeviceChanged(adapter, device);

  // We need to trigger this manually if the device is connected when it is
  // added. This may happen for a incoming connection from an unknown device.
  if (device->IsConnected())
    DeviceConnectedStateChanged(adapter, device, /*is_now_connected=*/true);
}

void ArcBluetoothBridge::DeviceChanged(BluetoothAdapter* adapter,
                                       BluetoothDevice* device) {
  if (!arc_bridge_service_->bluetooth()->IsConnected())
    return;

  std::string addr = device->GetAddress();
  if (IsDiscoveringOrScanning() && discovered_devices_.insert(addr).second) {
    auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
        arc_bridge_service_->bluetooth(), OnDeviceFound);
    if (bluetooth_instance) {
      bluetooth_instance->OnDeviceFound(
          GetDeviceProperties(mojom::BluetoothPropertyType::ALL, device));
    }
  } else if (discovered_devices_.contains(addr)) {
    auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
        arc_bridge_service_->bluetooth(), OnDevicePropertiesChanged);
    if (bluetooth_instance) {
      bluetooth_instance->OnDevicePropertiesChanged(
          mojom::BluetoothAddress::From(addr),
          GetDeviceProperties(mojom::BluetoothPropertyType::ALL, device));
    }
  }

  TrackPairingState(device);
}

void ArcBluetoothBridge::TrackPairingState(const BluetoothDevice* device) {
  const std::string addr = device->GetAddress();

  // A device in pairing is in |devices_paired_by_arc_| from CreateBond() is
  // called until at least pairing is finished (either succeed or fail), so we
  // don't need to do anything here if the device is not in the list.
  if (devices_paired_by_arc_.find(addr) == devices_paired_by_arc_.end())
    return;

  const auto itr = devices_pairing_.find(addr);
  bool was_pairing = itr != devices_pairing_.end();

  // The actions we need to take depends on the combination of |was_pairing|,
  // IsConnecting() and IsPaired():
  // If not |was_pairing|:
  // - !IsConnecting() means device is not pairing, do nothing;
  // - IsPaired() means device has already been paired, do nothing;
  // - IsConnecting() && !IsPaired() means device is pairing now, we should add
  //   it into our list.
  // If |was_pairing|:
  // - IsPaired() means pairing succeeded, we should remove the device from our
  //   list.
  // - IsConnecting() && !IsPaired() means device is still in pairing, do
  //   nothing;
  // - !IsConnecting() && !IsPaired() means pairing failed, we should notify
  //   Android, and remove the device from our list;
  if (!was_pairing) {
    if (device->IsConnecting() && !device->IsPaired())
      devices_pairing_.insert(addr);
    return;
  }

  if (device->IsPaired()) {
    devices_pairing_.erase(itr);
  } else if (!device->IsConnecting()) {
    LOG(WARNING) << "Pairing failed for device " << addr;
    OnPairedError(mojom::BluetoothAddress::From(addr),
                  BluetoothDevice::ERROR_FAILED);
    devices_pairing_.erase(itr);
  }
}

void ArcBluetoothBridge::DeviceAddressChanged(BluetoothAdapter* adapter,
                                              BluetoothDevice* device,
                                              const std::string& old_address) {
  if (!arc_bridge_service_->bluetooth()->IsConnected())
    return;

  std::string new_address = device->GetAddress();
  if (old_address == new_address)
    return;

  if (!(device->GetType() & device::BLUETOOTH_TRANSPORT_LE))
    return;

  if (devices_paired_by_arc_.erase(old_address) == 1)
    devices_paired_by_arc_.insert(new_address);

  auto it = gatt_connections_.find(old_address);
  if (it == gatt_connections_.end())
    return;

  gatt_connections_.emplace(new_address, std::move(it->second));
  gatt_connections_.erase(it);

  auto* btle_instance = ARC_GET_INSTANCE_FOR_METHOD(
      arc_bridge_service_->bluetooth(), OnLEDeviceAddressChange);
  if (!btle_instance)
    return;

  btle_instance->OnLEDeviceAddressChange(
      mojom::BluetoothAddress::From(old_address),
      mojom::BluetoothAddress::From(new_address));
}

void ArcBluetoothBridge::DevicePairedChanged(BluetoothAdapter* adapter,
                                             BluetoothDevice* device,
                                             bool new_paired_status) {
  if (!arc_bridge_service_->bluetooth()->IsConnected())
    return;

  DCHECK(adapter);
  DCHECK(device);

  mojom::BluetoothAddressPtr addr =
      mojom::BluetoothAddress::From(device->GetAddress());

  if (new_paired_status) {
    // OnBondStateChanged must be called with BluetoothBondState::BONDING to
    // make sure the bond state machine on Android is ready to take the
    // pair-done event. Otherwise the pair-done event will be dropped as an
    // invalid change of paired status.
    OnPairing(addr->Clone());
    OnPairedDone(std::move(addr));
  } else {
    OnForgetDone(std::move(addr));
  }
}

void ArcBluetoothBridge::DeviceMTUChanged(BluetoothAdapter* adapter,
                                          BluetoothDevice* device,
                                          uint16_t mtu) {
  if (!arc_bridge_service_->bluetooth()->IsConnected())
    return;

  auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
      arc_bridge_service_->bluetooth(), OnMTUReceived);
  if (!device->IsConnected() || bluetooth_instance == nullptr)
    return;
  bluetooth_instance->OnMTUReceived(
      mojom::BluetoothAddress::From(device->GetAddress()), mtu);
}

void ArcBluetoothBridge::DeviceAdvertisementReceived(
    BluetoothAdapter* adapter,
    BluetoothDevice* device,
    int16_t rssi,
    const std::vector<uint8_t>& eir) {
  if (!arc_bridge_service_->bluetooth()->IsConnected())
    return;

  mojom::BluetoothAddressPtr addr =
      mojom::BluetoothAddress::From(device->GetAddress());

  if ((IsDiscoveringOrScanning() &&
       scanned_devices_.insert(device->GetAddress()).second) ||
      scanned_devices_.contains(device->GetAddress())) {
    auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
        arc_bridge_service_->bluetooth(), OnLEDeviceFound);
    if (bluetooth_instance) {
      bluetooth_instance->OnLEDeviceFound(std::move(addr), rssi, eir);
    }
  }
}

void ArcBluetoothBridge::DeviceConnectedStateChanged(BluetoothAdapter* adapter,
                                                     BluetoothDevice* device,
                                                     bool is_now_connected) {
  if (!arc_bridge_service_->bluetooth()->IsConnected())
    return;

  const std::string addr = device->GetAddress();

  // If this event is about 1) an device supports LE becomes disconnected and 2)
  // we are holding the connection object for this device, we need to remove
  // this object and notify Android.
  bool support_le = device->GetType() & device::BLUETOOTH_TRANSPORT_LE;
  auto it = gatt_connections_.find(addr);
  if (support_le && it != gatt_connections_.end() && !is_now_connected)
    OnGattDisconnected(mojom::BluetoothAddress::From(addr));

  auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
      arc_bridge_service_->bluetooth(), OnConnectionStateChanged);
  if (!bluetooth_instance)
    return;

  bluetooth_instance->OnConnectionStateChanged(
      mojom::BluetoothAddress::From(addr), device->GetType(), is_now_connected);
}

void ArcBluetoothBridge::DeviceRemoved(BluetoothAdapter* adapter,
                                       BluetoothDevice* device) {
  if (!arc_bridge_service_->bluetooth()->IsConnected())
    return;

  DCHECK(adapter);
  DCHECK(device);

  std::string address = device->GetAddress();
  if (gatt_connections_.find(address) != gatt_connections_.end())
    OnGattDisconnected(mojom::BluetoothAddress::From(address));
  OnForgetDone(mojom::BluetoothAddress::From(address));
}

void ArcBluetoothBridge::OnGetServiceRecordsFinished(
    mojom::BluetoothAddressPtr remote_addr,
    const BluetoothUUID& target_uuid,
    const std::vector<bluez::BluetoothServiceRecordBlueZ>& records_bluez) {
  // TODO(b/288866953): ARCVM crashes if records are empty
  if (records_bluez.size() == 0) {
    return;
  }

  auto* sdp_bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
      arc_bridge_service_->bluetooth(), OnGetSdpRecords);
  if (!sdp_bluetooth_instance) {
    LOG(ERROR) << "Could not get bluetooth instance to return SDP records";
    return;
  }

  std::vector<mojom::BluetoothSdpRecordPtr> records;
  for (const auto& r : records_bluez) {
    records.push_back(mojom::BluetoothSdpRecord::From(r));
  }

  sdp_bluetooth_instance->OnGetSdpRecords(mojom::BluetoothStatus::SUCCESS,
                                          std::move(remote_addr), target_uuid,
                                          std::move(records));
}

void ArcBluetoothBridge::OnGetServiceRecordsError(
    mojom::BluetoothAddressPtr remote_addr,
    const BluetoothUUID& target_uuid,
    bluez::BluetoothServiceRecordBlueZ::ErrorCode error_code) {
  auto* sdp_bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
      arc_bridge_service_->bluetooth(), OnGetSdpRecords);
  if (!sdp_bluetooth_instance) {
    LOG(ERROR) << "Could not get bluetooth instance to return SDP error";
    return;
  }

  mojom::BluetoothStatus status;

  switch (error_code) {
    case bluez::BluetoothServiceRecordBlueZ::ErrorCode::ERROR_ADAPTER_NOT_READY:
      status = mojom::BluetoothStatus::NOT_READY;
      break;
    case bluez::BluetoothServiceRecordBlueZ::ErrorCode::
        ERROR_DEVICE_DISCONNECTED:
      status = mojom::BluetoothStatus::RMT_DEV_DOWN;
      break;
    default:
      status = mojom::BluetoothStatus::FAIL;
      break;
  }

  sdp_bluetooth_instance->OnGetSdpRecords(
      status, std::move(remote_addr), target_uuid,
      std::vector<mojom::BluetoothSdpRecordPtr>());
}

void ArcBluetoothBridge::GattServiceAdded(BluetoothAdapter* adapter,
                                          BluetoothDevice* device,
                                          BluetoothRemoteGattService* service) {
  if (!arc_bridge_service_->bluetooth()->IsConnected())
    return;
  // Placeholder for GATT client functionality
  GattServiceChanged(adapter, service);
}

void ArcBluetoothBridge::GattServiceRemoved(
    BluetoothAdapter* adapter,
    BluetoothDevice* device,
    BluetoothRemoteGattService* service) {
  if (!arc_bridge_service_->bluetooth()->IsConnected())
    return;
  // Placeholder for GATT client functionality
  GattServiceChanged(adapter, service);
}

void ArcBluetoothBridge::GattServicesDiscovered(BluetoothAdapter* adapter,
                                                BluetoothDevice* device) {
  if (!arc_bridge_service_->bluetooth()->IsConnected())
    return;

  auto* btle_instance = ARC_GET_INSTANCE_FOR_METHOD(
      arc_bridge_service_->bluetooth(), OnSearchComplete);
  if (!btle_instance)
    return;

  mojom::BluetoothAddressPtr addr =
      mojom::BluetoothAddress::From(device->GetAddress());

  btle_instance->OnSearchComplete(std::move(addr),
                                  mojom::BluetoothGattStatus::GATT_SUCCESS);
}

void ArcBluetoothBridge::GattDiscoveryCompleteForService(
    BluetoothAdapter* adapter,
    BluetoothRemoteGattService* service) {
  if (!arc_bridge_service_->bluetooth()->IsConnected())
    return;
  // Placeholder for GATT client functionality
}

void ArcBluetoothBridge::GattNeedsDiscovery(BluetoothDevice* device) {
  if (!arc_bridge_service_->bluetooth()->IsConnected()) {
    return;
  }

  // This is a bit of a misnomer from ARC side: OnServiceChanged needs to be
  // called when we get the signal that something is changed on the peer side,
  // so ARC can start to re-discover everything again.
  // However, the GattServiceChanged below indicates we have updated a service,
  // so it doesn't actually mean ARC needs to re-discover everything.
  auto* btle_instance = ARC_GET_INSTANCE_FOR_METHOD(
      arc_bridge_service_->bluetooth(), OnServiceChanged);
  if (!btle_instance) {
    return;
  }
  btle_instance->OnServiceChanged(
      mojom::BluetoothAddress::From(device->GetAddress()));
}

// TODO(b/284429795) This is wrong. See GattNeedsDiscovery above.
void ArcBluetoothBridge::GattServiceChanged(
    BluetoothAdapter* adapter,
    BluetoothRemoteGattService* service) {
  if (!arc_bridge_service_->bluetooth()->IsConnected())
    return;
  BluetoothDevice* device = service->GetDevice();
  if (!device)
    return;

  auto* btle_instance = ARC_GET_INSTANCE_FOR_METHOD(
      arc_bridge_service_->bluetooth(), OnServiceChanged);
  if (!btle_instance)
    return;
  btle_instance->OnServiceChanged(
      mojom::BluetoothAddress::From(device->GetAddress()));
}

void ArcBluetoothBridge::GattCharacteristicAdded(
    BluetoothAdapter* adapter,
    BluetoothRemoteGattCharacteristic* characteristic) {
  if (!arc_bridge_service_->bluetooth()->IsConnected())
    return;
  // Placeholder for GATT client functionality
}

void ArcBluetoothBridge::GattCharacteristicRemoved(
    BluetoothAdapter* adapter,
    BluetoothRemoteGattCharacteristic* characteristic) {
  if (!arc_bridge_service_->bluetooth()->IsConnected())
    return;
  // Placeholder for GATT client functionality
}

void ArcBluetoothBridge::GattDescriptorAdded(
    BluetoothAdapter* adapter,
    BluetoothRemoteGattDescriptor* descriptor) {
  if (!arc_bridge_service_->bluetooth()->IsConnected())
    return;
  // Placeholder for GATT client functionality
}

void ArcBluetoothBridge::GattDescriptorRemoved(
    BluetoothAdapter* adapter,
    BluetoothRemoteGattDescriptor* descriptor) {
  if (!arc_bridge_service_->bluetooth()->IsConnected())
    return;
  // Placeholder for GATT client functionality
}

void ArcBluetoothBridge::GattCharacteristicValueChanged(
    BluetoothAdapter* adapter,
    BluetoothRemoteGattCharacteristic* characteristic,
    const std::vector<uint8_t>& value) {
  if (!arc_bridge_service_->bluetooth()->IsConnected())
    return;

  auto* btle_instance = ARC_GET_INSTANCE_FOR_METHOD(
      arc_bridge_service_->bluetooth(), OnGattNotify);
  if (!btle_instance)
    return;

  const std::optional<uint16_t> char_inst_id =
      ConvertGattIdentifierToId(characteristic->GetIdentifier());
  if (!char_inst_id)
    return;

  BluetoothRemoteGattService* service = characteristic->GetService();
  const std::optional<uint16_t> service_inst_id =
      ConvertGattIdentifierToId(service->GetIdentifier());
  if (!service_inst_id)
    return;

  BluetoothDevice* device = service->GetDevice();
  mojom::BluetoothAddressPtr address =
      mojom::BluetoothAddress::From(device->GetAddress());
  mojom::BluetoothGattServiceIDPtr service_id =
      mojom::BluetoothGattServiceID::New();
  service_id->is_primary = service->IsPrimary();
  service_id->id = mojom::BluetoothGattID::New();
  service_id->id->instance_id = *service_inst_id;
  // TODO(b/191129417) remove once ARC++ handles new field
  service_id->id->deprecated_inst_id = *service_inst_id;
  service_id->id->uuid = service->GetUUID();

  mojom::BluetoothGattIDPtr char_id = mojom::BluetoothGattID::New();
  char_id->instance_id = *char_inst_id;
  // TODO(b/191129417) remove once ARC++ handles new field
  char_id->deprecated_inst_id = *char_inst_id;
  char_id->uuid = characteristic->GetUUID();

  btle_instance->OnGattNotify(std::move(address), std::move(service_id),
                              std::move(char_id), true /* is_notify */, value);
}

void ArcBluetoothBridge::GattDescriptorValueChanged(
    BluetoothAdapter* adapter,
    BluetoothRemoteGattDescriptor* descriptor,
    const std::vector<uint8_t>& value) {
  if (!arc_bridge_service_->bluetooth()->IsConnected())
    return;
  // Placeholder for GATT client functionality
}

template <class LocalGattAttribute>
void ArcBluetoothBridge::OnGattAttributeReadRequest(
    const BluetoothDevice* device,
    const LocalGattAttribute* attribute,
    int offset,
    mojom::BluetoothGattDBAttributeType attribute_type,
    ValueCallback callback) {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
      arc_bridge_service_->bluetooth(), RequestGattRead);
  if (!device) {
    LOG(WARNING) << __func__ << ": Device is null.";
    std::move(callback).Run(BluetoothGattService::GattErrorCode::kFailed,
                            /*value=*/std::vector<uint8_t>());
    return;
  }
  if (!bluetooth_instance || !IsGattOffsetValid(offset)) {
    std::move(callback).Run(BluetoothGattService::GattErrorCode::kFailed,
                            /*value=*/std::vector<uint8_t>());
    return;
  }

  DCHECK(gatt_handle_.find(attribute->GetIdentifier()) != gatt_handle_.end());

  bluetooth_instance->RequestGattRead(
      mojom::BluetoothAddress::From(device->GetAddress()),
      gatt_handle_[attribute->GetIdentifier()], offset, false /* is_long */,
      attribute_type, base::BindOnce(&OnGattServerRead, std::move(callback)));
}

void ArcBluetoothBridge::OnGattServerPrepareWrite(
    mojom::BluetoothAddressPtr addr,
    bool has_subsequent_write,
    base::OnceClosure success_callback,
    ErrorCallback error_callback,
    mojom::BluetoothGattStatus status) {
  bool success = (status == mojom::BluetoothGattStatus::GATT_SUCCESS);
  if (success && has_subsequent_write) {
    std::move(success_callback).Run();
    return;
  }

  auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
      arc_bridge_service_->bluetooth(), RequestGattExecuteWrite);
  if (bluetooth_instance == nullptr) {
    std::move(error_callback).Run();
    return;
  }

  if (!success) {
    auto split_callback = base::SplitOnceCallback(std::move(error_callback));
    success_callback = std::move(split_callback.first);
    error_callback = std::move(split_callback.second);
  }
  bluetooth_instance->RequestGattExecuteWrite(
      std::move(addr), success,
      base::BindOnce(&OnGattServerWrite, std::move(success_callback),
                     std::move(error_callback)));
}

template <class LocalGattAttribute>
void ArcBluetoothBridge::OnGattAttributeWriteRequest(
    const BluetoothDevice* device,
    const LocalGattAttribute* attribute,
    const std::vector<uint8_t>& value,
    int offset,
    mojom::BluetoothGattDBAttributeType attribute_type,
    bool is_prepare,
    bool has_subsequent_write,
    base::OnceClosure success_callback,
    ErrorCallback error_callback) {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
      arc_bridge_service_->bluetooth(), RequestGattWrite);
  if (!device) {
    LOG(WARNING) << __func__ << ": Device is null.";
    std::move(error_callback).Run();
    return;
  }
  if (!bluetooth_instance || !IsGattOffsetValid(offset)) {
    std::move(error_callback).Run();
    return;
  }

  GattStatusCallback callback =
      is_prepare
          ? base::BindOnce(&ArcBluetoothBridge::OnGattServerPrepareWrite,
                           weak_factory_.GetWeakPtr(),
                           mojom::BluetoothAddress::From(device->GetAddress()),
                           has_subsequent_write, std::move(success_callback),
                           std::move(error_callback))
          : base::BindOnce(&OnGattServerWrite, std::move(success_callback),
                           std::move(error_callback));
  DCHECK(gatt_handle_.find(attribute->GetIdentifier()) != gatt_handle_.end());
  bluetooth_instance->RequestGattWrite(
      mojom::BluetoothAddress::From(device->GetAddress()),
      gatt_handle_[attribute->GetIdentifier()], offset, value, attribute_type,
      is_prepare, std::move(callback));
}

void ArcBluetoothBridge::OnCharacteristicReadRequest(
    const BluetoothDevice* device,
    const BluetoothLocalGattCharacteristic* characteristic,
    int offset,
    ValueCallback callback) {
  OnGattAttributeReadRequest(
      device, characteristic, offset,
      mojom::BluetoothGattDBAttributeType::BTGATT_DB_CHARACTERISTIC,
      std::move(callback));
}

void ArcBluetoothBridge::OnCharacteristicWriteRequest(
    const BluetoothDevice* device,
    const BluetoothLocalGattCharacteristic* characteristic,
    const std::vector<uint8_t>& value,
    int offset,
    base::OnceClosure callback,
    ErrorCallback error_callback) {
  OnGattAttributeWriteRequest(
      device, characteristic, value, offset,
      mojom::BluetoothGattDBAttributeType::BTGATT_DB_CHARACTERISTIC,
      /* is_prepare = */ false, /* has_subsequent_write, = */ false,
      std::move(callback), std::move(error_callback));
}

void ArcBluetoothBridge::OnCharacteristicPrepareWriteRequest(
    const BluetoothDevice* device,
    const BluetoothLocalGattCharacteristic* characteristic,
    const std::vector<uint8_t>& value,
    int offset,
    bool has_subsequent_write,
    base::OnceClosure callback,
    ErrorCallback error_callback) {
  OnGattAttributeWriteRequest(
      device, characteristic, value, offset,
      mojom::BluetoothGattDBAttributeType::BTGATT_DB_CHARACTERISTIC,
      /* is_prepare = */ true, has_subsequent_write, std::move(callback),
      std::move(error_callback));
}

void ArcBluetoothBridge::OnDescriptorReadRequest(
    const BluetoothDevice* device,
    const BluetoothLocalGattDescriptor* descriptor,
    int offset,
    ValueCallback callback) {
  OnGattAttributeReadRequest(
      device, descriptor, offset,
      mojom::BluetoothGattDBAttributeType::BTGATT_DB_DESCRIPTOR,
      std::move(callback));
}

void ArcBluetoothBridge::OnDescriptorWriteRequest(
    const BluetoothDevice* device,
    const BluetoothLocalGattDescriptor* descriptor,
    const std::vector<uint8_t>& value,
    int offset,
    base::OnceClosure callback,
    ErrorCallback error_callback) {
  OnGattAttributeWriteRequest(
      device, descriptor, value, offset,
      mojom::BluetoothGattDBAttributeType::BTGATT_DB_DESCRIPTOR,
      /* is_prepare = */ false, /* has_subsequent_write = */ false,
      std::move(callback), std::move(error_callback));
}

void ArcBluetoothBridge::OnNotificationsStart(
    const BluetoothDevice* device,
    device::BluetoothGattCharacteristic::NotificationType notification_type,
    const BluetoothLocalGattCharacteristic* characteristic) {
  const BluetoothLocalGattDescriptor* cccd = FindCCCD(characteristic);
  if (cccd == nullptr)
    return;
  OnDescriptorWriteRequest(
      device, cccd,
      MakeCCCDValue(notification_type ==
                            device::BluetoothRemoteGattCharacteristic::
                                NotificationType::kNotification
                        ? ENABLE_NOTIFICATION_VALUE
                        : ENABLE_INDICATION_VALUE),
      0, base::DoNothing(), base::DoNothing());
}

void ArcBluetoothBridge::OnNotificationsStop(
    const BluetoothDevice* device,
    const BluetoothLocalGattCharacteristic* characteristic) {
  const BluetoothLocalGattDescriptor* cccd = FindCCCD(characteristic);
  if (cccd == nullptr)
    return;
  OnDescriptorWriteRequest(device, cccd,
                           MakeCCCDValue(DISABLE_NOTIFICATION_VALUE), 0,
                           base::DoNothing(), base::DoNothing());
}

void ArcBluetoothBridge::OnDeviceFound(
    device::BluetoothLowEnergyScanSession* scan_session,
    device::BluetoothDevice* device) {
  DeviceAdded(bluetooth_adapter_.get(), device);
}

void ArcBluetoothBridge::OnDeviceLost(
    device::BluetoothLowEnergyScanSession* scan_session,
    device::BluetoothDevice* device) {}

void ArcBluetoothBridge::OnSessionStarted(
    device::BluetoothLowEnergyScanSession* scan_session,
    std::optional<device::BluetoothLowEnergyScanSession::ErrorCode>
        error_code) {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);

  if (error_code) {
    LOG(WARNING) << "failed to start LE scan, error_code = "
                 << static_cast<int>(error_code.value());
    ResetLEScanSession();
  } else {
    StartLEScanOffTimer();
  }

  discovery_queue_.Pop();
}

void ArcBluetoothBridge::OnSessionInvalidated(
    device::BluetoothLowEnergyScanSession* scan_session) {
  LOG(WARNING) << "LE scan session was invalidated";
  ResetLEScanSession();
}

void ArcBluetoothBridge::EnableAdapter(EnableAdapterCallback callback) {
  DCHECK(bluetooth_adapter_);
  if (IsPowerChangeInitiatedByLocal(AdapterPowerState::TURN_ON)) {
    BLUETOOTH_LOG(EVENT) << "Received a request to enable adapter (local)";
    DequeueLocalPowerChange(AdapterPowerState::TURN_ON);
  } else {
    BLUETOOTH_LOG(EVENT) << "Received a request to enable adapter (remote)";
    if (!bluetooth_adapter_->IsPowered()) {
      EnqueueRemotePowerChange(AdapterPowerState::TURN_ON, std::move(callback));
      return;
    }
  }

  OnPoweredOn(std::move(callback), false /* save_user_pref */);
}

void ArcBluetoothBridge::DisableAdapter(DisableAdapterCallback callback) {
  DCHECK(bluetooth_adapter_);
  if (IsPowerChangeInitiatedByLocal(AdapterPowerState::TURN_OFF)) {
    BLUETOOTH_LOG(EVENT) << "Received a request to disable adapter (local)";
    DequeueLocalPowerChange(AdapterPowerState::TURN_OFF);
  } else {
    BLUETOOTH_LOG(EVENT) << "Received a request to disable adapter (remote)";
    // Silently ignore any request to turn off Bluetooth from Android.
    // Android will still receive the success callback.
    // (https://crbug.com/40580051)
  }

  OnPoweredOff(std::move(callback), false /* save_user_pref */);
}

void ArcBluetoothBridge::GetAdapterProperty(mojom::BluetoothPropertyType type) {
  DCHECK(bluetooth_adapter_);
  auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
      arc_bridge_service_->bluetooth(), OnAdapterProperties);
  if (!bluetooth_instance)
    return;

  std::vector<mojom::BluetoothPropertyPtr> properties =
      GetAdapterProperties(type);

  bluetooth_instance->OnAdapterProperties(mojom::BluetoothStatus::SUCCESS,
                                          std::move(properties));
}

void ArcBluetoothBridge::OnSetDiscoverable(bool discoverable,
                                           bool success,
                                           uint32_t timeout) {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);

  if (success && discoverable && timeout > 0) {
    discoverable_off_timer_.Start(
        FROM_HERE, base::Seconds(timeout),
        base::BindOnce(&ArcBluetoothBridge::SetDiscoverable,
                       weak_factory_.GetWeakPtr(), false, 0));
  }

  auto status =
      success ? mojom::BluetoothStatus::SUCCESS : mojom::BluetoothStatus::FAIL;
  OnSetAdapterProperty(status, GetDiscoveryTimeoutProperty(timeout));
}

// Set discoverable state to on / off.
// In case of turning on, start timer to turn it back off in |timeout| seconds.
void ArcBluetoothBridge::SetDiscoverable(bool discoverable, uint32_t timeout) {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  DCHECK(bluetooth_adapter_);

  bool currently_discoverable = bluetooth_adapter_->IsDiscoverable();

  if (!discoverable && !currently_discoverable)
    return;

  if (discoverable && currently_discoverable) {
    if (base::Seconds(timeout) > discoverable_off_timer_.GetCurrentDelay()) {
      // Restart discoverable_off_timer_ if new timeout is greater
      OnSetDiscoverable(true, true, timeout);
    } else {
      // Just send message to Android if new timeout is lower.
      OnSetAdapterProperty(mojom::BluetoothStatus::SUCCESS,
                           GetDiscoveryTimeoutProperty(timeout));
    }
    return;
  }

  bluetooth_adapter_->SetDiscoverable(
      discoverable,
      base::BindOnce(&ArcBluetoothBridge::OnSetDiscoverable,
                     weak_factory_.GetWeakPtr(), discoverable, true, timeout),
      base::BindOnce(&ArcBluetoothBridge::OnSetDiscoverable,
                     weak_factory_.GetWeakPtr(), discoverable, false, timeout));
}

void ArcBluetoothBridge::OnSetAdapterProperty(
    mojom::BluetoothStatus status,
    mojom::BluetoothPropertyPtr property) {
  auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
      arc_bridge_service_->bluetooth(), OnAdapterProperties);
  if (!bluetooth_instance)
    return;

  std::vector<arc::mojom::BluetoothPropertyPtr> properties;
  properties.push_back(std::move(property));

  bluetooth_instance->OnAdapterProperties(status, std::move(properties));
}

void ArcBluetoothBridge::SetAdapterProperty(
    mojom::BluetoothPropertyPtr property) {
  DCHECK(bluetooth_adapter_);

  if (property->is_discovery_timeout()) {
    uint32_t discovery_timeout = property->get_discovery_timeout();
    discoverable_off_timeout_ = std::make_optional(discovery_timeout);
    OnSetAdapterProperty(mojom::BluetoothStatus::SUCCESS, std::move(property));
  } else if (property->is_bdname()) {
    auto property_clone = property.Clone();
    const std::string bdname = property->get_bdname();
    bluetooth_adapter_->SetName(
        bdname,
        base::BindOnce(&ArcBluetoothBridge::OnSetAdapterProperty,
                       weak_factory_.GetWeakPtr(),
                       mojom::BluetoothStatus::SUCCESS, std::move(property)),
        base::BindOnce(&ArcBluetoothBridge::OnSetAdapterProperty,
                       weak_factory_.GetWeakPtr(), mojom::BluetoothStatus::FAIL,
                       std::move(property_clone)));
  } else if (property->is_adapter_scan_mode()) {
    // Only set the BT scan mode to discoverable if requested and Android has
    // set a discovery timeout previously.
    if (property->get_adapter_scan_mode() ==
            mojom::BluetoothScanMode::CONNECTABLE_DISCOVERABLE &&
        discoverable_off_timeout_.has_value()) {
      SetDiscoverable(/*discoverable=*/true, discoverable_off_timeout_.value());
    } else {
      SetDiscoverable(/*discoverable=*/false, /*timeout=*/0);
    }
    OnSetAdapterProperty(mojom::BluetoothStatus::SUCCESS, std::move(property));
  } else {
    // Android does not set any other property type.
    OnSetAdapterProperty(mojom::BluetoothStatus::UNSUPPORTED,
                         std::move(property));
  }
}

void ArcBluetoothBridge::StartDiscovery() {
  discovery_queue_.Push(base::BindOnce(&ArcBluetoothBridge::StartDiscoveryImpl,
                                       weak_factory_.GetWeakPtr()));
}

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

  if (discovery_session_) {
    LOG(ERROR) << "Discovery session already running; Reset timeout.";
    discovery_off_timer_.Start(
        FROM_HERE, kDiscoveryTimeout,
        base::BindOnce(&ArcBluetoothBridge::CancelDiscovery,
                       weak_factory_.GetWeakPtr()));
    discovered_devices_.clear();
    discovery_queue_.Pop();
    return;
  }

  bluetooth_adapter_->StartDiscoverySession(
      kScanClientName,
      base::BindOnce(&ArcBluetoothBridge::OnDiscoveryStarted,
                     weak_factory_.GetWeakPtr()),
      base::BindOnce(&ArcBluetoothBridge::OnDiscoveryError,
                     weak_factory_.GetWeakPtr()));
}

void ArcBluetoothBridge::CancelDiscovery() {
  discovery_queue_.Push(base::BindOnce(&ArcBluetoothBridge::CancelDiscoveryImpl,
                                       weak_factory_.GetWeakPtr()));
}

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

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

  bluetooth_adapter_->StartDiscoverySessionWithFilter(
      std::make_unique<BluetoothDiscoveryFilter>(
          device::BLUETOOTH_TRANSPORT_LE),
      kScanClientName,
      base::BindOnce(&ArcBluetoothBridge::OnLEScanStarted,
                     weak_factory_.GetWeakPtr()),
      base::BindOnce(&ArcBluetoothBridge::OnLEScanError,
                     weak_factory_.GetWeakPtr()));
}

void ArcBluetoothBridge::CancelDiscoveryImpl() {
  discovery_off_timer_.Stop();
  discovery_session_ = nullptr;
  auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
      arc_bridge_service_->bluetooth(), OnDiscoveryStateChanged);
  if (bluetooth_instance != nullptr) {
    bluetooth_instance->OnDiscoveryStateChanged(
        mojom::BluetoothDiscoveryState::STOPPED);
  }
  discovery_queue_.Pop();
}

void ArcBluetoothBridge::StartLEScanOffTimer() {
  // TODO(b/152463320): Android expects to stop the LE scan by itself but not by
  // a timer automatically. We set this timer here due to the potential
  // complains about the power consumption since we cannot set scan parameters
  // and filters now.
  le_scan_off_timer_.Start(
      FROM_HERE, kDiscoveryTimeout,
      base::BindOnce(&ArcBluetoothBridge::StopLEScanByTimer,
                     weak_factory_.GetWeakPtr()));
}

void ArcBluetoothBridge::ResetLEScanSession() {
  le_scan_session_ = nullptr;
}

bool ArcBluetoothBridge::IsDiscoveringOrScanning() {
  return discovery_session_ || le_scan_session_;
}

void ArcBluetoothBridge::StopLEScanImpl() {
  le_scan_off_timer_.Stop();
  ResetLEScanSession();
  discovery_queue_.Pop();
}

void ArcBluetoothBridge::OnPoweredOn(
    ArcBluetoothBridge::AdapterStateCallback callback,
    bool save_user_pref) {
  // Saves the power state to user preference only if Android initiated it.
  if (save_user_pref)
    SetPrimaryUserBluetoothPowerSetting(true);

  std::move(callback).Run(mojom::BluetoothAdapterState::ON);

  // Sends cached devices to Android after its Bluetooth stack is ready. We
  // should do this after the above callback since Android will clear its
  // device cache after receiving the "ON" state of adapter.
  SendCachedDevices();

  // Allow derived classes to perform additional logic if desired.
  HandlePoweredOn();
}

void ArcBluetoothBridge::OnPoweredOff(
    ArcBluetoothBridge::AdapterStateCallback callback,
    bool save_user_pref) {
  // Saves the power state to user preference only if Android initiated it.
  if (save_user_pref)
    SetPrimaryUserBluetoothPowerSetting(false);

  std::move(callback).Run(mojom::BluetoothAdapterState::OFF);
}

void ArcBluetoothBridge::OnPoweredError(
    ArcBluetoothBridge::AdapterStateCallback callback) const {
  LOG(WARNING) << "failed to change power state";

  std::move(callback).Run(bluetooth_adapter_->IsPowered()
                              ? mojom::BluetoothAdapterState::ON
                              : mojom::BluetoothAdapterState::OFF);
}

void ArcBluetoothBridge::OnDiscoveryStarted(
    std::unique_ptr<BluetoothDiscoverySession> session) {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);

  // We need to set timer to turn device discovery off because of the difference
  // between Android API (do device discovery once) and Chrome API (do device
  // discovery until user turns it off).
  discovery_off_timer_.Start(
      FROM_HERE, kDiscoveryTimeout,
      base::BindOnce(&ArcBluetoothBridge::CancelDiscovery,
                     weak_factory_.GetWeakPtr()));
  discovery_session_ = std::move(session);
  discovered_devices_.clear();

  auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
      arc_bridge_service_->bluetooth(), OnDiscoveryStateChanged);
  if (bluetooth_instance != nullptr) {
    bluetooth_instance->OnDiscoveryStateChanged(
        mojom::BluetoothDiscoveryState::STARTED);
  }
  discovery_queue_.Pop();
}

void ArcBluetoothBridge::OnLEScanStarted(
    std::unique_ptr<BluetoothDiscoverySession> session) {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);

  StartLEScanOffTimer();
  le_scan_session_ = std::move(session);
  scanned_devices_.clear();

  // Android doesn't need a callback for discovery started event for a LE scan.
  discovery_queue_.Pop();
}

void ArcBluetoothBridge::CreateBond(mojom::BluetoothAddressPtr addr,
                                    int32_t transport) {
  std::string addr_str = addr->To<std::string>();
  BluetoothDevice* device = bluetooth_adapter_->GetDevice(addr_str);
  if (!device || !device->IsPairable()) {
    VLOG(1) << __func__ << ": device " << addr_str
            << " is no longer valid or pairable";
    OnPairedError(std::move(addr), BluetoothDevice::ERROR_FAILED);
    return;
  }

  if (device->IsPaired()) {
    OnPairedDone(std::move(addr));
    return;
  }

  devices_paired_by_arc_.insert(addr_str);

  // BluetoothPairingDialog will automatically pair the device and handle all
  // the incoming pairing requests.
  ash::BluetoothPairingDialog::ShowDialog(device->GetAddress());
}

void ArcBluetoothBridge::RemoveBond(mojom::BluetoothAddressPtr addr) {
  // Forget the device if it is no longer valid or not even paired.
  BluetoothDevice* device =
      bluetooth_adapter_->GetDevice(addr->To<std::string>());
  if (!device || !device->IsPaired()) {
    OnForgetDone(std::move(addr));
    return;
  }

  // If unpairing finished successfully, DevicePairedChanged will notify Android
  // on paired state change event, so DoNothing is passed as a success callback.
  device->Forget(base::DoNothing(),
                 base::BindOnce(&ArcBluetoothBridge::OnForgetError,
                                weak_factory_.GetWeakPtr(), std::move(addr)));
}

void ArcBluetoothBridge::CancelBond(mojom::BluetoothAddressPtr addr) {
  BluetoothDevice* device =
      bluetooth_adapter_->GetDevice(addr->To<std::string>());
  if (!device) {
    OnForgetDone(std::move(addr));
    return;
  }

  device->CancelPairing();
  OnForgetDone(std::move(addr));
}

void ArcBluetoothBridge::GetConnectionState(
    mojom::BluetoothAddressPtr addr,
    GetConnectionStateCallback callback) {
  if (!bluetooth_adapter_) {
    std::move(callback).Run(false);
    return;
  }

  BluetoothDevice* device =
      bluetooth_adapter_->GetDevice(addr->To<std::string>());
  if (!device) {
    std::move(callback).Run(false);
    return;
  }

  std::move(callback).Run(device->IsConnected());
}

void ArcBluetoothBridge::StartLEScan() {
  discovery_queue_.Push(base::BindOnce(&ArcBluetoothBridge::StartLEScanImpl,
                                       weak_factory_.GetWeakPtr()));
}

void ArcBluetoothBridge::StopLEScan() {
  discovery_queue_.Push(base::BindOnce(&ArcBluetoothBridge::StopLEScanImpl,
                                       weak_factory_.GetWeakPtr()));
}

void ArcBluetoothBridge::StopLEScanByTimer() {
  // If the scan is stopped by the timer, it is possible that the following scan
  // client in Android cannot start the scan successfully but that client will
  // not get an error.
  LOG(WARNING) << "The discovery session for LE scan is stopped by the timer";
  discovery_queue_.Push(base::BindOnce(&ArcBluetoothBridge::StopLEScanImpl,
                                       weak_factory_.GetWeakPtr()));
}

void ArcBluetoothBridge::OnGattConnectStateChanged(
    mojom::BluetoothAddressPtr addr,
    bool connected) const {
  auto* btle_instance = ARC_GET_INSTANCE_FOR_METHOD(
      arc_bridge_service_->bluetooth(), OnLEConnectionStateChange);
  if (!btle_instance)
    return;

  DCHECK(addr);

  btle_instance->OnLEConnectionStateChange(std::move(addr), connected);
}

void ArcBluetoothBridge::OnGattConnect(
    mojom::BluetoothAddressPtr addr,
    std::unique_ptr<BluetoothGattConnection> connection,
    std::optional<BluetoothDevice::ConnectErrorCode> error_code) {
  if (error_code.has_value()) {
    LOG(WARNING) << "GattConnectError: error_code = " << error_code.value();
    OnGattDisconnected(std::move(addr));
    return;
  }
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  const std::string addr_str = addr->To<std::string>();
  GattConnection& conn = gatt_connections_[addr_str];
  conn.state = GattConnection::ConnectionState::CONNECTED;
  conn.connection = std::move(connection);
  devices_paired_by_arc_.erase(addr_str);
  OnGattConnectStateChanged(std::move(addr), true);
}

void ArcBluetoothBridge::OnGattDisconnected(mojom::BluetoothAddressPtr addr) {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  if (gatt_connections_.erase(addr->To<std::string>()) == 0) {
    LOG(WARNING) << "OnGattDisconnected called, "
                 << "but no gatt connection was found";
    return;
  }
  OnGattConnectStateChanged(std::move(addr), false);
}

void ArcBluetoothBridge::ConnectLEDevice(
    mojom::BluetoothAddressPtr remote_addr) {
  auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
      arc_bridge_service_->bluetooth(), OnLEConnectionStateChange);
  if (!bluetooth_instance)
    return;

  std::string addr = remote_addr->To<std::string>();
  BluetoothDevice* device = bluetooth_adapter_->GetDevice(addr);

  if (!device) {
    LOG(ERROR) << "Unknown device " << addr;
    OnGattConnect(std::move(remote_addr),
                  /*connection=*/nullptr,
                  BluetoothDevice::ConnectErrorCode::ERROR_FAILED);
    return;
  }

  auto it = gatt_connections_.find(addr);
  if (it != gatt_connections_.end()) {
    if (it->second.state == GattConnection::ConnectionState::CONNECTED) {
      bluetooth_instance->OnLEConnectionStateChange(std::move(remote_addr),
                                                    true);
    } else {
      OnGattConnect(std::move(remote_addr),
                    /*connection=*/nullptr,
                    BluetoothDevice::ConnectErrorCode::ERROR_INPROGRESS);
    }
    return;
  }

  bool need_hard_disconnect =
      devices_paired_by_arc_.find(addr) != devices_paired_by_arc_.end() ||
      !device->IsConnected();

  // Also pass disconnect callback in error case since it would be disconnected
  // anyway.
  gatt_connections_.emplace(
      addr, GattConnection(GattConnection::ConnectionState::CONNECTING, nullptr,
                           need_hard_disconnect));
  device->CreateGattConnection(
      base::BindOnce(&ArcBluetoothBridge::OnGattConnect,
                     weak_factory_.GetWeakPtr(), std::move(remote_addr)));
}

void ArcBluetoothBridge::DisconnectLEDevice(
    mojom::BluetoothAddressPtr remote_addr) {
  auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
      arc_bridge_service_->bluetooth(), OnLEConnectionStateChange);
  if (!bluetooth_instance)
    return;

  const std::string addr_str = remote_addr->To<std::string>();

  BluetoothDevice* device = bluetooth_adapter_->GetDevice(addr_str);
  const auto conn_itr = gatt_connections_.find(remote_addr->To<std::string>());

  if (!device || !device->IsConnected() ||
      conn_itr == gatt_connections_.end()) {
    bluetooth_instance->OnLEConnectionStateChange(std::move(remote_addr),
                                                  false);
    return;
  }

  if (conn_itr->second.need_hard_disconnect)
    device->Disconnect(base::DoNothing(), base::DoNothing());

  // Removes the connection object held by us and notifies Android.
  OnGattDisconnected(std::move(remote_addr));
}

void ArcBluetoothBridge::SearchService(mojom::BluetoothAddressPtr remote_addr) {
  auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
      arc_bridge_service_->bluetooth(), OnSearchComplete);
  if (!bluetooth_instance)
    return;

  BluetoothDevice* device =
      bluetooth_adapter_->GetDevice(remote_addr->To<std::string>());
  if (!device) {
    LOG(ERROR) << "Unknown device " << remote_addr->To<std::string>();
    bluetooth_instance->OnSearchComplete(
        std::move(remote_addr), mojom::BluetoothGattStatus::GATT_FAILURE);
    return;
  }

  // Call the callback if discovery is completed
  if (device->IsGattServicesDiscoveryComplete()) {
    bluetooth_instance->OnSearchComplete(
        std::move(remote_addr), mojom::BluetoothGattStatus::GATT_SUCCESS);
    return;
  }

  // Discard result. Will call the callback when discovery is completed.
  device->GetGattServices();
}

void ArcBluetoothBridge::GetGattDB(mojom::BluetoothAddressPtr remote_addr) {
  auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
      arc_bridge_service_->bluetooth(), OnGetGattDB);
  if (!bluetooth_instance)
    return;

  BluetoothDevice* device =
      bluetooth_adapter_->GetDevice(remote_addr->To<std::string>());
  std::vector<mojom::BluetoothGattDBElementPtr> db;

  if (!device) {
    LOG(ERROR) << "Unknown device " << remote_addr->To<std::string>();
    bluetooth_instance->OnGetGattDB(std::move(remote_addr), std::move(db));
    return;
  }

  for (auto* service : device->GetGattServices()) {
    mojom::BluetoothGattDBElementPtr service_element = CreateGattDBElement(
        service->IsPrimary()
            ? mojom::BluetoothGattDBAttributeType::BTGATT_DB_PRIMARY_SERVICE
            : mojom::BluetoothGattDBAttributeType::BTGATT_DB_SECONDARY_SERVICE,
        service);
    if (!service_element)
      continue;

    const auto& characteristics = service->GetCharacteristics();
    if (characteristics.size() > 0) {
      const auto& descriptors = characteristics.back()->GetDescriptors();
      const std::optional<uint16_t> start_handle =
          ConvertGattIdentifierToId(characteristics.front()->GetIdentifier());
      if (!start_handle)
        continue;

      const std::optional<uint16_t> end_handle = ConvertGattIdentifierToId(
          descriptors.size() > 0 ? descriptors.back()->GetIdentifier()
                                 : characteristics.back()->GetIdentifier());
      if (!end_handle)
        continue;

      service_element->start_handle = *start_handle;
      service_element->end_handle = *end_handle;
    }
    db.push_back(std::move(service_element));

    for (auto* characteristic : characteristics) {
      mojom::BluetoothGattDBElementPtr characteristic_element =
          CreateGattDBElement(
              mojom::BluetoothGattDBAttributeType::BTGATT_DB_CHARACTERISTIC,
              characteristic);
      if (!characteristic_element)
        continue;

      characteristic_element->properties = characteristic->GetProperties();
      db.push_back(std::move(characteristic_element));

      for (auto* descriptor : characteristic->GetDescriptors()) {
        mojom::BluetoothGattDBElementPtr descriptor_element =
            CreateGattDBElement(
                mojom::BluetoothGattDBAttributeType::BTGATT_DB_DESCRIPTOR,
                descriptor);
        if (!descriptor_element)
          continue;

        db.push_back(std::move(descriptor_element));
      }
    }
  }

  bluetooth_instance->OnGetGattDB(std::move(remote_addr), std::move(db));
}

BluetoothRemoteGattCharacteristic* ArcBluetoothBridge::FindGattCharacteristic(
    mojom::BluetoothAddressPtr remote_addr,
    mojom::BluetoothGattServiceIDPtr service_id,
    mojom::BluetoothGattIDPtr char_id) const {
  DCHECK(remote_addr);
  DCHECK(service_id);
  DCHECK(char_id);

  BluetoothDevice* device =
      bluetooth_adapter_->GetDevice(remote_addr->To<std::string>());
  if (!device)
    return nullptr;

  BluetoothRemoteGattService* service =
      FindGattAttributeByUuid(device->GetGattServices(), service_id->id->uuid);
  if (!service)
    return nullptr;

  return FindGattAttributeByUuid(service->GetCharacteristics(), char_id->uuid);
}

BluetoothRemoteGattDescriptor* ArcBluetoothBridge::FindGattDescriptor(
    mojom::BluetoothAddressPtr remote_addr,
    mojom::BluetoothGattServiceIDPtr service_id,
    mojom::BluetoothGattIDPtr char_id,
    mojom::BluetoothGattIDPtr desc_id) const {
  BluetoothRemoteGattCharacteristic* characteristic = FindGattCharacteristic(
      std::move(remote_addr), std::move(service_id), std::move(char_id));
  if (!characteristic)
    return nullptr;

  return FindGattAttributeByUuid(characteristic->GetDescriptors(),
                                 desc_id->uuid);
}

void ArcBluetoothBridge::SendBluetoothPoweredStateBroadcast(
    AdapterPowerState powered) const {
  auto* intent_instance = ARC_GET_INSTANCE_FOR_METHOD(
      arc_bridge_service_->intent_helper(), SendBroadcast);
  if (!intent_instance)
    return;

  base::DictValue extras;
  extras.Set("enable", powered == AdapterPowerState::TURN_ON);
  std::string extras_json;
  bool write_success = base::JSONWriter::Write(extras, &extras_json);
  DCHECK(write_success);

  BLUETOOTH_LOG(EVENT) << "Sending Android intent to set power: "
                       << (powered == AdapterPowerState::TURN_ON);
  intent_instance->SendBroadcast(
      ArcIntentHelperBridge::AppendStringToIntentHelperPackageName(
          "SET_BLUETOOTH_STATE"),
      kArcIntentHelperPackageName,
      ArcIntentHelperBridge::AppendStringToIntentHelperPackageName(
          "SettingsReceiver"),
      extras_json);
}

void ArcBluetoothBridge::ReadGattCharacteristic(
    mojom::BluetoothAddressPtr remote_addr,
    mojom::BluetoothGattServiceIDPtr service_id,
    mojom::BluetoothGattIDPtr char_id,
    ReadGattCharacteristicCallback callback) {
  BluetoothRemoteGattCharacteristic* characteristic = FindGattCharacteristic(
      std::move(remote_addr), std::move(service_id), std::move(char_id));
  if (!characteristic) {
    // TODO(b/201737474): Investigate in what case this could happen.
    LOG(ERROR) << "Requested GATT characteristic does not exist";
    OnGattRead(std::move(callback),
               device::BluetoothGattService::GattErrorCode::kFailed,
               /*result=*/{});
    return;
  }

  characteristic->ReadRemoteCharacteristic(
      base::BindOnce(&OnGattRead, std::move(callback)));
}

void ArcBluetoothBridge::WriteGattCharacteristic(
    mojom::BluetoothAddressPtr remote_addr,
    mojom::BluetoothGattServiceIDPtr service_id,
    mojom::BluetoothGattIDPtr char_id,
    mojom::BluetoothGattValuePtr value,
    bool prepare,
    WriteGattCharacteristicCallback callback) {
  BluetoothRemoteGattCharacteristic* characteristic = FindGattCharacteristic(
      std::move(remote_addr), std::move(service_id), std::move(char_id));
  if (!characteristic) {
    BLUETOOTH_LOG(ERROR) << __func__
                         << " failed to write GATT characteristic, "
                            "characteristic does not exist.";
    std::move(callback).Run(mojom::BluetoothGattStatus::GATT_FAILURE);
    return;
  }

  auto split_callback = base::SplitOnceCallback(std::move(callback));
  if (prepare) {
    characteristic->PrepareWriteRemoteCharacteristic(
        value->value,
        base::BindOnce(&OnGattOperationDone, std::move(split_callback.first)),
        base::BindOnce(&OnGattOperationError,
                       std::move(split_callback.second)));
  } else {
    characteristic->DeprecatedWriteRemoteCharacteristic(
        value->value,
        base::BindOnce(&OnGattOperationDone, std::move(split_callback.first)),
        base::BindOnce(&OnGattOperationError,
                       std::move(split_callback.second)));
  }
}

void ArcBluetoothBridge::ReadGattDescriptor(
    mojom::BluetoothAddressPtr remote_addr,
    mojom::BluetoothGattServiceIDPtr service_id,
    mojom::BluetoothGattIDPtr char_id,
    mojom::BluetoothGattIDPtr desc_id,
    ReadGattDescriptorCallback callback) {
  BluetoothRemoteGattDescriptor* descriptor =
      FindGattDescriptor(std::move(remote_addr), std::move(service_id),
                         std::move(char_id), std::move(desc_id));

  if (!descriptor) {
    BLUETOOTH_LOG(ERROR)
        << __func__
        << " failed to read GATT descriptor, descriptor does not exist.";
    arc::mojom::BluetoothGattValuePtr gatt_value =
        mojom::BluetoothGattValue::New();
    gatt_value->status = mojom::BluetoothGattStatus::GATT_FAILURE;
    std::move(callback).Run(std::move(gatt_value));
    return;
  }

  descriptor->ReadRemoteDescriptor(
      base::BindOnce(&OnGattRead, std::move(callback)));
}

void ArcBluetoothBridge::WriteGattDescriptor(
    mojom::BluetoothAddressPtr remote_addr,
    mojom::BluetoothGattServiceIDPtr service_id,
    mojom::BluetoothGattIDPtr char_id,
    mojom::BluetoothGattIDPtr desc_id,
    mojom::BluetoothGattValuePtr value,
    WriteGattDescriptorCallback callback) {
  BluetoothRemoteGattDescriptor* descriptor =
      FindGattDescriptor(std::move(remote_addr), std::move(service_id),
                         std::move(char_id), std::move(desc_id));

  if (!descriptor) {
    BLUETOOTH_LOG(ERROR)
        << __func__
        << " failed to write GATT descriptor, descriptor does not exist.";
    std::move(callback).Run(mojom::BluetoothGattStatus::GATT_FAILURE);
    return;
  }

  if (value->value.empty()) {
    std::move(callback).Run(mojom::BluetoothGattStatus::GATT_FAILURE);
    return;
  }

  if (descriptor->GetUUID() !=
      BluetoothGattDescriptor::ClientCharacteristicConfigurationUuid()) {
    auto split_callback = base::SplitOnceCallback(std::move(callback));
    descriptor->WriteRemoteDescriptor(
        value->value,
        base::BindOnce(&OnGattOperationDone, std::move(split_callback.first)),
        base::BindOnce(&OnGattOperationError,
                       std::move(split_callback.second)));
    return;
  }

  BluetoothRemoteGattCharacteristic* characteristic =
      descriptor->GetCharacteristic();
  std::string char_id_str = characteristic->GetIdentifier();
  auto it = notification_session_.find(char_id_str);
  if (it != notification_session_.end()) {
    // Stop the previous session while keeping this client registered.
    it->second.reset();
  }
  // If notification session does not exist, a write to gatt descriptor will
  // only enable peer device to notify us, but our clients won't hear the change
  // events. Though this use case is not typical, we should still allow the API
  // to succeed since the public API doesn't disallow this
  // (https://developer.android.com/reference/android/bluetooth/BluetoothGatt#writeDescriptor(android.bluetooth.BluetoothGattDescriptor)).

  switch (value->value[0]) {
    case DISABLE_NOTIFICATION_VALUE:
      std::move(callback).Run(mojom::BluetoothGattStatus::GATT_SUCCESS);
      return;
    case ENABLE_NOTIFICATION_VALUE: {
      auto split_callback = base::SplitOnceCallback(std::move(callback));
      characteristic->StartNotifySession(
          device::BluetoothGattCharacteristic::NotificationType::kNotification,
          base::BindOnce(&ArcBluetoothBridge::OnGattNotifyStartDone,
                         weak_factory_.GetWeakPtr(),
                         std::move(split_callback.first),
                         std::move(char_id_str)),
          base::BindOnce(&OnGattOperationError,
                         std::move(split_callback.second)));
      return;
    }
    case ENABLE_INDICATION_VALUE: {
      auto split_callback = base::SplitOnceCallback(std::move(callback));
      characteristic->StartNotifySession(
          device::BluetoothGattCharacteristic::NotificationType::kIndication,
          base::BindOnce(&ArcBluetoothBridge::OnGattNotifyStartDone,
                         weak_factory_.GetWeakPtr(),
                         std::move(split_callback.first),
                         std::move(char_id_str)),
          base::BindOnce(&OnGattOperationError,
                         std::move(split_callback.second)));
      return;
    }
    default:
      std::move(callback).Run(mojom::BluetoothGattStatus::GATT_FAILURE);
  }
}

void ArcBluetoothBridge::ExecuteWrite(mojom::BluetoothAddressPtr remote_addr,
                                      bool execute,
                                      ExecuteWriteCallback callback) {
  device::BluetoothDevice* device =
      bluetooth_adapter_->GetDevice(remote_addr->To<std::string>());

  if (device == nullptr) {
    std::move(callback).Run(mojom::BluetoothGattStatus::GATT_FAILURE);
    return;
  }

  auto split_callback = base::SplitOnceCallback(std::move(callback));
  if (execute) {
    device->ExecuteWrite(
        base::BindOnce(&OnGattOperationDone, std::move(split_callback.first)),
        base::BindOnce(&OnGattOperationError,
                       std::move(split_callback.second)));
  } else {
    device->AbortWrite(
        base::BindOnce(&OnGattOperationDone, std::move(split_callback.first)),
        base::BindOnce(&OnGattOperationError,
                       std::move(split_callback.second)));
  }
}

void ArcBluetoothBridge::OnGattNotifyStartDone(
    ArcBluetoothBridge::GattStatusCallback callback,
    std::string char_string_id,
    std::unique_ptr<BluetoothGattNotifySession> notify_session) {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  // Hold on to |notify_session|. Destruction of |notify_session| is equivalent
  // to stopping this session.
  notification_session_[char_string_id] = std::move(notify_session);
  std::move(callback).Run(mojom::BluetoothGattStatus::GATT_SUCCESS);
}

void ArcBluetoothBridge::RegisterForGattNotification(
    mojom::BluetoothAddressPtr remote_addr,
    mojom::BluetoothGattServiceIDPtr service_id,
    mojom::BluetoothGattIDPtr char_id,
    RegisterForGattNotificationCallback callback) {
  BluetoothRemoteGattCharacteristic* characteristic = FindGattCharacteristic(
      std::move(remote_addr), std::move(service_id), std::move(char_id));
  if (characteristic == nullptr) {
    LOG(WARNING) << __func__ << " Characteristic is not existed.";
    std::move(callback).Run(mojom::BluetoothGattStatus::GATT_FAILURE);
    return;
  }

  std::string char_id_str = characteristic->GetIdentifier();
  // Create an empty notification session object - which will be fully
  // instantiated by a future call to WriteGattDescriptor - if one does not yet
  // exist. Note that the notification session object may have been created as
  // a result of a previous call to WriteGattDescriptor, which can be left
  // as-is.
  if (!notification_session_.contains(char_id_str)) {
    notification_session_.emplace(char_id_str, nullptr);
  }

  std::move(callback).Run(mojom::BluetoothGattStatus::GATT_SUCCESS);
}

void ArcBluetoothBridge::DeregisterForGattNotification(
    mojom::BluetoothAddressPtr remote_addr,
    mojom::BluetoothGattServiceIDPtr service_id,
    mojom::BluetoothGattIDPtr char_id,
    DeregisterForGattNotificationCallback callback) {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);

  BluetoothRemoteGattCharacteristic* characteristic = FindGattCharacteristic(
      std::move(remote_addr), std::move(service_id), std::move(char_id));
  if (characteristic == nullptr) {
    LOG(WARNING) << __func__ << " Characteristic is not existed.";
    std::move(callback).Run(mojom::BluetoothGattStatus::GATT_FAILURE);
    return;
  }

  std::string char_id_str = characteristic->GetIdentifier();
  auto it = notification_session_.find(char_id_str);
  if (it == notification_session_.end()) {
    LOG(WARNING) << "Notification session not found " << char_id_str;
    std::move(callback).Run(mojom::BluetoothGattStatus::GATT_FAILURE);
    return;
  }
  notification_session_.erase(it);
  std::move(callback).Run(mojom::BluetoothGattStatus::GATT_SUCCESS);
}

void ArcBluetoothBridge::ReadRemoteRssi(mojom::BluetoothAddressPtr remote_addr,
                                        ReadRemoteRssiCallback callback) {
  BluetoothDevice* device =
      bluetooth_adapter_->GetDevice(remote_addr->To<std::string>());
  if (!device) {
    std::move(callback).Run(mojom::kUnknownPower);
    return;
  }

  if (device->IsConnected()) {
    device->GetConnectionInfo(
        base::BindOnce(&SendRssiOnGetConnectionInfoDone, std::move(callback)));
  } else {
    std::move(callback).Run(
        device->GetInquiryRSSI().value_or(mojom::kUnknownPower));
  }
}

bool ArcBluetoothBridge::IsGattServerAttributeHandleAvailable(int need) {
  return gatt_server_attribute_next_handle_ + need <= kMaxGattAttributeHandle;
}

int32_t ArcBluetoothBridge::GetNextGattServerAttributeHandle() {
  return IsGattServerAttributeHandleAvailable(1)
             ? ++gatt_server_attribute_next_handle_
             : kInvalidGattAttributeHandle;
}

template <class LocalGattAttribute>
int32_t ArcBluetoothBridge::CreateGattAttributeHandle(
    LocalGattAttribute* attribute) {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  if (!attribute)
    return kInvalidGattAttributeHandle;
  int32_t handle = GetNextGattServerAttributeHandle();
  if (handle == kInvalidGattAttributeHandle)
    return kInvalidGattAttributeHandle;
  const std::string& identifier = attribute->GetIdentifier();
  gatt_identifier_[handle] = identifier;
  gatt_handle_[identifier] = handle;
  return handle;
}

void ArcBluetoothBridge::AddService(mojom::BluetoothGattServiceIDPtr service_id,
                                    int32_t num_handles,
                                    AddServiceCallback callback) {
  if (!IsGattServerAttributeHandleAvailable(num_handles)) {
    std::move(callback).Run(kInvalidGattAttributeHandle);
    return;
  }

  base::WeakPtr<BluetoothLocalGattService> service =
      bluetooth_adapter_->CreateLocalGattService(
          service_id->id->uuid, service_id->is_primary, this /* delegate */);
  std::move(callback).Run(CreateGattAttributeHandle(service.get()));
}

void ArcBluetoothBridge::AddCharacteristic(int32_t service_handle,
                                           const BluetoothUUID& uuid,
                                           int32_t properties,
                                           int32_t permissions,
                                           AddCharacteristicCallback callback) {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);

  auto gatt_id = gatt_identifier_.find(service_handle);
  DCHECK(gatt_id != gatt_identifier_.end());

  if (!IsGattServerAttributeHandleAvailable(1)) {
    std::move(callback).Run(kInvalidGattAttributeHandle);
    return;
  }

  const auto& [bluez_properties, bluez_permissions] =
      floss::BluetoothGattCharacteristicFloss::ConvertPropsAndPermsFromFloss(
          static_cast<uint8_t>(properties), static_cast<uint16_t>(permissions));

  auto* service = bluetooth_adapter_->GetGattService(gatt_id->second);
  if (!service) {
    return;
  }

  base::WeakPtr<BluetoothLocalGattCharacteristic> characteristic =
      service->CreateCharacteristic(uuid, bluez_properties, bluez_permissions);
  int32_t characteristic_handle =
      CreateGattAttributeHandle(characteristic.get());
  last_characteristic_[service_handle] = characteristic_handle;
  std::move(callback).Run(characteristic_handle);
}

void ArcBluetoothBridge::AddDescriptor(int32_t service_handle,
                                       const BluetoothUUID& uuid,
                                       int32_t permissions,
                                       AddDescriptorCallback callback) {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  if (!IsGattServerAttributeHandleAvailable(1)) {
    std::move(callback).Run(kInvalidGattAttributeHandle);
    return;
  }

  DCHECK(gatt_identifier_.find(service_handle) != gatt_identifier_.end());
  BluetoothLocalGattService* service =
      bluetooth_adapter_->GetGattService(gatt_identifier_[service_handle]);
  DCHECK(service);
  // Since the Android API does not give information about which characteristic
  // is the parent of the new descriptor, we assume that it would be the last
  // characteristic that was added to the given service. This matches the
  // Android framework code at android/bluetooth/BluetoothGattServer.java#594.
  // https://android.googlesource.com/platform/frameworks/base/+/android-6.0.1_r55/core/java/android/bluetooth/BluetoothGattServer.java#586
  DCHECK(last_characteristic_.find(service_handle) !=
         last_characteristic_.end());
  int32_t last_characteristic_handle = last_characteristic_[service_handle];

  DCHECK(gatt_identifier_.find(last_characteristic_handle) !=
         gatt_identifier_.end());
  BluetoothLocalGattCharacteristic* characteristic =
      service->GetCharacteristic(gatt_identifier_[last_characteristic_handle]);
  DCHECK(characteristic);

  const auto& [unused, bluez_permissions] =
      floss::BluetoothGattCharacteristicFloss::ConvertPropsAndPermsFromFloss(
          /*properties=*/0, static_cast<uint16_t>(permissions));

  base::WeakPtr<BluetoothLocalGattDescriptor> descriptor =
      BluetoothLocalGattDescriptor::Create(uuid, bluez_permissions,
                                           characteristic);
  std::move(callback).Run(CreateGattAttributeHandle(descriptor.get()));
}

void ArcBluetoothBridge::StartService(int32_t service_handle,
                                      StartServiceCallback callback) {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  DCHECK(gatt_identifier_.find(service_handle) != gatt_identifier_.end());
  BluetoothLocalGattService* service =
      bluetooth_adapter_->GetGattService(gatt_identifier_[service_handle]);
  DCHECK(service);

  auto split_callback = base::SplitOnceCallback(std::move(callback));
  service->Register(
      base::BindOnce(&OnGattOperationDone, std::move(split_callback.first)),
      base::BindOnce(&OnGattOperationError, std::move(split_callback.second)));
}

void ArcBluetoothBridge::StopService(int32_t service_handle,
                                     StopServiceCallback callback) {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  DCHECK(gatt_identifier_.find(service_handle) != gatt_identifier_.end());
  BluetoothLocalGattService* service =
      bluetooth_adapter_->GetGattService(gatt_identifier_[service_handle]);
  DCHECK(service);

  auto split_callback = base::SplitOnceCallback(std::move(callback));
  service->Unregister(
      base::BindOnce(&OnGattOperationDone, std::move(split_callback.first)),
      base::BindOnce(&OnGattOperationError, std::move(split_callback.second)));
}

void ArcBluetoothBridge::DeleteService(int32_t service_handle,
                                       DeleteServiceCallback callback) {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  auto itr = gatt_identifier_.find(service_handle);
  if (itr == gatt_identifier_.end()) {
    LOG(WARNING) << "DeleteService called with invalid service handle.";
    return;
  }

  BluetoothLocalGattService* service =
      bluetooth_adapter_->GetGattService(itr->second);
  DCHECK(service);
  gatt_identifier_.erase(itr);
  gatt_handle_.erase(service->GetIdentifier());
  service->Delete();
  OnGattOperationDone(std::move(callback));
}

void ArcBluetoothBridge::SendIndication(int32_t attribute_handle,
                                        mojom::BluetoothAddressPtr address,
                                        bool confirm,
                                        const std::vector<uint8_t>& value,
                                        SendIndicationCallback callback) {
  BluetoothDevice* device =
      bluetooth_adapter_->GetDevice(address->To<std::string>());
  auto identifier = gatt_identifier_.find(attribute_handle);
  if (device == nullptr || identifier == gatt_identifier_.end()) {
    std::move(callback).Run(mojom::BluetoothGattStatus::GATT_FAILURE);
    return;
  }

  for (const auto& service_handle : last_characteristic_) {
    auto it = gatt_identifier_.find(service_handle.first);
    if (it == gatt_identifier_.end())
      continue;
    BluetoothLocalGattService* service =
        bluetooth_adapter_->GetGattService(it->second);
    if (service == nullptr)
      continue;
    BluetoothLocalGattCharacteristic* characteristic =
        service->GetCharacteristic(identifier->second);
    if (characteristic == nullptr)
      continue;
    characteristic->NotifyValueChanged(device, value, confirm);
    std::move(callback).Run(mojom::BluetoothGattStatus::GATT_SUCCESS);
    return;
  }

  std::move(callback).Run(mojom::BluetoothGattStatus::GATT_FAILURE);
}

bool ArcBluetoothBridge::GetAdvertisementHandle(int32_t* adv_handle) {
  for (int i = 0; i < kMaxAdvertisements; i++) {
    if (advertisements_.find(i) == advertisements_.end()) {
      *adv_handle = i;
      return true;
    }
  }
  return false;
}

void ArcBluetoothBridge::ReserveAdvertisementHandle(
    ReserveAdvertisementHandleCallback callback) {
  advertisement_queue_.Push(
      base::BindOnce(&ArcBluetoothBridge::ReserveAdvertisementHandleImpl,
                     weak_factory_.GetWeakPtr(), std::move(callback)));
}

void ArcBluetoothBridge::ReserveAdvertisementHandleImpl(
    ReserveAdvertisementHandleCallback callback) {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);

  // Find an empty advertisement slot.
  int32_t adv_handle;
  if (!GetAdvertisementHandle(&adv_handle)) {
    LOG(WARNING) << "Out of space for advertisement data";
    std::move(callback).Run(mojom::BluetoothGattStatus::GATT_FAILURE,
                            kInvalidAdvertisementHandle);
    advertisement_queue_.Pop();
    return;
  }

  // We have a handle. Put an entry in the map to reserve it.
  advertisements_[adv_handle] = nullptr;

  // The advertisement will be registered when we get the call
  // to SetAdvertisingData. For now, just return the adv_handle.
  std::move(callback).Run(mojom::BluetoothGattStatus::GATT_SUCCESS, adv_handle);
  advertisement_queue_.Pop();
}

void ArcBluetoothBridge::EnableAdvertisement(
    int32_t adv_handle,
    std::unique_ptr<device::BluetoothAdvertisement::Data> advertisement,
    EnableAdvertisementCallback callback) {
  advertisement_queue_.Push(base::BindOnce(
      &ArcBluetoothBridge::EnableAdvertisementImpl, weak_factory_.GetWeakPtr(),
      adv_handle, std::move(advertisement), std::move(callback)));
}

void ArcBluetoothBridge::EnableAdvertisementImpl(
    int32_t adv_handle,
    std::unique_ptr<device::BluetoothAdvertisement::Data> advertisement,
    EnableAdvertisementCallback callback) {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);

  auto split_callback = base::SplitOnceCallback(std::move(callback));
  base::OnceClosure done_callback = base::BindOnce(
      &ArcBluetoothBridge::OnReadyToRegisterAdvertisement,
      weak_factory_.GetWeakPtr(), std::move(split_callback.first), adv_handle,
      std::move(advertisement));
  base::OnceCallback<void(BluetoothAdvertisement::ErrorCode)> error_callback =
      base::BindOnce(&ArcBluetoothBridge::OnRegisterAdvertisementError,
                     weak_factory_.GetWeakPtr(),
                     std::move(split_callback.second), adv_handle);

  auto it = advertisements_.find(adv_handle);
  if (it == advertisements_.end()) {
    std::move(error_callback)
        .Run(BluetoothAdvertisement::ErrorCode::
                 ERROR_ADVERTISEMENT_DOES_NOT_EXIST);
    return;
  }
  if (it->second == nullptr) {
    std::move(done_callback).Run();
    return;
  }
  it->second->Unregister(std::move(done_callback), std::move(error_callback));
}

void ArcBluetoothBridge::DisableAdvertisement(
    int32_t adv_handle,
    EnableAdvertisementCallback callback) {
  advertisement_queue_.Push(base::BindOnce(
      &ArcBluetoothBridge::DisableAdvertisementImpl, weak_factory_.GetWeakPtr(),
      adv_handle, std::move(callback)));
}

void ArcBluetoothBridge::DisableAdvertisementImpl(
    int32_t adv_handle,
    EnableAdvertisementCallback callback) {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);

  auto split_callback = base::SplitOnceCallback(std::move(callback));
  base::OnceClosure done_callback = base::BindOnce(
      &ArcBluetoothBridge::OnUnregisterAdvertisementDone,
      weak_factory_.GetWeakPtr(), std::move(split_callback.first), adv_handle);
  base::OnceCallback<void(BluetoothAdvertisement::ErrorCode)> error_callback =
      base::BindOnce(&ArcBluetoothBridge::OnUnregisterAdvertisementError,
                     weak_factory_.GetWeakPtr(),
                     std::move(split_callback.second), adv_handle);

  auto it = advertisements_.find(adv_handle);
  if (it == advertisements_.end()) {
    std::move(error_callback)
        .Run(BluetoothAdvertisement::ErrorCode::
                 ERROR_ADVERTISEMENT_DOES_NOT_EXIST);
    return;
  }
  if (it->second == nullptr) {
    std::move(done_callback).Run();
    return;
  }
  it->second->Unregister(std::move(done_callback), std::move(error_callback));
}

void ArcBluetoothBridge::ReleaseAdvertisementHandle(
    int32_t adv_handle,
    ReleaseAdvertisementHandleCallback callback) {
  advertisement_queue_.Push(base::BindOnce(
      &ArcBluetoothBridge::ReleaseAdvertisementHandleImpl,
      weak_factory_.GetWeakPtr(), adv_handle, std::move(callback)));
}

void ArcBluetoothBridge::ReleaseAdvertisementHandleImpl(
    int32_t adv_handle,
    ReleaseAdvertisementHandleCallback callback) {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  if (advertisements_.find(adv_handle) == advertisements_.end()) {
    std::move(callback).Run(mojom::BluetoothGattStatus::GATT_FAILURE);
    advertisement_queue_.Pop();
    return;
  }

  if (!advertisements_[adv_handle]) {
    advertisements_.erase(adv_handle);
    std::move(callback).Run(mojom::BluetoothGattStatus::GATT_SUCCESS);
    advertisement_queue_.Pop();
    return;
  }

  auto split_callback = base::SplitOnceCallback(std::move(callback));
  advertisements_[adv_handle]->Unregister(
      base::BindOnce(&ArcBluetoothBridge::OnReleaseAdvertisementHandleDone,
                     weak_factory_.GetWeakPtr(),
                     std::move(split_callback.first), adv_handle),
      base::BindOnce(&ArcBluetoothBridge::OnReleaseAdvertisementHandleError,
                     weak_factory_.GetWeakPtr(),
                     std::move(split_callback.second), adv_handle));
}

void ArcBluetoothBridge::OnReadyToRegisterAdvertisement(
    ArcBluetoothBridge::GattStatusCallback callback,
    int32_t adv_handle,
    std::unique_ptr<device::BluetoothAdvertisement::Data> data) {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);

  auto split_callback = base::SplitOnceCallback(std::move(callback));
  bluetooth_adapter_->RegisterAdvertisement(
      std::move(data),
      base::BindOnce(&ArcBluetoothBridge::OnRegisterAdvertisementDone,
                     weak_factory_.GetWeakPtr(),
                     std::move(split_callback.first), adv_handle),
      base::BindOnce(&ArcBluetoothBridge::OnRegisterAdvertisementError,
                     weak_factory_.GetWeakPtr(),
                     std::move(split_callback.second), adv_handle));
}

void ArcBluetoothBridge::OnRegisterAdvertisementDone(
    ArcBluetoothBridge::GattStatusCallback callback,
    int32_t adv_handle,
    scoped_refptr<BluetoothAdvertisement> advertisement) {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  advertisements_[adv_handle] = std::move(advertisement);
  std::move(callback).Run(mojom::BluetoothGattStatus::GATT_SUCCESS);
  advertisement_queue_.Pop();
}

void ArcBluetoothBridge::OnRegisterAdvertisementError(
    ArcBluetoothBridge::GattStatusCallback callback,
    int32_t adv_handle,
    BluetoothAdvertisement::ErrorCode error_code) {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  LOG(WARNING) << "Failed to register advertisement for handle " << adv_handle
               << ", error code = " << error_code;
  advertisements_[adv_handle] = nullptr;
  std::move(callback).Run(mojom::BluetoothGattStatus::GATT_FAILURE);
  advertisement_queue_.Pop();
}

void ArcBluetoothBridge::OnUnregisterAdvertisementDone(
    ArcBluetoothBridge::GattStatusCallback callback,
    int32_t adv_handle) {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  advertisements_[adv_handle] = nullptr;
  std::move(callback).Run(mojom::BluetoothGattStatus::GATT_SUCCESS);
  advertisement_queue_.Pop();
}

void ArcBluetoothBridge::OnUnregisterAdvertisementError(
    ArcBluetoothBridge::GattStatusCallback callback,
    int32_t adv_handle,
    BluetoothAdvertisement::ErrorCode error_code) {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  LOG(WARNING) << "Failed to unregister advertisement for handle " << adv_handle
               << ", error code = " << error_code;
  advertisements_[adv_handle] = nullptr;
  std::move(callback).Run(mojom::BluetoothGattStatus::GATT_FAILURE);
  advertisement_queue_.Pop();
}

void ArcBluetoothBridge::OnReleaseAdvertisementHandleDone(
    ArcBluetoothBridge::GattStatusCallback callback,
    int32_t adv_handle) {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  advertisements_.erase(adv_handle);
  std::move(callback).Run(mojom::BluetoothGattStatus::GATT_SUCCESS);
  advertisement_queue_.Pop();
}

void ArcBluetoothBridge::OnReleaseAdvertisementHandleError(
    ArcBluetoothBridge::GattStatusCallback callback,
    int32_t adv_handle,
    BluetoothAdvertisement::ErrorCode error_code) {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  LOG(WARNING) << "Failed to relase advertisement handle " << adv_handle
               << ", error code = " << error_code;
  advertisements_.erase(adv_handle);
  std::move(callback).Run(mojom::BluetoothGattStatus::GATT_FAILURE);
  advertisement_queue_.Pop();
}

void ArcBluetoothBridge::OnDiscoveryError() {
  LOG(WARNING) << "failed to change discovery state";
  discovery_queue_.Pop();
}

void ArcBluetoothBridge::OnLEScanError() {
  LOG(WARNING) << "failed to start LE scan";
  discovery_queue_.Pop();
}

void ArcBluetoothBridge::OnPairing(mojom::BluetoothAddressPtr addr) const {
  auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
      arc_bridge_service_->bluetooth(), OnBondStateChanged);
  if (!bluetooth_instance)
    return;

  bluetooth_instance->OnBondStateChanged(mojom::BluetoothStatus::SUCCESS,
                                         std::move(addr),
                                         mojom::BluetoothBondState::BONDING);
}

void ArcBluetoothBridge::OnPairedDone(mojom::BluetoothAddressPtr addr) const {
  auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
      arc_bridge_service_->bluetooth(), OnBondStateChanged);
  if (!bluetooth_instance)
    return;

  bluetooth_instance->OnBondStateChanged(mojom::BluetoothStatus::SUCCESS,
                                         std::move(addr),
                                         mojom::BluetoothBondState::BONDED);
}

void ArcBluetoothBridge::OnPairedError(
    mojom::BluetoothAddressPtr addr,
    BluetoothDevice::ConnectErrorCode error_code) const {
  auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
      arc_bridge_service_->bluetooth(), OnBondStateChanged);
  if (!bluetooth_instance)
    return;

  bluetooth_instance->OnBondStateChanged(mojom::BluetoothStatus::FAIL,
                                         std::move(addr),
                                         mojom::BluetoothBondState::NONE);
}

void ArcBluetoothBridge::OnForgetDone(mojom::BluetoothAddressPtr addr) {
  devices_paired_by_arc_.erase(addr->To<std::string>());
  auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
      arc_bridge_service_->bluetooth(), OnBondStateChanged);
  if (!bluetooth_instance)
    return;

  bluetooth_instance->OnBondStateChanged(mojom::BluetoothStatus::SUCCESS,
                                         std::move(addr),
                                         mojom::BluetoothBondState::NONE);
}

void ArcBluetoothBridge::OnForgetError(mojom::BluetoothAddressPtr addr) const {
  auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
      arc_bridge_service_->bluetooth(), OnBondStateChanged);
  if (!bluetooth_instance)
    return;

  BluetoothDevice* device =
      bluetooth_adapter_->GetDevice(addr->To<std::string>());
  mojom::BluetoothBondState bond_state = mojom::BluetoothBondState::NONE;
  if (device && device->IsPaired()) {
    bond_state = mojom::BluetoothBondState::BONDED;
  }
  bluetooth_instance->OnBondStateChanged(mojom::BluetoothStatus::FAIL,
                                         std::move(addr), bond_state);
}

bool ArcBluetoothBridge::IsPowerChangeInitiatedByRemote(
    ArcBluetoothBridge::AdapterPowerState powered) const {
  return !remote_power_changes_.empty() &&
         remote_power_changes_.front() == powered;
}

bool ArcBluetoothBridge::IsPowerChangeInitiatedByLocal(
    ArcBluetoothBridge::AdapterPowerState powered) const {
  return !local_power_changes_.empty() &&
         local_power_changes_.front() == powered;
}

void ArcBluetoothBridge::OnConnectionReady() {
  if (!bluetooth_adapter_ || !bluetooth_adapter_->IsPowered()) {
    // The default power state of Bluetooth on Android is off, so there is no
    // need to send an intent to turn off Bluetooth if the initial power state
    // is off.
    return;
  }

  // Send initial power state in case both, Intent Helper and App instances are
  // present. Intent Helper is required to dispatch this event and App is sign
  // that ARC is fully started. In case of initial boot, App instance is started
  // after the Intent Helper instance. In case of next boot Intent Helper and
  // App instances are started at almost the same time and order of start is not
  // determined.
  if (!arc_bridge_service_->app()->IsConnected() ||
      !arc_bridge_service_->intent_helper()->IsConnected()) {
    return;
  }

  EnqueueLocalPowerChange(AdapterPowerState::TURN_ON);
}

void ArcBluetoothBridge::EnqueueLocalPowerChange(
    ArcBluetoothBridge::AdapterPowerState powered) {
  local_power_changes_.push(powered);

  if (power_intent_timer_.IsRunning())
    return;

  SendBluetoothPoweredStateBroadcast(local_power_changes_.front());
  power_intent_timer_.Start(
      FROM_HERE, kPowerIntentTimeout,
      base::BindOnce(&ArcBluetoothBridge::DequeueLocalPowerChange,
                     weak_factory_.GetWeakPtr(), powered));
}

void ArcBluetoothBridge::DequeueLocalPowerChange(
    ArcBluetoothBridge::AdapterPowerState powered) {
  power_intent_timer_.Stop();

  if (!IsPowerChangeInitiatedByLocal(powered))
    return;

  AdapterPowerState current_change = local_power_changes_.front();
  AdapterPowerState last_change = local_power_changes_.back();

  // Compress the queue for power intent to reduce the amount of intents being
  // sent to Android so that the powered state will be synced between Android
  // and Chrome even if the state is toggled repeatedly on Chrome.
  base::queue<AdapterPowerState> empty_queue;
  std::swap(local_power_changes_, empty_queue);

  if (last_change == current_change)
    return;

  local_power_changes_.push(last_change);

  SendBluetoothPoweredStateBroadcast(last_change);
  power_intent_timer_.Start(
      FROM_HERE, kPowerIntentTimeout,
      base::BindOnce(&ArcBluetoothBridge::DequeueLocalPowerChange,
                     weak_factory_.GetWeakPtr(), last_change));
}

void ArcBluetoothBridge::EnqueueRemotePowerChange(
    ArcBluetoothBridge::AdapterPowerState powered,
    ArcBluetoothBridge::AdapterStateCallback callback) {
  remote_power_changes_.push(powered);

  bool turn_on = (powered == AdapterPowerState::TURN_ON);
  auto split_callback = base::SplitOnceCallback(std::move(callback));
  BLUETOOTH_LOG(EVENT) << "ARC bluetooth set power: " << turn_on;
  bluetooth_adapter_->SetPowered(
      turn_on,
      base::BindOnce(turn_on ? &ArcBluetoothBridge::OnPoweredOn
                             : &ArcBluetoothBridge::OnPoweredOff,
                     weak_factory_.GetWeakPtr(),
                     std::move(split_callback.first),
                     /*save_user_pref=*/true),
      base::BindOnce(&ArcBluetoothBridge::OnPoweredError,
                     weak_factory_.GetWeakPtr(),
                     std::move(split_callback.second)));
}

void ArcBluetoothBridge::DequeueRemotePowerChange(
    ArcBluetoothBridge::AdapterPowerState powered) {
  remote_power_changes_.pop();
}

void ArcBluetoothBridge::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()) {
    // 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));
  }
}

std::vector<mojom::BluetoothPropertyPtr>
ArcBluetoothBridge::GetDeviceProperties(mojom::BluetoothPropertyType type,
                                        const BluetoothDevice* device) const {
  std::vector<mojom::BluetoothPropertyPtr> properties;

  if (!device) {
    return properties;
  }

  if (type == mojom::BluetoothPropertyType::ALL ||
      type == mojom::BluetoothPropertyType::BDNAME) {
    properties.push_back(mojom::BluetoothProperty::NewBdname(
        device->GetName() ? device->GetName().value() : ""));
  }
  if (type == mojom::BluetoothPropertyType::ALL ||
      type == mojom::BluetoothPropertyType::BDADDR) {
    properties.push_back(mojom::BluetoothProperty::NewBdaddr(
        mojom::BluetoothAddress::From(device->GetAddress())));
  }
  if (type == mojom::BluetoothPropertyType::ALL ||
      type == mojom::BluetoothPropertyType::UUIDS) {
    BluetoothDevice::UUIDSet uuids = device->GetUUIDs();
    if (uuids.size() > 0) {
      properties.push_back(mojom::BluetoothProperty::NewUuids(
          std::vector<BluetoothUUID>(uuids.begin(), uuids.end())));
    }
  }
  if (type == mojom::BluetoothPropertyType::ALL ||
      type == mojom::BluetoothPropertyType::CLASS_OF_DEVICE) {
    properties.push_back(
        mojom::BluetoothProperty::NewDeviceClass(device->GetBluetoothClass()));
  }
  if (type == mojom::BluetoothPropertyType::ALL ||
      type == mojom::BluetoothPropertyType::TYPE_OF_DEVICE) {
    properties.push_back(
        mojom::BluetoothProperty::NewDeviceType(device->GetType()));
  }
  if (type == mojom::BluetoothPropertyType::ALL ||
      type == mojom::BluetoothPropertyType::REMOTE_FRIENDLY_NAME) {
    properties.push_back(mojom::BluetoothProperty::NewRemoteFriendlyName(
        base::UTF16ToUTF8(device->GetNameForDisplay())));
  }
  if (type == mojom::BluetoothPropertyType::ALL ||
      type == mojom::BluetoothPropertyType::REMOTE_RSSI) {
    std::optional<int8_t> rssi = device->GetInquiryRSSI();
    if (rssi.has_value()) {
      properties.push_back(
          mojom::BluetoothProperty::NewRemoteRssi(rssi.value()));
    }
  }
  // TODO(smbarber): Add remote version info

  return properties;
}

std::vector<mojom::BluetoothPropertyPtr>
ArcBluetoothBridge::GetAdapterProperties(
    mojom::BluetoothPropertyType type) const {
  // Since this function is invoked from ARC side, it is possible that adapter
  // is not present or powered here. It's not meaningful to return any property
  // when that happens.
  const std::string adapter_address = bluetooth_adapter_->GetAddress();
  if (adapter_address.empty()) {
    LOG(ERROR) << "Bluetooth adapter does not have a valid address";
    return {};
  }

  std::vector<mojom::BluetoothPropertyPtr> properties;

  if (type == mojom::BluetoothPropertyType::ALL ||
      type == mojom::BluetoothPropertyType::BDNAME) {
    properties.push_back(
        mojom::BluetoothProperty::NewBdname(bluetooth_adapter_->GetName()));
  }
  if (type == mojom::BluetoothPropertyType::ALL ||
      type == mojom::BluetoothPropertyType::BDADDR) {
    properties.push_back(mojom::BluetoothProperty::NewBdaddr(
        mojom::BluetoothAddress::From(adapter_address)));
  }
  if (type == mojom::BluetoothPropertyType::ALL ||
      type == mojom::BluetoothPropertyType::UUIDS) {
    properties.push_back(
        mojom::BluetoothProperty::NewUuids(bluetooth_adapter_->GetUUIDs()));
  }
  if (type == mojom::BluetoothPropertyType::ALL ||
      type == mojom::BluetoothPropertyType::CLASS_OF_DEVICE) {
    properties.push_back(
        mojom::BluetoothProperty::NewDeviceClass(kBluetoothComputerClass));
  }
  if (type == mojom::BluetoothPropertyType::ALL ||
      type == mojom::BluetoothPropertyType::TYPE_OF_DEVICE) {
    // Assume that all ChromeOS devices are dual mode Bluetooth device.
    properties.push_back(mojom::BluetoothProperty::NewDeviceType(
        device::BLUETOOTH_TRANSPORT_DUAL));
  }
  if (type == mojom::BluetoothPropertyType::ALL ||
      type == mojom::BluetoothPropertyType::ADAPTER_SCAN_MODE) {
    mojom::BluetoothScanMode scan_mode = mojom::BluetoothScanMode::CONNECTABLE;

    if (bluetooth_adapter_->IsDiscoverable())
      scan_mode = mojom::BluetoothScanMode::CONNECTABLE_DISCOVERABLE;

    properties.push_back(
        mojom::BluetoothProperty::NewAdapterScanMode(scan_mode));
  }
  if (type == mojom::BluetoothPropertyType::ALL ||
      type == mojom::BluetoothPropertyType::ADAPTER_BONDED_DEVICES) {
    BluetoothAdapter::DeviceList devices = bluetooth_adapter_->GetDevices();

    std::vector<mojom::BluetoothAddressPtr> bonded_devices;

    for (auto* device : devices) {
      if (!device->IsPaired())
        continue;

      mojom::BluetoothAddressPtr addr =
          mojom::BluetoothAddress::From(device->GetAddress());
      bonded_devices.push_back(std::move(addr));
    }

    properties.push_back(
        mojom::BluetoothProperty::NewBondedDevices(std::move(bonded_devices)));
  }
  if (type == mojom::BluetoothPropertyType::ALL ||
      type == mojom::BluetoothPropertyType::ADAPTER_DISCOVERY_TIMEOUT) {
    properties.push_back(
        mojom::BluetoothProperty::NewDiscoveryTimeout(static_cast<uint32_t>(
            bluetooth_adapter_->GetDiscoverableTimeout().InSeconds())));
  }
  if (type == mojom::BluetoothPropertyType::ALL ||
      type == mojom::BluetoothPropertyType::LOCAL_LE_FEATURES) {
    // TODO(crbug.com/40480399) Investigate all the le_features.
    mojom::BluetoothLocalLEFeaturesPtr le_features =
        mojom::BluetoothLocalLEFeatures::New();
    le_features->version_supported = kAndroidMBluetoothVersionNumber;
    le_features->local_privacy_enabled = 0;
    le_features->max_adv_instance = kMaxAdvertisements;
    le_features->rpa_offload_supported = 0;
    le_features->max_irk_list_size = 0;
    le_features->max_adv_filter_supported = 0;
    le_features->activity_energy_info_supported = 0;
    le_features->scan_result_storage_size = 0;
    le_features->total_trackable_advertisers = 0;
    le_features->extended_scan_support = false;
    le_features->debug_logging_supported = false;
    properties.push_back(
        mojom::BluetoothProperty::NewLocalLeFeatures(std::move(le_features)));
  }

  return properties;
}

// Android support 6 types of Advertising Data which are Advertising Data Flags,
// Local Name, Service UUIDs, Tx Power Level, Service Data, and Manufacturer
// Data. Note that we need to use 16-bit UUID in Service Data section because
// Android does not support 128-bit UUID there.
std::vector<mojom::BluetoothAdvertisingDataPtr>
ArcBluetoothBridge::GetAdvertisingData(const BluetoothDevice* device) const {
  std::vector<mojom::BluetoothAdvertisingDataPtr> advertising_data;

  // Advertising Data Flags
  if (device->GetAdvertisingDataFlags().has_value()) {
    advertising_data.push_back(mojom::BluetoothAdvertisingData::NewFlags(
        device->GetAdvertisingDataFlags().value()));
  }

  // Local Name
  advertising_data.push_back(mojom::BluetoothAdvertisingData::NewLocalName(
      device->GetName() ? device->GetName().value() : ""));

  // Service UUIDs
  BluetoothDevice::UUIDSet uuid_set = device->GetUUIDs();
  for (const BluetoothRemoteGattService* gatt_service :
       device->GetGattServices()) {
    uuid_set.erase(gatt_service->GetUUID());
  }
  if (uuid_set.size() > 0) {
    advertising_data.push_back(mojom::BluetoothAdvertisingData::NewServiceUuids(
        std::vector<BluetoothUUID>(uuid_set.begin(), uuid_set.end())));
  }

  // Tx Power Level
  if (device->GetInquiryTxPower().has_value()) {
    advertising_data.push_back(mojom::BluetoothAdvertisingData::NewTxPowerLevel(
        device->GetInquiryTxPower().value()));
  }

  // Service Data
  for (const BluetoothUUID& uuid : device->GetServiceDataUUIDs()) {
    std::optional<uint16_t> uuid16 = GetUUID16(uuid);
    if (!uuid16)
      continue;

    mojom::BluetoothServiceDataPtr service_data =
        mojom::BluetoothServiceData::New();

    // Android only supports UUID 16 bit here.
    service_data->uuid_16bit = *uuid16;

    const std::vector<uint8_t>* data = device->GetServiceDataForUUID(uuid);
    DCHECK(data != nullptr);

    service_data->data = *data;

    advertising_data.push_back(mojom::BluetoothAdvertisingData::NewServiceData(
        std::move(service_data)));
  }

  // Manufacturer Data
  if (!device->GetManufacturerData().empty()) {
    std::vector<uint8_t> manufacturer_data;
    for (const auto& pair : device->GetManufacturerData()) {
      uint16_t id = pair.first;
      // Use little endian here.
      manufacturer_data.push_back(id & 0xff);
      manufacturer_data.push_back(id >> 8);
      manufacturer_data.insert(manufacturer_data.end(), pair.second.begin(),
                               pair.second.end());
    }
    advertising_data.push_back(
        mojom::BluetoothAdvertisingData::NewManufacturerData(
            std::move(manufacturer_data)));
  }

  return advertising_data;
}

void ArcBluetoothBridge::SetPrimaryUserBluetoothPowerSetting(
    bool enabled) const {
  const auto* primary_session =
      session_manager::SessionManager::Get()->GetPrimarySession();
  DCHECK(primary_session);
  Profile* profile = Profile::FromBrowserContext(
      ash::BrowserContextHelper::Get()->GetBrowserContextByAccountId(
          primary_session->account_id()));
  DCHECK(profile);
  profile->GetPrefs()->SetBoolean(ash::prefs::kUserBluetoothAdapterEnabled,
                                  enabled);
}

ArcBluetoothBridge::GattConnection::GattConnection(
    ArcBluetoothBridge::GattConnection::ConnectionState state,
    std::unique_ptr<device::BluetoothGattConnection> connection,
    bool need_hard_disconnect)
    : state(state),
      connection(std::move(connection)),
      need_hard_disconnect(need_hard_disconnect) {}
ArcBluetoothBridge::GattConnection::GattConnection()
    : connection(nullptr), need_hard_disconnect(false) {}
ArcBluetoothBridge::GattConnection::~GattConnection() = default;
ArcBluetoothBridge::GattConnection::GattConnection(
    ArcBluetoothBridge::GattConnection&&) = default;
ArcBluetoothBridge::GattConnection&
ArcBluetoothBridge::GattConnection::operator=(
    ArcBluetoothBridge::GattConnection&&) = default;

namespace {

bool IsValidPort(mojom::BluetoothSocketType sock_type, int port) {
  switch (sock_type) {
    case mojom::BluetoothSocketType::TYPE_RFCOMM:
      return port <= ArcBluetoothBridge::kMaxRfcommChannel &&
             port >= ArcBluetoothBridge::kMinRfcommChannel;
    case mojom::BluetoothSocketType::TYPE_L2CAP_LE:
      return port <= ArcBluetoothBridge::kMaxL2capLePsm &&
             port >= ArcBluetoothBridge::kMinL2capLePsm;
  }
}

}  // namespace

void ArcBluetoothBridge::BluetoothSocketListen(
    mojom::BluetoothSocketType sock_type,
    mojom::BluetoothSocketFlagsPtr sock_flags,
    int32_t port,
    BluetoothSocketListenCallback callback) {
  if (!mojom::IsKnownEnumValue(sock_type)) {
    LOG(ERROR) << "Unsupported sock type " << sock_type;
    std::move(callback).Run(
        mojom::BluetoothStatus::UNSUPPORTED, /*port=*/0,
        mojo::PendingReceiver<mojom::BluetoothListenSocketClient>());
    return;
  }

  if (port != kAutoSockPort && !IsValidPort(sock_type, port)) {
    LOG(ERROR) << "Invalid port number " << port;
    std::move(callback).Run(
        mojom::BluetoothStatus::FAIL, /*port=*/0,
        mojo::PendingReceiver<mojom::BluetoothListenSocketClient>());
    return;
  }

  CreateBluetoothListenSocket(sock_type, std::move(sock_flags), port,
                              std::move(callback));
}

void ArcBluetoothBridge::BluetoothSocketConnect(
    mojom::BluetoothSocketType sock_type,
    mojom::BluetoothSocketFlagsPtr sock_flags,
    mojom::BluetoothAddressPtr remote_addr,
    int32_t port,
    BluetoothSocketConnectCallback callback) {
  if (!mojom::IsKnownEnumValue(sock_type)) {
    LOG(ERROR) << "Unsupported sock type " << sock_type;
    std::move(callback).Run(
        mojom::BluetoothStatus::UNSUPPORTED,
        mojo::PendingReceiver<arc::mojom::BluetoothConnectSocketClient>());
    return;
  }

  if (!IsValidPort(sock_type, port)) {
    LOG(ERROR) << "Invalid port number " << port;
    std::move(callback).Run(
        mojom::BluetoothStatus::FAIL,
        mojo::PendingReceiver<arc::mojom::BluetoothConnectSocketClient>());
    return;
  }

  CreateBluetoothConnectSocket(sock_type, std::move(sock_flags),
                               std::move(remote_addr), port,
                               std::move(callback));
}

// static
void ArcBluetoothBridge::EnsureFactoryBuilt() {
  ArcBluezBridgeFactory::GetInstance();
  ArcFlossBridgeFactory::GetInstance();
}

ArcBluetoothBridge::BluetoothListeningSocket::BluetoothListeningSocket() =
    default;
ArcBluetoothBridge::BluetoothListeningSocket::~BluetoothListeningSocket() =
    default;
ArcBluetoothBridge::BluetoothConnectingSocket::BluetoothConnectingSocket() =
    default;
ArcBluetoothBridge::BluetoothConnectingSocket::~BluetoothConnectingSocket() =
    default;

ArcBluetoothBridge::BluetoothArcConnectionObserver::
    BluetoothArcConnectionObserver(ArcBluetoothBridge* arc_bluetooth_bridge)
    : arc_bluetooth_bridge_(arc_bluetooth_bridge) {
  arc_bluetooth_bridge_->arc_bridge_service_->bluetooth()->AddObserver(this);
}

ArcBluetoothBridge::BluetoothArcConnectionObserver::
    ~BluetoothArcConnectionObserver() {
  arc_bluetooth_bridge_->arc_bridge_service_->bluetooth()->RemoveObserver(this);
}

void ArcBluetoothBridge::BluetoothArcConnectionObserver::OnConnectionClosed() {
  // Stops the ongoing discovery sessions.
  arc_bluetooth_bridge_->CancelDiscovery();
  arc_bluetooth_bridge_->StopLEScan();

  // Cleanup for CreateBond().
  for (const auto& addr : arc_bluetooth_bridge_->devices_paired_by_arc_) {
    BluetoothDevice* device =
        arc_bluetooth_bridge_->bluetooth_adapter_->GetDevice(addr);
    if (!device)
      continue;
    if (device->IsPaired()) {
      device->Disconnect(base::DoNothing(), base::DoNothing());
    } else {
      device->CancelPairing();
    }
  }
  arc_bluetooth_bridge_->devices_paired_by_arc_.clear();
  arc_bluetooth_bridge_->devices_pairing_.clear();

  // Cleanup for GATT connections.
  // TODO(b/151573141): Remove the following loops when Chrome can perform hard
  // disconnect on a paired device correctly.
  for (const auto& addr_conn : arc_bluetooth_bridge_->gatt_connections_) {
    BluetoothDevice* device =
        arc_bluetooth_bridge_->bluetooth_adapter_->GetDevice(addr_conn.first);
    if (!device || !device->IsPaired() || !device->IsConnected())
      continue;
    device->Disconnect(base::DoNothing(), base::DoNothing());
  }
  arc_bluetooth_bridge_->gatt_connections_.clear();
}

}  // namespace arc
