diff options
17 files changed, 1015 insertions, 104 deletions
diff --git a/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothDevice.java b/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothDevice.java index 88e290f..b7fd6a1 100644 --- a/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothDevice.java +++ b/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothDevice.java @@ -255,6 +255,44 @@ final class ChromeBluetoothDevice { } }); } + + @Override + public void onDescriptorRead( + final Wrappers.BluetoothGattDescriptorWrapper descriptor, final int status) { + ThreadUtils.runOnUiThread(new Runnable() { + @Override + public void run() { + ChromeBluetoothRemoteGattDescriptor chromeDescriptor = + mWrapperToChromeDescriptorsMap.get(descriptor); + if (chromeDescriptor == null) { + // Android events arriving with no Chrome object is expected rarely: only + // when the event races object destruction. + Log.v(TAG, "onDescriptorRead when chromeDescriptor == null."); + } else { + chromeDescriptor.onDescriptorRead(status); + } + } + }); + } + + @Override + public void onDescriptorWrite( + final Wrappers.BluetoothGattDescriptorWrapper descriptor, final int status) { + ThreadUtils.runOnUiThread(new Runnable() { + @Override + public void run() { + ChromeBluetoothRemoteGattDescriptor chromeDescriptor = + mWrapperToChromeDescriptorsMap.get(descriptor); + if (chromeDescriptor == null) { + // Android events arriving with no Chrome object is expected rarely: only + // when the event races object destruction. + Log.v(TAG, "onDescriptorWrite when chromeDescriptor == null."); + } else { + chromeDescriptor.onDescriptorWrite(status); + } + } + }); + } } // --------------------------------------------------------------------------------------------- diff --git a/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothRemoteGattCharacteristic.java b/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothRemoteGattCharacteristic.java index ea0a4dc..6d19c1f 100644 --- a/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothRemoteGattCharacteristic.java +++ b/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothRemoteGattCharacteristic.java @@ -5,8 +5,6 @@ package org.chromium.device.bluetooth; import android.annotation.TargetApi; -import android.bluetooth.BluetoothGattCharacteristic; -import android.bluetooth.BluetoothGattDescriptor; import android.os.Build; import org.chromium.base.Log; @@ -14,7 +12,6 @@ import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; import java.util.List; -import java.util.UUID; /** * Exposes android.bluetooth.BluetoothGattCharacteristic as necessary @@ -115,51 +112,6 @@ final class ChromeBluetoothRemoteGattCharacteristic { return mCharacteristic.getProperties(); } - // Implements BluetoothRemoteGattCharacteristicAndroid::StartNotifySession. - @CalledByNative - private boolean startNotifySession() { - // Verify properties first, to provide clearest error log. - int properties = mCharacteristic.getProperties(); - boolean hasNotify = (properties & BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0; - boolean hasIndicate = (properties & BluetoothGattCharacteristic.PROPERTY_INDICATE) != 0; - if (!hasNotify && !hasIndicate) { - Log.v(TAG, "startNotifySession failed! Characteristic needs NOTIFY or INDICATE."); - return false; - } - - // Find config descriptor. - Wrappers.BluetoothGattDescriptorWrapper clientCharacteristicConfigurationDescriptor = - mCharacteristic.getDescriptor(UUID.fromString( - "00002902-0000-1000-8000-00805F9B34FB" /* Config's standard UUID*/)); - if (clientCharacteristicConfigurationDescriptor == null) { - Log.v(TAG, "startNotifySession config descriptor failed!"); - return false; - } - - // Request Android route onCharacteristicChanged notifications for this characteristic. - if (!mChromeDevice.mBluetoothGatt.setCharacteristicNotification(mCharacteristic, true)) { - Log.i(TAG, "startNotifySession setCharacteristicNotification failed."); - return false; - } - - // Enable notification on remote device's characteristic: - if (!clientCharacteristicConfigurationDescriptor.setValue(hasNotify - ? BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE - : BluetoothGattDescriptor.ENABLE_INDICATION_VALUE)) { - Log.v(TAG, "startNotifySession descriptor setValue failed!"); - return false; - } - Log.v(TAG, hasNotify ? "startNotifySession NOTIFY." : "startNotifySession INDICATE."); - - if (!mChromeDevice.mBluetoothGatt.writeDescriptor( - clientCharacteristicConfigurationDescriptor)) { - Log.i(TAG, "startNotifySession writeDescriptor failed!"); - return false; - } - - return true; - } - // Implements BluetoothRemoteGattCharacteristicAndroid::ReadRemoteCharacteristic. @CalledByNative private boolean readRemoteCharacteristic() { @@ -184,6 +136,12 @@ final class ChromeBluetoothRemoteGattCharacteristic { return true; } + // Enable or disable the notifications for this characteristic. + @CalledByNative + private boolean setCharacteristicNotification(boolean enabled) { + return mChromeDevice.mBluetoothGatt.setCharacteristicNotification(mCharacteristic, enabled); + } + // Creates objects for all descriptors. Designed only to be called by // BluetoothRemoteGattCharacteristicAndroid::EnsureDescriptorsCreated. @CalledByNative diff --git a/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothRemoteGattDescriptor.java b/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothRemoteGattDescriptor.java index e87b57b..ac7489d 100644 --- a/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothRemoteGattDescriptor.java +++ b/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothRemoteGattDescriptor.java @@ -18,19 +18,14 @@ import org.chromium.base.annotations.JNINamespace; final class ChromeBluetoothRemoteGattDescriptor { private static final String TAG = "Bluetooth"; - // TODO(scheib): Will need c++ pointer eventually: - // private long mNativeBluetoothRemoteGattDescriptorAndroid; + private long mNativeBluetoothRemoteGattDescriptorAndroid; final Wrappers.BluetoothGattDescriptorWrapper mDescriptor; final ChromeBluetoothDevice mChromeDevice; - private ChromeBluetoothRemoteGattDescriptor( - // TODO(scheib): Will need c++ pointer eventually: - // long nativeBluetoothRemoteGattDescriptorAndroid, + private ChromeBluetoothRemoteGattDescriptor(long nativeBluetoothRemoteGattDescriptorAndroid, Wrappers.BluetoothGattDescriptorWrapper descriptorWrapper, ChromeBluetoothDevice chromeDevice) { - // TODO(scheib): Will need c++ pointer eventually: - // mNativeBluetoothRemoteGattDescriptorAndroid = - // nativeBluetoothRemoteGattDescriptorAndroid; + mNativeBluetoothRemoteGattDescriptorAndroid = nativeBluetoothRemoteGattDescriptorAndroid; mDescriptor = descriptorWrapper; mChromeDevice = chromeDevice; @@ -45,11 +40,27 @@ final class ChromeBluetoothRemoteGattDescriptor { @CalledByNative private void onBluetoothRemoteGattDescriptorAndroidDestruction() { Log.v(TAG, "ChromeBluetoothRemoteGattDescriptor Destroyed."); - // TODO(scheib): Will need c++ pointer eventually: - // mNativeBluetoothRemoteGattDescriptorAndroid = 0; + mNativeBluetoothRemoteGattDescriptorAndroid = 0; mChromeDevice.mWrapperToChromeDescriptorsMap.remove(mDescriptor); } + void onDescriptorRead(int status) { + Log.i(TAG, "onDescriptorRead status:%d==%s", status, + status == android.bluetooth.BluetoothGatt.GATT_SUCCESS ? "OK" : "Error"); + if (mNativeBluetoothRemoteGattDescriptorAndroid != 0) { + nativeOnRead( + mNativeBluetoothRemoteGattDescriptorAndroid, status, mDescriptor.getValue()); + } + } + + void onDescriptorWrite(int status) { + Log.i(TAG, "onDescriptorWrite status:%d==%s", status, + status == android.bluetooth.BluetoothGatt.GATT_SUCCESS ? "OK" : "Error"); + if (mNativeBluetoothRemoteGattDescriptorAndroid != 0) { + nativeOnWrite(mNativeBluetoothRemoteGattDescriptorAndroid, status); + } + } + // --------------------------------------------------------------------------------------------- // BluetoothRemoteGattDescriptorAndroid methods implemented in java: @@ -57,12 +68,9 @@ final class ChromeBluetoothRemoteGattDescriptor { // TODO(http://crbug.com/505554): Replace 'Object' with specific type when JNI fixed. @CalledByNative private static ChromeBluetoothRemoteGattDescriptor create( - // TODO(scheib): Will need c++ pointer eventually: - // long nativeBluetoothRemoteGattDescriptorAndroid, - Object bluetoothGattDescriptorWrapper, ChromeBluetoothDevice chromeDevice) { - return new ChromeBluetoothRemoteGattDescriptor( - // TODO(scheib): Will need c++ pointer eventually: - // nativeBluetoothRemoteGattDescriptorAndroid, + long nativeBluetoothRemoteGattDescriptorAndroid, Object bluetoothGattDescriptorWrapper, + ChromeBluetoothDevice chromeDevice) { + return new ChromeBluetoothRemoteGattDescriptor(nativeBluetoothRemoteGattDescriptorAndroid, (Wrappers.BluetoothGattDescriptorWrapper) bluetoothGattDescriptorWrapper, chromeDevice); } @@ -72,4 +80,38 @@ final class ChromeBluetoothRemoteGattDescriptor { private String getUUID() { return mDescriptor.getUuid().toString(); } + + // Implements BluetoothRemoteGattDescriptorAndroid::ReadRemoteDescriptor. + @CalledByNative + private boolean readRemoteDescriptor() { + if (!mChromeDevice.mBluetoothGatt.readDescriptor(mDescriptor)) { + Log.i(TAG, "readRemoteDescriptor readDescriptor failed."); + return false; + } + return true; + } + + // Implements BluetoothRemoteGattDescriptorAndroid::WriteRemoteDescriptor. + @CalledByNative + private boolean writeRemoteDescriptor(byte[] value) { + if (!mDescriptor.setValue(value)) { + Log.i(TAG, "writeRemoteDescriptor setValue failed."); + return false; + } + if (!mChromeDevice.mBluetoothGatt.writeDescriptor(mDescriptor)) { + Log.i(TAG, "writeRemoteDescriptor writeDescriptor failed."); + return false; + } + return true; + } + + // --------------------------------------------------------------------------------------------- + // BluetoothAdapterDevice C++ methods declared for access from java: + + // Binds to BluetoothRemoteGattDescriptorAndroid::OnRead. + native void nativeOnRead( + long nativeBluetoothRemoteGattDescriptorAndroid, int status, byte[] value); + + // Binds to BluetoothRemoteGattDescriptorAndroid::OnWrite. + native void nativeOnWrite(long nativeBluetoothRemoteGattDescriptorAndroid, int status); } diff --git a/device/bluetooth/android/java/src/org/chromium/device/bluetooth/Wrappers.java b/device/bluetooth/android/java/src/org/chromium/device/bluetooth/Wrappers.java index 96634b3..b61c7cd 100644 --- a/device/bluetooth/android/java/src/org/chromium/device/bluetooth/Wrappers.java +++ b/device/bluetooth/android/java/src/org/chromium/device/bluetooth/Wrappers.java @@ -365,6 +365,10 @@ class Wrappers { return mGatt.writeCharacteristic(characteristic.mCharacteristic); } + boolean readDescriptor(BluetoothGattDescriptorWrapper descriptor) { + return mGatt.readDescriptor(descriptor.mDescriptor); + } + boolean writeDescriptor(BluetoothGattDescriptorWrapper descriptor) { return mGatt.writeDescriptor(descriptor.mDescriptor); } @@ -411,6 +415,20 @@ class Wrappers { } @Override + public void onDescriptorRead( + BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { + mWrapperCallback.onDescriptorRead( + mDeviceWrapper.mDescriptorsToWrappers.get(descriptor), status); + } + + @Override + public void onDescriptorWrite( + BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { + mWrapperCallback.onDescriptorWrite( + mDeviceWrapper.mDescriptorsToWrappers.get(descriptor), status); + } + + @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { mWrapperCallback.onConnectionStateChange(status, newState); } @@ -438,6 +456,10 @@ class Wrappers { BluetoothGattCharacteristicWrapper characteristic, int status); public abstract void onCharacteristicWrite( BluetoothGattCharacteristicWrapper characteristic, int status); + public abstract void onDescriptorRead( + BluetoothGattDescriptorWrapper descriptor, int status); + public abstract void onDescriptorWrite( + BluetoothGattDescriptorWrapper descriptor, int status); public abstract void onConnectionStateChange(int status, int newState); public abstract void onServicesDiscovered(int status); } @@ -505,7 +527,7 @@ class Wrappers { mDeviceWrapper.mDescriptorsToWrappers.get(descriptor); if (descriptorWrapper == null) { - descriptorWrapper = new BluetoothGattDescriptorWrapper(descriptor); + descriptorWrapper = new BluetoothGattDescriptorWrapper(descriptor, mDeviceWrapper); mDeviceWrapper.mDescriptorsToWrappers.put(descriptor, descriptorWrapper); } return descriptorWrapper; @@ -521,7 +543,8 @@ class Wrappers { BluetoothGattDescriptorWrapper descriptorWrapper = mDeviceWrapper.mDescriptorsToWrappers.get(descriptor); if (descriptorWrapper == null) { - descriptorWrapper = new BluetoothGattDescriptorWrapper(descriptor); + descriptorWrapper = + new BluetoothGattDescriptorWrapper(descriptor, mDeviceWrapper); mDeviceWrapper.mDescriptorsToWrappers.put(descriptor, descriptorWrapper); } descriptorsWrapped.add(descriptorWrapper); @@ -555,9 +578,16 @@ class Wrappers { */ static class BluetoothGattDescriptorWrapper { private final BluetoothGattDescriptor mDescriptor; + final BluetoothDeviceWrapper mDeviceWrapper; - public BluetoothGattDescriptorWrapper(BluetoothGattDescriptor descriptor) { + public BluetoothGattDescriptorWrapper( + BluetoothGattDescriptor descriptor, BluetoothDeviceWrapper deviceWrapper) { mDescriptor = descriptor; + mDeviceWrapper = deviceWrapper; + } + + public BluetoothGattCharacteristicWrapper getCharacteristic() { + return mDeviceWrapper.mCharacteristicsToWrappers.get(mDescriptor.getCharacteristic()); } public UUID getUuid() { diff --git a/device/bluetooth/bluetooth_gatt_characteristic.cc b/device/bluetooth/bluetooth_gatt_characteristic.cc index f7bb6d8..66c39f6 100644 --- a/device/bluetooth/bluetooth_gatt_characteristic.cc +++ b/device/bluetooth/bluetooth_gatt_characteristic.cc @@ -5,6 +5,7 @@ #include "device/bluetooth/bluetooth_gatt_characteristic.h" #include "base/logging.h" +#include "device/bluetooth/bluetooth_gatt_descriptor.h" namespace device { @@ -24,4 +25,14 @@ BluetoothGattCharacteristic* BluetoothGattCharacteristic::Create( return NULL; } +BluetoothGattDescriptor* BluetoothGattCharacteristic::GetDescriptorForUUID( + const BluetoothUUID& uuid) { + for (BluetoothGattDescriptor* descriptor : GetDescriptors()) { + if (descriptor->GetUUID() == uuid) { + return descriptor; + } + } + return NULL; +} + } // namespace device diff --git a/device/bluetooth/bluetooth_gatt_characteristic.h b/device/bluetooth/bluetooth_gatt_characteristic.h index 8cce601..2eeeeff 100644 --- a/device/bluetooth/bluetooth_gatt_characteristic.h +++ b/device/bluetooth/bluetooth_gatt_characteristic.h @@ -158,6 +158,10 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothGattCharacteristic { virtual BluetoothGattDescriptor* GetDescriptor( const std::string& identifier) const = 0; + // Returns the GATT characteristic descriptor that matches |uuid|. + virtual BluetoothGattDescriptor* GetDescriptorForUUID( + const BluetoothUUID& uuid); + // Adds a characteristic descriptor to the locally hosted characteristic // represented by this instance. This method only makes sense for local // characteristics and won't have an effect if this instance represents a diff --git a/device/bluetooth/bluetooth_gatt_characteristic_unittest.cc b/device/bluetooth/bluetooth_gatt_characteristic_unittest.cc index d320f95..ba3e25b 100644 --- a/device/bluetooth/bluetooth_gatt_characteristic_unittest.cc +++ b/device/bluetooth/bluetooth_gatt_characteristic_unittest.cc @@ -907,14 +907,7 @@ TEST_F(BluetoothGattCharacteristicTest, StartNotifySession_Multiple) { characteristic1_->StartNotifySession( GetNotifyCallback(Call::EXPECTED), GetGattErrorCallback(Call::NOT_EXPECTED)); -#if defined(OS_ANDROID) - // TODO(crbug.com/551634): Decide when implementing IsNotifying if Android - // should trust the notification request always worked, or if we should always - // redundantly set the value to the OS. - EXPECT_EQ(2, gatt_notify_characteristic_attempts_); -#else EXPECT_EQ(1, gatt_notify_characteristic_attempts_); -#endif EXPECT_EQ(0, callback_count_); SimulateGattNotifySessionStarted(characteristic1_); EXPECT_EQ(2, callback_count_); diff --git a/device/bluetooth/bluetooth_gatt_descriptor_unittest.cc b/device/bluetooth/bluetooth_gatt_descriptor_unittest.cc index a2c0bd9..190de7d 100644 --- a/device/bluetooth/bluetooth_gatt_descriptor_unittest.cc +++ b/device/bluetooth/bluetooth_gatt_descriptor_unittest.cc @@ -16,7 +16,42 @@ namespace device { #if defined(OS_ANDROID) || defined(OS_MACOSX) -class BluetoothGattDescriptorTest : public BluetoothTest {}; +class BluetoothGattDescriptorTest : public BluetoothTest { + public: + // Creates adapter_, device_, service_, characteristic_, + // descriptor1_, & descriptor2_. + void FakeDescriptorBoilerplate() { + InitWithFakeAdapter(); + StartLowEnergyDiscoverySession(); + device_ = DiscoverLowEnergyDevice(3); + device_->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED), + GetConnectErrorCallback(Call::NOT_EXPECTED)); + SimulateGattConnection(device_); + std::vector<std::string> services; + std::string uuid("00000000-0000-1000-8000-00805f9b34fb"); + services.push_back(uuid); + SimulateGattServicesDiscovered(device_, services); + ASSERT_EQ(1u, device_->GetGattServices().size()); + service_ = device_->GetGattServices()[0]; + SimulateGattCharacteristic(service_, uuid, 0); + ASSERT_EQ(1u, service_->GetCharacteristics().size()); + characteristic_ = service_->GetCharacteristics()[0]; + SimulateGattDescriptor(characteristic_, + "00000001-0000-1000-8000-00805f9b34fb"); + SimulateGattDescriptor(characteristic_, + "00000002-0000-1000-8000-00805f9b34fb"); + ASSERT_EQ(2u, characteristic_->GetDescriptors().size()); + descriptor1_ = characteristic_->GetDescriptors()[0]; + descriptor2_ = characteristic_->GetDescriptors()[1]; + ResetEventCounts(); + } + + BluetoothDevice* device_ = nullptr; + BluetoothGattService* service_ = nullptr; + BluetoothGattCharacteristic* characteristic_ = nullptr; + BluetoothGattDescriptor* descriptor1_ = nullptr; + BluetoothGattDescriptor* descriptor2_ = nullptr; +}; #endif #if defined(OS_ANDROID) @@ -137,4 +172,425 @@ TEST_F(BluetoothGattDescriptorTest, GetUUID) { } #endif // defined(OS_ANDROID) +#if defined(OS_ANDROID) +// Tests ReadRemoteDescriptor and GetValue with empty value buffer. +TEST_F(BluetoothGattDescriptorTest, ReadRemoteDescriptor_Empty) { + ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate()); + + descriptor1_->ReadRemoteDescriptor(GetReadValueCallback(Call::EXPECTED), + GetGattErrorCallback(Call::NOT_EXPECTED)); + EXPECT_EQ(1, gatt_read_descriptor_attempts_); + std::vector<uint8_t> empty_vector; + SimulateGattDescriptorRead(descriptor1_, empty_vector); + + // Duplicate read reported from OS shouldn't cause a problem: + SimulateGattDescriptorRead(descriptor1_, empty_vector); + + EXPECT_EQ(empty_vector, last_read_value_); + EXPECT_EQ(empty_vector, descriptor1_->GetValue()); +} +#endif // defined(OS_ANDROID) + +#if defined(OS_ANDROID) +// Tests WriteRemoteDescriptor with empty value buffer. +TEST_F(BluetoothGattDescriptorTest, WriteRemoteDescriptor_Empty) { + ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate()); + + std::vector<uint8_t> empty_vector; + descriptor1_->WriteRemoteDescriptor(empty_vector, GetCallback(Call::EXPECTED), + GetGattErrorCallback(Call::NOT_EXPECTED)); + EXPECT_EQ(1, gatt_write_descriptor_attempts_); + SimulateGattDescriptorWrite(descriptor1_); + + EXPECT_EQ(empty_vector, last_write_value_); +} +#endif // defined(OS_ANDROID) + +#if defined(OS_ANDROID) +// Tests ReadRemoteDescriptor completing after Chrome objects are deleted. +TEST_F(BluetoothGattDescriptorTest, ReadRemoteDescriptor_AfterDeleted) { + ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate()); + + descriptor1_->ReadRemoteDescriptor(GetReadValueCallback(Call::NOT_EXPECTED), + GetGattErrorCallback(Call::NOT_EXPECTED)); + + RememberDescriptorForSubsequentAction(descriptor1_); + DeleteDevice(device_); + + std::vector<uint8_t> empty_vector; + SimulateGattDescriptorRead(/* use remembered descriptor */ nullptr, + empty_vector); + EXPECT_TRUE("Did not crash!"); +} +#endif // defined(OS_ANDROID) + +#if defined(OS_ANDROID) +// Tests WriteRemoteDescriptor completing after Chrome objects are deleted. +TEST_F(BluetoothGattDescriptorTest, WriteRemoteDescriptor_AfterDeleted) { + ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate()); + + std::vector<uint8_t> empty_vector; + descriptor1_->WriteRemoteDescriptor(empty_vector, + GetCallback(Call::NOT_EXPECTED), + GetGattErrorCallback(Call::NOT_EXPECTED)); + + RememberDescriptorForSubsequentAction(descriptor1_); + DeleteDevice(device_); + + SimulateGattDescriptorWrite(/* use remembered descriptor */ nullptr); + EXPECT_TRUE("Did not crash!"); +} +#endif // defined(OS_ANDROID) + +#if defined(OS_ANDROID) +// Tests ReadRemoteDescriptor and GetValue with non-empty value buffer. +TEST_F(BluetoothGattDescriptorTest, ReadRemoteDescriptor) { + ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate()); + + descriptor1_->ReadRemoteDescriptor(GetReadValueCallback(Call::EXPECTED), + GetGattErrorCallback(Call::NOT_EXPECTED)); + EXPECT_EQ(1, gatt_read_descriptor_attempts_); + + uint8_t values[] = {0, 1, 2, 3, 4, 0xf, 0xf0, 0xff}; + std::vector<uint8_t> test_vector(values, values + arraysize(values)); + SimulateGattDescriptorRead(descriptor1_, test_vector); + + // Duplicate read reported from OS shouldn't cause a problem: + std::vector<uint8_t> empty_vector; + SimulateGattDescriptorRead(descriptor1_, empty_vector); + + EXPECT_EQ(test_vector, last_read_value_); + EXPECT_EQ(test_vector, descriptor1_->GetValue()); +} +#endif // defined(OS_ANDROID) + +#if defined(OS_ANDROID) +// Tests WriteRemoteDescriptor with non-empty value buffer. +TEST_F(BluetoothGattDescriptorTest, WriteRemoteDescriptor) { + ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate()); + + uint8_t values[] = {0, 1, 2, 3, 4, 0xf, 0xf0, 0xff}; + std::vector<uint8_t> test_vector(values, values + arraysize(values)); + descriptor1_->WriteRemoteDescriptor(test_vector, GetCallback(Call::EXPECTED), + GetGattErrorCallback(Call::NOT_EXPECTED)); + EXPECT_EQ(1, gatt_write_descriptor_attempts_); + + SimulateGattDescriptorWrite(descriptor1_); + + EXPECT_EQ(test_vector, last_write_value_); +} +#endif // defined(OS_ANDROID) + +#if defined(OS_ANDROID) +// Tests ReadRemoteDescriptor and GetValue multiple times. +TEST_F(BluetoothGattDescriptorTest, ReadRemoteDescriptor_Twice) { + ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate()); + + descriptor1_->ReadRemoteDescriptor(GetReadValueCallback(Call::EXPECTED), + GetGattErrorCallback(Call::NOT_EXPECTED)); + EXPECT_EQ(1, gatt_read_descriptor_attempts_); + + uint8_t values[] = {0, 1, 2, 3, 4, 0xf, 0xf0, 0xff}; + std::vector<uint8_t> test_vector(values, values + arraysize(values)); + SimulateGattDescriptorRead(descriptor1_, test_vector); + EXPECT_EQ(1, callback_count_); + EXPECT_EQ(0, error_callback_count_); + EXPECT_EQ(test_vector, last_read_value_); + EXPECT_EQ(test_vector, descriptor1_->GetValue()); + + // Read again, with different value: + ResetEventCounts(); + descriptor1_->ReadRemoteDescriptor(GetReadValueCallback(Call::EXPECTED), + GetGattErrorCallback(Call::NOT_EXPECTED)); + EXPECT_EQ(1, gatt_read_descriptor_attempts_); + std::vector<uint8_t> empty_vector; + SimulateGattDescriptorRead(descriptor1_, empty_vector); + EXPECT_EQ(1, callback_count_); + EXPECT_EQ(0, error_callback_count_); + EXPECT_EQ(empty_vector, last_read_value_); + EXPECT_EQ(empty_vector, descriptor1_->GetValue()); +} +#endif // defined(OS_ANDROID) + +#if defined(OS_ANDROID) +// Tests WriteRemoteDescriptor multiple times. +TEST_F(BluetoothGattDescriptorTest, WriteRemoteDescriptor_Twice) { + ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate()); + + uint8_t values[] = {0, 1, 2, 3, 4, 0xf, 0xf0, 0xff}; + std::vector<uint8_t> test_vector(values, values + arraysize(values)); + descriptor1_->WriteRemoteDescriptor(test_vector, GetCallback(Call::EXPECTED), + GetGattErrorCallback(Call::NOT_EXPECTED)); + EXPECT_EQ(1, gatt_write_descriptor_attempts_); + + SimulateGattDescriptorWrite(descriptor1_); + EXPECT_EQ(1, callback_count_); + EXPECT_EQ(0, error_callback_count_); + EXPECT_EQ(test_vector, last_write_value_); + + // Write again, with different value: + ResetEventCounts(); + std::vector<uint8_t> empty_vector; + descriptor1_->WriteRemoteDescriptor(empty_vector, GetCallback(Call::EXPECTED), + GetGattErrorCallback(Call::NOT_EXPECTED)); + EXPECT_EQ(1, gatt_write_descriptor_attempts_); + SimulateGattDescriptorWrite(descriptor1_); + EXPECT_EQ(1, callback_count_); + EXPECT_EQ(0, error_callback_count_); + EXPECT_EQ(empty_vector, last_write_value_); +} +#endif // defined(OS_ANDROID) + +#if defined(OS_ANDROID) +// Tests ReadRemoteDescriptor on two descriptors. +TEST_F(BluetoothGattDescriptorTest, ReadRemoteDescriptor_MultipleDescriptors) { + ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate()); + + descriptor1_->ReadRemoteDescriptor(GetReadValueCallback(Call::EXPECTED), + GetGattErrorCallback(Call::NOT_EXPECTED)); + descriptor2_->ReadRemoteDescriptor(GetReadValueCallback(Call::EXPECTED), + GetGattErrorCallback(Call::NOT_EXPECTED)); + EXPECT_EQ(2, gatt_read_descriptor_attempts_); + EXPECT_EQ(0, callback_count_); + EXPECT_EQ(0, error_callback_count_); + + std::vector<uint8_t> test_vector1; + test_vector1.push_back(111); + SimulateGattDescriptorRead(descriptor1_, test_vector1); + EXPECT_EQ(test_vector1, last_read_value_); + + std::vector<uint8_t> test_vector2; + test_vector2.push_back(222); + SimulateGattDescriptorRead(descriptor2_, test_vector2); + EXPECT_EQ(test_vector2, last_read_value_); + + EXPECT_EQ(2, callback_count_); + EXPECT_EQ(0, error_callback_count_); + EXPECT_EQ(test_vector1, descriptor1_->GetValue()); + EXPECT_EQ(test_vector2, descriptor2_->GetValue()); +} +#endif // defined(OS_ANDROID) + +#if defined(OS_ANDROID) +// Tests WriteRemoteDescriptor on two descriptors. +TEST_F(BluetoothGattDescriptorTest, WriteRemoteDescriptor_MultipleDescriptors) { + ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate()); + + std::vector<uint8_t> test_vector1; + test_vector1.push_back(111); + descriptor1_->WriteRemoteDescriptor(test_vector1, GetCallback(Call::EXPECTED), + GetGattErrorCallback(Call::NOT_EXPECTED)); + EXPECT_EQ(test_vector1, last_write_value_); + + std::vector<uint8_t> test_vector2; + test_vector2.push_back(222); + descriptor2_->WriteRemoteDescriptor(test_vector2, GetCallback(Call::EXPECTED), + GetGattErrorCallback(Call::NOT_EXPECTED)); + EXPECT_EQ(test_vector2, last_write_value_); + + EXPECT_EQ(2, gatt_write_descriptor_attempts_); + EXPECT_EQ(0, callback_count_); + EXPECT_EQ(0, error_callback_count_); + + SimulateGattDescriptorWrite(descriptor1_); + SimulateGattDescriptorWrite(descriptor2_); + + EXPECT_EQ(2, callback_count_); + EXPECT_EQ(0, error_callback_count_); +} +#endif // defined(OS_ANDROID) + +#if defined(OS_ANDROID) +// Tests ReadRemoteDescriptor asynchronous error. +TEST_F(BluetoothGattDescriptorTest, ReadError) { + ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate()); + + descriptor1_->ReadRemoteDescriptor(GetReadValueCallback(Call::NOT_EXPECTED), + GetGattErrorCallback(Call::EXPECTED)); + SimulateGattDescriptorReadError( + descriptor1_, BluetoothGattService::GATT_ERROR_INVALID_LENGTH); + SimulateGattDescriptorReadError(descriptor1_, + BluetoothGattService::GATT_ERROR_FAILED); + EXPECT_EQ(BluetoothGattService::GATT_ERROR_INVALID_LENGTH, + last_gatt_error_code_); +} +#endif // defined(OS_ANDROID) + +#if defined(OS_ANDROID) +// Tests WriteRemoteDescriptor asynchronous error. +TEST_F(BluetoothGattDescriptorTest, WriteError) { + ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate()); + + std::vector<uint8_t> empty_vector; + descriptor1_->WriteRemoteDescriptor(empty_vector, + GetCallback(Call::NOT_EXPECTED), + GetGattErrorCallback(Call::EXPECTED)); + SimulateGattDescriptorWriteError( + descriptor1_, BluetoothGattService::GATT_ERROR_INVALID_LENGTH); + SimulateGattDescriptorWriteError(descriptor1_, + BluetoothGattService::GATT_ERROR_FAILED); + + EXPECT_EQ(BluetoothGattService::GATT_ERROR_INVALID_LENGTH, + last_gatt_error_code_); +} +#endif // defined(OS_ANDROID) + +#if defined(OS_ANDROID) +// Tests ReadRemoteDescriptor synchronous error. +TEST_F(BluetoothGattDescriptorTest, ReadSynchronousError) { + ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate()); + + SimulateGattDescriptorReadWillFailSynchronouslyOnce(descriptor1_); + descriptor1_->ReadRemoteDescriptor(GetReadValueCallback(Call::NOT_EXPECTED), + GetGattErrorCallback(Call::EXPECTED)); + EXPECT_EQ(0, gatt_read_descriptor_attempts_); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(0, callback_count_); + EXPECT_EQ(1, error_callback_count_); + EXPECT_EQ(BluetoothGattService::GATT_ERROR_FAILED, last_gatt_error_code_); + + // After failing once, can succeed: + ResetEventCounts(); + descriptor1_->ReadRemoteDescriptor(GetReadValueCallback(Call::EXPECTED), + GetGattErrorCallback(Call::NOT_EXPECTED)); + EXPECT_EQ(1, gatt_read_descriptor_attempts_); + std::vector<uint8_t> empty_vector; + SimulateGattDescriptorRead(descriptor1_, empty_vector); + EXPECT_EQ(1, callback_count_); + EXPECT_EQ(0, error_callback_count_); +} +#endif // defined(OS_ANDROID) + +#if defined(OS_ANDROID) +// Tests WriteRemoteDescriptor synchronous error. +TEST_F(BluetoothGattDescriptorTest, WriteSynchronousError) { + ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate()); + + SimulateGattDescriptorWriteWillFailSynchronouslyOnce(descriptor1_); + std::vector<uint8_t> empty_vector; + descriptor1_->WriteRemoteDescriptor(empty_vector, + GetCallback(Call::NOT_EXPECTED), + GetGattErrorCallback(Call::EXPECTED)); + EXPECT_EQ(0, gatt_write_descriptor_attempts_); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(0, callback_count_); + EXPECT_EQ(1, error_callback_count_); + EXPECT_EQ(BluetoothGattService::GATT_ERROR_FAILED, last_gatt_error_code_); + + // After failing once, can succeed: + ResetEventCounts(); + descriptor1_->WriteRemoteDescriptor(empty_vector, GetCallback(Call::EXPECTED), + GetGattErrorCallback(Call::NOT_EXPECTED)); + EXPECT_EQ(1, gatt_write_descriptor_attempts_); + SimulateGattDescriptorWrite(descriptor1_); + EXPECT_EQ(1, callback_count_); + EXPECT_EQ(0, error_callback_count_); +} +#endif // defined(OS_ANDROID) + +#if defined(OS_ANDROID) +// Tests ReadRemoteDescriptor error with a pending read operation. +TEST_F(BluetoothGattDescriptorTest, ReadRemoteDescriptor_ReadPending) { + ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate()); + + descriptor1_->ReadRemoteDescriptor(GetReadValueCallback(Call::EXPECTED), + GetGattErrorCallback(Call::NOT_EXPECTED)); + descriptor1_->ReadRemoteDescriptor(GetReadValueCallback(Call::NOT_EXPECTED), + GetGattErrorCallback(Call::EXPECTED)); + + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(0, callback_count_); + EXPECT_EQ(1, error_callback_count_); + EXPECT_EQ(BluetoothGattService::GATT_ERROR_IN_PROGRESS, + last_gatt_error_code_); + + // Initial read should still succeed: + ResetEventCounts(); + std::vector<uint8_t> empty_vector; + SimulateGattDescriptorRead(descriptor1_, empty_vector); + EXPECT_EQ(1, callback_count_); + EXPECT_EQ(0, error_callback_count_); +} +#endif // defined(OS_ANDROID) + +#if defined(OS_ANDROID) +// Tests WriteRemoteDescriptor error with a pending write operation. +TEST_F(BluetoothGattDescriptorTest, WriteRemoteDescriptor_WritePending) { + ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate()); + + std::vector<uint8_t> empty_vector; + descriptor1_->WriteRemoteDescriptor(empty_vector, GetCallback(Call::EXPECTED), + GetGattErrorCallback(Call::NOT_EXPECTED)); + descriptor1_->WriteRemoteDescriptor(empty_vector, + GetCallback(Call::NOT_EXPECTED), + GetGattErrorCallback(Call::EXPECTED)); + + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(0, callback_count_); + EXPECT_EQ(1, error_callback_count_); + EXPECT_EQ(BluetoothGattService::GATT_ERROR_IN_PROGRESS, + last_gatt_error_code_); + + // Initial write should still succeed: + ResetEventCounts(); + SimulateGattDescriptorWrite(descriptor1_); + EXPECT_EQ(1, callback_count_); + EXPECT_EQ(0, error_callback_count_); +} +#endif // defined(OS_ANDROID) + +#if defined(OS_ANDROID) +// Tests ReadRemoteDescriptor error with a pending write operation. +TEST_F(BluetoothGattDescriptorTest, ReadRemoteDescriptor_WritePending) { + ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate()); + + std::vector<uint8_t> empty_vector; + descriptor1_->WriteRemoteDescriptor(empty_vector, GetCallback(Call::EXPECTED), + GetGattErrorCallback(Call::NOT_EXPECTED)); + descriptor1_->ReadRemoteDescriptor(GetReadValueCallback(Call::NOT_EXPECTED), + GetGattErrorCallback(Call::EXPECTED)); + + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(0, callback_count_); + EXPECT_EQ(1, error_callback_count_); + EXPECT_EQ(BluetoothGattService::GATT_ERROR_IN_PROGRESS, + last_gatt_error_code_); + + // Initial write should still succeed: + ResetEventCounts(); + SimulateGattDescriptorWrite(descriptor1_); + EXPECT_EQ(1, callback_count_); + EXPECT_EQ(0, error_callback_count_); +} +#endif // defined(OS_ANDROID) + +#if defined(OS_ANDROID) +// Tests WriteRemoteDescriptor error with a pending Read operation. +TEST_F(BluetoothGattDescriptorTest, WriteRemoteDescriptor_ReadPending) { + ASSERT_NO_FATAL_FAILURE(FakeDescriptorBoilerplate()); + + std::vector<uint8_t> empty_vector; + descriptor1_->ReadRemoteDescriptor(GetReadValueCallback(Call::EXPECTED), + GetGattErrorCallback(Call::NOT_EXPECTED)); + descriptor1_->WriteRemoteDescriptor(empty_vector, + GetCallback(Call::NOT_EXPECTED), + GetGattErrorCallback(Call::EXPECTED)); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(0, callback_count_); + EXPECT_EQ(1, error_callback_count_); + EXPECT_EQ(BluetoothGattService::GATT_ERROR_IN_PROGRESS, + last_gatt_error_code_); + + // Initial read should still succeed: + ResetEventCounts(); + SimulateGattDescriptorRead(descriptor1_, empty_vector); + EXPECT_EQ(1, callback_count_); + EXPECT_EQ(0, error_callback_count_); +} +#endif // defined(OS_ANDROID) + } // namespace device diff --git a/device/bluetooth/bluetooth_remote_gatt_characteristic_android.cc b/device/bluetooth/bluetooth_remote_gatt_characteristic_android.cc index d8d0299..bb7e669 100644 --- a/device/bluetooth/bluetooth_remote_gatt_characteristic_android.cc +++ b/device/bluetooth/bluetooth_remote_gatt_characteristic_android.cc @@ -136,20 +136,60 @@ bool BluetoothRemoteGattCharacteristicAndroid::UpdateValue( void BluetoothRemoteGattCharacteristicAndroid::StartNotifySession( const NotifySessionCallback& callback, const ErrorCallback& error_callback) { - if (Java_ChromeBluetoothRemoteGattCharacteristic_startNotifySession( - AttachCurrentThread(), j_characteristic_.obj())) { - // TODO(crbug.com/569664): Wait until descriptor write completes before - // reporting success via calling |callback|. - scoped_ptr<device::BluetoothGattNotifySession> notify_session( - new BluetoothGattNotifySessionAndroid(instance_id_)); + if (!pending_start_notify_calls_.empty()) { + pending_start_notify_calls_.push(std::make_pair(callback, error_callback)); + return; + } + + Properties properties = GetProperties(); + + bool hasNotify = properties & PROPERTY_NOTIFY; + bool hasIndicate = properties & PROPERTY_INDICATE; + + if (!hasNotify && !hasIndicate) { + LOG(ERROR) << "Characteristic needs NOTIFY or INDICATE"; + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(error_callback, + BluetoothRemoteGattServiceAndroid::GATT_ERROR_FAILED)); + return; + } + + BluetoothGattDescriptor* descriptor = GetDescriptorForUUID( + BluetoothGattDescriptor::ClientCharacteristicConfigurationUuid()); + + if (!descriptor) { + LOG(ERROR) + << "Could not find client characteristic configuration descriptor"; base::MessageLoop::current()->PostTask( - FROM_HERE, base::Bind(callback, base::Passed(¬ify_session))); - } else { + FROM_HERE, + base::Bind(error_callback, + BluetoothRemoteGattServiceAndroid::GATT_ERROR_FAILED)); + return; + } + + if (!Java_ChromeBluetoothRemoteGattCharacteristic_setCharacteristicNotification( + AttachCurrentThread(), j_characteristic_.obj(), true)) { + LOG(ERROR) << "Error enabling characteristic notification"; base::MessageLoop::current()->PostTask( FROM_HERE, base::Bind(error_callback, BluetoothRemoteGattServiceAndroid::GATT_ERROR_FAILED)); + return; } + + std::vector<uint8_t> value; + value.push_back(hasNotify ? 1 : 2); + value.push_back(0); + + pending_start_notify_calls_.push(std::make_pair(callback, error_callback)); + descriptor->WriteRemoteDescriptor( + value, base::Bind(&BluetoothRemoteGattCharacteristicAndroid:: + OnStartNotifySessionSuccess, + base::Unretained(this)), + base::Bind( + &BluetoothRemoteGattCharacteristicAndroid::OnStartNotifySessionError, + base::Unretained(this))); } void BluetoothRemoteGattCharacteristicAndroid::ReadRemoteCharacteristic( @@ -211,6 +251,25 @@ void BluetoothRemoteGattCharacteristicAndroid::OnChanged( adapter_->NotifyGattCharacteristicValueChanged(this, value_); } +void BluetoothRemoteGattCharacteristicAndroid::OnStartNotifySessionSuccess() { + while (!pending_start_notify_calls_.empty()) { + PendingStartNotifyCall callbacks = pending_start_notify_calls_.front(); + pending_start_notify_calls_.pop(); + scoped_ptr<device::BluetoothGattNotifySession> notify_session( + new BluetoothGattNotifySessionAndroid(instance_id_)); + callbacks.first.Run(std::move(notify_session)); + } +} + +void BluetoothRemoteGattCharacteristicAndroid::OnStartNotifySessionError( + BluetoothGattService::GattErrorCode error) { + while (!pending_start_notify_calls_.empty()) { + PendingStartNotifyCall callbacks = pending_start_notify_calls_.front(); + pending_start_notify_calls_.pop(); + callbacks.second.Run(error); + } +} + void BluetoothRemoteGattCharacteristicAndroid::OnRead( JNIEnv* env, const JavaParamRef<jobject>& jcaller, diff --git a/device/bluetooth/bluetooth_remote_gatt_characteristic_android.h b/device/bluetooth/bluetooth_remote_gatt_characteristic_android.h index 4493a94..606b7d4 100644 --- a/device/bluetooth/bluetooth_remote_gatt_characteristic_android.h +++ b/device/bluetooth/bluetooth_remote_gatt_characteristic_android.h @@ -7,6 +7,8 @@ #include <stdint.h> +#include <queue> + #include "base/android/jni_android.h" #include "base/containers/scoped_ptr_hash_map.h" #include "base/macros.h" @@ -73,18 +75,24 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattCharacteristicAndroid const base::Closure& callback, const ErrorCallback& error_callback) override; + // Called when StartNotifySession operation succeeds. + void OnStartNotifySessionSuccess(); + + // Called when StartNotifySession operation fails. + void OnStartNotifySessionError(BluetoothGattService::GattErrorCode error); + // Called when value changed event occurs. void OnChanged(JNIEnv* env, const base::android::JavaParamRef<jobject>& jcaller, const base::android::JavaParamRef<jbyteArray>& value); - // Callback after Read operation completes. + // Called when Read operation completes. void OnRead(JNIEnv* env, const base::android::JavaParamRef<jobject>& jcaller, int32_t status, const base::android::JavaParamRef<jbyteArray>& value); - // Callback after Write operation completes. + // Called when Write operation completes. void OnWrite(JNIEnv* env, const base::android::JavaParamRef<jobject>& jcaller, int32_t status); @@ -123,6 +131,11 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattCharacteristicAndroid // Adapter unique instance ID. std::string instance_id_; + // StartNotifySession callbacks and pending state. + typedef std::pair<NotifySessionCallback, ErrorCallback> + PendingStartNotifyCall; + std::queue<PendingStartNotifyCall> pending_start_notify_calls_; + // ReadRemoteCharacteristic callbacks and pending state. bool read_pending_ = false; ValueCallback read_callback_; diff --git a/device/bluetooth/bluetooth_remote_gatt_descriptor_android.cc b/device/bluetooth/bluetooth_remote_gatt_descriptor_android.cc index d197a78..39b812f 100644 --- a/device/bluetooth/bluetooth_remote_gatt_descriptor_android.cc +++ b/device/bluetooth/bluetooth_remote_gatt_descriptor_android.cc @@ -31,9 +31,7 @@ BluetoothRemoteGattDescriptorAndroid::Create( descriptor->j_descriptor_.Reset( Java_ChromeBluetoothRemoteGattDescriptor_create( - AttachCurrentThread(), - // TODO(scheib) Will eventually need to pass c++ pointer: - // reinterpret_cast<intptr_t>(descriptor.get()), + AttachCurrentThread(), reinterpret_cast<intptr_t>(descriptor.get()), bluetooth_gatt_descriptor_wrapper, chrome_bluetooth_device)); return descriptor; @@ -71,9 +69,7 @@ bool BluetoothRemoteGattDescriptorAndroid::IsLocal() const { const std::vector<uint8_t>& BluetoothRemoteGattDescriptorAndroid::GetValue() const { - NOTIMPLEMENTED(); - static std::vector<uint8_t> empty_value; - return empty_value; + return value_; } BluetoothGattCharacteristic* @@ -91,20 +87,98 @@ BluetoothRemoteGattDescriptorAndroid::GetPermissions() const { void BluetoothRemoteGattDescriptorAndroid::ReadRemoteDescriptor( const ValueCallback& callback, const ErrorCallback& error_callback) { - NOTIMPLEMENTED(); - base::MessageLoop::current()->PostTask( - FROM_HERE, - base::Bind(error_callback, BluetoothGattService::GATT_ERROR_FAILED)); + if (read_pending_ || write_pending_) { + base::MessageLoop::current()->PostTask( + FROM_HERE, base::Bind(error_callback, + BluetoothGattService::GATT_ERROR_IN_PROGRESS)); + return; + } + + if (!Java_ChromeBluetoothRemoteGattDescriptor_readRemoteDescriptor( + AttachCurrentThread(), j_descriptor_.obj())) { + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(error_callback, + BluetoothRemoteGattServiceAndroid::GATT_ERROR_FAILED)); + return; + } + + read_pending_ = true; + read_callback_ = callback; + read_error_callback_ = error_callback; } void BluetoothRemoteGattDescriptorAndroid::WriteRemoteDescriptor( const std::vector<uint8_t>& new_value, const base::Closure& callback, const ErrorCallback& error_callback) { - NOTIMPLEMENTED(); - base::MessageLoop::current()->PostTask( - FROM_HERE, - base::Bind(error_callback, BluetoothGattService::GATT_ERROR_FAILED)); + if (read_pending_ || write_pending_) { + base::MessageLoop::current()->PostTask( + FROM_HERE, base::Bind(error_callback, + BluetoothGattService::GATT_ERROR_IN_PROGRESS)); + return; + } + + JNIEnv* env = AttachCurrentThread(); + if (!Java_ChromeBluetoothRemoteGattDescriptor_writeRemoteDescriptor( + env, j_descriptor_.obj(), + base::android::ToJavaByteArray(env, new_value).obj())) { + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(error_callback, + BluetoothRemoteGattServiceAndroid::GATT_ERROR_FAILED)); + return; + } + + write_pending_ = true; + write_callback_ = callback; + write_error_callback_ = error_callback; +} + +void BluetoothRemoteGattDescriptorAndroid::OnRead( + JNIEnv* env, + const JavaParamRef<jobject>& jcaller, + int32_t status, + const JavaParamRef<jbyteArray>& value) { + read_pending_ = false; + + // Clear callbacks before calling to avoid reentrancy issues. + ValueCallback read_callback = read_callback_; + ErrorCallback read_error_callback = read_error_callback_; + read_callback_.Reset(); + read_error_callback_.Reset(); + + if (status == 0 // android.bluetooth.BluetoothGatt.GATT_SUCCESS + && !read_callback.is_null()) { + base::android::JavaByteArrayToByteVector(env, value, &value_); + read_callback.Run(value_); + // TODO(https://crbug.com/584369): Call GattDescriptorValueChanged. + } else if (!read_error_callback.is_null()) { + read_error_callback.Run( + BluetoothRemoteGattServiceAndroid::GetGattErrorCode(status)); + } +} + +void BluetoothRemoteGattDescriptorAndroid::OnWrite( + JNIEnv* env, + const JavaParamRef<jobject>& jcaller, + int32_t status) { + write_pending_ = false; + + // Clear callbacks before calling to avoid reentrancy issues. + base::Closure write_callback = write_callback_; + ErrorCallback write_error_callback = write_error_callback_; + write_callback_.Reset(); + write_error_callback_.Reset(); + + if (status == 0 // android.bluetooth.BluetoothGatt.GATT_SUCCESS + && !write_callback.is_null()) { + write_callback.Run(); + // TODO(https://crbug.com/584369): Call GattDescriptorValueChanged. + } else if (!write_error_callback.is_null()) { + write_error_callback.Run( + BluetoothRemoteGattServiceAndroid::GetGattErrorCode(status)); + } } BluetoothRemoteGattDescriptorAndroid::BluetoothRemoteGattDescriptorAndroid( diff --git a/device/bluetooth/bluetooth_remote_gatt_descriptor_android.h b/device/bluetooth/bluetooth_remote_gatt_descriptor_android.h index bc50d65..2b0fa58 100644 --- a/device/bluetooth/bluetooth_remote_gatt_descriptor_android.h +++ b/device/bluetooth/bluetooth_remote_gatt_descriptor_android.h @@ -50,6 +50,17 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattDescriptorAndroid const base::Closure& callback, const ErrorCallback& error_callback) override; + // Called when Read operation completes. + void OnRead(JNIEnv* env, + const base::android::JavaParamRef<jobject>& jcaller, + int32_t status, + const base::android::JavaParamRef<jbyteArray>& value); + + // Called when Write operation completes. + void OnWrite(JNIEnv* env, + const base::android::JavaParamRef<jobject>& jcaller, + int32_t status); + private: BluetoothRemoteGattDescriptorAndroid(const std::string& instanceId); @@ -60,6 +71,18 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothRemoteGattDescriptorAndroid // Adapter unique instance ID. std::string instance_id_; + // ReadRemoteCharacteristic callbacks and pending state. + bool read_pending_ = false; + ValueCallback read_callback_; + ErrorCallback read_error_callback_; + + // WriteRemoteCharacteristic callbacks and pending state. + bool write_pending_ = false; + base::Closure write_callback_; + ErrorCallback write_error_callback_; + + std::vector<uint8_t> value_; + DISALLOW_COPY_AND_ASSIGN(BluetoothRemoteGattDescriptorAndroid); }; diff --git a/device/bluetooth/test/android/java/src/org/chromium/device/bluetooth/Fakes.java b/device/bluetooth/test/android/java/src/org/chromium/device/bluetooth/Fakes.java index 46f9c66..f3d98a7 100644 --- a/device/bluetooth/test/android/java/src/org/chromium/device/bluetooth/Fakes.java +++ b/device/bluetooth/test/android/java/src/org/chromium/device/bluetooth/Fakes.java @@ -368,6 +368,7 @@ class Fakes { boolean mReadCharacteristicWillFailSynchronouslyOnce = false; boolean mSetCharacteristicNotificationWillFailSynchronouslyOnce = false; boolean mWriteCharacteristicWillFailSynchronouslyOnce = false; + boolean mReadDescriptorWillFailSynchronouslyOnce = false; boolean mWriteDescriptorWillFailSynchronouslyOnce = false; public FakeBluetoothGatt(FakeBluetoothDevice device) { @@ -431,6 +432,16 @@ class Fakes { } @Override + boolean readDescriptor(Wrappers.BluetoothGattDescriptorWrapper descriptor) { + if (mReadDescriptorWillFailSynchronouslyOnce) { + mReadDescriptorWillFailSynchronouslyOnce = false; + return false; + } + nativeOnFakeBluetoothGattReadDescriptor(mDevice.mAdapter.mNativeBluetoothTestAndroid); + return true; + } + + @Override boolean writeDescriptor(Wrappers.BluetoothGattDescriptorWrapper descriptor) { if (mWriteDescriptorWillFailSynchronouslyOnce) { mWriteDescriptorWillFailSynchronouslyOnce = false; @@ -686,15 +697,65 @@ class Fakes { final FakeBluetoothGattCharacteristic mCharacteristic; final UUID mUuid; byte[] mValue; + static FakeBluetoothGattDescriptor sRememberedDescriptor; public FakeBluetoothGattDescriptor( FakeBluetoothGattCharacteristic characteristic, UUID uuid) { - super(null); + super(null, null); mCharacteristic = characteristic; mUuid = uuid; mValue = new byte[0]; } + // Implements BluetoothTestAndroid::RememberDescriptorForSubsequentAction. + @CalledByNative("FakeBluetoothGattDescriptor") + private static void rememberDescriptorForSubsequentAction( + ChromeBluetoothRemoteGattDescriptor chromeDescriptor) { + sRememberedDescriptor = (FakeBluetoothGattDescriptor) chromeDescriptor.mDescriptor; + } + + // Simulate a value being read from a descriptor. + @CalledByNative("FakeBluetoothGattDescriptor") + private static void valueRead( + ChromeBluetoothRemoteGattDescriptor chromeDescriptor, int status, byte[] value) { + if (chromeDescriptor == null && sRememberedDescriptor == null) + throw new IllegalArgumentException("rememberDescriptor wasn't called previously."); + + FakeBluetoothGattDescriptor fakeDescriptor = (chromeDescriptor == null) + ? sRememberedDescriptor + : (FakeBluetoothGattDescriptor) chromeDescriptor.mDescriptor; + + fakeDescriptor.mValue = value; + fakeDescriptor.mCharacteristic.mService.mDevice.mGattCallback.onDescriptorRead( + fakeDescriptor, status); + } + + // Simulate a value being written to a descriptor. + @CalledByNative("FakeBluetoothGattDescriptor") + private static void valueWrite( + ChromeBluetoothRemoteGattDescriptor chromeDescriptor, int status) { + if (chromeDescriptor == null && sRememberedDescriptor == null) + throw new IllegalArgumentException("rememberDescriptor wasn't called previously."); + + FakeBluetoothGattDescriptor fakeDescriptor = (chromeDescriptor == null) + ? sRememberedDescriptor + : (FakeBluetoothGattDescriptor) chromeDescriptor.mDescriptor; + + fakeDescriptor.mCharacteristic.mService.mDevice.mGattCallback.onDescriptorWrite( + fakeDescriptor, status); + } + + // Cause subsequent value read of a descriptor to fail synchronously. + @CalledByNative("FakeBluetoothGattDescriptor") + private static void setReadDescriptorWillFailSynchronouslyOnce( + ChromeBluetoothRemoteGattDescriptor chromeDescriptor) { + FakeBluetoothGattDescriptor fakeDescriptor = + (FakeBluetoothGattDescriptor) chromeDescriptor.mDescriptor; + + fakeDescriptor.mCharacteristic.mService.mDevice.mGatt + .mReadDescriptorWillFailSynchronouslyOnce = true; + } + // Cause subsequent value write of a descriptor to fail synchronously. @CalledByNative("FakeBluetoothGattDescriptor") private static void setWriteDescriptorWillFailSynchronouslyOnce( @@ -710,6 +771,11 @@ class Fakes { // Wrappers.BluetoothGattDescriptorWrapper overrides: @Override + public Wrappers.BluetoothGattCharacteristicWrapper getCharacteristic() { + return mCharacteristic; + } + + @Override public UUID getUuid() { return mUuid; } @@ -759,6 +825,10 @@ class Fakes { private static native void nativeOnFakeBluetoothGattWriteCharacteristic( long nativeBluetoothTestAndroid, byte[] value); + // Binds to BluetoothTestAndroid::OnFakeBluetoothGattReadDescriptor. + private static native void nativeOnFakeBluetoothGattReadDescriptor( + long nativeBluetoothTestAndroid); + // Binds to BluetoothTestAndroid::OnFakeBluetoothGattWriteDescriptor. private static native void nativeOnFakeBluetoothGattWriteDescriptor( long nativeBluetoothTestAndroid, byte[] value); diff --git a/device/bluetooth/test/bluetooth_test.cc b/device/bluetooth/test/bluetooth_test.cc index 5feec01..77ecddc 100644 --- a/device/bluetooth/test/bluetooth_test.cc +++ b/device/bluetooth/test/bluetooth_test.cc @@ -231,6 +231,7 @@ void BluetoothTestBase::ResetEventCounts() { gatt_notify_characteristic_attempts_ = 0; gatt_read_characteristic_attempts_ = 0; gatt_write_characteristic_attempts_ = 0; + gatt_read_descriptor_attempts_ = 0; gatt_write_descriptor_attempts_ = 0; } diff --git a/device/bluetooth/test/bluetooth_test.h b/device/bluetooth/test/bluetooth_test.h index 5570ca1..c840935 100644 --- a/device/bluetooth/test/bluetooth_test.h +++ b/device/bluetooth/test/bluetooth_test.h @@ -189,6 +189,41 @@ class BluetoothTestBase : public testing::Test { BluetoothGattCharacteristic* characteristic, const std::string& uuid) {} + // Remembers |descriptor|'s platform specific object to be used in a + // subsequent call to methods such as SimulateGattDescriptorRead that + // accept a nullptr value to select this remembered descriptor. This + // enables tests where the platform attempts to reference descriptor + // objects after the Chrome objects have been deleted, e.g. with DeleteDevice. + virtual void RememberDescriptorForSubsequentAction( + BluetoothGattDescriptor* descriptor) {} + + // Simulates a Descriptor Read operation succeeding, returning |value|. + // If |descriptor| is null, acts upon the descriptor provided to + // RememberDescriptorForSubsequentAction. + virtual void SimulateGattDescriptorRead(BluetoothGattDescriptor* descriptor, + const std::vector<uint8_t>& value) {} + + // Simulates a Descriptor Read operation failing with a GattErrorCode. + virtual void SimulateGattDescriptorReadError( + BluetoothGattDescriptor* descriptor, + BluetoothGattService::GattErrorCode) {} + + // Simulates a Descriptor Read operation failing synchronously once for an + // unknown reason. + virtual void SimulateGattDescriptorReadWillFailSynchronouslyOnce( + BluetoothGattDescriptor* descriptor) {} + + // Simulates a Descriptor Write operation succeeding, returning |value|. + // If |descriptor| is null, acts upon the descriptor provided to + // RememberDescriptorForSubsequentAction. + virtual void SimulateGattDescriptorWrite( + BluetoothGattDescriptor* descriptor) {} + + // Simulates a Descriptor Write operation failing with a GattErrorCode. + virtual void SimulateGattDescriptorWriteError( + BluetoothGattDescriptor* descriptor, + BluetoothGattService::GattErrorCode) {} + // Simulates a Descriptor Write operation failing synchronously once for // an unknown reason. virtual void SimulateGattDescriptorWriteWillFailSynchronouslyOnce( @@ -250,6 +285,7 @@ class BluetoothTestBase : public testing::Test { int gatt_notify_characteristic_attempts_ = 0; int gatt_read_characteristic_attempts_ = 0; int gatt_write_characteristic_attempts_ = 0; + int gatt_read_descriptor_attempts_ = 0; int gatt_write_descriptor_attempts_ = 0; // The following values are used to make sure the correct callbacks diff --git a/device/bluetooth/test/bluetooth_test_android.cc b/device/bluetooth/test/bluetooth_test_android.cc index 522cd61..b1cd357 100644 --- a/device/bluetooth/test/bluetooth_test_android.cc +++ b/device/bluetooth/test/bluetooth_test_android.cc @@ -176,10 +176,14 @@ void BluetoothTestAndroid::RememberCharacteristicForSubsequentAction( void BluetoothTestAndroid::SimulateGattNotifySessionStarted( BluetoothGattCharacteristic* characteristic) { - // Android doesn't provide any sort of callback for when notifications have - // been enabled. So, just run the message loop to process the success - // callback. - base::RunLoop().RunUntilIdle(); + BluetoothGattDescriptor* descriptor = characteristic->GetDescriptorForUUID( + BluetoothGattDescriptor::ClientCharacteristicConfigurationUuid()); + BluetoothRemoteGattDescriptorAndroid* descriptor_android = + static_cast<BluetoothRemoteGattDescriptorAndroid*>(descriptor); + Java_FakeBluetoothGattDescriptor_valueWrite( + base::android::AttachCurrentThread(), + descriptor_android ? descriptor_android->GetJavaObject().obj() : nullptr, + 0); // android.bluetooth.BluetoothGatt.GATT_SUCCESS } void BluetoothTestAndroid:: @@ -291,6 +295,75 @@ void BluetoothTestAndroid::SimulateGattDescriptor( base::android::ConvertUTF8ToJavaString(env, uuid).obj()); } +void BluetoothTestAndroid::RememberDescriptorForSubsequentAction( + BluetoothGattDescriptor* descriptor) { + BluetoothRemoteGattDescriptorAndroid* descriptor_android = + static_cast<BluetoothRemoteGattDescriptorAndroid*>(descriptor); + + Java_FakeBluetoothGattDescriptor_rememberDescriptorForSubsequentAction( + base::android::AttachCurrentThread(), + descriptor_android->GetJavaObject().obj()); +} + +void BluetoothTestAndroid::SimulateGattDescriptorRead( + BluetoothGattDescriptor* descriptor, + const std::vector<uint8_t>& value) { + BluetoothRemoteGattDescriptorAndroid* descriptor_android = + static_cast<BluetoothRemoteGattDescriptorAndroid*>(descriptor); + JNIEnv* env = base::android::AttachCurrentThread(); + + Java_FakeBluetoothGattDescriptor_valueRead( + env, + descriptor_android ? descriptor_android->GetJavaObject().obj() : nullptr, + 0, // android.bluetooth.BluetoothGatt.GATT_SUCCESS + base::android::ToJavaByteArray(env, value).obj()); +} + +void BluetoothTestAndroid::SimulateGattDescriptorReadError( + BluetoothGattDescriptor* descriptor, + BluetoothGattService::GattErrorCode error_code) { + BluetoothRemoteGattDescriptorAndroid* descriptor_android = + static_cast<BluetoothRemoteGattDescriptorAndroid*>(descriptor); + JNIEnv* env = base::android::AttachCurrentThread(); + std::vector<uint8_t> empty_value; + + Java_FakeBluetoothGattDescriptor_valueRead( + env, descriptor_android->GetJavaObject().obj(), + BluetoothRemoteGattServiceAndroid::GetAndroidErrorCode(error_code), + base::android::ToJavaByteArray(env, empty_value).obj()); +} + +void BluetoothTestAndroid::SimulateGattDescriptorReadWillFailSynchronouslyOnce( + BluetoothGattDescriptor* descriptor) { + BluetoothRemoteGattDescriptorAndroid* descriptor_android = + static_cast<BluetoothRemoteGattDescriptorAndroid*>(descriptor); + JNIEnv* env = base::android::AttachCurrentThread(); + + Java_FakeBluetoothGattDescriptor_setReadDescriptorWillFailSynchronouslyOnce( + env, descriptor_android->GetJavaObject().obj()); +} + +void BluetoothTestAndroid::SimulateGattDescriptorWrite( + BluetoothGattDescriptor* descriptor) { + BluetoothRemoteGattDescriptorAndroid* descriptor_android = + static_cast<BluetoothRemoteGattDescriptorAndroid*>(descriptor); + Java_FakeBluetoothGattDescriptor_valueWrite( + base::android::AttachCurrentThread(), + descriptor_android ? descriptor_android->GetJavaObject().obj() : nullptr, + 0); // android.bluetooth.BluetoothGatt.GATT_SUCCESS +} + +void BluetoothTestAndroid::SimulateGattDescriptorWriteError( + BluetoothGattDescriptor* descriptor, + BluetoothGattService::GattErrorCode error_code) { + BluetoothRemoteGattDescriptorAndroid* descriptor_android = + static_cast<BluetoothRemoteGattDescriptorAndroid*>(descriptor); + Java_FakeBluetoothGattDescriptor_valueWrite( + base::android::AttachCurrentThread(), + descriptor_android->GetJavaObject().obj(), + BluetoothRemoteGattServiceAndroid::GetAndroidErrorCode(error_code)); +} + void BluetoothTestAndroid::SimulateGattDescriptorWriteWillFailSynchronouslyOnce( BluetoothGattDescriptor* descriptor) { BluetoothRemoteGattDescriptorAndroid* descriptor_android = @@ -348,6 +421,12 @@ void BluetoothTestAndroid::OnFakeBluetoothGattWriteCharacteristic( base::android::JavaByteArrayToByteVector(env, value, &last_write_value_); } +void BluetoothTestAndroid::OnFakeBluetoothGattReadDescriptor( + JNIEnv* env, + const JavaParamRef<jobject>& caller) { + gatt_read_descriptor_attempts_++; +} + void BluetoothTestAndroid::OnFakeBluetoothGattWriteDescriptor( JNIEnv* env, const JavaParamRef<jobject>& caller, diff --git a/device/bluetooth/test/bluetooth_test_android.h b/device/bluetooth/test/bluetooth_test_android.h index b855eca..8fbf61c 100644 --- a/device/bluetooth/test/bluetooth_test_android.h +++ b/device/bluetooth/test/bluetooth_test_android.h @@ -51,6 +51,7 @@ class BluetoothTestAndroid : public BluetoothTestBase { void SimulateGattCharacteristicChanged( BluetoothGattCharacteristic* characteristic, const std::vector<uint8_t>& value) override; + void SimulateGattCharacteristicRead( BluetoothGattCharacteristic* characteristic, const std::vector<uint8_t>& value) override; @@ -59,6 +60,7 @@ class BluetoothTestAndroid : public BluetoothTestBase { BluetoothGattService::GattErrorCode) override; void SimulateGattCharacteristicReadWillFailSynchronouslyOnce( BluetoothGattCharacteristic* characteristic) override; + void SimulateGattCharacteristicWrite( BluetoothGattCharacteristic* characteristic) override; void SimulateGattCharacteristicWriteError( @@ -66,8 +68,25 @@ class BluetoothTestAndroid : public BluetoothTestBase { BluetoothGattService::GattErrorCode) override; void SimulateGattCharacteristicWriteWillFailSynchronouslyOnce( BluetoothGattCharacteristic* characteristic) override; + void SimulateGattDescriptor(BluetoothGattCharacteristic* characteristic, const std::string& uuid) override; + void RememberDescriptorForSubsequentAction( + BluetoothGattDescriptor* descriptor) override; + + void SimulateGattDescriptorRead(BluetoothGattDescriptor* descriptor, + const std::vector<uint8_t>& value) override; + void SimulateGattDescriptorReadError( + BluetoothGattDescriptor* descriptor, + BluetoothGattService::GattErrorCode) override; + void SimulateGattDescriptorReadWillFailSynchronouslyOnce( + BluetoothGattDescriptor* descriptor) override; + + void SimulateGattDescriptorWrite( + BluetoothGattDescriptor* descriptor) override; + void SimulateGattDescriptorWriteError( + BluetoothGattDescriptor* descriptor, + BluetoothGattService::GattErrorCode) override; void SimulateGattDescriptorWriteWillFailSynchronouslyOnce( BluetoothGattDescriptor* descriptor) override; @@ -112,6 +131,11 @@ class BluetoothTestAndroid : public BluetoothTestBase { const base::android::JavaParamRef<jobject>& caller, const base::android::JavaParamRef<jbyteArray>& value); + // Records that Java FakeBluetoothGatt readDescriptor was called. + void OnFakeBluetoothGattReadDescriptor( + JNIEnv* env, + const base::android::JavaParamRef<jobject>& caller); + // Records that Java FakeBluetoothGatt writeDescriptor was called. void OnFakeBluetoothGattWriteDescriptor( JNIEnv* env, |