summaryrefslogtreecommitdiffstats
path: root/device
diff options
context:
space:
mode:
authorscheib <scheib@chromium.org>2015-12-06 11:24:14 -0800
committerCommit bot <commit-bot@chromium.org>2015-12-06 19:25:14 +0000
commit2e0fee481296bcda15bf34b258e28be978a9b2d8 (patch)
tree8d11533890fbe519bc2c8f98877835b644fbda06 /device
parent292ab9f948fd6ff4e6f2d4583724fb7acee19ef0 (diff)
downloadchromium_src-2e0fee481296bcda15bf34b258e28be978a9b2d8.zip
chromium_src-2e0fee481296bcda15bf34b258e28be978a9b2d8.tar.gz
chromium_src-2e0fee481296bcda15bf34b258e28be978a9b2d8.tar.bz2
bluetooth: android: Crash fix: Check Characteristic isn't already destroyed.
Read and Write operations may not complete until after Chrome has destroyed C++ and Java Chrome Characteristic objects, resulting in a null object when looking up a Chrome Characteristic from the Android Wrapper. BUG=559294 Review URL: https://codereview.chromium.org/1471173004 Cr-Commit-Position: refs/heads/master@{#363377}
Diffstat (limited to 'device')
-rw-r--r--device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothDevice.java26
-rw-r--r--device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothRemoteGattCharacteristic.java1
-rw-r--r--device/bluetooth/bluetooth_gatt_characteristic_unittest.cc36
-rw-r--r--device/bluetooth/test/android/java/src/org/chromium/device/bluetooth/Fakes.java27
-rw-r--r--device/bluetooth/test/bluetooth_test.h14
-rw-r--r--device/bluetooth/test/bluetooth_test_android.cc17
-rw-r--r--device/bluetooth/test/bluetooth_test_android.h2
7 files changed, 108 insertions, 15 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 6e20600..b11244e 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
@@ -186,10 +186,15 @@ final class ChromeBluetoothDevice {
ThreadUtils.runOnUiThread(new Runnable() {
@Override
public void run() {
- ChromeBluetoothRemoteGattCharacteristic
- chromeBluetoothRemoteGattCharacteristic =
- mWrapperToChromeCharacteristicsMap.get(characteristic);
- chromeBluetoothRemoteGattCharacteristic.onCharacteristicRead(status);
+ ChromeBluetoothRemoteGattCharacteristic chromeCharacteristic =
+ mWrapperToChromeCharacteristicsMap.get(characteristic);
+ if (chromeCharacteristic == null) {
+ // Android events arriving with no Chrome object is expected rarely: only
+ // when the event races object destruction.
+ Log.v(TAG, "onCharacteristicRead when chromeCharacteristic == null.");
+ } else {
+ chromeCharacteristic.onCharacteristicRead(status);
+ }
}
});
}
@@ -201,10 +206,15 @@ final class ChromeBluetoothDevice {
ThreadUtils.runOnUiThread(new Runnable() {
@Override
public void run() {
- ChromeBluetoothRemoteGattCharacteristic
- chromeBluetoothRemoteGattCharacteristic =
- mWrapperToChromeCharacteristicsMap.get(characteristic);
- chromeBluetoothRemoteGattCharacteristic.onCharacteristicWrite(status);
+ ChromeBluetoothRemoteGattCharacteristic chromeCharacteristic =
+ mWrapperToChromeCharacteristicsMap.get(characteristic);
+ if (chromeCharacteristic == null) {
+ // Android events arriving with no Chrome object is expected rarely: only
+ // when the event races object destruction.
+ Log.v(TAG, "onCharacteristicWrite when chromeCharacteristic == null.");
+ } else {
+ chromeCharacteristic.onCharacteristicWrite(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 dd0d261..3b64681 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
@@ -42,6 +42,7 @@ final class ChromeBluetoothRemoteGattCharacteristic {
*/
@CalledByNative
private void onBluetoothRemoteGattCharacteristicAndroidDestruction() {
+ Log.v(TAG, "ChromeBluetoothRemoteGattCharacteristic Destroyed.");
mNativeBluetoothRemoteGattCharacteristicAndroid = 0;
mChromeBluetoothDevice.mWrapperToChromeCharacteristicsMap.remove(mCharacteristic);
}
diff --git a/device/bluetooth/bluetooth_gatt_characteristic_unittest.cc b/device/bluetooth/bluetooth_gatt_characteristic_unittest.cc
index 17e77e9..bbca500 100644
--- a/device/bluetooth/bluetooth_gatt_characteristic_unittest.cc
+++ b/device/bluetooth/bluetooth_gatt_characteristic_unittest.cc
@@ -214,6 +214,42 @@ TEST_F(BluetoothGattCharacteristicTest, WriteRemoteCharacteristic_Empty) {
#endif // defined(OS_ANDROID)
#if defined(OS_ANDROID)
+// Tests ReadRemoteCharacteristic completing after Chrome objects are deleted.
+TEST_F(BluetoothGattCharacteristicTest, ReadRemoteCharacteristic_AfterDeleted) {
+ ASSERT_NO_FATAL_FAILURE(FakeCharacteristicBoilerplate());
+
+ characteristic1_->ReadRemoteCharacteristic(
+ GetReadValueCallback(Call::NOT_EXPECTED),
+ GetGattErrorCallback(Call::NOT_EXPECTED));
+
+ RememberCharacteristicForSubsequentAction(characteristic1_);
+ DeleteDevice(device_);
+
+ std::vector<uint8_t> empty_vector;
+ SimulateGattCharacteristicRead(/* use remembered characteristic */ nullptr,
+ empty_vector);
+}
+#endif // defined(OS_ANDROID)
+
+#if defined(OS_ANDROID)
+// Tests WriteRemoteCharacteristic completing after Chrome objects are deleted.
+TEST_F(BluetoothGattCharacteristicTest,
+ WriteRemoteCharacteristic_AfterDeleted) {
+ ASSERT_NO_FATAL_FAILURE(FakeCharacteristicBoilerplate());
+
+ std::vector<uint8_t> empty_vector;
+ characteristic1_->WriteRemoteCharacteristic(
+ empty_vector, GetCallback(Call::NOT_EXPECTED),
+ GetGattErrorCallback(Call::NOT_EXPECTED));
+
+ RememberCharacteristicForSubsequentAction(characteristic1_);
+ DeleteDevice(device_);
+
+ SimulateGattCharacteristicWrite(/* use remembered characteristic */ nullptr);
+}
+#endif // defined(OS_ANDROID)
+
+#if defined(OS_ANDROID)
// Tests ReadRemoteCharacteristic and GetValue with non-empty value buffer.
TEST_F(BluetoothGattCharacteristicTest, ReadRemoteCharacteristic) {
ASSERT_NO_FATAL_FAILURE(FakeCharacteristicBoilerplate());
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 d01cc1e..e9485e5 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
@@ -428,6 +428,7 @@ class Fakes {
final int mProperties;
final UUID mUuid;
byte[] mValue;
+ static FakeBluetoothGattCharacteristic sRememberedCharacteristic;
public FakeBluetoothGattCharacteristic(
FakeBluetoothGattService service, int instanceId, int properties, UUID uuid) {
@@ -441,10 +442,23 @@ class Fakes {
// Simulate a value being read from a characteristic.
@CalledByNative("FakeBluetoothGattCharacteristic")
+ private static void rememberCharacteristic(
+ ChromeBluetoothRemoteGattCharacteristic chromeCharacteristic) {
+ sRememberedCharacteristic =
+ (FakeBluetoothGattCharacteristic) chromeCharacteristic.mCharacteristic;
+ }
+
+ // Simulate a value being read from a characteristic.
+ @CalledByNative("FakeBluetoothGattCharacteristic")
private static void valueRead(ChromeBluetoothRemoteGattCharacteristic chromeCharacteristic,
int status, byte[] value) {
- FakeBluetoothGattCharacteristic fakeCharacteristic =
- (FakeBluetoothGattCharacteristic) chromeCharacteristic.mCharacteristic;
+ if (chromeCharacteristic == null && sRememberedCharacteristic == null)
+ throw new IllegalArgumentException(
+ "rememberCharacteristic wasn't called previously.");
+
+ FakeBluetoothGattCharacteristic fakeCharacteristic = (chromeCharacteristic == null)
+ ? sRememberedCharacteristic
+ : (FakeBluetoothGattCharacteristic) chromeCharacteristic.mCharacteristic;
fakeCharacteristic.mValue = value;
fakeCharacteristic.mService.mDevice.mGattCallback.onCharacteristicRead(
@@ -455,8 +469,13 @@ class Fakes {
@CalledByNative("FakeBluetoothGattCharacteristic")
private static void valueWrite(
ChromeBluetoothRemoteGattCharacteristic chromeCharacteristic, int status) {
- FakeBluetoothGattCharacteristic fakeCharacteristic =
- (FakeBluetoothGattCharacteristic) chromeCharacteristic.mCharacteristic;
+ if (chromeCharacteristic == null && sRememberedCharacteristic == null)
+ throw new IllegalArgumentException(
+ "rememberCharacteristic wasn't called previously.");
+
+ FakeBluetoothGattCharacteristic fakeCharacteristic = (chromeCharacteristic == null)
+ ? sRememberedCharacteristic
+ : (FakeBluetoothGattCharacteristic) chromeCharacteristic.mCharacteristic;
fakeCharacteristic.mService.mDevice.mGattCallback.onCharacteristicWrite(
fakeCharacteristic, status);
diff --git a/device/bluetooth/test/bluetooth_test.h b/device/bluetooth/test/bluetooth_test.h
index a0066e9..3ddfec9 100644
--- a/device/bluetooth/test/bluetooth_test.h
+++ b/device/bluetooth/test/bluetooth_test.h
@@ -118,6 +118,14 @@ class BluetoothTestBase : public testing::Test {
const std::string& uuid,
int properties) {}
+ // Remembers |characteristic|'s platform specific object to be used in a
+ // subsequent call to methods such as SimulateGattCharacteristicRead that
+ // accept a nullptr value to select this remembered characteristic. This
+ // enables tests where the platform attempts to reference characteristic
+ // objects after the Chrome objects have been deleted, e.g. with DeleteDevice.
+ virtual void RememberCharacteristicForSubsequentAction(
+ BluetoothGattCharacteristic* characteristic) {}
+
// Simulates a Characteristic Set Notify success.
virtual void SimulateGattNotifySessionStarted(
BluetoothGattCharacteristic* characteristic) {}
@@ -128,6 +136,8 @@ class BluetoothTestBase : public testing::Test {
BluetoothGattCharacteristic* characteristic) {}
// Simulates a Characteristic Read operation succeeding, returning |value|.
+ // If |characteristic| is null, acts upon the characteristic provided to
+ // RememberCharacteristicForSubsequentAction.
virtual void SimulateGattCharacteristicRead(
BluetoothGattCharacteristic* characteristic,
const std::vector<uint8>& value) {}
@@ -143,6 +153,8 @@ class BluetoothTestBase : public testing::Test {
BluetoothGattCharacteristic* characteristic) {}
// Simulates a Characteristic Write operation succeeding, returning |value|.
+ // If |characteristic| is null, acts upon the characteristic provided to
+ // RememberCharacteristicForSubsequentAction.
virtual void SimulateGattCharacteristicWrite(
BluetoothGattCharacteristic* characteristic) {}
@@ -156,7 +168,7 @@ class BluetoothTestBase : public testing::Test {
virtual void SimulateGattCharacteristicWriteWillFailSynchronouslyOnce(
BluetoothGattCharacteristic* characteristic) {}
- // Remove the device from the adapter and delete it.
+ // Removes the device from the adapter and deletes it.
virtual void DeleteDevice(BluetoothDevice* device);
// Callbacks that increment |callback_count_|, |error_callback_count_|:
diff --git a/device/bluetooth/test/bluetooth_test_android.cc b/device/bluetooth/test/bluetooth_test_android.cc
index bb55650..d822a7c 100644
--- a/device/bluetooth/test/bluetooth_test_android.cc
+++ b/device/bluetooth/test/bluetooth_test_android.cc
@@ -163,6 +163,16 @@ void BluetoothTestAndroid::SimulateGattCharacteristic(
base::android::ConvertUTF8ToJavaString(env, uuid).obj(), properties);
}
+void BluetoothTestAndroid::RememberCharacteristicForSubsequentAction(
+ BluetoothGattCharacteristic* characteristic) {
+ BluetoothRemoteGattCharacteristicAndroid* characteristic_android =
+ static_cast<BluetoothRemoteGattCharacteristicAndroid*>(characteristic);
+
+ Java_FakeBluetoothGattCharacteristic_rememberCharacteristic(
+ base::android::AttachCurrentThread(),
+ characteristic_android->GetJavaObject().obj());
+}
+
void BluetoothTestAndroid::SimulateGattNotifySessionStarted(
BluetoothGattCharacteristic* characteristic) {
// Android doesn't provide any sort of callback for when notifications have
@@ -190,7 +200,9 @@ void BluetoothTestAndroid::SimulateGattCharacteristicRead(
JNIEnv* env = base::android::AttachCurrentThread();
Java_FakeBluetoothGattCharacteristic_valueRead(
- env, characteristic_android->GetJavaObject().obj(),
+ env,
+ characteristic_android ? characteristic_android->GetJavaObject().obj()
+ : nullptr,
0, // android.bluetooth.BluetoothGatt.GATT_SUCCESS
base::android::ToJavaByteArray(env, value).obj());
}
@@ -226,7 +238,8 @@ void BluetoothTestAndroid::SimulateGattCharacteristicWrite(
static_cast<BluetoothRemoteGattCharacteristicAndroid*>(characteristic);
Java_FakeBluetoothGattCharacteristic_valueWrite(
base::android::AttachCurrentThread(),
- characteristic_android->GetJavaObject().obj(),
+ characteristic_android ? characteristic_android->GetJavaObject().obj()
+ : nullptr,
0); // android.bluetooth.BluetoothGatt.GATT_SUCCESS
}
diff --git a/device/bluetooth/test/bluetooth_test_android.h b/device/bluetooth/test/bluetooth_test_android.h
index e4c1c82..664863a 100644
--- a/device/bluetooth/test/bluetooth_test_android.h
+++ b/device/bluetooth/test/bluetooth_test_android.h
@@ -39,6 +39,8 @@ class BluetoothTestAndroid : public BluetoothTestBase {
void SimulateGattCharacteristic(BluetoothGattService* service,
const std::string& uuid,
int properties) override;
+ void RememberCharacteristicForSubsequentAction(
+ BluetoothGattCharacteristic* characteristic) override;
void SimulateGattNotifySessionStarted(
BluetoothGattCharacteristic* characteristic) override;
void SimulateGattCharacteristicSetNotifyWillFailSynchronouslyOnce(