| /* |
| * Copyright 2021 HIMSA II K/S - www.himsa.com. |
| * Represented by EHIMA - www.ehima.com |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <base/bind.h> |
| #include <base/callback.h> |
| #include <base/logging.h> |
| #include <base/strings/string_number_conversions.h> |
| #include <hardware/bt_csis.h> |
| #include <hardware/bt_gatt_types.h> |
| |
| #include <list> |
| #include <string> |
| #include <vector> |
| |
| #include "advertise_data_parser.h" |
| #include "bta_api.h" |
| #include "bta_csis_api.h" |
| #include "bta_gatt_api.h" |
| #include "bta_gatt_queue.h" |
| #include "bta_groups.h" |
| #include "btif_storage.h" |
| #include "csis_types.h" |
| #include "gap_api.h" |
| #include "gatt_api.h" |
| #include "osi/include/osi.h" |
| #include "osi/include/properties.h" |
| #include "stack/btm/btm_dev.h" |
| #include "stack/btm/btm_int.h" |
| #include "stack/btm/btm_sec.h" |
| #include "stack/crypto_toolbox/crypto_toolbox.h" |
| |
| using base::Closure; |
| using bluetooth::Uuid; |
| using bluetooth::csis::ConnectionState; |
| using bluetooth::csis::CsisClient; |
| using bluetooth::csis::CsisDevice; |
| using bluetooth::csis::CsisDiscoveryState; |
| using bluetooth::csis::CsisGroup; |
| using bluetooth::csis::CsisGroupLockStatus; |
| using bluetooth::csis::CsisInstance; |
| using bluetooth::csis::CsisLockCb; |
| using bluetooth::csis::CsisLockState; |
| using bluetooth::csis::kCsisLockUuid; |
| using bluetooth::csis::kCsisRankUuid; |
| using bluetooth::csis::kCsisServiceUuid; |
| using bluetooth::csis::kCsisSirkUuid; |
| using bluetooth::csis::kCsisSizeUuid; |
| |
| using bluetooth::groups::DeviceGroups; |
| using bluetooth::groups::DeviceGroupsCallbacks; |
| |
| namespace { |
| class CsisClientImpl; |
| CsisClientImpl* instance; |
| DeviceGroupsCallbacks* device_group_callbacks; |
| |
| /** |
| * ----------------------------------------------------------------------------- |
| * Coordinated Set Service - Client role |
| * ----------------------------------------------------------------------------- |
| * |
| * CSIP allows to organize audio servers into sets e.g. Stereo Set, 5.1 Set |
| * and speed up connecting it. |
| * |
| * Since leaudio has already grouping API it was decided to integrate here CSIS |
| * and allow it to group devices semi-automatically. |
| * |
| * Flow: |
| * If connected device contains CSIS services, and it is included into CAP |
| * service or is not included at all, implementation reads all its |
| * characteristisc. The only mandatory characteristic is Set Identity Resolving |
| * Key (SIRK) and once this is read implementation assumes there is at least 2 |
| * devices in the set and start to search for other members by looking for new |
| * Advertising Type (RSI Type) and Resolvable Set Identifier (RSI) in it. |
| * In the meantime other CSIS characteristics are read and Set Size might be |
| * updated. When new set member is found, there is callback called to upper |
| * layer with the address and group id for which member has been found. During |
| * this time Search is stopped. Upper layers bonds new devices and connect Le |
| * Audio profile. If there are other members to find, implementations repeats |
| * the procedure. |
| * |
| */ |
| |
| class CsisClientImpl : public CsisClient { |
| static constexpr uint8_t CSIS_STORAGE_CURRENT_LAYOUT_MAGIC = 0x10; |
| static constexpr size_t CSIS_STORAGE_HEADER_SZ = |
| sizeof(CSIS_STORAGE_CURRENT_LAYOUT_MAGIC) + |
| sizeof(uint8_t); /* num_of_sets */ |
| static constexpr size_t CSIS_STORAGE_ENTRY_SZ = |
| sizeof(uint8_t) /* set_id */ + sizeof(uint8_t) /* desired_size */ + |
| Octet16().size(); |
| |
| public: |
| CsisClientImpl(bluetooth::csis::CsisClientCallbacks* callbacks, |
| Closure initCb) |
| : gatt_if_(0), callbacks_(callbacks) { |
| BTA_GATTC_AppRegister( |
| [](tBTA_GATTC_EVT event, tBTA_GATTC* p_data) { |
| if (instance && p_data) instance->GattcCallback(event, p_data); |
| }, |
| base::Bind( |
| [](Closure initCb, uint8_t client_id, uint8_t status) { |
| if (status != GATT_SUCCESS) { |
| LOG(ERROR) << "Can't start Coordinated Set Service client " |
| "profile - no gatt clients left!"; |
| return; |
| } |
| instance->gatt_if_ = client_id; |
| initCb.Run(); |
| |
| DeviceGroups::Initialize(device_group_callbacks); |
| instance->dev_groups_ = DeviceGroups::Get(); |
| }, |
| initCb), |
| true); |
| |
| DLOG(INFO) << __func__ << " Background scan enabled"; |
| CsisObserverSetBackground(true); |
| } |
| |
| ~CsisClientImpl() override = default; |
| |
| std::shared_ptr<bluetooth::csis::CsisGroup> AssignCsisGroup( |
| const RawAddress& address, int group_id, |
| bool create_group_if_non_existing, const bluetooth::Uuid& uuid) { |
| auto csis_group = FindCsisGroup(group_id); |
| if (!csis_group) { |
| if (create_group_if_non_existing) { |
| /* Let's create a group */ |
| auto g = std::make_shared<CsisGroup>(group_id, uuid); |
| csis_groups_.push_back(g); |
| csis_group = FindCsisGroup(group_id); |
| } else { |
| LOG(ERROR) << __func__ << ": Missing group - that shall not happen"; |
| return nullptr; |
| } |
| } |
| |
| auto device = FindDeviceByAddress(address); |
| if (device == nullptr) { |
| auto dev = std::make_shared<CsisDevice>(address, false); |
| devices_.push_back(dev); |
| device = FindDeviceByAddress(address); |
| } |
| |
| if (!csis_group->IsDeviceInTheGroup(device)) csis_group->AddDevice(device); |
| |
| return csis_group; |
| } |
| |
| void OnGroupAddedCb(const RawAddress& address, const bluetooth::Uuid& uuid, |
| int group_id) { |
| DLOG(INFO) << __func__ << " address: " << address << " uuid: " << uuid |
| << " group_id: " << group_id; |
| |
| AssignCsisGroup(address, group_id, true, uuid); |
| } |
| |
| void OnGroupMemberAddedCb(const RawAddress& address, int group_id) { |
| DLOG(INFO) << __func__ << " address: " << address |
| << " group_id: " << group_id; |
| |
| AssignCsisGroup(address, group_id, false, Uuid::kEmpty); |
| } |
| |
| void OnGroupRemovedCb(const bluetooth::Uuid& uuid, int group_id) { |
| RemoveCsisGroup(group_id); |
| } |
| |
| void OnGroupMemberRemovedCb(const RawAddress& address, int group_id) { |
| DLOG(INFO) << __func__ << ": " << address << " group_id: " << group_id; |
| |
| auto device = FindDeviceByAddress(address); |
| if (device) RemoveCsisDevice(device, group_id); |
| } |
| |
| void OnGroupAddFromStorageCb(const RawAddress& address, |
| const bluetooth::Uuid& uuid, int group_id) { |
| auto device = FindDeviceByAddress(address); |
| if (device == nullptr) return; |
| |
| auto csis_group = FindCsisGroup(group_id); |
| if (csis_group == nullptr) { |
| LOG(ERROR) << __func__ << "the csis group (id: " << group_id |
| << ") does not exist"; |
| return; |
| } |
| |
| if (!csis_group->IsDeviceInTheGroup(device)) { |
| LOG(ERROR) << __func__ << "the csis group (id: " << group_id |
| << ") does contain the device: " << address; |
| return; |
| } |
| |
| if (csis_group->GetUuid() == Uuid::kEmpty) { |
| csis_group->SetUuid(uuid); |
| } |
| |
| callbacks_->OnDeviceAvailable(device->addr, csis_group->GetGroupId(), |
| csis_group->GetDesiredSize(), uuid); |
| } |
| |
| void Connect(const RawAddress& address) override { |
| DLOG(INFO) << __func__ << ": " << address; |
| |
| auto device = FindDeviceByAddress(address); |
| if (device == nullptr) { |
| devices_.emplace_back(std::make_shared<CsisDevice>(address, true)); |
| } else { |
| device->connecting_actively = true; |
| } |
| |
| BTA_GATTC_Open(gatt_if_, address, true, false); |
| } |
| |
| void Disconnect(const RawAddress& addr) override { |
| DLOG(INFO) << __func__ << ": " << addr; |
| |
| btif_storage_set_csis_autoconnect(addr, false); |
| |
| auto device = FindDeviceByAddress(addr); |
| if (device == nullptr) { |
| LOG(WARNING) << "Device not connected to profile" << addr; |
| return; |
| } |
| |
| /* Removes all active connections or registrations for connection */ |
| if (device->IsConnected()) { |
| BTA_GATTC_Close(device->conn_id); |
| } else { |
| BTA_GATTC_CancelOpen(gatt_if_, addr, false); |
| DoDisconnectCleanUp(device); |
| } |
| } |
| |
| void RemoveDevice(const RawAddress& addr) override { |
| DLOG(INFO) << __func__ << ": " << addr; |
| |
| auto device = FindDeviceByAddress(addr); |
| if (!device) return; |
| |
| Disconnect(addr); |
| |
| dev_groups_->RemoveDevice(addr); |
| btif_storage_remove_csis_device(addr); |
| } |
| |
| int GetGroupId(const RawAddress& addr, Uuid uuid) override { |
| auto device = FindDeviceByAddress(addr); |
| if (device == nullptr) return bluetooth::groups::kGroupUnknown; |
| |
| int group_id = dev_groups_->GetGroupId(addr, uuid); |
| auto csis_group = FindCsisGroup(group_id); |
| if (csis_group == nullptr) return bluetooth::groups::kGroupUnknown; |
| |
| return csis_group->GetGroupId(); |
| } |
| |
| void HandleCsisLockProcedureError( |
| std::shared_ptr<CsisGroup>& csis_group, |
| std::shared_ptr<CsisDevice>& csis_device, |
| CsisGroupLockStatus status = |
| CsisGroupLockStatus::FAILED_LOCKED_BY_OTHER) { |
| /* Clear information about ongoing lock procedure */ |
| CsisLockCb cb = csis_group->GetLockCb(); |
| csis_group->SetTargetLockState(CsisLockState::CSIS_STATE_UNSET); |
| |
| int group_id = csis_group->GetGroupId(); |
| CsisLockState current_lock_state = csis_group->GetCurrentLockState(); |
| /* Send unlock to previous devices. It shall be done in reverse order. */ |
| auto prev_dev = csis_group->GetPrevDevice(csis_device); |
| while (prev_dev) { |
| if (prev_dev->IsConnected()) { |
| auto prev_csis_instance = prev_dev->GetCsisInstanceByGroupId(group_id); |
| LOG_ASSERT(prev_csis_instance) << " prev_csis_instance does not exist!"; |
| SetLock(prev_dev, prev_csis_instance, current_lock_state); |
| } |
| prev_dev = csis_group->GetPrevDevice(prev_dev); |
| } |
| /* Call application callback */ |
| NotifyGroupStatus(group_id, false, status, std::move(cb)); |
| } |
| |
| void OnGattCsisWriteLockRsp(uint16_t conn_id, tGATT_STATUS status, |
| uint16_t handle, void* data) { |
| auto device = FindDeviceByConnId(conn_id); |
| if (device == nullptr) { |
| LOG(ERROR) << __func__ << " Device not there"; |
| return; |
| } |
| |
| int group_id = PTR_TO_UINT(data); |
| auto csis_group = FindCsisGroup(group_id); |
| if (csis_group == nullptr) { |
| LOG(ERROR) << __func__ << " There is no group? " << group_id; |
| return; |
| } |
| |
| CsisLockState target_lock_state = csis_group->GetTargetLockState(); |
| if (target_lock_state == CsisLockState::CSIS_STATE_UNSET) return; |
| |
| if (status != GATT_SUCCESS && |
| status != bluetooth::csis::kCsisErrorCodeLockAlreadyGranted) { |
| if (target_lock_state == CsisLockState::CSIS_STATE_UNLOCKED) { |
| /* When unlocking just drop the counter on error and that is it */ |
| csis_group->UpdateLockTransitionCnt(-1); |
| return; |
| } |
| |
| /* In case of GATT ERROR */ |
| LOG(ERROR) << __func__ << " Incorrect write status " |
| << loghex((int)(status)); |
| |
| /* Unlock previous devices */ |
| HandleCsisLockProcedureError(csis_group, device); |
| return; |
| } |
| |
| /* All is good, continue. Try to send lock to other devices.*/ |
| auto csis_instance = device->GetCsisInstanceByGroupId(group_id); |
| LOG_ASSERT(csis_instance) << " csis_instance does not exist!"; |
| csis_instance->SetLockState(target_lock_state); |
| |
| if (csis_group->GetLockTransitionCnt() == 0) { |
| LOG(ERROR) << __func__ << " Not expected lock state"; |
| return; |
| } |
| |
| if (csis_group->UpdateLockTransitionCnt(-1) == 0) { |
| csis_group->SetCurrentLockState(csis_group->GetTargetLockState()); |
| CsisLockCompleted( |
| csis_group, |
| csis_group->GetCurrentLockState() == CsisLockState::CSIS_STATE_LOCKED, |
| CsisGroupLockStatus::SUCCESS); |
| return; |
| } |
| |
| if (target_lock_state == CsisLockState::CSIS_STATE_LOCKED) { |
| std::shared_ptr<CsisDevice> next_dev; |
| |
| do { |
| next_dev = csis_group->GetNextDevice(device); |
| if (!next_dev) break; |
| } while (!next_dev->IsConnected()); |
| |
| if (next_dev) { |
| auto next_csis_inst = next_dev->GetCsisInstanceByGroupId(group_id); |
| LOG_ASSERT(csis_instance) << " csis_instance does not exist!"; |
| if (next_csis_inst->GetLockState() == |
| CsisLockState::CSIS_STATE_LOCKED) { |
| /* Somebody else managed to lock it. |
| * Unlock previous devices |
| */ |
| HandleCsisLockProcedureError(csis_group, device); |
| return; |
| } |
| SetLock(next_dev, next_csis_inst, CsisLockState::CSIS_STATE_LOCKED); |
| } |
| } |
| } |
| |
| void SetLock(std::shared_ptr<CsisDevice>& device, |
| std::shared_ptr<CsisInstance>& csis_instance, |
| CsisLockState lock) { |
| std::vector<uint8_t> value = { |
| (std::underlying_type<CsisLockState>::type)lock}; |
| |
| LOG(INFO) << __func__ << " " << device->addr |
| << " rank: " << int(csis_instance->GetRank()) << " conn_id " |
| << device->conn_id << " handle " |
| << loghex(+(csis_instance->svc_data.lock_handle.val_hdl)); |
| |
| BtaGattQueue::WriteCharacteristic( |
| device->conn_id, csis_instance->svc_data.lock_handle.val_hdl, value, |
| GATT_WRITE, |
| [](uint16_t conn_id, tGATT_STATUS status, uint16_t handle, uint16_t len, |
| const uint8_t* value, void* data) { |
| if (instance) |
| instance->OnGattCsisWriteLockRsp(conn_id, status, handle, data); |
| }, |
| UINT_TO_PTR(csis_instance->GetGroupId())); |
| } |
| |
| void NotifyGroupStatus(int group_id, bool lock, CsisGroupLockStatus status, |
| CsisLockCb cb) { |
| callbacks_->OnGroupLockChanged(group_id, lock, status); |
| if (cb) std::move(cb).Run(group_id, lock, status); |
| } |
| |
| std::vector<RawAddress> GetDeviceList(int group_id) override { |
| std::vector<RawAddress> result; |
| auto csis_group = FindCsisGroup(group_id); |
| |
| if (!csis_group || csis_group->IsEmpty()) return result; |
| |
| auto csis_device = csis_group->GetFirstDevice(); |
| while (csis_device) { |
| result.push_back(csis_device->addr); |
| csis_device = csis_group->GetNextDevice(csis_device); |
| } |
| |
| return result; |
| } |
| |
| void LockGroup(int group_id, bool lock, CsisLockCb cb) override { |
| if (lock) |
| DLOG(INFO) << __func__ << " Locking group: " << int(group_id); |
| else |
| DLOG(INFO) << __func__ << " Unlocking group: " << int(group_id); |
| |
| /* For now we try to lock only connected devices in the group |
| * TODO: We can consider reconnected to not connected devices and then |
| * locked them |
| */ |
| auto csis_group = FindCsisGroup(group_id); |
| if (csis_group == nullptr) { |
| LOG(ERROR) << __func__ << " Group not found: " << group_id; |
| NotifyGroupStatus(group_id, false, |
| CsisGroupLockStatus::FAILED_INVALID_GROUP, |
| std::move(cb)); |
| return; |
| } |
| |
| if (csis_group->IsEmpty()) { |
| NotifyGroupStatus(group_id, false, |
| CsisGroupLockStatus::FAILED_GROUP_EMPTY, std::move(cb)); |
| return; |
| } |
| |
| if (csis_group->GetTargetLockState() != CsisLockState::CSIS_STATE_UNSET) { |
| /* CSIS operation ongoing */ |
| |
| DLOG(INFO) << __func__ << " Lock operation ongoing:" |
| << "group id: " << group_id << "target state " |
| << (csis_group->GetTargetLockState() == |
| CsisLockState::CSIS_STATE_LOCKED |
| ? "lock" |
| : "unlock"); |
| return; |
| } |
| |
| CsisLockState new_lock_state = lock ? CsisLockState::CSIS_STATE_LOCKED |
| : CsisLockState::CSIS_STATE_UNLOCKED; |
| |
| if (csis_group->GetCurrentLockState() == new_lock_state) { |
| DLOG(INFO) << __func__ << " Nothing to do as requested lock is there"; |
| NotifyGroupStatus(group_id, lock, CsisGroupLockStatus::SUCCESS, |
| std::move(cb)); |
| return; |
| } |
| |
| if (lock && !csis_group->IsAvailableForCsisLockOperation()) { |
| DLOG(INFO) << __func__ << " Group " << group_id << " locked by other"; |
| NotifyGroupStatus(group_id, false, |
| CsisGroupLockStatus::FAILED_LOCKED_BY_OTHER, |
| std::move(cb)); |
| return; |
| } |
| |
| csis_group->SetTargetLockState(new_lock_state, std::move(cb)); |
| |
| if (lock) { |
| /* In locking case we need to make sure we lock all the device |
| * and that in case of error on the way to lock the group, we |
| * can revert lock previously locked devices as per specification. |
| */ |
| auto csis_device = csis_group->GetFirstDevice(); |
| auto csis_instance = csis_device->GetCsisInstanceByGroupId(group_id); |
| LOG_ASSERT(csis_instance) << " csis_instance does not exist!"; |
| SetLock(csis_device, csis_instance, new_lock_state); |
| } else { |
| /* For unlocking, we don't have to monitor status of unlocking device, |
| * therefore, we can just send unlock to all of them, in oposite rank |
| * order and check if we get new state notification. |
| */ |
| auto csis_device = csis_group->GetLastDevice(); |
| auto csis_instance = csis_device->GetCsisInstanceByGroupId(group_id); |
| LOG_ASSERT(csis_instance) << " csis_instance does not exist!"; |
| while (csis_device) { |
| if ((csis_device->IsConnected()) && |
| ((csis_instance->GetLockState() != new_lock_state))) { |
| csis_group->UpdateLockTransitionCnt(1); |
| SetLock(csis_device, csis_instance, new_lock_state); |
| } |
| csis_device = csis_group->GetPrevDevice(csis_device); |
| } |
| } |
| } |
| |
| bool SerializeSets(const RawAddress& addr, std::vector<uint8_t>& out) const { |
| auto device = FindDeviceByAddress(addr); |
| if (device == nullptr) { |
| LOG(WARNING) << __func__ << " Skipping unknown device addr= " << addr; |
| return false; |
| } |
| |
| if (device->GetNumberOfCsisInstances() == 0) { |
| LOG(WARNING) << __func__ << " No CSIS instances for addr= " << addr; |
| return false; |
| } |
| |
| DLOG(INFO) << __func__ << ": device=" << device->addr; |
| |
| auto num_sets = device->GetNumberOfCsisInstances(); |
| if ((num_sets == 0) || (num_sets > std::numeric_limits<uint8_t>::max())) |
| return false; |
| |
| out.resize(CSIS_STORAGE_HEADER_SZ + (num_sets * CSIS_STORAGE_ENTRY_SZ)); |
| auto* ptr = out.data(); |
| |
| /* header */ |
| UINT8_TO_STREAM(ptr, CSIS_STORAGE_CURRENT_LAYOUT_MAGIC); |
| UINT8_TO_STREAM(ptr, num_sets); |
| |
| /* set entries */ |
| device->ForEachCsisInstance( |
| [&](const std::shared_ptr<CsisInstance>& csis_inst) { |
| auto gid = csis_inst->GetGroupId(); |
| auto csis_group = FindCsisGroup(gid); |
| if (csis_group == nullptr) { |
| LOG(ERROR) << "SerializeSets: No matching group found!"; |
| return; |
| } |
| |
| UINT8_TO_STREAM(ptr, gid); |
| UINT8_TO_STREAM(ptr, csis_group->GetDesiredSize()); |
| Octet16 sirk = csis_group->GetSirk(); |
| memcpy(ptr, sirk.data(), sirk.size()); |
| ptr += sirk.size(); |
| }); |
| |
| return true; |
| } |
| |
| void DeserializeSets(const RawAddress& addr, const std::vector<uint8_t>& in) { |
| if (in.size() < CSIS_STORAGE_HEADER_SZ + CSIS_STORAGE_ENTRY_SZ) return; |
| auto* ptr = in.data(); |
| |
| uint8_t magic; |
| STREAM_TO_UINT8(magic, ptr); |
| |
| if (magic == CSIS_STORAGE_CURRENT_LAYOUT_MAGIC) { |
| uint8_t num_sets; |
| STREAM_TO_UINT8(num_sets, ptr); |
| |
| if (in.size() < |
| CSIS_STORAGE_HEADER_SZ + (num_sets * CSIS_STORAGE_ENTRY_SZ)) { |
| LOG(ERROR) << "Invalid persistent storage data"; |
| return; |
| } |
| |
| /* sets entries */ |
| while (num_sets--) { |
| uint8_t gid; |
| Octet16 sirk; |
| uint8_t size; |
| |
| STREAM_TO_UINT8(gid, ptr); |
| STREAM_TO_UINT8(size, ptr); |
| STREAM_TO_ARRAY(sirk.data(), ptr, (int)sirk.size()); |
| |
| // Set grouping and SIRK |
| auto csis_group = AssignCsisGroup(addr, gid, true, Uuid::kEmpty); |
| csis_group->SetDesiredSize(size); |
| csis_group->SetSirk(sirk); |
| } |
| } |
| } |
| |
| void AddFromStorage(const RawAddress& addr, const std::vector<uint8_t>& in, |
| bool autoconnect) { |
| DeserializeSets(addr, in); |
| |
| auto device = FindDeviceByAddress(addr); |
| if (device == nullptr) { |
| device = std::make_shared<CsisDevice>(addr, false); |
| devices_.push_back(device); |
| } |
| |
| for (const auto& csis_group : csis_groups_) { |
| if (!csis_group->IsDeviceInTheGroup(device)) continue; |
| |
| if (csis_group->GetUuid() != Uuid::kEmpty) { |
| callbacks_->OnDeviceAvailable(device->addr, csis_group->GetGroupId(), |
| csis_group->GetDesiredSize(), |
| csis_group->GetUuid()); |
| } |
| } |
| |
| if (autoconnect) { |
| BTA_GATTC_Open(gatt_if_, addr, false, false); |
| } |
| } |
| |
| void CleanUp() { |
| DLOG(INFO) << __func__; |
| |
| BTA_GATTC_AppDeregister(gatt_if_); |
| for (auto& device : devices_) { |
| if (device->IsConnected()) BTA_GATTC_Close(device->conn_id); |
| DoDisconnectCleanUp(device); |
| } |
| |
| devices_.clear(); |
| |
| CsisObserverSetBackground(false); |
| dev_groups_->CleanUp(device_group_callbacks); |
| } |
| |
| void Dump(int fd) { |
| std::stringstream stream; |
| |
| stream << " Groups\n"; |
| for (const auto& g : csis_groups_) { |
| stream << " == id: " << g->GetGroupId() << " ==\n" |
| << " uuid: " << g->GetUuid() << "\n" |
| << " desired size: " << g->GetDesiredSize() << "\n" |
| << " discoverable state: " |
| << static_cast<int>(g->GetDiscoveryState()) << "\n" |
| << " current lock state: " |
| << static_cast<int>(g->GetCurrentLockState()) << "\n" |
| << " target lock state: " |
| << static_cast<int>(g->GetTargetLockState()) << "\n" |
| << " devices: \n"; |
| for (auto& device : devices_) { |
| if (!g->IsDeviceInTheGroup(device)) continue; |
| |
| stream << " == addr: " << device->addr << " ==\n" |
| << " csis instance: data:" |
| << "\n"; |
| |
| auto instance = device->GetCsisInstanceByGroupId(g->GetGroupId()); |
| if (!instance) { |
| stream << " No csis instance available\n"; |
| } else { |
| stream << " rank: " << instance->GetRank() << "\n"; |
| } |
| |
| if (!device->IsConnected()) { |
| stream << " Not connected\n"; |
| } else { |
| stream << " Connected conn_id = " |
| << std::to_string(device->conn_id) << "\n"; |
| } |
| } |
| } |
| |
| dprintf(fd, "%s", stream.str().c_str()); |
| } |
| |
| private: |
| std::shared_ptr<CsisDevice> FindDeviceByConnId(uint16_t conn_id) { |
| auto it = find_if(devices_.begin(), devices_.end(), |
| CsisDevice::MatchConnId(conn_id)); |
| if (it != devices_.end()) return (*it); |
| |
| return nullptr; |
| } |
| |
| void RemoveCsisDevice(std::shared_ptr<CsisDevice>& device, int group_id) { |
| auto it = find_if(devices_.begin(), devices_.end(), |
| CsisDevice::MatchAddress(device->addr)); |
| if (it == devices_.end()) return; |
| |
| if (group_id != bluetooth::groups::kGroupUnknown) { |
| auto csis_group = FindCsisGroup(group_id); |
| if (!csis_group) { |
| /* This could happen when remove device is called when bonding is |
| * removed */ |
| DLOG(INFO) << __func__ << " group not found " << group_id; |
| return; |
| } |
| |
| csis_group->RemoveDevice(device->addr); |
| if (csis_group->IsEmpty()) { |
| RemoveCsisGroup(group_id); |
| } |
| device->RemoveCsisInstance(group_id); |
| } |
| |
| if (device->GetNumberOfCsisInstances() == 0) { |
| devices_.erase(it); |
| } |
| } |
| |
| std::shared_ptr<CsisDevice> FindDeviceByAddress( |
| const RawAddress& addr) const { |
| auto it = find_if(devices_.cbegin(), devices_.cend(), |
| CsisDevice::MatchAddress(addr)); |
| if (it != devices_.end()) return (*it); |
| |
| return nullptr; |
| } |
| |
| std::shared_ptr<CsisGroup> FindCsisGroup(int group_id) const { |
| auto it = |
| find_if(csis_groups_.cbegin(), csis_groups_.cend(), |
| [group_id](auto& g) { return (group_id == g->GetGroupId()); }); |
| |
| if (it == csis_groups_.end()) return nullptr; |
| return (*it); |
| } |
| |
| void RemoveCsisGroup(int group_id) { |
| for (auto it = csis_groups_.begin(); it != csis_groups_.end(); it++) { |
| if ((*it)->GetGroupId() == group_id) { |
| csis_groups_.erase(it); |
| return; |
| } |
| } |
| } |
| |
| /* Handle encryption */ |
| void OnEncrypted(std::shared_ptr<CsisDevice>& device) { |
| DLOG(INFO) << __func__ << " " << device->addr; |
| |
| if (device->is_gatt_service_valid) { |
| NotifyCsisDeviceValidAndStoreIfNeeded(device); |
| } else { |
| BTA_GATTC_ServiceSearchRequest(device->conn_id, &kCsisServiceUuid); |
| } |
| } |
| |
| void NotifyCsisDeviceValidAndStoreIfNeeded( |
| std::shared_ptr<CsisDevice>& device) { |
| /* Notify that we are ready to go. Notice that multiple callback calls |
| * for a single device address can be called if device is in more than one |
| * CSIS group. |
| */ |
| bool notify_connected = false; |
| for (const auto& csis_group : csis_groups_) { |
| if (!csis_group->IsDeviceInTheGroup(device)) continue; |
| |
| int group_id = csis_group->GetGroupId(); |
| auto csis_instance = device->GetCsisInstanceByGroupId(group_id); |
| DLOG(INFO) << __func__ << " group id " << group_id; |
| |
| if (!csis_instance) { |
| /* This can happen when some other user added device to group in the |
| * context which is not existing on the peer side. e.g. LeAudio added it |
| * in the CAP context, but CSIS exist on the peer device without a |
| * context. We will endup in having device in 2 groups. One in generic |
| * context with valid csis_instance, and one in CAP context without csis |
| * instance */ |
| LOG(INFO) << __func__ << " csis_instance does not exist for group " |
| << group_id; |
| continue; |
| } |
| |
| callbacks_->OnDeviceAvailable(device->addr, group_id, |
| csis_group->GetDesiredSize(), |
| csis_instance->GetUuid()); |
| notify_connected = true; |
| } |
| if (notify_connected) |
| callbacks_->OnConnectionState(device->addr, ConnectionState::CONNECTED); |
| |
| if (device->first_connection) { |
| device->first_connection = false; |
| btif_storage_set_csis_autoconnect(device->addr, true); |
| } |
| } |
| |
| void OnGattWriteCcc(uint16_t conn_id, tGATT_STATUS status, uint16_t handle, |
| void* user_data) { |
| LOG(INFO) << __func__ << " handle=" << loghex(handle); |
| |
| auto device = FindDeviceByConnId(conn_id); |
| if (device == nullptr) { |
| LOG(INFO) << __func__ << " unknown conn_id=" << loghex(conn_id); |
| BtaGattQueue::Clean(conn_id); |
| return; |
| } |
| } |
| |
| void OnCsisNotification(uint16_t conn_id, uint16_t handle, uint16_t len, |
| const uint8_t* value) { |
| auto device = FindDeviceByConnId(conn_id); |
| if (device == nullptr) { |
| LOG(WARNING) << "Skipping unknown device, conn_id=" << loghex(conn_id); |
| return; |
| } |
| |
| auto csis_instance = device->GetCsisInstanceByOwningHandle(handle); |
| if (csis_instance == nullptr) { |
| LOG(ERROR) << __func__ |
| << " unknown notification handle: " << loghex(handle) |
| << " for conn_id: " << loghex(conn_id); |
| return; |
| } |
| |
| if (handle == csis_instance->svc_data.sirk_handle.val_hdl) { |
| OnCsisSirkValueUpdate(conn_id, GATT_SUCCESS, handle, len, value); |
| } else if (handle == csis_instance->svc_data.lock_handle.val_hdl) { |
| OnCsisLockNotifications(device, csis_instance, len, value); |
| } else if (handle == csis_instance->svc_data.size_handle.val_hdl) { |
| OnCsisSizeValueUpdate(conn_id, GATT_SUCCESS, handle, len, value); |
| } else { |
| LOG(WARNING) << __func__ << " unknown notification handle " |
| << loghex(handle) << " for conn_id " << loghex(conn_id); |
| } |
| } |
| |
| static CsisGroupLockStatus LockError2GroupLockStatus(tGATT_STATUS status) { |
| switch (status) { |
| case bluetooth::csis::kCsisErrorCodeLockDenied: |
| return CsisGroupLockStatus::FAILED_LOCKED_BY_OTHER; |
| case bluetooth::csis::kCsisErrorCodeReleaseNotAllowed: |
| return CsisGroupLockStatus::FAILED_LOCKED_BY_OTHER; |
| case bluetooth::csis::kCsisErrorCodeInvalidValue: |
| return CsisGroupLockStatus::FAILED_OTHER_REASON; |
| default: |
| return CsisGroupLockStatus::FAILED_OTHER_REASON; |
| } |
| } |
| |
| void CsisLockCompleted(std::shared_ptr<CsisGroup>& csis_group, bool lock, |
| CsisGroupLockStatus status) { |
| DLOG(INFO) << __func__ << " group id: " << int(csis_group->GetGroupId()) |
| << "target state " << (lock ? "lock" : "unlock"); |
| |
| NotifyGroupStatus(csis_group->GetGroupId(), lock, status, |
| std::move(csis_group->GetLockCb())); |
| csis_group->SetTargetLockState(CsisLockState::CSIS_STATE_UNSET); |
| } |
| |
| void OnCsisLockNotifications(std::shared_ptr<CsisDevice>& device, |
| std::shared_ptr<CsisInstance>& csis_instance, |
| uint16_t len, const uint8_t* value) { |
| if (len != 1) { |
| LOG(ERROR) << __func__ << " invalid notification len: " << loghex(len); |
| return; |
| } |
| |
| CsisLockState new_lock = (CsisLockState)(value[0]); |
| |
| DLOG(INFO) << " New lock state: " << int(new_lock) |
| << " device rank: " << int(csis_instance->GetRank()) << "\n"; |
| |
| csis_instance->SetLockState(new_lock); |
| |
| auto csis_group = FindCsisGroup(csis_instance->GetGroupId()); |
| if (!csis_group) return; |
| |
| CsisLockCb cb = csis_group->GetLockCb(); |
| if (csis_group->GetTargetLockState() == CsisLockState::CSIS_STATE_UNSET) { |
| if (csis_group->GetCurrentLockState() == |
| CsisLockState::CSIS_STATE_LOCKED && |
| new_lock == CsisLockState::CSIS_STATE_UNLOCKED) { |
| /* We are here when members fires theirs lock timeout. |
| * Not sure what to do with our current lock state. For now we will |
| * change local lock state after first set member removes its lock. Then |
| * we count that others will do the same |
| */ |
| csis_group->SetCurrentLockState(CsisLockState::CSIS_STATE_UNLOCKED); |
| NotifyGroupStatus(csis_group->GetGroupId(), false, |
| CsisGroupLockStatus::SUCCESS, std::move(cb)); |
| } |
| return; |
| } |
| |
| if (csis_group->GetCurrentLockState() != csis_group->GetTargetLockState()) { |
| /* We are in process of changing lock state. If new device lock |
| * state is what is targeted that means all is good, we don't need |
| * to do here nothing, as state will be changed once all the |
| * characteristics are written. If new device state is not what is |
| * targeted, that means, device changed stated unexpectedly and locking |
| * procedure is broken |
| */ |
| if (new_lock != csis_group->GetTargetLockState()) { |
| /* Device changed back the lock state from what we expected, skip |
| * locking and notify user about that |
| */ |
| CsisLockCompleted(csis_group, false, |
| CsisGroupLockStatus::FAILED_OTHER_REASON); |
| } |
| } |
| } |
| |
| void OnCsisSizeValueUpdate(uint16_t conn_id, tGATT_STATUS status, |
| uint16_t handle, uint16_t len, |
| const uint8_t* value) { |
| auto device = FindDeviceByConnId(conn_id); |
| |
| if (device == nullptr) { |
| LOG(WARNING) << "Skipping unknown device, conn_id=" << loghex(conn_id); |
| return; |
| } |
| |
| DLOG(INFO) << __func__ << " " << device->addr |
| << " status: " << loghex(+status); |
| |
| if (status != GATT_SUCCESS) { |
| LOG(ERROR) << __func__ << " Could not read characteristic at handle=" |
| << loghex(handle); |
| BTA_GATTC_Close(device->conn_id); |
| return; |
| } |
| |
| if (len != 1) { |
| LOG(ERROR) << "Invalid size value length=" << +len |
| << " at handle=" << loghex(handle); |
| BTA_GATTC_Close(device->conn_id); |
| return; |
| } |
| |
| auto csis_instance = device->GetCsisInstanceByOwningHandle(handle); |
| if (csis_instance == nullptr) { |
| LOG(ERROR) << __func__ << " Unknown csis instance"; |
| BTA_GATTC_Close(device->conn_id); |
| return; |
| } |
| auto csis_group = FindCsisGroup(csis_instance->GetGroupId()); |
| if (!csis_group) { |
| LOG(ERROR) << __func__ << " Unknown group id yet"; |
| return; |
| } |
| |
| csis_group->SetDesiredSize(value[0]); |
| } |
| |
| void OnCsisLockReadRsp(uint16_t conn_id, tGATT_STATUS status, uint16_t handle, |
| uint16_t len, const uint8_t* value) { |
| auto device = FindDeviceByConnId(conn_id); |
| if (device == nullptr) { |
| LOG(WARNING) << "Skipping unknown device, conn_id=" << loghex(conn_id); |
| return; |
| } |
| |
| LOG(INFO) << __func__ << " " << device->addr |
| << " status: " << loghex(+status); |
| |
| if (status != GATT_SUCCESS) { |
| LOG(ERROR) << __func__ << " Could not read characteristic at handle=" |
| << loghex(handle); |
| BTA_GATTC_Close(device->conn_id); |
| return; |
| } |
| |
| if (len != 1) { |
| LOG(ERROR) << " Invalid lock value length=" << +len |
| << " at handle=" << loghex(handle); |
| BTA_GATTC_Close(device->conn_id); |
| return; |
| } |
| |
| auto csis_instance = device->GetCsisInstanceByOwningHandle(handle); |
| if (csis_instance == nullptr) { |
| LOG(ERROR) << __func__ << " Unknown csis instance"; |
| BTA_GATTC_Close(device->conn_id); |
| return; |
| } |
| csis_instance->SetLockState((CsisLockState)(value[0])); |
| } |
| |
| void OnCsisRankReadRsp(uint16_t conn_id, tGATT_STATUS status, uint16_t handle, |
| uint16_t len, const uint8_t* value) { |
| auto device = FindDeviceByConnId(conn_id); |
| if (device == nullptr) { |
| LOG(WARNING) << __func__ |
| << " Skipping unknown device, conn_id=" << loghex(conn_id); |
| return; |
| } |
| |
| DLOG(INFO) << __func__ << " " << device->addr |
| << " status: " << loghex(+status) << " rank:" << int(value[0]); |
| |
| if (status != GATT_SUCCESS) { |
| LOG(ERROR) << __func__ << " Could not read characteristic at handle=" |
| << loghex(handle); |
| BTA_GATTC_Close(device->conn_id); |
| return; |
| } |
| |
| if (len != 1) { |
| LOG(ERROR) << __func__ << "Invalid rank value length=" << +len |
| << " at handle=" << loghex(handle); |
| BTA_GATTC_Close(device->conn_id); |
| return; |
| } |
| |
| auto csis_instance = device->GetCsisInstanceByOwningHandle(handle); |
| if (csis_instance == nullptr) { |
| LOG(ERROR) << __func__ << " Unknown csis instance handle " << int(handle); |
| BTA_GATTC_Close(device->conn_id); |
| return; |
| } |
| |
| csis_instance->SetRank((value[0])); |
| auto csis_group = FindCsisGroup(csis_instance->GetGroupId()); |
| csis_group->SortByCsisRank(); |
| } |
| |
| void OnCsisObserveCompleted(void) { |
| if (discovering_group_ == -1) { |
| LOG(ERROR) << __func__ << " No ongoing CSIS discovery - disable scan"; |
| return; |
| } |
| |
| auto csis_group = FindCsisGroup(discovering_group_); |
| discovering_group_ = -1; |
| if (csis_group->IsGroupComplete()) |
| csis_group->SetDiscoveryState( |
| CsisDiscoveryState::CSIS_DISCOVERY_COMPLETED); |
| else |
| csis_group->SetDiscoveryState(CsisDiscoveryState::CSIS_DISCOVERY_IDLE); |
| |
| LOG(INFO) << __func__; |
| } |
| |
| /* |
| * Sirk shall be in LE order |
| * encrypted_sirk: LE order |
| */ |
| bool sdf(const RawAddress& address, const Octet16& encrypted_sirk, |
| Octet16& sirk) { |
| tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev(address); |
| if (!p_dev_rec) { |
| LOG(ERROR) << __func__ << " No security for " << address; |
| return false; |
| } |
| |
| DLOG(INFO) << __func__ << " LTK " |
| << base::HexEncode(p_dev_rec->ble.keys.pltk.data(), 16); |
| DLOG(INFO) << __func__ << " IRK " |
| << base::HexEncode(p_dev_rec->ble.keys.irk.data(), 16); |
| |
| /* Calculate salt CSIS d1.0r05 4.3 */ |
| Octet16 zero_key; |
| memset(zero_key.data(), 0, 16); |
| |
| std::string msg1 = "SIRKenc"; |
| std::reverse(msg1.begin(), msg1.end()); |
| |
| Octet16 s1 = crypto_toolbox::aes_cmac(zero_key, (uint8_t*)(msg1.c_str()), |
| msg1.size()); |
| DLOG(INFO) << "s1 (le) " << base::HexEncode(s1.data(), 16); |
| |
| /* Create K = LTK */ |
| DLOG(INFO) << "K (le) " |
| << base::HexEncode(p_dev_rec->ble.keys.pltk.data(), 16) << "\n"; |
| |
| Octet16 T = crypto_toolbox::aes_cmac(s1, p_dev_rec->ble.keys.pltk); |
| DLOG(INFO) << "T (le)" << base::HexEncode(T.data(), 16) << "\n"; |
| |
| std::string msg2 = "csis"; |
| std::reverse(msg2.begin(), msg2.end()); |
| |
| Octet16 k1 = |
| crypto_toolbox::aes_cmac(T, (uint8_t*)(msg2.c_str()), msg2.size()); |
| DLOG(INFO) << "K1 (le) " << base::HexEncode(k1.data(), 16) << "\n"; |
| |
| for (int i = 0; i < 16; i++) sirk[i] = encrypted_sirk[i] ^ k1[i]; |
| |
| DLOG(INFO) << "SIRK (le)" << base::HexEncode(sirk.data(), 16) << "\n"; |
| return true; |
| } |
| |
| std::vector<RawAddress> GetAllRsiFromAdvertising( |
| const tBTA_DM_INQ_RES* result) { |
| const uint8_t* p_service_data = result->p_eir; |
| uint16_t remaining_data_len = result->eir_len; |
| std::vector<RawAddress> devices; |
| uint8_t service_data_len = 0; |
| |
| while ((p_service_data = AdvertiseDataParser::GetFieldByType( |
| p_service_data + service_data_len, |
| (remaining_data_len -= service_data_len), BTM_BLE_AD_TYPE_RSI, |
| &service_data_len))) { |
| RawAddress bda; |
| STREAM_TO_BDADDR(bda, p_service_data); |
| devices.push_back(std::move(bda)); |
| } |
| |
| return std::move(devices); |
| } |
| |
| void OnActiveScanResult(const tBTA_DM_INQ_RES* result) { |
| auto csis_device = FindDeviceByAddress(result->bd_addr); |
| if (csis_device) { |
| DLOG(INFO) << __func__ << " Drop same device .." << result->bd_addr; |
| return; |
| } |
| |
| auto all_rsi = GetAllRsiFromAdvertising(result); |
| if (all_rsi.empty()) return; |
| |
| /* Notify only the actively searched group */ |
| auto csis_group = FindCsisGroup(discovering_group_); |
| if (csis_group == nullptr) { |
| LOG(ERROR) << " No ongoing CSIS discovery - disable scan"; |
| CsisActiveObserverSet(false); |
| return; |
| } |
| |
| auto discovered_group_rsi = std::find_if( |
| all_rsi.cbegin(), all_rsi.cend(), [&csis_group](const auto& rsi) { |
| return csis_group->IsRsiMatching(rsi); |
| }); |
| if (discovered_group_rsi != all_rsi.cend()) { |
| DLOG(INFO) << "Found set member " << result->bd_addr; |
| callbacks_->OnSetMemberAvailable(result->bd_addr, |
| csis_group->GetGroupId()); |
| |
| /* Switch back to the opportunistic observer mode. |
| * When second device will pair, csis will restart active scan |
| * to search more members if needed */ |
| CsisActiveObserverSet(false); |
| csis_group->SetDiscoveryState(CsisDiscoveryState::CSIS_DISCOVERY_IDLE); |
| } |
| } |
| |
| void CsisActiveObserverSet(bool enable) { |
| LOG(INFO) << __func__ << " CSIS Discovery SET: " << enable; |
| |
| BTA_DmBleCsisObserve( |
| enable, [](tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH* p_data) { |
| /* If there's no instance we are most likely shutting |
| * down the whole stack and we can ignore this event. |
| */ |
| if (instance == nullptr) return; |
| |
| if (event == BTA_DM_INQ_CMPL_EVT) { |
| LOG(INFO) << "BLE observe complete. Num Resp: " |
| << static_cast<int>(p_data->inq_cmpl.num_resps); |
| instance->OnCsisObserveCompleted(); |
| instance->CsisObserverSetBackground(true); |
| return; |
| } |
| |
| if (event != BTA_DM_INQ_RES_EVT) { |
| LOG(WARNING) << "Unknown event: " << event; |
| return; |
| } |
| |
| instance->OnActiveScanResult(&p_data->inq_res); |
| }); |
| BTA_DmBleScan(enable, bluetooth::csis::kDefaultScanDurationS); |
| |
| /* Need to call it by ourselfs */ |
| if (!enable) { |
| OnCsisObserveCompleted(); |
| CsisObserverSetBackground(true); |
| } |
| } |
| |
| void CsisActiveDiscovery(std::shared_ptr<CsisGroup> csis_group) { |
| if ((csis_group->GetDiscoveryState() != |
| CsisDiscoveryState::CSIS_DISCOVERY_IDLE)) { |
| LOG(ERROR) << __func__ |
| << " Incorrect ase group: " << csis_group->GetGroupId() |
| << " state " |
| << loghex(static_cast<int>(csis_group->GetDiscoveryState())); |
| return; |
| } |
| |
| csis_group->SetDiscoveryState(CsisDiscoveryState::CSIS_DISCOVERY_ONGOING); |
| /* TODO Maybe we don't need it */ |
| discovering_group_ = csis_group->GetGroupId(); |
| CsisActiveObserverSet(true); |
| } |
| |
| void OnScanBackgroundResult(const tBTA_DM_INQ_RES* result) { |
| if (csis_groups_.empty()) return; |
| |
| auto csis_device = FindDeviceByAddress(result->bd_addr); |
| if (csis_device) { |
| DLOG(INFO) << __func__ << " Drop same device .." << result->bd_addr; |
| return; |
| } |
| |
| auto all_rsi = GetAllRsiFromAdvertising(result); |
| if (all_rsi.empty()) return; |
| |
| /* Notify all the groups this device belongs to. */ |
| for (auto& group : csis_groups_) { |
| for (auto& rsi : all_rsi) { |
| if (group->IsRsiMatching(rsi)) { |
| DLOG(INFO) << " Found set member in background scan " |
| << result->bd_addr; |
| callbacks_->OnSetMemberAvailable(result->bd_addr, |
| group->GetGroupId()); |
| break; |
| } |
| } |
| } |
| } |
| |
| void CsisObserverSetBackground(bool enable) { |
| DLOG(INFO) << __func__ << " CSIS Discovery background: " << enable; |
| |
| BTA_DmBleCsisObserve( |
| enable, [](tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH* p_data) { |
| /* If there's no instance we are most likely shutting |
| * down the whole stack and we can ignore this event. |
| */ |
| if (instance == nullptr) return; |
| |
| if (event == BTA_DM_INQ_CMPL_EVT) { |
| DLOG(INFO) << "BLE observe complete. Num Resp: " |
| << static_cast<int>(p_data->inq_cmpl.num_resps); |
| return; |
| } |
| |
| if (event != BTA_DM_INQ_RES_EVT) { |
| LOG(WARNING) << "Unknown event: " << event; |
| return; |
| } |
| |
| instance->OnScanBackgroundResult(&p_data->inq_res); |
| }); |
| } |
| |
| void OnCsisSirkValueUpdate(uint16_t conn_id, tGATT_STATUS status, |
| uint16_t handle, uint16_t len, |
| const uint8_t* value, |
| bool notify_valid_services = true) { |
| auto device = FindDeviceByConnId(conn_id); |
| if (device == nullptr) { |
| LOG(WARNING) << __func__ |
| << " Skipping unknown device, conn_id=" << loghex(conn_id); |
| return; |
| } |
| |
| DLOG(INFO) << __func__ << " " << device->addr |
| << " status: " << loghex(+status); |
| |
| if (status != GATT_SUCCESS) { |
| /* TODO handle error codes: |
| * kCsisErrorCodeLockAccessSirkRejected |
| * kCsisErrorCodeLockOobSirkOnly |
| */ |
| LOG(ERROR) << __func__ << " Could not read characteristic at handle=" |
| << loghex(handle); |
| BTA_GATTC_Close(device->conn_id); |
| return; |
| } |
| |
| if (len != bluetooth::csis::kCsisSirkCharLen) { |
| LOG(ERROR) << "Invalid sirk value length=" << +len |
| << " at handle=" << loghex(handle); |
| BTA_GATTC_Close(device->conn_id); |
| return; |
| } |
| |
| auto csis_instance = device->GetCsisInstanceByOwningHandle(handle); |
| if (csis_instance == nullptr) { |
| LOG(ERROR) << __func__ << " Unknown csis instance: handle " |
| << loghex(handle); |
| BTA_GATTC_Close(device->conn_id); |
| return; |
| } |
| |
| uint8_t sirk_type = value[0]; |
| LOG(INFO) << __func__ << " SIRK Type: " << +sirk_type; |
| |
| /* Verify if sirk is not all zeros */ |
| Octet16 zero{}; |
| if (memcmp(zero.data(), value + 1, 16) == 0) { |
| LOG(ERROR) << "Received invalid zero SIRK address: " |
| << loghex(device->conn_id) << ". Disconnecting "; |
| BTA_GATTC_Close(device->conn_id); |
| return; |
| } |
| |
| Octet16 received_sirk; |
| memcpy(received_sirk.data(), value + 1, 16); |
| |
| if (sirk_type == bluetooth::csis::kCsisSirkTypeEncrypted) { |
| /* Decrypt encrypted SIRK */ |
| Octet16 sirk; |
| sdf(device->addr, received_sirk, sirk); |
| received_sirk = sirk; |
| } |
| |
| /* SIRK is ready. Add device to the group */ |
| |
| std::shared_ptr<CsisGroup> csis_group; |
| int group_id = csis_instance->GetGroupId(); |
| if (group_id != bluetooth::groups::kGroupUnknown) { |
| /* Group already exist. */ |
| csis_group = FindCsisGroup(group_id); |
| LOG_ASSERT(csis_group) << " group does not exist? " << group_id; |
| } else { |
| /* Now having SIRK we can decide if the device belongs to some group we |
| * know or this is a new group |
| */ |
| for (auto& g : csis_groups_) { |
| if (g->IsSirkBelongsToGroup(received_sirk)) { |
| group_id = g->GetGroupId(); |
| break; |
| } |
| } |
| |
| if (group_id == bluetooth::groups::kGroupUnknown) { |
| /* Here it means, we have new group. Let's us create it */ |
| group_id = |
| dev_groups_->AddDevice(device->addr, csis_instance->GetUuid()); |
| LOG_ASSERT(group_id != -1); |
| |
| /* Create new group */ |
| auto g = |
| std::make_shared<CsisGroup>(group_id, csis_instance->GetUuid()); |
| csis_groups_.push_back(g); |
| } else { |
| dev_groups_->AddDevice(device->addr, csis_instance->GetUuid(), |
| group_id); |
| } |
| |
| csis_group = FindCsisGroup(group_id); |
| csis_group->AddDevice(device); |
| /* Let's update csis instance group id */ |
| csis_instance->SetGroupId(group_id); |
| } |
| |
| csis_group->SetSirk(received_sirk); |
| device->is_gatt_service_valid = true; |
| btif_storage_update_csis_info(device->addr); |
| |
| if (notify_valid_services) NotifyCsisDeviceValidAndStoreIfNeeded(device); |
| |
| DLOG(INFO) << " SIRK " << base::HexEncode(received_sirk.data(), 16) |
| << " address" << device->addr; |
| |
| DLOG(INFO) << " Expected group size " |
| << loghex(csis_group->GetDesiredSize()) |
| << ", actual group Size: " |
| << loghex(csis_group->GetCurrentSize()); |
| |
| /* Start active search for the other device */ |
| if (csis_group->GetDesiredSize() > csis_group->GetCurrentSize()) |
| CsisActiveDiscovery(csis_group); |
| } |
| |
| void DoDisconnectCleanUp(std::shared_ptr<CsisDevice> device) { |
| DLOG(INFO) << __func__ << ": device=" << device->addr; |
| |
| device->ForEachCsisInstance( |
| [&](const std::shared_ptr<CsisInstance>& csis_inst) { |
| DisableGattNotification(device->conn_id, device->addr, |
| csis_inst->svc_data.lock_handle.val_hdl); |
| DisableGattNotification(device->conn_id, device->addr, |
| csis_inst->svc_data.sirk_handle.val_hdl); |
| DisableGattNotification(device->conn_id, device->addr, |
| csis_inst->svc_data.size_handle.val_hdl); |
| }); |
| |
| if (device->IsConnected()) { |
| BtaGattQueue::Clean(device->conn_id); |
| device->conn_id = GATT_INVALID_CONN_ID; |
| } |
| } |
| |
| bool OnCsisServiceFound(std::shared_ptr<CsisDevice> device, |
| const gatt::Service* service, |
| const bluetooth::Uuid& context_uuid, |
| bool is_last_instance) { |
| DLOG(INFO) << __func__ << " service handle: " << loghex(service->handle) |
| << " end handle: " << loghex(service->end_handle) |
| << " uuid: " << context_uuid; |
| |
| auto csis_inst = std::make_shared<CsisInstance>( |
| (uint16_t)service->handle, (uint16_t)service->end_handle, context_uuid); |
| |
| /* Let's check if we know group of this device */ |
| int group_id = dev_groups_->GetGroupId(device->addr, context_uuid); |
| if (group_id != bluetooth::groups::kGroupUnknown) |
| csis_inst->SetGroupId(group_id); |
| |
| /* Initially validate and store GATT service discovery data */ |
| for (const gatt::Characteristic& charac : service->characteristics) { |
| if (charac.uuid == kCsisLockUuid) { |
| /* Find the mandatory CCC descriptor */ |
| uint16_t ccc_handle = |
| FindCccHandle(device->conn_id, charac.value_handle); |
| if (ccc_handle == GAP_INVALID_HANDLE) { |
| DLOG(ERROR) << __func__ |
| << ": no HAS Active Preset CCC descriptor found!"; |
| return false; |
| } |
| csis_inst->svc_data.lock_handle.val_hdl = charac.value_handle; |
| csis_inst->svc_data.lock_handle.ccc_hdl = ccc_handle; |
| |
| SubscribeForNotifications(device->conn_id, device->addr, |
| charac.value_handle, ccc_handle); |
| |
| DLOG(INFO) << __func__ << " Lock UUID found handle: " |
| << loghex(csis_inst->svc_data.lock_handle.val_hdl) |
| << " ccc handle: " |
| << loghex(csis_inst->svc_data.lock_handle.ccc_hdl); |
| } else if (charac.uuid == kCsisRankUuid) { |
| csis_inst->svc_data.rank_handle = charac.value_handle; |
| |
| DLOG(INFO) << __func__ << " Rank UUID found handle: " |
| << loghex(csis_inst->svc_data.rank_handle); |
| } else if (charac.uuid == kCsisSirkUuid) { |
| /* Find the optional CCC descriptor */ |
| uint16_t ccc_handle = |
| FindCccHandle(device->conn_id, charac.value_handle); |
| csis_inst->svc_data.sirk_handle.ccc_hdl = ccc_handle; |
| csis_inst->svc_data.sirk_handle.val_hdl = charac.value_handle; |
| |
| if (ccc_handle != GAP_INVALID_HANDLE) |
| SubscribeForNotifications(device->conn_id, device->addr, |
| charac.value_handle, ccc_handle); |
| |
| DLOG(INFO) << __func__ << " SIRK UUID found handle: " |
| << loghex(csis_inst->svc_data.sirk_handle.val_hdl) |
| << " ccc handle: " |
| << loghex(csis_inst->svc_data.sirk_handle.ccc_hdl); |
| } else if (charac.uuid == kCsisSizeUuid) { |
| /* Find the optional CCC descriptor */ |
| uint16_t ccc_handle = |
| FindCccHandle(device->conn_id, charac.value_handle); |
| csis_inst->svc_data.size_handle.ccc_hdl = ccc_handle; |
| csis_inst->svc_data.size_handle.val_hdl = charac.value_handle; |
| |
| if (ccc_handle != GAP_INVALID_HANDLE) |
| SubscribeForNotifications(device->conn_id, device->addr, |
| charac.value_handle, ccc_handle); |
| |
| DLOG(INFO) << __func__ << " Size UUID found handle: " |
| << loghex(csis_inst->svc_data.size_handle.val_hdl) |
| << " ccc handle: " |
| << loghex(csis_inst->svc_data.size_handle.ccc_hdl); |
| } |
| } |
| |
| /* Sirk is the only mandatory characteristic. If it is in |
| * place, service is OK |
| */ |
| if (csis_inst->svc_data.sirk_handle.val_hdl == GAP_INVALID_HANDLE) { |
| /* We have some characteristics but all dependencies are not satisfied */ |
| LOG(ERROR) << __func__ << " Service has a broken structure."; |
| return false; |
| } |
| device->SetCsisInstance(csis_inst->svc_data.start_handle, csis_inst); |
| |
| /* Read SIRK */ |
| BtaGattQueue::ReadCharacteristic( |
| device->conn_id, csis_inst->svc_data.sirk_handle.val_hdl, |
| [](uint16_t conn_id, tGATT_STATUS status, uint16_t handle, uint16_t len, |
| uint8_t* value, void* user_data) { |
| if (instance) |
| instance->OnCsisSirkValueUpdate(conn_id, status, handle, len, value, |
| (bool)user_data); |
| }, |
| (void*)is_last_instance); |
| |
| /* Read Lock */ |
| if (csis_inst->svc_data.lock_handle.val_hdl != GAP_INVALID_HANDLE) { |
| BtaGattQueue::ReadCharacteristic( |
| device->conn_id, csis_inst->svc_data.lock_handle.val_hdl, |
| [](uint16_t conn_id, tGATT_STATUS status, uint16_t handle, |
| uint16_t len, uint8_t* value, void* user_data) { |
| if (instance) |
| instance->OnCsisLockReadRsp(conn_id, status, handle, len, value); |
| }, |
| nullptr); |
| } |
| |
| /* Read Size */ |
| if (csis_inst->svc_data.size_handle.val_hdl != GAP_INVALID_HANDLE) { |
| BtaGattQueue::ReadCharacteristic( |
| device->conn_id, csis_inst->svc_data.size_handle.val_hdl, |
| [](uint16_t conn_id, tGATT_STATUS status, uint16_t handle, |
| uint16_t len, uint8_t* value, void* user_data) { |
| if (instance) |
| instance->OnCsisSizeValueUpdate(conn_id, status, handle, len, |
| value); |
| }, |
| nullptr); |
| } |
| |
| /* Read Rank */ |
| if (csis_inst->svc_data.rank_handle != GAP_INVALID_HANDLE) { |
| BtaGattQueue::ReadCharacteristic( |
| device->conn_id, csis_inst->svc_data.rank_handle, |
| [](uint16_t conn_id, tGATT_STATUS status, uint16_t handle, |
| uint16_t len, uint8_t* value, void* user_data) { |
| if (instance) |
| instance->OnCsisRankReadRsp(conn_id, status, handle, len, value); |
| }, |
| nullptr); |
| } |
| return true; |
| } |
| |
| /* These are all generic GATT event handlers calling HAS specific code. */ |
| void GattcCallback(tBTA_GATTC_EVT event, tBTA_GATTC* p_data) { |
| LOG(INFO) << __func__ << " event = " << static_cast<int>(event); |
| |
| /* This is in case Csis CleanUp is already done |
| * while GATT is still up and could send events |
| */ |
| if (!instance) return; |
| |
| switch (event) { |
| case BTA_GATTC_DEREG_EVT: |
| break; |
| |
| case BTA_GATTC_OPEN_EVT: |
| OnGattConnected(p_data->open); |
| break; |
| |
| case BTA_GATTC_CLOSE_EVT: |
| OnGattDisconnected(p_data->close); |
| break; |
| |
| case BTA_GATTC_SEARCH_CMPL_EVT: |
| OnGattServiceSearchComplete(p_data->search_cmpl); |
| break; |
| |
| case BTA_GATTC_NOTIF_EVT: |
| OnGattNotification(p_data->notify); |
| break; |
| |
| case BTA_GATTC_ENC_CMPL_CB_EVT: |
| OnLeEncryptionComplete(p_data->enc_cmpl.remote_bda, BTM_SUCCESS); |
| break; |
| |
| case BTA_GATTC_SRVC_CHG_EVT: |
| OnGattServiceChangeEvent(p_data->remote_bda); |
| break; |
| |
| case BTA_GATTC_SRVC_DISC_DONE_EVT: |
| OnGattServiceDiscoveryDoneEvent(p_data->remote_bda); |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| void OnGattConnected(const tBTA_GATTC_OPEN& evt) { |
| DLOG(INFO) << __func__ << ": address=" << evt.remote_bda |
| << ", conn_id=" << evt.conn_id; |
| |
| auto device = FindDeviceByAddress(evt.remote_bda); |
| if (device == nullptr) { |
| DLOG(WARNING) << "Skipping unknown device, address=" << evt.remote_bda; |
| BTA_GATTC_Close(evt.conn_id); |
| return; |
| } |
| |
| if (evt.status != GATT_SUCCESS) { |
| DLOG(INFO) << "Failed to connect to server device"; |
| if (device->connecting_actively) |
| callbacks_->OnConnectionState(evt.remote_bda, |
| ConnectionState::DISCONNECTED); |
| DoDisconnectCleanUp(device); |
| return; |
| } |
| |
| device->connecting_actively = false; |
| device->conn_id = evt.conn_id; |
| |
| /* Verify bond */ |
| uint8_t sec_flag = 0; |
| BTM_GetSecurityFlagsByTransport(evt.remote_bda, &sec_flag, BT_TRANSPORT_LE); |
| |
| /* If link has been encrypted look for the service or report */ |
| if (sec_flag & BTM_SEC_FLAG_ENCRYPTED) { |
| if (device->is_gatt_service_valid) { |
| instance->OnEncrypted(device); |
| } else { |
| BTA_GATTC_ServiceSearchRequest(device->conn_id, &kCsisServiceUuid); |
| } |
| |
| return; |
| } |
| |
| int result = BTM_SetEncryption( |
| evt.remote_bda, BT_TRANSPORT_LE, |
| [](const RawAddress* bd_addr, tBT_TRANSPORT transport, void* p_ref_data, |
| tBTM_STATUS status) { |
| if (instance) instance->OnLeEncryptionComplete(*bd_addr, status); |
| }, |
| nullptr, BTM_BLE_SEC_ENCRYPT); |
| |
| DLOG(INFO) << __func__ |
| << " Encryption required. Request result: " << result; |
| } |
| |
| void OnGattDisconnected(const tBTA_GATTC_CLOSE& evt) { |
| auto device = FindDeviceByAddress(evt.remote_bda); |
| if (device == nullptr) { |
| LOG(WARNING) << "Skipping unknown device disconnect, conn_id=" |
| << loghex(evt.conn_id); |
| return; |
| } |
| |
| DLOG(INFO) << __func__ << ": device=" << device->addr; |
| |
| callbacks_->OnConnectionState(evt.remote_bda, |
| ConnectionState::DISCONNECTED); |
| |
| // Unlock others only if device was locked by us but has disconnected |
| // unexpectedly. |
| if ((evt.reason == GATT_CONN_TIMEOUT) || |
| (evt.reason == GATT_CONN_TERMINATE_PEER_USER)) { |
| device->ForEachCsisInstance( |
| [&](const std::shared_ptr<CsisInstance>& csis_inst) { |
| auto csis_group = FindCsisGroup(csis_inst->GetGroupId()); |
| if (csis_group == nullptr) return; |
| if ((csis_group->GetCurrentLockState() == |
| CsisLockState::CSIS_STATE_LOCKED)) { |
| HandleCsisLockProcedureError( |
| csis_group, device, |
| CsisGroupLockStatus::LOCKED_GROUP_MEMBER_LOST); |
| } |
| }); |
| } |
| |
| DoDisconnectCleanUp(device); |
| } |
| |
| void OnGattServiceSearchComplete(const tBTA_GATTC_SEARCH_CMPL& evt) { |
| auto device = FindDeviceByConnId(evt.conn_id); |
| |
| if (device == nullptr) { |
| LOG(WARNING) << __func__ << " Skipping unknown device, conn_id=" |
| << loghex(evt.conn_id); |
| return; |
| } |
| |
| /* Ignore if our service data is valid (discovery initiated by someone |
| * else?) */ |
| if (!device->is_gatt_service_valid) { |
| if (evt.status != GATT_SUCCESS) { |
| LOG(ERROR) << __func__ << " Service discovery failed"; |
| BTA_GATTC_Close(device->conn_id); |
| DoDisconnectCleanUp(device); |
| return; |
| } |
| |
| DLOG(INFO) << __func__; |
| |
| const std::list<gatt::Service>* all_services = |
| BTA_GATTC_GetServices(device->conn_id); |
| |
| std::vector<uint16_t> all_csis_start_handles; |
| |
| /* Le's just find all the CSIS primary services and store the start |
| * handles */ |
| for (auto& svrc : *all_services) { |
| if (svrc.uuid == kCsisServiceUuid) { |
| all_csis_start_handles.push_back(svrc.handle); |
| } |
| } |
| |
| if (all_csis_start_handles.size() == 0) { |
| DLOG(INFO) << __func__ << " No Csis instances found"; |
| BTA_GATTC_Close(device->conn_id); |
| RemoveCsisDevice(device, bluetooth::groups::kGroupUnknown); |
| return; |
| } |
| |
| for (auto& svrc : *all_services) { |
| if (svrc.uuid == kCsisServiceUuid) continue; |
| |
| /* Try to find context for CSIS instances */ |
| for (auto& included_srvc : svrc.included_services) { |
| if (included_srvc.uuid == kCsisServiceUuid) { |
| auto csis_svrc = BTA_GATTC_GetOwningService( |
| device->conn_id, included_srvc.start_handle); |
| auto iter = std::find(all_csis_start_handles.begin(), |
| all_csis_start_handles.end(), |
| included_srvc.start_handle); |
| if (iter != all_csis_start_handles.end()) |
| all_csis_start_handles.erase(iter); |
| instance->OnCsisServiceFound(device, csis_svrc, svrc.uuid, |
| all_csis_start_handles.empty()); |
| } |
| } |
| } |
| |
| /* Here if CSIS is included, all_csis_start_handles should be empty |
| * Otherwise it means, we have some primary CSIS without a context, |
| * which means it is for the complete device. |
| * As per spec, there can be only one service like this. |
| */ |
| if (all_csis_start_handles.size()) { |
| DLOG(INFO) << __func__ << " there is " << all_csis_start_handles.size() |
| << " primary services without a context"; |
| auto csis_svrc = BTA_GATTC_GetOwningService(device->conn_id, |
| all_csis_start_handles[0]); |
| instance->OnCsisServiceFound( |
| device, csis_svrc, bluetooth::groups::kGenericContextUuid, true); |
| all_csis_start_handles.clear(); |
| } |
| } else { |
| /* This might be set already if there is no optional attributes to read |
| * or write. |
| */ |
| if (evt.status == GATT_SUCCESS) { |
| NotifyCsisDeviceValidAndStoreIfNeeded(device); |
| } |
| } |
| } |
| |
| void OnGattNotification(const tBTA_GATTC_NOTIFY& evt) { |
| /* Reject invalid lengths and indications as they are not supported */ |
| if (!evt.is_notify || evt.len > GATT_MAX_ATTR_LEN) { |
| LOG(ERROR) << __func__ << ": rejected BTA_GATTC_NOTIF_EVT. is_notify = " |
| << evt.is_notify << ", len=" << static_cast<int>(evt.len); |
| } |
| |
| OnCsisNotification(evt.conn_id, evt.handle, evt.len, evt.value); |
| } |
| |
| void OnLeEncryptionComplete(const RawAddress& address, uint8_t status) { |
| DLOG(INFO) << __func__ << " " << address; |
| auto device = FindDeviceByAddress(address); |
| if (device == nullptr) { |
| LOG(WARNING) << "Skipping unknown device" << address; |
| return; |
| } |
| |
| if (status != BTM_SUCCESS) { |
| LOG(ERROR) << "encryption failed" |
| << " status: " << status; |
| |
| BTA_GATTC_Close(device->conn_id); |
| return; |
| } |
| |
| if (device->is_gatt_service_valid) { |
| instance->OnEncrypted(device); |
| } else { |
| device->first_connection = true; |
| BTA_GATTC_ServiceSearchRequest(device->conn_id, &kCsisServiceUuid); |
| } |
| } |
| |
| void OnGattServiceChangeEvent(const RawAddress& address) { |
| auto device = FindDeviceByAddress(address); |
| if (!device) { |
| LOG(WARNING) << "Skipping unknown device" << address; |
| return; |
| } |
| |
| DLOG(INFO) << __func__ << ": address=" << address; |
| |
| /* Invalidate service discovery results */ |
| BtaGattQueue::Clean(device->conn_id); |
| device->first_connection = true; |
| device->ClearSvcData(); |
| } |
| |
| void OnGattServiceDiscoveryDoneEvent(const RawAddress& address) { |
| auto device = FindDeviceByAddress(address); |
| if (!device) { |
| LOG(WARNING) << "Skipping unknown device" << address; |
| return; |
| } |
| |
| DLOG(INFO) << __func__ << ": address=" << address; |
| |
| if (!device->is_gatt_service_valid) |
| BTA_GATTC_ServiceSearchRequest(device->conn_id, &kCsisServiceUuid); |
| } |
| |
| static uint16_t FindCccHandle(uint16_t conn_id, uint16_t char_handle) { |
| const gatt::Characteristic* p_char = |
| BTA_GATTC_GetCharacteristic(conn_id, char_handle); |
| if (!p_char) { |
| LOG(WARNING) << __func__ << ": No such characteristic: " << char_handle; |
| return GAP_INVALID_HANDLE; |
| } |
| |
| for (const gatt::Descriptor& desc : p_char->descriptors) { |
| if (desc.uuid == Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG)) |
| return desc.handle; |
| } |
| |
| return GAP_INVALID_HANDLE; |
| } |
| |
| void SubscribeForNotifications(uint16_t conn_id, const RawAddress& address, |
| uint16_t value_handle, uint16_t ccc_handle) { |
| if (value_handle != GAP_INVALID_HANDLE) { |
| tGATT_STATUS register_status = |
| BTA_GATTC_RegisterForNotifications(gatt_if_, address, value_handle); |
| DLOG(INFO) << __func__ << ": BTA_GATTC_RegisterForNotifications, status=" |
| << loghex(+register_status) |
| << " value=" << loghex(value_handle) |
| << " ccc=" << loghex(ccc_handle); |
| |
| if (register_status != GATT_SUCCESS) return; |
| } |
| |
| std::vector<uint8_t> value(2); |
| uint8_t* value_ptr = value.data(); |
| UINT16_TO_STREAM(value_ptr, GATT_CHAR_CLIENT_CONFIG_NOTIFICATION); |
| BtaGattQueue::WriteDescriptor( |
| conn_id, ccc_handle, std::move(value), GATT_WRITE, |
| [](uint16_t conn_id, tGATT_STATUS status, uint16_t value_handle, |
| uint16_t len, const uint8_t* value, void* user_data) { |
| if (instance) |
| instance->OnGattWriteCcc(conn_id, status, value_handle, user_data); |
| }, |
| nullptr); |
| } |
| |
| void DisableGattNotification(uint16_t conn_id, const RawAddress& address, |
| uint16_t value_handle) { |
| if (value_handle != GAP_INVALID_HANDLE) { |
| tGATT_STATUS register_status = |
| BTA_GATTC_DeregisterForNotifications(gatt_if_, address, value_handle); |
| DLOG(INFO) << __func__ << ": DisableGattNotification, status=" |
| << loghex(+register_status) |
| << " value=" << loghex(value_handle); |
| |
| if (register_status != GATT_SUCCESS) return; |
| } |
| } |
| |
| uint8_t gatt_if_; |
| bluetooth::csis::CsisClientCallbacks* callbacks_; |
| std::list<std::shared_ptr<CsisDevice>> devices_; |
| std::list<std::shared_ptr<CsisGroup>> csis_groups_; |
| DeviceGroups* dev_groups_; |
| int discovering_group_ = -1; |
| }; |
| |
| class DeviceGroupsCallbacksImpl : public DeviceGroupsCallbacks { |
| public: |
| void OnGroupAdded(const RawAddress& address, const bluetooth::Uuid& uuid, |
| int group_id) override { |
| if (instance) instance->OnGroupAddedCb(address, uuid, group_id); |
| } |
| |
| void OnGroupMemberAdded(const RawAddress& address, int group_id) override { |
| if (instance) instance->OnGroupMemberAddedCb(address, group_id); |
| } |
| |
| void OnGroupRemoved(const bluetooth::Uuid& uuid, int group_id) override { |
| if (instance) instance->OnGroupRemovedCb(uuid, group_id); |
| } |
| |
| void OnGroupMemberRemoved(const RawAddress& address, int group_id) override { |
| if (instance) instance->OnGroupMemberRemovedCb(address, group_id); |
| } |
| |
| void OnGroupAddFromStorage(const RawAddress& address, |
| const bluetooth::Uuid& uuid, |
| int group_id) override { |
| if (instance) instance->OnGroupAddFromStorageCb(address, uuid, group_id); |
| } |
| }; |
| |
| class DeviceGroupsCallbacksImpl; |
| DeviceGroupsCallbacksImpl deviceGroupsCallbacksImpl; |
| |
| } // namespace |
| |
| void CsisClient::Initialize(bluetooth::csis::CsisClientCallbacks* callbacks, |
| Closure initCb) { |
| if (instance) { |
| LOG(ERROR) << __func__ << ": Already initialized!"; |
| return; |
| } |
| |
| device_group_callbacks = &deviceGroupsCallbacksImpl; |
| instance = new CsisClientImpl(callbacks, initCb); |
| } |
| |
| bool CsisClient::IsCsisClientRunning() { return instance; } |
| |
| CsisClient* CsisClient::Get(void) { |
| CHECK(instance); |
| return instance; |
| } |
| |
| void CsisClient::AddFromStorage(const RawAddress& addr, |
| const std::vector<uint8_t>& in, |
| bool autoconnect) { |
| if (!instance) { |
| LOG(ERROR) << __func__ << ": Not initialized yet!"; |
| return; |
| } |
| |
| instance->AddFromStorage(addr, in, autoconnect); |
| } |
| |
| bool CsisClient::GetForStorage(const RawAddress& addr, |
| std::vector<uint8_t>& out) { |
| if (!instance) { |
| LOG(ERROR) << __func__ << ": Not initialized yet"; |
| return false; |
| } |
| |
| return instance->SerializeSets(addr, out); |
| } |
| |
| void CsisClient::CleanUp() { |
| CsisClientImpl* ptr = instance; |
| instance = nullptr; |
| |
| if (ptr) { |
| ptr->CleanUp(); |
| delete ptr; |
| } |
| } |
| |
| void CsisClient::DebugDump(int fd) { |
| dprintf(fd, "Coordinated Set Service Client:\n"); |
| if (instance) instance->Dump(fd); |
| dprintf(fd, "\n"); |
| } |