Loading system/bta/has/has_client.cc +81 −24 Original line number Diff line number Diff line Loading @@ -84,6 +84,7 @@ void btif_storage_set_leaudio_has_features(const RawAddress& address, uint8_t features); void btif_storage_set_leaudio_has_active_preset(const RawAddress& address, uint8_t active_preset); void btif_storage_remove_leaudio_has(const RawAddress& address); extern bool gatt_profile_get_eatt_support(const RawAddress& remote_bda); Loading Loading @@ -309,6 +310,11 @@ class HasClientImpl : public HasClient { auto op = op_opt.value(); callbacks_->OnActivePresetSelectError(op.addr_or_group, GattStatus2SvcErrorCode(status)); if (status == GATT_DATABASE_OUT_OF_SYNC) { LOG_INFO("Database out of sync for %s", device->addr.ToString().c_str()); ClearDeviceInformationAndStartSearch(device); } } void OnHasPresetNameSetStatus(uint16_t conn_id, tGATT_STATUS status, Loading Loading @@ -339,6 +345,10 @@ class HasClientImpl : public HasClient { auto op = op_opt.value(); callbacks_->OnSetPresetNameError(device->addr, op.index, GattStatus2SvcErrorCode(status)); if (status == GATT_DATABASE_OUT_OF_SYNC) { LOG_INFO("Database out of sync for %s", device->addr.ToString().c_str()); ClearDeviceInformationAndStartSearch(device); } } void OnHasPresetNameGetStatus(uint16_t conn_id, tGATT_STATUS status, Loading Loading @@ -367,9 +377,14 @@ class HasClientImpl : public HasClient { callbacks_->OnPresetInfoError(device->addr, op.index, GattStatus2SvcErrorCode(status)); if (status == GATT_DATABASE_OUT_OF_SYNC) { LOG_INFO("Database out of sync for %s", device->addr.ToString().c_str()); ClearDeviceInformationAndStartSearch(device); } else { LOG_ERROR("Devices %s: Control point not usable. Disconnecting!", device->addr.ToString().c_str()); BTA_GATTC_Close(conn_id); BTA_GATTC_Close(device->conn_id); } } void OnHasPresetIndexOperation(uint16_t conn_id, tGATT_STATUS status, Loading Loading @@ -409,9 +424,15 @@ class HasClientImpl : public HasClient { callbacks_->OnActivePresetSelectError(op.addr_or_group, GattStatus2SvcErrorCode(status)); } if (status == GATT_DATABASE_OUT_OF_SYNC) { LOG_INFO("Database out of sync for %s", device->addr.ToString().c_str()); ClearDeviceInformationAndStartSearch(device); } else { LOG_ERROR("Devices %s: Control point not usable. Disconnecting!", device->addr.ToString().c_str()); BTA_GATTC_Close(conn_id); BTA_GATTC_Close(device->conn_id); } } void CpReadAllPresetsOperation(HasCtpOp operation) { Loading Loading @@ -988,6 +1009,12 @@ class HasClientImpl : public HasClient { return; } if (status == GATT_DATABASE_OUT_OF_SYNC) { LOG_INFO("Database out of sync for %s", device->addr.ToString().c_str()); ClearDeviceInformationAndStartSearch(device); return; } HasGattOpContext context(user_data); bool enabling_ntf = context.context_flags & HasGattOpContext::kContextFlagsEnableNotification; Loading Loading @@ -1058,9 +1085,14 @@ class HasClientImpl : public HasClient { } if (status != GATT_SUCCESS) { LOG(ERROR) << __func__ << ": Could not read characteristic at handle=" << loghex(handle); if (status == GATT_DATABASE_OUT_OF_SYNC) { LOG_INFO("Database out of sync for %s", device->addr.ToString().c_str()); ClearDeviceInformationAndStartSearch(device); } else { LOG_ERROR("Could not read characteristic at handle=0x%04x", handle); BTA_GATTC_Close(device->conn_id); } return; } Loading Loading @@ -1434,10 +1466,14 @@ class HasClientImpl : public HasClient { } if (status != GATT_SUCCESS) { LOG(ERROR) << __func__ << ": Could not read characteristic at handle=" << loghex(handle); if (status == GATT_DATABASE_OUT_OF_SYNC) { LOG_INFO("Database out of sync for %s", device->addr.ToString().c_str()); ClearDeviceInformationAndStartSearch(device); } else { LOG_ERROR("Could not read characteristic at handle=0x%04x", handle); BTA_GATTC_Close(device->conn_id); return; } } if (len != 1) { Loading Loading @@ -1507,11 +1543,7 @@ class HasClientImpl : public HasClient { } } /* Cleans up after the device disconnection */ void DoDisconnectCleanUp(HasDevice& device, bool invalidate_gatt_service = true) { DLOG(INFO) << __func__ << ": device=" << device.addr; void DeregisterNotifications(HasDevice& device) { /* Deregister from optional features notifications */ if (device.features_ccc_handle != GAP_INVALID_HANDLE) { BTA_GATTC_DeregisterForNotifications(gatt_if_, device.addr, Loading @@ -1529,6 +1561,14 @@ class HasClientImpl : public HasClient { BTA_GATTC_DeregisterForNotifications(gatt_if_, device.addr, device.cp_handle); } } /* Cleans up after the device disconnection */ void DoDisconnectCleanUp(HasDevice& device, bool invalidate_gatt_service = true) { LOG_DEBUG(": device=%s", device.addr.ToString().c_str()); DeregisterNotifications(device); if (device.conn_id != GATT_INVALID_CONN_ID) { BtaGattQueue::Clean(device.conn_id); Loading Loading @@ -1940,19 +1980,36 @@ class HasClientImpl : public HasClient { } } void OnGattServiceChangeEvent(const RawAddress& address) { auto device = std::find_if(devices_.begin(), devices_.end(), HasDevice::MatchAddress(address)); if (device == devices_.end()) { LOG(WARNING) << "Skipping unknown device" << address; void ClearDeviceInformationAndStartSearch(HasDevice* device) { if (!device) { LOG_ERROR("Device is null"); return; } DLOG(INFO) << __func__ << ": address=" << address; LOG_INFO("%s", device->addr.ToString().c_str()); if (!device->isGattServiceValid()) { LOG_INFO("Service already invalidated"); return; } /* Invalidate service discovery results */ DeregisterNotifications(*device); BtaGattQueue::Clean(device->conn_id); device->ClearSvcData(); btif_storage_remove_leaudio_has(device->addr); BTA_GATTC_ServiceSearchRequest(device->conn_id, &kUuidHearingAccessService); } void OnGattServiceChangeEvent(const RawAddress& address) { auto device = std::find_if(devices_.begin(), devices_.end(), HasDevice::MatchAddress(address)); if (device == devices_.end()) { LOG(WARNING) << "Skipping unknown device" << address; return; } LOG_INFO("%s", address.ToString().c_str()); ClearDeviceInformationAndStartSearch(&(*device)); } void OnGattServiceDiscoveryDoneEvent(const RawAddress& address) { Loading system/bta/has/has_client_test.cc +43 −0 Original line number Diff line number Diff line Loading @@ -2926,6 +2926,49 @@ TEST_F(HasClientTest, test_dumpsys) { ASSERT_TRUE(SimpleJsonValidator(sv[1], &dumpsys_byte_cnt)); } TEST_F(HasClientTest, test_connect_database_out_of_sync) { osi_property_set_bool("persist.bluetooth.has.always_use_preset_cache", false); const RawAddress test_address = GetTestAddress(1); std::set<HasPreset, HasPreset::ComparatorDesc> has_presets = {{ HasPreset(1, HasPreset::kPropertyAvailable, "Universal"), HasPreset(2, HasPreset::kPropertyAvailable | HasPreset::kPropertyWritable, "Preset2"), }}; SetSampleDatabaseHasPresetsNtf( test_address, bluetooth::has::kFeatureBitHearingAidTypeBanded | bluetooth::has::kFeatureBitWritablePresets | bluetooth::has::kFeatureBitDynamicPresets, has_presets); EXPECT_CALL(*callbacks, OnDeviceAvailable( test_address, bluetooth::has::kFeatureBitHearingAidTypeBanded | bluetooth::has::kFeatureBitWritablePresets | bluetooth::has::kFeatureBitDynamicPresets)); EXPECT_CALL(*callbacks, OnConnectionState(ConnectionState::CONNECTED, test_address)); TestConnect(test_address); ON_CALL(gatt_queue, WriteCharacteristic(_, _, _, _, _, _)) .WillByDefault( Invoke([this](uint16_t conn_id, uint16_t handle, std::vector<uint8_t> value, tGATT_WRITE_TYPE write_type, GATT_WRITE_OP_CB cb, void* cb_data) { auto* svc = gatt::FindService(services_map[conn_id], handle); if (svc == nullptr) return; tGATT_STATUS status = GATT_DATABASE_OUT_OF_SYNC; if (cb) cb(conn_id, status, handle, value.size(), value.data(), cb_data); })); ON_CALL(gatt_interface, ServiceSearchRequest(_, _)).WillByDefault(Return()); EXPECT_CALL(gatt_interface, ServiceSearchRequest(_, _)); HasClient::Get()->GetPresetInfo(test_address, 1); } class HasTypesTest : public ::testing::Test { protected: void SetUp(void) override { mock_function_count_map.clear(); } Loading system/bta/test/common/btif_storage_mock.cc +5 −0 Original line number Diff line number Diff line Loading @@ -111,3 +111,8 @@ void btif_storage_set_leaudio_has_active_preset(const RawAddress& address, LOG_ASSERT(btif_storage_interface) << "Mock storage module not set!"; btif_storage_interface->SetLeaudioHasActivePreset(address, active_preset); } void btif_storage_remove_leaudio_has(const RawAddress& address) { LOG_ASSERT(btif_storage_interface) << "Mock storage module not set!"; btif_storage_interface->RemoveLeaudioHas(address); } No newline at end of file system/bta/test/common/btif_storage_mock.h +3 −0 Original line number Diff line number Diff line Loading @@ -50,6 +50,7 @@ class BtifStorageInterface { virtual bool GetLeaudioHasPresets(const RawAddress& address, std::vector<uint8_t>& presets_bin, uint8_t& active_preset) = 0; virtual void RemoveLeaudioHas(const RawAddress& address) = 0; virtual ~BtifStorageInterface() = default; }; Loading Loading @@ -88,6 +89,8 @@ class MockBtifStorageInterface : public BtifStorageInterface { (const RawAddress& address, uint8_t features), (override)); MOCK_METHOD((void), SetLeaudioHasActivePreset, (const RawAddress& address, uint8_t active_preset), (override)); MOCK_METHOD((void), RemoveLeaudioHas, (const RawAddress& address), (override)); }; /** Loading Loading
system/bta/has/has_client.cc +81 −24 Original line number Diff line number Diff line Loading @@ -84,6 +84,7 @@ void btif_storage_set_leaudio_has_features(const RawAddress& address, uint8_t features); void btif_storage_set_leaudio_has_active_preset(const RawAddress& address, uint8_t active_preset); void btif_storage_remove_leaudio_has(const RawAddress& address); extern bool gatt_profile_get_eatt_support(const RawAddress& remote_bda); Loading Loading @@ -309,6 +310,11 @@ class HasClientImpl : public HasClient { auto op = op_opt.value(); callbacks_->OnActivePresetSelectError(op.addr_or_group, GattStatus2SvcErrorCode(status)); if (status == GATT_DATABASE_OUT_OF_SYNC) { LOG_INFO("Database out of sync for %s", device->addr.ToString().c_str()); ClearDeviceInformationAndStartSearch(device); } } void OnHasPresetNameSetStatus(uint16_t conn_id, tGATT_STATUS status, Loading Loading @@ -339,6 +345,10 @@ class HasClientImpl : public HasClient { auto op = op_opt.value(); callbacks_->OnSetPresetNameError(device->addr, op.index, GattStatus2SvcErrorCode(status)); if (status == GATT_DATABASE_OUT_OF_SYNC) { LOG_INFO("Database out of sync for %s", device->addr.ToString().c_str()); ClearDeviceInformationAndStartSearch(device); } } void OnHasPresetNameGetStatus(uint16_t conn_id, tGATT_STATUS status, Loading Loading @@ -367,9 +377,14 @@ class HasClientImpl : public HasClient { callbacks_->OnPresetInfoError(device->addr, op.index, GattStatus2SvcErrorCode(status)); if (status == GATT_DATABASE_OUT_OF_SYNC) { LOG_INFO("Database out of sync for %s", device->addr.ToString().c_str()); ClearDeviceInformationAndStartSearch(device); } else { LOG_ERROR("Devices %s: Control point not usable. Disconnecting!", device->addr.ToString().c_str()); BTA_GATTC_Close(conn_id); BTA_GATTC_Close(device->conn_id); } } void OnHasPresetIndexOperation(uint16_t conn_id, tGATT_STATUS status, Loading Loading @@ -409,9 +424,15 @@ class HasClientImpl : public HasClient { callbacks_->OnActivePresetSelectError(op.addr_or_group, GattStatus2SvcErrorCode(status)); } if (status == GATT_DATABASE_OUT_OF_SYNC) { LOG_INFO("Database out of sync for %s", device->addr.ToString().c_str()); ClearDeviceInformationAndStartSearch(device); } else { LOG_ERROR("Devices %s: Control point not usable. Disconnecting!", device->addr.ToString().c_str()); BTA_GATTC_Close(conn_id); BTA_GATTC_Close(device->conn_id); } } void CpReadAllPresetsOperation(HasCtpOp operation) { Loading Loading @@ -988,6 +1009,12 @@ class HasClientImpl : public HasClient { return; } if (status == GATT_DATABASE_OUT_OF_SYNC) { LOG_INFO("Database out of sync for %s", device->addr.ToString().c_str()); ClearDeviceInformationAndStartSearch(device); return; } HasGattOpContext context(user_data); bool enabling_ntf = context.context_flags & HasGattOpContext::kContextFlagsEnableNotification; Loading Loading @@ -1058,9 +1085,14 @@ class HasClientImpl : public HasClient { } if (status != GATT_SUCCESS) { LOG(ERROR) << __func__ << ": Could not read characteristic at handle=" << loghex(handle); if (status == GATT_DATABASE_OUT_OF_SYNC) { LOG_INFO("Database out of sync for %s", device->addr.ToString().c_str()); ClearDeviceInformationAndStartSearch(device); } else { LOG_ERROR("Could not read characteristic at handle=0x%04x", handle); BTA_GATTC_Close(device->conn_id); } return; } Loading Loading @@ -1434,10 +1466,14 @@ class HasClientImpl : public HasClient { } if (status != GATT_SUCCESS) { LOG(ERROR) << __func__ << ": Could not read characteristic at handle=" << loghex(handle); if (status == GATT_DATABASE_OUT_OF_SYNC) { LOG_INFO("Database out of sync for %s", device->addr.ToString().c_str()); ClearDeviceInformationAndStartSearch(device); } else { LOG_ERROR("Could not read characteristic at handle=0x%04x", handle); BTA_GATTC_Close(device->conn_id); return; } } if (len != 1) { Loading Loading @@ -1507,11 +1543,7 @@ class HasClientImpl : public HasClient { } } /* Cleans up after the device disconnection */ void DoDisconnectCleanUp(HasDevice& device, bool invalidate_gatt_service = true) { DLOG(INFO) << __func__ << ": device=" << device.addr; void DeregisterNotifications(HasDevice& device) { /* Deregister from optional features notifications */ if (device.features_ccc_handle != GAP_INVALID_HANDLE) { BTA_GATTC_DeregisterForNotifications(gatt_if_, device.addr, Loading @@ -1529,6 +1561,14 @@ class HasClientImpl : public HasClient { BTA_GATTC_DeregisterForNotifications(gatt_if_, device.addr, device.cp_handle); } } /* Cleans up after the device disconnection */ void DoDisconnectCleanUp(HasDevice& device, bool invalidate_gatt_service = true) { LOG_DEBUG(": device=%s", device.addr.ToString().c_str()); DeregisterNotifications(device); if (device.conn_id != GATT_INVALID_CONN_ID) { BtaGattQueue::Clean(device.conn_id); Loading Loading @@ -1940,19 +1980,36 @@ class HasClientImpl : public HasClient { } } void OnGattServiceChangeEvent(const RawAddress& address) { auto device = std::find_if(devices_.begin(), devices_.end(), HasDevice::MatchAddress(address)); if (device == devices_.end()) { LOG(WARNING) << "Skipping unknown device" << address; void ClearDeviceInformationAndStartSearch(HasDevice* device) { if (!device) { LOG_ERROR("Device is null"); return; } DLOG(INFO) << __func__ << ": address=" << address; LOG_INFO("%s", device->addr.ToString().c_str()); if (!device->isGattServiceValid()) { LOG_INFO("Service already invalidated"); return; } /* Invalidate service discovery results */ DeregisterNotifications(*device); BtaGattQueue::Clean(device->conn_id); device->ClearSvcData(); btif_storage_remove_leaudio_has(device->addr); BTA_GATTC_ServiceSearchRequest(device->conn_id, &kUuidHearingAccessService); } void OnGattServiceChangeEvent(const RawAddress& address) { auto device = std::find_if(devices_.begin(), devices_.end(), HasDevice::MatchAddress(address)); if (device == devices_.end()) { LOG(WARNING) << "Skipping unknown device" << address; return; } LOG_INFO("%s", address.ToString().c_str()); ClearDeviceInformationAndStartSearch(&(*device)); } void OnGattServiceDiscoveryDoneEvent(const RawAddress& address) { Loading
system/bta/has/has_client_test.cc +43 −0 Original line number Diff line number Diff line Loading @@ -2926,6 +2926,49 @@ TEST_F(HasClientTest, test_dumpsys) { ASSERT_TRUE(SimpleJsonValidator(sv[1], &dumpsys_byte_cnt)); } TEST_F(HasClientTest, test_connect_database_out_of_sync) { osi_property_set_bool("persist.bluetooth.has.always_use_preset_cache", false); const RawAddress test_address = GetTestAddress(1); std::set<HasPreset, HasPreset::ComparatorDesc> has_presets = {{ HasPreset(1, HasPreset::kPropertyAvailable, "Universal"), HasPreset(2, HasPreset::kPropertyAvailable | HasPreset::kPropertyWritable, "Preset2"), }}; SetSampleDatabaseHasPresetsNtf( test_address, bluetooth::has::kFeatureBitHearingAidTypeBanded | bluetooth::has::kFeatureBitWritablePresets | bluetooth::has::kFeatureBitDynamicPresets, has_presets); EXPECT_CALL(*callbacks, OnDeviceAvailable( test_address, bluetooth::has::kFeatureBitHearingAidTypeBanded | bluetooth::has::kFeatureBitWritablePresets | bluetooth::has::kFeatureBitDynamicPresets)); EXPECT_CALL(*callbacks, OnConnectionState(ConnectionState::CONNECTED, test_address)); TestConnect(test_address); ON_CALL(gatt_queue, WriteCharacteristic(_, _, _, _, _, _)) .WillByDefault( Invoke([this](uint16_t conn_id, uint16_t handle, std::vector<uint8_t> value, tGATT_WRITE_TYPE write_type, GATT_WRITE_OP_CB cb, void* cb_data) { auto* svc = gatt::FindService(services_map[conn_id], handle); if (svc == nullptr) return; tGATT_STATUS status = GATT_DATABASE_OUT_OF_SYNC; if (cb) cb(conn_id, status, handle, value.size(), value.data(), cb_data); })); ON_CALL(gatt_interface, ServiceSearchRequest(_, _)).WillByDefault(Return()); EXPECT_CALL(gatt_interface, ServiceSearchRequest(_, _)); HasClient::Get()->GetPresetInfo(test_address, 1); } class HasTypesTest : public ::testing::Test { protected: void SetUp(void) override { mock_function_count_map.clear(); } Loading
system/bta/test/common/btif_storage_mock.cc +5 −0 Original line number Diff line number Diff line Loading @@ -111,3 +111,8 @@ void btif_storage_set_leaudio_has_active_preset(const RawAddress& address, LOG_ASSERT(btif_storage_interface) << "Mock storage module not set!"; btif_storage_interface->SetLeaudioHasActivePreset(address, active_preset); } void btif_storage_remove_leaudio_has(const RawAddress& address) { LOG_ASSERT(btif_storage_interface) << "Mock storage module not set!"; btif_storage_interface->RemoveLeaudioHas(address); } No newline at end of file
system/bta/test/common/btif_storage_mock.h +3 −0 Original line number Diff line number Diff line Loading @@ -50,6 +50,7 @@ class BtifStorageInterface { virtual bool GetLeaudioHasPresets(const RawAddress& address, std::vector<uint8_t>& presets_bin, uint8_t& active_preset) = 0; virtual void RemoveLeaudioHas(const RawAddress& address) = 0; virtual ~BtifStorageInterface() = default; }; Loading Loading @@ -88,6 +89,8 @@ class MockBtifStorageInterface : public BtifStorageInterface { (const RawAddress& address, uint8_t features), (override)); MOCK_METHOD((void), SetLeaudioHasActivePreset, (const RawAddress& address, uint8_t active_preset), (override)); MOCK_METHOD((void), RemoveLeaudioHas, (const RawAddress& address), (override)); }; /** Loading