/****************************************************************************** * * Copyright 2022 The Android Open Source Project * * 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. * ******************************************************************************/ #define LOG_TAG "bt_btif_profile_storage" #include "btif_profile_storage.h" #include #include #include #include #include #include #include #include "bta_csis_api.h" #include "bta_groups.h" #include "bta_has_api.h" #include "bta_hd_api.h" #include "bta_hearing_aid_api.h" #include "bta_hh_api.h" #include "bta_le_audio_api.h" #include "bta_vc_api.h" #include "btif/include/btif_dm.h" #include "btif/include/btif_jni_task.h" #include "btif_config.h" #include "btif_hh.h" #include "btif_storage.h" #include "stack/include/bt_uuid16.h" #include "stack/include/main_thread.h" #include "storage/config_keys.h" #include "types/bluetooth/uuid.h" #include "types/raw_address.h" using base::Bind; using bluetooth::Uuid; using bluetooth::csis::CsisClient; using bluetooth::groups::DeviceGroups; using namespace bluetooth; /******************************************************************************* * Constants & Macros ******************************************************************************/ #define STORAGE_HID_ATRR_MASK_SIZE (4) #define STORAGE_HID_SUB_CLASS_SIZE (2) #define STORAGE_HID_APP_ID_SIZE (2) #define STORAGE_HID_VENDOR_ID_SIZE (4) #define STORAGE_HID_PRODUCT_ID_SIZE (4) #define STORAGE_HID_VERSION_SIZE (4) #define STORAGE_HID_CTRY_CODE_SIZE (2) #define STORAGE_HID_DESC_LEN_SIZE (4) #define STORAGE_HID_DESC_MAX_SIZE (2 * 512) /* <18 char bd addr> LIST > > > */ #define BTIF_HID_INFO_ENTRY_SIZE_MAX \ (STORAGE_BDADDR_STRING_SZ + 1 + STORAGE_HID_ATRR_MASK_SIZE + 1 + \ STORAGE_HID_SUB_CLASS_SIZE + 1 + STORAGE_HID_APP_ID_SIZE + 1 + \ STORAGE_HID_VENDOR_ID_SIZE + 1 + STORAGE_HID_PRODUCT_ID_SIZE + 1 + \ STORAGE_HID_VERSION_SIZE + 1 + STORAGE_HID_CTRY_CODE_SIZE + 1 + \ STORAGE_HID_DESC_LEN_SIZE + 1 + STORAGE_HID_DESC_MAX_SIZE + 1) #define STORAGE_HID_DB_VERSION (1) static void btif_storage_hid_device_info( std::string bdstr, uint16_t attr_mask, uint8_t sub_class, uint8_t app_id, uint16_t vendor_id, uint16_t product_id, uint16_t version, uint8_t ctry_code, uint16_t ssr_max_latency, uint16_t ssr_min_tout, uint16_t dl_len, uint8_t* dsc_list) { btif_config_set_int(bdstr, BTIF_STORAGE_KEY_HID_ATTR_MASK, attr_mask); btif_config_set_int(bdstr, BTIF_STORAGE_KEY_HID_SUB_CLASS, sub_class); btif_config_set_int(bdstr, BTIF_STORAGE_KEY_HID_APP_ID, app_id); btif_config_set_int(bdstr, BTIF_STORAGE_KEY_HID_VENDOR_ID, vendor_id); btif_config_set_int(bdstr, BTIF_STORAGE_KEY_HID_PRODUCT_ID, product_id); btif_config_set_int(bdstr, BTIF_STORAGE_KEY_HID_VERSION, version); btif_config_set_int(bdstr, BTIF_STORAGE_KEY_HID_COUNTRY_CODE, ctry_code); btif_config_set_int(bdstr, BTIF_STORAGE_KEY_HID_SSR_MAX_LATENCY, ssr_max_latency); btif_config_set_int(bdstr, BTIF_STORAGE_KEY_HID_SSR_MIN_TIMEOUT, ssr_min_tout); if (dl_len > 0) btif_config_set_bin(bdstr, BTIF_STORAGE_KEY_HID_DESCRIPTOR, dsc_list, dl_len); } static void btif_storage_hogp_device_info(std::string bdstr, uint16_t attr_mask, uint8_t sub_class, uint8_t app_id, uint16_t vendor_id, uint16_t product_id, uint16_t version, uint8_t ctry_code, uint16_t dl_len, uint8_t* dsc_list) { btif_config_set_int(bdstr, BTIF_STORAGE_KEY_HOGP_ATTR_MASK, attr_mask); btif_config_set_int(bdstr, BTIF_STORAGE_KEY_HOGP_SUB_CLASS, sub_class); btif_config_set_int(bdstr, BTIF_STORAGE_KEY_HOGP_APP_ID, app_id); btif_config_set_int(bdstr, BTIF_STORAGE_KEY_HOGP_VENDOR_ID, vendor_id); btif_config_set_int(bdstr, BTIF_STORAGE_KEY_HOGP_PRODUCT_ID, product_id); btif_config_set_int(bdstr, BTIF_STORAGE_KEY_HOGP_VERSION, version); btif_config_set_int(bdstr, BTIF_STORAGE_KEY_HOGP_COUNTRY_CODE, ctry_code); if (dl_len > 0) btif_config_set_bin(bdstr, BTIF_STORAGE_KEY_HOGP_DESCRIPTOR, dsc_list, dl_len); } /******************************************************************************* * * Function btif_storage_add_hid_device_info * * Description BTIF storage API - Adds the hid information of bonded hid * devices-to NVRAM * * Returns BT_STATUS_SUCCESS if the store was successful, * BT_STATUS_FAIL otherwise * ******************************************************************************/ bt_status_t btif_storage_add_hid_device_info( const tAclLinkSpec& link_spec, uint16_t attr_mask, uint8_t sub_class, uint8_t app_id, uint16_t vendor_id, uint16_t product_id, uint16_t version, uint8_t ctry_code, uint16_t ssr_max_latency, uint16_t ssr_min_tout, uint16_t dl_len, uint8_t* dsc_list) { log::verbose("link spec: {}", link_spec.ToRedactedStringForLogging()); std::string bdstr = link_spec.addrt.bda.ToString(); if (!com::android::bluetooth::flags::allow_switching_hid_and_hogp()) { btif_storage_hid_device_info( bdstr, attr_mask, sub_class, app_id, vendor_id, product_id, version, ctry_code, ssr_max_latency, ssr_min_tout, dl_len, dsc_list); return BT_STATUS_SUCCESS; } if (link_spec.transport == BT_TRANSPORT_AUTO) { log::error("Unexpected transport!"); return BT_STATUS_UNHANDLED; } btif_config_set_int(bdstr, BTIF_STORAGE_KEY_HID_DB_VERSION, STORAGE_HID_DB_VERSION); if (link_spec.transport == BT_TRANSPORT_BR_EDR) { btif_storage_hid_device_info( bdstr, attr_mask, sub_class, app_id, vendor_id, product_id, version, ctry_code, ssr_max_latency, ssr_min_tout, dl_len, dsc_list); } else { btif_storage_hogp_device_info(bdstr, attr_mask, sub_class, app_id, vendor_id, product_id, version, ctry_code, dl_len, dsc_list); } return BT_STATUS_SUCCESS; } static void btif_storage_load_bonded_hid_device(const tAclLinkSpec link_spec) { auto name = link_spec.addrt.bda.ToString(); int value; bool reconnect_allowed = true; if (!btif_config_get_int(name, BTIF_STORAGE_KEY_HID_ATTR_MASK, &value)) return; uint16_t attr_mask = (uint16_t)value; if (btif_in_fetch_bonded_device(name) != BT_STATUS_SUCCESS) { btif_storage_remove_hid_info(link_spec); return; } tBTA_HH_DEV_DSCP_INFO dscp_info = {}; btif_config_get_int(name, BTIF_STORAGE_KEY_HID_SUB_CLASS, &value); uint8_t sub_class = (uint8_t)value; btif_config_get_int(name, BTIF_STORAGE_KEY_HID_APP_ID, &value); uint8_t app_id = (uint8_t)value; btif_config_get_int(name, BTIF_STORAGE_KEY_HID_VENDOR_ID, &value); dscp_info.vendor_id = (uint16_t)value; btif_config_get_int(name, BTIF_STORAGE_KEY_HID_PRODUCT_ID, &value); dscp_info.product_id = (uint16_t)value; btif_config_get_int(name, BTIF_STORAGE_KEY_HID_VERSION, &value); dscp_info.version = (uint16_t)value; btif_config_get_int(name, BTIF_STORAGE_KEY_HID_COUNTRY_CODE, &value); dscp_info.ctry_code = (uint8_t)value; value = 0; btif_config_get_int(name, BTIF_STORAGE_KEY_HID_SSR_MAX_LATENCY, &value); dscp_info.ssr_max_latency = (uint16_t)value; value = 0; btif_config_get_int(name, BTIF_STORAGE_KEY_HID_SSR_MIN_TIMEOUT, &value); dscp_info.ssr_min_tout = (uint16_t)value; size_t len = btif_config_get_bin_length(name, BTIF_STORAGE_KEY_HID_DESCRIPTOR); if (len > 0) { dscp_info.descriptor.dl_len = (uint16_t)len; dscp_info.descriptor.dsc_list = (uint8_t*)alloca(len); btif_config_get_bin(name, BTIF_STORAGE_KEY_HID_DESCRIPTOR, (uint8_t*)dscp_info.descriptor.dsc_list, &len); } btif_storage_get_hid_connection_policy(link_spec, &reconnect_allowed); // add extracted information to BTA HH btif_hh_load_bonded_dev(link_spec, attr_mask, sub_class, app_id, dscp_info, reconnect_allowed); } static void btif_storage_load_bonded_hogp_device(const tAclLinkSpec link_spec) { auto name = link_spec.addrt.bda.ToString(); int value; bool reconnect_allowed = true; if (!btif_config_get_int(name, BTIF_STORAGE_KEY_HOGP_ATTR_MASK, &value)) return; uint16_t attr_mask = (uint16_t)value; if (btif_in_fetch_bonded_device(name) != BT_STATUS_SUCCESS) { btif_storage_remove_hid_info(link_spec); return; } tBTA_HH_DEV_DSCP_INFO dscp_info = {}; btif_config_get_int(name, BTIF_STORAGE_KEY_HOGP_SUB_CLASS, &value); uint8_t sub_class = (uint8_t)value; btif_config_get_int(name, BTIF_STORAGE_KEY_HOGP_APP_ID, &value); uint8_t app_id = (uint8_t)value; btif_config_get_int(name, BTIF_STORAGE_KEY_HOGP_VENDOR_ID, &value); dscp_info.vendor_id = (uint16_t)value; btif_config_get_int(name, BTIF_STORAGE_KEY_HOGP_PRODUCT_ID, &value); dscp_info.product_id = (uint16_t)value; btif_config_get_int(name, BTIF_STORAGE_KEY_HOGP_VERSION, &value); dscp_info.version = (uint16_t)value; btif_config_get_int(name, BTIF_STORAGE_KEY_HOGP_COUNTRY_CODE, &value); dscp_info.ctry_code = (uint8_t)value; size_t len = btif_config_get_bin_length(name, BTIF_STORAGE_KEY_HOGP_DESCRIPTOR); if (len > 0) { dscp_info.descriptor.dl_len = (uint16_t)len; dscp_info.descriptor.dsc_list = (uint8_t*)alloca(len); btif_config_get_bin(name, BTIF_STORAGE_KEY_HOGP_DESCRIPTOR, (uint8_t*)dscp_info.descriptor.dsc_list, &len); } btif_storage_get_hid_connection_policy(link_spec, &reconnect_allowed); // add extracted information to BTA HH btif_hh_load_bonded_dev(link_spec, attr_mask, sub_class, app_id, dscp_info, reconnect_allowed); } /******************************************************************************* * * Function btif_storage_load_bonded_hid_info * * Description BTIF storage API - Loads hid info for all the bonded devices * from NVRAM and adds those devices to the BTA_HH. * * Returns BT_STATUS_SUCCESS if successful, BT_STATUS_FAIL otherwise * ******************************************************************************/ bt_status_t btif_storage_load_bonded_hid_info(void) { for (const auto& bd_addr : btif_config_get_paired_devices()) { auto name = bd_addr.ToString(); tAclLinkSpec link_spec = {}; link_spec.addrt.bda = bd_addr; link_spec.addrt.type = BLE_ADDR_PUBLIC; link_spec.transport = BT_TRANSPORT_AUTO; int db_version = 0; if (com::android::bluetooth::flags::allow_switching_hid_and_hogp()) { btif_config_get_int(name, BTIF_STORAGE_KEY_HID_DB_VERSION, &db_version); } log::info("link spec: {}; db version: {}", link_spec, db_version); if (db_version == 0) { btif_storage_load_bonded_hid_device(link_spec); } else { link_spec.transport = BT_TRANSPORT_BR_EDR; btif_storage_load_bonded_hid_device(link_spec); link_spec.transport = BT_TRANSPORT_LE; btif_storage_load_bonded_hogp_device(link_spec); } } return BT_STATUS_SUCCESS; } /******************************************************************************* * * Function btif_storage_remove_hid_info * * Description BTIF storage API - Deletes the bonded hid device info from * NVRAM * * Returns BT_STATUS_SUCCESS if the deletion was successful, * BT_STATUS_FAIL otherwise * ******************************************************************************/ bt_status_t btif_storage_remove_hid_info(const tAclLinkSpec& link_spec) { std::string bdstr = link_spec.addrt.bda.ToString(); btif_config_remove(bdstr, BTIF_STORAGE_KEY_HID_ATTR_MASK); btif_config_remove(bdstr, BTIF_STORAGE_KEY_HID_SUB_CLASS); btif_config_remove(bdstr, BTIF_STORAGE_KEY_HID_APP_ID); btif_config_remove(bdstr, BTIF_STORAGE_KEY_HID_VENDOR_ID); btif_config_remove(bdstr, BTIF_STORAGE_KEY_HID_PRODUCT_ID); btif_config_remove(bdstr, BTIF_STORAGE_KEY_HID_VERSION); btif_config_remove(bdstr, BTIF_STORAGE_KEY_HID_COUNTRY_CODE); btif_config_remove(bdstr, BTIF_STORAGE_KEY_HID_SSR_MAX_LATENCY); btif_config_remove(bdstr, BTIF_STORAGE_KEY_HID_SSR_MIN_TIMEOUT); btif_config_remove(bdstr, BTIF_STORAGE_KEY_HID_DESCRIPTOR); btif_config_remove(bdstr, BTIF_STORAGE_KEY_HID_RECONNECT_ALLOWED); btif_config_remove(bdstr, BTIF_STORAGE_KEY_HOGP_REPORT); btif_config_remove(bdstr, BTIF_STORAGE_KEY_HOGP_REPORT_VERSION); if (com::android::bluetooth::flags::allow_switching_hid_and_hogp()) { int db_version = 0; btif_config_get_int(bdstr, BTIF_STORAGE_KEY_HID_DB_VERSION, &db_version); if (db_version == STORAGE_HID_DB_VERSION) { btif_config_remove(bdstr, BTIF_STORAGE_KEY_HOGP_ATTR_MASK); btif_config_remove(bdstr, BTIF_STORAGE_KEY_HOGP_SUB_CLASS); btif_config_remove(bdstr, BTIF_STORAGE_KEY_HOGP_APP_ID); btif_config_remove(bdstr, BTIF_STORAGE_KEY_HOGP_VENDOR_ID); btif_config_remove(bdstr, BTIF_STORAGE_KEY_HOGP_PRODUCT_ID); btif_config_remove(bdstr, BTIF_STORAGE_KEY_HOGP_VERSION); btif_config_remove(bdstr, BTIF_STORAGE_KEY_HOGP_COUNTRY_CODE); btif_config_remove(bdstr, BTIF_STORAGE_KEY_HOGP_DESCRIPTOR); btif_config_remove(bdstr, BTIF_STORAGE_KEY_HOGP_RECONNECT_ALLOWED); } btif_config_remove(bdstr, BTIF_STORAGE_KEY_HID_DB_VERSION); } return BT_STATUS_SUCCESS; } // Check if a given profile is supported. static bool btif_device_supports_profile(const std::string& device, const Uuid& profile) { int size = STORAGE_UUID_STRING_SIZE * BT_MAX_NUM_UUIDS; char uuid_str[size]; if (btif_config_get_str(device, BTIF_STORAGE_KEY_REMOTE_SERVICE, uuid_str, &size)) { Uuid p_uuid[BT_MAX_NUM_UUIDS]; size_t num_uuids = btif_split_uuids_string(uuid_str, p_uuid, BT_MAX_NUM_UUIDS); for (size_t i = 0; i < num_uuids; i++) { if (p_uuid[i] == profile) { return true; } } } return false; } static bool btif_device_supports_hogp(const std::string& device) { return btif_device_supports_profile(device, Uuid::From16Bit(UUID_SERVCLASS_LE_HID)); } static bool btif_device_supports_classic_hid(const std::string& device) { return btif_device_supports_profile( device, Uuid::From16Bit(UUID_SERVCLASS_HUMAN_INTERFACE)); } /******************************************************************************* * * Function btif_storage_get_le_hid_devices * * Description BTIF storage API - Finds all bonded LE HID devices * * Returns std::vector of (RawAddress, AddressType) * ******************************************************************************/ bool btif_get_address_type(const RawAddress& bda, tBLE_ADDR_TYPE* p_addr_type); std::vector> btif_storage_get_le_hid_devices( void) { std::vector> hid_addresses; for (const auto& bd_addr : btif_config_get_paired_devices()) { auto name = bd_addr.ToString(); if (btif_device_supports_hogp(name)) { tBLE_ADDR_TYPE type = BLE_ADDR_PUBLIC; btif_get_address_type(bd_addr, &type); hid_addresses.push_back({bd_addr, type}); log::debug("Remote device: {}", bd_addr); } } return hid_addresses; } std::vector btif_storage_get_wake_capable_classic_hid_devices( void) { std::vector hid_addresses; for (const auto& bd_addr : btif_config_get_paired_devices()) { auto name = bd_addr.ToString(); if (btif_device_supports_classic_hid(name)) { // Filter out devices that aren't keyboards or pointing devices. // 0x500 = HID Major // 0x080 = Pointing device // 0x040 = Keyboard constexpr int kHidMask = COD_HID_MAJOR; constexpr int kKeyboardMouseMask = COD_HID_COMBO & ~COD_HID_MAJOR; int cod_value; if (!btif_config_get_int(name, BTIF_STORAGE_KEY_DEV_CLASS, &cod_value) || (cod_value & kHidMask) != kHidMask || (cod_value & kKeyboardMouseMask) == 0) { continue; } hid_addresses.push_back(bd_addr); log::debug("Remote device: {}", bd_addr); } } return hid_addresses; } void btif_storage_add_hearing_aid(const HearingDevice& dev_info) { do_in_jni_thread( Bind( [](const HearingDevice& dev_info) { std::string bdstr = dev_info.address.ToString(); log::verbose("saving hearing aid device: {}", dev_info.address); btif_config_set_int( bdstr, BTIF_STORAGE_KEY_HEARING_AID_SERVICE_CHANGED_CCC_HANDLE, dev_info.service_changed_ccc_handle); btif_config_set_int(bdstr, BTIF_STORAGE_KEY_HEARING_AID_READ_PSM_HANDLE, dev_info.read_psm_handle); btif_config_set_int(bdstr, BTIF_STORAGE_KEY_HEARING_AID_CAPABILITIES, dev_info.capabilities); btif_config_set_int(bdstr, BTIF_STORAGE_KEY_HEARING_AID_CODECS, dev_info.codecs); btif_config_set_int( bdstr, BTIF_STORAGE_KEY_HEARING_AID_AUDIO_CONTROL_POINT, dev_info.audio_control_point_handle); btif_config_set_int(bdstr, BTIF_STORAGE_KEY_HEARING_AID_VOLUME_HANDLE, dev_info.volume_handle); btif_config_set_int( bdstr, BTIF_STORAGE_KEY_HEARING_AID_AUDIO_STATUS_HANDLE, dev_info.audio_status_handle); btif_config_set_int( bdstr, BTIF_STORAGE_KEY_HEARING_AID_AUDIO_STATUS_CCC_HANDLE, dev_info.audio_status_ccc_handle); btif_config_set_uint64(bdstr, BTIF_STORAGE_KEY_HEARING_AID_SYNC_ID, dev_info.hi_sync_id); btif_config_set_int(bdstr, BTIF_STORAGE_KEY_HEARING_AID_RENDER_DELAY, dev_info.render_delay); btif_config_set_int(bdstr, BTIF_STORAGE_KEY_HEARING_AID_PREPARATION_DELAY, dev_info.preparation_delay); btif_config_set_int( bdstr, BTIF_STORAGE_KEY_HEARING_AID_IS_ACCEPTLISTED, true); }, dev_info)); } /** Loads information about bonded hearing aid devices */ void btif_storage_load_bonded_hearing_aids() { for (const auto& bd_addr : btif_config_get_paired_devices()) { const std::string& name = bd_addr.ToString(); int size = STORAGE_UUID_STRING_SIZE * BT_MAX_NUM_UUIDS; char uuid_str[size]; bool isHearingaidDevice = false; if (btif_config_get_str(name, BTIF_STORAGE_KEY_REMOTE_SERVICE, uuid_str, &size)) { Uuid p_uuid[BT_MAX_NUM_UUIDS]; size_t num_uuids = btif_split_uuids_string(uuid_str, p_uuid, BT_MAX_NUM_UUIDS); for (size_t i = 0; i < num_uuids; i++) { if (p_uuid[i] == Uuid::FromString("FDF0")) { isHearingaidDevice = true; break; } } } if (!isHearingaidDevice) { continue; } log::verbose("Remote device:{}", bd_addr); if (btif_in_fetch_bonded_device(name) != BT_STATUS_SUCCESS) { btif_storage_remove_hearing_aid(bd_addr); continue; } int value; uint8_t capabilities = 0; if (btif_config_get_int(name, BTIF_STORAGE_KEY_HEARING_AID_CAPABILITIES, &value)) capabilities = value; uint16_t codecs = 0; if (btif_config_get_int(name, BTIF_STORAGE_KEY_HEARING_AID_CODECS, &value)) codecs = value; uint16_t audio_control_point_handle = 0; if (btif_config_get_int( name, BTIF_STORAGE_KEY_HEARING_AID_AUDIO_CONTROL_POINT, &value)) audio_control_point_handle = value; uint16_t audio_status_handle = 0; if (btif_config_get_int( name, BTIF_STORAGE_KEY_HEARING_AID_AUDIO_STATUS_HANDLE, &value)) audio_status_handle = value; uint16_t audio_status_ccc_handle = 0; if (btif_config_get_int( name, BTIF_STORAGE_KEY_HEARING_AID_AUDIO_STATUS_CCC_HANDLE, &value)) audio_status_ccc_handle = value; uint16_t service_changed_ccc_handle = 0; if (btif_config_get_int( name, BTIF_STORAGE_KEY_HEARING_AID_SERVICE_CHANGED_CCC_HANDLE, &value)) service_changed_ccc_handle = value; uint16_t volume_handle = 0; if (btif_config_get_int(name, BTIF_STORAGE_KEY_HEARING_AID_VOLUME_HANDLE, &value)) volume_handle = value; uint16_t read_psm_handle = 0; if (btif_config_get_int(name, BTIF_STORAGE_KEY_HEARING_AID_READ_PSM_HANDLE, &value)) read_psm_handle = value; uint64_t lvalue; uint64_t hi_sync_id = 0; if (btif_config_get_uint64(name, BTIF_STORAGE_KEY_HEARING_AID_SYNC_ID, &lvalue)) hi_sync_id = lvalue; uint16_t render_delay = 0; if (btif_config_get_int(name, BTIF_STORAGE_KEY_HEARING_AID_RENDER_DELAY, &value)) render_delay = value; uint16_t preparation_delay = 0; if (btif_config_get_int( name, BTIF_STORAGE_KEY_HEARING_AID_PREPARATION_DELAY, &value)) preparation_delay = value; bool is_acceptlisted = false; if (btif_config_get_int(name, BTIF_STORAGE_KEY_HEARING_AID_IS_ACCEPTLISTED, &value)) is_acceptlisted = value; // add extracted information to BTA Hearing Aid do_in_main_thread( FROM_HERE, Bind(&HearingAid::AddFromStorage, HearingDevice(bd_addr, capabilities, codecs, audio_control_point_handle, audio_status_handle, audio_status_ccc_handle, service_changed_ccc_handle, volume_handle, read_psm_handle, hi_sync_id, render_delay, preparation_delay), is_acceptlisted)); } } /** Deletes the bonded hearing aid device info from NVRAM */ void btif_storage_remove_hearing_aid(const RawAddress& address) { std::string addrstr = address.ToString(); btif_config_remove(addrstr, BTIF_STORAGE_KEY_HEARING_AID_READ_PSM_HANDLE); btif_config_remove(addrstr, BTIF_STORAGE_KEY_HEARING_AID_CAPABILITIES); btif_config_remove(addrstr, BTIF_STORAGE_KEY_HEARING_AID_CODECS); btif_config_remove(addrstr, BTIF_STORAGE_KEY_HEARING_AID_AUDIO_CONTROL_POINT); btif_config_remove(addrstr, BTIF_STORAGE_KEY_HEARING_AID_VOLUME_HANDLE); btif_config_remove(addrstr, BTIF_STORAGE_KEY_HEARING_AID_AUDIO_STATUS_HANDLE); btif_config_remove(addrstr, BTIF_STORAGE_KEY_HEARING_AID_AUDIO_STATUS_CCC_HANDLE); btif_config_remove(addrstr, BTIF_STORAGE_KEY_HEARING_AID_SERVICE_CHANGED_CCC_HANDLE); btif_config_remove(addrstr, BTIF_STORAGE_KEY_HEARING_AID_SYNC_ID); btif_config_remove(addrstr, BTIF_STORAGE_KEY_HEARING_AID_RENDER_DELAY); btif_config_remove(addrstr, BTIF_STORAGE_KEY_HEARING_AID_PREPARATION_DELAY); btif_config_remove(addrstr, BTIF_STORAGE_KEY_HEARING_AID_IS_ACCEPTLISTED); } /** Set/Unset the hearing aid device HEARING_AID_IS_ACCEPTLISTED flag. */ void btif_storage_set_hearing_aid_acceptlist(const RawAddress& address, bool add_to_acceptlist) { std::string addrstr = address.ToString(); btif_config_set_int(addrstr, BTIF_STORAGE_KEY_HEARING_AID_IS_ACCEPTLISTED, add_to_acceptlist); } /** Get the hearing aid device properties. */ bool btif_storage_get_hearing_aid_prop( const RawAddress& address, uint8_t* capabilities, uint64_t* hi_sync_id, uint16_t* render_delay, uint16_t* preparation_delay, uint16_t* codecs) { std::string addrstr = address.ToString(); int value; if (btif_config_get_int(addrstr, BTIF_STORAGE_KEY_HEARING_AID_CAPABILITIES, &value)) { *capabilities = value; } else { return false; } if (btif_config_get_int(addrstr, BTIF_STORAGE_KEY_HEARING_AID_CODECS, &value)) { *codecs = value; } else { return false; } if (btif_config_get_int(addrstr, BTIF_STORAGE_KEY_HEARING_AID_RENDER_DELAY, &value)) { *render_delay = value; } else { return false; } if (btif_config_get_int( addrstr, BTIF_STORAGE_KEY_HEARING_AID_PREPARATION_DELAY, &value)) { *preparation_delay = value; } else { return false; } uint64_t lvalue; if (btif_config_get_uint64(addrstr, BTIF_STORAGE_KEY_HEARING_AID_SYNC_ID, &lvalue)) { *hi_sync_id = lvalue; } else { return false; } return true; } /** Set autoconnect information for LeAudio device */ void btif_storage_set_leaudio_autoconnect(const RawAddress& addr, bool autoconnect) { do_in_jni_thread(Bind( [](const RawAddress& addr, bool autoconnect) { std::string bdstr = addr.ToString(); log::verbose("saving le audio device: {}", addr); btif_config_set_int(bdstr, BTIF_STORAGE_KEY_LEAUDIO_AUTOCONNECT, autoconnect); }, addr, autoconnect)); } /** Store ASEs information */ void btif_storage_leaudio_update_handles_bin(const RawAddress& addr) { std::vector handles; if (LeAudioClient::GetHandlesForStorage(addr, handles)) { do_in_jni_thread(Bind( [](const RawAddress& bd_addr, std::vector handles) { auto bdstr = bd_addr.ToString(); btif_config_set_bin(bdstr, BTIF_STORAGE_KEY_LEAUDIO_HANDLES_BIN, handles.data(), handles.size()); }, addr, std::move(handles))); } } /** Store PACs information */ void btif_storage_leaudio_update_pacs_bin(const RawAddress& addr) { std::vector sink_pacs; if (LeAudioClient::GetSinkPacsForStorage(addr, sink_pacs)) { do_in_jni_thread(Bind( [](const RawAddress& bd_addr, std::vector sink_pacs) { auto bdstr = bd_addr.ToString(); btif_config_set_bin(bdstr, BTIF_STORAGE_KEY_LEAUDIO_SINK_PACS_BIN, sink_pacs.data(), sink_pacs.size()); }, addr, std::move(sink_pacs))); } std::vector source_pacs; if (LeAudioClient::GetSourcePacsForStorage(addr, source_pacs)) { do_in_jni_thread(Bind( [](const RawAddress& bd_addr, std::vector source_pacs) { auto bdstr = bd_addr.ToString(); btif_config_set_bin(bdstr, BTIF_STORAGE_KEY_LEAUDIO_SOURCE_PACS_BIN, source_pacs.data(), source_pacs.size()); }, addr, std::move(source_pacs))); } } /** Store ASEs information */ void btif_storage_leaudio_update_ase_bin(const RawAddress& addr) { std::vector ases; if (LeAudioClient::GetAsesForStorage(addr, ases)) { do_in_jni_thread(Bind( [](const RawAddress& bd_addr, std::vector ases) { auto bdstr = bd_addr.ToString(); btif_config_set_bin(bdstr, BTIF_STORAGE_KEY_LEAUDIO_ASES_BIN, ases.data(), ases.size()); }, addr, std::move(ases))); } } /** Store Le Audio device audio locations */ void btif_storage_set_leaudio_audio_location(const RawAddress& addr, uint32_t sink_location, uint32_t source_location) { do_in_jni_thread( Bind( [](const RawAddress& addr, int sink_location, int source_location) { std::string bdstr = addr.ToString(); log::debug("saving le audio device: {}", addr); btif_config_set_int(bdstr, BTIF_STORAGE_KEY_LEAUDIO_SINK_AUDIOLOCATION, sink_location); btif_config_set_int(bdstr, BTIF_STORAGE_KEY_LEAUDIO_SOURCE_AUDIOLOCATION, source_location); }, addr, sink_location, source_location)); } /** Store Le Audio device context types */ void btif_storage_set_leaudio_supported_context_types( const RawAddress& addr, uint16_t sink_supported_context_type, uint16_t source_supported_context_type) { do_in_jni_thread( Bind( [](const RawAddress& addr, int sink_supported_context_type, int source_supported_context_type) { std::string bdstr = addr.ToString(); log::debug("saving le audio device: {}", addr); btif_config_set_int( bdstr, BTIF_STORAGE_KEY_LEAUDIO_SINK_SUPPORTED_CONTEXT_TYPE, sink_supported_context_type); btif_config_set_int( bdstr, BTIF_STORAGE_KEY_LEAUDIO_SOURCE_SUPPORTED_CONTEXT_TYPE, source_supported_context_type); }, addr, sink_supported_context_type, source_supported_context_type)); } /** Loads information about bonded Le Audio devices */ void btif_storage_load_bonded_leaudio() { for (const auto& bd_addr : btif_config_get_paired_devices()) { auto name = bd_addr.ToString(); int size = STORAGE_UUID_STRING_SIZE * BT_MAX_NUM_UUIDS; char uuid_str[size]; bool isLeAudioDevice = false; if (btif_config_get_str(name, BTIF_STORAGE_KEY_REMOTE_SERVICE, uuid_str, &size)) { Uuid p_uuid[BT_MAX_NUM_UUIDS]; size_t num_uuids = btif_split_uuids_string(uuid_str, p_uuid, BT_MAX_NUM_UUIDS); for (size_t i = 0; i < num_uuids; i++) { if (p_uuid[i] == Uuid::FromString("184E")) { isLeAudioDevice = true; break; } } } if (!isLeAudioDevice) { continue; } log::verbose("Remote device:{}", bd_addr); int value; bool autoconnect = false; if (btif_config_get_int(name, BTIF_STORAGE_KEY_LEAUDIO_AUTOCONNECT, &value)) autoconnect = !!value; int sink_audio_location = 0; if (btif_config_get_int(name, BTIF_STORAGE_KEY_LEAUDIO_SINK_AUDIOLOCATION, &value)) sink_audio_location = value; int source_audio_location = 0; if (btif_config_get_int(name, BTIF_STORAGE_KEY_LEAUDIO_SOURCE_AUDIOLOCATION, &value)) source_audio_location = value; int sink_supported_context_type = 0; if (btif_config_get_int( name, BTIF_STORAGE_KEY_LEAUDIO_SINK_SUPPORTED_CONTEXT_TYPE, &value)) sink_supported_context_type = value; int source_supported_context_type = 0; if (btif_config_get_int( name, BTIF_STORAGE_KEY_LEAUDIO_SOURCE_SUPPORTED_CONTEXT_TYPE, &value)) source_supported_context_type = value; size_t buffer_size = btif_config_get_bin_length(name, BTIF_STORAGE_KEY_LEAUDIO_HANDLES_BIN); std::vector handles(buffer_size); if (buffer_size > 0) { btif_config_get_bin(name, BTIF_STORAGE_KEY_LEAUDIO_HANDLES_BIN, handles.data(), &buffer_size); } buffer_size = btif_config_get_bin_length( name, BTIF_STORAGE_KEY_LEAUDIO_SINK_PACS_BIN); std::vector sink_pacs(buffer_size); if (buffer_size > 0) { btif_config_get_bin(name, BTIF_STORAGE_KEY_LEAUDIO_SINK_PACS_BIN, sink_pacs.data(), &buffer_size); } buffer_size = btif_config_get_bin_length( name, BTIF_STORAGE_KEY_LEAUDIO_SOURCE_PACS_BIN); std::vector source_pacs(buffer_size); if (buffer_size > 0) { btif_config_get_bin(name, BTIF_STORAGE_KEY_LEAUDIO_SOURCE_PACS_BIN, source_pacs.data(), &buffer_size); } buffer_size = btif_config_get_bin_length(name, BTIF_STORAGE_KEY_LEAUDIO_ASES_BIN); std::vector ases(buffer_size); if (buffer_size > 0) { btif_config_get_bin(name, BTIF_STORAGE_KEY_LEAUDIO_ASES_BIN, ases.data(), &buffer_size); } do_in_main_thread( FROM_HERE, Bind(&LeAudioClient::AddFromStorage, bd_addr, autoconnect, sink_audio_location, source_audio_location, sink_supported_context_type, source_supported_context_type, std::move(handles), std::move(sink_pacs), std::move(source_pacs), std::move(ases))); } } void btif_storage_leaudio_clear_service_data(const RawAddress& address) { auto bdstr = address.ToString(); btif_config_remove(bdstr, BTIF_STORAGE_KEY_LEAUDIO_HANDLES_BIN); btif_config_remove(bdstr, BTIF_STORAGE_KEY_LEAUDIO_SINK_PACS_BIN); btif_config_remove(bdstr, BTIF_STORAGE_KEY_LEAUDIO_ASES_BIN); } /** Remove the Le Audio device from storage */ void btif_storage_remove_leaudio(const RawAddress& address) { std::string addrstr = address.ToString(); btif_config_set_int(addrstr, BTIF_STORAGE_KEY_LEAUDIO_AUTOCONNECT, false); } void btif_storage_add_leaudio_has_device(const RawAddress& address, std::vector presets_bin, uint8_t features, uint8_t active_preset) { do_in_jni_thread( Bind( [](const RawAddress& address, std::vector presets_bin, uint8_t features, uint8_t active_preset) { const std::string& name = address.ToString(); btif_config_set_int(name, BTIF_STORAGE_KEY_LEAUDIO_HAS_FLAGS, features); btif_config_set_int(name, BTIF_STORAGE_KEY_LEAUDIO_HAS_ACTIVE_PRESET, active_preset); btif_config_set_bin(name, BTIF_STORAGE_KEY_LEAUDIO_HAS_SERIALIZED_PRESETS, presets_bin.data(), presets_bin.size()); btif_config_set_int( name, BTIF_STORAGE_KEY_LEAUDIO_HAS_IS_ACCEPTLISTED, true); }, address, std::move(presets_bin), features, active_preset)); } void btif_storage_set_leaudio_has_active_preset(const RawAddress& address, uint8_t active_preset) { do_in_jni_thread(Bind( [](const RawAddress& address, uint8_t active_preset) { const std::string& name = address.ToString(); btif_config_set_int(name, BTIF_STORAGE_KEY_LEAUDIO_HAS_ACTIVE_PRESET, active_preset); }, address, active_preset)); } bool btif_storage_get_leaudio_has_features(const RawAddress& address, uint8_t& features) { std::string name = address.ToString(); int value; if (!btif_config_get_int(name, BTIF_STORAGE_KEY_LEAUDIO_HAS_FLAGS, &value)) return false; features = value; return true; } void btif_storage_set_leaudio_has_features(const RawAddress& address, uint8_t features) { do_in_jni_thread(Bind( [](const RawAddress& address, uint8_t features) { const std::string& name = address.ToString(); btif_config_set_int(name, BTIF_STORAGE_KEY_LEAUDIO_HAS_FLAGS, features); }, address, features)); } void btif_storage_load_bonded_leaudio_has_devices() { for (const auto& bd_addr : btif_config_get_paired_devices()) { const std::string& name = bd_addr.ToString(); if (!btif_config_exist(name, BTIF_STORAGE_KEY_LEAUDIO_HAS_IS_ACCEPTLISTED) && !btif_config_exist(name, BTIF_STORAGE_KEY_LEAUDIO_HAS_FLAGS)) continue; #ifndef TARGET_FLOSS int value; uint16_t is_acceptlisted = 0; if (btif_config_get_int(name, BTIF_STORAGE_KEY_LEAUDIO_HAS_IS_ACCEPTLISTED, &value)) is_acceptlisted = value; uint8_t features = 0; if (btif_config_get_int(name, BTIF_STORAGE_KEY_LEAUDIO_HAS_FLAGS, &value)) features = value; do_in_main_thread(FROM_HERE, Bind(&bluetooth::le_audio::has::HasClient::AddFromStorage, bd_addr, features, is_acceptlisted)); #else log::fatal("TODO - Fix LE audio build."); #endif } } void btif_storage_remove_leaudio_has(const RawAddress& address) { std::string addrstr = address.ToString(); btif_config_remove(addrstr, BTIF_STORAGE_KEY_LEAUDIO_HAS_IS_ACCEPTLISTED); btif_config_remove(addrstr, BTIF_STORAGE_KEY_LEAUDIO_HAS_FLAGS); btif_config_remove(addrstr, BTIF_STORAGE_KEY_LEAUDIO_HAS_ACTIVE_PRESET); btif_config_remove(addrstr, BTIF_STORAGE_KEY_LEAUDIO_HAS_SERIALIZED_PRESETS); } void btif_storage_set_leaudio_has_acceptlist(const RawAddress& address, bool add_to_acceptlist) { std::string addrstr = address.ToString(); btif_config_set_int(addrstr, BTIF_STORAGE_KEY_LEAUDIO_HAS_IS_ACCEPTLISTED, add_to_acceptlist); } void btif_storage_set_leaudio_has_presets(const RawAddress& address, std::vector presets_bin) { do_in_jni_thread( Bind( [](const RawAddress& address, std::vector presets_bin) { const std::string& name = address.ToString(); btif_config_set_bin(name, BTIF_STORAGE_KEY_LEAUDIO_HAS_SERIALIZED_PRESETS, presets_bin.data(), presets_bin.size()); }, address, std::move(presets_bin))); } bool btif_storage_get_leaudio_has_presets(const RawAddress& address, std::vector& presets_bin, uint8_t& active_preset) { std::string name = address.ToString(); int value; if (!btif_config_get_int(name, BTIF_STORAGE_KEY_LEAUDIO_HAS_ACTIVE_PRESET, &value)) return false; active_preset = value; auto bin_sz = btif_config_get_bin_length( name, BTIF_STORAGE_KEY_LEAUDIO_HAS_SERIALIZED_PRESETS); presets_bin.resize(bin_sz); if (!btif_config_get_bin(name, BTIF_STORAGE_KEY_LEAUDIO_HAS_SERIALIZED_PRESETS, presets_bin.data(), &bin_sz)) return false; return true; } /** Adds the bonded Le Audio device grouping info into the NVRAM */ void btif_storage_add_groups(const RawAddress& addr) { std::vector group_info; auto not_empty = DeviceGroups::GetForStorage(addr, group_info); if (not_empty) do_in_jni_thread(Bind( [](const RawAddress& bd_addr, std::vector group_info) { auto bdstr = bd_addr.ToString(); btif_config_set_bin(bdstr, BTIF_STORAGE_KEY_DEVICE_GROUP_BIN, group_info.data(), group_info.size()); }, addr, std::move(group_info))); } /** Deletes the bonded Le Audio device grouping info from the NVRAM */ void btif_storage_remove_groups(const RawAddress& address) { std::string addrstr = address.ToString(); btif_config_remove(addrstr, BTIF_STORAGE_KEY_DEVICE_GROUP_BIN); } /** Loads information about bonded group devices */ void btif_storage_load_bonded_groups(void) { for (const auto& bd_addr : btif_config_get_paired_devices()) { auto name = bd_addr.ToString(); size_t buffer_size = btif_config_get_bin_length(name, BTIF_STORAGE_KEY_DEVICE_GROUP_BIN); if (buffer_size == 0) continue; log::verbose("Grouped device:{}", bd_addr); std::vector in(buffer_size); if (btif_config_get_bin(name, BTIF_STORAGE_KEY_DEVICE_GROUP_BIN, in.data(), &buffer_size)) { do_in_main_thread(FROM_HERE, Bind(&DeviceGroups::AddFromStorage, bd_addr, std::move(in))); } } } /** Loads information about bonded group devices */ void btif_storage_load_bonded_volume_control_devices(void) { for (const auto& bd_addr : btif_config_get_paired_devices()) { auto device = bd_addr.ToString(); if (btif_device_supports_profile( device, Uuid::From16Bit(UUID_SERVCLASS_VOLUME_CONTROL_SERVER))) { do_in_main_thread(FROM_HERE, Bind(&VolumeControl::AddFromStorage, bd_addr)); } } } /** Stores information about the bonded CSIS device */ void btif_storage_update_csis_info(const RawAddress& addr) { std::vector set_info; auto not_empty = CsisClient::GetForStorage(addr, set_info); if (not_empty) do_in_jni_thread(Bind( [](const RawAddress& bd_addr, std::vector set_info) { auto bdstr = bd_addr.ToString(); btif_config_set_bin(bdstr, BTIF_STORAGE_KEY_CSIS_SET_INFO_BIN, set_info.data(), set_info.size()); }, addr, std::move(set_info))); } /** Loads information about the bonded CSIS device */ void btif_storage_load_bonded_csis_devices(void) { for (const auto& bd_addr : btif_config_get_paired_devices()) { auto name = bd_addr.ToString(); log::verbose("Loading CSIS device:{}", bd_addr); size_t buffer_size = btif_config_get_bin_length(name, BTIF_STORAGE_KEY_CSIS_SET_INFO_BIN); std::vector in(buffer_size); if (buffer_size != 0) btif_config_get_bin(name, BTIF_STORAGE_KEY_CSIS_SET_INFO_BIN, in.data(), &buffer_size); if (buffer_size != 0) do_in_main_thread( FROM_HERE, Bind(&CsisClient::AddFromStorage, bd_addr, std::move(in))); } } /** Removes information about the bonded CSIS device */ void btif_storage_remove_csis_device(const RawAddress& address) { std::string addrstr = address.ToString(); btif_config_remove(addrstr, BTIF_STORAGE_KEY_CSIS_AUTOCONNECT); btif_config_remove(addrstr, BTIF_STORAGE_KEY_CSIS_SET_INFO_BIN); } /******************************************************************************* * Function btif_storage_load_hidd * * Description Loads hidd bonded device and "plugs" it into hidd * * Returns BT_STATUS_SUCCESS if successful, BT_STATUS_FAIL otherwise * ******************************************************************************/ bt_status_t btif_storage_load_hidd(void) { for (const auto& bd_addr : btif_config_get_paired_devices()) { auto name = bd_addr.ToString(); log::verbose("Remote device:{}", bd_addr); int value; if (btif_in_fetch_bonded_device(name) == BT_STATUS_SUCCESS) { if (btif_config_get_int(name, BTIF_STORAGE_KEY_HID_DEVICE_CABLED, &value)) { BTA_HdAddDevice(bd_addr); break; } } } return BT_STATUS_SUCCESS; } /******************************************************************************* * * Function btif_storage_set_hidd * * Description Stores currently used HIDD device info in nvram and remove * the "HidDeviceCabled" flag from unused devices * * Returns BT_STATUS_SUCCESS * ******************************************************************************/ bt_status_t btif_storage_set_hidd(const RawAddress& remote_bd_addr) { std::string remote_device_address_string = remote_bd_addr.ToString(); for (const auto& bd_addr : btif_config_get_paired_devices()) { auto name = bd_addr.ToString(); if (bd_addr == remote_bd_addr) continue; if (btif_in_fetch_bonded_device(name) == BT_STATUS_SUCCESS) { btif_config_remove(name, BTIF_STORAGE_KEY_HID_DEVICE_CABLED); } } btif_config_set_int(remote_device_address_string, BTIF_STORAGE_KEY_HID_DEVICE_CABLED, 1); return BT_STATUS_SUCCESS; } /******************************************************************************* * * Function btif_storage_remove_hidd * * Description Removes hidd bonded device info from nvram * * Returns BT_STATUS_SUCCESS * ******************************************************************************/ bt_status_t btif_storage_remove_hidd(RawAddress* remote_bd_addr) { btif_config_remove(remote_bd_addr->ToString(), BTIF_STORAGE_KEY_HID_DEVICE_CABLED); return BT_STATUS_SUCCESS; } /******************************************************************************* * * Function btif_storage_set_hid_connection_policy * * Description Stores connection policy info in nvram * * Returns BT_STATUS_SUCCESS * ******************************************************************************/ bt_status_t btif_storage_set_hid_connection_policy( const tAclLinkSpec& link_spec, bool reconnect_allowed) { std::string bdstr = link_spec.addrt.bda.ToString(); if (link_spec.transport == BT_TRANSPORT_LE) { btif_config_set_int(bdstr, BTIF_STORAGE_KEY_HOGP_RECONNECT_ALLOWED, reconnect_allowed); } else if (link_spec.transport == BT_TRANSPORT_BR_EDR) { btif_config_set_int(bdstr, BTIF_STORAGE_KEY_HID_RECONNECT_ALLOWED, reconnect_allowed); } else { log::error("Unexpected!"); } return BT_STATUS_SUCCESS; } /******************************************************************************* * * Function btif_storage_get_hid_connection_policy * * Description get connection policy info from nvram * * Returns BT_STATUS_SUCCESS * ******************************************************************************/ bt_status_t btif_storage_get_hid_connection_policy( const tAclLinkSpec& link_spec, bool* reconnect_allowed) { std::string bdstr = link_spec.addrt.bda.ToString(); int value = 0; if (link_spec.transport == BT_TRANSPORT_LE) { btif_config_get_int(bdstr, BTIF_STORAGE_KEY_HOGP_RECONNECT_ALLOWED, &value); } else if (link_spec.transport == BT_TRANSPORT_BR_EDR) { btif_config_get_int(bdstr, BTIF_STORAGE_KEY_HID_RECONNECT_ALLOWED, &value); } else { log::error("Un expected!"); } *reconnect_allowed = value ? true : false; return BT_STATUS_SUCCESS; } /******************************************************************************* * *Function : btif_storage_set_pce_profile_version * * Description : * This function store remote PCE profile version in config file * ******************************************************************************/ void btif_storage_set_pce_profile_version(const RawAddress& remote_bd_addr, uint16_t peer_pce_version) { log::verbose("peer_pce_version : 0x{:x}", peer_pce_version); if (btif_config_set_bin( remote_bd_addr.ToString(), BTIF_STORAGE_KEY_PBAP_PCE_VERSION, (const uint8_t*)&peer_pce_version, sizeof(peer_pce_version))) { } else { log::warn("Failed to store peer_pce_version for {}", remote_bd_addr); } } /******************************************************************************* * * Function btif_storage_is_pce_version_102 * * Description checks if remote supports PBAP 1.2 * * Returns true/false depending on remote PBAP version support found in *file. * ******************************************************************************/ bool btif_storage_is_pce_version_102(const RawAddress& remote_bd_addr) { bool entry_found = false; // Read and restore the PBAP PCE version from local storage uint16_t pce_version = 0; size_t version_value_size = sizeof(pce_version); if (!btif_config_get_bin(remote_bd_addr.ToString(), BTIF_STORAGE_KEY_PBAP_PCE_VERSION, (uint8_t*)&pce_version, &version_value_size)) { log::verbose("Failed to read cached peer PCE version for {}", remote_bd_addr); return entry_found; } if (pce_version == 0x0102) { entry_found = true; } log::verbose("read cached peer PCE version 0x{:04x} for {}", pce_version, remote_bd_addr); return entry_found; }