Commit 39c6286b authored by Qiyu Hu's avatar Qiyu Hu Committed by Commit Bot

bluetooth: Implement "PrepareWriteValue" for GATT characteristic

PrepareWriteValue is a procedure to reliably write to GATT
characteristics in a GATT service. A client can PrepareWrite to
various characteristics in a reliable write session and Execute or Abort
those writes later.

For now, BlueZ handles prepare writes by caching them and then sends out
regular write requests to the application in the case of commit, or
silently drops them in the case of abort.

We keep the caching in BlueZ but implement a new function named
PrepareWriteValue() such that applications can tell prepare write
requests from regular write requests. Since BlueZ only sends out the
write requests when the remote device commits, applications not
interested in achieving reliable writes can safely regard all prepare
write requests as regular ones.

A relevant patch is crrev.com/c/1094214

Bug: b:78650442
CQ-DEPEND: CL:1094214
Test: Pass Android CtsVerifier test on GATT server reliable write
Change-Id: If2464cf3a17e7c91dc11dc8f4c37734a2987543e
Reviewed-on: https://chromium-review.googlesource.com/1094316
Commit-Queue: Qiyu Hu <qiyuh@google.com>
Reviewed-by: default avatarGreg Kerr <kerrnel@chromium.org>
Reviewed-by: default avatarRahul Chaturvedi <rkc@chromium.org>
Reviewed-by: default avatarLuis Hector Chavez <lhchavez@chromium.org>
Reviewed-by: default avatarMiao-chen Chou <mcchou@chromium.org>
Reviewed-by: default avatarToni Barzic <tbarzic@chromium.org>
Cr-Commit-Position: refs/heads/master@{#573539}
parent 4eb127ce
...@@ -843,6 +843,31 @@ void ArcBluetoothBridge::OnGattAttributeReadRequest( ...@@ -843,6 +843,31 @@ void ArcBluetoothBridge::OnGattAttributeReadRequest(
base::BindOnce(&OnGattServerRead, success_callback, error_callback)); base::BindOnce(&OnGattServerRead, success_callback, error_callback));
} }
void ArcBluetoothBridge::OnGattServerPrepareWrite(
mojom::BluetoothAddressPtr addr,
bool has_subsequent_write,
const base::Closure& success_callback,
const ErrorCallback& error_callback,
mojom::BluetoothGattStatus status) {
bool success = (status == mojom::BluetoothGattStatus::GATT_SUCCESS);
const base::Closure& callback = (success ? success_callback : error_callback);
if (success && has_subsequent_write) {
callback.Run();
return;
}
auto* bluetooth_instance = ARC_GET_INSTANCE_FOR_METHOD(
arc_bridge_service_->bluetooth(), RequestGattExecuteWrite);
if (bluetooth_instance == nullptr) {
error_callback.Run();
return;
}
bluetooth_instance->RequestGattExecuteWrite(
std::move(addr), success,
base::BindOnce(&OnGattServerWrite, callback, error_callback));
}
template <class LocalGattAttribute> template <class LocalGattAttribute>
void ArcBluetoothBridge::OnGattAttributeWriteRequest( void ArcBluetoothBridge::OnGattAttributeWriteRequest(
const BluetoothDevice* device, const BluetoothDevice* device,
...@@ -850,6 +875,8 @@ void ArcBluetoothBridge::OnGattAttributeWriteRequest( ...@@ -850,6 +875,8 @@ void ArcBluetoothBridge::OnGattAttributeWriteRequest(
const std::vector<uint8_t>& value, const std::vector<uint8_t>& value,
int offset, int offset,
mojom::BluetoothGattDBAttributeType attribute_type, mojom::BluetoothGattDBAttributeType attribute_type,
bool is_prepare,
bool has_subsequent_write,
const base::Closure& success_callback, const base::Closure& success_callback,
const ErrorCallback& error_callback) { const ErrorCallback& error_callback) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
...@@ -860,12 +887,19 @@ void ArcBluetoothBridge::OnGattAttributeWriteRequest( ...@@ -860,12 +887,19 @@ void ArcBluetoothBridge::OnGattAttributeWriteRequest(
return; return;
} }
GattStatusCallback callback =
is_prepare ? base::BindOnce(
&ArcBluetoothBridge::OnGattServerPrepareWrite,
weak_factory_.GetWeakPtr(),
mojom::BluetoothAddress::From(device->GetAddress()),
has_subsequent_write, success_callback, error_callback)
: base::BindOnce(&OnGattServerWrite, success_callback,
error_callback);
DCHECK(gatt_handle_.find(attribute->GetIdentifier()) != gatt_handle_.end()); DCHECK(gatt_handle_.find(attribute->GetIdentifier()) != gatt_handle_.end());
bluetooth_instance->RequestGattWrite( bluetooth_instance->RequestGattWrite(
mojom::BluetoothAddress::From(device->GetAddress()), mojom::BluetoothAddress::From(device->GetAddress()),
gatt_handle_[attribute->GetIdentifier()], offset, value, attribute_type, gatt_handle_[attribute->GetIdentifier()], offset, value, attribute_type,
base::BindOnce(&OnGattServerWrite, success_callback, error_callback)); is_prepare, std::move(callback));
} }
void ArcBluetoothBridge::OnCharacteristicReadRequest( void ArcBluetoothBridge::OnCharacteristicReadRequest(
...@@ -889,10 +923,25 @@ void ArcBluetoothBridge::OnCharacteristicWriteRequest( ...@@ -889,10 +923,25 @@ void ArcBluetoothBridge::OnCharacteristicWriteRequest(
const ErrorCallback& error_callback) { const ErrorCallback& error_callback) {
OnGattAttributeWriteRequest( OnGattAttributeWriteRequest(
device, characteristic, value, offset, device, characteristic, value, offset,
mojom::BluetoothGattDBAttributeType::BTGATT_DB_CHARACTERISTIC, callback, mojom::BluetoothGattDBAttributeType::BTGATT_DB_CHARACTERISTIC,
/* is_prepare = */ false, /* has_subsequent_write, = */ false, callback,
error_callback); error_callback);
} }
void ArcBluetoothBridge::OnCharacteristicPrepareWriteRequest(
const BluetoothDevice* device,
const BluetoothLocalGattCharacteristic* characteristic,
const std::vector<uint8_t>& value,
int offset,
bool has_subsequent_write,
const base::Closure& callback,
const ErrorCallback& error_callback) {
OnGattAttributeWriteRequest(
device, characteristic, value, offset,
mojom::BluetoothGattDBAttributeType::BTGATT_DB_CHARACTERISTIC,
/* is_prepare = */ true, has_subsequent_write, callback, error_callback);
}
void ArcBluetoothBridge::OnDescriptorReadRequest( void ArcBluetoothBridge::OnDescriptorReadRequest(
const BluetoothDevice* device, const BluetoothDevice* device,
const BluetoothLocalGattDescriptor* descriptor, const BluetoothLocalGattDescriptor* descriptor,
...@@ -914,7 +963,8 @@ void ArcBluetoothBridge::OnDescriptorWriteRequest( ...@@ -914,7 +963,8 @@ void ArcBluetoothBridge::OnDescriptorWriteRequest(
const ErrorCallback& error_callback) { const ErrorCallback& error_callback) {
OnGattAttributeWriteRequest( OnGattAttributeWriteRequest(
device, descriptor, value, offset, device, descriptor, value, offset,
mojom::BluetoothGattDBAttributeType::BTGATT_DB_DESCRIPTOR, callback, mojom::BluetoothGattDBAttributeType::BTGATT_DB_DESCRIPTOR,
/* is_prepare = */ false, /* has_subsequent_write = */ false, callback,
error_callback); error_callback);
} }
......
...@@ -162,6 +162,15 @@ class ArcBluetoothBridge ...@@ -162,6 +162,15 @@ class ArcBluetoothBridge
const base::Closure& callback, const base::Closure& callback,
const ErrorCallback& error_callback) override; const ErrorCallback& error_callback) override;
void OnCharacteristicPrepareWriteRequest(
const device::BluetoothDevice* device,
const device::BluetoothLocalGattCharacteristic* characteristic,
const std::vector<uint8_t>& value,
int offset,
bool has_subsequent_write,
const base::Closure& callback,
const ErrorCallback& error_callback) override;
void OnDescriptorReadRequest( void OnDescriptorReadRequest(
const device::BluetoothDevice* device, const device::BluetoothDevice* device,
const device::BluetoothLocalGattDescriptor* descriptor, const device::BluetoothLocalGattDescriptor* descriptor,
...@@ -448,6 +457,9 @@ class ArcBluetoothBridge ...@@ -448,6 +457,9 @@ class ArcBluetoothBridge
const ErrorCallback& error_callback); const ErrorCallback& error_callback);
// Common code for OnCharacteristicWriteRequest and OnDescriptorWriteRequest // Common code for OnCharacteristicWriteRequest and OnDescriptorWriteRequest
// |is_prepare| is only set when a local characteristic receives a prepare
// write request, and |has_subsequent_write| indicates whether there are
// subsequent prepare write requests following the current one.
template <class LocalGattAttribute> template <class LocalGattAttribute>
void OnGattAttributeWriteRequest( void OnGattAttributeWriteRequest(
const device::BluetoothDevice* device, const device::BluetoothDevice* device,
...@@ -455,6 +467,8 @@ class ArcBluetoothBridge ...@@ -455,6 +467,8 @@ class ArcBluetoothBridge
const std::vector<uint8_t>& value, const std::vector<uint8_t>& value,
int offset, int offset,
mojom::BluetoothGattDBAttributeType attribute_type, mojom::BluetoothGattDBAttributeType attribute_type,
bool is_prepare,
bool has_subsequent_write,
const base::Closure& success_callback, const base::Closure& success_callback,
const ErrorCallback& error_callback); const ErrorCallback& error_callback);
...@@ -519,6 +533,19 @@ class ArcBluetoothBridge ...@@ -519,6 +533,19 @@ class ArcBluetoothBridge
void SendDevice(const device::BluetoothDevice* device, void SendDevice(const device::BluetoothDevice* device,
bool include_cached_device) const; bool include_cached_device) const;
void OnGattServerPrepareWrite(mojom::BluetoothAddressPtr addr,
bool has_subsequent_write,
const base::Closure& success_callback,
const ErrorCallback& error_callback,
mojom::BluetoothGattStatus status);
void SendDevice(const device::BluetoothDevice* device) const;
// Shows a pairing dialog to handle incoming pairing requests.
// Returns the pairing delegate of the dialog UI.
device::BluetoothDevice::PairingDelegate* ShowPairingDialog(
device::BluetoothDevice* device);
ArcBridgeService* const arc_bridge_service_; // Owned by ArcServiceManager. ArcBridgeService* const arc_bridge_service_; // Owned by ArcServiceManager.
scoped_refptr<bluez::BluetoothAdapterBlueZ> bluetooth_adapter_; scoped_refptr<bluez::BluetoothAdapterBlueZ> bluetooth_adapter_;
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// Next MinVersion: 12 // Next MinVersion: 13
module arc.mojom; module arc.mojom;
...@@ -409,7 +409,7 @@ interface BluetoothHost { ...@@ -409,7 +409,7 @@ interface BluetoothHost {
=> (BluetoothGattStatus status); => (BluetoothGattStatus status);
}; };
// Next Method ID: 20 // Next Method ID: 21
interface BluetoothInstance { interface BluetoothInstance {
// DEPRECATED: Please use Init@18 instead. // DEPRECATED: Please use Init@18 instead.
InitDeprecated@0(BluetoothHost host_ptr); InitDeprecated@0(BluetoothHost host_ptr);
...@@ -467,7 +467,12 @@ interface BluetoothInstance { ...@@ -467,7 +467,12 @@ interface BluetoothInstance {
int32 attribute_handle, int32 attribute_handle,
int32 offset, int32 offset,
array<uint8> value, array<uint8> value,
[MinVersion=9] BluetoothGattDBAttributeType attribute_type) [MinVersion=9] BluetoothGattDBAttributeType attribute_type,
[MinVersion=12] bool is_prepare)
=> (BluetoothGattStatus status);
[MinVersion=12] RequestGattExecuteWrite@20(
BluetoothAddress address,
bool execute)
=> (BluetoothGattStatus status); => (BluetoothGattStatus status);
[MinVersion=10] OnMTUReceived@19(BluetoothAddress remote_addr, uint16 mtu); [MinVersion=10] OnMTUReceived@19(BluetoothAddress remote_addr, uint16 mtu);
......
...@@ -122,8 +122,14 @@ void FakeBluetoothInstance::RequestGattWrite( ...@@ -122,8 +122,14 @@ void FakeBluetoothInstance::RequestGattWrite(
int32_t offset, int32_t offset,
const std::vector<uint8_t>& value, const std::vector<uint8_t>& value,
mojom::BluetoothGattDBAttributeType attribute_type, mojom::BluetoothGattDBAttributeType attribute_type,
bool is_prepare,
RequestGattWriteCallback callback) {} RequestGattWriteCallback callback) {}
void FakeBluetoothInstance::RequestGattExecuteWrite(
mojom::BluetoothAddressPtr address,
bool execute,
RequestGattExecuteWriteCallback callback) {}
void FakeBluetoothInstance::OnGetSdpRecords( void FakeBluetoothInstance::OnGetSdpRecords(
mojom::BluetoothStatus status, mojom::BluetoothStatus status,
mojom::BluetoothAddressPtr remote_addr, mojom::BluetoothAddressPtr remote_addr,
......
...@@ -124,8 +124,14 @@ class FakeBluetoothInstance : public mojom::BluetoothInstance { ...@@ -124,8 +124,14 @@ class FakeBluetoothInstance : public mojom::BluetoothInstance {
int32_t offset, int32_t offset,
const std::vector<uint8_t>& value, const std::vector<uint8_t>& value,
mojom::BluetoothGattDBAttributeType attribute_type, mojom::BluetoothGattDBAttributeType attribute_type,
bool is_prepare,
RequestGattWriteCallback callback) override; RequestGattWriteCallback callback) override;
void RequestGattExecuteWrite(
mojom::BluetoothAddressPtr address,
bool execute,
RequestGattExecuteWriteCallback callback) override;
void OnGetSdpRecords( void OnGetSdpRecords(
mojom::BluetoothStatus status, mojom::BluetoothStatus status,
mojom::BluetoothAddressPtr remote_addr, mojom::BluetoothAddressPtr remote_addr,
......
...@@ -87,6 +87,36 @@ TEST_F(BluetoothLocalGattCharacteristicTest, ...@@ -87,6 +87,36 @@ TEST_F(BluetoothLocalGattCharacteristicTest,
EXPECT_EQ(device_->GetIdentifier(), delegate_->last_seen_device_); EXPECT_EQ(device_->GetIdentifier(), delegate_->last_seen_device_);
} }
#if defined(OS_CHROMEOS) || defined(OS_LINUX)
#define MAYBE_PrepareWriteLocalCharacteristicValue \
PrepareWriteLocalCharacteristicValue
#else
#define MAYBE_PrepareWriteLocalCharacteristicValue \
DISABLED_PrepareWriteLocalCharacteristicValue
#endif
TEST_F(BluetoothLocalGattCharacteristicTest,
MAYBE_PrepareWriteLocalCharacteristicValue) {
const uint64_t kValueToWrite = 0x7331ul;
// Clear existing value.
SimulateLocalGattCharacteristicValueWriteRequest(
device_, write_characteristic_.get(), GetValue(0),
GetCallback(Call::EXPECTED), GetCallback(Call::NOT_EXPECTED));
// Reliable write session is going on.
SimulateLocalGattCharacteristicValuePrepareWriteRequest(
device_, write_characteristic_.get(), GetValue(402289342ul), 0, true,
GetCallback(Call::EXPECTED), GetCallback(Call::NOT_EXPECTED));
EXPECT_EQ(0ul, delegate_->last_written_value_);
EXPECT_EQ(device_->GetIdentifier(), delegate_->last_seen_device_);
// Reliable write session ends.
SimulateLocalGattCharacteristicValuePrepareWriteRequest(
device_, write_characteristic_.get(), GetValue(kValueToWrite), 0, false,
GetCallback(Call::EXPECTED), GetCallback(Call::NOT_EXPECTED));
EXPECT_EQ(kValueToWrite, delegate_->last_written_value_);
EXPECT_EQ(device_->GetIdentifier(), delegate_->last_seen_device_);
}
#if defined(OS_CHROMEOS) || defined(OS_LINUX) #if defined(OS_CHROMEOS) || defined(OS_LINUX)
#define MAYBE_ReadLocalCharacteristicValueFail ReadLocalCharacteristicValueFail #define MAYBE_ReadLocalCharacteristicValueFail ReadLocalCharacteristicValueFail
#else #else
......
...@@ -87,6 +87,31 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothLocalGattService ...@@ -87,6 +87,31 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothLocalGattService
const base::Closure& callback, const base::Closure& callback,
const ErrorCallback& error_callback) = 0; const ErrorCallback& error_callback) = 0;
// Called when a remote device |device| requests to prepare write the value
// of the characteristic |characteristic| starting at offset |offset|.
// This method is only called if the characteristic was specified as
// reliable writable and any authentication and authorization challenges
// were satisfied by the remote device.
//
// |has_subsequent_request| is true when the reliable write session is still
// ongoing, false otherwise. When |has_subsequent_request| is false,
// delegates MUST tear down the current reliable write session with |device|
// and commit all the prepare writes in that session in order.
//
// To respond to the request with success the delegate must invoke
// |callback|. To respond to the request with failure delegates must invoke
// |error_callback|. If neither callback parameter is invoked, the request
// will time out and result in an error. Therefore, delegates MUST invoke
// either |callback| or |error_callback|.
virtual void OnCharacteristicPrepareWriteRequest(
const BluetoothDevice* device,
const BluetoothLocalGattCharacteristic* characteristic,
const std::vector<uint8_t>& value,
int offset,
bool has_subsequent_request,
const base::Closure& callback,
const ErrorCallback& error_callback) = 0;
// Called when a remote device |device| requests to read the value of the // Called when a remote device |device| requests to read the value of the
// descriptor |descriptor| starting at offset |offset|. // descriptor |descriptor| starting at offset |offset|.
// This method is only called if the descriptor was specified as // This method is only called if the descriptor was specified as
......
...@@ -73,6 +73,19 @@ class BluetoothGattAttributeValueDelegate { ...@@ -73,6 +73,19 @@ class BluetoothGattAttributeValueDelegate {
// descriptors. // descriptors.
virtual void StopNotifications(const dbus::ObjectPath& device_path) = 0; virtual void StopNotifications(const dbus::ObjectPath& device_path) = 0;
// This method will be called, when a remote device requests to prepare
// write the value of the exported GATT characteristic. Invoke |callback| to
// report that the request was successful. Invoke |error_callback| to report
// a failure. This will never be called for descriptors.
virtual void PrepareSetValue(
const dbus::ObjectPath& device_path,
const std::vector<uint8_t>& value,
int offset,
bool has_subsequent_request,
const base::Closure& callback,
const device::BluetoothLocalGattService::Delegate::ErrorCallback&
error_callback) {}
protected: protected:
// Gets the Bluetooth device object on the current service's adapter with // Gets the Bluetooth device object on the current service's adapter with
// the given object path. // the given object path.
......
...@@ -49,4 +49,17 @@ void BluetoothGattCharacteristicDelegateWrapper::StopNotifications( ...@@ -49,4 +49,17 @@ void BluetoothGattCharacteristicDelegateWrapper::StopNotifications(
characteristic_); characteristic_);
} }
void BluetoothGattCharacteristicDelegateWrapper::PrepareSetValue(
const dbus::ObjectPath& device_path,
const std::vector<uint8_t>& value,
int offset,
bool has_subsequent_request,
const base::Closure& callback,
const device::BluetoothLocalGattService::Delegate::ErrorCallback&
error_callback) {
service()->GetDelegate()->OnCharacteristicPrepareWriteRequest(
GetDeviceWithPath(device_path), characteristic_, value, offset,
has_subsequent_request, callback, error_callback);
}
} // namespace bluez } // namespace bluez
...@@ -41,6 +41,14 @@ class BluetoothGattCharacteristicDelegateWrapper ...@@ -41,6 +41,14 @@ class BluetoothGattCharacteristicDelegateWrapper
const base::Closure& callback, const base::Closure& callback,
const device::BluetoothLocalGattService::Delegate::ErrorCallback& const device::BluetoothLocalGattService::Delegate::ErrorCallback&
error_callback) override; error_callback) override;
void PrepareSetValue(
const dbus::ObjectPath& device_path,
const std::vector<uint8_t>& value,
int offset,
bool has_subsequent_request,
const base::Closure& callback,
const device::BluetoothLocalGattService::Delegate::ErrorCallback&
error_callback) override;
void StartNotifications(const dbus::ObjectPath& device_path, void StartNotifications(const dbus::ObjectPath& device_path,
device::BluetoothGattCharacteristic::NotificationType device::BluetoothGattCharacteristic::NotificationType
notification_type) override; notification_type) override;
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
#include "device/bluetooth/bluetooth_gatt_characteristic.h" #include "device/bluetooth/bluetooth_gatt_characteristic.h"
#include "device/bluetooth/dbus/bluetooth_gatt_attribute_helpers.h" #include "device/bluetooth/dbus/bluetooth_gatt_attribute_helpers.h"
#include "device/bluetooth/dbus/bluetooth_gatt_characteristic_delegate_wrapper.h"
#include "third_party/cros_system_api/dbus/service_constants.h" #include "third_party/cros_system_api/dbus/service_constants.h"
namespace bluez { namespace bluez {
...@@ -89,6 +90,14 @@ BluetoothGattCharacteristicServiceProviderImpl:: ...@@ -89,6 +90,14 @@ BluetoothGattCharacteristicServiceProviderImpl::
weak_ptr_factory_.GetWeakPtr()), weak_ptr_factory_.GetWeakPtr()),
base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::OnExported, base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::OnExported,
weak_ptr_factory_.GetWeakPtr())); weak_ptr_factory_.GetWeakPtr()));
exported_object_->ExportMethod(
bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface,
bluetooth_gatt_characteristic::kPrepareWriteValue,
base::Bind(
&BluetoothGattCharacteristicServiceProviderImpl::PrepareWriteValue,
weak_ptr_factory_.GetWeakPtr()),
base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::OnExported,
weak_ptr_factory_.GetWeakPtr()));
exported_object_->ExportMethod( exported_object_->ExportMethod(
bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface, bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface,
bluetooth_gatt_characteristic::kStartNotify, bluetooth_gatt_characteristic::kStartNotify,
...@@ -123,9 +132,9 @@ void BluetoothGattCharacteristicServiceProviderImpl::SendValueChanged( ...@@ -123,9 +132,9 @@ void BluetoothGattCharacteristicServiceProviderImpl::SendValueChanged(
dbus::Signal signal(dbus::kDBusPropertiesInterface, dbus::Signal signal(dbus::kDBusPropertiesInterface,
dbus::kDBusPropertiesChangedSignal); dbus::kDBusPropertiesChangedSignal);
dbus::MessageWriter writer(&signal); dbus::MessageWriter writer(&signal);
dbus::MessageWriter array_writer(NULL); dbus::MessageWriter array_writer(nullptr);
dbus::MessageWriter dict_entry_writer(NULL); dbus::MessageWriter dict_entry_writer(nullptr);
dbus::MessageWriter variant_writer(NULL); dbus::MessageWriter variant_writer(nullptr);
// interface_name // interface_name
writer.AppendString( writer.AppendString(
...@@ -187,7 +196,7 @@ void BluetoothGattCharacteristicServiceProviderImpl::Get( ...@@ -187,7 +196,7 @@ void BluetoothGattCharacteristicServiceProviderImpl::Get(
std::unique_ptr<dbus::Response> response = std::unique_ptr<dbus::Response> response =
dbus::Response::FromMethodCall(method_call); dbus::Response::FromMethodCall(method_call);
dbus::MessageWriter writer(response.get()); dbus::MessageWriter writer(response.get());
dbus::MessageWriter variant_writer(NULL); dbus::MessageWriter variant_writer(nullptr);
if (property_name == bluetooth_gatt_characteristic::kUUIDProperty) { if (property_name == bluetooth_gatt_characteristic::kUUIDProperty) {
writer.OpenVariant("s", &variant_writer); writer.OpenVariant("s", &variant_writer);
...@@ -299,7 +308,7 @@ void BluetoothGattCharacteristicServiceProviderImpl::WriteValue( ...@@ -299,7 +308,7 @@ void BluetoothGattCharacteristicServiceProviderImpl::WriteValue(
DCHECK(OnOriginThread()); DCHECK(OnOriginThread());
dbus::MessageReader reader(method_call); dbus::MessageReader reader(method_call);
const uint8_t* bytes = NULL; const uint8_t* bytes = nullptr;
size_t length = 0; size_t length = 0;
std::vector<uint8_t> value; std::vector<uint8_t> value;
...@@ -334,6 +343,57 @@ void BluetoothGattCharacteristicServiceProviderImpl::WriteValue( ...@@ -334,6 +343,57 @@ void BluetoothGattCharacteristicServiceProviderImpl::WriteValue(
weak_ptr_factory_.GetWeakPtr(), method_call, response_sender)); weak_ptr_factory_.GetWeakPtr(), method_call, response_sender));
} }
void BluetoothGattCharacteristicServiceProviderImpl::PrepareWriteValue(
dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender) {
VLOG(3) << "BluetoothGattCharacteristicServiceProvider::PrepareWriteValue: "
<< object_path_.value();
DCHECK(OnOriginThread());
dbus::MessageReader reader(method_call);
const uint8_t* bytes = nullptr;
size_t length = 0;
std::vector<uint8_t> value;
if (!reader.PopArrayOfBytes(&bytes, &length)) {
LOG(WARNING) << "Error reading value parameter. PrepareWriteValue called "
<< "with incorrect parameters: " << method_call->ToString();
}
if (bytes)
value.assign(bytes, bytes + length);
std::map<std::string, dbus::MessageReader> options;
dbus::ObjectPath device_path;
uint16_t offset = 0;
bool has_subsequent_write = false;
ReadOptions(&reader, &options);
auto it = options.find(bluetooth_gatt_characteristic::kOptionDevice);
if (it != options.end())
it->second.PopObjectPath(&device_path);
it = options.find(bluetooth_gatt_characteristic::kOptionOffset);
if (it != options.end())
it->second.PopUint16(&offset);
// TODO(b/78650442): kOptionHasSubsequentWrite
it = options.find("has-subsequent-write");
if (it != options.end())
it->second.PopBool(&has_subsequent_write);
if (device_path.value().empty()) {
LOG(WARNING) << "PrepareWriteValue called with incorrect parameters: "
<< method_call->ToString();
// Continue on with an empty device path. This will return a null device to
// the delegate, which should know how to handle it.
}
DCHECK(delegate_);
delegate_->PrepareSetValue(
device_path, value, offset, has_subsequent_write,
base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::OnWriteValue,
weak_ptr_factory_.GetWeakPtr(), method_call, response_sender),
base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::OnFailure,
weak_ptr_factory_.GetWeakPtr(), method_call, response_sender));
}
void BluetoothGattCharacteristicServiceProviderImpl::StartNotify( void BluetoothGattCharacteristicServiceProviderImpl::StartNotify(
dbus::MethodCall* method_call, dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender) { dbus::ExportedObject::ResponseSender response_sender) {
...@@ -418,9 +478,9 @@ void BluetoothGattCharacteristicServiceProviderImpl::OnWriteValue( ...@@ -418,9 +478,9 @@ void BluetoothGattCharacteristicServiceProviderImpl::OnWriteValue(
void BluetoothGattCharacteristicServiceProviderImpl::WriteProperties( void BluetoothGattCharacteristicServiceProviderImpl::WriteProperties(
dbus::MessageWriter* writer) { dbus::MessageWriter* writer) {
dbus::MessageWriter array_writer(NULL); dbus::MessageWriter array_writer(nullptr);
dbus::MessageWriter dict_entry_writer(NULL); dbus::MessageWriter dict_entry_writer(nullptr);
dbus::MessageWriter variant_writer(NULL); dbus::MessageWriter variant_writer(nullptr);
writer->OpenArray("{sv}", &array_writer); writer->OpenArray("{sv}", &array_writer);
......
...@@ -70,6 +70,11 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothGattCharacteristicServiceProviderImpl ...@@ -70,6 +70,11 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothGattCharacteristicServiceProviderImpl
void WriteValue(dbus::MethodCall* method_call, void WriteValue(dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender); dbus::ExportedObject::ResponseSender response_sender);
// Called by BlueZ when a remote central is requesting to prepare the reliable
// write value of this characteristic.
void PrepareWriteValue(dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender);
// Called by BlueZ when a remote central is requesting to start a // Called by BlueZ when a remote central is requesting to start a
// notification session for this characteristic. // notification session for this characteristic.
void StartNotify(dbus::MethodCall* method_call, void StartNotify(dbus::MethodCall* method_call,
......
...@@ -170,6 +170,38 @@ void FakeBluetoothGattCharacteristicServiceProvider::SetValue( ...@@ -170,6 +170,38 @@ void FakeBluetoothGattCharacteristicServiceProvider::SetValue(
delegate_->SetValue(device_path, value, callback, error_callback); delegate_->SetValue(device_path, value, callback, error_callback);
} }
void FakeBluetoothGattCharacteristicServiceProvider::PrepareSetValue(
const dbus::ObjectPath& device_path,
const std::vector<uint8_t>& value,
int offset,
bool has_subsequent_write,
const base::Closure& callback,
const device::BluetoothLocalGattService::Delegate::ErrorCallback&
error_callback) {
VLOG(1) << "GATT characteristic value Prepare Set request: "
<< object_path_.value() << " UUID: " << uuid_;
// Check if this characteristic is registered.
FakeBluetoothGattManagerClient* fake_bluetooth_gatt_manager_client =
static_cast<FakeBluetoothGattManagerClient*>(
bluez::BluezDBusManager::Get()->GetBluetoothGattManagerClient());
if (!fake_bluetooth_gatt_manager_client->IsServiceRegistered(service_path_)) {
VLOG(1) << "GATT characteristic not registered.";
error_callback.Run();
return;
}
if (!CanWrite(flags_)) {
VLOG(1) << "GATT characteristic not writeable.";
error_callback.Run();
return;
}
// Pass on to the delegate.
DCHECK(delegate_);
delegate_->PrepareSetValue(device_path, value, offset, has_subsequent_write,
callback, error_callback);
}
bool FakeBluetoothGattCharacteristicServiceProvider::NotificationsChange( bool FakeBluetoothGattCharacteristicServiceProvider::NotificationsChange(
bool start) { bool start) {
VLOG(1) << "GATT characteristic value notification request: " VLOG(1) << "GATT characteristic value notification request: "
......
...@@ -51,6 +51,14 @@ class DEVICE_BLUETOOTH_EXPORT FakeBluetoothGattCharacteristicServiceProvider ...@@ -51,6 +51,14 @@ class DEVICE_BLUETOOTH_EXPORT FakeBluetoothGattCharacteristicServiceProvider
const base::Closure& callback, const base::Closure& callback,
const device::BluetoothLocalGattService::Delegate::ErrorCallback& const device::BluetoothLocalGattService::Delegate::ErrorCallback&
error_callback); error_callback);
void PrepareSetValue(
const dbus::ObjectPath& device_path,
const std::vector<uint8_t>& value,
int offset,
bool has_subsequent_write,
const base::Closure& callback,
const device::BluetoothLocalGattService::Delegate::ErrorCallback&
error_callback);
// Method to simulate starting and stopping notifications. // Method to simulate starting and stopping notifications.
bool NotificationsChange(bool start); bool NotificationsChange(bool start);
......
...@@ -378,6 +378,17 @@ class BluetoothTestBase : public testing::Test { ...@@ -378,6 +378,17 @@ class BluetoothTestBase : public testing::Test {
const base::Closure& success_callback, const base::Closure& success_callback,
const base::Closure& error_callback) {} const base::Closure& error_callback) {}
// Simulates prepare write a value to a locally hosted GATT characteristic by
// a remote central device.
virtual void SimulateLocalGattCharacteristicValuePrepareWriteRequest(
BluetoothDevice* from_device,
BluetoothLocalGattCharacteristic* characteristic,
const std::vector<uint8_t>& value_to_write,
int offset,
bool has_subsequent_write,
const base::Closure& success_callback,
const base::Closure& error_callback) {}
// Simulates reading a value from a locally hosted GATT descriptor by a // Simulates reading a value from a locally hosted GATT descriptor by a
// remote central device. Returns the value that was read from the local // remote central device. Returns the value that was read from the local
// GATT descriptor in the value callback. // GATT descriptor in the value callback.
......
...@@ -224,6 +224,41 @@ void BluetoothTestBlueZ::SimulateLocalGattCharacteristicValueWriteRequest( ...@@ -224,6 +224,41 @@ void BluetoothTestBlueZ::SimulateLocalGattCharacteristicValueWriteRequest(
run_loop.Run(); run_loop.Run();
} }
void BluetoothTestBlueZ::
SimulateLocalGattCharacteristicValuePrepareWriteRequest(
BluetoothDevice* from_device,
BluetoothLocalGattCharacteristic* characteristic,
const std::vector<uint8_t>& value_to_write,
int offset,
bool has_subsequent_write,
const base::Closure& success_callback,
const base::Closure& error_callback) {
bluez::BluetoothLocalGattCharacteristicBlueZ* characteristic_bluez =
static_cast<bluez::BluetoothLocalGattCharacteristicBlueZ*>(
characteristic);
bluez::FakeBluetoothGattManagerClient* fake_bluetooth_gatt_manager_client =
static_cast<bluez::FakeBluetoothGattManagerClient*>(
bluez::BluezDBusManager::Get()->GetBluetoothGattManagerClient());
bluez::FakeBluetoothGattCharacteristicServiceProvider*
characteristic_provider =
fake_bluetooth_gatt_manager_client->GetCharacteristicServiceProvider(
characteristic_bluez->object_path());
bluez::BluetoothLocalGattServiceBlueZ* service_bluez =
static_cast<bluez::BluetoothLocalGattServiceBlueZ*>(
characteristic->GetService());
static_cast<TestBluetoothLocalGattServiceDelegate*>(
service_bluez->GetDelegate())
->set_expected_characteristic(characteristic);
base::RunLoop run_loop;
characteristic_provider->PrepareSetValue(
GetDevicePath(from_device), value_to_write, offset, has_subsequent_write,
base::Bind(&ClosureCallback, run_loop.QuitClosure(), success_callback),
base::Bind(&ClosureCallback, run_loop.QuitClosure(), error_callback));
run_loop.Run();
}
void BluetoothTestBlueZ::SimulateLocalGattDescriptorValueReadRequest( void BluetoothTestBlueZ::SimulateLocalGattDescriptorValueReadRequest(
BluetoothDevice* from_device, BluetoothDevice* from_device,
BluetoothLocalGattDescriptor* descriptor, BluetoothLocalGattDescriptor* descriptor,
......
...@@ -47,6 +47,14 @@ class BluetoothTestBlueZ : public BluetoothTestBase { ...@@ -47,6 +47,14 @@ class BluetoothTestBlueZ : public BluetoothTestBase {
const std::vector<uint8_t>& value_to_write, const std::vector<uint8_t>& value_to_write,
const base::Closure& success_callback, const base::Closure& success_callback,
const base::Closure& error_callback) override; const base::Closure& error_callback) override;
void SimulateLocalGattCharacteristicValuePrepareWriteRequest(
BluetoothDevice* from_device,
BluetoothLocalGattCharacteristic* characteristic,
const std::vector<uint8_t>& value_to_write,
int offset,
bool has_subsequent_write,
const base::Closure& success_callback,
const base::Closure& error_callback) override;
void SimulateLocalGattDescriptorValueReadRequest( void SimulateLocalGattDescriptorValueReadRequest(
BluetoothDevice* from_device, BluetoothDevice* from_device,
BluetoothLocalGattDescriptor* descriptor, BluetoothLocalGattDescriptor* descriptor,
......
...@@ -53,6 +53,29 @@ void TestBluetoothLocalGattServiceDelegate::OnCharacteristicWriteRequest( ...@@ -53,6 +53,29 @@ void TestBluetoothLocalGattServiceDelegate::OnCharacteristicWriteRequest(
callback.Run(); callback.Run();
} }
void TestBluetoothLocalGattServiceDelegate::OnCharacteristicPrepareWriteRequest(
const BluetoothDevice* device,
const BluetoothLocalGattCharacteristic* characteristic,
const std::vector<uint8_t>& value,
int offset,
bool has_subsequent_request,
const base::Closure& callback,
const ErrorCallback& error_callback) {
EXPECT_EQ(expected_characteristic_->GetIdentifier(),
characteristic->GetIdentifier());
if (should_fail_) {
error_callback.Run();
return;
}
// For testing purpose, we don't maintain a queue for all the pending prepare
// write requests. Instead, we just write the last value, that is, we assume
// |offset| is always 0.
if (!has_subsequent_request)
last_written_value_ = BluetoothGattServerTest::GetInteger(value);
last_seen_device_ = device->GetIdentifier();
callback.Run();
}
void TestBluetoothLocalGattServiceDelegate::OnDescriptorReadRequest( void TestBluetoothLocalGattServiceDelegate::OnDescriptorReadRequest(
const BluetoothDevice* device, const BluetoothDevice* device,
const BluetoothLocalGattDescriptor* descriptor, const BluetoothLocalGattDescriptor* descriptor,
......
...@@ -36,6 +36,14 @@ class TestBluetoothLocalGattServiceDelegate ...@@ -36,6 +36,14 @@ class TestBluetoothLocalGattServiceDelegate
int offset, int offset,
const base::Closure& callback, const base::Closure& callback,
const ErrorCallback& error_callback) override; const ErrorCallback& error_callback) override;
void OnCharacteristicPrepareWriteRequest(
const BluetoothDevice* device,
const BluetoothLocalGattCharacteristic* characteristic,
const std::vector<uint8_t>& value,
int offset,
bool has_subsequent_request,
const base::Closure& callback,
const ErrorCallback& error_callback) override;
void OnDescriptorReadRequest(const BluetoothDevice* device, void OnDescriptorReadRequest(const BluetoothDevice* device,
const BluetoothLocalGattDescriptor* descriptor, const BluetoothLocalGattDescriptor* descriptor,
int offset, int offset,
......
...@@ -1142,6 +1142,19 @@ void BluetoothLowEnergyEventRouter::OnCharacteristicWriteRequest( ...@@ -1142,6 +1142,19 @@ void BluetoothLowEnergyEventRouter::OnCharacteristicWriteRequest(
request, characteristic->GetIdentifier())); request, characteristic->GetIdentifier()));
} }
void BluetoothLowEnergyEventRouter::OnCharacteristicPrepareWriteRequest(
const device::BluetoothDevice* device,
const device::BluetoothLocalGattCharacteristic* characteristic,
const std::vector<uint8_t>& value,
int offset,
bool has_subsequent_request,
const base::Closure& callback,
const Delegate::ErrorCallback& error_callback) {
// TODO(crbug/856869): Support reliable write.
OnCharacteristicWriteRequest(device, characteristic, value, offset, callback,
error_callback);
}
void BluetoothLowEnergyEventRouter::OnDescriptorReadRequest( void BluetoothLowEnergyEventRouter::OnDescriptorReadRequest(
const device::BluetoothDevice* device, const device::BluetoothDevice* device,
const device::BluetoothLocalGattDescriptor* descriptor, const device::BluetoothLocalGattDescriptor* descriptor,
......
...@@ -319,6 +319,14 @@ class BluetoothLowEnergyEventRouter ...@@ -319,6 +319,14 @@ class BluetoothLowEnergyEventRouter
int offset, int offset,
const base::Closure& callback, const base::Closure& callback,
const Delegate::ErrorCallback& error_callback) override; const Delegate::ErrorCallback& error_callback) override;
void OnCharacteristicPrepareWriteRequest(
const device::BluetoothDevice* device,
const device::BluetoothLocalGattCharacteristic* characteristic,
const std::vector<uint8_t>& value,
int offset,
bool has_subsequent_request,
const base::Closure& callback,
const Delegate::ErrorCallback& error_callback) override;
void OnDescriptorReadRequest( void OnDescriptorReadRequest(
const device::BluetoothDevice* device, const device::BluetoothDevice* device,
const device::BluetoothLocalGattDescriptor* descriptor, const device::BluetoothLocalGattDescriptor* descriptor,
......
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