| // Copyright 2015 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "device/bluetooth/bluetooth_device_android.h" |
| |
| #include <jni.h> |
| |
| #include <vector> |
| |
| #include "base/android/jni_android.h" |
| #include "base/android/jni_array.h" |
| #include "base/android/jni_string.h" |
| #include "base/android/scoped_java_ref.h" |
| #include "base/containers/flat_set.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "base/notimplemented.h" |
| #include "base/stl_util.h" |
| #include "base/task/sequenced_task_runner.h" |
| #include "device/bluetooth/android/outcome.h" |
| #include "device/bluetooth/bluetooth_adapter_android.h" |
| #include "device/bluetooth/bluetooth_common.h" |
| #include "device/bluetooth/bluetooth_remote_gatt_service_android.h" |
| #include "device/bluetooth/bluetooth_socket_android.h" |
| // Must come after all headers that specialize FromJniType() / ToJniType(). |
| #include "device/bluetooth/jni_headers/ChromeBluetoothDevice_jni.h" |
| |
| using base::android::AttachCurrentThread; |
| using base::android::ConvertJavaStringToUTF8; |
| using base::android::JavaRef; |
| |
| namespace device { |
| |
| class BluetoothSocketThread; |
| |
| std::unique_ptr<BluetoothDeviceAndroid> BluetoothDeviceAndroid::Create( |
| BluetoothAdapterAndroid* adapter, |
| const JavaRef<jobject>& |
| bluetooth_device_wrapper, // Java Type: bluetoothDeviceWrapper |
| scoped_refptr<base::SequencedTaskRunner> task_runner, |
| scoped_refptr<BluetoothSocketThread> socket_thread) { |
| std::unique_ptr<BluetoothDeviceAndroid> device( |
| new BluetoothDeviceAndroid(adapter, task_runner, socket_thread)); |
| |
| device->j_device_.Reset(Java_ChromeBluetoothDevice_create( |
| AttachCurrentThread(), reinterpret_cast<intptr_t>(device.get()), |
| bluetooth_device_wrapper)); |
| |
| device->LoadInitialCachedMetadata(); |
| |
| return device; |
| } |
| |
| BluetoothDeviceAndroid::~BluetoothDeviceAndroid() { |
| Java_ChromeBluetoothDevice_onBluetoothDeviceAndroidDestruction( |
| AttachCurrentThread(), j_device_); |
| } |
| |
| base::android::ScopedJavaLocalRef<jobject> |
| BluetoothDeviceAndroid::GetJavaObject() { |
| return base::android::ScopedJavaLocalRef<jobject>(j_device_); |
| } |
| |
| void BluetoothDeviceAndroid::LoadInitialCachedMetadata() { |
| CHECK(adapter_->IsPowered()); |
| |
| // We call a few getters in which cached metadata are updated. |
| GetName(); |
| GetAddress(); |
| GetUUIDs(); |
| GetBluetoothClass(); |
| IsPaired(); |
| } |
| |
| uint32_t BluetoothDeviceAndroid::GetBluetoothClass() const { |
| if (adapter_->IsPowered()) { |
| cached_class_ = Java_ChromeBluetoothDevice_getBluetoothClass( |
| AttachCurrentThread(), j_device_); |
| } |
| return cached_class_; |
| } |
| |
| BluetoothTransport BluetoothDeviceAndroid::GetType() const { |
| if (adapter_->IsPowered()) { |
| // Device types in Android BluetoothDevice share the same value as |
| // BluetoothTransport. |
| cached_type_ = static_cast<BluetoothTransport>( |
| Java_ChromeBluetoothDevice_getType(AttachCurrentThread(), j_device_)); |
| } |
| return cached_type_; |
| } |
| |
| std::string BluetoothDeviceAndroid::GetAddress() const { |
| return ConvertJavaStringToUTF8( |
| Java_ChromeBluetoothDevice_getAddress(AttachCurrentThread(), j_device_)); |
| } |
| |
| BluetoothDevice::AddressType BluetoothDeviceAndroid::GetAddressType() const { |
| NOTIMPLEMENTED(); |
| return ADDR_TYPE_UNKNOWN; |
| } |
| |
| BluetoothDevice::VendorIDSource BluetoothDeviceAndroid::GetVendorIDSource() |
| const { |
| // Android API does not provide Vendor ID. |
| return VENDOR_ID_UNKNOWN; |
| } |
| |
| uint16_t BluetoothDeviceAndroid::GetVendorID() const { |
| // Android API does not provide Vendor ID. |
| return 0; |
| } |
| |
| uint16_t BluetoothDeviceAndroid::GetProductID() const { |
| // Android API does not provide Product ID. |
| return 0; |
| } |
| |
| uint16_t BluetoothDeviceAndroid::GetDeviceID() const { |
| // Android API does not provide Device ID. |
| return 0; |
| } |
| |
| uint16_t BluetoothDeviceAndroid::GetAppearance() const { |
| // TODO(crbug.com/41240161): Implementing GetAppearance() |
| // on mac, win, and android platforms for chrome |
| NOTIMPLEMENTED(); |
| return 0; |
| } |
| |
| std::optional<std::string> BluetoothDeviceAndroid::GetName() const { |
| if (adapter_->IsPowered()) { |
| auto name = |
| Java_ChromeBluetoothDevice_getName(AttachCurrentThread(), j_device_); |
| if (name.is_null()) { |
| cached_name_.reset(); |
| } else { |
| cached_name_ = ConvertJavaStringToUTF8(name); |
| } |
| } |
| return cached_name_; |
| } |
| |
| bool BluetoothDeviceAndroid::IsPaired() const { |
| if (adapter_->IsPowered()) { |
| cached_paired_ = |
| Java_ChromeBluetoothDevice_isPaired(AttachCurrentThread(), j_device_); |
| } |
| return cached_paired_; |
| } |
| |
| bool BluetoothDeviceAndroid::IsConnected() const { |
| return IsGattConnected() || (connected_transport_ && adapter_->IsPowered()); |
| } |
| |
| bool BluetoothDeviceAndroid::IsGattConnected() const { |
| return gatt_connected_; |
| } |
| |
| bool BluetoothDeviceAndroid::IsConnectable() const { |
| NOTIMPLEMENTED(); |
| return false; |
| } |
| |
| bool BluetoothDeviceAndroid::IsConnecting() const { |
| NOTIMPLEMENTED(); |
| return false; |
| } |
| |
| BluetoothDevice::UUIDSet BluetoothDeviceAndroid::GetUUIDs() const { |
| BluetoothTransport device_type = GetType(); |
| if (device_type == BLUETOOTH_TRANSPORT_LE || |
| device_type == BLUETOOTH_TRANSPORT_INVALID) { |
| return BluetoothDevice::GetUUIDs(); |
| } |
| |
| if (adapter_->IsPowered()) { |
| // Java type: String[] |
| base::android::ScopedJavaLocalRef<jobjectArray> sdp_uuids = |
| Java_ChromeBluetoothDevice_getUuids(AttachCurrentThread(), j_device_); |
| std::vector<std::string> sdp_uuid_strings; |
| base::android::AppendJavaStringArrayToStringVector( |
| AttachCurrentThread(), sdp_uuids, &sdp_uuid_strings); |
| for (std::string& uuid : sdp_uuid_strings) { |
| cached_sdp_uuids_.insert(BluetoothUUID(std::move(uuid))); |
| } |
| } |
| |
| if (device_type == BLUETOOTH_TRANSPORT_CLASSIC) { |
| return cached_sdp_uuids_; |
| } |
| |
| // Dual transport device |
| return base::STLSetUnion<BluetoothDevice::UUIDSet>( |
| cached_sdp_uuids_, BluetoothDevice::GetUUIDs()); |
| } |
| |
| bool BluetoothDeviceAndroid::ExpectingPinCode() const { |
| NOTIMPLEMENTED(); |
| return false; |
| } |
| |
| bool BluetoothDeviceAndroid::ExpectingPasskey() const { |
| NOTIMPLEMENTED(); |
| return false; |
| } |
| |
| bool BluetoothDeviceAndroid::ExpectingConfirmation() const { |
| NOTIMPLEMENTED(); |
| return false; |
| } |
| |
| void BluetoothDeviceAndroid::GetConnectionInfo( |
| ConnectionInfoCallback callback) { |
| NOTIMPLEMENTED(); |
| std::move(callback).Run(ConnectionInfo()); |
| } |
| |
| void BluetoothDeviceAndroid::SetConnectionLatency( |
| ConnectionLatency connection_latency, |
| base::OnceClosure callback, |
| ErrorCallback error_callback) { |
| NOTIMPLEMENTED(); |
| } |
| |
| void BluetoothDeviceAndroid::Connect(PairingDelegate* pairing_delegate, |
| ConnectCallback callback) { |
| NOTIMPLEMENTED(); |
| } |
| |
| void BluetoothDeviceAndroid::SetPinCode(const std::string& pincode) { |
| NOTIMPLEMENTED(); |
| } |
| |
| void BluetoothDeviceAndroid::SetPasskey(uint32_t passkey) { |
| NOTIMPLEMENTED(); |
| } |
| |
| void BluetoothDeviceAndroid::ConfirmPairing() { |
| NOTIMPLEMENTED(); |
| } |
| |
| void BluetoothDeviceAndroid::RejectPairing() { |
| NOTIMPLEMENTED(); |
| } |
| |
| void BluetoothDeviceAndroid::CancelPairing() { |
| NOTIMPLEMENTED(); |
| } |
| |
| void BluetoothDeviceAndroid::Disconnect(base::OnceClosure callback, |
| ErrorCallback error_callback) { |
| // TODO(scheib): Also update unit tests for BluetoothGattConnection. |
| NOTIMPLEMENTED(); |
| } |
| |
| void BluetoothDeviceAndroid::Forget(base::OnceClosure callback, |
| ErrorCallback error_callback) { |
| NOTIMPLEMENTED(); |
| } |
| |
| void BluetoothDeviceAndroid::ConnectToService( |
| const BluetoothUUID& uuid, |
| ConnectToServiceCallback callback, |
| ConnectToServiceErrorCallback error_callback) { |
| Outcome outcome(Java_ChromeBluetoothDevice_connectToService( |
| AttachCurrentThread(), j_device_, uuid.canonical_value())); |
| if (!outcome) { |
| std::move(error_callback).Run(outcome.GetExceptionMessage()); |
| return; |
| } |
| |
| scoped_refptr<BluetoothSocketAndroid> socket = BluetoothSocketAndroid::Create( |
| outcome.GetResult(), ui_task_runner_, socket_thread_); |
| socket->Connect(base::BindOnce(std::move(callback), socket), |
| std::move(error_callback)); |
| } |
| |
| void BluetoothDeviceAndroid::ConnectToServiceInsecurely( |
| const BluetoothUUID& uuid, |
| ConnectToServiceCallback callback, |
| ConnectToServiceErrorCallback error_callback) { |
| Outcome outcome(Java_ChromeBluetoothDevice_connectToServiceInsecurely( |
| AttachCurrentThread(), j_device_, uuid.canonical_value())); |
| if (!outcome) { |
| std::move(error_callback).Run(outcome.GetExceptionMessage()); |
| return; |
| } |
| |
| scoped_refptr<BluetoothSocketAndroid> socket = BluetoothSocketAndroid::Create( |
| outcome.GetResult(), ui_task_runner_, socket_thread_); |
| socket->Connect(base::BindOnce(std::move(callback), socket), |
| std::move(error_callback)); |
| } |
| |
| void BluetoothDeviceAndroid::OnConnectionStateChange(JNIEnv* env, |
| int32_t status, |
| bool connected) { |
| gatt_connected_ = connected; |
| if (gatt_connected_) { |
| DidConnectGatt(/*error_code=*/std::nullopt); |
| } else if (!create_gatt_connection_callbacks_.empty()) { |
| // We assume that if there are any pending connection callbacks there |
| // was a failed connection attempt. |
| // TODO(ortuno): Return an error code based on |status| |
| // http://crbug.com/578191 |
| DidConnectGatt(ERROR_FAILED); |
| } else { |
| // Otherwise an existing connection was terminated. |
| gatt_services_.clear(); |
| device_uuids_.ClearServiceUUIDs(); |
| SetGattServicesDiscoveryComplete(false); |
| DidDisconnectGatt(); |
| } |
| } |
| |
| void BluetoothDeviceAndroid::OnGattServicesDiscovered(JNIEnv* env) { |
| device_uuids_.ReplaceServiceUUIDs(gatt_services_); |
| SetGattServicesDiscoveryComplete(true); |
| adapter_->NotifyGattServicesDiscovered(this); |
| adapter_->NotifyDeviceChanged(this); |
| } |
| |
| void BluetoothDeviceAndroid::CreateGattRemoteService( |
| JNIEnv* env, |
| const JavaRef<jstring>& instance_id, |
| const JavaRef<jobject>& |
| bluetooth_gatt_service_wrapper) { // BluetoothGattServiceWrapper |
| std::string instance_id_string = ConvertJavaStringToUTF8(env, instance_id); |
| |
| if (gatt_services_.contains(instance_id_string)) |
| return; |
| |
| std::unique_ptr<BluetoothRemoteGattServiceAndroid> service = |
| BluetoothRemoteGattServiceAndroid::Create(GetAndroidAdapter(), this, |
| bluetooth_gatt_service_wrapper, |
| instance_id_string, j_device_); |
| BluetoothRemoteGattServiceAndroid* service_ptr = service.get(); |
| gatt_services_[instance_id_string] = std::move(service); |
| |
| adapter_->NotifyGattServiceAdded(service_ptr); |
| } |
| |
| BluetoothDeviceAndroid::BluetoothDeviceAndroid( |
| BluetoothAdapterAndroid* adapter, |
| scoped_refptr<base::SequencedTaskRunner> ui_task_runner, |
| scoped_refptr<BluetoothSocketThread> socket_thread) |
| : BluetoothDevice(adapter), |
| ui_task_runner_(ui_task_runner), |
| socket_thread_(socket_thread) {} |
| |
| void BluetoothDeviceAndroid::CreateGattConnectionImpl( |
| std::optional<device::BluetoothUUID> service_uuid) { |
| Java_ChromeBluetoothDevice_createGattConnectionImpl(AttachCurrentThread(), |
| j_device_); |
| } |
| |
| void BluetoothDeviceAndroid::DisconnectGatt() { |
| Java_ChromeBluetoothDevice_disconnectGatt(AttachCurrentThread(), j_device_); |
| } |
| |
| void BluetoothDeviceAndroid::UpdateAclConnectState(uint8_t transport, |
| bool connected) { |
| if (connected) { |
| connected_transport_ |= transport; |
| } else { |
| connected_transport_ &= ~transport; |
| } |
| } |
| |
| } // namespace device |
| |
| DEFINE_JNI(ChromeBluetoothDevice) |