// TODO(armansito): Implement.
}
+void HeartRateServer::OnNotificationSent(
+ const std::string& device_address, int status) {
+ // TODO(armansito): Implement.
+}
+
} // namespace heart_rate
void OnExecuteWriteRequest(
const std::string& device_address,
int request_id, bool is_execute) override;
+ void OnNotificationSent(const std::string& device_address,
+ int status) override;
std::mutex mutex_;
return true;
}
+bool GattServer::SendNotification(
+ const std::string& device_address,
+ const GattIdentifier& characteristic_id,
+ bool confirm,
+ const std::vector<uint8_t>& value,
+ const GattCallback& callback) {
+ VLOG(1) << " - server_if: " << server_if_
+ << " device_address: " << device_address
+ << " confirm: " << confirm;
+ lock_guard<mutex> lock(mutex_);
+
+ bt_bdaddr_t addr;
+ if (!util::BdAddrFromString(device_address, &addr)) {
+ LOG(ERROR) << "Invalid device address given: " << device_address;
+ return false;
+ }
+
+ // Get the connection IDs for which we will send this notification.
+ auto conn_iter = conn_addr_map_.find(device_address);
+ if (conn_iter == conn_addr_map_.end()) {
+ LOG(ERROR) << "No known connections for device with address: "
+ << device_address;
+ return false;
+ }
+
+ // Make sure that |characteristic_id| matches a valid attribute handle.
+ auto handle_iter = id_to_handle_map_.find(characteristic_id);
+ if (handle_iter == id_to_handle_map_.end()) {
+ LOG(ERROR) << "Unknown characteristic";
+ return false;
+ }
+
+ std::shared_ptr<PendingIndication> pending_ind(
+ new PendingIndication(callback));
+
+ // Send the notification/indication on all matching connections.
+ int send_count = 0;
+ for (auto conn : conn_iter->second) {
+ // Make sure that one isn't already pending for this connection.
+ if (pending_indications_.find(conn->conn_id) !=
+ pending_indications_.end()) {
+ VLOG(1) << "A" << (confirm ? "n indication" : " notification")
+ << " is already pending for connection: " << conn->conn_id;
+ continue;
+ }
+
+ // The HAL API takes char* rather const char* for |value|, so we have to
+ // cast away the const.
+ // TODO(armansito): Make HAL accept const char*.
+ bt_status_t status = hal::BluetoothGattInterface::Get()->
+ GetServerHALInterface()->send_indication(
+ server_if_,
+ handle_iter->second,
+ conn->conn_id,
+ value.size(),
+ confirm,
+ reinterpret_cast<char*>(const_cast<uint8_t*>(value.data())));
+
+ // Increment the send count if this was successful. We don't immediately
+ // fail if the HAL returned an error. It's better to report success as long
+ // as we sent out at least one notification to this device as
+ // multi-transport GATT connections from the same BD_ADDR will be rare
+ // enough already.
+ if (status != BT_STATUS_SUCCESS)
+ continue;
+
+ send_count++;
+ pending_indications_[conn->conn_id] = pending_ind;
+ }
+
+ if (send_count == 0) {
+ LOG(ERROR) << "Failed to send notifications/indications to device: "
+ << device_address;
+ return false;
+ }
+
+ return true;
+}
+
void GattServer::ConnectionCallback(
hal::BluetoothGattInterface* /* gatt_iface */,
int conn_id, int server_if,
delegate_->OnExecuteWriteRequest(this, device_address, trans_id, exec_write);
}
+void GattServer::IndicationSentCallback(
+ hal::BluetoothGattInterface* /* gatt_iface */,
+ int conn_id, int status) {
+ VLOG(1) << __func__ << " conn_id: " << conn_id << " status: " << status;
+ lock_guard<mutex> lock(mutex_);
+
+ const auto& pending_ind_iter = pending_indications_.find(conn_id);
+ if (pending_ind_iter == pending_indications_.end()) {
+ VLOG(1) << "Unknown connection: " << conn_id;
+ return;
+ }
+
+ std::shared_ptr<PendingIndication> pending_ind = pending_ind_iter->second;
+ pending_indications_.erase(pending_ind_iter);
+
+ if (status == BT_STATUS_SUCCESS)
+ pending_ind->has_success = true;
+
+ // Invoke it if this was the last reference to the confirmation callback.
+ if (pending_ind.unique() && pending_ind->callback) {
+ pending_ind->callback(
+ pending_ind->has_success ?
+ GATT_ERROR_NONE : static_cast<GATTError>(status));
+ }
+}
+
void GattServer::NotifyEndCallbackAndClearData(
BLEStatus status, const GattIdentifier& id) {
VLOG(1) << __func__ << " status: " << status;
// operation.
using ResultCallback =
std::function<void(BLEStatus status, const GattIdentifier& id)>;
+ using GattCallback = std::function<void(GATTError error)>;
// Starts a new GATT service declaration for the service with the given
// parameters. In the case of an error, for example If a service declaration
GATTError error, int offset,
const std::vector<uint8_t>& value);
+ // Sends an ATT Handle-Value Notification to the device with BD_ADDR
+ // |device_address| for the characteristic with ID |characteristic_id| and
+ // value |value|. If |confirm| is true, then an ATT Handle-Value Indication
+ // will be sent instead, which requires the remote to confirm receipt. Returns
+ // false if there was an immediate error in initiating the notification
+ // procedure. Otherwise, returns true and reports the asynchronous result of
+ // the operation in |callback|.
+ //
+ // If |confirm| is true, then |callback| will be run when the remote device
+ // sends a ATT Handle-Value Confirmation packet. Otherwise, it will be run as
+ // soon as the notification has been sent out.
+ bool SendNotification(const std::string& device_address,
+ const GattIdentifier& characteristic_id,
+ bool confirm, const std::vector<uint8_t>& value,
+ const GattCallback& callback);
+
private:
friend class GattServerFactory;
bt_bdaddr_t bdaddr;
};
+ // Used to keep track of a pending Handle-Value indication.
+ struct PendingIndication {
+ PendingIndication(const GattCallback& callback)
+ : has_success(false), callback(callback) {}
+
+ bool has_success;
+ GattCallback callback;
+ };
+
// Constructor shouldn't be called directly as instance are meant to be
// obtained from the factory.
GattServer(const UUID& uuid, int server_if);
hal::BluetoothGattInterface* gatt_iface,
int conn_id, int trans_id,
const bt_bdaddr_t& bda, int exec_write) override;
+ void IndicationSentCallback(
+ hal::BluetoothGattInterface* gatt_iface,
+ int conn_id, int status) override;
// Helper function that notifies and clears the pending callback.
void NotifyEndCallbackAndClearData(BLEStatus status,
std::unordered_map<std::string, std::vector<std::shared_ptr<Connection>>>
conn_addr_map_;
+ // Connections for which a Handle-Value indication is pending. Since there can
+ // be multiple indications to the same device (in the case of a dual-mode
+ // device with simulatenous BR/EDR & LE GATT connections), we also keep track
+ // of whether there has been at least one successful confirmation.
+ std::unordered_map<int, std::shared_ptr<PendingIndication>>
+ pending_indications_;
+
// Raw handle to the Delegate, which must outlive this GattServer instance.
Delegate* delegate_;
g_interface, conn_id, trans_id, *bda, exec_write));
}
+void IndicationSentCallback(int conn_id, int status) {
+ lock_guard<mutex> lock(g_instance_lock);
+ VLOG(2) << __func__ << " - conn_id: " << conn_id << " status: " << status;
+ VERIFY_INTERFACE_OR_RETURN();
+
+ FOR_EACH_SERVER_OBSERVER(IndicationSentCallback(
+ g_interface, conn_id, status));
+}
+
// The HAL Bluetooth GATT client interface callbacks. These signal a mixture of
// GATT client-role and GAP events.
const btgatt_client_callbacks_t gatt_client_callbacks = {
RequestWriteCallback,
RequestExecWriteCallback,
nullptr, // response_confirmation_cb,
- nullptr, // indication_sent_cb
+ IndicationSentCallback,
nullptr, // congestion_cb
nullptr, // mtu_changed_cb
};
}
void BluetoothGattInterface::ServerObserver::RequestExecWriteCallback(
- BluetoothGattInterface* gatt_iface,
+ BluetoothGattInterface* /* gatt_iface */,
int /* conn_id */,
int /* trans_id */,
const bt_bdaddr_t& /* bda */,
// Do nothing.
}
+void BluetoothGattInterface::ServerObserver::IndicationSentCallback(
+ BluetoothGattInterface* /* gatt_iface */,
+ int /* conn_id */,
+ int /* status */) {
+ // Do nothing.
+}
+
// static
bool BluetoothGattInterface::Initialize() {
lock_guard<mutex> lock(g_instance_lock);
int conn_id, int trans_id,
const bt_bdaddr_t& bda, int exec_write);
+ virtual void IndicationSentCallback(
+ BluetoothGattInterface* gatt_iface, int conn_id, int status);
+
// TODO(armansito): Complete the list of callbacks.
};
return BT_STATUS_FAIL;
}
+bt_status_t FakeSendIndication(int server_if, int attribute_handle,
+ int conn_id, int len, int confirm,
+ char* value) {
+ if (g_server_handler)
+ return g_server_handler->SendIndication(server_if, attribute_handle,
+ conn_id, len, confirm, value);
+
+ return BT_STATUS_FAIL;
+}
+
bt_status_t FakeSendResponse(int conn_id, int trans_id, int status,
btgatt_response_t* response) {
if (g_server_handler)
FakeStartService,
nullptr, // stop_service
FakeDeleteService,
- nullptr, // send_indication
+ FakeSendIndication,
FakeSendResponse,
};
RequestExecWriteCallback(this, conn_id, trans_id, bda, exec_write));
}
+void FakeBluetoothGattInterface::NotifyIndicationSentCallback(
+ int conn_id, int status) {
+ FOR_EACH_OBSERVER(ServerObserver, server_observers_,
+ IndicationSentCallback(this, conn_id, status));
+}
+
void FakeBluetoothGattInterface::AddClientObserver(ClientObserver* observer) {
CHECK(observer);
client_observers_.AddObserver(observer);
virtual bt_status_t StartService(
int server_if, int srvc_handle, int transport) = 0;
virtual bt_status_t DeleteService(int server_if, int srvc_handle) = 0;
+ virtual bt_status_t SendIndication(int server_if, int attribute_handle,
+ int conn_id, int len, int confirm,
+ char* value) = 0;
virtual bt_status_t SendResponse(int conn_id, int trans_id, int status,
btgatt_response_t* response) = 0;
};
bool need_rsp, bool is_prep, uint8_t* value);
void NotifyRequestExecWriteCallback(int conn_id, int trans_id,
const bt_bdaddr_t& bda, int exec_write);
+ void NotifyIndicationSentCallback(int conn_id, int status);
// BluetoothGattInterface overrides:
void AddClientObserver(ClientObserver* observer) override;
return android::NO_ERROR;
}
+ case SEND_NOTIFICATION_TRANSACTION: {
+ int server_if = data.readInt32();
+ std::string device_address = data.readCString();
+ auto char_id = CreateGattIdentifierFromParcel(data);
+ CHECK(char_id);
+ bool confirm = data.readInt32();
+
+ std::vector<uint8_t> value;
+ int value_len = data.readInt32();
+ if (value_len != -1) {
+ uint8_t bytes[value_len];
+ data.read(bytes, value_len);
+ value.insert(value.begin(), bytes, bytes + value_len);
+ }
+
+ bool result = SendNotification(server_if, device_address, *char_id, confirm,
+ value);
+
+ reply->writeInt32(result);
+
+ return android::NO_ERROR;
+ }
default:
return BBinder::onTransact(code, data, reply, flags);
}
return reply.readInt32();
}
+bool BpBluetoothGattServer::SendNotification(
+ int server_if,
+ const std::string& device_address,
+ const bluetooth::GattIdentifier& characteristic_id,
+ bool confirm,
+ const std::vector<uint8_t>& value) {
+ Parcel data, reply;
+
+ data.writeInterfaceToken(IBluetoothGattServer::getInterfaceDescriptor());
+ data.writeInt32(server_if);
+ data.writeCString(device_address.c_str());
+ WriteGattIdentifierToParcel(characteristic_id, &data);
+ data.writeInt32(confirm);
+ data.writeByteArray(value.size(), value.data());
+
+ remote()->transact(IBluetoothGattServer::SEND_NOTIFICATION_TRANSACTION,
+ data, &reply);
+
+ return reply.readInt32();
+}
+
IMPLEMENT_META_INTERFACE(BluetoothGattServer,
IBluetoothGattServer::kServiceName);
int status, int offset,
const std::vector<uint8_t>& value) = 0;
+ virtual bool SendNotification(
+ int server_if,
+ const std::string& device_address,
+ const bluetooth::GattIdentifier& characteristic_id,
+ bool confirm,
+ const std::vector<uint8_t>& value) = 0;
+
// TODO(armansito): Complete the API definition.
private:
int request_id,
int status, int offset,
const std::vector<uint8_t>& value) override;
+ bool SendNotification(
+ int server_if,
+ const std::string& device_address,
+ const bluetooth::GattIdentifier& characteristic_id,
+ bool confirm,
+ const std::vector<uint8_t>& value) override;
private:
DISALLOW_COPY_AND_ASSIGN(BpBluetoothGattServer);
OnExecuteWriteRequest(device_address, request_id, is_exec);
return android::NO_ERROR;
}
+ case ON_NOTIFICATION_SENT_TRANSACTION: {
+ std::string device_address = data.readCString();
+ int status = data.readInt32();
+
+ OnNotificationSent(device_address, status);
+ return android::NO_ERROR;
+ }
default:
return BBinder::onTransact(code, data, reply, flags);
}
IBinder::FLAG_ONEWAY);
}
+void BpBluetoothGattServerCallback::OnNotificationSent(
+ const std::string& device_address,
+ int status) {
+ Parcel data, reply;
+
+ data.writeInterfaceToken(
+ IBluetoothGattServerCallback::getInterfaceDescriptor());
+ data.writeCString(device_address.c_str());
+ data.writeInt32(status);
+
+ remote()->transact(
+ IBluetoothGattServerCallback::ON_NOTIFICATION_SENT_TRANSACTION,
+ data, &reply,
+ IBinder::FLAG_ONEWAY);
+}
+
IMPLEMENT_META_INTERFACE(BluetoothGattServerCallback,
IBluetoothGattServerCallback::kServiceName);
const std::string& device_address,
int request_id, bool is_execute) = 0;
- // TODO(armansito): Complete the API definition.
+ virtual void OnNotificationSent(const std::string& device_address,
+ int status) = 0;
private:
DISALLOW_COPY_AND_ASSIGN(IBluetoothGattServerCallback);
void OnExecuteWriteRequest(
const std::string& device_address,
int request_id, bool is_execute) override;
+ void OnNotificationSent(const std::string& device_address,
+ int status) override;
private:
DISALLOW_COPY_AND_ASSIGN(BpBluetoothGattServerCallback);
offset, value);
}
+bool BluetoothGattServerBinderServer::SendNotification(
+ int server_if,
+ const std::string& device_address,
+ const bluetooth::GattIdentifier& characteristic_id,
+ bool confirm,
+ const std::vector<uint8_t>& value) {
+ VLOG(2) << __func__;
+ std::lock_guard<std::mutex> lock(*maps_lock());
+
+ auto gatt_server = GetGattServer(server_if);
+ if (!gatt_server) {
+ LOG(ERROR) << "Unknown server_if: " << server_if;
+ return false;
+ }
+
+ // Create a weak pointer and pass that to the callback to prevent a potential
+ // use after free.
+ android::wp<BluetoothGattServerBinderServer> weak_ptr_to_this(this);
+ auto callback = [=](bluetooth::GATTError error) {
+ auto sp_to_this = weak_ptr_to_this.promote();
+ if (!sp_to_this.get()) {
+ VLOG(2) << "BluetoothLowEnergyBinderServer was deleted";
+ return;
+ }
+
+ std::lock_guard<std::mutex> lock(*maps_lock());
+
+ auto gatt_cb = GetGattServerCallback(server_if);
+ if (!gatt_cb.get()) {
+ VLOG(2) << "The callback was deleted";
+ return;
+ }
+
+ gatt_cb->OnNotificationSent(device_address, error);
+ };
+
+ if (!gatt_server->SendNotification(device_address, characteristic_id,
+ confirm, value, callback)) {
+ LOG(ERROR) << "Failed to send notification";
+ return false;
+ }
+
+ return true;
+}
+
void BluetoothGattServerBinderServer::OnCharacteristicReadRequest(
bluetooth::GattServer* gatt_server,
const std::string& device_address,
bool SendResponse(int server_if, const std::string& device_address,
int request_id, int status, int offset,
const std::vector<uint8_t>& value) override;
+ bool SendNotification(
+ int server_if,
+ const std::string& device_address,
+ const bluetooth::GattIdentifier& characteristic_id,
+ bool confirm,
+ const std::vector<uint8_t>& value) override;
// bluetooth::GattServer::Delegate overrides:
void OnCharacteristicReadRequest(
MOCK_METHOD4(AddDescriptor, bt_status_t(int, int, bt_uuid_t*, int));
MOCK_METHOD3(StartService, bt_status_t(int, int, int));
MOCK_METHOD2(DeleteService, bt_status_t(int, int));
+ MOCK_METHOD6(SendIndication, bt_status_t(int, int, int, int, int, char*));
MOCK_METHOD4(SendResponse, bt_status_t(int, int, int, btgatt_response_t*));
private:
gatt_server_->SetDelegate(nullptr);
}
+TEST_F(GattServerPostRegisterTest, SendNotification) {
+ SetUpTestService();
+
+ const std::string kTestAddress0 = "01:23:45:67:89:AB";
+ const std::string kTestAddress1 = "cd:ef:01:23:45:67";
+ const std::string kInvalidAddress = "thingamajig blabbidyboop";
+ const int kConnId0 = 0;
+ const int kConnId1 = 1;
+ std::vector<uint8_t> value;
+ bt_bdaddr_t hal_addr0;
+ ASSERT_TRUE(util::BdAddrFromString(kTestAddress0, &hal_addr0));
+
+ // Set up two connections with the same address.
+ fake_hal_gatt_iface_->NotifyServerConnectionCallback(
+ kConnId0, kDefaultServerId, true, hal_addr0);
+ fake_hal_gatt_iface_->NotifyServerConnectionCallback(
+ kConnId1, kDefaultServerId, true, hal_addr0);
+
+ // Set up a test callback.
+ GATTError gatt_error;
+ int callback_count = 0;
+ auto callback = [&](GATTError in_error) {
+ gatt_error = in_error;
+ callback_count++;
+ };
+
+ // Bad device address.
+ EXPECT_FALSE(gatt_server_->SendNotification(
+ kInvalidAddress,
+ test_char_id_, false, value, callback));
+
+ // Bad connection.
+ EXPECT_FALSE(gatt_server_->SendNotification(
+ kTestAddress1,
+ test_char_id_, false, value, callback));
+
+ // We should get a HAL call for each connection for this address. The calls
+ // fail.
+ EXPECT_CALL(*mock_handler_,
+ SendIndication(kDefaultServerId, char_handle_, kConnId0,
+ value.size(), 0, nullptr))
+ .Times(1)
+ .WillOnce(Return(BT_STATUS_FAIL));
+ EXPECT_CALL(*mock_handler_,
+ SendIndication(kDefaultServerId, char_handle_, kConnId1,
+ value.size(), 0, nullptr))
+ .Times(1)
+ .WillOnce(Return(BT_STATUS_FAIL));
+ EXPECT_FALSE(gatt_server_->SendNotification(
+ kTestAddress0,
+ test_char_id_, false, value, callback));
+
+ // One of the calls succeeds.
+ EXPECT_CALL(*mock_handler_,
+ SendIndication(kDefaultServerId, char_handle_, kConnId0,
+ value.size(), 0, nullptr))
+ .Times(1)
+ .WillOnce(Return(BT_STATUS_SUCCESS));
+ EXPECT_CALL(*mock_handler_,
+ SendIndication(kDefaultServerId, char_handle_, kConnId1,
+ value.size(), 0, nullptr))
+ .Times(1)
+ .WillOnce(Return(BT_STATUS_FAIL));
+ EXPECT_TRUE(gatt_server_->SendNotification(
+ kTestAddress0,
+ test_char_id_, false, value, callback));
+
+ // One of the connections is already pending so there should be only one call.
+ // This one we send with confirm=true.
+ EXPECT_CALL(*mock_handler_,
+ SendIndication(kDefaultServerId, char_handle_, kConnId1,
+ value.size(), 1, nullptr))
+ .Times(1)
+ .WillOnce(Return(BT_STATUS_SUCCESS));
+ EXPECT_TRUE(gatt_server_->SendNotification(
+ kTestAddress0,
+ test_char_id_, true, value, callback));
+
+ // Calls are already pending.
+ EXPECT_FALSE(gatt_server_->SendNotification(
+ kTestAddress0, test_char_id_, true, value, callback));
+
+ // Trigger one confirmation callback. We should get calls for two callbacks
+ // since we have two separate calls pending.
+ fake_hal_gatt_iface_->NotifyIndicationSentCallback(
+ kConnId0, BT_STATUS_SUCCESS);
+ fake_hal_gatt_iface_->NotifyIndicationSentCallback(
+ kConnId1, BT_STATUS_SUCCESS);
+ EXPECT_EQ(2, callback_count);
+ EXPECT_EQ(GATT_ERROR_NONE, gatt_error);
+
+ callback_count = 0;
+
+ // Restart. Both calls succeed now.
+ EXPECT_CALL(*mock_handler_,
+ SendIndication(kDefaultServerId, char_handle_, kConnId0,
+ value.size(), 0, nullptr))
+ .Times(1)
+ .WillOnce(Return(BT_STATUS_SUCCESS));
+ EXPECT_CALL(*mock_handler_,
+ SendIndication(kDefaultServerId, char_handle_, kConnId1,
+ value.size(), 0, nullptr))
+ .Times(1)
+ .WillOnce(Return(BT_STATUS_SUCCESS));
+ EXPECT_TRUE(gatt_server_->SendNotification(
+ kTestAddress0,
+ test_char_id_, false, value, callback));
+
+ // Trigger one confirmation callback. The callback we passed should still be
+ // pending. The first callback is for the wrong connection ID.
+ fake_hal_gatt_iface_->NotifyIndicationSentCallback(
+ kConnId0 + 50, BT_STATUS_FAIL);
+ fake_hal_gatt_iface_->NotifyIndicationSentCallback(
+ kConnId0, BT_STATUS_SUCCESS);
+ EXPECT_EQ(0, callback_count);
+
+ // This should be ignored since |kConnId0| was already processed.
+ fake_hal_gatt_iface_->NotifyIndicationSentCallback(
+ kConnId0, BT_STATUS_SUCCESS);
+ EXPECT_EQ(0, callback_count);
+
+ // Run the callback with failure. Since the previous callback reported
+ // success, we should report success.
+ fake_hal_gatt_iface_->NotifyIndicationSentCallback(
+ kConnId1, BT_STATUS_SUCCESS);
+ EXPECT_EQ(1, callback_count);
+ EXPECT_EQ(GATT_ERROR_NONE, gatt_error);
+}
+
} // namespace
} // namespace bluetooth