'use strict'
function convertToBidiManufacturerData(manufacturerData) {
const bidiManufacturerData = [];
for (const key in manufacturerData) {
bidiManufacturerData.push({
key: parseInt(key),
data: btoa(String.fromCharCode(...manufacturerData[key]))
})
}
return bidiManufacturerData;
}
function ArrayToMojoCharacteristicProperties(arr) {
const struct = {};
arr.forEach(property => {
struct[property] = true;
});
return struct;
}
class FakeBluetooth {
constructor() {
this.fake_central_ = null;
}
async simulateCentral({state}) {
if (this.fake_central_) {
throw 'simulateCentral() should only be called once';
}
await test_driver.bidi.bluetooth.simulate_adapter({state: state});
this.fake_central_ = new FakeCentral();
return this.fake_central_;
}
}
class FakeCentral {
constructor() {
this.peripherals_ = new Map();
}
async simulatePreconnectedPeripheral(
{address, name, manufacturerData = {}, knownServiceUUIDs = []}) {
await test_driver.bidi.bluetooth.simulate_preconnected_peripheral({
address: address,
name: name,
manufacturerData: convertToBidiManufacturerData(manufacturerData),
knownServiceUuids:
knownServiceUUIDs.map(uuid => BluetoothUUID.getService(uuid))
});
return this.fetchOrCreatePeripheral_(address);
}
fetchOrCreatePeripheral_(address) {
let peripheral = this.peripherals_.get(address);
if (peripheral === undefined) {
peripheral = new FakePeripheral(address);
this.peripherals_.set(address, peripheral);
}
return peripheral;
}
}
class FakePeripheral {
constructor(address) {
this.address = address;
}
async addFakeService({uuid}) {
const service_uuid = BluetoothUUID.getService(uuid);
await test_driver.bidi.bluetooth.simulate_service({
address: this.address,
uuid: service_uuid,
type: 'add',
});
return new FakeRemoteGATTService(service_uuid, this.address);
}
async setNextGATTConnectionResponse({code}) {
const remove_handler =
test_driver.bidi.bluetooth.gatt_connection_attempted.on((event) => {
if (event.address != this.address) {
return;
}
remove_handler();
test_driver.bidi.bluetooth.simulate_gatt_connection_response({
address: event.address,
code,
});
});
}
async setNextGATTDiscoveryResponse({code}) {
return Promise.resolve();
}
async simulateGATTConnectionResponse(code) {
await test_driver.bidi.bluetooth.simulate_gatt_connection_response(
{address: this.address, code});
}
async simulateGATTDisconnection() {
await test_driver.bidi.bluetooth.simulate_gatt_disconnection(
{address: this.address});
}
}
class FakeRemoteGATTService {
constructor(service_uuid, peripheral_address) {
this.service_uuid_ = service_uuid;
this.peripheral_address_ = peripheral_address;
}
async addFakeCharacteristic({uuid, properties}) {
const characteristic_uuid = BluetoothUUID.getCharacteristic(uuid);
await test_driver.bidi.bluetooth.simulate_characteristic({
address: this.peripheral_address_,
serviceUuid: this.service_uuid_,
characteristicUuid: characteristic_uuid,
characteristicProperties: ArrayToMojoCharacteristicProperties(properties),
type: 'add'
});
return new FakeRemoteGATTCharacteristic(
characteristic_uuid, this.service_uuid_, this.peripheral_address_);
}
async remove() {
await test_driver.bidi.bluetooth.simulate_service({
address: this.peripheral_address_,
uuid: this.service_uuid_,
type: 'remove'
});
}
}
class FakeRemoteGATTCharacteristic {
constructor(characteristic_uuid, service_uuid, peripheral_address) {
this.characteristic_uuid_ = characteristic_uuid;
this.service_uuid_ = service_uuid;
this.peripheral_address_ = peripheral_address;
this.last_written_value_ = {lastValue: null, lastWriteType: 'none'};
}
async addFakeDescriptor({uuid}) {
const descriptor_uuid = BluetoothUUID.getDescriptor(uuid);
await test_driver.bidi.bluetooth.simulate_descriptor({
address: this.peripheral_address_,
serviceUuid: this.service_uuid_,
characteristicUuid: this.characteristic_uuid_,
descriptorUuid: descriptor_uuid,
type: 'add'
});
return new FakeRemoteGATTDescriptor(
descriptor_uuid, this.characteristic_uuid_, this.service_uuid_,
this.peripheral_address_);
}
async simulateResponse(type, code, data) {
await test_driver.bidi.bluetooth.simulate_characteristic_response({
address: this.peripheral_address_,
serviceUuid: this.service_uuid_,
characteristicUuid: this.characteristic_uuid_,
type,
code,
data,
});
}
async simulateReadResponse(code, data) {
await this.simulateResponse('read', code, data);
}
async simulateWriteResponse(code) {
await this.simulateResponse('write', code);
}
async setNextReadResponse(gatt_code, value = null) {
if (gatt_code === 0 && value === null) {
throw '|value| can\'t be null if read should success.';
}
if (gatt_code !== 0 && value !== null) {
throw '|value| must be null if read should fail.';
}
const remove_handler =
test_driver.bidi.bluetooth.characteristic_event_generated.on(
(event) => {
if (event.address != this.peripheral_address_) {
return;
}
remove_handler();
this.simulateReadResponse(gatt_code, value);
});
}
async setNextWriteResponse(gatt_code) {
const remove_handler =
test_driver.bidi.bluetooth.characteristic_event_generated.on(
(event) => {
if (event.address != this.peripheral_address_) {
return;
}
this.last_written_value_ = {
lastValue: event.data,
lastWriteType: event.type
};
remove_handler();
if (event.type == 'write-with-response') {
this.simulateWriteResponse(gatt_code);
}
});
}
async getLastWrittenValue() {
return this.last_written_value_;
}
async remove() {
await test_driver.bidi.bluetooth.simulate_characteristic({
address: this.peripheral_address_,
serviceUuid: this.service_uuid_,
characteristicUuid: this.characteristic_uuid_,
characteristicProperties: undefined,
type: 'remove'
});
}
}
class FakeRemoteGATTDescriptor {
constructor(
descriptor_uuid, characteristic_uuid, service_uuid, peripheral_address) {
this.descriptor_uuid_ = descriptor_uuid;
this.characteristic_uuid_ = characteristic_uuid;
this.service_uuid_ = service_uuid;
this.peripheral_address_ = peripheral_address;
this.last_written_value_ = null;
}
async simulateResponse(type, code, data) {
await test_driver.bidi.bluetooth.simulate_descriptor_response({
address: this.peripheral_address_,
serviceUuid: this.service_uuid_,
characteristicUuid: this.characteristic_uuid_,
descriptorUuid: this.descriptor_uuid_,
type,
code,
data,
});
}
async simulateReadResponse(code, data) {
await this.simulateResponse('read', code, data);
}
async simulateWriteResponse(code) {
await this.simulateResponse('write', code);
}
async setNextReadResponse(gatt_code, value = null) {
if (gatt_code === 0 && value === null) {
throw '|value| can\'t be null if read should success.';
}
if (gatt_code !== 0 && value !== null) {
throw '|value| must be null if read should fail.';
}
const remove_handler =
test_driver.bidi.bluetooth.descriptor_event_generated.on((event) => {
if (event.address != this.peripheral_address_) {
return;
}
remove_handler();
this.simulateReadResponse(gatt_code, value);
});
}
async setNextWriteResponse(gatt_code) {
const remove_handler =
test_driver.bidi.bluetooth.descriptor_event_generated.on((event) => {
if (event.address != this.peripheral_address_) {
return;
}
this.last_written_value_ = {
lastValue: event.data,
lastWriteType: event.type
};
remove_handler();
if (event.type == 'write-with-response') {
this.simulateWriteResponse(gatt_code);
}
});
}
async getLastWrittenValue() {
return this.last_written_value_;
}
async remove() {
await test_driver.bidi.bluetooth.simulate_descriptor({
address: this.peripheral_address_,
serviceUuid: this.service_uuid_,
characteristicUuid: this.characteristic_uuid_,
descriptorUuid: this.descriptor_uuid_,
type: 'remove'
});
}
}
function initializeBluetoothBidiResources() {
navigator.bluetooth.test = new FakeBluetooth();
}