Commit 4eee6eb0 authored by ortuno's avatar ortuno Committed by Commit bot

bluetooth: Add characteristics to the device's attribute instance map

Results in multiple calls to getCharacteristic(s) to return the same object.

BUG=495270

Review-Url: https://codereview.chromium.org/2474863004
Cr-Commit-Position: refs/heads/master@{#430207}
parent 344468c8
<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
<!DOCTYPE html>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
<script>
'use strict';
promise_test(() => {
return setBluetoothFakeAdapter('HeartRateAdapter')
.then(() => requestDeviceWithKeyDown({
filters: [{services: ['heart_rate']}]}))
.then(device => device.gatt.connect())
.then(gattServer => gattServer.getPrimaryService('heart_rate'))
.then(service => Promise.all([
service.getCharacteristic('body_sensor_location'),
service.getCharacteristic('body_sensor_location')]))
.then(characteristics_arrays => {
// Convert to arrays if necessary.
for (let i = 0; i < characteristics_arrays.length; i++) {
characteristics_arrays[i] = [].concat(characteristics_arrays[i]);
}
for (let i = 1; i < characteristics_arrays.length; i++) {
assert_equals(characteristics_arrays[0].length,
characteristics_arrays[i].length);
}
let base_set = new Set(characteristics_arrays[0]);
for (let characteristics of characteristics_arrays) {
characteristics.forEach(
characteristic => assert_true(base_set.has(characteristic)));
}
});
}, 'Calls to getCharacteristic should return the same object.');
</script>
<!DOCTYPE html>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
<script>
'use strict';
promise_test(() => {
return setBluetoothFakeAdapter('HeartRateAdapter')
.then(() => requestDeviceWithKeyDown({
filters: [{services: ['heart_rate']}],
optionalServices: ['generic_access']}))
.then(device => device.gatt.connect())
.then(gattServer => gattServer.getPrimaryService('generic_access'))
.then(services => Promise.all([
services.getCharacteristic('gap.device_name'),
services.getCharacteristic('gap.device_name')]))
.then(characteristics => {
// TODO(ortuno): getCharacteristic should return the same object
// if it was created earlier.
// https://crbug.com/495270
for (var i = 1; i < characteristics.length; i++) {
assert_not_equals(
characteristics[0], characteristics[i],
'Should return the same characteristic as the first call.');
}
});
}, 'Calls to get the same characteristic should return the same object.');
</script>
<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
<!DOCTYPE html> <!DOCTYPE html>
<script src="../../resources/testharness.js"></script> <script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script> <script src="../../resources/testharnessreport.js"></script>
...@@ -13,16 +14,23 @@ promise_test(() => { ...@@ -13,16 +14,23 @@ promise_test(() => {
.then(service => Promise.all([ .then(service => Promise.all([
service.getCharacteristics('body_sensor_location'), service.getCharacteristics('body_sensor_location'),
service.getCharacteristics('body_sensor_location')])) service.getCharacteristics('body_sensor_location')]))
.then(characteristics => { .then(characteristics_arrays => {
let chars1 = characteristics[0]; // Convert to arrays if necessary.
let chars2 = characteristics[1]; for (let i = 0; i < characteristics_arrays.length; i++) {
assert_equals(chars1.length, chars2.length); characteristics_arrays[i] = [].concat(characteristics_arrays[i]);
// TODO(ortuno): getCharacteristics should return the same objects }
// if they were created earlier.
// https://crbug.com/495270 for (let i = 1; i < characteristics_arrays.length; i++) {
for (let i = 0; i < chars1.length; i++) { assert_equals(characteristics_arrays[0].length,
assert_not_equals(chars1[i], chars2[i]); characteristics_arrays[i].length);
}
let base_set = new Set(characteristics_arrays[0]);
for (let characteristics of characteristics_arrays) {
characteristics.forEach(
characteristic => assert_true(base_set.has(characteristic)));
} }
}); });
}, 'Calls to get the same characteristics should return the same objects.'); }, 'Calls to getCharacteristics should return the same object.');
</script> </script>
<!-- Generated by //third_party/WebKit/LayoutTests/bluetooth/generate.py -->
<!DOCTYPE html>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
<script>
'use strict';
promise_test(() => {
return setBluetoothFakeAdapter('HeartRateAdapter')
.then(() => requestDeviceWithKeyDown({
filters: [{services: ['heart_rate']}]}))
.then(device => device.gatt.connect())
.then(gattServer => gattServer.getPrimaryService('heart_rate'))
.then(service => Promise.all([
service.getCharacteristics(),
service.getCharacteristics()]))
.then(characteristics_arrays => {
// Convert to arrays if necessary.
for (let i = 0; i < characteristics_arrays.length; i++) {
characteristics_arrays[i] = [].concat(characteristics_arrays[i]);
}
for (let i = 1; i < characteristics_arrays.length; i++) {
assert_equals(characteristics_arrays[0].length,
characteristics_arrays[i].length);
}
let base_set = new Set(characteristics_arrays[0]);
for (let characteristics of characteristics_arrays) {
characteristics.forEach(
characteristic => assert_true(base_set.has(characteristic)));
}
});
}, 'Calls to getCharacteristics should return the same object.');
</script>
'use strict';
promise_test(() => {
return setBluetoothFakeAdapter('HeartRateAdapter')
.then(() => requestDeviceWithKeyDown({
filters: [{services: ['heart_rate']}]}))
.then(device => device.gatt.connect())
.then(gattServer => gattServer.getPrimaryService('heart_rate'))
.then(service => Promise.all([
service.CALLS([
getCharacteristic('body_sensor_location')|
getCharacteristics()|
getCharacteristics('body_sensor_location')[UUID]]),
service.PREVIOUS_CALL]))
.then(characteristics_arrays => {
// Convert to arrays if necessary.
for (let i = 0; i < characteristics_arrays.length; i++) {
characteristics_arrays[i] = [].concat(characteristics_arrays[i]);
}
for (let i = 1; i < characteristics_arrays.length; i++) {
assert_equals(characteristics_arrays[0].length,
characteristics_arrays[i].length);
}
let base_set = new Set(characteristics_arrays[0]);
for (let characteristics of characteristics_arrays) {
characteristics.forEach(
characteristic => assert_true(base_set.has(characteristic)));
}
});
}, 'Calls to FUNCTION_NAME should return the same object.');
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include "modules/bluetooth/BluetoothDevice.h" #include "modules/bluetooth/BluetoothDevice.h"
#include "modules/bluetooth/BluetoothRemoteGATTService.h" #include "modules/bluetooth/BluetoothRemoteGATTService.h"
#include "public/platform/modules/bluetooth/WebBluetoothRemoteGATTCharacteristicInit.h"
#include "public/platform/modules/bluetooth/WebBluetoothRemoteGATTService.h" #include "public/platform/modules/bluetooth/WebBluetoothRemoteGATTService.h"
#include <memory> #include <memory>
#include <utility> #include <utility>
...@@ -38,6 +39,25 @@ bool BluetoothAttributeInstanceMap::containsService( ...@@ -38,6 +39,25 @@ bool BluetoothAttributeInstanceMap::containsService(
return m_serviceIdToObject.contains(serviceInstanceId); return m_serviceIdToObject.contains(serviceInstanceId);
} }
BluetoothRemoteGATTCharacteristic*
BluetoothAttributeInstanceMap::getOrCreateBluetoothRemoteGATTCharacteristic(
ExecutionContext* context,
std::unique_ptr<WebBluetoothRemoteGATTCharacteristicInit> webCharacteristic,
BluetoothRemoteGATTService* service) {
String characteristicInstanceId = webCharacteristic->characteristicInstanceID;
BluetoothRemoteGATTCharacteristic* characteristic =
m_characteristicIdToObject.get(characteristicInstanceId);
if (!characteristic) {
characteristic = BluetoothRemoteGATTCharacteristic::create(
context, std::move(webCharacteristic), service);
m_characteristicIdToObject.add(characteristicInstanceId, characteristic);
}
return characteristic;
}
void BluetoothAttributeInstanceMap::Clear() { void BluetoothAttributeInstanceMap::Clear() {
m_serviceIdToObject.clear(); m_serviceIdToObject.clear();
} }
...@@ -45,6 +65,7 @@ void BluetoothAttributeInstanceMap::Clear() { ...@@ -45,6 +65,7 @@ void BluetoothAttributeInstanceMap::Clear() {
DEFINE_TRACE(BluetoothAttributeInstanceMap) { DEFINE_TRACE(BluetoothAttributeInstanceMap) {
visitor->trace(m_device); visitor->trace(m_device);
visitor->trace(m_serviceIdToObject); visitor->trace(m_serviceIdToObject);
visitor->trace(m_characteristicIdToObject);
} }
} // namespace blink } // namespace blink
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#ifndef BluetoothAttributeInstanceMap_h #ifndef BluetoothAttributeInstanceMap_h
#define BluetoothAttributeInstanceMap_h #define BluetoothAttributeInstanceMap_h
#include "modules/bluetooth/BluetoothRemoteGATTCharacteristic.h"
#include "modules/bluetooth/BluetoothRemoteGATTService.h" #include "modules/bluetooth/BluetoothRemoteGATTService.h"
#include "platform/heap/Handle.h" #include "platform/heap/Handle.h"
#include "platform/heap/Heap.h" #include "platform/heap/Heap.h"
...@@ -13,7 +14,10 @@ ...@@ -13,7 +14,10 @@
namespace blink { namespace blink {
class BluetoothDevice; class BluetoothDevice;
class ExecutionContext;
class ScriptPromiseResolver; class ScriptPromiseResolver;
struct WebBluetoothRemoteGATTCharacteristicInit;
struct WebBluetoothRemoteGATTService; struct WebBluetoothRemoteGATTService;
// Map that holds all GATT attributes, i.e. BluetoothRemoteGATTService, // Map that holds all GATT attributes, i.e. BluetoothRemoteGATTService,
...@@ -36,6 +40,16 @@ class BluetoothAttributeInstanceMap final ...@@ -36,6 +40,16 @@ class BluetoothAttributeInstanceMap final
// is in the map. // is in the map.
bool containsService(const String& serviceInstanceId); bool containsService(const String& serviceInstanceId);
// Constructs a new BluetoothRemoteGATTCharacteristic object if there was no
// characteristic with the same instance id and adds it to the map.
// Otherwise returns the BluetoothRemoteGATTCharacteristic object already in
// the map.
BluetoothRemoteGATTCharacteristic*
getOrCreateBluetoothRemoteGATTCharacteristic(
ExecutionContext*,
std::unique_ptr<WebBluetoothRemoteGATTCharacteristicInit>,
BluetoothRemoteGATTService*);
// Removes all Attributes from the map. // Removes all Attributes from the map.
// TODO(crbug.com/654950): Remove characteristics and descriptors when // TODO(crbug.com/654950): Remove characteristics and descriptors when
// implemented. // implemented.
...@@ -48,6 +62,9 @@ class BluetoothAttributeInstanceMap final ...@@ -48,6 +62,9 @@ class BluetoothAttributeInstanceMap final
Member<BluetoothDevice> m_device; Member<BluetoothDevice> m_device;
// Map of service instance ids to objects. // Map of service instance ids to objects.
HeapHashMap<String, Member<BluetoothRemoteGATTService>> m_serviceIdToObject; HeapHashMap<String, Member<BluetoothRemoteGATTService>> m_serviceIdToObject;
// Map of characteristic instance ids to objects.
HeapHashMap<String, Member<BluetoothRemoteGATTCharacteristic>>
m_characteristicIdToObject;
}; };
} // namespace blink } // namespace blink
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "modules/bluetooth/BluetoothRemoteGATTServer.h" #include "modules/bluetooth/BluetoothRemoteGATTServer.h"
#include "modules/bluetooth/BluetoothSupplement.h" #include "modules/bluetooth/BluetoothSupplement.h"
#include "public/platform/modules/bluetooth/WebBluetooth.h" #include "public/platform/modules/bluetooth/WebBluetooth.h"
#include "public/platform/modules/bluetooth/WebBluetoothRemoteGATTCharacteristicInit.h"
#include <memory> #include <memory>
#include <utility> #include <utility>
...@@ -49,6 +50,15 @@ bool BluetoothDevice::isValidService(const String& serviceInstanceId) { ...@@ -49,6 +50,15 @@ bool BluetoothDevice::isValidService(const String& serviceInstanceId) {
return m_attributeInstanceMap->containsService(serviceInstanceId); return m_attributeInstanceMap->containsService(serviceInstanceId);
} }
BluetoothRemoteGATTCharacteristic*
BluetoothDevice::getOrCreateBluetoothRemoteGATTCharacteristic(
ExecutionContext* context,
std::unique_ptr<WebBluetoothRemoteGATTCharacteristicInit> webCharacteristic,
BluetoothRemoteGATTService* service) {
return m_attributeInstanceMap->getOrCreateBluetoothRemoteGATTCharacteristic(
context, std::move(webCharacteristic), service);
}
void BluetoothDevice::dispose() { void BluetoothDevice::dispose() {
disconnectGATTIfConnected(); disconnectGATTIfConnected();
} }
......
...@@ -18,11 +18,13 @@ ...@@ -18,11 +18,13 @@
namespace blink { namespace blink {
class BluetoothAttributeInstanceMap; class BluetoothAttributeInstanceMap;
class BluetoothRemoteGATTCharacteristic;
class BluetoothRemoteGATTServer; class BluetoothRemoteGATTServer;
class BluetoothRemoteGATTService; class BluetoothRemoteGATTService;
class ScriptPromise; class ScriptPromise;
class ScriptPromiseResolver; class ScriptPromiseResolver;
struct WebBluetoothRemoteGATTCharacteristicInit;
struct WebBluetoothRemoteGATTService; struct WebBluetoothRemoteGATTService;
// BluetoothDevice represents a physical bluetooth device in the DOM. See IDL. // BluetoothDevice represents a physical bluetooth device in the DOM. See IDL.
...@@ -50,6 +52,12 @@ class BluetoothDevice final : public EventTargetWithInlineData, ...@@ -50,6 +52,12 @@ class BluetoothDevice final : public EventTargetWithInlineData,
std::unique_ptr<WebBluetoothRemoteGATTService>); std::unique_ptr<WebBluetoothRemoteGATTService>);
bool isValidService(const String& serviceInstanceId); bool isValidService(const String& serviceInstanceId);
BluetoothRemoteGATTCharacteristic*
getOrCreateBluetoothRemoteGATTCharacteristic(
ExecutionContext*,
std::unique_ptr<WebBluetoothRemoteGATTCharacteristicInit>,
BluetoothRemoteGATTService*);
// We should disconnect from the device in all of the following cases: // We should disconnect from the device in all of the following cases:
// 1. When the object gets GarbageCollected e.g. it went out of scope. // 1. When the object gets GarbageCollected e.g. it went out of scope.
// dispose() is called in this case. // dispose() is called in this case.
......
...@@ -4,7 +4,6 @@ ...@@ -4,7 +4,6 @@
#include "modules/bluetooth/BluetoothRemoteGATTCharacteristic.h" #include "modules/bluetooth/BluetoothRemoteGATTCharacteristic.h"
#include "bindings/core/v8/CallbackPromiseAdapter.h"
#include "bindings/core/v8/ScriptPromise.h" #include "bindings/core/v8/ScriptPromise.h"
#include "bindings/core/v8/ScriptPromiseResolver.h" #include "bindings/core/v8/ScriptPromiseResolver.h"
#include "core/dom/DOMDataView.h" #include "core/dom/DOMDataView.h"
...@@ -52,17 +51,15 @@ BluetoothRemoteGATTCharacteristic::BluetoothRemoteGATTCharacteristic( ...@@ -52,17 +51,15 @@ BluetoothRemoteGATTCharacteristic::BluetoothRemoteGATTCharacteristic(
ThreadState::current()->registerPreFinalizer(this); ThreadState::current()->registerPreFinalizer(this);
} }
BluetoothRemoteGATTCharacteristic* BluetoothRemoteGATTCharacteristic::take( BluetoothRemoteGATTCharacteristic* BluetoothRemoteGATTCharacteristic::create(
ScriptPromiseResolver* resolver, ExecutionContext* context,
std::unique_ptr<WebBluetoothRemoteGATTCharacteristicInit> webCharacteristic, std::unique_ptr<WebBluetoothRemoteGATTCharacteristicInit> webCharacteristic,
BluetoothRemoteGATTService* service) { BluetoothRemoteGATTService* service) {
if (!webCharacteristic) { DCHECK(webCharacteristic);
return nullptr;
}
BluetoothRemoteGATTCharacteristic* characteristic = BluetoothRemoteGATTCharacteristic* characteristic =
new BluetoothRemoteGATTCharacteristic(resolver->getExecutionContext(), new BluetoothRemoteGATTCharacteristic(
std::move(webCharacteristic), context, std::move(webCharacteristic), service);
service);
// See note in ActiveDOMObject about suspendIfNeeded. // See note in ActiveDOMObject about suspendIfNeeded.
characteristic->suspendIfNeeded(); characteristic->suspendIfNeeded();
return characteristic; return characteristic;
......
...@@ -47,10 +47,8 @@ class BluetoothRemoteGATTCharacteristic final ...@@ -47,10 +47,8 @@ class BluetoothRemoteGATTCharacteristic final
std::unique_ptr<WebBluetoothRemoteGATTCharacteristicInit>, std::unique_ptr<WebBluetoothRemoteGATTCharacteristicInit>,
BluetoothRemoteGATTService*); BluetoothRemoteGATTService*);
// Interface required by CallbackPromiseAdapter. static BluetoothRemoteGATTCharacteristic* create(
using WebType = std::unique_ptr<WebBluetoothRemoteGATTCharacteristicInit>; ExecutionContext*,
static BluetoothRemoteGATTCharacteristic* take(
ScriptPromiseResolver*,
std::unique_ptr<WebBluetoothRemoteGATTCharacteristicInit>, std::unique_ptr<WebBluetoothRemoteGATTCharacteristicInit>,
BluetoothRemoteGATTService*); BluetoothRemoteGATTService*);
......
...@@ -76,8 +76,10 @@ class GetCharacteristicsCallback ...@@ -76,8 +76,10 @@ class GetCharacteristicsCallback
if (m_quantity == mojom::blink::WebBluetoothGATTQueryQuantity::SINGLE) { if (m_quantity == mojom::blink::WebBluetoothGATTQueryQuantity::SINGLE) {
DCHECK_EQ(1u, webCharacteristics.size()); DCHECK_EQ(1u, webCharacteristics.size());
m_resolver->resolve(BluetoothRemoteGATTCharacteristic::take( m_resolver->resolve(
m_resolver, wrapUnique(webCharacteristics[0]), m_service)); m_service->device()->getOrCreateBluetoothRemoteGATTCharacteristic(
m_resolver->getExecutionContext(),
wrapUnique(webCharacteristics[0]), m_service));
return; return;
} }
...@@ -85,8 +87,10 @@ class GetCharacteristicsCallback ...@@ -85,8 +87,10 @@ class GetCharacteristicsCallback
characteristics.reserveInitialCapacity(webCharacteristics.size()); characteristics.reserveInitialCapacity(webCharacteristics.size());
for (WebBluetoothRemoteGATTCharacteristicInit* webCharacteristic : for (WebBluetoothRemoteGATTCharacteristicInit* webCharacteristic :
webCharacteristics) { webCharacteristics) {
characteristics.append(BluetoothRemoteGATTCharacteristic::take( characteristics.append(
m_resolver, wrapUnique(webCharacteristic), m_service)); m_service->device()->getOrCreateBluetoothRemoteGATTCharacteristic(
m_resolver->getExecutionContext(), wrapUnique(webCharacteristic),
m_service));
} }
m_resolver->resolve(characteristics); m_resolver->resolve(characteristics);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment