summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorarmansito@chromium.org <armansito@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-03 02:25:23 +0000
committerarmansito@chromium.org <armansito@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-03 02:25:23 +0000
commitce8c2a80fddc4866d88dfba43b8d4c39ba1b0063 (patch)
treeb94dd6b80502cdfa77f39545bfb74f627997f03b
parentf1a571eb319c7390f4ab6d2dfdb804fd142236c4 (diff)
downloadchromium_src-ce8c2a80fddc4866d88dfba43b8d4c39ba1b0063.zip
chromium_src-ce8c2a80fddc4866d88dfba43b8d4c39ba1b0063.tar.gz
chromium_src-ce8c2a80fddc4866d88dfba43b8d4c39ba1b0063.tar.bz2
chrome.bluetoothLowEnergy: Implement getCharacteristics.
This CL implements the getCharacteristics method of the bluetoothLowEnergy API and adds a mapping for Characteristic instance IDs to BluetoothLowEnergyEventRouter. BUG=265663 TEST=browser_tests --gtest_filter=BluetoothLowEnergyApiTest.* Review URL: https://codereview.chromium.org/255053002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@268009 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/extensions/api/bluetooth_low_energy/bluetooth_low_energy_api.cc75
-rw-r--r--chrome/browser/extensions/api/bluetooth_low_energy/bluetooth_low_energy_apitest.cc102
-rw-r--r--chrome/browser/extensions/api/bluetooth_low_energy/bluetooth_low_energy_event_router.cc139
-rw-r--r--chrome/browser/extensions/api/bluetooth_low_energy/bluetooth_low_energy_event_router.h15
-rw-r--r--chrome/test/data/extensions/api_test/bluetooth_low_energy/get_characteristics/manifest.json11
-rw-r--r--chrome/test/data/extensions/api_test/bluetooth_low_energy/get_characteristics/runtest.js81
-rw-r--r--device/bluetooth/bluetooth.gyp2
-rw-r--r--device/bluetooth/test/mock_bluetooth_gatt_characteristic.cc37
-rw-r--r--device/bluetooth/test/mock_bluetooth_gatt_characteristic.h55
9 files changed, 509 insertions, 8 deletions
diff --git a/chrome/browser/extensions/api/bluetooth_low_energy/bluetooth_low_energy_api.cc b/chrome/browser/extensions/api/bluetooth_low_energy/bluetooth_low_energy_api.cc
index 972d395..cb637d4 100644
--- a/chrome/browser/extensions/api/bluetooth_low_energy/bluetooth_low_energy_api.cc
+++ b/chrome/browser/extensions/api/bluetooth_low_energy/bluetooth_low_energy_api.cc
@@ -38,6 +38,35 @@ void DoWorkCallback(const base::Callback<bool()>& callback) {
callback.Run();
}
+// TODO(armansito): Remove this function once the described bug is fixed.
+// (See crbug.com/368368).
+//
+// Converts an apibtle::Characteristic to a base::Value. This function is
+// necessary, as json_schema_compiler::util::AddItemToList has no template
+// specialization for user defined enums, which get treated as integers. This is
+// because Characteristic contains a list of enum CharacteristicProperty.
+scoped_ptr<base::DictionaryValue> CharacteristicToValue(
+ apibtle::Characteristic* from) {
+ // Copy the properties. Use Characteristic::ToValue to generate the result
+ // dictionary without the properties, to prevent json_schema_compiler from
+ // failing.
+ std::vector<apibtle::CharacteristicProperty> properties = from->properties;
+ from->properties.clear();
+ scoped_ptr<base::DictionaryValue> to = from->ToValue();
+
+ // Manually set each property.
+ scoped_ptr<base::ListValue> property_list(new base::ListValue());
+ for (std::vector<apibtle::CharacteristicProperty>::iterator iter =
+ properties.begin();
+ iter != properties.end();
+ ++iter)
+ property_list->Append(new base::StringValue(apibtle::ToString(*iter)));
+
+ to->Set("properties", property_list.release());
+
+ return to.Pass();
+}
+
} // namespace
namespace extensions {
@@ -175,10 +204,48 @@ bool BluetoothLowEnergyGetCharacteristicFunction::DoWork() {
}
bool BluetoothLowEnergyGetCharacteristicsFunction::DoWork() {
- // TODO(armansito): Implement.
- SetError("Call not supported.");
- SendResponse(false);
- return false;
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ BluetoothLowEnergyEventRouter* event_router =
+ GetEventRouter(browser_context());
+
+ // The adapter must be initialized at this point, but return an error instead
+ // of asserting.
+ if (!event_router->HasAdapter()) {
+ SetError(kErrorAdapterNotInitialized);
+ SendResponse(false);
+ return false;
+ }
+
+ scoped_ptr<apibtle::GetCharacteristics::Params> params(
+ apibtle::GetCharacteristics::Params::Create(*args_));
+ EXTENSION_FUNCTION_VALIDATE(params.get() != NULL);
+
+ std::string service_id = params->service_id;
+
+ BluetoothLowEnergyEventRouter::CharacteristicList characteristic_list;
+ if (!event_router->GetCharacteristics(service_id, &characteristic_list)) {
+ SetError(
+ base::StringPrintf(kErrorServiceNotFoundFormat, service_id.c_str()));
+
+ SendResponse(false);
+ return false;
+ }
+
+ // Manually construct the result instead of using
+ // apibtle::GetCharacteristics::Result::Create as it doesn't convert lists of
+ // enums correctly.
+ scoped_ptr<base::ListValue> result(new base::ListValue());
+ for (BluetoothLowEnergyEventRouter::CharacteristicList::iterator iter =
+ characteristic_list.begin();
+ iter != characteristic_list.end();
+ ++iter)
+ result->Append(CharacteristicToValue(iter->get()).release());
+
+ SetResult(result.release());
+ SendResponse(true);
+
+ return true;
}
bool BluetoothLowEnergyGetIncludedServicesFunction::DoWork() {
diff --git a/chrome/browser/extensions/api/bluetooth_low_energy/bluetooth_low_energy_apitest.cc b/chrome/browser/extensions/api/bluetooth_low_energy/bluetooth_low_energy_apitest.cc
index fb1da70..3519ac8 100644
--- a/chrome/browser/extensions/api/bluetooth_low_energy/bluetooth_low_energy_apitest.cc
+++ b/chrome/browser/extensions/api/bluetooth_low_energy/bluetooth_low_energy_apitest.cc
@@ -10,17 +10,21 @@
#include "chrome/browser/extensions/extension_test_message_listener.h"
#include "device/bluetooth/test/mock_bluetooth_adapter.h"
#include "device/bluetooth/test/mock_bluetooth_device.h"
+#include "device/bluetooth/test/mock_bluetooth_gatt_characteristic.h"
#include "device/bluetooth/test/mock_bluetooth_gatt_service.h"
#include "testing/gmock/include/gmock/gmock.h"
using device::BluetoothUUID;
using device::BluetoothAdapter;
using device::BluetoothDevice;
+using device::BluetoothGattCharacteristic;
using device::BluetoothGattService;
using device::MockBluetoothAdapter;
using device::MockBluetoothDevice;
+using device::MockBluetoothGattCharacteristic;
using device::MockBluetoothGattService;
using testing::Return;
+using testing::ReturnRefOfCopy;
using testing::_;
namespace utils = extension_function_test_utils;
@@ -32,9 +36,32 @@ const char kTestLeDeviceName[] = "Test LE Device";
const char kTestServiceId0[] = "service_id0";
const char kTestServiceUuid0[] = "1234";
+
const char kTestServiceId1[] = "service_id1";
const char kTestServiceUuid1[] = "5678";
+const char kTestCharacteristicId0[] = "char_id0";
+const char kTestCharacteristicUuid0[] = "1211";
+const BluetoothGattCharacteristic::Properties kTestCharacteristicProperties0 =
+ BluetoothGattCharacteristic::kPropertyBroadcast |
+ BluetoothGattCharacteristic::kPropertyRead |
+ BluetoothGattCharacteristic::kPropertyWriteWithoutResponse |
+ BluetoothGattCharacteristic::kPropertyIndicate;
+const uint8 kTestCharacteristicDefaultValue0[] = {0x01, 0x02, 0x03, 0x04, 0x05};
+
+const char kTestCharacteristicId1[] = "char_id1";
+const char kTestCharacteristicUuid1[] = "1212";
+const BluetoothGattCharacteristic::Properties kTestCharacteristicProperties1 =
+ BluetoothGattCharacteristic::kPropertyRead |
+ BluetoothGattCharacteristic::kPropertyWrite |
+ BluetoothGattCharacteristic::kPropertyNotify;
+const uint8 kTestCharacteristicDefaultValue1[] = {0x06, 0x07, 0x08};
+
+const char kTestCharacteristicId2[] = "char_id1";
+const char kTestCharacteristicUuid2[] = "1213";
+const BluetoothGattCharacteristic::Properties kTestCharacteristicProperties2 =
+ BluetoothGattCharacteristic::kPropertyNone;
+
class BluetoothLowEnergyApiTest : public ExtensionApiTest {
public:
BluetoothLowEnergyApiTest() {}
@@ -79,6 +106,43 @@ class BluetoothLowEnergyApiTest : public ExtensionApiTest {
BluetoothUUID(kTestServiceUuid1),
false /* is_primary */,
false /* is_local */));
+
+ // Assign characteristics some random properties and permissions. They don't
+ // need to reflect what the characteristic is actually capable of, since
+ // the JS API just passes values through from
+ // device::BluetoothGattCharacteristic.
+ std::vector<uint8> default_value;
+ chrc0_.reset(new testing::NiceMock<MockBluetoothGattCharacteristic>(
+ service0_.get(),
+ kTestCharacteristicId0,
+ BluetoothUUID(kTestCharacteristicUuid0),
+ false /* is_local */,
+ kTestCharacteristicProperties0,
+ BluetoothGattCharacteristic::kPermissionNone));
+ default_value.assign(kTestCharacteristicDefaultValue0,
+ (kTestCharacteristicDefaultValue0 +
+ sizeof(kTestCharacteristicDefaultValue0)));
+ ON_CALL(*chrc0_, GetValue()).WillByDefault(ReturnRefOfCopy(default_value));
+
+ chrc1_.reset(new testing::NiceMock<MockBluetoothGattCharacteristic>(
+ service0_.get(),
+ kTestCharacteristicId1,
+ BluetoothUUID(kTestCharacteristicUuid1),
+ false /* is_local */,
+ kTestCharacteristicProperties1,
+ BluetoothGattCharacteristic::kPermissionNone));
+ default_value.assign(kTestCharacteristicDefaultValue1,
+ (kTestCharacteristicDefaultValue1 +
+ sizeof(kTestCharacteristicDefaultValue1)));
+ ON_CALL(*chrc1_, GetValue()).WillByDefault(ReturnRefOfCopy(default_value));
+
+ chrc2_.reset(new testing::NiceMock<MockBluetoothGattCharacteristic>(
+ service1_.get(),
+ kTestCharacteristicId2,
+ BluetoothUUID(kTestCharacteristicUuid2),
+ false /* is_local */,
+ kTestCharacteristicProperties2,
+ BluetoothGattCharacteristic::kPermissionNone));
}
protected:
@@ -91,6 +155,9 @@ class BluetoothLowEnergyApiTest : public ExtensionApiTest {
scoped_ptr<testing::NiceMock<MockBluetoothDevice> > device_;
scoped_ptr<testing::NiceMock<MockBluetoothGattService> > service0_;
scoped_ptr<testing::NiceMock<MockBluetoothGattService> > service1_;
+ scoped_ptr<testing::NiceMock<MockBluetoothGattCharacteristic> > chrc0_;
+ scoped_ptr<testing::NiceMock<MockBluetoothGattCharacteristic> > chrc1_;
+ scoped_ptr<testing::NiceMock<MockBluetoothGattCharacteristic> > chrc2_;
private:
scoped_refptr<extensions::Extension> empty_extension_;
@@ -256,6 +323,41 @@ IN_PROC_BROWSER_TEST_F(BluetoothLowEnergyApiTest, GetIncludedServices) {
listener.Reply("go");
listener.Reset();
+
+ EXPECT_TRUE(listener.WaitUntilSatisfied());
+
+ listener.Reply("go");
+
+ EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
+ event_router()->GattServiceRemoved(device_.get(), service0_.get());
+ event_router()->DeviceRemoved(mock_adapter_, device_.get());
+}
+
+IN_PROC_BROWSER_TEST_F(BluetoothLowEnergyApiTest, GetCharacteristics) {
+ ResultCatcher catcher;
+ catcher.RestrictToProfile(browser()->profile());
+
+ std::vector<BluetoothGattCharacteristic*> characteristics;
+ characteristics.push_back(chrc0_.get());
+ characteristics.push_back(chrc1_.get());
+
+ event_router()->DeviceAdded(mock_adapter_, device_.get());
+ event_router()->GattServiceAdded(device_.get(), service0_.get());
+
+ EXPECT_CALL(*mock_adapter_, GetDevice(_)).Times(3).WillRepeatedly(
+ Return(device_.get()));
+ EXPECT_CALL(*device_, GetGattService(kTestServiceId0))
+ .Times(3)
+ .WillOnce(Return(static_cast<BluetoothGattService*>(NULL)))
+ .WillRepeatedly(Return(service0_.get()));
+ EXPECT_CALL(*service0_, GetCharacteristics())
+ .Times(2)
+ .WillOnce(Return(std::vector<BluetoothGattCharacteristic*>()))
+ .WillOnce(Return(characteristics));
+
+ ExtensionTestMessageListener listener("ready", true);
+ ASSERT_TRUE(LoadExtension(
+ test_data_dir_.AppendASCII("bluetooth_low_energy/get_characteristics")));
EXPECT_TRUE(listener.WaitUntilSatisfied());
listener.Reply("go");
diff --git a/chrome/browser/extensions/api/bluetooth_low_energy/bluetooth_low_energy_event_router.cc b/chrome/browser/extensions/api/bluetooth_low_energy/bluetooth_low_energy_event_router.cc
index d9d96c4..c505780 100644
--- a/chrome/browser/extensions/api/bluetooth_low_energy/bluetooth_low_energy_event_router.cc
+++ b/chrome/browser/extensions/api/bluetooth_low_energy/bluetooth_low_energy_event_router.cc
@@ -9,6 +9,7 @@
#include "base/values.h"
#include "content/public/browser/browser_thread.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
+#include "device/bluetooth/bluetooth_gatt_characteristic.h"
#include "extensions/browser/event_router.h"
using content::BrowserThread;
@@ -39,6 +40,64 @@ void PopulateService(const BluetoothGattService* service,
new std::string(service->GetDevice()->GetAddress()));
}
+void PopulateCharacteristicProperties(
+ BluetoothGattCharacteristic::Properties properties,
+ std::vector<apibtle::CharacteristicProperty>* api_properties) {
+ DCHECK(api_properties && api_properties->empty());
+
+ if (properties == BluetoothGattCharacteristic::kPropertyNone)
+ return;
+
+ if (properties & BluetoothGattCharacteristic::kPropertyBroadcast)
+ api_properties->push_back(apibtle::CHARACTERISTIC_PROPERTY_BROADCAST);
+ if (properties & BluetoothGattCharacteristic::kPropertyRead)
+ api_properties->push_back(apibtle::CHARACTERISTIC_PROPERTY_READ);
+ if (properties & BluetoothGattCharacteristic::kPropertyWriteWithoutResponse) {
+ api_properties->push_back(
+ apibtle::CHARACTERISTIC_PROPERTY_WRITEWITHOUTRESPONSE);
+ }
+ if (properties & BluetoothGattCharacteristic::kPropertyWrite)
+ api_properties->push_back(apibtle::CHARACTERISTIC_PROPERTY_WRITE);
+ if (properties & BluetoothGattCharacteristic::kPropertyNotify)
+ api_properties->push_back(apibtle::CHARACTERISTIC_PROPERTY_NOTIFY);
+ if (properties & BluetoothGattCharacteristic::kPropertyIndicate)
+ api_properties->push_back(apibtle::CHARACTERISTIC_PROPERTY_INDICATE);
+ if (properties &
+ BluetoothGattCharacteristic::kPropertyAuthenticatedSignedWrites) {
+ api_properties->push_back(
+ apibtle::CHARACTERISTIC_PROPERTY_AUTHENTICATEDSIGNEDWRITES);
+ }
+ if (properties & BluetoothGattCharacteristic::kPropertyExtendedProperties) {
+ api_properties->push_back(
+ apibtle::CHARACTERISTIC_PROPERTY_EXTENDEDPROPERTIES);
+ }
+ if (properties & BluetoothGattCharacteristic::kPropertyReliableWrite)
+ api_properties->push_back(apibtle::CHARACTERISTIC_PROPERTY_RELIABLEWRITE);
+ if (properties & BluetoothGattCharacteristic::kPropertyWriteableAuxiliaries) {
+ api_properties->push_back(
+ apibtle::CHARACTERISTIC_PROPERTY_WRITEABLEAUXILIARIES);
+ }
+}
+
+void PopulateCharacteristic(const BluetoothGattCharacteristic* characteristic,
+ apibtle::Characteristic* out) {
+ DCHECK(out);
+
+ out->uuid = characteristic->GetUUID().canonical_value();
+ out->is_local = characteristic->IsLocal();
+ out->instance_id.reset(new std::string(characteristic->GetIdentifier()));
+
+ PopulateService(characteristic->GetService(), &out->service);
+ PopulateCharacteristicProperties(characteristic->GetProperties(),
+ &out->properties);
+
+ const std::vector<uint8>& value = characteristic->GetValue();
+ if (value.empty())
+ return;
+
+ out->value.reset(new std::string(value.begin(), value.end()));
+}
+
} // namespace
namespace extensions {
@@ -217,6 +276,42 @@ bool BluetoothLowEnergyEventRouter::GetIncludedServices(
return true;
}
+bool BluetoothLowEnergyEventRouter::GetCharacteristics(
+ const std::string& instance_id,
+ CharacteristicList* out_characteristics) const {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(out_characteristics);
+ if (!adapter_) {
+ VLOG(1) << "BlutoothAdapter not ready.";
+ return false;
+ }
+
+ BluetoothGattService* service = FindServiceById(instance_id);
+ if (!service) {
+ VLOG(1) << "Service not found: " << instance_id;
+ return false;
+ }
+
+ out_characteristics->clear();
+
+ const std::vector<BluetoothGattCharacteristic*>& characteristics =
+ service->GetCharacteristics();
+ for (std::vector<BluetoothGattCharacteristic*>::const_iterator iter =
+ characteristics.begin();
+ iter != characteristics.end();
+ ++iter) {
+ // Populate an API characteristic and add it to the return value.
+ const BluetoothGattCharacteristic* characteristic = *iter;
+ linked_ptr<apibtle::Characteristic> api_characteristic(
+ new apibtle::Characteristic());
+ PopulateCharacteristic(characteristic, api_characteristic.get());
+
+ out_characteristics->push_back(api_characteristic);
+ }
+
+ return true;
+}
+
void BluetoothLowEnergyEventRouter::SetAdapterForTesting(
device::BluetoothAdapter* adapter) {
adapter_ = adapter;
@@ -324,13 +419,33 @@ void BluetoothLowEnergyEventRouter::GattServiceChanged(
void BluetoothLowEnergyEventRouter::GattCharacteristicAdded(
BluetoothGattService* service,
BluetoothGattCharacteristic* characteristic) {
- // TODO(armansito): Implement.
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ VLOG(2) << "GATT characteristic added: " << characteristic->GetIdentifier();
+
+ DCHECK(chrc_ids_to_objects_.find(characteristic->GetIdentifier()) ==
+ chrc_ids_to_objects_.end());
+
+ GattObjectData data;
+ data.device_address = service->GetDevice()->GetAddress();
+ data.service_id = service->GetIdentifier();
+ data.characteristic_id = characteristic->GetIdentifier();
+ chrc_ids_to_objects_[characteristic->GetIdentifier()] = data;
}
void BluetoothLowEnergyEventRouter::GattCharacteristicRemoved(
BluetoothGattService* service,
BluetoothGattCharacteristic* characteristic) {
- // TODO(armansito): Implement.
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ VLOG(2) << "GATT characteristic removed: " << characteristic->GetIdentifier();
+
+ DCHECK(chrc_ids_to_objects_.find(characteristic->GetIdentifier()) !=
+ chrc_ids_to_objects_.end());
+ DCHECK(service->GetDevice()->GetAddress() ==
+ chrc_ids_to_objects_[characteristic->GetIdentifier()].device_address);
+ DCHECK(service->GetIdentifier() ==
+ chrc_ids_to_objects_[characteristic->GetIdentifier()].service_id);
+
+ chrc_ids_to_objects_.erase(characteristic->GetIdentifier());
}
void BluetoothLowEnergyEventRouter::GattCharacteristicValueChanged(
@@ -359,6 +474,7 @@ void BluetoothLowEnergyEventRouter::OnGetAdapter(
}
void BluetoothLowEnergyEventRouter::InitializeIdentifierMappings() {
+ // Devices
BluetoothAdapter::DeviceList devices = adapter_->GetDevices();
for (BluetoothAdapter::DeviceList::iterator iter = devices.begin();
iter != devices.end();
@@ -367,6 +483,7 @@ void BluetoothLowEnergyEventRouter::InitializeIdentifierMappings() {
device->AddObserver(this);
observed_devices_.insert(device->GetAddress());
+ // Services
std::vector<BluetoothGattService*> services = device->GetGattServices();
for (std::vector<BluetoothGattService*>::iterator siter = services.begin();
siter != services.end();
@@ -380,7 +497,23 @@ void BluetoothLowEnergyEventRouter::InitializeIdentifierMappings() {
service_data.service_id = service->GetIdentifier();
service_ids_to_objects_[service_data.service_id] = service_data;
- // TODO(armansito): Initialize mapping for characteristics & descriptors.
+ // Characteristics
+ std::vector<BluetoothGattCharacteristic*> characteristics =
+ service->GetCharacteristics();
+ for (std::vector<BluetoothGattCharacteristic*>::iterator citer =
+ characteristics.begin();
+ citer != characteristics.end();
+ ++citer) {
+ BluetoothGattCharacteristic* characteristic = *citer;
+ GattObjectData characteristic_data;
+ characteristic_data.device_address = device->GetAddress();
+ characteristic_data.service_id = service->GetIdentifier();
+ characteristic_data.characteristic_id = characteristic->GetIdentifier();
+ chrc_ids_to_objects_[characteristic_data.characteristic_id] =
+ characteristic_data;
+
+ // TODO(armansito): Initialize mapping for descriptors.
+ }
}
}
}
diff --git a/chrome/browser/extensions/api/bluetooth_low_energy/bluetooth_low_energy_event_router.h b/chrome/browser/extensions/api/bluetooth_low_energy/bluetooth_low_energy_event_router.h
index 82e20ad..6ad37262 100644
--- a/chrome/browser/extensions/api/bluetooth_low_energy/bluetooth_low_energy_event_router.h
+++ b/chrome/browser/extensions/api/bluetooth_low_energy/bluetooth_low_energy_event_router.h
@@ -61,7 +61,7 @@ class BluetoothLowEnergyEventRouter
// with the Bluetooth device with address |device_address| in |out_services|.
// Returns false, if no device with the given address is known. If the device
// is found but it has no GATT services, then returns true and leaves
- // |out_services| as empty. Returns true, on success. |out_services| must not
+ // |out_services| empty. Returns true, on success. |out_services| must not
// be NULL. If it is non-empty, then its contents will be cleared.
typedef std::vector<linked_ptr<api::bluetooth_low_energy::Service> >
ServiceList;
@@ -83,6 +83,18 @@ class BluetoothLowEnergyEventRouter
bool GetIncludedServices(const std::string& instance_id,
ServiceList* out_services) const;
+ // Returns the list of api::bluetooth_low_energy::Characteristic objects
+ // associated with the GATT service with instance ID |instance_id| in
+ // |out_characteristics|. Returns false, if no service with the given instance
+ // ID is known. If the service is found but it has no characteristics, then
+ // returns true and leaves |out_characteristics| empty. Returns true on
+ // success. |out_characteristics| must not be NULL and if it is non-empty,
+ // then its contents will be cleared.
+ typedef std::vector<linked_ptr<api::bluetooth_low_energy::Characteristic> >
+ CharacteristicList;
+ bool GetCharacteristics(const std::string& instance_id,
+ CharacteristicList* out_characteristics) const;
+
// Initializes the adapter for testing. Used by unit tests only.
void SetAdapterForTesting(device::BluetoothAdapter* adapter);
@@ -153,6 +165,7 @@ class BluetoothLowEnergyEventRouter
// device::BluetoothGattService that owns the characteristic.
typedef std::map<std::string, GattObjectData> InstanceIdToObjectDataMap;
InstanceIdToObjectDataMap service_ids_to_objects_;
+ InstanceIdToObjectDataMap chrc_ids_to_objects_;
// Sets of BluetoothDevice and BluetoothGattService objects that are being
// observed, used to remove the BluetoothLowEnergyEventRouter as an observer
diff --git a/chrome/test/data/extensions/api_test/bluetooth_low_energy/get_characteristics/manifest.json b/chrome/test/data/extensions/api_test/bluetooth_low_energy/get_characteristics/manifest.json
new file mode 100644
index 0000000..b6ec89b
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/bluetooth_low_energy/get_characteristics/manifest.json
@@ -0,0 +1,11 @@
+{
+ "manifest_version": 2,
+ "name": "Test the Bluetooth Low Energy getCharacteristics API",
+ "version": "1.0",
+ "app": {
+ "background": {
+ "scripts": ["runtest.js"]
+ }
+ },
+ "bluetooth": {}
+}
diff --git a/chrome/test/data/extensions/api_test/bluetooth_low_energy/get_characteristics/runtest.js b/chrome/test/data/extensions/api_test/bluetooth_low_energy/get_characteristics/runtest.js
new file mode 100644
index 0000000..9e16b4b
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/bluetooth_low_energy/get_characteristics/runtest.js
@@ -0,0 +1,81 @@
+// 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.
+
+function testGetCharacteristics() {
+ chrome.test.assertEq(2, chrcs.length);
+
+ chrome.test.assertEq('char_id0', chrcs[0].instanceId),
+ chrome.test.assertEq('00001211-0000-1000-8000-00805f9b34fb', chrcs[0].uuid);
+ chrome.test.assertEq(false, chrcs[0].isLocal);
+ chrome.test.assertEq(serviceId, chrcs[0].service.instanceId);
+ chrome.test.assertEq(4, chrcs[0].properties.length);
+ chrome.test.assertTrue(chrcs[0].properties.indexOf('broadcast') > -1,
+ '\'broadcast\' not in chrcs[0].properties');
+ chrome.test.assertTrue(chrcs[0].properties.indexOf('read') > -1,
+ '\'read\' not in chrcs[0].properties');
+ chrome.test.assertTrue(chrcs[0].properties.indexOf('indicate') > -1,
+ '\'indicate\' not in chrcs[0].properties');
+ chrome.test.assertTrue(
+ chrcs[0].properties.indexOf('writeWithoutResponse') > -1,
+ '\'writeWithoutResponse\' not in chrcs[0].properties');
+
+ var valueBytes = new Uint8Array(chrcs[0].value);
+ chrome.test.assertEq(5, chrcs[0].value.byteLength);
+ chrome.test.assertEq(0x01, valueBytes[0]);
+ chrome.test.assertEq(0x02, valueBytes[1]);
+ chrome.test.assertEq(0x03, valueBytes[2]);
+ chrome.test.assertEq(0x04, valueBytes[3]);
+ chrome.test.assertEq(0x05, valueBytes[4]);
+
+ chrome.test.assertEq('char_id1', chrcs[1].instanceId),
+ chrome.test.assertEq('00001212-0000-1000-8000-00805f9b34fb', chrcs[1].uuid);
+ chrome.test.assertEq(false, chrcs[1].isLocal);
+ chrome.test.assertEq(serviceId, chrcs[1].service.instanceId);
+ chrome.test.assertEq(3, chrcs[1].properties.length);
+ chrome.test.assertTrue(chrcs[1].properties.indexOf('read') > -1,
+ '\'read\' not in chrcs[1].properties');
+ chrome.test.assertTrue(chrcs[1].properties.indexOf('write') > -1,
+ '\'write\' not in chrcs[1].properties');
+ chrome.test.assertTrue(chrcs[1].properties.indexOf('notify') > -1,
+ '\'notify\' not in chrcs[1].properties');
+
+ valueBytes = new Uint8Array(chrcs[1].value);
+ chrome.test.assertEq(3, chrcs[1].value.byteLength);
+ chrome.test.assertEq(0x06, valueBytes[0]);
+ chrome.test.assertEq(0x07, valueBytes[1]);
+ chrome.test.assertEq(0x08, valueBytes[2]);
+
+ chrome.test.succeed();
+}
+
+var serviceId = 'service_id0';
+var chrcs = null;
+
+function failOnError() {
+ if (chrome.runtime.lastError) {
+ chrome.test.fail(chrome.runtime.lastError.message);
+ }
+}
+
+chrome.bluetoothLowEnergy.getCharacteristics(serviceId, function (result) {
+ if (result || !chrome.runtime.lastError) {
+ chrome.test.fail('getCharacteristics should have failed.');
+ }
+
+ chrome.bluetoothLowEnergy.getCharacteristics(serviceId, function (result) {
+ failOnError();
+ if (!result || result.length != 0) {
+ chrome.test.fail('Characteristics should be empty.');
+ }
+
+ chrome.bluetoothLowEnergy.getCharacteristics(serviceId, function (result) {
+ failOnError();
+ chrcs = result;
+
+ chrome.test.sendMessage('ready', function (message) {
+ chrome.test.runTests([testGetCharacteristics]);
+ });
+ });
+ });
+});
diff --git a/device/bluetooth/bluetooth.gyp b/device/bluetooth/bluetooth.gyp
index deb7d26a..ec06dec 100644
--- a/device/bluetooth/bluetooth.gyp
+++ b/device/bluetooth/bluetooth.gyp
@@ -136,6 +136,8 @@
'test/mock_bluetooth_device.h',
'test/mock_bluetooth_discovery_session.cc',
'test/mock_bluetooth_discovery_session.h',
+ 'test/mock_bluetooth_gatt_characteristic.cc',
+ 'test/mock_bluetooth_gatt_characteristic.h',
'test/mock_bluetooth_gatt_service.cc',
'test/mock_bluetooth_gatt_service.h',
'test/mock_bluetooth_profile.cc',
diff --git a/device/bluetooth/test/mock_bluetooth_gatt_characteristic.cc b/device/bluetooth/test/mock_bluetooth_gatt_characteristic.cc
new file mode 100644
index 0000000..5cc4da3
--- /dev/null
+++ b/device/bluetooth/test/mock_bluetooth_gatt_characteristic.cc
@@ -0,0 +1,37 @@
+// 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/test/mock_bluetooth_gatt_characteristic.h"
+
+#include "device/bluetooth/test/mock_bluetooth_gatt_service.h"
+
+using testing::Return;
+using testing::ReturnRefOfCopy;
+using testing::_;
+
+namespace device {
+
+MockBluetoothGattCharacteristic::MockBluetoothGattCharacteristic(
+ MockBluetoothGattService* service,
+ const std::string& identifier,
+ const BluetoothUUID& uuid,
+ bool is_local,
+ Properties properties,
+ Permissions permissions) {
+ ON_CALL(*this, GetIdentifier()).WillByDefault(Return(identifier));
+ ON_CALL(*this, GetUUID()).WillByDefault(Return(uuid));
+ ON_CALL(*this, IsLocal()).WillByDefault(Return(is_local));
+ ON_CALL(*this, GetValue())
+ .WillByDefault(ReturnRefOfCopy(std::vector<uint8>()));
+ ON_CALL(*this, GetService()).WillByDefault(Return(service));
+ ON_CALL(*this, GetProperties()).WillByDefault(Return(properties));
+ ON_CALL(*this, GetPermissions()).WillByDefault(Return(permissions));
+ ON_CALL(*this, GetDescriptors())
+ .WillByDefault(Return(std::vector<BluetoothGattDescriptor*>()));
+}
+
+MockBluetoothGattCharacteristic::~MockBluetoothGattCharacteristic() {
+}
+
+} // namespace device
diff --git a/device/bluetooth/test/mock_bluetooth_gatt_characteristic.h b/device/bluetooth/test/mock_bluetooth_gatt_characteristic.h
new file mode 100644
index 0000000..6619543
--- /dev/null
+++ b/device/bluetooth/test/mock_bluetooth_gatt_characteristic.h
@@ -0,0 +1,55 @@
+// 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.
+
+#ifndef DEVICE_BLUETOOTH_TEST_MOCK_BLUETOOTH_GATT_CHARACTERISTIC_H_
+#define DEVICE_BLUETOOTH_TEST_MOCK_BLUETOOTH_GATT_CHARACTERISTIC_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "device/bluetooth/bluetooth_gatt_characteristic.h"
+#include "device/bluetooth/bluetooth_uuid.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace device {
+
+class BluetoothGattDescriptor;
+class BluetoothGattService;
+class MockBluetoothGattService;
+
+class MockBluetoothGattCharacteristic : public BluetoothGattCharacteristic {
+ public:
+ MockBluetoothGattCharacteristic(MockBluetoothGattService* service,
+ const std::string& identifier,
+ const BluetoothUUID& uuid,
+ bool is_local,
+ Properties properties,
+ Permissions permissions);
+ virtual ~MockBluetoothGattCharacteristic();
+
+ MOCK_CONST_METHOD0(GetIdentifier, std::string());
+ MOCK_CONST_METHOD0(GetUUID, BluetoothUUID());
+ MOCK_CONST_METHOD0(IsLocal, bool());
+ MOCK_CONST_METHOD0(GetValue, const std::vector<uint8>&());
+ MOCK_CONST_METHOD0(GetService, BluetoothGattService*());
+ MOCK_CONST_METHOD0(GetProperties, Properties());
+ MOCK_CONST_METHOD0(GetPermissions, Permissions());
+ MOCK_CONST_METHOD0(GetDescriptors, std::vector<BluetoothGattDescriptor*>());
+ MOCK_METHOD1(AddDescriptor, bool(BluetoothGattDescriptor*));
+ MOCK_METHOD1(UpdateValue, bool(const std::vector<uint8>&));
+ MOCK_METHOD2(ReadRemoteCharacteristic,
+ void(const ValueCallback&, const ErrorCallback&));
+ MOCK_METHOD3(WriteRemoteCharacteristic,
+ void(const std::vector<uint8>&,
+ const base::Closure&,
+ const ErrorCallback&));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockBluetoothGattCharacteristic);
+};
+
+} // namespace device
+
+#endif // DEVICE_BLUETOOTH_TEST_MOCK_BLUETOOTH_GATT_CHARACTERISTIC_H_