blob: 0e820216af041cb4ef3179f6c623b2d714707624 [file] [log] [blame]
// 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 <utility>
#include "ash/constants/ash_pref_names.h"
#include "base/containers/contains.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/memory/singleton.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/ash/profiles/profile_helper.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/webui/ash/bluetooth/bluetooth_pairing_dialog.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/user_manager/user_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(
const std::string& 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() {
return base::Singleton<ArcBluezBridgeFactory>::get();
}
private:
friend base::DefaultSingletonTraits<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() {
return base::Singleton<ArcFlossBridgeFactory>::get();
}
private:
friend base::DefaultSingletonTraits<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 (!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 (!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/851097)
}
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::Value::Dict 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 (!base::Contains(notification_session_, 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 {
// TODO(crbug.com/40189401): 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 user_manager::User* const user =
user_manager::UserManager::Get()->GetPrimaryUser();
Profile* profile = ash::ProfileHelper::Get()->GetProfileByUser(user);
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