Skip to content

Commit

Permalink
bluetooth: android: BluetoothRemoteGattServiceAndroid::GetUUID
Browse files Browse the repository at this point in the history
Create a Java class ChromeBluetoothRemoteGattService to
implement BluetoothRemoteGattServiceAndroid, and implement
GetUUID.

Correct mistaken implementation of instanceID. Android's
getInstanceId is only unique for services with the same
UUID, so a device unique instance ID must join the UUID and
Android's getInstanceId.

Add the ability for tests to create services by providing a
vector of UUID strings.

BUG=541400

Review URL: https://codereview.chromium.org/1395783005

Cr-Commit-Position: refs/heads/master@{#354420}
  • Loading branch information
scheib authored and Commit bot committed Oct 16, 2015
1 parent d4b788f commit 33aa3f4
Show file tree
Hide file tree
Showing 17 changed files with 189 additions and 28 deletions.
1 change: 1 addition & 0 deletions device/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ test("device_unittests") {
"bluetooth/bluetooth_device_win_unittest.cc",
"bluetooth/bluetooth_discovery_filter_unittest.cc",
"bluetooth/bluetooth_gatt_chromeos_unittest.cc",
"bluetooth/bluetooth_gatt_service_unittest.cc",
"bluetooth/bluetooth_low_energy_win_unittest.cc",
"bluetooth/bluetooth_service_record_win_unittest.cc",
"bluetooth/bluetooth_socket_chromeos_unittest.cc",
Expand Down
1 change: 1 addition & 0 deletions device/bluetooth/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ if (is_android) {
java_sources_needing_jni = [
"android/java/src/org/chromium/device/bluetooth/ChromeBluetoothAdapter.java",
"android/java/src/org/chromium/device/bluetooth/ChromeBluetoothDevice.java",
"android/java/src/org/chromium/device/bluetooth/ChromeBluetoothRemoteGattService.java",
"android/java/src/org/chromium/device/bluetooth/Wrappers.java",
]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,12 @@ public void run() {
if (mNativeBluetoothDeviceAndroid != 0) {
for (Wrappers.BluetoothGattServiceWrapper service :
mBluetoothGatt.getServices()) {
nativeCreateGattRemoteService(mNativeBluetoothDeviceAndroid,
service.getInstanceId(), service);
// Create a device unique service ID. getInstanceId only differs
// between service instances with the same UUID.
String serviceInstanceId =
service.getUuid().toString() + service.getInstanceId();
nativeCreateGattRemoteService(
mNativeBluetoothDeviceAndroid, serviceInstanceId, service);
}
}
}
Expand All @@ -183,6 +187,6 @@ private native void nativeOnConnectionStateChange(
// 'Object' type must be used for |bluetoothGattServiceWrapper| because inner class
// Wrappers.BluetoothGattServiceWrapper reference is not handled by jni_generator.py JavaToJni.
// http://crbug.com/505554
private native void nativeCreateGattRemoteService(
long nativeBluetoothDeviceAndroid, int instanceId, Object bluetoothGattServiceWrapper);
private native void nativeCreateGattRemoteService(long nativeBluetoothDeviceAndroid,
String instanceId, Object bluetoothGattServiceWrapper);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright 2015 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.

package org.chromium.device.bluetooth;

import org.chromium.base.Log;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;

/**
* Exposes android.bluetooth.BluetoothGattService as necessary
* for C++ device::BluetoothRemoteGattServiceAndroid.
*
* Lifetime is controlled by
* device::BluetoothRemoteGattServiceAndroid.
*/
@JNINamespace("device")
final class ChromeBluetoothRemoteGattService {
private static final String TAG = "Bluetooth";

final Wrappers.BluetoothGattServiceWrapper mService;

private ChromeBluetoothRemoteGattService(Wrappers.BluetoothGattServiceWrapper serviceWrapper) {
mService = serviceWrapper;
Log.v(TAG, "ChromeBluetoothRemoteGattService created.");
}

// ---------------------------------------------------------------------------------------------
// BluetoothRemoteGattServiceAndroid methods implemented in java:

// Implements BluetoothRemoteGattServiceAndroid::Create.
// 'Object' type must be used because inner class Wrappers.BluetoothGattServiceWrapper reference
// is not handled by jni_generator.py JavaToJni. http://crbug.com/505554
@CalledByNative
private static ChromeBluetoothRemoteGattService create(Object serviceWrapper) {
return new ChromeBluetoothRemoteGattService(
(Wrappers.BluetoothGattServiceWrapper) serviceWrapper);
}

// Implements BluetoothRemoteGattServiceAndroid::GetUUID.
@CalledByNative
private String getUUID() {
return mService.getUuid().toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;

/**
* Wrapper classes around android.bluetooth.* classes that provide an
Expand Down Expand Up @@ -333,5 +334,9 @@ public BluetoothGattServiceWrapper(BluetoothGattService service) {
public int getInstanceId() {
return mService.getInstanceId();
}

public UUID getUuid() {
return mService.getUuid();
}
}
}
1 change: 1 addition & 0 deletions device/bluetooth/bluetooth.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@
'sources': [
'android/java/src/org/chromium/device/bluetooth/ChromeBluetoothAdapter.java',
'android/java/src/org/chromium/device/bluetooth/ChromeBluetoothDevice.java',
'android/java/src/org/chromium/device/bluetooth/ChromeBluetoothRemoteGattService.java',
'android/java/src/org/chromium/device/bluetooth/Wrappers.java',
],
'variables': {
Expand Down
5 changes: 3 additions & 2 deletions device/bluetooth/bluetooth_device_android.cc
Original file line number Diff line number Diff line change
Expand Up @@ -226,11 +226,12 @@ void BluetoothDeviceAndroid::OnConnectionStateChange(JNIEnv* env,
void BluetoothDeviceAndroid::CreateGattRemoteService(
JNIEnv* env,
jobject caller,
int32_t instanceId,
const jstring& instanceId,
jobject bluetooth_gatt_service_wrapper // Java Type:
// BluetoothGattServiceWrapper
) {
std::string instanceIdString = base::StringPrintf("%d", instanceId);
std::string instanceIdString =
base::android::ConvertJavaStringToUTF8(env, instanceId);

if (gatt_services_.contains(instanceIdString))
return;
Expand Down
2 changes: 1 addition & 1 deletion device/bluetooth/bluetooth_device_android.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothDeviceAndroid final
void CreateGattRemoteService(
JNIEnv* env,
jobject caller,
int32_t instanceId,
const jstring& instanceId,
jobject bluetooth_gatt_service_wrapper); // Java Type:
// BluetoothGattServiceWrapper

Expand Down
11 changes: 7 additions & 4 deletions device/bluetooth/bluetooth_device_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -436,10 +436,13 @@ TEST_F(BluetoothTest, SimulateGattServicesDiscovered) {
SimulateGattConnection(device);
EXPECT_EQ(1, gatt_discovery_attempts_);

// TODO(scheib): Add more control over how many services are created and
// their properties. http://crbug.com/541400
SimulateGattServicesDiscovered(device);
EXPECT_EQ(2u, device->GetGattServices().size());
std::vector<std::string> services;
services.push_back("00000000-0000-1000-8000-00805f9b34fb");
// 2 duplicate UUIDs creating 2 instances.
services.push_back("00000001-0000-1000-8000-00805f9b34fb");
services.push_back("00000001-0000-1000-8000-00805f9b34fb");
SimulateGattServicesDiscovered(device, services);
EXPECT_EQ(3u, device->GetGattServices().size());
}
#endif // defined(OS_ANDROID)

Expand Down
45 changes: 45 additions & 0 deletions device/bluetooth/bluetooth_gatt_service_unittest.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright 2014 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 "device/bluetooth/bluetooth_gatt_service.h"

#include "testing/gtest/include/gtest/gtest.h"

#if defined(OS_ANDROID)
#include "device/bluetooth/test/bluetooth_test_android.h"
#elif defined(OS_MACOSX)
#include "device/bluetooth/test/bluetooth_test_mac.h"
#endif

namespace device {

#if defined(OS_ANDROID)
TEST_F(BluetoothTest, GetUUIDAndGetIdentifier) {
InitWithFakeAdapter();
StartDiscoverySession();
BluetoothDevice* device = DiscoverLowEnergyDevice(3);
device->CreateGattConnection(GetGattConnectionCallback(),
GetConnectErrorCallback());
ResetEventCounts();
SimulateGattConnection(device);
EXPECT_EQ(1, gatt_discovery_attempts_);

// Create multiple instances with the same UUID.
BluetoothUUID uuid("00000000-0000-1000-8000-00805f9b34fb");
std::vector<std::string> services;
services.push_back(uuid.canonical_value());
services.push_back(uuid.canonical_value());
SimulateGattServicesDiscovered(device, services);

// Each has the same UUID.
EXPECT_EQ(uuid, device->GetGattServices()[0]->GetUUID());
EXPECT_EQ(uuid, device->GetGattServices()[1]->GetUUID());

// Instance IDs are unique.
EXPECT_NE(device->GetGattServices()[0]->GetIdentifier(),
device->GetGattServices()[1]->GetIdentifier());
}
#endif // defined(OS_ANDROID)

} // namespace device
19 changes: 17 additions & 2 deletions device/bluetooth/bluetooth_remote_gatt_service_android.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,13 @@

#include "device/bluetooth/bluetooth_remote_gatt_service_android.h"

#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "device/bluetooth/bluetooth_adapter_android.h"
#include "device/bluetooth/bluetooth_device_android.h"
#include "jni/ChromeBluetoothRemoteGattService_jni.h"

using base::android::AttachCurrentThread;

namespace device {

Expand All @@ -18,16 +23,26 @@ BluetoothRemoteGattServiceAndroid* BluetoothRemoteGattServiceAndroid::Create(
BluetoothRemoteGattServiceAndroid* service =
new BluetoothRemoteGattServiceAndroid(adapter, device, instanceId);

service->j_service_.Reset(Java_ChromeBluetoothRemoteGattService_create(
AttachCurrentThread(), bluetooth_remote_gatt_service_wrapper));

return service;
}

// static
bool BluetoothRemoteGattServiceAndroid::RegisterJNI(JNIEnv* env) {
return RegisterNativesImpl(
env); // Generated in ChromeBluetoothRemoteGattService_jni.h
}

std::string BluetoothRemoteGattServiceAndroid::GetIdentifier() const {
return instanceId_;
}

device::BluetoothUUID BluetoothRemoteGattServiceAndroid::GetUUID() const {
NOTIMPLEMENTED();
return device::BluetoothUUID();
return device::BluetoothUUID(
ConvertJavaStringToUTF8(Java_ChromeBluetoothRemoteGattService_getUUID(
AttachCurrentThread(), j_service_.obj())));
}

bool BluetoothRemoteGattServiceAndroid::IsLocal() const {
Expand Down
6 changes: 6 additions & 0 deletions device/bluetooth/bluetooth_remote_gatt_service_android.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ class BluetoothRemoteGattServiceAndroid : public device::BluetoothGattService {
// BluetoothRemoteGattServiceWrapper
std::string instanceId);

// Register C++ methods exposed to Java using JNI.
static bool RegisterJNI(JNIEnv* env);

// device::BluetoothGattService overrides.
std::string GetIdentifier() const override;
device::BluetoothUUID GetUUID() const override;
Expand Down Expand Up @@ -69,6 +72,9 @@ class BluetoothRemoteGattServiceAndroid : public device::BluetoothGattService {
std::string instanceId);
~BluetoothRemoteGattServiceAndroid() override;

// Java object org.chromium.device.bluetooth.ChromeBluetoothRemoteGattService.
base::android::ScopedJavaGlobalRef<jobject> j_service_;

// The adapter associated with this service. It's ok to store a raw pointer
// here since |adapter_| indirectly owns this instance.
BluetoothAdapterAndroid* adapter_;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
import org.chromium.base.annotations.JNINamespace;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;

/**
* Fake implementations of android.bluetooth.* classes for testing.
Expand Down Expand Up @@ -212,15 +214,20 @@ private static void connectionStateChange(
// Create a call to onServicesDiscovered on the |chrome_device| using parameter
// |status|.
@CalledByNative("FakeBluetoothDevice")
private static void servicesDiscovered(ChromeBluetoothDevice chromeDevice, int status) {
private static void servicesDiscovered(
ChromeBluetoothDevice chromeDevice, int status, String uuidsSpaceDelimited) {
FakeBluetoothDevice fakeDevice = (FakeBluetoothDevice) chromeDevice.mDevice;

// TODO(scheib): Add more control over how many services are created and
// their properties. http://crbug.com/541400
if (status == android.bluetooth.BluetoothGatt.GATT_SUCCESS) {
fakeDevice.mGatt.mServices.clear();
fakeDevice.mGatt.mServices.add(new FakeBluetoothGattService(0));
fakeDevice.mGatt.mServices.add(new FakeBluetoothGattService(1));
HashMap<String, Integer> uuidsToInstanceIdMap = new HashMap<String, Integer>();
for (String uuid : uuidsSpaceDelimited.split(" ")) {
Integer previousId = uuidsToInstanceIdMap.get(uuid);
int instanceId = (previousId == null) ? 0 : previousId + 1;
uuidsToInstanceIdMap.put(uuid, instanceId);
fakeDevice.mGatt.mServices.add(
new FakeBluetoothGattService(UUID.fromString(uuid), instanceId));
}
}

fakeDevice.mGattCallback.onServicesDiscovered(status);
Expand Down Expand Up @@ -297,16 +304,23 @@ public List<Wrappers.BluetoothGattServiceWrapper> getServices() {
*/
static class FakeBluetoothGattService extends Wrappers.BluetoothGattServiceWrapper {
final int mInstanceId;
final UUID mUuid;

public FakeBluetoothGattService(int instanceId) {
public FakeBluetoothGattService(UUID uuid, int instanceId) {
super(null);
mUuid = uuid;
mInstanceId = instanceId;
}

@Override
public int getInstanceId() {
return mInstanceId;
}

@Override
public UUID getUuid() {
return mUuid;
}
}

// ---------------------------------------------------------------------------------------------
Expand Down
10 changes: 6 additions & 4 deletions device/bluetooth/test/bluetooth_test.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,12 @@ class BluetoothTestBase : public testing::Test {
// Simulates GattConnection disconnecting.
virtual void SimulateGattDisconnection(BluetoothDevice* device) {}

// Simulates success of discovering services. Two services are created.
// TODO(scheib): Add more control over how many services are created and
// their properties. http://crbug.com/541400
virtual void SimulateGattServicesDiscovered(BluetoothDevice* device) {}
// Simulates success of discovering services. |uuids| is used to create a
// service for each UUID string. Multiple UUIDs with the same value produce
// multiple service instances.
virtual void SimulateGattServicesDiscovered(
BluetoothDevice* device,
const std::vector<std::string>& uuids) {}

// Simulates failure to discover services.
virtual void SimulateGattServicesDiscoveryError(BluetoothDevice* device) {}
Expand Down
22 changes: 18 additions & 4 deletions device/bluetooth/test/bluetooth_test_android.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

#include "device/bluetooth/test/bluetooth_test_android.h"

#include <iterator>
#include <sstream>

#include "base/android/jni_string.h"
#include "base/logging.h"
#include "device/bluetooth/android/wrappers.h"
#include "device/bluetooth/bluetooth_adapter_android.h"
Expand Down Expand Up @@ -107,13 +111,22 @@ void BluetoothTestAndroid::SimulateGattDisconnection(BluetoothDevice* device) {
}

void BluetoothTestAndroid::SimulateGattServicesDiscovered(
BluetoothDevice* device) {
BluetoothDevice* device,
const std::vector<std::string>& uuids) {
BluetoothDeviceAndroid* device_android =
static_cast<BluetoothDeviceAndroid*>(device);
JNIEnv* env = base::android::AttachCurrentThread();

// Join UUID strings into a single string.
std::ostringstream uuids_space_delimited;
std::copy(uuids.begin(), uuids.end(),
std::ostream_iterator<std::string>(uuids_space_delimited, " "));

Java_FakeBluetoothDevice_servicesDiscovered(
AttachCurrentThread(), device_android->GetJavaObject().obj(),
0); // android.bluetooth.BluetoothGatt.GATT_SUCCESS
env, device_android->GetJavaObject().obj(),
0, // android.bluetooth.BluetoothGatt.GATT_SUCCESS
base::android::ConvertUTF8ToJavaString(env, uuids_space_delimited.str())
.obj());
}

void BluetoothTestAndroid::SimulateGattServicesDiscoveryError(
Expand All @@ -123,7 +136,8 @@ void BluetoothTestAndroid::SimulateGattServicesDiscoveryError(

Java_FakeBluetoothDevice_servicesDiscovered(
AttachCurrentThread(), device_android->GetJavaObject().obj(),
0x00000101); // android.bluetooth.BluetoothGatt.GATT_FAILURE
0x00000101, // android.bluetooth.BluetoothGatt.GATT_FAILURE
nullptr);
}

void BluetoothTestAndroid::OnFakeBluetoothDeviceConnectGattCalled(
Expand Down
4 changes: 3 additions & 1 deletion device/bluetooth/test/bluetooth_test_android.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ class BluetoothTestAndroid : public BluetoothTestBase {
void SimulateGattConnectionError(BluetoothDevice* device,
BluetoothDevice::ConnectErrorCode) override;
void SimulateGattDisconnection(BluetoothDevice* device) override;
void SimulateGattServicesDiscovered(BluetoothDevice* device) override;
void SimulateGattServicesDiscovered(
BluetoothDevice* device,
const std::vector<std::string>& uuids) override;
void SimulateGattServicesDiscoveryError(BluetoothDevice* device) override;

// Records that Java FakeBluetoothDevice connectGatt was called.
Expand Down
Loading

0 comments on commit 33aa3f4

Please sign in to comment.