| // 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/chromeos/arc/bluetooth/arc_bluetooth_bridge.h" |
| |
| #include <bluetooth/bluetooth.h> |
| #include <fcntl.h> |
| #include <stddef.h> |
| #include <sys/socket.h> |
| |
| #include <iomanip> |
| #include <string> |
| #include <utility> |
| |
| #include "ash/public/cpp/ash_pref_names.h" |
| #include "base/bind.h" |
| #include "base/callback.h" |
| #include "base/callback_helpers.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/strings/string_number_conversions.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "base/time/time.h" |
| #include "chrome/browser/chromeos/profiles/profile_helper.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "components/arc/arc_bridge_service.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/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 "mojo/edk/embedder/embedder.h" |
| #include "mojo/edk/embedder/scoped_platform_handle.h" |
| |
| using device::BluetoothAdapter; |
| using device::BluetoothAdapterFactory; |
| using device::BluetoothAdvertisement; |
| using device::BluetoothDevice; |
| using device::BluetoothDiscoveryFilter; |
| using device::BluetoothDiscoverySession; |
| using device::BluetoothGattConnection; |
| using device::BluetoothGattNotifySession; |
| using device::BluetoothGattCharacteristic; |
| using device::BluetoothGattDescriptor; |
| 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 { |
| 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; |
| 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::TimeDelta::FromSeconds(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 5 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::TimeDelta::FromSeconds(5); |
| |
| using GattReadCallback = |
| base::OnceCallback<void(arc::mojom::BluetoothGattValuePtr)>; |
| using CreateSdpRecordCallback = |
| base::OnceCallback<void(arc::mojom::BluetoothCreateSdpRecordResultPtr)>; |
| using RemoveSdpRecordCallback = |
| base::OnceCallback<void(arc::mojom::BluetoothStatus)>; |
| |
| // 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. |
| int ConvertGattIdentifierToId(const std::string identifier) { |
| return std::stoi(identifier.substr(identifier.size() - 4), nullptr, 16); |
| } |
| |
| // 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) { |
| 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 = |
| ConvertGattIdentifierToId(attribute->GetIdentifier()); |
| 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( |
| mojo::ConvertTo<arc::mojom::BluetoothGattStatus>(error_code)); |
| } |
| |
| // Common success callback for ReadGattCharacteristic and ReadGattDescriptor |
| void OnGattReadDone(GattReadCallback callback, |
| const std::vector<uint8_t>& result) { |
| arc::mojom::BluetoothGattValuePtr gattValue = |
| arc::mojom::BluetoothGattValue::New(); |
| gattValue->status = arc::mojom::BluetoothGattStatus::GATT_SUCCESS; |
| gattValue->value = result; |
| std::move(callback).Run(std::move(gattValue)); |
| } |
| |
| // Common error callback for ReadGattCharacteristic and ReadGattDescriptor |
| void OnGattReadError(GattReadCallback callback, |
| BluetoothGattService::GattErrorCode error_code) { |
| arc::mojom::BluetoothGattValuePtr gattValue = |
| arc::mojom::BluetoothGattValue::New(); |
| gattValue->status = |
| mojo::ConvertTo<arc::mojom::BluetoothGattStatus>(error_code); |
| std::move(callback).Run(std::move(gattValue)); |
| } |
| |
| // Callback function for mojom::BluetoothInstance::RequestGattRead |
| void OnGattServerRead( |
| const BluetoothLocalGattService::Delegate::ValueCallback& success_callback, |
| const BluetoothLocalGattService::Delegate::ErrorCallback& error_callback, |
| arc::mojom::BluetoothGattStatus status, |
| const std::vector<uint8_t>& value) { |
| if (status == arc::mojom::BluetoothGattStatus::GATT_SUCCESS) |
| success_callback.Run(value); |
| else |
| error_callback.Run(); |
| } |
| |
| // Callback function for mojom::BluetoothInstance::RequestGattWrite |
| void OnGattServerWrite( |
| const base::Closure& success_callback, |
| const BluetoothLocalGattService::Delegate::ErrorCallback& error_callback, |
| arc::mojom::BluetoothGattStatus status) { |
| if (status == arc::mojom::BluetoothGattStatus::GATT_SUCCESS) |
| success_callback.Run(); |
| else |
| 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 |
| uint16_t GetUUID16(const BluetoothUUID& uuid) { |
| // Convert xxxxyyyy-xxxx-xxxx-xxxx-xxxxxxxxxxxx to int16 yyyy |
| return std::stoi(uuid.canonical_value().substr(4, 4), nullptr, 16); |
| } |
| |
| 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); |
| } |
| |
| } // 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); |
| } |
| |
| template <typename InstanceType, typename HostType> |
| class ArcBluetoothBridge::ConnectionObserverImpl |
| : public ConnectionObserver<InstanceType> { |
| public: |
| ConnectionObserverImpl(ArcBluetoothBridge* owner, |
| ArcBridgeService* arc_bridge_service) |
| : owner_(owner), arc_bridge_service_(arc_bridge_service) { |
| GetHolder()->AddObserver(this); |
| } |
| |
| ~ConnectionObserverImpl() override { GetHolder()->RemoveObserver(this); } |
| |
| protected: |
| ConnectionHolder<InstanceType, HostType>* GetHolder(); |
| |
| ArcBridgeService* arc_bridge_service() { return arc_bridge_service_; } |
| |
| private: |
| // ConnectionObserver<T>: |
| void OnConnectionReady() override { owner_->MaybeSendInitialPowerChange(); } |
| |
| // Unowned pointer |
| ArcBluetoothBridge* const owner_; |
| ArcBridgeService* const arc_bridge_service_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ConnectionObserverImpl); |
| }; |
| |
| template <> |
| ConnectionHolder<mojom::AppInstance, mojom::AppHost>* |
| ArcBluetoothBridge::ConnectionObserverImpl<mojom::AppInstance, |
| mojom::AppHost>::GetHolder() { |
| return arc_bridge_service()->app(); |
| } |
| |
| template <> |
| ConnectionHolder<mojom::IntentHelperInstance, mojom::IntentHelperHost>* |
| ArcBluetoothBridge::ConnectionObserverImpl< |
| mojom::IntentHelperInstance, |
| mojom::IntentHelperHost>::GetHolder() { |
| return arc_bridge_service()->intent_helper(); |
| } |
| |
| ArcBluetoothBridge::ArcBluetoothBridge(content::BrowserContext* context, |
| ArcBridgeService* bridge_service) |
| : arc_bridge_service_(bridge_service), weak_factory_(this) { |
| arc_bridge_service_->bluetooth()->SetHost(this); |
| arc_bridge_service_->bluetooth()->AddObserver(this); |
| |
| app_observer_ = std::make_unique< |
| ConnectionObserverImpl<mojom::AppInstance, mojom::AppHost>>( |
| this, arc_bridge_service_); |
| intent_helper_observer_ = |
| std::make_unique<ConnectionObserverImpl<mojom::IntentHelperInstance, |
| mojom::IntentHelperHost>>( |
| this, arc_bridge_service_); |
| |
| if (BluetoothAdapterFactory::IsBluetoothSupported()) { |
| VLOG(1) << "Registering bluetooth adapter."; |
| BluetoothAdapterFactory::GetAdapter(base::Bind( |
| &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_->bluetooth()->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); |
| } |
| |
| void ArcBluetoothBridge::OnConnectionReady() { |
| // TODO(hidehiko): Replace this by ConnectionHolder::IsConnected(). |
| is_bluetooth_instance_up_ = true; |
| } |
| |
| void ArcBluetoothBridge::OnConnectionClosed() { |
| is_bluetooth_instance_up_ = false; |
| } |
| |
| void ArcBluetoothBridge::SendDevice(const BluetoothDevice* device) const { |
| auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD( |
| arc_bridge_service_->bluetooth(), OnDeviceFound); |
| if (!bluetooth_instance) |
| return; |
| |
| std::vector<mojom::BluetoothPropertyPtr> properties = |
| GetDeviceProperties(mojom::BluetoothPropertyType::ALL, device); |
| |
| bluetooth_instance->OnDeviceFound(std::move(properties)); |
| |
| if (!(device->GetType() & device::BLUETOOTH_TRANSPORT_LE)) |
| return; |
| |
| base::Optional<int8_t> rssi = device->GetInquiryRSSI(); |
| mojom::BluetoothAddressPtr addr; |
| |
| // We only want to send updated advertise data to Android only when we are |
| // scanning which is checked by the validity of rssi. Here are the 2 cases |
| // that we don't want to send updated advertise data to Android. |
| // 1) Cached found device and 2) rssi became invalid when we stop scanning. |
| if (rssi.has_value()) { |
| auto* btle_instance = ARC_GET_INSTANCE_FOR_METHOD( |
| arc_bridge_service_->bluetooth(), OnLEDeviceFound); |
| if (!btle_instance) |
| return; |
| std::vector<mojom::BluetoothAdvertisingDataPtr> adv_data = |
| GetAdvertisingData(device); |
| addr = mojom::BluetoothAddress::From(device->GetAddress()); |
| btle_instance->OnLEDeviceFound(std::move(addr), rssi.value(), |
| std::move(adv_data)); |
| } |
| |
| if (!device->IsConnected()) |
| return; |
| |
| addr = mojom::BluetoothAddress::From(device->GetAddress()); |
| OnGattConnectStateChanged(std::move(addr), true); |
| } |
| |
| 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) { |
| if (!IsInstanceUp()) |
| return; |
| |
| SendDevice(device); |
| } |
| |
| void ArcBluetoothBridge::DeviceChanged(BluetoothAdapter* adapter, |
| BluetoothDevice* device) { |
| if (!IsInstanceUp()) |
| return; |
| |
| SendDevice(device); |
| |
| if (!(device->GetType() & device::BLUETOOTH_TRANSPORT_LE)) |
| return; |
| |
| auto it = gatt_connection_cache_.find(device->GetAddress()); |
| bool was_connected = it != gatt_connection_cache_.end(); |
| bool is_connected = device->IsConnected(); |
| |
| if (is_connected == was_connected) |
| return; |
| |
| if (is_connected) |
| gatt_connection_cache_.insert(device->GetAddress()); |
| else // was_connected |
| gatt_connection_cache_.erase(it); |
| |
| mojom::BluetoothAddressPtr addr = |
| mojom::BluetoothAddress::From(device->GetAddress()); |
| OnGattConnectStateChanged(std::move(addr), is_connected); |
| } |
| |
| void ArcBluetoothBridge::DeviceAddressChanged(BluetoothAdapter* adapter, |
| BluetoothDevice* device, |
| const std::string& old_address) { |
| if (!IsInstanceUp()) |
| return; |
| |
| if (old_address == device->GetAddress()) |
| return; |
| |
| if (!(device->GetType() & device::BLUETOOTH_TRANSPORT_LE)) |
| return; |
| |
| auto it = gatt_connection_cache_.find(old_address); |
| if (it == gatt_connection_cache_.end()) |
| return; |
| |
| gatt_connection_cache_.erase(it); |
| gatt_connection_cache_.insert(device->GetAddress()); |
| |
| auto* btle_instance = ARC_GET_INSTANCE_FOR_METHOD( |
| arc_bridge_service_->bluetooth(), OnLEDeviceAddressChange); |
| if (!btle_instance) |
| return; |
| |
| mojom::BluetoothAddressPtr old_addr = |
| mojom::BluetoothAddress::From(old_address); |
| mojom::BluetoothAddressPtr new_addr = |
| mojom::BluetoothAddress::From(device->GetAddress()); |
| btle_instance->OnLEDeviceAddressChange(std::move(old_addr), |
| std::move(new_addr)); |
| } |
| |
| void ArcBluetoothBridge::DevicePairedChanged(BluetoothAdapter* adapter, |
| BluetoothDevice* device, |
| bool new_paired_status) { |
| if (!IsInstanceUp()) |
| 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::DeviceRemoved(BluetoothAdapter* adapter, |
| BluetoothDevice* device) { |
| if (!IsInstanceUp()) |
| return; |
| |
| DCHECK(adapter); |
| DCHECK(device); |
| |
| mojom::BluetoothAddressPtr addr = |
| mojom::BluetoothAddress::From(device->GetAddress()); |
| OnForgetDone(std::move(addr)); |
| |
| auto it = gatt_connection_cache_.find(device->GetAddress()); |
| if (it == gatt_connection_cache_.end()) |
| return; |
| |
| addr = mojom::BluetoothAddress::From(device->GetAddress()); |
| gatt_connection_cache_.erase(it); |
| OnGattConnectStateChanged(std::move(addr), false); |
| } |
| |
| void ArcBluetoothBridge::GattServiceAdded(BluetoothAdapter* adapter, |
| BluetoothDevice* device, |
| BluetoothRemoteGattService* service) { |
| if (!IsInstanceUp()) |
| return; |
| // Placeholder for GATT client functionality |
| } |
| |
| void ArcBluetoothBridge::GattServiceRemoved( |
| BluetoothAdapter* adapter, |
| BluetoothDevice* device, |
| BluetoothRemoteGattService* service) { |
| if (!IsInstanceUp()) |
| return; |
| // Placeholder for GATT client functionality |
| } |
| |
| void ArcBluetoothBridge::GattServicesDiscovered(BluetoothAdapter* adapter, |
| BluetoothDevice* device) { |
| if (!IsInstanceUp()) |
| 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 (!IsInstanceUp()) |
| return; |
| // Placeholder for GATT client functionality |
| } |
| |
| void ArcBluetoothBridge::GattServiceChanged( |
| BluetoothAdapter* adapter, |
| BluetoothRemoteGattService* service) { |
| if (!IsInstanceUp()) |
| return; |
| // Placeholder for GATT client functionality |
| } |
| |
| void ArcBluetoothBridge::GattCharacteristicAdded( |
| BluetoothAdapter* adapter, |
| BluetoothRemoteGattCharacteristic* characteristic) { |
| if (!IsInstanceUp()) |
| return; |
| // Placeholder for GATT client functionality |
| } |
| |
| void ArcBluetoothBridge::GattCharacteristicRemoved( |
| BluetoothAdapter* adapter, |
| BluetoothRemoteGattCharacteristic* characteristic) { |
| if (!IsInstanceUp()) |
| return; |
| // Placeholder for GATT client functionality |
| } |
| |
| void ArcBluetoothBridge::GattDescriptorAdded( |
| BluetoothAdapter* adapter, |
| BluetoothRemoteGattDescriptor* descriptor) { |
| if (!IsInstanceUp()) |
| return; |
| // Placeholder for GATT client functionality |
| } |
| |
| void ArcBluetoothBridge::GattDescriptorRemoved( |
| BluetoothAdapter* adapter, |
| BluetoothRemoteGattDescriptor* descriptor) { |
| if (!IsInstanceUp()) |
| return; |
| // Placeholder for GATT client functionality |
| } |
| |
| void ArcBluetoothBridge::GattCharacteristicValueChanged( |
| BluetoothAdapter* adapter, |
| BluetoothRemoteGattCharacteristic* characteristic, |
| const std::vector<uint8_t>& value) { |
| if (!IsInstanceUp()) |
| return; |
| |
| auto* btle_instance = ARC_GET_INSTANCE_FOR_METHOD( |
| arc_bridge_service_->bluetooth(), OnGattNotify); |
| if (!btle_instance) |
| return; |
| |
| BluetoothRemoteGattService* service = characteristic->GetService(); |
| 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 = ConvertGattIdentifierToId(service->GetIdentifier()); |
| service_id->id->uuid = service->GetUUID(); |
| |
| mojom::BluetoothGattIDPtr char_id = mojom::BluetoothGattID::New(); |
| char_id->inst_id = ConvertGattIdentifierToId(characteristic->GetIdentifier()); |
| 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 (!IsInstanceUp()) |
| return; |
| // Placeholder for GATT client functionality |
| } |
| |
| template <class LocalGattAttribute> |
| void ArcBluetoothBridge::OnGattAttributeReadRequest( |
| const BluetoothDevice* device, |
| const LocalGattAttribute* attribute, |
| int offset, |
| const ValueCallback& success_callback, |
| const ErrorCallback& error_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)) { |
| error_callback.Run(); |
| 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 */, |
| base::BindOnce(&OnGattServerRead, success_callback, error_callback)); |
| } |
| |
| template <class LocalGattAttribute> |
| void ArcBluetoothBridge::OnGattAttributeWriteRequest( |
| const BluetoothDevice* device, |
| const LocalGattAttribute* attribute, |
| const std::vector<uint8_t>& value, |
| int offset, |
| const base::Closure& success_callback, |
| const 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)) { |
| error_callback.Run(); |
| return; |
| } |
| |
| DCHECK(gatt_handle_.find(attribute->GetIdentifier()) != gatt_handle_.end()); |
| |
| bluetooth_instance->RequestGattWrite( |
| mojom::BluetoothAddress::From(device->GetAddress()), |
| gatt_handle_[attribute->GetIdentifier()], offset, value, |
| base::BindOnce(&OnGattServerWrite, success_callback, error_callback)); |
| } |
| |
| void ArcBluetoothBridge::OnCharacteristicReadRequest( |
| const BluetoothDevice* device, |
| const BluetoothLocalGattCharacteristic* characteristic, |
| int offset, |
| const ValueCallback& callback, |
| const ErrorCallback& error_callback) { |
| OnGattAttributeReadRequest(device, characteristic, offset, callback, |
| error_callback); |
| } |
| |
| void ArcBluetoothBridge::OnCharacteristicWriteRequest( |
| const BluetoothDevice* device, |
| const BluetoothLocalGattCharacteristic* characteristic, |
| const std::vector<uint8_t>& value, |
| int offset, |
| const base::Closure& callback, |
| const ErrorCallback& error_callback) { |
| OnGattAttributeWriteRequest(device, characteristic, value, offset, callback, |
| error_callback); |
| } |
| |
| void ArcBluetoothBridge::OnDescriptorReadRequest( |
| const BluetoothDevice* device, |
| const BluetoothLocalGattDescriptor* descriptor, |
| int offset, |
| const ValueCallback& callback, |
| const ErrorCallback& error_callback) { |
| OnGattAttributeReadRequest(device, descriptor, offset, callback, |
| error_callback); |
| } |
| |
| void ArcBluetoothBridge::OnDescriptorWriteRequest( |
| const BluetoothDevice* device, |
| const BluetoothLocalGattDescriptor* descriptor, |
| const std::vector<uint8_t>& value, |
| int offset, |
| const base::Closure& callback, |
| const ErrorCallback& error_callback) { |
| OnGattAttributeWriteRequest(device, descriptor, value, offset, callback, |
| error_callback); |
| } |
| |
| void ArcBluetoothBridge::OnNotificationsStart( |
| const BluetoothDevice* device, |
| const BluetoothLocalGattCharacteristic* characteristic) {} |
| |
| void ArcBluetoothBridge::OnNotificationsStop( |
| const BluetoothDevice* device, |
| const BluetoothLocalGattCharacteristic* characteristic) {} |
| |
| void ArcBluetoothBridge::EnableAdapter(EnableAdapterCallback callback) { |
| DCHECK(bluetooth_adapter_); |
| if (IsPowerChangeInitiatedByLocal(AdapterPowerState::TURN_ON)) { |
| DequeueLocalPowerChange(AdapterPowerState::TURN_ON); |
| } else { |
| 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)) { |
| DequeueLocalPowerChange(AdapterPowerState::TURN_OFF); |
| } else { |
| if (bluetooth_adapter_->IsPowered()) { |
| EnqueueRemotePowerChange(AdapterPowerState::TURN_OFF, |
| std::move(callback)); |
| return; |
| } |
| } |
| |
| 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::TimeDelta::FromSeconds(timeout), |
| base::Bind(&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::TimeDelta::FromSeconds(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::Bind(&ArcBluetoothBridge::OnSetDiscoverable, |
| weak_factory_.GetWeakPtr(), discoverable, true, timeout), |
| base::Bind(&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) { |
| SetDiscoverable(true, discovery_timeout); |
| } else { |
| OnSetAdapterProperty(mojom::BluetoothStatus::PARM_INVALID, |
| std::move(property)); |
| } |
| } else if (property->is_bdname()) { |
| auto property_clone = property.Clone(); |
| bluetooth_adapter_->SetName( |
| property->get_bdname(), |
| base::Bind(&ArcBluetoothBridge::OnSetAdapterProperty, |
| weak_factory_.GetWeakPtr(), mojom::BluetoothStatus::SUCCESS, |
| base::Passed(&property)), |
| base::Bind(&ArcBluetoothBridge::OnSetAdapterProperty, |
| weak_factory_.GetWeakPtr(), mojom::BluetoothStatus::FAIL, |
| base::Passed(&property_clone))); |
| } else if (property->is_adapter_scan_mode()) { |
| // Android will set adapter scan mode in these 3 situations. |
| // 1) Set to BT_SCAN_MODE_NONE just before turning BT off. |
| // 2) Set to BT_SCAN_MODE_CONNECTABLE just after turning on. |
| // 3) Set to BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE just before set the |
| // discoverable timeout. |
| // Since turning BT off/on implied scan mode none/connectable and setting |
| // discovery timeout implied scan mode discoverable, we don't need to |
| // do anything here. We will just call success callback in this case. |
| 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::GetRemoteDeviceProperty( |
| mojom::BluetoothAddressPtr remote_addr, |
| mojom::BluetoothPropertyType type) { |
| DCHECK(bluetooth_adapter_); |
| auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD( |
| arc_bridge_service_->bluetooth(), OnRemoteDeviceProperties); |
| if (!bluetooth_instance) |
| return; |
| |
| std::string addr_str = remote_addr->To<std::string>(); |
| BluetoothDevice* device = bluetooth_adapter_->GetDevice(addr_str); |
| |
| std::vector<mojom::BluetoothPropertyPtr> properties = |
| GetDeviceProperties(type, device); |
| mojom::BluetoothStatus status = mojom::BluetoothStatus::SUCCESS; |
| |
| if (!device) { |
| VLOG(1) << __func__ << ": device " << addr_str << " not available"; |
| status = mojom::BluetoothStatus::FAIL; |
| } |
| |
| bluetooth_instance->OnRemoteDeviceProperties(status, std::move(remote_addr), |
| std::move(properties)); |
| } |
| |
| void ArcBluetoothBridge::SetRemoteDeviceProperty( |
| mojom::BluetoothAddressPtr remote_addr, |
| mojom::BluetoothPropertyPtr property) { |
| DCHECK(bluetooth_adapter_); |
| auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD( |
| arc_bridge_service_->bluetooth(), OnRemoteDeviceProperties); |
| if (!bluetooth_instance) |
| return; |
| |
| // Unsupported. Only used by Android hidden API, BluetoothDevice.SetAlias(). |
| // And only Android Settings App / Android TV / NFC used that. |
| bluetooth_instance->OnRemoteDeviceProperties( |
| mojom::BluetoothStatus::UNSUPPORTED, std::move(remote_addr), |
| std::vector<mojom::BluetoothPropertyPtr>()); |
| } |
| |
| void ArcBluetoothBridge::GetRemoteServiceRecord( |
| mojom::BluetoothAddressPtr remote_addr, |
| const BluetoothUUID& uuid) { |
| // TODO(smbarber): Implement GetRemoteServiceRecord |
| } |
| |
| void ArcBluetoothBridge::GetRemoteServices( |
| mojom::BluetoothAddressPtr remote_addr) { |
| // TODO(smbarber): Implement GetRemoteServices |
| } |
| |
| void ArcBluetoothBridge::StartDiscovery() { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| DCHECK(bluetooth_adapter_); |
| |
| if (discovery_session_) { |
| LOG(ERROR) << "Discovery session already running; Reset timeout."; |
| discovery_off_timer_.Start(FROM_HERE, kDiscoveryTimeout, |
| base::Bind(&ArcBluetoothBridge::CancelDiscovery, |
| weak_factory_.GetWeakPtr())); |
| SendCachedDevicesFound(); |
| return; |
| } |
| |
| bluetooth_adapter_->StartDiscoverySession( |
| base::Bind(&ArcBluetoothBridge::OnDiscoveryStarted, |
| weak_factory_.GetWeakPtr()), |
| base::Bind(&ArcBluetoothBridge::OnDiscoveryError, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| void ArcBluetoothBridge::CancelDiscovery() { |
| if (!discovery_session_) { |
| return; |
| } |
| |
| discovery_session_->Stop(base::Bind(&ArcBluetoothBridge::OnDiscoveryStopped, |
| weak_factory_.GetWeakPtr()), |
| base::Bind(&ArcBluetoothBridge::OnDiscoveryError, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| 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); |
| SendCachedPairedDevices(); |
| } |
| |
| 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_); |
| |
| auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD( |
| arc_bridge_service_->bluetooth(), OnDiscoveryStateChanged); |
| if (!bluetooth_instance) |
| return; |
| |
| discovery_session_ = std::move(session); |
| |
| // 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::Bind(&ArcBluetoothBridge::CancelDiscovery, |
| weak_factory_.GetWeakPtr())); |
| |
| bluetooth_instance->OnDiscoveryStateChanged( |
| mojom::BluetoothDiscoveryState::STARTED); |
| |
| SendCachedDevicesFound(); |
| } |
| |
| void ArcBluetoothBridge::OnDiscoveryStopped() { |
| auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD( |
| arc_bridge_service_->bluetooth(), OnDiscoveryStateChanged); |
| if (!bluetooth_instance) |
| return; |
| |
| discovery_session_.reset(); |
| discovery_off_timer_.Stop(); |
| |
| bluetooth_instance->OnDiscoveryStateChanged( |
| mojom::BluetoothDiscoveryState::STOPPED); |
| } |
| |
| 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; |
| } |
| |
| // Use the default pairing delegate which is the delegate registered and owned |
| // by ash. |
| BluetoothDevice::PairingDelegate* delegate = |
| bluetooth_adapter_->DefaultPairingDelegate(); |
| |
| if (!delegate) { |
| OnPairedError(std::move(addr), BluetoothDevice::ERROR_FAILED); |
| return; |
| } |
| |
| // If pairing finished successfully, DevicePairedChanged will notify Android |
| // on paired state change event, so DoNothing is passed as a success callback. |
| device->Pair(delegate, base::DoNothing(), |
| base::Bind(&ArcBluetoothBridge::OnPairedError, |
| weak_factory_.GetWeakPtr(), base::Passed(&addr))); |
| } |
| |
| 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::Bind(&ArcBluetoothBridge::OnForgetError, |
| weak_factory_.GetWeakPtr(), base::Passed(&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() { |
| DCHECK(bluetooth_adapter_); |
| if (discovery_session_) { |
| LOG(WARNING) << "Discovery session already running; leaving alone"; |
| SendCachedDevicesFound(); |
| return; |
| } |
| bluetooth_adapter_->StartDiscoverySessionWithFilter( |
| std::make_unique<BluetoothDiscoveryFilter>( |
| device::BLUETOOTH_TRANSPORT_LE), |
| base::Bind(&ArcBluetoothBridge::OnDiscoveryStarted, |
| weak_factory_.GetWeakPtr()), |
| base::Bind(&ArcBluetoothBridge::OnDiscoveryError, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| void ArcBluetoothBridge::StopLEScan() { |
| CancelDiscovery(); |
| } |
| |
| 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::OnGattConnected( |
| mojom::BluetoothAddressPtr addr, |
| std::unique_ptr<BluetoothGattConnection> connection) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| gatt_connections_[addr->To<std::string>()] = std::move(connection); |
| OnGattConnectStateChanged(std::move(addr), true); |
| } |
| |
| void ArcBluetoothBridge::OnGattConnectError( |
| mojom::BluetoothAddressPtr addr, |
| BluetoothDevice::ConnectErrorCode error_code) const { |
| OnGattConnectStateChanged(std::move(addr), false); |
| } |
| |
| void ArcBluetoothBridge::OnGattDisconnected(mojom::BluetoothAddressPtr addr) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| auto it = gatt_connections_.find(addr->To<std::string>()); |
| if (it == gatt_connections_.end()) { |
| LOG(WARNING) << "OnGattDisconnected called, " |
| << "but no gatt connection was found"; |
| } else { |
| gatt_connections_.erase(it); |
| } |
| |
| 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; |
| |
| BluetoothDevice* device = |
| bluetooth_adapter_->GetDevice(remote_addr->To<std::string>()); |
| |
| if (!device) { |
| LOG(ERROR) << "Unknown device " << remote_addr->To<std::string>(); |
| OnGattConnectError(std::move(remote_addr), |
| BluetoothDevice::ConnectErrorCode::ERROR_FAILED); |
| return; |
| } |
| |
| if (device->IsConnected()) { |
| bluetooth_instance->OnLEConnectionStateChange(std::move(remote_addr), true); |
| return; |
| } |
| |
| // Also pass disconnect callback in error case since it would be disconnected |
| // anyway. |
| mojom::BluetoothAddressPtr remote_addr_clone = remote_addr.Clone(); |
| device->CreateGattConnection( |
| base::Bind(&ArcBluetoothBridge::OnGattConnected, |
| weak_factory_.GetWeakPtr(), base::Passed(&remote_addr)), |
| base::Bind(&ArcBluetoothBridge::OnGattConnectError, |
| weak_factory_.GetWeakPtr(), base::Passed(&remote_addr_clone))); |
| } |
| |
| void ArcBluetoothBridge::DisconnectLEDevice( |
| mojom::BluetoothAddressPtr remote_addr) { |
| auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD( |
| arc_bridge_service_->bluetooth(), OnLEConnectionStateChange); |
| if (!bluetooth_instance) |
| return; |
| |
| BluetoothDevice* device = |
| bluetooth_adapter_->GetDevice(remote_addr->To<std::string>()); |
| |
| if (!device || !device->IsConnected()) { |
| bluetooth_instance->OnLEConnectionStateChange(std::move(remote_addr), |
| false); |
| return; |
| } |
| |
| mojom::BluetoothAddressPtr remote_addr_clone = remote_addr.Clone(); |
| device->Disconnect( |
| base::Bind(&ArcBluetoothBridge::OnGattDisconnected, |
| weak_factory_.GetWeakPtr(), base::Passed(&remote_addr)), |
| base::Bind(&ArcBluetoothBridge::OnGattDisconnected, |
| weak_factory_.GetWeakPtr(), base::Passed(&remote_addr_clone))); |
| } |
| |
| 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::OnStartLEListenDone( |
| ArcBluetoothBridge::GattStatusCallback callback, |
| scoped_refptr<BluetoothAdvertisement> advertisement) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| advertisment_ = advertisement; |
| std::move(callback).Run(mojom::BluetoothGattStatus::GATT_SUCCESS); |
| } |
| |
| void ArcBluetoothBridge::OnStartLEListenError( |
| ArcBluetoothBridge::GattStatusCallback callback, |
| BluetoothAdvertisement::ErrorCode error_code) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| advertisment_ = nullptr; |
| std::move(callback).Run(mojom::BluetoothGattStatus::GATT_FAILURE); |
| } |
| |
| void ArcBluetoothBridge::StartLEListen(StartLEListenCallback callback) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| std::unique_ptr<BluetoothAdvertisement::Data> adv_data = |
| std::make_unique<BluetoothAdvertisement::Data>( |
| BluetoothAdvertisement::ADVERTISEMENT_TYPE_BROADCAST); |
| // TODO(crbug.com/730593): Remove AdaptCallbackForRepeating() by updating |
| // the callee interface. |
| auto repeating_callback = |
| base::AdaptCallbackForRepeating(std::move(callback)); |
| bluetooth_adapter_->RegisterAdvertisement( |
| std::move(adv_data), |
| base::Bind(&ArcBluetoothBridge::OnStartLEListenDone, |
| weak_factory_.GetWeakPtr(), repeating_callback), |
| base::Bind(&ArcBluetoothBridge::OnStartLEListenError, |
| weak_factory_.GetWeakPtr(), repeating_callback)); |
| } |
| |
| void ArcBluetoothBridge::OnStopLEListenDone( |
| ArcBluetoothBridge::GattStatusCallback callback) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| advertisment_ = nullptr; |
| std::move(callback).Run(mojom::BluetoothGattStatus::GATT_SUCCESS); |
| } |
| |
| void ArcBluetoothBridge::OnStopLEListenError( |
| ArcBluetoothBridge::GattStatusCallback callback, |
| BluetoothAdvertisement::ErrorCode error_code) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| advertisment_ = nullptr; |
| std::move(callback).Run(mojom::BluetoothGattStatus::GATT_FAILURE); |
| } |
| |
| void ArcBluetoothBridge::StopLEListen(StopLEListenCallback callback) { |
| if (!advertisment_) { |
| OnStopLEListenError( |
| std::move(callback), |
| BluetoothAdvertisement::ErrorCode::ERROR_ADVERTISEMENT_DOES_NOT_EXIST); |
| return; |
| } |
| // TODO(crbug.com/730593): Remove AdaptCallbackForRepeating() by updating |
| // the callee interface. |
| auto repeating_callback = |
| base::AdaptCallbackForRepeating(std::move(callback)); |
| advertisment_->Unregister( |
| base::Bind(&ArcBluetoothBridge::OnStopLEListenDone, |
| weak_factory_.GetWeakPtr(), repeating_callback), |
| base::Bind(&ArcBluetoothBridge::OnStopLEListenError, |
| weak_factory_.GetWeakPtr(), repeating_callback)); |
| } |
| |
| 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); |
| |
| const auto& characteristics = service->GetCharacteristics(); |
| if (characteristics.size() > 0) { |
| const auto& descriptors = characteristics.back()->GetDescriptors(); |
| service_element->start_handle = |
| ConvertGattIdentifierToId(characteristics.front()->GetIdentifier()); |
| service_element->end_handle = ConvertGattIdentifierToId( |
| descriptors.size() > 0 ? descriptors.back()->GetIdentifier() |
| : characteristics.back()->GetIdentifier()); |
| } |
| db.push_back(std::move(service_element)); |
| |
| for (auto* characteristic : characteristics) { |
| mojom::BluetoothGattDBElementPtr characteristic_element = |
| CreateGattDBElement( |
| mojom::BluetoothGattDBAttributeType::BTGATT_DB_CHARACTERISTIC, |
| characteristic); |
| characteristic_element->properties = characteristic->GetProperties(); |
| db.push_back(std::move(characteristic_element)); |
| |
| for (auto* descriptor : characteristic->GetDescriptors()) { |
| db.push_back(CreateGattDBElement( |
| mojom::BluetoothGattDBAttributeType::BTGATT_DB_DESCRIPTOR, |
| descriptor)); |
| } |
| } |
| } |
| |
| 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); |
| |
| 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); |
| |
| // TODO(crbug.com/730593): Remove AdaptCallbackForRepeating() by updating |
| // the callee interface. |
| auto repeating_callback = |
| base::AdaptCallbackForRepeating(std::move(callback)); |
| characteristic->ReadRemoteCharacteristic( |
| base::Bind(&OnGattReadDone, repeating_callback), |
| base::Bind(&OnGattReadError, repeating_callback)); |
| } |
| |
| void ArcBluetoothBridge::WriteGattCharacteristic( |
| mojom::BluetoothAddressPtr remote_addr, |
| mojom::BluetoothGattServiceIDPtr service_id, |
| mojom::BluetoothGattIDPtr char_id, |
| mojom::BluetoothGattValuePtr value, |
| WriteGattCharacteristicCallback callback) { |
| BluetoothRemoteGattCharacteristic* characteristic = FindGattCharacteristic( |
| std::move(remote_addr), std::move(service_id), std::move(char_id)); |
| DCHECK(characteristic); |
| DCHECK(characteristic->GetPermissions() & kGattWritePermission); |
| |
| // TODO(crbug.com/730593): Remove AdaptCallbackForRepeating() by updating |
| // the callee interface. |
| auto repeating_callback = |
| base::AdaptCallbackForRepeating(std::move(callback)); |
| characteristic->WriteRemoteCharacteristic( |
| value->value, base::Bind(&OnGattOperationDone, repeating_callback), |
| base::Bind(&OnGattOperationError, repeating_callback)); |
| } |
| |
| 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); |
| |
| // TODO(crbug.com/730593): Remove AdaptCallbackForRepeating() by updating |
| // the callee interface. |
| auto repeating_callback = |
| base::AdaptCallbackForRepeating(std::move(callback)); |
| descriptor->ReadRemoteDescriptor( |
| base::Bind(&OnGattReadDone, repeating_callback), |
| base::Bind(&OnGattReadError, repeating_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); |
| |
| // To register / deregister GATT notification, we need to |
| // 1) Write to CCC Descriptor to enable/disable the notification |
| // 2) Ask BT hw to register / deregister the notification |
| // The Chrome API groups both steps into one API, and does not support writing |
| // directly to the CCC Descriptor. Therefore, until we fix |
| // https://crbug.com/622832, we return successfully when we encounter this. |
| // TODO(http://crbug.com/622832) |
| if (descriptor->GetUUID() == |
| BluetoothGattDescriptor::ClientCharacteristicConfigurationUuid()) { |
| OnGattOperationDone(std::move(callback)); |
| return; |
| } |
| |
| // TODO(crbug.com/730593): Remove AdaptCallbackForRepeating() by updating |
| // the callee interface. |
| auto repeating_callback = |
| base::AdaptCallbackForRepeating(std::move(callback)); |
| descriptor->WriteRemoteDescriptor( |
| value->value, base::Bind(&OnGattOperationDone, repeating_callback), |
| base::Bind(&OnGattOperationError, repeating_callback)); |
| } |
| |
| 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_); |
| 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) { |
| LOG(WARNING) << __func__ << " Characteristic is not existed."; |
| return; |
| } |
| |
| // TODO(crbug.com/730593): Remove AdaptCallbackForRepeating() by updating |
| // the callee interface. |
| auto repeating_callback = |
| base::AdaptCallbackForRepeating(std::move(callback)); |
| characteristic->StartNotifySession( |
| base::Bind(&ArcBluetoothBridge::OnGattNotifyStartDone, |
| weak_factory_.GetWeakPtr(), repeating_callback, |
| characteristic->GetIdentifier()), |
| base::Bind(&OnGattOperationError, repeating_callback)); |
| } |
| |
| 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) { |
| LOG(WARNING) << __func__ << " Characteristic is not existed."; |
| return; |
| } |
| |
| // TODO(rkc): Return an error code when failing. crbug.com/771055 |
| 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; |
| return; |
| } |
| std::unique_ptr<BluetoothGattNotifySession> notify = std::move(it->second); |
| notification_session_.erase(it); |
| notify->Stop( |
| base::Bind(&OnGattOperationDone, base::Passed(std::move(callback)))); |
| } |
| |
| 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; |
| } |
| std::move(callback).Run( |
| device->GetInquiryRSSI().value_or(mojom::kUnknownPower)); |
| } |
| |
| void ArcBluetoothBridge::OpenBluetoothSocket( |
| OpenBluetoothSocketCallback 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::edk::ScopedPlatformHandle platform_handle{ |
| mojo::edk::PlatformHandle(sock.release())}; |
| MojoHandle wrapped_handle; |
| MojoResult wrap_result = mojo::edk::CreatePlatformHandleWrapper( |
| std::move(platform_handle), &wrapped_handle); |
| if (wrap_result != MOJO_RESULT_OK) { |
| LOG(ERROR) << "Failed to wrap handles. Closing: " << wrap_result; |
| std::move(callback).Run(mojo::ScopedHandle()); |
| return; |
| } |
| mojo::ScopedHandle scoped_handle{mojo::Handle(wrapped_handle)}; |
| |
| std::move(callback).Run(std::move(scoped_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; |
| } |
| base::WeakPtr<BluetoothLocalGattCharacteristic> characteristic = |
| BluetoothLocalGattCharacteristic::Create( |
| uuid, properties, 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; |
| } |
| // Chrome automatically adds a CCC Descriptor to a characteristic when needed. |
| // We will generate a bogus handle for Android. |
| if (uuid == |
| BluetoothGattDescriptor::ClientCharacteristicConfigurationUuid()) { |
| int32_t handle = GetNextGattServerAttributeHandle(); |
| std::move(callback).Run(handle); |
| 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, 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); |
| // TODO(crbug.com/730593): Remove AdaptCallbackForRepeating() by updating |
| // the callee interface. |
| auto repeating_callback = |
| base::AdaptCallbackForRepeating(std::move(callback)); |
| service->Register(base::Bind(&OnGattOperationDone, repeating_callback), |
| base::Bind(&OnGattOperationError, repeating_callback)); |
| } |
| |
| 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); |
| // TODO(crbug.com/730593): Remove AdaptCallbackForRepeating() by updating |
| // the callee interface. |
| auto repeating_callback = |
| base::AdaptCallbackForRepeating(std::move(callback)); |
| service->Unregister(base::Bind(&OnGattOperationDone, repeating_callback), |
| base::Bind(&OnGattOperationError, repeating_callback)); |
| } |
| |
| void ArcBluetoothBridge::DeleteService(int32_t service_handle, |
| DeleteServiceCallback 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); |
| gatt_identifier_.erase(service_handle); |
| 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) {} |
| |
| 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::Bind(&ArcBluetoothBridge::OnGetServiceRecordsDone, |
| weak_factory_.GetWeakPtr(), base::Passed(&remote_addr), |
| target_uuid), |
| base::Bind(&ArcBluetoothBridge::OnGetServiceRecordsError, |
| weak_factory_.GetWeakPtr(), base::Passed(&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; |
| } |
| |
| // TODO(crbug.com/730593): Remove AdaptCallbackForRepeating() by updating |
| // the callee interface. |
| auto repeating_callback = |
| base::AdaptCallbackForRepeating(std::move(callback)); |
| bluetooth_adapter_->CreateServiceRecord( |
| record, base::Bind(&OnCreateServiceRecordDone, repeating_callback), |
| base::Bind(&OnCreateServiceRecordError, repeating_callback)); |
| } |
| |
| void ArcBluetoothBridge::RemoveSdpRecord(uint32_t service_handle, |
| RemoveSdpRecordCallback callback) { |
| // TODO(crbug.com/730593): Remove AdaptCallbackForRepeating() by updating |
| // the callee interface. |
| auto repeating_callback = |
| base::AdaptCallbackForRepeating(std::move(callback)); |
| bluetooth_adapter_->RemoveServiceRecord( |
| service_handle, |
| base::Bind(&OnRemoveServiceRecordDone, repeating_callback), |
| base::Bind(&OnRemoveServiceRecordError, repeating_callback)); |
| } |
| |
| 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) { |
| 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); |
| 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); |
| } |
| |
| void ArcBluetoothBridge::BroadcastAdvertisement( |
| int32_t adv_handle, |
| std::unique_ptr<device::BluetoothAdvertisement::Data> advertisement, |
| BroadcastAdvertisementCallback callback) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| if (advertisements_.find(adv_handle) == advertisements_.end()) { |
| std::move(callback).Run(mojom::BluetoothGattStatus::GATT_FAILURE); |
| return; |
| } |
| |
| if (!advertisements_[adv_handle]) { |
| OnReadyToRegisterAdvertisement(std::move(callback), adv_handle, |
| std::move(advertisement)); |
| return; |
| } |
| |
| // TODO(crbug.com/730593): Remove AdaptCallbackForRepeating() by updating |
| // the callee interface. |
| auto repeating_callback = |
| base::AdaptCallbackForRepeating(std::move(callback)); |
| advertisements_[adv_handle]->Unregister( |
| base::Bind(&ArcBluetoothBridge::OnReadyToRegisterAdvertisement, |
| weak_factory_.GetWeakPtr(), repeating_callback, adv_handle, |
| base::Passed(std::move(advertisement))), |
| base::Bind(&ArcBluetoothBridge::OnRegisterAdvertisementError, |
| weak_factory_.GetWeakPtr(), repeating_callback, adv_handle)); |
| } |
| |
| void ArcBluetoothBridge::ReleaseAdvertisementHandle( |
| 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); |
| return; |
| } |
| |
| if (!advertisements_[adv_handle]) { |
| advertisements_.erase(adv_handle); |
| std::move(callback).Run(mojom::BluetoothGattStatus::GATT_SUCCESS); |
| return; |
| } |
| |
| // TODO(crbug.com/730593): Remove AdaptCallbackForRepeating() by updating |
| // the callee interface. |
| auto repeating_callback = |
| base::AdaptCallbackForRepeating(std::move(callback)); |
| advertisements_[adv_handle]->Unregister( |
| base::Bind(&ArcBluetoothBridge::OnUnregisterAdvertisementDone, |
| weak_factory_.GetWeakPtr(), repeating_callback, adv_handle), |
| base::Bind(&ArcBluetoothBridge::OnUnregisterAdvertisementError, |
| weak_factory_.GetWeakPtr(), repeating_callback, 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_); |
| // TODO(crbug.com/730593): Remove AdaptCallbackForRepeating() by updating |
| // the callee interface. |
| auto repeating_callback = |
| base::AdaptCallbackForRepeating(std::move(callback)); |
| bluetooth_adapter_->RegisterAdvertisement( |
| std::move(data), |
| base::Bind(&ArcBluetoothBridge::OnRegisterAdvertisementDone, |
| weak_factory_.GetWeakPtr(), repeating_callback, adv_handle), |
| base::Bind(&ArcBluetoothBridge::OnRegisterAdvertisementError, |
| weak_factory_.GetWeakPtr(), repeating_callback, 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); |
| } |
| |
| 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); |
| } |
| |
| void ArcBluetoothBridge::OnUnregisterAdvertisementDone( |
| 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); |
| } |
| |
| 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_.erase(adv_handle); |
| std::move(callback).Run(mojom::BluetoothGattStatus::GATT_FAILURE); |
| } |
| |
| void ArcBluetoothBridge::OnDiscoveryError() { |
| LOG(WARNING) << "failed to change discovery state"; |
| } |
| |
| 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) 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::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::MaybeSendInitialPowerChange() { |
| 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::Bind(&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::Bind(&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); |
| // TODO(crbug.com/730593): Remove AdaptCallbackForRepeating() by updating |
| // the callee interface. |
| auto repeating_callback = |
| base::AdaptCallbackForRepeating(std::move(callback)); |
| BLUETOOTH_LOG(EVENT) << "ARC bluetooth set power: " << turn_on; |
| bluetooth_adapter_->SetPowered( |
| turn_on, |
| base::Bind(turn_on ? &ArcBluetoothBridge::OnPoweredOn |
| : &ArcBluetoothBridge::OnPoweredOff, |
| weak_factory_.GetWeakPtr(), repeating_callback, |
| true /* save_user_pref */), |
| base::Bind(&ArcBluetoothBridge::OnPoweredError, |
| weak_factory_.GetWeakPtr(), repeating_callback)); |
| } |
| |
| void ArcBluetoothBridge::DequeueRemotePowerChange( |
| ArcBluetoothBridge::AdapterPowerState powered) { |
| remote_power_changes_.pop(); |
| } |
| |
| 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) { |
| base::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 |
| const BluetoothDevice::UUIDSet& uuid_set = device->GetUUIDs(); |
| 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()) { |
| 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 = GetUUID16(uuid); |
| |
| 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::SendCachedDevicesFound() const { |
| DCHECK(bluetooth_adapter_); |
| |
| // Send devices that have already been discovered, but aren't connected. |
| BluetoothAdapter::DeviceList devices = bluetooth_adapter_->GetDevices(); |
| for (auto* device : devices) { |
| if (device->IsPaired()) |
| continue; |
| |
| SendDevice(device); |
| } |
| } |
| |
| void ArcBluetoothBridge::SendCachedPairedDevices() const { |
| DCHECK(bluetooth_adapter_); |
| |
| BluetoothAdapter::DeviceList devices = bluetooth_adapter_->GetDevices(); |
| for (auto* device : devices) { |
| if (!device->IsPaired()) |
| continue; |
| |
| SendDevice(device); |
| |
| // OnBondStateChanged must be called with mojom::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. |
| mojom::BluetoothAddressPtr addr = |
| mojom::BluetoothAddress::From(device->GetAddress()); |
| OnPairing(addr->Clone()); |
| OnPairedDone(std::move(addr)); |
| } |
| } |
| |
| 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); |
| } |
| |
| } // namespace arc |