diff --git a/chrome/browser/ash/BUILD.gn b/chrome/browser/ash/BUILD.gn index aac7dfb1a3d41..2cbaaf39afef9 100644 --- a/chrome/browser/ash/BUILD.gn +++ b/chrome/browser/ash/BUILD.gn @@ -266,6 +266,10 @@ source_set("ash") { "arc/bluetooth/arc_bluetooth_bridge.h", "arc/bluetooth/arc_bluetooth_task_queue.cc", "arc/bluetooth/arc_bluetooth_task_queue.h", + "arc/bluetooth/arc_bluez_bridge.cc", + "arc/bluetooth/arc_bluez_bridge.h", + "arc/bluetooth/arc_floss_bridge.cc", + "arc/bluetooth/arc_floss_bridge.h", "arc/boot_phase_monitor/arc_boot_phase_monitor_bridge.cc", "arc/boot_phase_monitor/arc_boot_phase_monitor_bridge.h", "arc/enterprise/arc_data_snapshotd_delegate.cc", @@ -4814,6 +4818,7 @@ source_set("unit_tests") { "arc/arc_util_unittest.cc", "arc/bluetooth/arc_bluetooth_bridge_unittest.cc", "arc/bluetooth/arc_bluetooth_task_queue_unittest.cc", + "arc/bluetooth/arc_floss_bridge_unittest.cc", "arc/boot_phase_monitor/arc_boot_phase_monitor_bridge_unittest.cc", "arc/enterprise/arc_data_snapshotd_delegate_unittest.cc", "arc/enterprise/arc_enterprise_reporting_service_unittest.cc", diff --git a/chrome/browser/ash/arc/bluetooth/arc_bluetooth_bridge.cc b/chrome/browser/ash/arc/bluetooth/arc_bluetooth_bridge.cc index 8ccdcbc4d836b..f0bc71eee81a9 100644 --- a/chrome/browser/ash/arc/bluetooth/arc_bluetooth_bridge.cc +++ b/chrome/browser/ash/arc/bluetooth/arc_bluetooth_bridge.cc @@ -32,6 +32,8 @@ #include "base/task/sequenced_task_runner.h" #include "base/task/thread_pool.h" #include "base/time/time.h" +#include "chrome/browser/ash/arc/bluetooth/arc_bluez_bridge.h" +#include "chrome/browser/ash/arc/bluetooth/arc_floss_bridge.h" #include "chrome/browser/ash/profiles/profile_helper.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/webui/ash/bluetooth_pairing_dialog.h" @@ -161,8 +163,6 @@ 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 @@ -191,13 +191,6 @@ constexpr base::TimeDelta kPowerIntentTimeout = base::Seconds(8); // Client name for logging in BLE scanning. constexpr char kScanClientName[] = "ARC"; -using GattReadCallback = - base::OnceCallback; -using CreateSdpRecordCallback = - base::OnceCallback; -using RemoveSdpRecordCallback = - base::OnceCallback; - device::BluetoothGattCharacteristic::Permissions ConvertToBlueZGattPermissions( int32_t permissions) { device::BluetoothGattCharacteristic::Permissions result = @@ -303,7 +296,7 @@ void OnGattOperationError(arc::ArcBluetoothBridge::GattStatusCallback callback, // Common callback (success and error) for ReadGattCharacteristic and // ReadGattDescriptor. void OnGattRead( - GattReadCallback callback, + arc::ArcBluetoothBridge::GattReadCallback callback, absl::optional error_code, const std::vector& result) { arc::mojom::BluetoothGattValuePtr gattValue = @@ -362,48 +355,6 @@ arc::mojom::BluetoothPropertyPtr GetDiscoveryTimeoutProperty(uint32_t timeout) { return arc::mojom::BluetoothProperty::NewDiscoveryTimeout(timeout); } -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 : @@ -434,22 +385,41 @@ namespace arc { namespace { // Singleton factory for ArcAccessibilityHelperBridge. -class ArcBluetoothBridgeFactory +class ArcBluezBridgeFactory + : public internal::ArcBrowserContextKeyedServiceFactoryBase< + ArcBluezBridge, + ArcBluezBridgeFactory> { + public: + // Factory name used by ArcBrowserContextKeyedServiceFactoryBase. + static constexpr const char* kName = "ArcBluezBridgeFactory"; + + static ArcBluezBridgeFactory* GetInstance() { + return base::Singleton::get(); + } + + private: + friend base::DefaultSingletonTraits; + ArcBluezBridgeFactory() = default; + ~ArcBluezBridgeFactory() override = default; +}; + +// Singleton factory for ArcAccessibilityHelperBridge. +class ArcFlossBridgeFactory : public internal::ArcBrowserContextKeyedServiceFactoryBase< - ArcBluetoothBridge, - ArcBluetoothBridgeFactory> { + ArcFlossBridge, + ArcFlossBridgeFactory> { public: // Factory name used by ArcBrowserContextKeyedServiceFactoryBase. - static constexpr const char* kName = "ArcBluetoothBridgeFactory"; + static constexpr const char* kName = "ArcFlossBridgeFactory"; - static ArcBluetoothBridgeFactory* GetInstance() { - return base::Singleton::get(); + static ArcFlossBridgeFactory* GetInstance() { + return base::Singleton::get(); } private: - friend base::DefaultSingletonTraits; - ArcBluetoothBridgeFactory() = default; - ~ArcBluetoothBridgeFactory() override = default; + friend base::DefaultSingletonTraits; + ArcFlossBridgeFactory() = default; + ~ArcFlossBridgeFactory() override = default; }; } // namespace @@ -457,7 +427,11 @@ class ArcBluetoothBridgeFactory // static ArcBluetoothBridge* ArcBluetoothBridge::GetForBrowserContext( content::BrowserContext* context) { - return ArcBluetoothBridgeFactory::GetForBrowserContext(context); + if (floss::features::IsFlossEnabled()) { + return ArcFlossBridgeFactory::GetForBrowserContext(context); + } else { + return ArcBluezBridgeFactory::GetForBrowserContext(context); + } } ArcBluetoothBridge::ArcBluetoothBridge(content::BrowserContext* context, @@ -467,11 +441,6 @@ ArcBluetoothBridge::ArcBluetoothBridge(content::BrowserContext* context, arc_bridge_service_->app()->AddObserver(this); arc_bridge_service_->intent_helper()->AddObserver(this); - if (floss::features::IsFlossEnabled()) { - VLOG(1) << "Disabling ArcBluetoothBridge, Floss not yet supported."; - return; - } - if (BluetoothAdapterFactory::IsBluetoothSupported()) { VLOG(1) << "Registering bluetooth adapter."; BluetoothAdapterFactory::Get()->GetAdapter(base::BindOnce( @@ -497,10 +466,7 @@ void ArcBluetoothBridge::OnAdapterInitialized( 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(adapter.get()); + bluetooth_adapter_ = adapter; if (!bluetooth_adapter_->HasObserver(this)) bluetooth_adapter_->AddObserver(this); @@ -512,6 +478,10 @@ void ArcBluetoothBridge::OnAdapterInitialized( arc_bridge_service_->bluetooth()->SetHost(this); } +bluez::BluetoothAdapterBlueZ* ArcBluetoothBridge::GetBluezAdapter() const { + return static_cast(bluetooth_adapter_.get()); +} + void ArcBluetoothBridge::AdapterPoweredChanged(BluetoothAdapter* adapter, bool powered) { AdapterPowerState power_change = @@ -2148,66 +2118,6 @@ void ArcBluetoothBridge::SendIndication(int32_t attribute_handle, 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()); - if (!device) { - OnGetServiceRecordsError(std::move(remote_addr), target_uuid, - bluez::BluetoothServiceRecordBlueZ::ErrorCode:: - ERROR_DEVICE_DISCONNECTED); - return; - } - - bluez::BluetoothDeviceBlueZ* device_bluez = - static_cast(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(); - - // 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()) { @@ -2755,7 +2665,7 @@ ArcBluetoothBridge::GetAdapterProperties( if (type == mojom::BluetoothPropertyType::ALL || type == mojom::BluetoothPropertyType::ADAPTER_DISCOVERY_TIMEOUT) { properties.push_back(mojom::BluetoothProperty::NewDiscoveryTimeout( - bluetooth_adapter_->GetDiscoverableTimeout())); + GetBluezAdapter()->GetDiscoverableTimeout())); } if (type == mojom::BluetoothPropertyType::ALL || type == mojom::BluetoothPropertyType::LOCAL_LE_FEATURES) { @@ -2855,53 +2765,6 @@ ArcBluetoothBridge::GetAdvertisingData(const BluetoothDevice* device) const { return advertising_data; } -void ArcBluetoothBridge::OnGetServiceRecordsDone( - mojom::BluetoothAddressPtr remote_addr, - const BluetoothUUID& target_uuid, - const std::vector& records_bluez) { - auto* sdp_bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD( - arc_bridge_service_->bluetooth(), OnGetSdpRecords); - if (!sdp_bluetooth_instance) - return; - - std::vector 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()); -} - void ArcBluetoothBridge::SetPrimaryUserBluetoothPowerSetting( bool enabled) const { const user_manager::User* const user = diff --git a/chrome/browser/ash/arc/bluetooth/arc_bluetooth_bridge.h b/chrome/browser/ash/arc/bluetooth/arc_bluetooth_bridge.h index f1fa608b3fa5f..2ae078b781c9c 100644 --- a/chrome/browser/ash/arc/bluetooth/arc_bluetooth_bridge.h +++ b/chrome/browser/ash/arc/bluetooth/arc_bluetooth_bridge.h @@ -66,6 +66,12 @@ class ArcBluetoothBridge base::OnceCallback; using AdapterStateCallback = base::OnceCallback; + using GattReadCallback = + base::OnceCallback; + using CreateSdpRecordCallback = + base::OnceCallback; + using RemoveSdpRecordCallback = + base::OnceCallback; // Returns singleton instance for the given BrowserContext, // or nullptr if the browser |context| is not allowed to use ARC. @@ -308,14 +314,6 @@ class ArcBluetoothBridge const std::vector& value, SendIndicationCallback callback) override; - // Bluetooth Mojo host interface - Bluetooth SDP functions - void GetSdpRecords(mojom::BluetoothAddressPtr remote_addr, - const device::BluetoothUUID& target_uuid) override; - void CreateSdpRecord(mojom::BluetoothSdpRecordPtr record_mojo, - CreateSdpRecordCallback callback) override; - void RemoveSdpRecord(uint32_t service_handle, - RemoveSdpRecordCallback callback) override; - // Bluetooth Mojo host interface - Bluetooth socket functions void BluetoothSocketListen(mojom::BluetoothSocketType sock_type, mojom::BluetoothSocketFlagsPtr sock_flags, @@ -340,7 +338,9 @@ class ArcBluetoothBridge int32_t adv_handle, ReleaseAdvertisementHandleCallback callback) override; - private: + protected: + bluez::BluetoothAdapterBlueZ* GetBluezAdapter() const; + void ReserveAdvertisementHandleImpl( ReserveAdvertisementHandleCallback callback); void EnableAdvertisementImpl( @@ -477,15 +477,6 @@ class ArcBluetoothBridge void OnSetDiscoverable(bool discoverable, bool success, uint32_t timeout); void SetDiscoverable(bool discoverable, uint32_t timeout); - void OnGetServiceRecordsDone( - mojom::BluetoothAddressPtr remote_addr, - const device::BluetoothUUID& target_uuid, - const std::vector& records_bluez); - void OnGetServiceRecordsError( - mojom::BluetoothAddressPtr remote_addr, - const device::BluetoothUUID& target_uuid, - bluez::BluetoothServiceRecordBlueZ::ErrorCode error_code); - void OnSetAdapterProperty(mojom::BluetoothStatus success, mojom::BluetoothPropertyPtr property); @@ -606,7 +597,7 @@ class ArcBluetoothBridge ArcBridgeService* const arc_bridge_service_; // Owned by ArcServiceManager. - scoped_refptr bluetooth_adapter_; + scoped_refptr bluetooth_adapter_; scoped_refptr advertisment_; // Discovery session created by StartDiscovery(). std::unique_ptr discovery_session_; diff --git a/chrome/browser/ash/arc/bluetooth/arc_bluetooth_bridge_unittest.cc b/chrome/browser/ash/arc/bluetooth/arc_bluetooth_bridge_unittest.cc index 897a5148a70a3..47e1b11daad67 100644 --- a/chrome/browser/ash/arc/bluetooth/arc_bluetooth_bridge_unittest.cc +++ b/chrome/browser/ash/arc/bluetooth/arc_bluetooth_bridge_unittest.cc @@ -19,6 +19,7 @@ #include "base/system/sys_info.h" #include "base/test/scoped_chromeos_version_info.h" #include "base/test/task_environment.h" +#include "chrome/browser/ash/arc/bluetooth/arc_bluez_bridge.h" #include "device/bluetooth/dbus/bluez_dbus_manager.h" #include "device/bluetooth/dbus/fake_bluetooth_adapter_client.h" #include "device/bluetooth/dbus/fake_bluetooth_device_client.h" @@ -40,6 +41,7 @@ namespace arc { constexpr int kFailureAdvHandle = -1; +// This unittest defaults to testing BlueZ. For Floss, use |ArcFlossBridgeTest|. class ArcBluetoothBridgeTest : public testing::Test { protected: void AddTestDevice() { @@ -115,8 +117,8 @@ class ArcBluetoothBridgeTest : public testing::Test { arc_bridge_service_ = std::make_unique(); // TODO(hidehiko): Use Singleton instance tied to BrowserContext. - arc_bluetooth_bridge_ = std::make_unique( - nullptr, arc_bridge_service_.get()); + arc_bluetooth_bridge_ = + std::make_unique(nullptr, arc_bridge_service_.get()); fake_bluetooth_instance_ = std::make_unique(); arc_bridge_service_->bluetooth()->SetInstance( fake_bluetooth_instance_.get(), 20); diff --git a/chrome/browser/ash/arc/bluetooth/arc_bluez_bridge.cc b/chrome/browser/ash/arc/bluetooth/arc_bluez_bridge.cc new file mode 100644 index 0000000000000..ef1038c57592c --- /dev/null +++ b/chrome/browser/ash/arc/bluetooth/arc_bluez_bridge.cc @@ -0,0 +1,207 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ash/arc/bluetooth/arc_bluez_bridge.h" + +#include "ash/components/arc/bluetooth/bluetooth_type_converters.h" +#include "ash/components/arc/session/arc_bridge_service.h" +#include "device/bluetooth/bluetooth_common.h" +#include "device/bluetooth/bluetooth_device.h" +#include "device/bluetooth/bluez/bluetooth_device_bluez.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 { + +// Bluetooth SDP Service Class ID List Attribute identifier +constexpr uint16_t kServiceClassIDListAttributeID = 0x0001; + +void OnCreateServiceRecordDone( + arc::ArcBluetoothBridge::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( + arc::ArcBluetoothBridge::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( + arc::ArcBluetoothBridge::RemoveSdpRecordCallback callback) { + std::move(callback).Run(arc::mojom::BluetoothStatus::SUCCESS); +} + +void OnRemoveServiceRecordError( + arc::ArcBluetoothBridge::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); +} + +} // namespace + +namespace arc { + +ArcBluezBridge::ArcBluezBridge(content::BrowserContext* context, + ArcBridgeService* bridge_service) + : ArcBluetoothBridge(context, bridge_service) {} + +ArcBluezBridge::~ArcBluezBridge() = default; + +bluez::BluetoothAdapterBlueZ* ArcBluezBridge::GetAdapter() const { + return static_cast(bluetooth_adapter_.get()); +} + +void ArcBluezBridge::GetSdpRecords(mojom::BluetoothAddressPtr remote_addr, + const BluetoothUUID& target_uuid) { + BluetoothDevice* device = + GetAdapter()->GetDevice(remote_addr->To()); + if (!device) { + OnGetServiceRecordsError(std::move(remote_addr), target_uuid, + bluez::BluetoothServiceRecordBlueZ::ErrorCode:: + ERROR_DEVICE_DISCONNECTED); + return; + } + + bluez::BluetoothDeviceBlueZ* device_bluez = + static_cast(device); + + mojom::BluetoothAddressPtr remote_addr_clone = remote_addr.Clone(); + + device_bluez->GetServiceRecords( + base::BindOnce(&ArcBluezBridge::OnGetServiceRecordsDone, + weak_factory_.GetWeakPtr(), std::move(remote_addr), + target_uuid), + base::BindOnce(&ArcBluezBridge::OnGetServiceRecordsError, + weak_factory_.GetWeakPtr(), std::move(remote_addr_clone), + target_uuid)); +} + +void ArcBluezBridge::CreateSdpRecord( + mojom::BluetoothSdpRecordPtr record_mojo, + arc::ArcBluetoothBridge::CreateSdpRecordCallback callback) { + auto record = record_mojo.To(); + + // 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)); + GetAdapter()->CreateServiceRecord( + record, + base::BindOnce(&OnCreateServiceRecordDone, + std::move(split_callback.first)), + base::BindOnce(&OnCreateServiceRecordError, + std::move(split_callback.second))); +} + +void ArcBluezBridge::RemoveSdpRecord(uint32_t service_handle, + RemoveSdpRecordCallback callback) { + auto split_callback = base::SplitOnceCallback(std::move(callback)); + GetAdapter()->RemoveServiceRecord( + service_handle, + base::BindOnce(&OnRemoveServiceRecordDone, + std::move(split_callback.first)), + base::BindOnce(&OnRemoveServiceRecordError, + std::move(split_callback.second))); +} + +void ArcBluezBridge::OnGetServiceRecordsDone( + mojom::BluetoothAddressPtr remote_addr, + const BluetoothUUID& target_uuid, + const std::vector& records_bluez) { + auto* sdp_bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD( + arc_bridge_service_->bluetooth(), OnGetSdpRecords); + if (!sdp_bluetooth_instance) { + return; + } + + std::vector 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 ArcBluezBridge::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()); +} + +} // namespace arc diff --git a/chrome/browser/ash/arc/bluetooth/arc_bluez_bridge.h b/chrome/browser/ash/arc/bluetooth/arc_bluez_bridge.h new file mode 100644 index 0000000000000..d03cd810833c2 --- /dev/null +++ b/chrome/browser/ash/arc/bluetooth/arc_bluez_bridge.h @@ -0,0 +1,52 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +#ifndef CHROME_BROWSER_ASH_ARC_BLUETOOTH_ARC_BLUEZ_BRIDGE_H_ +#define CHROME_BROWSER_ASH_ARC_BLUETOOTH_ARC_BLUEZ_BRIDGE_H_ + +#include "ash/components/arc/mojom/bluetooth.mojom.h" +#include "chrome/browser/ash/arc/bluetooth/arc_bluetooth_bridge.h" +#include "device/bluetooth/bluez/bluetooth_adapter_bluez.h" + +namespace arc { + +// Bluez specialization for Arc Bluetooth bridge. Use this class whenever the +// common |device::BluetoothAdapter| or |device::BluetoothDevice| class apis are +// insufficient. +class ArcBluezBridge : public ArcBluetoothBridge { + public: + ArcBluezBridge(content::BrowserContext* context, + ArcBridgeService* bridge_service); + ~ArcBluezBridge() override; + + ArcBluezBridge(const ArcBluezBridge&) = delete; + ArcBluezBridge& operator=(const ArcBluezBridge&) = delete; + + // Bluetooth Mojo host interface - Bluetooth SDP functions + void GetSdpRecords(mojom::BluetoothAddressPtr remote_addr, + const device::BluetoothUUID& target_uuid) override; + void CreateSdpRecord(mojom::BluetoothSdpRecordPtr record_mojo, + CreateSdpRecordCallback callback) override; + void RemoveSdpRecord(uint32_t service_handle, + RemoveSdpRecordCallback callback) override; + + protected: + bluez::BluetoothAdapterBlueZ* GetAdapter() const; + + void OnGetServiceRecordsDone( + mojom::BluetoothAddressPtr remote_addr, + const device::BluetoothUUID& target_uuid, + const std::vector& records_bluez); + void OnGetServiceRecordsError( + mojom::BluetoothAddressPtr remote_addr, + const device::BluetoothUUID& target_uuid, + bluez::BluetoothServiceRecordBlueZ::ErrorCode error_code); + + private: + // WeakPtrFactory to use for callbacks. + base::WeakPtrFactory weak_factory_{this}; +}; + +} // namespace arc + +#endif // CHROME_BROWSER_ASH_ARC_BLUETOOTH_ARC_BLUEZ_BRIDGE_H_ diff --git a/chrome/browser/ash/arc/bluetooth/arc_floss_bridge.cc b/chrome/browser/ash/arc/bluetooth/arc_floss_bridge.cc new file mode 100644 index 0000000000000..2dfef9dd7b842 --- /dev/null +++ b/chrome/browser/ash/arc/bluetooth/arc_floss_bridge.cc @@ -0,0 +1,42 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ash/arc/bluetooth/arc_floss_bridge.h" + +using device::BluetoothUUID; + +namespace arc { + +ArcFlossBridge::ArcFlossBridge(content::BrowserContext* context, + ArcBridgeService* bridge_service) + : ArcBluetoothBridge(context, bridge_service) {} + +ArcFlossBridge::~ArcFlossBridge() = default; + +floss::BluetoothAdapterFloss* ArcFlossBridge::GetAdapter() const { + return static_cast(bluetooth_adapter_.get()); +} + +void ArcFlossBridge::GetSdpRecords(mojom::BluetoothAddressPtr remote_addr, + const BluetoothUUID& target_uuid) { + NOTIMPLEMENTED(); +} + +void ArcFlossBridge::CreateSdpRecord(mojom::BluetoothSdpRecordPtr record_mojo, + CreateSdpRecordCallback callback) { + auto result = mojom::BluetoothCreateSdpRecordResult::New(); + result->status = mojom::BluetoothStatus::FAIL; + std::move(callback).Run(std::move(result)); + + NOTIMPLEMENTED(); +} + +void ArcFlossBridge::RemoveSdpRecord(uint32_t service_handle, + RemoveSdpRecordCallback callback) { + std::move(callback).Run(mojom::BluetoothStatus::FAIL); + + NOTIMPLEMENTED(); +} + +} // namespace arc diff --git a/chrome/browser/ash/arc/bluetooth/arc_floss_bridge.h b/chrome/browser/ash/arc/bluetooth/arc_floss_bridge.h new file mode 100644 index 0000000000000..9cfabed4bc6bf --- /dev/null +++ b/chrome/browser/ash/arc/bluetooth/arc_floss_bridge.h @@ -0,0 +1,39 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +#ifndef CHROME_BROWSER_ASH_ARC_BLUETOOTH_ARC_FLOSS_BRIDGE_H_ +#define CHROME_BROWSER_ASH_ARC_BLUETOOTH_ARC_FLOSS_BRIDGE_H_ + +#include "ash/components/arc/mojom/bluetooth.mojom.h" +#include "chrome/browser/ash/arc/bluetooth/arc_bluetooth_bridge.h" +#include "device/bluetooth/floss/bluetooth_adapter_floss.h" + +namespace arc { + +// Floss specialization for Arc Bluetooth bridge. Use this class whenever the +// common |device::BluetoothAdapter| or |device::BluetoothDevice| class apis are +// insufficient. +class ArcFlossBridge : public ArcBluetoothBridge { + public: + ArcFlossBridge(content::BrowserContext* context, + ArcBridgeService* bridge_service); + ~ArcFlossBridge() override; + + ArcFlossBridge(const ArcFlossBridge&) = delete; + ArcFlossBridge& operator=(const ArcFlossBridge&) = delete; + + // Bluetooth Mojo host interface - Bluetooth SDP functions + void GetSdpRecords(mojom::BluetoothAddressPtr remote_addr, + const device::BluetoothUUID& target_uuid) override; + void CreateSdpRecord(mojom::BluetoothSdpRecordPtr record_mojo, + CreateSdpRecordCallback callback) override; + void RemoveSdpRecord(uint32_t service_handle, + RemoveSdpRecordCallback callback) override; + + protected: + floss::BluetoothAdapterFloss* GetAdapter() const; +}; + +} // namespace arc + +#endif // CHROME_BROWSER_ASH_ARC_BLUETOOTH_ARC_FLOSS_BRIDGE_H_ diff --git a/chrome/browser/ash/arc/bluetooth/arc_floss_bridge_unittest.cc b/chrome/browser/ash/arc/bluetooth/arc_floss_bridge_unittest.cc new file mode 100644 index 0000000000000..6fc3d564274ce --- /dev/null +++ b/chrome/browser/ash/arc/bluetooth/arc_floss_bridge_unittest.cc @@ -0,0 +1,37 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ash/arc/bluetooth/arc_floss_bridge.h" + +#include + +#include "ash/components/arc/session/arc_bridge_service.h" +#include "base/test/task_environment.h" +#include "chrome/browser/ash/arc/bluetooth/arc_bluetooth_bridge.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +namespace arc { + +class ArcFlossBridgeTest : public testing::Test { + public: + void SetUp() override { + arc_bridge_service_ = std::make_unique(); + // TODO: Use Singleton instance tied to BrowserContext. + arc_bluetooth_bridge_ = + std::make_unique(nullptr, arc_bridge_service_.get()); + } + + void TearDown() override { + arc_bluetooth_bridge_.reset(); + arc_bridge_service_.reset(); + } + + std::unique_ptr arc_bridge_service_; + std::unique_ptr arc_bluetooth_bridge_; + base::test::SingleThreadTaskEnvironment task_environment_; +}; + +TEST_F(ArcFlossBridgeTest, Noop) {} + +} // namespace arc