blob: cd192c4c1a90800c89fcdb09bb54337bdd9e3500 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// 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 <iomanip>
#include <string>
#include <utility>
#include "ash/constants/ash_pref_names.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/callback_helpers.h"
#include "base/containers/contains.h"
#include "base/containers/queue.h"
#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/memory/singleton.h"
#include "base/posix/eintr_wrapper.h"
#include "base/task/post_task.h"
#include "base/task/thread_pool.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/webui/chromeos/bluetooth_pairing_dialog.h"
#include "components/arc/arc_browser_context_keyed_service_factory_base.h"
#include "components/arc/bluetooth/bluetooth_type_converters.h"
#include "components/arc/intent_helper/arc_intent_helper_bridge.h"
#include "components/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/floss_features.h"
#include "mojo/public/cpp/platform/platform_handle.h"
#include "mojo/public/cpp/system/platform_handle.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
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 {
// https://android.googlesource.com/platform/system/bt/+/master/stack/include/gatt_api.h
constexpr int32_t GATT_CHAR_PROP_BIT_BROADCAST = (1 << 0);
constexpr int32_t GATT_CHAR_PROP_BIT_READ = (1 << 1);
constexpr int32_t GATT_CHAR_PROP_BIT_WRITE_NR = (1 << 2);
constexpr int32_t GATT_CHAR_PROP_BIT_WRITE = (1 << 3);
constexpr int32_t GATT_CHAR_PROP_BIT_NOTIFY = (1 << 4);
constexpr int32_t GATT_CHAR_PROP_BIT_INDICATE = (1 << 5);
constexpr int32_t GATT_CHAR_PROP_BIT_AUTH = (1 << 6);
constexpr int32_t GATT_CHAR_PROP_BIT_EXT_PROP = (1 << 7);
constexpr int32_t GATT_PERM_READ = (1 << 0);
constexpr int32_t GATT_PERM_READ_ENCRYPTED = (1 << 1);
constexpr int32_t GATT_PERM_READ_ENC_MITM = (1 << 2);
constexpr int32_t GATT_PERM_WRITE = (1 << 4);
constexpr int32_t GATT_PERM_WRITE_ENCRYPTED = (1 << 5);
constexpr int32_t GATT_PERM_WRITE_ENC_MITM = (1 << 6);
constexpr int32_t GATT_PERM_WRITE_SIGNED = (1 << 7);
constexpr int32_t GATT_PERM_WRITE_SIGNED_MITM = (1 << 8);
constexpr std::pair<int32_t, device::BluetoothGattCharacteristic::Permission>
kPermissionMapping[] = {
{GATT_PERM_READ, device::BluetoothGattCharacteristic::PERMISSION_READ},
{GATT_PERM_READ_ENCRYPTED,
device::BluetoothGattCharacteristic::PERMISSION_READ_ENCRYPTED},
{GATT_PERM_READ_ENC_MITM, device::BluetoothGattCharacteristic::
PERMISSION_READ_ENCRYPTED_AUTHENTICATED},
{GATT_PERM_WRITE,
device::BluetoothGattCharacteristic::PERMISSION_WRITE},
{GATT_PERM_WRITE_ENCRYPTED,
device::BluetoothGattCharacteristic::PERMISSION_WRITE_ENCRYPTED},
{GATT_PERM_WRITE_ENC_MITM,
device::BluetoothGattCharacteristic::
PERMISSION_WRITE_ENCRYPTED_AUTHENTICATED},
{GATT_PERM_WRITE_SIGNED_MITM,
device::BluetoothGattCharacteristic::
PERMISSION_WRITE_ENCRYPTED_AUTHENTICATED},
};
constexpr std::pair<int32_t, device::BluetoothGattCharacteristic::Properties>
kPropertyMapping[] = {
{GATT_CHAR_PROP_BIT_BROADCAST,
device::BluetoothGattCharacteristic::PROPERTY_BROADCAST},
{GATT_CHAR_PROP_BIT_READ,
device::BluetoothGattCharacteristic::PROPERTY_READ},
{GATT_CHAR_PROP_BIT_WRITE_NR,
device::BluetoothGattCharacteristic::PROPERTY_WRITE_WITHOUT_RESPONSE},
{GATT_CHAR_PROP_BIT_WRITE,
device::BluetoothGattCharacteristic::PROPERTY_WRITE},
{GATT_CHAR_PROP_BIT_NOTIFY,
device::BluetoothGattCharacteristic::PROPERTY_NOTIFY},
{GATT_CHAR_PROP_BIT_INDICATE,
device::BluetoothGattCharacteristic::PROPERTY_INDICATE},
{GATT_CHAR_PROP_BIT_AUTH, device::BluetoothGattCharacteristic::
PROPERTY_AUTHENTICATED_SIGNED_WRITES},
{GATT_CHAR_PROP_BIT_EXT_PROP,
device::BluetoothGattCharacteristic::PROPERTY_EXTENDED_PROPERTIES},
};
constexpr uint32_t kGattReadPermission =
BluetoothGattCharacteristic::Permission::PERMISSION_READ |
BluetoothGattCharacteristic::Permission::PERMISSION_READ_ENCRYPTED |
BluetoothGattCharacteristic::Permission::
PERMISSION_READ_ENCRYPTED_AUTHENTICATED;
constexpr uint32_t kGattWritePermission =
BluetoothGattCharacteristic::Permission::PERMISSION_WRITE |
BluetoothGattCharacteristic::Permission::PERMISSION_WRITE_ENCRYPTED |
BluetoothGattCharacteristic::Permission::
PERMISSION_WRITE_ENCRYPTED_AUTHENTICATED;
// 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://goo.gl/k7PM6u
constexpr uint16_t kAndroidMBluetoothVersionNumber = 95;
// Bluetooth SDP Service Class ID List Attribute identifier
constexpr uint16_t kServiceClassIDListAttributeID = 0x0001;
// 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";
using GattReadCallback =
base::OnceCallback<void(arc::mojom::BluetoothGattValuePtr)>;
using CreateSdpRecordCallback =
base::OnceCallback<void(arc::mojom::BluetoothCreateSdpRecordResultPtr)>;
using RemoveSdpRecordCallback =
base::OnceCallback<void(arc::mojom::BluetoothStatus)>;
device::BluetoothGattCharacteristic::Permissions ConvertToBlueZGattPermissions(
int32_t permissions) {
device::BluetoothGattCharacteristic::Permissions result =
device::BluetoothGattCharacteristic::PERMISSION_NONE;
for (const auto& permission_pair : kPermissionMapping) {
if (permissions & permission_pair.first)
result |= permission_pair.second;
}
return result;
}
device::BluetoothGattCharacteristic::Properties ConvertToBlueZGattProperties(
int32_t properties) {
device::BluetoothGattCharacteristic::Properties result =
device::BluetoothGattCharacteristic::PROPERTY_NONE;
for (const auto& property_pair : kPropertyMapping) {
if (properties & property_pair.first)
result |= property_pair.second;
}
return result;
}
arc::mojom::BluetoothGattStatus ConvertGattErrorCodeToStatus(
const device::BluetoothGattService::GattErrorCode& error_code,
bool is_read_operation) {
switch (error_code) {
case device::BluetoothGattService::GattErrorCode::GATT_ERROR_INVALID_LENGTH:
return arc::mojom::BluetoothGattStatus::GATT_INVALID_ATTRIBUTE_LENGTH;
case device::BluetoothGattService::GattErrorCode::GATT_ERROR_NOT_PERMITTED:
return is_read_operation
? arc::mojom::BluetoothGattStatus::GATT_READ_NOT_PERMITTED
: arc::mojom::BluetoothGattStatus::GATT_WRITE_NOT_PERMITTED;
case device::BluetoothGattService::GattErrorCode::GATT_ERROR_NOT_AUTHORIZED:
return arc::mojom::BluetoothGattStatus::GATT_INSUFFICIENT_AUTHENTICATION;
case device::BluetoothGattService::GattErrorCode::GATT_ERROR_NOT_SUPPORTED:
return arc::mojom::BluetoothGattStatus::GATT_REQUEST_NOT_SUPPORTED;
case device::BluetoothGattService::GattErrorCode::GATT_ERROR_UNKNOWN:
case device::BluetoothGattService::GattErrorCode::GATT_ERROR_FAILED:
case device::BluetoothGattService::GattErrorCode::GATT_ERROR_IN_PROGRESS:
case device::BluetoothGattService::GattErrorCode::GATT_ERROR_NOT_PAIRED:
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.
absl::optional<uint16_t> ConvertGattIdentifierToId(
const std::string identifier) {
uint32_t result;
if (identifier.size() < 4 ||
!base::HexStringToUInt(identifier.substr(identifier.size() - 4), &result))
return absl::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) {
absl::optional<int16_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->id = element->attribute_handle = element->start_handle =
element->end_handle = *id;
element->properties = 0;
return element;
}
template <class RemoteGattAttribute>
RemoteGattAttribute* FindGattAttributeByUuid(
const std::vector<RemoteGattAttribute*>& attributes,
const BluetoothUUID& uuid) {
auto it = std::find_if(
attributes.begin(), attributes.end(),
[uuid](RemoteGattAttribute* attr) { return attr->GetUUID() == uuid; });
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(
GattReadCallback callback,
absl::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=*/absl::nullopt, value);
} else {
std::move(callback).Run(BluetoothGattService::GATT_ERROR_FAILED,
/*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
absl::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 absl::nullopt;
return result;
}
arc::mojom::BluetoothPropertyPtr GetDiscoveryTimeoutProperty(uint32_t timeout) {
arc::mojom::BluetoothPropertyPtr property =
arc::mojom::BluetoothProperty::New();
property->set_discovery_timeout(timeout);
return property;
}
void OnCreateServiceRecordDone(CreateSdpRecordCallback callback,
uint32_t service_handle) {
arc::mojom::BluetoothCreateSdpRecordResultPtr result =
arc::mojom::BluetoothCreateSdpRecordResult::New();
result->status = arc::mojom::BluetoothStatus::SUCCESS;
result->service_handle = service_handle;
std::move(callback).Run(std::move(result));
}
void OnCreateServiceRecordError(
CreateSdpRecordCallback callback,
bluez::BluetoothServiceRecordBlueZ::ErrorCode error_code) {
arc::mojom::BluetoothCreateSdpRecordResultPtr result =
arc::mojom::BluetoothCreateSdpRecordResult::New();
if (error_code ==
bluez::BluetoothServiceRecordBlueZ::ErrorCode::ERROR_ADAPTER_NOT_READY) {
result->status = arc::mojom::BluetoothStatus::NOT_READY;
} else {
result->status = arc::mojom::BluetoothStatus::FAIL;
}
std::move(callback).Run(std::move(result));
}
void OnRemoveServiceRecordDone(RemoveSdpRecordCallback callback) {
std::move(callback).Run(arc::mojom::BluetoothStatus::SUCCESS);
}
void OnRemoveServiceRecordError(
RemoveSdpRecordCallback callback,
bluez::BluetoothServiceRecordBlueZ::ErrorCode error_code) {
arc::mojom::BluetoothStatus status;
if (error_code ==
bluez::BluetoothServiceRecordBlueZ::ErrorCode::ERROR_ADAPTER_NOT_READY)
status = arc::mojom::BluetoothStatus::NOT_READY;
else
status = arc::mojom::BluetoothStatus::FAIL;
std::move(callback).Run(status);
}
const device::BluetoothLocalGattDescriptor* FindCCCD(
const device::BluetoothLocalGattCharacteristic* characteristic) {
for (const auto& descriptor :
static_cast<const bluez::BluetoothLocalGattCharacteristicBlueZ*>(
characteristic)
->GetDescriptors()) {
if (descriptor->GetUUID() ==
BluetoothGattDescriptor::ClientCharacteristicConfigurationUuid()) {
return descriptor.get();
}
}
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 ArcBluetoothBridgeFactory
: public internal::ArcBrowserContextKeyedServiceFactoryBase<
ArcBluetoothBridge,
ArcBluetoothBridgeFactory> {
public:
// Factory name used by ArcBrowserContextKeyedServiceFactoryBase.
static constexpr const char* kName = "ArcBluetoothBridgeFactory";
static ArcBluetoothBridgeFactory* GetInstance() {
return base::Singleton<ArcBluetoothBridgeFactory>::get();
}
private:
friend base::DefaultSingletonTraits<ArcBluetoothBridgeFactory>;
ArcBluetoothBridgeFactory() = default;
~ArcBluetoothBridgeFactory() override = default;
};
} // namespace
// static
ArcBluetoothBridge* ArcBluetoothBridge::GetForBrowserContext(
content::BrowserContext* context) {
return ArcBluetoothBridgeFactory::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 (base::FeatureList::IsEnabled(floss::features::kFlossEnabled)) {
VLOG(1) << "Disabling ArcBluetoothBridge, Floss not yet supported.";
return;
}
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_);
// We can downcast here because we are always running on Chrome OS, and
// so our adapter uses BlueZ.
bluetooth_adapter_ =
static_cast<bluez::BluetoothAdapterBlueZ*>(adapter.get());
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 (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 {
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());
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::GattServiceAdded(BluetoothAdapter* adapter,
BluetoothDevice* device,
BluetoothRemoteGattService* service) {
if (!arc_bridge_service_->bluetooth()->IsConnected())
return;
// Placeholder for GATT client functionality
}
void ArcBluetoothBridge::GattServiceRemoved(
BluetoothAdapter* adapter,
BluetoothDevice* device,
BluetoothRemoteGattService* service) {
if (!arc_bridge_service_->bluetooth()->IsConnected())
return;
// Placeholder for GATT client functionality
}
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::GattServiceChanged(
BluetoothAdapter* adapter,
BluetoothRemoteGattService* service) {
if (!arc_bridge_service_->bluetooth()->IsConnected())
return;
// Placeholder for GATT client functionality
}
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 absl::optional<int16_t> char_inst_id =
ConvertGattIdentifierToId(characteristic->GetIdentifier());
if (!char_inst_id)
return;
BluetoothRemoteGattService* service = characteristic->GetService();
const absl::optional<int16_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->inst_id = *service_inst_id;
service_id->id->uuid = service->GetUUID();
mojom::BluetoothGattIDPtr char_id = mojom::BluetoothGattID::New();
char_id->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::GATT_ERROR_FAILED,
/*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::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_);
DCHECK(!discoverable || timeout != 0);
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();
if (discovery_timeout > 0) {
discoverable_off_timeout_ = discovery_timeout;
} else {
OnSetAdapterProperty(mojom::BluetoothStatus::PARM_INVALID,
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) {
SetDiscoverable(discoverable_off_timeout_ > 0, discoverable_off_timeout_);
} 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.";
le_scan_off_timer_.Start(
FROM_HERE, kDiscoveryTimeout,
base::BindOnce(&ArcBluetoothBridge::StopLEScanByTimer,
weak_factory_.GetWeakPtr()));
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::StopLEScanImpl() {
le_scan_off_timer_.Stop();
le_scan_session_ = nullptr;
discovery_queue_.Pop();
}
void ArcBluetoothBridge::OnPoweredOn(
ArcBluetoothBridge::AdapterStateCallback callback,
bool save_user_pref) const {
// 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();
}
void ArcBluetoothBridge::OnPoweredOff(
ArcBluetoothBridge::AdapterStateCallback callback,
bool save_user_pref) const {
// 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_);
// 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()));
le_scan_session_ = std::move(session);
// 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.
chromeos::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,
absl::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 absl::optional<int16_t> start_handle =
ConvertGattIdentifierToId(characteristics.front()->GetIdentifier());
if (!start_handle)
continue;
const absl::optional<int16_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::DictionaryValue extras;
extras.SetBoolean("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"),
ArcIntentHelperBridge::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));
DCHECK(characteristic);
DCHECK(characteristic->GetPermissions() & kGattReadPermission);
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));
DCHECK(characteristic);
DCHECK(characteristic->GetPermissions() & kGattWritePermission);
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));
DCHECK(descriptor);
DCHECK(descriptor->GetPermissions() & kGattReadPermission);
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));
DCHECK(descriptor);
DCHECK(descriptor->GetPermissions() & kGattWritePermission);
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()) {
std::move(callback).Run(mojom::BluetoothGattStatus::GATT_FAILURE);
return;
}
// Stop the previous session while keeping this client registered.
it->second.reset();
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), 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), char_id_str),
base::BindOnce(&OnGattOperationError,
std::move(split_callback.first)));
return;
}
default:
std::move(callback).Run(mojom::BluetoothGattStatus::GATT_FAILURE);
}
}
void ArcBluetoothBridge::ExecuteWrite(mojom::BluetoothAddressPtr remote_addr,
bool execute,
ExecuteWriteCallback callback) {
bluez::BluetoothDeviceBlueZ* device =
static_cast<bluez::BluetoothDeviceBlueZ*>(
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,
const 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();
if (base::Contains(notification_session_, char_id_str)) {
// There can be only one notification session per characteristic.
std::move(callback).Run(mojom::BluetoothGattStatus::GATT_FAILURE);
return;
}
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));
}
}
void ArcBluetoothBridge::OpenBluetoothSocketDeprecated(
OpenBluetoothSocketDeprecatedCallback callback) {
base::ScopedFD sock(socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM));
if (!sock.is_valid()) {
LOG(ERROR) << "Failed to open socket.";
std::move(callback).Run(mojo::ScopedHandle());
return;
}
mojo::ScopedHandle handle =
mojo::WrapPlatformHandle(mojo::PlatformHandle(std::move(sock)));
if (!handle.is_valid()) {
LOG(ERROR) << "Failed to wrap socket handle. Closing";
std::move(callback).Run(mojo::ScopedHandle());
return;
}
std::move(callback).Run(std::move(handle));
}
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 =
BluetoothLocalGattService::Create(
bluetooth_adapter_.get(), service_id->id->uuid,
service_id->is_primary, nullptr /* included_service */,
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_);
DCHECK(gatt_identifier_.find(service_handle) != gatt_identifier_.end());
if (!IsGattServerAttributeHandleAvailable(1)) {
std::move(callback).Run(kInvalidGattAttributeHandle);
return;
}
device::BluetoothGattCharacteristic::Properties bluez_properties =
ConvertToBlueZGattProperties(properties);
// "WRITE_SIGNED" is defined as a permission in android, while it is a
// property in BlueZ. Thus, extra translation is required here.
if (permissions & GATT_PERM_WRITE_SIGNED ||
permissions & GATT_PERM_WRITE_SIGNED_MITM) {
bluez_properties |= device::BluetoothGattCharacteristic::
PROPERTY_AUTHENTICATED_SIGNED_WRITES;
}
base::WeakPtr<BluetoothLocalGattCharacteristic> characteristic =
BluetoothLocalGattCharacteristic::Create(
uuid, bluez_properties, ConvertToBlueZGattPermissions(permissions),
bluetooth_adapter_->GetGattService(gatt_identifier_[service_handle]));
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.
// Link: https://goo.gl/cJZl1u
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);
base::WeakPtr<BluetoothLocalGattDescriptor> descriptor =
BluetoothLocalGattDescriptor::Create(
uuid, ConvertToBlueZGattPermissions(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);
}
void ArcBluetoothBridge::GetSdpRecords(mojom::BluetoothAddressPtr remote_addr,
const BluetoothUUID& target_uuid) {
BluetoothDevice* device =
bluetooth_adapter_->GetDevice(remote_addr->To<std::string>());
if (!device) {
OnGetServiceRecordsError(std::move(remote_addr), target_uuid,
bluez::BluetoothServiceRecordBlueZ::ErrorCode::
ERROR_DEVICE_DISCONNECTED);
return;
}
bluez::BluetoothDeviceBlueZ* device_bluez =
static_cast<bluez::BluetoothDeviceBlueZ*>(device);
mojom::BluetoothAddressPtr remote_addr_clone = remote_addr.Clone();
device_bluez->GetServiceRecords(
base::BindOnce(&ArcBluetoothBridge::OnGetServiceRecordsDone,
weak_factory_.GetWeakPtr(), std::move(remote_addr),
target_uuid),
base::BindOnce(&ArcBluetoothBridge::OnGetServiceRecordsError,
weak_factory_.GetWeakPtr(), std::move(remote_addr_clone),
target_uuid));
}
void ArcBluetoothBridge::CreateSdpRecord(
mojom::BluetoothSdpRecordPtr record_mojo,
CreateSdpRecordCallback callback) {
auto record = record_mojo.To<bluez::BluetoothServiceRecordBlueZ>();
// Check if ServiceClassIDList attribute (attribute ID 0x0001) is included
// after type conversion, since it is mandatory for creating a service record.
if (!record.IsAttributePresented(kServiceClassIDListAttributeID)) {
mojom::BluetoothCreateSdpRecordResultPtr result =
mojom::BluetoothCreateSdpRecordResult::New();
result->status = mojom::BluetoothStatus::FAIL;
std::move(callback).Run(std::move(result));
return;
}
auto split_callback = base::SplitOnceCallback(std::move(callback));
bluetooth_adapter_->CreateServiceRecord(
record,
base::BindOnce(&OnCreateServiceRecordDone,
std::move(split_callback.first)),
base::BindOnce(&OnCreateServiceRecordError,
std::move(split_callback.second)));
}
void ArcBluetoothBridge::RemoveSdpRecord(uint32_t service_handle,
RemoveSdpRecordCallback callback) {
auto split_callback = base::SplitOnceCallback(std::move(callback));
bluetooth_adapter_->RemoveServiceRecord(
service_handle,
base::BindOnce(&OnRemoveServiceRecordDone,
std::move(split_callback.first)),
base::BindOnce(&OnRemoveServiceRecordError,
std::move(split_callback.second)));
}
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) {
mojom::BluetoothPropertyPtr btp = mojom::BluetoothProperty::New();
btp->set_bdname(device->GetName() ? device->GetName().value() : "");
properties.push_back(std::move(btp));
}
if (type == mojom::BluetoothPropertyType::ALL ||
type == mojom::BluetoothPropertyType::BDADDR) {
mojom::BluetoothPropertyPtr btp = mojom::BluetoothProperty::New();
btp->set_bdaddr(mojom::BluetoothAddress::From(device->GetAddress()));
properties.push_back(std::move(btp));
}
if (type == mojom::BluetoothPropertyType::ALL ||
type == mojom::BluetoothPropertyType::UUIDS) {
BluetoothDevice::UUIDSet uuids = device->GetUUIDs();
if (uuids.size() > 0) {
mojom::BluetoothPropertyPtr btp = mojom::BluetoothProperty::New();
btp->set_uuids(std::vector<BluetoothUUID>(uuids.begin(), uuids.end()));
properties.push_back(std::move(btp));
}
}
if (type == mojom::BluetoothPropertyType::ALL ||
type == mojom::BluetoothPropertyType::CLASS_OF_DEVICE) {
mojom::BluetoothPropertyPtr btp = mojom::BluetoothProperty::New();
btp->set_device_class(device->GetBluetoothClass());
properties.push_back(std::move(btp));
}
if (type == mojom::BluetoothPropertyType::ALL ||
type == mojom::BluetoothPropertyType::TYPE_OF_DEVICE) {
mojom::BluetoothPropertyPtr btp = mojom::BluetoothProperty::New();
btp->set_device_type(device->GetType());
properties.push_back(std::move(btp));
}
if (type == mojom::BluetoothPropertyType::ALL ||
type == mojom::BluetoothPropertyType::REMOTE_FRIENDLY_NAME) {
mojom::BluetoothPropertyPtr btp = mojom::BluetoothProperty::New();
btp->set_remote_friendly_name(
base::UTF16ToUTF8(device->GetNameForDisplay()));
properties.push_back(std::move(btp));
}
if (type == mojom::BluetoothPropertyType::ALL ||
type == mojom::BluetoothPropertyType::REMOTE_RSSI) {
absl::optional<int8_t> rssi = device->GetInquiryRSSI();
if (rssi.has_value()) {
mojom::BluetoothPropertyPtr btp = mojom::BluetoothProperty::New();
btp->set_remote_rssi(rssi.value());
properties.push_back(std::move(btp));
}
}
// TODO(smbarber): Add remote version info
return properties;
}
std::vector<mojom::BluetoothPropertyPtr>
ArcBluetoothBridge::GetAdapterProperties(
mojom::BluetoothPropertyType type) const {
std::vector<mojom::BluetoothPropertyPtr> properties;
if (type == mojom::BluetoothPropertyType::ALL ||
type == mojom::BluetoothPropertyType::BDNAME) {
mojom::BluetoothPropertyPtr btp = mojom::BluetoothProperty::New();
std::string name = bluetooth_adapter_->GetName();
btp->set_bdname(name);
properties.push_back(std::move(btp));
}
if (type == mojom::BluetoothPropertyType::ALL ||
type == mojom::BluetoothPropertyType::BDADDR) {
mojom::BluetoothPropertyPtr btp = mojom::BluetoothProperty::New();
btp->set_bdaddr(
mojom::BluetoothAddress::From(bluetooth_adapter_->GetAddress()));
properties.push_back(std::move(btp));
}
if (type == mojom::BluetoothPropertyType::ALL ||
type == mojom::BluetoothPropertyType::UUIDS) {
mojom::BluetoothPropertyPtr btp = mojom::BluetoothProperty::New();
btp->set_uuids(bluetooth_adapter_->GetUUIDs());
properties.push_back(std::move(btp));
}
if (type == mojom::BluetoothPropertyType::ALL ||
type == mojom::BluetoothPropertyType::CLASS_OF_DEVICE) {
mojom::BluetoothPropertyPtr btp = mojom::BluetoothProperty::New();
btp->set_device_class(kBluetoothComputerClass);
properties.push_back(std::move(btp));
}
if (type == mojom::BluetoothPropertyType::ALL ||
type == mojom::BluetoothPropertyType::TYPE_OF_DEVICE) {
// Assume that all ChromeOS devices are dual mode Bluetooth device.
mojom::BluetoothPropertyPtr btp = mojom::BluetoothProperty::New();
btp->set_device_type(device::BLUETOOTH_TRANSPORT_DUAL);
properties.push_back(std::move(btp));
}
if (type == mojom::BluetoothPropertyType::ALL ||
type == mojom::BluetoothPropertyType::ADAPTER_SCAN_MODE) {
mojom::BluetoothPropertyPtr btp = mojom::BluetoothProperty::New();
mojom::BluetoothScanMode scan_mode = mojom::BluetoothScanMode::CONNECTABLE;
if (bluetooth_adapter_->IsDiscoverable())
scan_mode = mojom::BluetoothScanMode::CONNECTABLE_DISCOVERABLE;
btp->set_adapter_scan_mode(scan_mode);
properties.push_back(std::move(btp));
}
if (type == mojom::BluetoothPropertyType::ALL ||
type == mojom::BluetoothPropertyType::ADAPTER_BONDED_DEVICES) {
mojom::BluetoothPropertyPtr btp = mojom::BluetoothProperty::New();
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));
}
btp->set_bonded_devices(std::move(bonded_devices));
properties.push_back(std::move(btp));
}
if (type == mojom::BluetoothPropertyType::ALL ||
type == mojom::BluetoothPropertyType::ADAPTER_DISCOVERY_TIMEOUT) {
mojom::BluetoothPropertyPtr btp = mojom::BluetoothProperty::New();
btp->set_discovery_timeout(bluetooth_adapter_->GetDiscoverableTimeout());
properties.push_back(std::move(btp));
}
if (type == mojom::BluetoothPropertyType::ALL ||
type == mojom::BluetoothPropertyType::LOCAL_LE_FEATURES) {
// TODO(crbug.com/637171) Investigate all the le_features.
mojom::BluetoothPropertyPtr btp = mojom::BluetoothProperty::New();
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;
btp->set_local_le_features(std::move(le_features));
properties.push_back(std::move(btp));
}
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()) {
mojom::BluetoothAdvertisingDataPtr flags =
mojom::BluetoothAdvertisingData::New();
flags->set_flags(device->GetAdvertisingDataFlags().value());
advertising_data.push_back(std::move(flags));
}
// Local Name
mojom::BluetoothAdvertisingDataPtr local_name =
mojom::BluetoothAdvertisingData::New();
local_name->set_local_name(device->GetName() ? device->GetName().value()
: "");
advertising_data.push_back(std::move(local_name));
// 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) {
mojom::BluetoothAdvertisingDataPtr service_uuids =
mojom::BluetoothAdvertisingData::New();
service_uuids->set_service_uuids(
std::vector<BluetoothUUID>(uuid_set.begin(), uuid_set.end()));
advertising_data.push_back(std::move(service_uuids));
}
// Tx Power Level
if (device->GetInquiryTxPower().has_value()) {
mojom::BluetoothAdvertisingDataPtr tx_power_level_element =
mojom::BluetoothAdvertisingData::New();
tx_power_level_element->set_tx_power_level(
device->GetInquiryTxPower().value());
advertising_data.push_back(std::move(tx_power_level_element));
}
// Service Data
for (const BluetoothUUID& uuid : device->GetServiceDataUUIDs()) {
absl::optional<uint16_t> uuid16 = GetUUID16(uuid);
if (!uuid16)
continue;
mojom::BluetoothAdvertisingDataPtr service_data_element =
mojom::BluetoothAdvertisingData::New();
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;
service_data_element->set_service_data(std::move(service_data));
advertising_data.push_back(std::move(service_data_element));
}
// 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());
}
mojom::BluetoothAdvertisingDataPtr manufacturer_data_element =
mojom::BluetoothAdvertisingData::New();
manufacturer_data_element->set_manufacturer_data(manufacturer_data);
advertising_data.push_back(std::move(manufacturer_data_element));
}
return advertising_data;
}
void ArcBluetoothBridge::OnGetServiceRecordsDone(
mojom::BluetoothAddressPtr remote_addr,
const BluetoothUUID& target_uuid,
const std::vector<bluez::BluetoothServiceRecordBlueZ>& records_bluez) {
auto* sdp_bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
arc_bridge_service_->bluetooth(), OnGetSdpRecords);
if (!sdp_bluetooth_instance)
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)
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::SetPrimaryUserBluetoothPowerSetting(
bool enabled) const {
const user_manager::User* const user =
user_manager::UserManager::Get()->GetPrimaryUser();
Profile* profile = chromeos::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 {
constexpr int kAutoSockPort = 0;
constexpr int kMinRfcommChannel = 1;
constexpr int kMaxRfcommChannel = 30;
// Copied from the values of L2CAP_PSM_LE_DYN_START and L2CAP_PSM_LE_DYN_END
// in /include/net/bluetooth/l2cap.h
constexpr int kMinL2capLePsm = 0x0080;
constexpr int kMaxL2capLePsm = 0x00FF;
union BluetoothSocketAddress {
sockaddr sock;
sockaddr_rc rfcomm;
sockaddr_l2 l2cap;
};
bool IsValidPort(mojom::BluetoothSocketType sock_type, int port) {
switch (sock_type) {
case mojom::BluetoothSocketType::TYPE_RFCOMM:
return port <= kMaxRfcommChannel && port >= kMinRfcommChannel;
case mojom::BluetoothSocketType::TYPE_L2CAP_LE:
return port <= kMaxL2capLePsm && port >= kMinL2capLePsm;
}
}
int32_t GetSockOptvalFromFlags(mojom::BluetoothSocketType sock_type,
mojom::BluetoothSocketFlagsPtr sock_flags) {
int optval = 0;
switch (sock_type) {
case mojom::BluetoothSocketType::TYPE_RFCOMM:
optval |= sock_flags->encrypt ? RFCOMM_LM_ENCRYPT : 0;
optval |= sock_flags->auth ? RFCOMM_LM_AUTH : 0;
optval |= sock_flags->auth_mitm ? RFCOMM_LM_SECURE : 0;
optval |= sock_flags->auth_16_digit ? RFCOMM_LM_SECURE : 0;
return optval;
case mojom::BluetoothSocketType::TYPE_L2CAP_LE:
optval |= sock_flags->encrypt ? L2CAP_LM_ENCRYPT : 0;
optval |= sock_flags->auth ? L2CAP_LM_AUTH : 0;
optval |= sock_flags->auth_mitm ? L2CAP_LM_SECURE : 0;
optval |= sock_flags->auth_16_digit ? L2CAP_LM_SECURE : 0;
return optval;
}
}
// Opens an AF_BLUETOOTH socket with |sock_type|, sets L2CAP_LM or RFCOMM_LM
// with |optval|, and binds the socket to address with |port|.
base::ScopedFD OpenBluetoothSocketImpl(mojom::BluetoothSocketType sock_type,
int32_t optval,
uint16_t port) {
int protocol;
int level;
int optname;
switch (sock_type) {
case mojom::BluetoothSocketType::TYPE_RFCOMM:
protocol = BTPROTO_RFCOMM;
level = SOL_RFCOMM;
optname = RFCOMM_LM;
break;
case mojom::BluetoothSocketType::TYPE_L2CAP_LE:
protocol = BTPROTO_L2CAP;
level = SOL_L2CAP;
optname = L2CAP_LM;
break;
default:
LOG(ERROR) << "Unknown socket type " << sock_type;
return {};
}
base::ScopedFD sock(socket(AF_BLUETOOTH, SOCK_STREAM, protocol));
if (!sock.is_valid()) {
PLOG(ERROR) << "Failed to open bluetooth socket.";
return {};
}
if (setsockopt(sock.get(), level, optname, &optval, sizeof(optval)) == -1) {
PLOG(ERROR) << "Failed to setopt() on socket.";
return {};
}
if (fcntl(sock.get(), F_SETFL, O_NONBLOCK | fcntl(sock.get(), F_GETFL)) ==
-1) {
PLOG(ERROR) << "Failed to fcntl() on socket.";
return {};
}
BluetoothSocketAddress sa = {};
switch (sock_type) {
case mojom::BluetoothSocketType::TYPE_RFCOMM:
sa.rfcomm.rc_family = AF_BLUETOOTH;
sa.rfcomm.rc_channel = port;
break;
case mojom::BluetoothSocketType::TYPE_L2CAP_LE:
sa.l2cap.l2_family = AF_BLUETOOTH;
sa.l2cap.l2_psm = htobs(port);
sa.l2cap.l2_bdaddr_type = BDADDR_LE_PUBLIC;
break;
default:
LOG(ERROR) << "Unknown socket type " << sock_type;
return {};
}
if (bind(sock.get(), &sa.sock, sizeof(sa)) == -1) {
PLOG(ERROR) << "Failed to bind()";
return {};
}
return sock;
}
} // namespace
void ArcBluetoothBridge::RfcommListenDeprecated(
int32_t channel,
int32_t optval,
RfcommListenDeprecatedCallback callback) {
if (channel != kAutoSockPort &&
!IsValidPort(mojom::BluetoothSocketType::TYPE_RFCOMM, channel)) {
LOG(ERROR) << "Invalid channel number";
std::move(callback).Run(
mojom::BluetoothStatus::FAIL, /*channel=*/0,
mojo::PendingReceiver<mojom::RfcommListeningSocketClient>());
return;
}
uint16_t listen_channel = static_cast<uint16_t>(channel);
auto sock_wrapper = CreateBluetoothListenSocket(
mojom::BluetoothSocketType::TYPE_RFCOMM, optval, &listen_channel);
if (!sock_wrapper) {
std::move(callback).Run(
mojom::BluetoothStatus::FAIL, /*channel=*/0,
mojo::PendingReceiver<mojom::RfcommListeningSocketClient>());
return;
}
sock_wrapper->created_by_deprecated_method = true;
std::move(callback).Run(
mojom::BluetoothStatus::SUCCESS, listen_channel,
sock_wrapper->deprecated_remote.BindNewPipeAndPassReceiver());
sock_wrapper->deprecated_remote.set_disconnect_handler(
base::BindOnce(&ArcBluetoothBridge::CloseBluetoothListeningSocket,
weak_factory_.GetWeakPtr(), sock_wrapper.get()));
listening_sockets_.insert(std::move(sock_wrapper));
}
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;
}
int32_t optval = GetSockOptvalFromFlags(sock_type, std::move(sock_flags));
uint16_t listen_port = static_cast<uint16_t>(port);
auto sock_wrapper =
CreateBluetoothListenSocket(sock_type, optval, &listen_port);
if (!sock_wrapper) {
std::move(callback).Run(
mojom::BluetoothStatus::FAIL, /*port=*/0,
mojo::PendingReceiver<mojom::BluetoothListenSocketClient>());
return;
}
std::move(callback).Run(mojom::BluetoothStatus::SUCCESS, listen_port,
sock_wrapper->remote.BindNewPipeAndPassReceiver());
sock_wrapper->remote.set_disconnect_handler(
base::BindOnce(&ArcBluetoothBridge::CloseBluetoothListeningSocket,
weak_factory_.GetWeakPtr(), sock_wrapper.get()));
listening_sockets_.insert(std::move(sock_wrapper));
}
void ArcBluetoothBridge::CloseBluetoothListeningSocket(
BluetoothListeningSocket* ptr) {
auto itr = listening_sockets_.find(ptr);
listening_sockets_.erase(itr);
}
void ArcBluetoothBridge::RfcommConnectDeprecated(
mojom::BluetoothAddressPtr remote_addr,
int32_t channel,
int32_t optval,
RfcommConnectDeprecatedCallback callback) {
if (!IsValidPort(mojom::BluetoothSocketType::TYPE_RFCOMM, channel)) {
LOG(ERROR) << "Invalid channel number " << channel;
std::move(callback).Run(mojom::BluetoothStatus::FAIL,
mojom::RfcommConnectingSocketClientRequest());
return;
}
auto sock_wrapper = CreateBluetoothConnectSocket(
mojom::BluetoothSocketType::TYPE_RFCOMM, optval, std::move(remote_addr),
static_cast<uint16_t>(channel));
if (!sock_wrapper) {
std::move(callback).Run(mojom::BluetoothStatus::FAIL,
mojom::RfcommConnectingSocketClientRequest());
return;
}
sock_wrapper->created_by_deprecated_method = true;
std::move(callback).Run(
mojom::BluetoothStatus::SUCCESS,
sock_wrapper->deprecated_remote.BindNewPipeAndPassReceiver());
sock_wrapper->deprecated_remote.set_disconnect_handler(
base::BindOnce(&ArcBluetoothBridge::CloseBluetoothConnectingSocket,
weak_factory_.GetWeakPtr(), sock_wrapper.get()));
connecting_sockets_.insert(std::move(sock_wrapper));
}
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,
mojom::BluetoothConnectSocketClientRequest());
return;
}
if (!IsValidPort(sock_type, port)) {
LOG(ERROR) << "Invalid port number " << port;
std::move(callback).Run(mojom::BluetoothStatus::FAIL,
mojom::BluetoothConnectSocketClientRequest());
return;
}
int32_t optval = GetSockOptvalFromFlags(sock_type, std::move(sock_flags));
auto sock_wrapper = CreateBluetoothConnectSocket(
sock_type, optval, std::move(remote_addr), static_cast<uint16_t>(port));
if (!sock_wrapper) {
std::move(callback).Run(mojom::BluetoothStatus::FAIL,
mojom::BluetoothConnectSocketClientRequest());
return;
}
std::move(callback).Run(mojom::BluetoothStatus::SUCCESS,
sock_wrapper->remote.BindNewPipeAndPassReceiver());
sock_wrapper->remote.set_disconnect_handler(
base::BindOnce(&ArcBluetoothBridge::CloseBluetoothConnectingSocket,
weak_factory_.GetWeakPtr(), sock_wrapper.get()));
connecting_sockets_.insert(std::move(sock_wrapper));
}
void ArcBluetoothBridge::CloseBluetoothConnectingSocket(
BluetoothConnectingSocket* ptr) {
auto itr = connecting_sockets_.find(ptr);
connecting_sockets_.erase(itr);
}
std::unique_ptr<ArcBluetoothBridge::BluetoothListeningSocket>
ArcBluetoothBridge::CreateBluetoothListenSocket(
mojom::BluetoothSocketType sock_type,
int32_t optval,
uint16_t* port) {
DCHECK(port);
base::ScopedFD sock = OpenBluetoothSocketImpl(sock_type, optval, *port);
if (!sock.is_valid()) {
LOG(ERROR) << "Failed to open listen socket.";
return nullptr;
}
if (listen(sock.get(), /*backlog=*/1) == -1) {
PLOG(ERROR) << "Failed to listen()";
return nullptr;
}
BluetoothSocketAddress local_addr;
socklen_t addr_len = sizeof(local_addr);
if (getsockname(sock.get(), &local_addr.sock, &addr_len) == -1) {
PLOG(ERROR) << "Failed to getsockname()";
return nullptr;
}
auto sock_wrapper = std::make_unique<BluetoothListeningSocket>();
sock_wrapper->sock_type = sock_type;
sock_wrapper->controller = base::FileDescriptorWatcher::WatchReadable(
sock.get(),
base::BindRepeating(&ArcBluetoothBridge::OnBluetoothListeningSocketReady,
weak_factory_.GetWeakPtr(), sock_wrapper.get()));
sock_wrapper->file = std::move(sock);
switch (sock_type) {
case mojom::BluetoothSocketType::TYPE_RFCOMM:
*port = local_addr.rfcomm.rc_channel;
break;
case mojom::BluetoothSocketType::TYPE_L2CAP_LE:
*port = btohs(local_addr.l2cap.l2_psm);
break;
default:
LOG(ERROR) << "Unknown socket type " << sock_type;
return nullptr;
}
return sock_wrapper;
}
void ArcBluetoothBridge::OnBluetoothListeningSocketReady(
ArcBluetoothBridge::BluetoothListeningSocket* sock_wrapper) {
BluetoothSocketAddress sa;
socklen_t addr_len = sizeof(sa);
base::ScopedFD accept_fd(
accept(sock_wrapper->file.get(), &sa.sock, &addr_len));
if (!accept_fd.is_valid()) {
PLOG(ERROR) << "Failed to accept()";
return;
}
if (fcntl(accept_fd.get(), F_SETFL,
O_NONBLOCK | fcntl(accept_fd.get(), F_GETFL)) == -1) {
PLOG(ERROR) << "Failed to fnctl()";
return;
}
mojo::ScopedHandle handle =
mojo::WrapPlatformHandle(mojo::PlatformHandle(std::move(accept_fd)));
// Tells Android we successfully accept() a new connection.
if (sock_wrapper->created_by_deprecated_method) {
auto connection = mojom::BluetoothRfcommConnection::New();
connection->sock = std::move(handle);
connection->addr =
mojom::BluetoothAddress::From<bdaddr_t>(sa.rfcomm.rc_bdaddr);
connection->channel = sa.rfcomm.rc_channel;
sock_wrapper->deprecated_remote->OnAccepted(std::move(connection));
} else {
auto connection = mojom::BluetoothSocketConnection::New();
connection->sock = std::move(handle);
switch (sock_wrapper->sock_type) {
case mojom::BluetoothSocketType::TYPE_RFCOMM:
connection->addr =
mojom::BluetoothAddress::From<bdaddr_t>(sa.rfcomm.rc_bdaddr);
connection->port = sa.rfcomm.rc_channel;
break;
case mojom::BluetoothSocketType::TYPE_L2CAP_LE:
connection->addr =
mojom::BluetoothAddress::From<bdaddr_t>(sa.l2cap.l2_bdaddr);
connection->port = btohs(sa.l2cap.l2_psm);
break;
default:
LOG(ERROR) << "Unknown socket type " << sock_wrapper->sock_type;
return;
}
sock_wrapper->remote->OnAccepted(std::move(connection));
}
}
std::unique_ptr<ArcBluetoothBridge::BluetoothConnectingSocket>
ArcBluetoothBridge::CreateBluetoothConnectSocket(
mojom::BluetoothSocketType sock_type,
int32_t optval,
mojom::BluetoothAddressPtr addr,
uint16_t port) {
base::ScopedFD sock =
OpenBluetoothSocketImpl(sock_type, optval, kAutoSockPort);
if (!sock.is_valid()) {
LOG(ERROR) << "Failed to open connect socket.";
return nullptr;
}
std::string addr_str = addr->To<std::string>();
BluetoothDevice* device = bluetooth_adapter_->GetDevice(addr_str);
if (!device)
return nullptr;
const auto addr_type = device->GetAddressType();
if (addr_type == BluetoothDevice::ADDR_TYPE_UNKNOWN) {
LOG(ERROR) << "Unknown address type.";
return nullptr;
}
BluetoothSocketAddress sa = {};
switch (sock_type) {
case mojom::BluetoothSocketType::TYPE_RFCOMM:
sa.rfcomm.rc_family = AF_BLUETOOTH;
sa.rfcomm.rc_bdaddr = addr->To<bdaddr_t>();
sa.rfcomm.rc_channel = static_cast<uint8_t>(port);
break;
case mojom::BluetoothSocketType::TYPE_L2CAP_LE:
sa.l2cap.l2_family = AF_BLUETOOTH;
sa.l2cap.l2_bdaddr = addr->To<bdaddr_t>();
sa.l2cap.l2_psm = htobs(port);
sa.l2cap.l2_bdaddr_type = addr_type == BluetoothDevice::ADDR_TYPE_PUBLIC
? BDADDR_LE_PUBLIC
: BDADDR_LE_RANDOM;
break;
default:
LOG(ERROR) << "Unknown socket type " << sock_type;
return nullptr;
}
int ret = HANDLE_EINTR(connect(
sock.get(), reinterpret_cast<const struct sockaddr*>(&sa), sizeof(sa)));
auto sock_wrapper =
std::make_unique<ArcBluetoothBridge::BluetoothConnectingSocket>();
sock_wrapper->sock_type = sock_type;
if (ret == 0) {
// connect() returns success immediately.
sock_wrapper->file = std::move(sock);
base::ThreadPool::PostTask(
FROM_HERE,
base::BindOnce(&ArcBluetoothBridge::OnBluetoothConnectingSocketReady,
weak_factory_.GetWeakPtr(), sock_wrapper.get()));
return sock_wrapper;
}
if (errno != EINPROGRESS) {
PLOG(ERROR) << "Failed to connect.";
return nullptr;
}
sock_wrapper->controller = base::FileDescriptorWatcher::WatchWritable(
sock.get(),
base::BindRepeating(&ArcBluetoothBridge::OnBluetoothConnectingSocketReady,
weak_factory_.GetWeakPtr(), sock_wrapper.get()));
sock_wrapper->file = std::move(sock);
return sock_wrapper;
}
void ArcBluetoothBridge::OnBluetoothConnectingSocketReady(
ArcBluetoothBridge::BluetoothConnectingSocket* sock_wrapper) {
// When connect() is ready, we will transfer this fd to Android, and Android
// is responsible for closing it.
base::ScopedFD fd = std::move(sock_wrapper->file);
// Checks whether connect() succeeded.
int err = 0;
socklen_t len = sizeof(err);
int ret = getsockopt(fd.get(), SOL_SOCKET, SO_ERROR, &err, &len);
if (ret != 0 || err != 0) {
LOG(ERROR) << "Failed to connect. err=" << err;
if (sock_wrapper->created_by_deprecated_method)
sock_wrapper->deprecated_remote->OnConnectFailed();
else
sock_wrapper->remote->OnConnectFailed();
return;
}
// Gets peer address.
BluetoothSocketAddress peer_sa;
socklen_t peer_sa_len = sizeof(peer_sa);
if (getpeername(fd.get(), &peer_sa.sock, &peer_sa_len) == -1) {
PLOG(ERROR) << "Failed to getpeername().";
if (sock_wrapper->created_by_deprecated_method)
sock_wrapper->deprecated_remote->OnConnectFailed();
else
sock_wrapper->remote->OnConnectFailed();
return;
}
// Gets our port.
BluetoothSocketAddress local_sa;
socklen_t local_sa_len = sizeof(local_sa);
if (getsockname(fd.get(), &local_sa.sock, &local_sa_len) == -1) {
PLOG(ERROR) << "Failed to getsockname()";
if (sock_wrapper->created_by_deprecated_method)
sock_wrapper->deprecated_remote->OnConnectFailed();
else
sock_wrapper->remote->OnConnectFailed();
return;
}
mojo::ScopedHandle handle =
mojo::WrapPlatformHandle(mojo::PlatformHandle(std::move(fd)));
// Notifies Android.
if (sock_wrapper->created_by_deprecated_method) {
auto connection = mojom::BluetoothRfcommConnection::New();
connection->sock = std::move(handle);
connection->addr =
mojom::BluetoothAddress::From<bdaddr_t>(peer_sa.rfcomm.rc_bdaddr);
connection->channel = local_sa.rfcomm.rc_channel;
sock_wrapper->deprecated_remote->OnConnected(std::move(connection));
} else {
auto connection = mojom::BluetoothSocketConnection::New();
connection->sock = std::move(handle);
switch (sock_wrapper->sock_type) {
case mojom::BluetoothSocketType::TYPE_RFCOMM:
connection->addr =
mojom::BluetoothAddress::From<bdaddr_t>(peer_sa.rfcomm.rc_bdaddr);
connection->port = local_sa.rfcomm.rc_channel;
break;
case mojom::BluetoothSocketType::TYPE_L2CAP_LE:
connection->addr =
mojom::BluetoothAddress::From<bdaddr_t>(peer_sa.l2cap.l2_bdaddr);
connection->port = btohs(local_sa.l2cap.l2_psm);
break;
default:
LOG(ERROR) << "Unknown socket type " << sock_wrapper->sock_type;
return;
}
sock_wrapper->remote->OnConnected(std::move(connection));
}
}
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