Commit 474e9bf3 authored by Ovidio Henriquez's avatar Ovidio Henriquez Committed by Commit Bot

bluetooth: Impl. setNextWriteResponse (descriptor)

Also implements getLastWrittenValue() and converts two tests to use
these.

FakeRemoteGattDescriptor::setNextWriteResponse allows tests to set the
next response for a write.

BUG=719825

Change-Id: I2807d23a55f79eeca35338e25916c6d5e0115e89
Reviewed-on: https://chromium-review.googlesource.com/964901
Commit-Queue: Ovidio Henriquez <odejesush@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarGiovanni Ortuño Urquidi <ortuno@chromium.org>
Reviewed-by: default avatarConley Owens <cco3@chromium.org>
Cr-Commit-Position: refs/heads/master@{#545508}
parent edbd432f
......@@ -293,7 +293,7 @@ interface FakeCentral {
// If the value can't be retrieved calls its callback with false. Calls its
// callback with null value if no value has been written to the
// characteristic.
GetLastWrittenValue(
GetLastWrittenCharacteristicValue(
string characteristic_id,
string service_id,
string peripheral_address) => (bool success, array<uint8>? value);
......@@ -314,4 +314,30 @@ interface FakeCentral {
string characteristic_id,
string service_id,
string peripheral_address) => (bool success);
// Sets the next write response for descriptor with |descriptor_id| in
// |characteristic_id| in |service_id| and in |peripheral_address| to |code|.
// |code| could be a GATT Error Response from BT 4.2 Vol 3 Part F 3.4.1.1
// Error Response or a number outside that range returned by specific
// platforms e.g. Android returns 0x101 to signal a GATT failure.
// https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#GATT_FAILURE
// Calls callback with false if there was any error when simulating the next
// response.
SetNextWriteDescriptorResponse(
uint16 gatt_code,
string descriptor_id,
string characteristic_id,
string service_id,
string peripheral_address) => (bool success);
// Gets the last successfully written value to the descriptor with
// |descriptor_id| in |characteristic_id| in |service_id| and in
// |peripheral_address|. If the value can't be retrieved, calls its callback
// with false. Calls its callback with null value if no value has been
// written to the descriptor.
GetLastWrittenDescriptorValue(
string descriptor_id,
string characteristic_id,
string service_id,
string peripheral_address) => (bool success, array<uint8>? value);
};
......@@ -317,10 +317,11 @@ void FakeCentral::SetNextSubscribeToNotificationsResponse(
std::move(callback).Run(true);
}
void FakeCentral::GetLastWrittenValue(const std::string& characteristic_id,
const std::string& service_id,
const std::string& peripheral_address,
GetLastWrittenValueCallback callback) {
void FakeCentral::GetLastWrittenCharacteristicValue(
const std::string& characteristic_id,
const std::string& service_id,
const std::string& peripheral_address,
GetLastWrittenCharacteristicValueCallback callback) {
FakeRemoteGattCharacteristic* fake_remote_gatt_characteristic =
GetFakeRemoteGattCharacteristic(peripheral_address, service_id,
characteristic_id);
......@@ -351,6 +352,41 @@ void FakeCentral::SetNextReadDescriptorResponse(
std::move(callback).Run(true);
}
void FakeCentral::SetNextWriteDescriptorResponse(
uint16_t gatt_code,
const std::string& descriptor_id,
const std::string& characteristic_id,
const std::string& service_id,
const std::string& peripheral_address,
SetNextWriteDescriptorResponseCallback callback) {
FakeRemoteGattDescriptor* fake_remote_gatt_descriptor =
GetFakeRemoteGattDescriptor(peripheral_address, service_id,
characteristic_id, descriptor_id);
if (!fake_remote_gatt_descriptor) {
std::move(callback).Run(false);
}
fake_remote_gatt_descriptor->SetNextWriteResponse(gatt_code);
std::move(callback).Run(true);
}
void FakeCentral::GetLastWrittenDescriptorValue(
const std::string& descriptor_id,
const std::string& characteristic_id,
const std::string& service_id,
const std::string& peripheral_address,
GetLastWrittenDescriptorValueCallback callback) {
FakeRemoteGattDescriptor* fake_remote_gatt_descriptor =
GetFakeRemoteGattDescriptor(peripheral_address, service_id,
characteristic_id, descriptor_id);
if (!fake_remote_gatt_descriptor) {
std::move(callback).Run(false, base::nullopt);
}
std::move(callback).Run(true,
fake_remote_gatt_descriptor->last_written_value());
}
std::string FakeCentral::GetAddress() const {
NOTREACHED();
return std::string();
......
......@@ -99,10 +99,11 @@ class FakeCentral : public mojom::FakeCentral, public device::BluetoothAdapter {
const std::string& service_id,
const std::string& peripheral_address,
SetNextWriteCharacteristicResponseCallback callback) override;
void GetLastWrittenValue(const std::string& characteristic_id,
const std::string& service_id,
const std::string& peripheral_address,
GetLastWrittenValueCallback callback) override;
void GetLastWrittenCharacteristicValue(
const std::string& characteristic_id,
const std::string& service_id,
const std::string& peripheral_address,
GetLastWrittenCharacteristicValueCallback callback) override;
void SetNextReadDescriptorResponse(
uint16_t gatt_code,
const base::Optional<std::vector<uint8_t>>& value,
......@@ -111,6 +112,19 @@ class FakeCentral : public mojom::FakeCentral, public device::BluetoothAdapter {
const std::string& service_id,
const std::string& peripheral_address,
SetNextReadDescriptorResponseCallback callback) override;
void SetNextWriteDescriptorResponse(
uint16_t gatt_code,
const std::string& descriptor_id,
const std::string& characteristic_id,
const std::string& service_id,
const std::string& peripheral_address,
SetNextWriteDescriptorResponseCallback callback) override;
void GetLastWrittenDescriptorValue(
const std::string& descriptor_id,
const std::string& characteristic_id,
const std::string& service_id,
const std::string& peripheral_address,
GetLastWrittenDescriptorValueCallback callback) override;
// BluetoothAdapter overrides:
std::string GetAddress() const override;
......
......@@ -29,10 +29,13 @@ void FakeRemoteGattDescriptor::SetNextReadResponse(
next_read_response_.emplace(gatt_code, value);
}
void FakeRemoteGattDescriptor::SetNextWriteResponse(uint16_t gatt_code) {
DCHECK(!next_write_response_);
next_write_response_.emplace(gatt_code);
}
bool FakeRemoteGattDescriptor::AllResponsesConsumed() {
// TODO(crbug.com/569709): Update this when SetNextWriteResponse is
// implemented.
return !next_read_response_;
return !next_read_response_ && !next_write_response_;
}
std::string FakeRemoteGattDescriptor::GetIdentifier() const {
......@@ -70,8 +73,14 @@ void FakeRemoteGattDescriptor::ReadRemoteDescriptor(
void FakeRemoteGattDescriptor::WriteRemoteDescriptor(
const std::vector<uint8_t>& value,
const base::Closure& callback,
const ErrorCallback& error_callback) {}
const base::RepeatingClosure& callback,
const ErrorCallback& error_callback) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindRepeating(&FakeRemoteGattDescriptor::DispatchWriteResponse,
weak_ptr_factory_.GetWeakPtr(), callback,
error_callback, value));
}
void FakeRemoteGattDescriptor::DispatchReadResponse(
const ValueCallback& callback,
......@@ -93,4 +102,25 @@ void FakeRemoteGattDescriptor::DispatchReadResponse(
}
}
void FakeRemoteGattDescriptor::DispatchWriteResponse(
const base::RepeatingClosure& callback,
const ErrorCallback& error_callback,
const std::vector<uint8_t>& value) {
DCHECK(next_write_response_);
uint16_t gatt_code = next_write_response_.value();
next_write_response_.reset();
switch (gatt_code) {
case mojom::kGATTSuccess:
last_written_value_ = value;
callback.Run();
break;
case mojom::kGATTInvalidHandle:
error_callback.Run(device::BluetoothGattService::GATT_ERROR_FAILED);
break;
default:
NOTREACHED();
}
}
} // namespace bluetooth
......@@ -35,6 +35,16 @@ class FakeRemoteGattDescriptor : public device::BluetoothRemoteGattDescriptor {
void SetNextReadResponse(uint16_t gatt_code,
const base::Optional<std::vector<uint8_t>>& value);
// If |gatt_code| is mojom::kGATTSuccess the next write request will call its
// success callback. Otherwise it will call its error callback.
void SetNextWriteResponse(uint16_t gatt_code);
// Returns the last successfully written value to the descriptor. Returns
// nullopt if no value has been written yet.
const base::Optional<std::vector<uint8_t>>& last_written_value() {
return last_written_value_;
}
// Returns true if there are no pending responses for this descriptor.
bool AllResponsesConsumed();
......@@ -50,22 +60,33 @@ class FakeRemoteGattDescriptor : public device::BluetoothRemoteGattDescriptor {
void ReadRemoteDescriptor(const ValueCallback& callback,
const ErrorCallback& error_callback) override;
void WriteRemoteDescriptor(const std::vector<uint8_t>& value,
const base::Closure& callback,
const base::RepeatingClosure& callback,
const ErrorCallback& error_callback) override;
private:
void DispatchReadResponse(const ValueCallback& callback,
const ErrorCallback& error_callback);
void DispatchWriteResponse(const base::RepeatingClosure& callback,
const ErrorCallback& error_callback,
const std::vector<uint8_t>& value);
const std::string descriptor_id_;
const device::BluetoothUUID descriptor_uuid_;
device::BluetoothRemoteGattCharacteristic* characteristic_;
std::vector<uint8_t> value_;
// Last successfully written value to the descriptor.
base::Optional<std::vector<uint8_t>> last_written_value_;
// Used to decide which callback should be called when
// ReadRemoteDescriptor is called.
base::Optional<FakeReadResponse> next_read_response_;
// Used to decide which callback should be called when WriteRemoteDescriptor
// is called.
base::Optional<uint16_t> next_write_response_;
base::WeakPtrFactory<FakeRemoteGattDescriptor> weak_ptr_factory_;
};
......
......@@ -6,19 +6,36 @@
<script src="../../../external/wpt/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
bluetooth_test(() => {
let length = 1;
let descriptor;
return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
.then(() => requestDeviceWithTrustedClick({
filters: [{services: ['health_thermometer']}]}))
.then(device => device.gatt.connect())
.then(gattServer => gattServer.getPrimaryService('health_thermometer'))
.then(service => service.getCharacteristic('measurement_interval'))
.then(characteristic => characteristic.getDescriptor(user_description.name))
.then(d => descriptor = d)
.then(() => descriptor.writeValue(new Uint8Array(length)))
.then(() => descriptor.writeValue(new ArrayBuffer(length)))
.then(() => descriptor.writeValue(new DataView(new ArrayBuffer(length))));
}, 'A regular write request to a writable descriptor should succeed.');
const test_desc = 'A regular write request to a writable descriptor ' +
'should succeed.';
let typed_array = Uint8Array.of(1, 2);
let array_buffer = Uint8Array.of(3, 4).buffer;
let data_view = new DataView(new ArrayBuffer(2));
let descriptor, fake_descriptor;
bluetooth_test(() => getUserDescriptionDescriptor()
.then(_ => ({descriptor, fake_descriptor} = _))
.then(() => new Promise(resolve => {
data_view.setUint8(0, 5);
data_view.setUint8(1, 6);
resolve();
}))
.then(() => fake_descriptor.getLastWrittenValue())
.then(last_value => assert_true(last_value === null))
.then(() => fake_descriptor.setNextWriteResponse(GATT_SUCCESS))
.then(() => descriptor.writeValue(typed_array))
.then(() => fake_descriptor.getLastWrittenValue())
.then(last_value => assert_array_equals(last_value, [1, 2]))
.then(() => fake_descriptor.setNextWriteResponse(GATT_SUCCESS))
.then(() => descriptor.writeValue(array_buffer))
.then(() => fake_descriptor.getLastWrittenValue())
.then(last_value => assert_array_equals(last_value, [3, 4]))
.then(() => fake_descriptor.setNextWriteResponse(GATT_SUCCESS))
.then(() => descriptor.writeValue(data_view))
.then(() => fake_descriptor.getLastWrittenValue())
.then(last_value => assert_array_equals(last_value, [5, 6])),
test_desc);
</script>
......@@ -6,23 +6,18 @@
<script src="../../../external/wpt/bluetooth/resources/bluetooth-helpers.js"></script>
<script>
'use strict';
bluetooth_test(() => {
return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
.then(() => requestDeviceWithTrustedClick({
filters: [{services: ['health_thermometer']}]}))
.then(device => device.gatt.connect())
.then(gattServer => gattServer.getPrimaryService('health_thermometer'))
.then(service => service.getCharacteristic('measurement_interval'))
.then(characteristic => characteristic.getDescriptor(user_description.name))
.then(descriptor => {
assert_equals(descriptor.value, null);
let textEncoder = new TextEncoder();
let newValue = textEncoder.encode('foo');
assert_true(newValue instanceof Uint8Array, 'newValue is Uint8Array');
return descriptor.writeValue(newValue).then(() => {
assert_true(descriptor.value instanceof DataView, 'descriptor.value is DataView');
assert_array_equals(Array.from(descriptor.value.getUint8()), Array.from(newValue.buffer));
})
})
}, 'Succesful read should update descriptor\'s value.');
const test_desc = 'Successful write should update descriptor\'s value.';
const newValue = new TextEncoder().encode('foo');
let descriptor, fake_descriptor;
bluetooth_test(() => getUserDescriptionDescriptor()
.then(_ => ({descriptor, fake_descriptor} = _))
.then(() => assert_equals(descriptor.value, null))
.then(() => fake_descriptor.setNextWriteResponse(GATT_SUCCESS))
.then(() => descriptor.writeValue(newValue))
.then(() => assert_array_equals(
new Uint8Array(descriptor.value.buffer), newValue))
.then(() => fake_descriptor.getLastWrittenValue())
.then(lastValue => assert_array_equals(lastValue, newValue)),
test_desc);
</script>
......@@ -377,7 +377,7 @@ class FakeRemoteGATTCharacteristic {
await this.fake_central_ptr_.setNextWriteCharacteristicResponse(
gatt_code, ...this.ids_);
if (!success) throw 'setNextWriteResponse failed';
if (!success) throw 'setNextWriteCharacteristicResponse failed';
}
// Sets the next subscribe to notifications response for characteristic with
......@@ -397,9 +397,10 @@ class FakeRemoteGATTCharacteristic {
// Returns null if no value has yet been written to the characteristic.
async getLastWrittenValue() {
let {success, value} =
await this.fake_central_ptr_.getLastWrittenValue(...this.ids_);
await this.fake_central_ptr_.getLastWrittenCharacteristicValue(
...this.ids_);
if (!success) throw 'getLastWrittenValue failed';
if (!success) throw 'getLastWrittenCharacteristicValue failed';
return value;
}
......@@ -445,6 +446,31 @@ class FakeRemoteGATTDescriptor {
if (!success) throw 'setNextReadDescriptorResponse failed';
}
// Sets the next write response for this descriptor to |code|.
// |code| could be a GATT Error Response from
// BT 4.2 Vol 3 Part F 3.4.1.1 Error Response or a number outside that range
// returned by specific platforms e.g. Android returns 0x101 to signal a GATT
// failure.
async setNextWriteResponse(gatt_code) {
let {success} =
await this.fake_central_ptr_.setNextWriteDescriptorResponse(
gatt_code, ...this.ids_);
if (!success) throw 'setNextWriteDescriptorResponse failed';
}
// Gets the last successfully written value to the descriptor.
// Returns null if no value has yet been written to the descriptor.
async getLastWrittenValue() {
let {success, value} =
await this.fake_central_ptr_.getLastWrittenDescriptorValue(
...this.ids_);
if (!success) throw 'getLastWrittenDescriptorValue failed';
return value;
}
// Removes the fake GATT Descriptor from its fake characteristic.
async remove() {
let {success} =
......
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