Skip to content

Commit

Permalink
[Web Bluetooth] Add manufacturerData filter when requesting device
Browse files Browse the repository at this point in the history
This CL adds support for new manufacturerData filter so that developers
can request Bluetooth LE devices based on manufacturer specific data
(company identifier and data).

Spec: WebBluetoothCG/web-bluetooth#545
Test: https://manufacturer-data.glitch.me/
Bug: 707635
Change-Id: I63b80812f35c8f0f557ceaf53632d0d6d2d52b9b
  • Loading branch information
beaufortfrancois authored and chromium-wpt-export-bot committed May 5, 2021
1 parent 2058465 commit a2de699
Show file tree
Hide file tree
Showing 10 changed files with 393 additions and 20 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// META: script=/resources/testharness.js
// META: script=/resources/testharnessreport.js
// META: script=/resources/testdriver.js
// META: script=/resources/testdriver-vendor.js
// META: script=/bluetooth/resources/bluetooth-test.js
// META: script=/bluetooth/resources/bluetooth-fake-devices.js
'use strict';
const test_desc =
'Manufacturer data mask size must be equal to dataPrefix size.';

bluetooth_test(async (t) => {
const companyIdentifier = 0x0001;
const dataPrefix = new Uint8Array([0x01, 0x02, 0x03, 0x04]);
const mask = new Uint8Array([0xff]);

await promise_rejects_js(
t, TypeError,
requestDeviceWithTrustedClick(
{filters: [{manufacturerData: [{companyIdentifier, mask}]}]}));
await promise_rejects_js(
t, TypeError, requestDeviceWithTrustedClick({
filters: [{manufacturerData: [{companyIdentifier, dataPrefix, mask}]}]
}));
}, test_desc);
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// META: script=/resources/testdriver.js
// META: script=/resources/testdriver-vendor.js
// META: script=/bluetooth/resources/bluetooth-test.js
// META: script=/bluetooth/resources/bluetooth-fake-devices.js
'use strict';
const test_desc = 'dataPrefix value buffer must not be detached';

function detachBuffer(buffer) {
window.postMessage('', '*', [buffer]);
}

bluetooth_test(async (t) => {
const companyIdentifier = 0x0001;

const typed_array = Uint8Array.of(1, 2);
detachBuffer(typed_array.buffer);

await promise_rejects_dom(
t, 'InvalidStateError', requestDeviceWithTrustedClick({
filters:
[{manufacturerData: [{companyIdentifier, dataPrefix: typed_array}]}]
}));

const array_buffer = Uint8Array.of(3, 4).buffer;
detachBuffer(array_buffer);

await promise_rejects_dom(
t, 'InvalidStateError', requestDeviceWithTrustedClick({
filters: [
{manufacturerData: [{companyIdentifier, dataPrefix: array_buffer}]}
]
}));
}, test_desc);
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// META: script=/resources/testharness.js
// META: script=/resources/testharnessreport.js
// META: script=/resources/testdriver.js
// META: script=/resources/testdriver-vendor.js
// META: script=/bluetooth/resources/bluetooth-test.js
// META: script=/bluetooth/resources/bluetooth-fake-devices.js
'use strict';
const test_desc = 'requestDevice with empty manufacturerData. ' +
'Should reject with TypeError.';
const test_specs = [
{filters: [{manufacturerData: []}]},
{filters: [{manufacturerData: [], name: 'Name'}]},
{filters: [{manufacturerData: [], services: ['heart_rate']}]},
{filters: [{manufacturerData: [], name: 'Name', services: ['heart_rate']}]},
{filters: [{manufacturerData: []}], optionalServices: ['heart_rate']}, {
filters: [{manufacturerData: [], name: 'Name'}],
optionalServices: ['heart_rate']
},
{
filters: [{manufacturerData: [], services: ['heart_rate']}],
optionalServices: ['heart_rate']
},
{
filters: [{manufacturerData: [], name: 'Name', services: ['heart_rate']}],
optionalServices: ['heart_rate']
}
];

bluetooth_test((t) => {
let test_promises = Promise.resolve();
test_specs.forEach(args => {
test_promises = test_promises.then(
() => promise_rejects_js(
t, TypeError, requestDeviceWithTrustedClick(args)));
});
return test_promises;
}, test_desc);
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// META: script=/resources/testdriver.js
// META: script=/resources/testdriver-vendor.js
// META: script=/bluetooth/resources/bluetooth-test.js
// META: script=/bluetooth/resources/bluetooth-fake-devices.js
'use strict';
const test_desc = 'companyIdentifier must be in the [0, 65535] range';

bluetooth_test(async (t) => {
await promise_rejects_js(
t, TypeError,
requestDeviceWithTrustedClick(
{filters: [{manufacturerData: [{companyIdentifier: -1}]}]}));
await promise_rejects_js(
t, TypeError,
requestDeviceWithTrustedClick(
{filters: [{manufacturerData: [{companyIdentifier: 65536}]}]}));
}, test_desc);
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// META: script=/resources/testdriver.js
// META: script=/resources/testdriver-vendor.js
// META: script=/bluetooth/resources/bluetooth-test.js
// META: script=/bluetooth/resources/bluetooth-fake-devices.js
'use strict';
const test_desc = 'mask value buffer must not be detached';

function detachBuffer(buffer) {
window.postMessage('', '*', [buffer]);
}

bluetooth_test(async (t) => {
const companyIdentifier = 0x0001;
const dataPrefix = Uint8Array.of(1, 2);

const typed_array = Uint8Array.of(1, 2);
detachBuffer(typed_array.buffer);

await promise_rejects_dom(
t, 'InvalidStateError', requestDeviceWithTrustedClick({
filters: [{
manufacturerData: [{companyIdentifier, dataPrefix, mask: typed_array}]
}]
}));

const array_buffer = Uint8Array.of(3, 4).buffer;
detachBuffer(array_buffer);

await promise_rejects_dom(
t, 'InvalidStateError', requestDeviceWithTrustedClick({
filters: [{
manufacturerData:
[{companyIdentifier, dataPrefix, mask: array_buffer}]
}]
}));
}, test_desc);
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// META: script=/resources/testharness.js
// META: script=/resources/testharnessreport.js
// META: script=/resources/testdriver.js
// META: script=/resources/testdriver-vendor.js
// META: script=/bluetooth/resources/bluetooth-test.js
// META: script=/bluetooth/resources/bluetooth-fake-devices.js
'use strict';
const test_desc = 'Manufacturer data company identifier must be unique.';
const expected = new TypeError();

let filters = [{
manufacturerData: [
{
companyIdentifier: 0x0001,
},
{
companyIdentifier: 0x0001,
}
]
}];

bluetooth_test(
(t) => promise_rejects_js(
t, TypeError, requestDeviceWithTrustedClick({filters})),
test_desc);
34 changes: 30 additions & 4 deletions bluetooth/requestDevice/filter-matches.https.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
let matching_services = [health_thermometer.uuid];
let matching_name = 'Health Thermometer';
let matching_namePrefix = 'Health';
let matching_manufacturerData = [{ companyIdentifier: 0x0001 }];

let test_specs = [
{
Expand All @@ -24,25 +25,50 @@
name: matching_name,
}]
},
{filters: [{services: matching_services, namePrefix: matching_namePrefix}]}, {
{
filters: [{
services: matching_services,
namePrefix: matching_namePrefix
}]
},
{
filters: [{
services: matching_services,
manufacturerData: matching_manufacturerData
}]
},
{
filters: [{
name: matching_name,
}],
optionalServices: matching_services
},
{
filters: [{name: matching_name, namePrefix: matching_namePrefix}],
filters: [{
namePrefix: matching_namePrefix
}],
optionalServices: matching_services
},
{
filters: [{
manufacturerData: matching_manufacturerData
}],
optionalServices: matching_services
},
{
filters: [{namePrefix: matching_namePrefix}],
filters: [{
name: matching_name,
namePrefix: matching_namePrefix,
manufacturerData: matching_manufacturerData
}],
optionalServices: matching_services
},
{
filters: [{
services: matching_services,
name: matching_name,
namePrefix: matching_namePrefix
namePrefix: matching_namePrefix,
manufacturerData: matching_manufacturerData
}]
}
];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// META: script=/resources/testharness.js
// META: script=/resources/testharnessreport.js
// META: script=/resources/testdriver.js
// META: script=/resources/testdriver-vendor.js
// META: script=/bluetooth/resources/bluetooth-test.js
// META: script=/bluetooth/resources/bluetooth-fake-devices.js
'use strict';
const test_desc = 'Matches a filter when manufacturer data match.';

let test_specs = [
{
filters: [{
manufacturerData: [{
companyIdentifier: 0x0001,
}],
}],
},
{
filters: [{
manufacturerData: [{
companyIdentifier: 0x0001,
dataPrefix: new Uint8Array([0x01]),
}],
}],
},
{
filters: [{
manufacturerData: [{
companyIdentifier: 0x0001,
dataPrefix: new Uint8Array([0x01]),
mask: new Uint8Array([0xff]),
}],
}],
},
{
filters: [{
manufacturerData: [{
companyIdentifier: 0x0001,
dataPrefix: new Uint8Array([0x01, 0x02]),
}],
}],
},
{
filters: [{
manufacturerData: [{
companyIdentifier: 0x0001,
dataPrefix: new Uint8Array([0x01, 0x02]),
mask: new Uint8Array([0xff, 0x01]),
}],
}],
},
{
filters: [{
manufacturerData: [
{
companyIdentifier: 0x0001,
dataPrefix: new Uint8Array([0x01, 0x02]),
mask: new Uint8Array([0xff, 0x01]),
},
{
companyIdentifier: 0x0002,
}
],
}],
},
{
filters: [{
manufacturerData: [
{
companyIdentifier: 0x0001,
dataPrefix: new Uint8Array([0x01, 0x02]),
mask: new Uint8Array([0xff, 0x01]),
},
{
companyIdentifier: 0x0002,
dataPrefix: new Uint8Array([0x03]),
}
],
}],
},
{
filters: [{
manufacturerData: [
{
companyIdentifier: 0x0001,
dataPrefix: new Uint8Array([0x01, 0x02]),
mask: new Uint8Array([0xff, 0x01]),
},
{
companyIdentifier: 0x0002,
dataPrefix: new Uint8Array([0x03]),
mask: new Uint8Array([0xff]),
}
],
}],
},
{
filters: [{
manufacturerData: [
{
companyIdentifier: 0x0001,
dataPrefix: new Uint8Array([0x01, 0x02]),
mask: new Uint8Array([0xff, 0x01]),
},
{
companyIdentifier: 0x0002,
dataPrefix: new Uint8Array([0x03, 0x04]),
}
],
}],
},
{
filters: [{
manufacturerData: [
{
companyIdentifier: 0x0001,
dataPrefix: new Uint8Array([0x01, 0x02]),
mask: new Uint8Array([0xff, 0x01]),
},
{
companyIdentifier: 0x0002,
dataPrefix: new Uint8Array([0x03, 0x04]),
mask: new Uint8Array([0xff, 0xff])
}
],
}],
},
];

bluetooth_test(
() => setUpHealthThermometerDevice().then(() => {
let test_promises = Promise.resolve();
test_specs.forEach(args => {
test_promises = test_promises.then(async () => {
const device = await requestDeviceWithTrustedClick(args);
assert_equals(device.name, 'Health Thermometer');
});
});
return test_promises;
}),
test_desc);
Loading

0 comments on commit a2de699

Please sign in to comment.