1 /******************************************************************************
2 *
3 * Copyright (C) 2016 The Linux Foundation
4 * Copyright 2015 Google, Inc.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at:
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 ******************************************************************************/
19
20 #define LOG_TAG "bt_device_interop"
21
22 #include "device/include/interop.h"
23
24 #include <assert.h>
25 #include <bluetooth/log.h>
26 #include <ctype.h>
27 #include <fcntl.h>
28 #include <hardware/bluetooth.h>
29 #include <pthread.h>
30 #include <string.h> // For memcmp
31 #include <sys/stat.h>
32 #include <unistd.h>
33
34 #include <iostream>
35 #include <map>
36 #include <string>
37 #include <utility>
38
39 #include "btcore/include/module.h"
40 #include "btif/include/btif_storage.h"
41 #include "device/include/interop_config.h"
42 #include "device/include/interop_database.h"
43 #include "osi/include/allocator.h"
44 #include "osi/include/compat.h"
45 #include "osi/include/config.h"
46 #include "osi/include/list.h"
47 #include "osi/include/osi.h"
48 #include "types/raw_address.h"
49
50 // TODO(b/369381361) Enfore -Wmissing-prototypes
51 #pragma GCC diagnostic ignored "-Wmissing-prototypes"
52
53 using namespace bluetooth;
54
55 #ifdef __ANDROID__
56 static const char* INTEROP_DYNAMIC_FILE_PATH = "/data/misc/bluedroid/interop_database_dynamic.conf";
57 static const char* INTEROP_STATIC_FILE_PATH =
58 "/apex/com.android.btservices/etc/bluetooth/interop_database.conf";
59 #elif TARGET_FLOSS
60 #include <base/files/file_util.h>
61
62 #include <filesystem>
63
64 static const std::filesystem::path kDynamicConfigFileConfigFile =
65 std::filesystem::temp_directory_path() / "interop_database_dynamic.conf";
66 static const char* INTEROP_DYNAMIC_FILE_PATH = kDynamicConfigFileConfigFile.c_str();
67
68 static const char* INTEROP_STATIC_FILE_PATH = "/var/lib/bluetooth/interop_database.conf";
69 #else // !TARGET_FLOSS and !__ANDROID__
70 #include <base/files/file_util.h>
71
72 #include <filesystem>
73
74 static const std::filesystem::path kDynamicConfigFileConfigFile =
75 std::filesystem::temp_directory_path() / "interop_database_dynamic.conf";
76 static const char* INTEROP_DYNAMIC_FILE_PATH = kDynamicConfigFileConfigFile.c_str();
77
78 static const std::filesystem::path kStaticConfigFileConfigFile =
79 std::filesystem::temp_directory_path() / "interop_database.conf";
80
81 static const char* INTEROP_STATIC_FILE_PATH = kStaticConfigFileConfigFile.c_str();
82 #endif // __ANDROID__
83
84 #define CASE_RETURN_STR(const) \
85 case const: \
86 return #const;
87
88 static list_t* interop_list = NULL;
89
90 bool interop_is_initialized = false;
91 // protects operations on |interop_list|
92 pthread_mutex_t interop_list_lock;
93
94 // protects operations on |config|
95 static pthread_mutex_t file_lock;
96 static std::unique_ptr<const config_t> config_static;
97 static std::unique_ptr<config_t> config_dynamic;
98 static const char* UNKNOWN_INTEROP_FEATURE = "UNKNOWN";
99 // map from feature name to feature id
100 static std::map<std::string, int> feature_name_id_map;
101
102 // Macro used to find the total number of feature_types
103 #define NO_OF_FEATURES(x) (sizeof(x) / sizeof((x)[0]))
104
105 #define SECTION_MAX_LENGTH (249)
106 #define KEY_MAX_LENGTH (249)
107 #define VALID_VNDR_PRDT_LEN (13)
108 #define VALID_MNFR_STR_LEN (6)
109 #define VALID_SSR_LAT_LEN (15)
110 #define VALID_VERSION_LEN (6)
111 #define VALID_LMP_VERSION_LEN (20)
112 #define VALID_ADDR_RANGE_LEN (35)
113 #define VENDOR_VALUE_SEPARATOR "-"
114
115 #define ADDR_BASED "Address_Based"
116 #define ADDR_RANGE_BASED "Address_Range_Based"
117 #define NAME_BASED "Name_Based"
118 #define MNFR_BASED "Manufacturer_Based"
119 #define VNDR_PRDT_BASED "Vndr_Prdt_Based"
120 #define SSR_MAX_LAT_BASED "SSR_Max_Lat_Based"
121 #define VERSION_BASED "Version_Based"
122 #define LMP_VERSION_BASED "LMP_Version_Based"
123
124 typedef struct {
125 char* key;
126 char* value;
127 } interop_entry_t;
128
129 typedef struct {
130 char* name;
131 list_t* entries;
132 } interop_section_t;
133
134 typedef struct {
135 RawAddress addr;
136 uint16_t max_lat;
137 interop_feature_t feature;
138 } interop_hid_ssr_max_lat_t;
139
140 typedef struct {
141 uint16_t version;
142 interop_feature_t feature;
143 } interop_version_t;
144
145 typedef struct {
146 RawAddress addr;
147 uint8_t lmp_ver;
148 uint16_t lmp_sub_ver;
149 interop_feature_t feature;
150 } interop_lmp_version_t;
151
152 typedef enum {
153 INTEROP_BL_TYPE_ADDR = 0,
154 INTEROP_BL_TYPE_NAME,
155 INTEROP_BL_TYPE_MANUFACTURE,
156 INTEROP_BL_TYPE_VNDR_PRDT,
157 INTEROP_BL_TYPE_SSR_MAX_LAT,
158 INTEROP_BL_TYPE_VERSION,
159 INTEROP_BL_TYPE_LMP_VERSION,
160 INTEROP_BL_TYPE_ADDR_RANGE,
161 } interop_bl_type;
162
163 typedef enum {
164 INTEROP_ENTRY_TYPE_STATIC = 1 << 0,
165 INTEROP_ENTRY_TYPE_DYNAMIC = 1 << 1
166 } interop_entry_type;
167
168 typedef struct {
169 interop_bl_type bl_type;
170 interop_entry_type bl_entry_type;
171
172 union {
173 interop_addr_entry_t addr_entry;
174 interop_name_entry_t name_entry;
175 interop_manufacturer_t mnfr_entry;
176 interop_hid_multitouch_t vnr_pdt_entry;
177 interop_hid_ssr_max_lat_t ssr_max_lat_entry;
178 interop_version_t version_entry;
179 interop_lmp_version_t lmp_version_entry;
180 interop_addr_range_entry_t addr_range_entry;
181 } entry_type;
182 } interop_db_entry_t;
183
184 namespace std {
185 template <>
186 struct formatter<interop_bl_type> : enum_formatter<interop_bl_type> {};
187 } // namespace std
188
189 static const char* interop_feature_string_(const interop_feature_t feature);
190 static void interop_free_entry_(void* data);
191 static void interop_lazy_init_(void);
192
193 // Config related functions
194 static void interop_config_cleanup(void);
195
196 // This function is used to initialize the interop list and load the entries
197 // from file
198 static void load_config();
199 static void interop_database_add_(interop_db_entry_t* db_entry, bool persist);
200 static bool interop_database_remove_(interop_db_entry_t* entry);
201 static bool interop_database_match(interop_db_entry_t* entry, interop_db_entry_t** ret_entry,
202 interop_entry_type entry_type);
203 static void interop_config_flush(void);
204 static bool interop_config_remove(const std::string& section, const std::string& key);
205
206 // Interface functions
207
interop_match_addr(const interop_feature_t feature,const RawAddress * addr)208 bool interop_match_addr(const interop_feature_t feature, const RawAddress* addr) {
209 log::assert_that(addr != nullptr, "assert failed: addr != nullptr");
210 return interop_database_match_addr(feature, addr);
211 }
212
interop_match_name(const interop_feature_t feature,const char * name)213 bool interop_match_name(const interop_feature_t feature, const char* name) {
214 log::assert_that(name != nullptr, "assert failed: name != nullptr");
215 return interop_database_match_name(feature, name);
216 }
217
interop_match_addr_or_name(const interop_feature_t feature,const RawAddress * addr,bt_status_t (* get_remote_device_property)(const RawAddress *,bt_property_t *))218 bool interop_match_addr_or_name(const interop_feature_t feature, const RawAddress* addr,
219 bt_status_t (*get_remote_device_property)(const RawAddress*,
220 bt_property_t*)) {
221 log::assert_that(addr != nullptr, "assert failed: addr != nullptr");
222 log::assert_that(get_remote_device_property != nullptr,
223 "assert failed: get_remote_device_property != nullptr");
224
225 bt_bdname_t bdname;
226 bt_property_t prop_name;
227
228 if (interop_match_addr(feature, addr)) {
229 return true;
230 }
231
232 BTIF_STORAGE_FILL_PROPERTY(&prop_name, BT_PROPERTY_BDNAME, sizeof(bt_bdname_t), bdname.name);
233
234 if (get_remote_device_property(addr, &prop_name) != BT_STATUS_SUCCESS) {
235 return false;
236 }
237 if (strlen((const char*)bdname.name) == 0) {
238 return false;
239 }
240
241 return interop_match_name(feature, (const char*)bdname.name);
242 }
243
interop_match_manufacturer(const interop_feature_t feature,uint16_t manufacturer)244 bool interop_match_manufacturer(const interop_feature_t feature, uint16_t manufacturer) {
245 return interop_database_match_manufacturer(feature, manufacturer);
246 }
247
interop_match_vendor_product_ids(const interop_feature_t feature,uint16_t vendor_id,uint16_t product_id)248 bool interop_match_vendor_product_ids(const interop_feature_t feature, uint16_t vendor_id,
249 uint16_t product_id) {
250 return interop_database_match_vndr_prdt(feature, vendor_id, product_id);
251 }
252
interop_match_addr_get_max_lat(const interop_feature_t feature,const RawAddress * addr,uint16_t * max_lat)253 bool interop_match_addr_get_max_lat(const interop_feature_t feature, const RawAddress* addr,
254 uint16_t* max_lat) {
255 return interop_database_match_addr_get_max_lat(feature, addr, max_lat);
256 }
257
interop_database_add(const uint16_t feature,const RawAddress * addr,size_t length)258 void interop_database_add(const uint16_t feature, const RawAddress* addr, size_t length) {
259 log::assert_that(addr != nullptr, "assert failed: addr != nullptr");
260 log::assert_that(length > 0, "assert failed: length > 0");
261 log::assert_that(length < sizeof(RawAddress), "assert failed: length < sizeof(RawAddress)");
262 interop_database_add_addr(feature, addr, length);
263 }
264
interop_database_clear()265 void interop_database_clear() {
266 log::debug("interop_is_initialized: {} interop_list: {}", interop_is_initialized,
267 std::format_ptr(interop_list));
268
269 if (interop_is_initialized && interop_list) {
270 for (int feature = BEGINNING_OF_INTEROP_LIST; feature != END_OF_INTEROP_LIST; feature++) {
271 interop_database_remove_feature((interop_feature_t)feature);
272 }
273 }
274 }
275
interop_init_feature_name_id_map()276 static void interop_init_feature_name_id_map() {
277 log::debug("");
278
279 feature_name_id_map.clear();
280
281 int feature;
282
283 for (feature = BEGINNING_OF_INTEROP_LIST; feature < END_OF_INTEROP_LIST; feature++) {
284 const char* feature_name = interop_feature_string_((interop_feature_t)feature);
285 if (!strcmp(UNKNOWN_INTEROP_FEATURE, feature_name)) {
286 continue;
287 }
288
289 feature_name_id_map.insert({feature_name, feature});
290 }
291 }
292
293 // Module life-cycle functions
interop_init(void)294 static future_t* interop_init(void) {
295 interop_init_feature_name_id_map();
296
297 interop_lazy_init_();
298 interop_is_initialized = true;
299 return future_new_immediate(FUTURE_SUCCESS);
300 }
301
interop_clean_up(void)302 static future_t* interop_clean_up(void) {
303 pthread_mutex_lock(&interop_list_lock);
304 list_free(interop_list);
305 interop_list = NULL;
306 interop_is_initialized = false;
307 pthread_mutex_unlock(&interop_list_lock);
308 pthread_mutex_destroy(&interop_list_lock);
309 interop_config_cleanup();
310
311 return future_new_immediate(FUTURE_SUCCESS);
312 }
313
314 EXPORT_SYMBOL module_t interop_module = {
315 .name = INTEROP_MODULE,
316 .init = interop_init,
317 .start_up = NULL,
318 .shut_down = NULL,
319 .clean_up = interop_clean_up,
320 .dependencies = {NULL},
321 };
322
323 // Local functions
324
interop_feature_string_(const interop_feature_t feature)325 static const char* interop_feature_string_(const interop_feature_t feature) {
326 switch (feature) {
327 CASE_RETURN_STR(INTEROP_DISABLE_LE_SECURE_CONNECTIONS)
328 CASE_RETURN_STR(INTEROP_AUTO_RETRY_PAIRING)
329 CASE_RETURN_STR(INTEROP_DISABLE_ABSOLUTE_VOLUME)
330 CASE_RETURN_STR(INTEROP_DISABLE_AUTO_PAIRING)
331 CASE_RETURN_STR(INTEROP_KEYBOARD_REQUIRES_FIXED_PIN)
332 CASE_RETURN_STR(INTEROP_2MBPS_LINK_ONLY)
333 CASE_RETURN_STR(INTEROP_HID_PREF_CONN_SUP_TIMEOUT_3S)
334 CASE_RETURN_STR(INTEROP_GATTC_NO_SERVICE_CHANGED_IND)
335 CASE_RETURN_STR(INTEROP_DISABLE_SDP_AFTER_PAIRING)
336 CASE_RETURN_STR(INTEROP_DISABLE_AUTH_FOR_HID_POINTING)
337 CASE_RETURN_STR(INTEROP_REMOVE_HID_DIG_DESCRIPTOR)
338 CASE_RETURN_STR(INTEROP_DISABLE_SNIFF_DURING_SCO)
339 CASE_RETURN_STR(INTEROP_INCREASE_AG_CONN_TIMEOUT)
340 CASE_RETURN_STR(INTEROP_DISABLE_LE_CONN_PREFERRED_PARAMS)
341 CASE_RETURN_STR(INTEROP_DISABLE_AAC_CODEC)
342 CASE_RETURN_STR(INTEROP_DISABLE_AAC_VBR_CODEC)
343 CASE_RETURN_STR(INTEROP_DYNAMIC_ROLE_SWITCH)
344 CASE_RETURN_STR(INTEROP_DISABLE_ROLE_SWITCH)
345 CASE_RETURN_STR(INTEROP_DISABLE_ROLE_SWITCH_POLICY)
346 CASE_RETURN_STR(INTEROP_HFP_1_7_DENYLIST)
347 CASE_RETURN_STR(INTEROP_ADV_PBAP_VER_1_1)
348 CASE_RETURN_STR(INTEROP_UPDATE_HID_SSR_MAX_LAT)
349 CASE_RETURN_STR(INTEROP_DISABLE_AVDTP_RECONFIGURE)
350 CASE_RETURN_STR(INTEROP_DISABLE_HF_INDICATOR)
351 CASE_RETURN_STR(INTEROP_DISABLE_LE_CONN_UPDATES)
352 CASE_RETURN_STR(INTEROP_DELAY_SCO_FOR_MT_CALL)
353 CASE_RETURN_STR(INTEROP_DISABLE_CODEC_NEGOTIATION)
354 CASE_RETURN_STR(INTEROP_DISABLE_PLAYER_APPLICATION_SETTING_CMDS)
355 CASE_RETURN_STR(INTEROP_ENABLE_AAC_CODEC)
356 CASE_RETURN_STR(INTEROP_DISABLE_CONNECTION_AFTER_COLLISION)
357 CASE_RETURN_STR(INTEROP_AVRCP_BROWSE_OPEN_CHANNEL_COLLISION)
358 CASE_RETURN_STR(INTEROP_ADV_PBAP_VER_1_2)
359 CASE_RETURN_STR(INTEROP_DISABLE_PCE_SDP_AFTER_PAIRING)
360 CASE_RETURN_STR(INTEROP_DISABLE_SNIFF_LINK_DURING_SCO)
361 CASE_RETURN_STR(INTEROP_DISABLE_SNIFF_DURING_CALL)
362 CASE_RETURN_STR(INTEROP_HID_HOST_LIMIT_SNIFF_INTERVAL)
363 CASE_RETURN_STR(INTEROP_DISABLE_REFRESH_ACCEPT_SIG_TIMER)
364 CASE_RETURN_STR(INTEROP_SKIP_INCOMING_STATE)
365 CASE_RETURN_STR(INTEROP_NOT_UPDATE_AVRCP_PAUSED_TO_REMOTE)
366 CASE_RETURN_STR(INTEROP_PHONE_POLICY_INCREASED_DELAY_CONNECT_OTHER_PROFILES)
367 CASE_RETURN_STR(INTEROP_PHONE_POLICY_REDUCED_DELAY_CONNECT_OTHER_PROFILES)
368 CASE_RETURN_STR(INTEROP_HFP_FAKE_INCOMING_CALL_INDICATOR)
369 CASE_RETURN_STR(INTEROP_HFP_SEND_CALL_INDICATORS_BACK_TO_BACK)
370 CASE_RETURN_STR(INTEROP_SETUP_SCO_WITH_NO_DELAY_AFTER_SLC_DURING_CALL)
371 CASE_RETURN_STR(INTEROP_ENABLE_PREFERRED_CONN_PARAMETER)
372 CASE_RETURN_STR(INTEROP_RETRY_SCO_AFTER_REMOTE_REJECT_SCO)
373 CASE_RETURN_STR(INTEROP_DELAY_SCO_FOR_MO_CALL)
374 CASE_RETURN_STR(INTEROP_CHANGE_HID_VID_PID)
375 CASE_RETURN_STR(END_OF_INTEROP_LIST)
376 CASE_RETURN_STR(INTEROP_HFP_1_8_DENYLIST)
377 CASE_RETURN_STR(INTEROP_DISABLE_ROLE_SWITCH_DURING_CONNECTION)
378 CASE_RETURN_STR(INTEROP_DISABLE_NAME_REQUEST)
379 CASE_RETURN_STR(INTEROP_AVRCP_1_4_ONLY)
380 CASE_RETURN_STR(INTEROP_DISABLE_SNIFF)
381 CASE_RETURN_STR(INTEROP_DISABLE_AVDTP_SUSPEND)
382 CASE_RETURN_STR(INTEROP_SLC_SKIP_BIND_COMMAND)
383 CASE_RETURN_STR(INTEROP_AVRCP_1_3_ONLY)
384 CASE_RETURN_STR(INTEROP_DISABLE_ROBUST_CACHING);
385 CASE_RETURN_STR(INTEROP_HFP_1_7_ALLOWLIST);
386 CASE_RETURN_STR(INTEROP_HFP_1_9_ALLOWLIST);
387 CASE_RETURN_STR(INTEROP_IGNORE_DISC_BEFORE_SIGNALLING_TIMEOUT);
388 CASE_RETURN_STR(INTEROP_SUSPEND_ATT_TRAFFIC_DURING_PAIRING);
389 CASE_RETURN_STR(INTEROP_INSERT_CALL_WHEN_SCO_START);
390 CASE_RETURN_STR(INTEROP_DELAY_AUTH);
391 CASE_RETURN_STR(INTEROP_MULTIPLE_HOGP_SERVICE_CHOOSE_THIRD);
392 CASE_RETURN_STR(INTEROP_A2DP_SKIP_SDP_DURING_RECONNECTION);
393 CASE_RETURN_STR(INTEROP_HID_PREF_CONN_ZERO_LATENCY);
394 CASE_RETURN_STR(INTEROP_HOGP_LONG_REPORT);
395 CASE_RETURN_STR(INTEROP_HOGP_FORCE_MTU_EXCHANGE);
396 }
397 return UNKNOWN_INTEROP_FEATURE;
398 }
399
interop_free_entry_(void * data)400 static void interop_free_entry_(void* data) {
401 interop_db_entry_t* entry = (interop_db_entry_t*)data;
402 osi_free(entry);
403 }
404
interop_lazy_init_(void)405 static void interop_lazy_init_(void) {
406 pthread_mutex_init(&interop_list_lock, NULL);
407 if (interop_list == NULL) {
408 interop_list = list_new(interop_free_entry_);
409 load_config();
410 }
411 }
412
413 // interop config related functions
414
interop_config_init(void)415 static int interop_config_init(void) {
416 struct stat sts;
417 pthread_mutex_init(&file_lock, NULL);
418 pthread_mutex_lock(&file_lock);
419
420 if (!stat(INTEROP_STATIC_FILE_PATH, &sts) && sts.st_size) {
421 if (!(config_static = config_new(INTEROP_STATIC_FILE_PATH))) {
422 log::warn("unable to load static config file for : {}", INTEROP_STATIC_FILE_PATH);
423 }
424 }
425 if (!config_static && !(config_static = config_new_empty())) {
426 goto error;
427 }
428
429 if (!stat(INTEROP_DYNAMIC_FILE_PATH, &sts) && sts.st_size) {
430 if (!(config_dynamic = config_new(INTEROP_DYNAMIC_FILE_PATH))) {
431 log::warn("unable to load dynamic config file for : {}", INTEROP_DYNAMIC_FILE_PATH);
432 }
433 }
434 if (!config_dynamic && !(config_dynamic = config_new_empty())) {
435 goto error;
436 }
437 pthread_mutex_unlock(&file_lock);
438 return 0;
439
440 error:
441 config_static.reset();
442 config_dynamic.reset();
443 pthread_mutex_unlock(&file_lock);
444 return -1;
445 }
446
interop_config_flush(void)447 static void interop_config_flush(void) {
448 log::assert_that(config_dynamic.get() != NULL, "assert failed: config_dynamic.get() != NULL");
449
450 pthread_mutex_lock(&file_lock);
451 config_save(*config_dynamic, INTEROP_DYNAMIC_FILE_PATH);
452 pthread_mutex_unlock(&file_lock);
453 }
454
interop_config_remove(const std::string & section,const std::string & key)455 static bool interop_config_remove(const std::string& section, const std::string& key) {
456 log::assert_that(config_dynamic.get() != NULL, "assert failed: config_dynamic.get() != NULL");
457
458 pthread_mutex_lock(&file_lock);
459 bool ret = config_remove_key(config_dynamic.get(), section, key);
460 pthread_mutex_unlock(&file_lock);
461
462 return ret;
463 }
464
interop_config_remove_section(const std::string & section)465 static bool interop_config_remove_section(const std::string& section) {
466 log::assert_that(config_dynamic.get() != NULL, "assert failed: config_dynamic.get() != NULL");
467
468 pthread_mutex_lock(&file_lock);
469 bool ret = config_remove_section(config_dynamic.get(), section);
470 pthread_mutex_unlock(&file_lock);
471
472 return ret;
473 }
474
interop_config_set_str(const std::string & section,const std::string & key,const std::string & value)475 static bool interop_config_set_str(const std::string& section, const std::string& key,
476 const std::string& value) {
477 log::assert_that(config_dynamic.get() != NULL, "assert failed: config_dynamic.get() != NULL");
478
479 pthread_mutex_lock(&file_lock);
480 config_set_string(config_dynamic.get(), section, key, value);
481 pthread_mutex_unlock(&file_lock);
482
483 return true;
484 }
485
interop_feature_name_to_feature_id(const char * feature_name)486 int interop_feature_name_to_feature_id(const char* feature_name) {
487 if (feature_name == NULL) {
488 return -1;
489 }
490
491 auto it = feature_name_id_map.find(std::string(feature_name));
492 if (it == feature_name_id_map.end()) {
493 log::warn("feature does not exist: {}", feature_name);
494 return -1;
495 }
496
497 return it->second;
498 }
499
interop_config_add_or_remove(interop_db_entry_t * db_entry,bool add)500 static bool interop_config_add_or_remove(interop_db_entry_t* db_entry, bool add) {
501 bool status = true;
502 std::string key;
503 std::string value;
504 interop_feature_t feature;
505
506 // add it to the config file as well
507 switch (db_entry->bl_type) {
508 case INTEROP_BL_TYPE_ADDR: {
509 interop_addr_entry_t addr_entry = db_entry->entry_type.addr_entry;
510
511 const std::string bdstr =
512 addr_entry.addr.ToColonSepHexString().substr(0, addr_entry.length * 3 - 1);
513
514 feature = db_entry->entry_type.addr_entry.feature;
515 key.assign(bdstr);
516 value.assign(ADDR_BASED);
517
518 break;
519 }
520 case INTEROP_BL_TYPE_NAME: {
521 feature = db_entry->entry_type.name_entry.feature;
522 key.assign(db_entry->entry_type.name_entry.name);
523 value.assign(NAME_BASED);
524
525 break;
526 }
527 case INTEROP_BL_TYPE_MANUFACTURE: {
528 char m_facturer[KEY_MAX_LENGTH] = {'\0'};
529 snprintf(m_facturer, sizeof(m_facturer), "0x%04x",
530 db_entry->entry_type.mnfr_entry.manufacturer);
531
532 feature = db_entry->entry_type.mnfr_entry.feature;
533 key.assign(m_facturer);
534 value.assign(MNFR_BASED);
535
536 break;
537 }
538 case INTEROP_BL_TYPE_VNDR_PRDT: {
539 char m_vnr_pdt[KEY_MAX_LENGTH] = {'\0'};
540 snprintf(m_vnr_pdt, sizeof(m_vnr_pdt), "0x%04x-0x%04x",
541 db_entry->entry_type.vnr_pdt_entry.vendor_id,
542 db_entry->entry_type.vnr_pdt_entry.product_id);
543
544 feature = db_entry->entry_type.vnr_pdt_entry.feature;
545 key.assign(m_vnr_pdt);
546 value.assign(VNDR_PRDT_BASED);
547
548 break;
549 }
550 case INTEROP_BL_TYPE_SSR_MAX_LAT: {
551 interop_hid_ssr_max_lat_t ssr_entry = db_entry->entry_type.ssr_max_lat_entry;
552 char m_ssr_max_lat[KEY_MAX_LENGTH] = {'\0'};
553
554 const std::string bdstr = ssr_entry.addr.ToColonSepHexString().substr(0, 3 * 3 - 1);
555
556 snprintf(m_ssr_max_lat, sizeof(m_ssr_max_lat), "%s-0x%04x", bdstr.c_str(),
557 db_entry->entry_type.ssr_max_lat_entry.max_lat);
558
559 feature = db_entry->entry_type.ssr_max_lat_entry.feature;
560 key.assign(m_ssr_max_lat);
561 value.assign(SSR_MAX_LAT_BASED);
562
563 break;
564 }
565 case INTEROP_BL_TYPE_VERSION: {
566 char m_version[KEY_MAX_LENGTH] = {'\0'};
567 snprintf(m_version, sizeof(m_version), "0x%04x", db_entry->entry_type.version_entry.version);
568
569 feature = db_entry->entry_type.version_entry.feature;
570 key.assign(m_version);
571 value.assign(VERSION_BASED);
572
573 break;
574 }
575 case INTEROP_BL_TYPE_LMP_VERSION: {
576 interop_lmp_version_t lmp_version_entry = db_entry->entry_type.lmp_version_entry;
577 char m_lmp_version[KEY_MAX_LENGTH] = {'\0'};
578 const std::string bdstr = lmp_version_entry.addr.ToColonSepHexString().substr(0, 3 * 3 - 1);
579
580 snprintf(m_lmp_version, sizeof(m_lmp_version), "%s-0x%02x-0x%04x", bdstr.c_str(),
581 db_entry->entry_type.lmp_version_entry.lmp_ver,
582 db_entry->entry_type.lmp_version_entry.lmp_sub_ver);
583
584 feature = db_entry->entry_type.lmp_version_entry.feature;
585 key.assign(m_lmp_version);
586 value.assign(LMP_VERSION_BASED);
587
588 break;
589 }
590 default:
591 log::error("bl_type: {} not handled", db_entry->bl_type);
592 status = false;
593 break;
594 }
595
596 if (status) {
597 if (add) {
598 interop_config_set_str(interop_feature_string_(feature), key, value);
599 } else {
600 interop_config_remove(interop_feature_string_(feature), key);
601 }
602 interop_config_flush();
603 }
604
605 return status;
606 }
607
interop_database_add_(interop_db_entry_t * db_entry,bool persist)608 static void interop_database_add_(interop_db_entry_t* db_entry, bool persist) {
609 interop_db_entry_t* ret_entry = NULL;
610 bool match_found = interop_database_match(
611 db_entry, &ret_entry,
612 (interop_entry_type)(INTEROP_ENTRY_TYPE_STATIC | INTEROP_ENTRY_TYPE_DYNAMIC));
613
614 if (match_found) {
615 // return as the entry is already present
616 log::debug("Entry is already present in the list");
617 return;
618 }
619
620 pthread_mutex_lock(&interop_list_lock);
621
622 if (interop_list) {
623 list_append(interop_list, db_entry);
624 }
625
626 pthread_mutex_unlock(&interop_list_lock);
627
628 if (!persist) {
629 // return if the persist option is not set
630 return;
631 }
632
633 interop_config_add_or_remove(db_entry, true);
634 }
635
interop_database_match(interop_db_entry_t * entry,interop_db_entry_t ** ret_entry,interop_entry_type entry_type)636 static bool interop_database_match(interop_db_entry_t* entry, interop_db_entry_t** ret_entry,
637 interop_entry_type entry_type) {
638 log::assert_that(entry != nullptr, "assert failed: entry != nullptr");
639 bool found = false;
640 pthread_mutex_lock(&interop_list_lock);
641 if (interop_list == NULL || list_length(interop_list) == 0) {
642 pthread_mutex_unlock(&interop_list_lock);
643 return false;
644 }
645
646 const list_node_t* node = list_begin(interop_list);
647
648 while (node != list_end(interop_list)) {
649 interop_db_entry_t* db_entry = (interop_db_entry_t*)list_node(node);
650 log::assert_that(db_entry != nullptr, "assert failed: db_entry != nullptr");
651
652 if (entry->bl_type != db_entry->bl_type) {
653 node = list_next(node);
654 continue;
655 }
656
657 if ((entry_type == INTEROP_ENTRY_TYPE_STATIC) || (entry_type == INTEROP_ENTRY_TYPE_DYNAMIC)) {
658 if (entry->bl_entry_type != db_entry->bl_entry_type) {
659 node = list_next(node);
660 continue;
661 }
662 }
663
664 switch (db_entry->bl_type) {
665 case INTEROP_BL_TYPE_ADDR: {
666 interop_addr_entry_t* src = &entry->entry_type.addr_entry;
667 interop_addr_entry_t* cur = &db_entry->entry_type.addr_entry;
668 if ((src->feature == cur->feature) && (!memcmp(&src->addr, &cur->addr, cur->length))) {
669 /* cur len is used to remove src entry from config file, when
670 * interop_database_remove_addr is called. */
671 src->length = cur->length;
672 found = true;
673 }
674 break;
675 }
676 case INTEROP_BL_TYPE_NAME: {
677 interop_name_entry_t* src = &entry->entry_type.name_entry;
678 interop_name_entry_t* cur = &db_entry->entry_type.name_entry;
679
680 if ((src->feature == cur->feature) && (strcasestr(src->name, cur->name) == src->name)) {
681 found = true;
682 }
683 break;
684 }
685 case INTEROP_BL_TYPE_MANUFACTURE: {
686 interop_manufacturer_t* src = &entry->entry_type.mnfr_entry;
687 interop_manufacturer_t* cur = &db_entry->entry_type.mnfr_entry;
688
689 if (src->feature == cur->feature && src->manufacturer == cur->manufacturer) {
690 found = true;
691 }
692 break;
693 }
694 case INTEROP_BL_TYPE_VNDR_PRDT: {
695 interop_hid_multitouch_t* src = &entry->entry_type.vnr_pdt_entry;
696 interop_hid_multitouch_t* cur = &db_entry->entry_type.vnr_pdt_entry;
697
698 if ((src->feature == cur->feature) && (src->vendor_id == cur->vendor_id) &&
699 (src->product_id == cur->product_id)) {
700 found = true;
701 }
702 break;
703 }
704 case INTEROP_BL_TYPE_SSR_MAX_LAT: {
705 interop_hid_ssr_max_lat_t* src = &entry->entry_type.ssr_max_lat_entry;
706 interop_hid_ssr_max_lat_t* cur = &db_entry->entry_type.ssr_max_lat_entry;
707
708 if ((src->feature == cur->feature) && !memcmp(&src->addr, &cur->addr, 3)) {
709 found = true;
710 }
711 break;
712 }
713 case INTEROP_BL_TYPE_VERSION: {
714 interop_version_t* src = &entry->entry_type.version_entry;
715 interop_version_t* cur = &db_entry->entry_type.version_entry;
716
717 if ((src->feature == cur->feature) && (src->version == cur->version)) {
718 found = true;
719 }
720 break;
721 }
722 case INTEROP_BL_TYPE_LMP_VERSION: {
723 interop_lmp_version_t* src = &entry->entry_type.lmp_version_entry;
724 interop_lmp_version_t* cur = &db_entry->entry_type.lmp_version_entry;
725
726 if ((src->feature == cur->feature) && (!memcmp(&src->addr, &cur->addr, 3))) {
727 found = true;
728 }
729 break;
730 }
731 case INTEROP_BL_TYPE_ADDR_RANGE: {
732 interop_addr_range_entry_t* src = &entry->entry_type.addr_range_entry;
733 interop_addr_range_entry_t* cur = &db_entry->entry_type.addr_range_entry;
734
735 // src->addr_start has the actual address, which need to be searched in
736 // the range
737 if ((src->feature == cur->feature) && (src->addr_start >= cur->addr_start) &&
738 (src->addr_start <= cur->addr_end)) {
739 found = true;
740 }
741 break;
742 }
743 default:
744 log::error("bl_type: {} not handled", db_entry->bl_type);
745 break;
746 }
747
748 if (found && ret_entry) {
749 *ret_entry = db_entry;
750 break;
751 }
752 node = list_next(node);
753 }
754 pthread_mutex_unlock(&interop_list_lock);
755 return found;
756 }
757
interop_database_remove_(interop_db_entry_t * entry)758 static bool interop_database_remove_(interop_db_entry_t* entry) {
759 interop_db_entry_t* ret_entry = NULL;
760
761 if (!interop_database_match(entry, &ret_entry,
762 (interop_entry_type)(INTEROP_ENTRY_TYPE_DYNAMIC))) {
763 log::error("Entry not found in the list");
764 return false;
765 }
766
767 // first remove it from linked list
768 pthread_mutex_lock(&interop_list_lock);
769 list_remove(interop_list, (void*)ret_entry);
770 pthread_mutex_unlock(&interop_list_lock);
771
772 return interop_config_add_or_remove(entry, false);
773 }
774
trim(char * str)775 static char* trim(char* str) {
776 while (isspace(*str)) {
777 ++str;
778 }
779
780 if (!*str) {
781 return str;
782 }
783
784 char* end_str = str + strlen(str) - 1;
785 while (end_str > str && isspace(*end_str)) {
786 --end_str;
787 }
788
789 end_str[1] = '\0';
790 return str;
791 }
792
token_to_ul(char * token,uint16_t * ul)793 bool token_to_ul(char* token, uint16_t* ul) {
794 char* e;
795 bool ret_value = false;
796
797 token = trim(token);
798 errno = 0;
799 *ul = (uint16_t)strtoul(token, &e, 16);
800 if ((e != NULL) && errno != EINVAL && errno != ERANGE) {
801 ret_value = true;
802 }
803 return ret_value;
804 }
805
get_vendor_product_id(char * vendorstr,uint16_t * vendor,uint16_t * product)806 static bool get_vendor_product_id(char* vendorstr, uint16_t* vendor, uint16_t* product) {
807 char* token;
808 char* saveptr = NULL;
809 bool ret_value = false;
810
811 if ((token = strtok_r(vendorstr, VENDOR_VALUE_SEPARATOR, &saveptr)) != NULL) {
812 ret_value = token_to_ul(token, vendor);
813 }
814
815 if (ret_value && (token = strtok_r(NULL, VENDOR_VALUE_SEPARATOR, &saveptr)) != NULL) {
816 ret_value = token_to_ul(token, product);
817 }
818 return ret_value;
819 }
820
get_addr_maxlat(char * str,char * bdaddrstr,uint16_t * max_lat)821 static bool get_addr_maxlat(char* str, char* bdaddrstr, uint16_t* max_lat) {
822 char* token;
823 char* saveptr = NULL;
824 bool ret_value = false;
825
826 if ((token = strtok_r(str, VENDOR_VALUE_SEPARATOR, &saveptr)) != NULL) {
827 trim(token);
828 osi_strlcpy(bdaddrstr, token, KEY_MAX_LENGTH);
829 } else {
830 return false;
831 }
832
833 if ((token = strtok_r(NULL, VENDOR_VALUE_SEPARATOR, &saveptr)) != NULL) {
834 ret_value = token_to_ul(token, max_lat);
835 }
836 return ret_value;
837 }
838
get_addr_range(char * str,RawAddress * addr_start,RawAddress * addr_end)839 static bool get_addr_range(char* str, RawAddress* addr_start, RawAddress* addr_end) {
840 char* token;
841 char* saveptr = NULL;
842 bool ret_value = false;
843 char addr_start_str[18] = {'\0'};
844 char addr_end_str[18] = {'\0'};
845
846 if ((token = strtok_r(str, VENDOR_VALUE_SEPARATOR, &saveptr)) != NULL) {
847 trim(token);
848 osi_strlcpy(addr_start_str, token, 18);
849 if (!RawAddress::FromString(addr_start_str, *addr_start)) {
850 return false;
851 }
852 } else {
853 return false;
854 }
855
856 if ((token = strtok_r(NULL, VENDOR_VALUE_SEPARATOR, &saveptr)) != NULL) {
857 trim(token);
858 osi_strlcpy(addr_end_str, token, 18);
859 if (RawAddress::FromString(addr_end_str, *addr_end)) {
860 ret_value = true;
861 }
862 }
863 return ret_value;
864 }
865
get_addr_lmp_ver(char * str,char * bdaddrstr,uint8_t * lmp_ver,uint16_t * lmp_sub_ver)866 static bool get_addr_lmp_ver(char* str, char* bdaddrstr, uint8_t* lmp_ver, uint16_t* lmp_sub_ver) {
867 char* token;
868 char* saveptr = NULL;
869 char* e;
870
871 if ((token = strtok_r(str, VENDOR_VALUE_SEPARATOR, &saveptr)) != NULL) {
872 trim(token);
873 osi_strlcpy(bdaddrstr, token, KEY_MAX_LENGTH);
874 } else {
875 return false;
876 }
877
878 if ((token = strtok_r(NULL, VENDOR_VALUE_SEPARATOR, &saveptr)) != NULL) {
879 trim(token);
880 errno = 0;
881 *lmp_ver = (uint8_t)strtoul(token, &e, 16);
882 if (errno == EINVAL || errno == ERANGE) {
883 return false;
884 }
885 } else {
886 return false;
887 }
888
889 if ((token = strtok_r(NULL, VENDOR_VALUE_SEPARATOR, &saveptr)) != NULL) {
890 return token_to_ul(token, lmp_sub_ver);
891 }
892 return false;
893 }
894
load_to_database(int feature,const char * key,const char * value,interop_entry_type entry_type)895 static bool load_to_database(int feature, const char* key, const char* value,
896 interop_entry_type entry_type) {
897 if (!strncasecmp(value, ADDR_BASED, strlen(ADDR_BASED))) {
898 RawAddress addr;
899 int len = 0;
900
901 len = (strlen(key) + 1) / 3;
902 if (len < 3 || len > 4) {
903 log::warn("Ignoring as invalid entry for Address {}", key);
904 return false;
905 }
906
907 std::string bdstr(key);
908 std::string append_str(":00");
909 for (int i = 6; i > len; i--) {
910 bdstr.append(append_str);
911 }
912
913 if (!RawAddress::FromString(bdstr, addr)) {
914 log::warn("key {} or Bluetooth Address {} is invalid, not added to interop list", key, addr);
915 return false;
916 }
917
918 interop_db_entry_t* entry = (interop_db_entry_t*)osi_calloc(sizeof(interop_db_entry_t));
919 entry->bl_type = INTEROP_BL_TYPE_ADDR;
920 entry->bl_entry_type = entry_type;
921 entry->entry_type.addr_entry.addr = addr;
922 entry->entry_type.addr_entry.feature = (interop_feature_t)feature;
923 entry->entry_type.addr_entry.length = len;
924 interop_database_add_(entry, false);
925
926 } else if (!strncasecmp(value, NAME_BASED, strlen(NAME_BASED))) {
927 if (strlen(key) > KEY_MAX_LENGTH - 1) {
928 log::warn("ignoring {} due to invalid length", key);
929 return false;
930 }
931 interop_db_entry_t* entry = (interop_db_entry_t*)osi_calloc(sizeof(interop_db_entry_t));
932 entry->bl_type = INTEROP_BL_TYPE_NAME;
933 entry->bl_entry_type = entry_type;
934 osi_strlcpy(entry->entry_type.name_entry.name, key, sizeof(entry->entry_type.name_entry.name));
935 entry->entry_type.name_entry.feature = (interop_feature_t)feature;
936 entry->entry_type.name_entry.length = strlen(key);
937 interop_database_add_(entry, false);
938
939 } else if (!strncasecmp(value, MNFR_BASED, strlen(MNFR_BASED))) {
940 uint16_t manufacturer;
941
942 if (strlen(key) != VALID_MNFR_STR_LEN) {
943 log::warn("ignoring {} due to invalid Manufacturer id in config file", key);
944 return false;
945 }
946
947 if (token_to_ul((char*)key, &manufacturer) == false) {
948 return false;
949 }
950
951 interop_db_entry_t* entry = (interop_db_entry_t*)osi_calloc(sizeof(interop_db_entry_t));
952 entry->bl_type = INTEROP_BL_TYPE_MANUFACTURE;
953 entry->bl_entry_type = entry_type;
954 entry->entry_type.mnfr_entry.feature = (interop_feature_t)feature;
955 entry->entry_type.mnfr_entry.manufacturer = manufacturer;
956 interop_database_add_(entry, false);
957
958 } else if (!strncasecmp(value, VNDR_PRDT_BASED, strlen(VNDR_PRDT_BASED))) {
959 uint16_t vendor_id;
960 uint16_t product_id = 0;
961 char tmp_key[VALID_VNDR_PRDT_LEN + 1] = {'\0'};
962
963 if (strlen(key) != VALID_VNDR_PRDT_LEN) {
964 log::warn("ignoring {} due to invalid vendor/product id in config file", key);
965 return false;
966 }
967
968 osi_strlcpy(tmp_key, key, VALID_VNDR_PRDT_LEN + 1);
969 if (!get_vendor_product_id(tmp_key, &vendor_id, &product_id)) {
970 log::warn("Error in parsing vendor/product id {}", key);
971 return false;
972 }
973
974 interop_db_entry_t* entry = (interop_db_entry_t*)osi_calloc(sizeof(interop_db_entry_t));
975 entry->bl_type = INTEROP_BL_TYPE_VNDR_PRDT;
976 entry->bl_entry_type = entry_type;
977 entry->entry_type.vnr_pdt_entry.feature = (interop_feature_t)feature;
978 entry->entry_type.vnr_pdt_entry.vendor_id = vendor_id;
979 entry->entry_type.vnr_pdt_entry.product_id = product_id;
980 interop_database_add_(entry, false);
981 } else if (!strncasecmp(value, SSR_MAX_LAT_BASED, strlen(SSR_MAX_LAT_BASED))) {
982 uint16_t max_lat;
983 char tmp_key[KEY_MAX_LENGTH] = {'\0'};
984 char bdaddr_str[KEY_MAX_LENGTH] = {'\0'};
985
986 if (strlen(key) != VALID_SSR_LAT_LEN) {
987 log::warn("ignoring {} due to invalid key for ssr max lat in config file", key);
988 return false;
989 }
990
991 osi_strlcpy(tmp_key, key, KEY_MAX_LENGTH);
992 if (!get_addr_maxlat(tmp_key, bdaddr_str, &max_lat)) {
993 log::warn("Error in parsing address and max_lat {}", key);
994 return false;
995 }
996
997 int len = 0;
998
999 len = (strlen(bdaddr_str) + 1) / 3;
1000 if (len != 3) {
1001 log::warn("Ignoring as invalid entry for Address {}", bdaddr_str);
1002 return false;
1003 }
1004
1005 std::string bdstr(bdaddr_str);
1006 std::string append_str(":00:00:00");
1007 RawAddress addr;
1008
1009 bdstr.append(append_str);
1010
1011 if (!RawAddress::FromString(bdstr, addr)) {
1012 log::warn("key {} or Bluetooth Address {} is invalid, not added to interop list", key, addr);
1013 return false;
1014 }
1015
1016 interop_db_entry_t* entry = (interop_db_entry_t*)osi_calloc(sizeof(interop_db_entry_t));
1017 entry->bl_type = INTEROP_BL_TYPE_SSR_MAX_LAT;
1018 entry->bl_entry_type = entry_type;
1019 entry->entry_type.ssr_max_lat_entry.feature = (interop_feature_t)feature;
1020 entry->entry_type.ssr_max_lat_entry.addr = addr;
1021 entry->entry_type.ssr_max_lat_entry.max_lat = max_lat;
1022 interop_database_add_(entry, false);
1023 } else if (!strncasecmp(value, VERSION_BASED, strlen(VERSION_BASED))) {
1024 uint16_t version;
1025
1026 if (strlen(key) != VALID_VERSION_LEN) {
1027 log::warn("ignoring {} due to invalid version in config file", key);
1028 return false;
1029 }
1030
1031 if (token_to_ul((char*)key, &version) == false) {
1032 return false;
1033 }
1034
1035 interop_db_entry_t* entry = (interop_db_entry_t*)osi_calloc(sizeof(interop_db_entry_t));
1036 entry->bl_type = INTEROP_BL_TYPE_VERSION;
1037 entry->bl_entry_type = entry_type;
1038 entry->entry_type.version_entry.feature = (interop_feature_t)feature;
1039 entry->entry_type.version_entry.version = version;
1040 interop_database_add_(entry, false);
1041 } else if (!strncasecmp(value, LMP_VERSION_BASED, strlen(LMP_VERSION_BASED))) {
1042 uint8_t lmp_ver;
1043 uint16_t lmp_sub_ver;
1044 char tmp_key[KEY_MAX_LENGTH] = {'\0'};
1045 char bdaddr_str[KEY_MAX_LENGTH] = {'\0'};
1046
1047 if (strlen(key) != VALID_LMP_VERSION_LEN) {
1048 log::warn("ignoring {} due to invalid key for lmp ver in config file", key);
1049 return false;
1050 }
1051
1052 osi_strlcpy(tmp_key, key, KEY_MAX_LENGTH);
1053 if (!get_addr_lmp_ver(tmp_key, bdaddr_str, &lmp_ver, &lmp_sub_ver)) {
1054 log::warn("Error in parsing address and lmp_ver {}", key);
1055 return false;
1056 }
1057
1058 int len = 0;
1059
1060 len = (strlen(bdaddr_str) + 1) / 3;
1061 if (len != 3) {
1062 log::warn("Ignoring as invalid entry for Address {}", bdaddr_str);
1063 return false;
1064 }
1065
1066 std::string bdstr(key);
1067 std::string append_str(":00:00:00");
1068 RawAddress addr;
1069
1070 bdstr.append(append_str);
1071
1072 if (!RawAddress::FromString(bdstr, addr)) {
1073 log::warn("key {} or Bluetooth Address {} is invalid, not added to interop list", key, addr);
1074 return false;
1075 }
1076
1077 interop_db_entry_t* entry = (interop_db_entry_t*)osi_calloc(sizeof(interop_db_entry_t));
1078 entry->bl_type = INTEROP_BL_TYPE_LMP_VERSION;
1079 entry->bl_entry_type = entry_type;
1080 entry->entry_type.lmp_version_entry.feature = (interop_feature_t)feature;
1081 entry->entry_type.lmp_version_entry.addr = addr;
1082 entry->entry_type.lmp_version_entry.lmp_ver = lmp_ver;
1083 entry->entry_type.lmp_version_entry.lmp_sub_ver = lmp_sub_ver;
1084 interop_database_add_(entry, false);
1085 } else if (!strncasecmp(value, ADDR_RANGE_BASED, strlen(ADDR_RANGE_BASED))) {
1086 RawAddress addr_start;
1087 RawAddress addr_end;
1088 char tmp_key[KEY_MAX_LENGTH] = {'\0'};
1089
1090 if (strlen(key) != VALID_ADDR_RANGE_LEN) {
1091 log::warn("Ignoring as invalid entry for Address range {}", key);
1092 return false;
1093 }
1094
1095 osi_strlcpy(tmp_key, key, VALID_ADDR_RANGE_LEN + 1);
1096 if (!get_addr_range(tmp_key, &addr_start, &addr_end)) {
1097 log::warn("key: {} addr_start {} or addr end {} is added to interop list", key, addr_start,
1098 addr_end);
1099
1100 return false;
1101 }
1102
1103 interop_db_entry_t* entry = (interop_db_entry_t*)osi_calloc(sizeof(interop_db_entry_t));
1104 entry->bl_type = INTEROP_BL_TYPE_ADDR_RANGE;
1105 entry->bl_entry_type = entry_type;
1106 entry->entry_type.addr_range_entry.addr_start = addr_start;
1107 entry->entry_type.addr_range_entry.addr_end = addr_end;
1108 entry->entry_type.addr_range_entry.feature = (interop_feature_t)feature;
1109 interop_database_add_(entry, false);
1110 }
1111
1112 log::verbose("feature:: {}, key :: {}, value :: {}", feature, key, value);
1113 return true;
1114 }
1115
load_config()1116 static void load_config() {
1117 int init_status = interop_config_init();
1118
1119 if (init_status == -1) {
1120 log::error("Error in initializing interop static config file");
1121 return;
1122 }
1123
1124 pthread_mutex_lock(&file_lock);
1125 for (const section_t& sec : config_static.get()->sections) {
1126 int feature = -1;
1127 if ((feature = interop_feature_name_to_feature_id(sec.name.c_str())) != -1) {
1128 for (const entry_t& entry : sec.entries) {
1129 load_to_database(feature, entry.key.c_str(), entry.value.c_str(),
1130 INTEROP_ENTRY_TYPE_STATIC);
1131 }
1132 }
1133 }
1134 // We no longer need the static config file
1135 config_static.reset();
1136
1137 for (const section_t& sec : config_dynamic.get()->sections) {
1138 int feature = -1;
1139 if ((feature = interop_feature_name_to_feature_id(sec.name.c_str())) != -1) {
1140 for (const entry_t& entry : sec.entries) {
1141 load_to_database(feature, entry.key.c_str(), entry.value.c_str(),
1142 INTEROP_ENTRY_TYPE_DYNAMIC);
1143 }
1144 }
1145 }
1146 pthread_mutex_unlock(&file_lock);
1147 }
1148
interop_config_cleanup(void)1149 static void interop_config_cleanup(void) {
1150 interop_config_flush();
1151
1152 pthread_mutex_lock(&file_lock);
1153 config_static.reset();
1154 config_dynamic.reset();
1155 pthread_mutex_unlock(&file_lock);
1156 pthread_mutex_destroy(&file_lock);
1157 }
1158
interop_database_add_addr(const uint16_t feature,const RawAddress * addr,size_t length)1159 void interop_database_add_addr(const uint16_t feature, const RawAddress* addr, size_t length) {
1160 log::assert_that(addr != nullptr, "assert failed: addr != nullptr");
1161 log::assert_that(length > 0, "assert failed: length > 0");
1162 log::assert_that(length < sizeof(RawAddress), "assert failed: length < sizeof(RawAddress)");
1163
1164 interop_db_entry_t* entry = (interop_db_entry_t*)osi_calloc(sizeof(interop_db_entry_t));
1165 entry->bl_type = INTEROP_BL_TYPE_ADDR;
1166 entry->bl_entry_type = INTEROP_ENTRY_TYPE_DYNAMIC;
1167 memcpy(&entry->entry_type.addr_entry.addr, addr, length);
1168 entry->entry_type.addr_entry.feature = (interop_feature_t)feature;
1169 entry->entry_type.addr_entry.length = length;
1170 interop_database_add_(entry, true);
1171 }
1172
interop_database_add_name(const uint16_t feature,const char * name)1173 void interop_database_add_name(const uint16_t feature, const char* name) {
1174 log::assert_that(name != nullptr, "assert failed: name != nullptr");
1175 const size_t name_length = strlen(name);
1176 log::assert_that(name_length < KEY_MAX_LENGTH, "assert failed: name_length < KEY_MAX_LENGTH");
1177
1178 interop_db_entry_t* entry = (interop_db_entry_t*)osi_calloc(sizeof(interop_db_entry_t));
1179 entry->bl_type = INTEROP_BL_TYPE_NAME;
1180 entry->bl_entry_type = INTEROP_ENTRY_TYPE_DYNAMIC;
1181 osi_strlcpy(entry->entry_type.name_entry.name, name, sizeof(entry->entry_type.name_entry.name));
1182 entry->entry_type.name_entry.feature = (interop_feature_t)feature;
1183 entry->entry_type.name_entry.length = name_length;
1184 interop_database_add_(entry, true);
1185 }
1186
interop_database_add_manufacturer(const interop_feature_t feature,uint16_t manufacturer)1187 void interop_database_add_manufacturer(const interop_feature_t feature, uint16_t manufacturer) {
1188 interop_db_entry_t* entry = (interop_db_entry_t*)osi_calloc(sizeof(interop_db_entry_t));
1189 entry->bl_type = INTEROP_BL_TYPE_MANUFACTURE;
1190 entry->bl_entry_type = INTEROP_ENTRY_TYPE_DYNAMIC;
1191 entry->entry_type.mnfr_entry.feature = feature;
1192 entry->entry_type.mnfr_entry.manufacturer = manufacturer;
1193 interop_database_add_(entry, true);
1194 }
1195
interop_database_add_vndr_prdt(const interop_feature_t feature,uint16_t vendor_id,uint16_t product_id)1196 void interop_database_add_vndr_prdt(const interop_feature_t feature, uint16_t vendor_id,
1197 uint16_t product_id) {
1198 interop_db_entry_t* entry = (interop_db_entry_t*)osi_calloc(sizeof(interop_db_entry_t));
1199 entry->bl_type = INTEROP_BL_TYPE_VNDR_PRDT;
1200 entry->bl_entry_type = INTEROP_ENTRY_TYPE_DYNAMIC;
1201 entry->entry_type.vnr_pdt_entry.feature = (interop_feature_t)feature;
1202 entry->entry_type.vnr_pdt_entry.vendor_id = vendor_id;
1203 entry->entry_type.vnr_pdt_entry.product_id = product_id;
1204 interop_database_add_(entry, true);
1205 }
1206
interop_database_add_addr_max_lat(const interop_feature_t feature,const RawAddress * addr,uint16_t max_lat)1207 void interop_database_add_addr_max_lat(const interop_feature_t feature, const RawAddress* addr,
1208 uint16_t max_lat) {
1209 log::assert_that(addr != nullptr, "assert failed: addr != nullptr");
1210
1211 interop_db_entry_t* entry = (interop_db_entry_t*)osi_calloc(sizeof(interop_db_entry_t));
1212 entry->bl_type = INTEROP_BL_TYPE_SSR_MAX_LAT;
1213 entry->bl_entry_type = INTEROP_ENTRY_TYPE_DYNAMIC;
1214 entry->entry_type.ssr_max_lat_entry.addr = *addr;
1215 entry->entry_type.ssr_max_lat_entry.feature = feature;
1216 entry->entry_type.ssr_max_lat_entry.max_lat = max_lat;
1217 interop_database_add_(entry, true);
1218 }
1219
interop_database_add_version(const interop_feature_t feature,uint16_t version)1220 void interop_database_add_version(const interop_feature_t feature, uint16_t version) {
1221 interop_db_entry_t* entry = (interop_db_entry_t*)osi_calloc(sizeof(interop_db_entry_t));
1222 entry->bl_type = INTEROP_BL_TYPE_VERSION;
1223 entry->bl_entry_type = INTEROP_ENTRY_TYPE_DYNAMIC;
1224 entry->entry_type.version_entry.feature = (interop_feature_t)feature;
1225 entry->entry_type.version_entry.version = version;
1226 interop_database_add_(entry, true);
1227 }
1228
interop_database_add_addr_lmp_version(const interop_feature_t feature,const RawAddress * addr,uint8_t lmp_ver,uint16_t lmp_sub_ver)1229 void interop_database_add_addr_lmp_version(const interop_feature_t feature, const RawAddress* addr,
1230 uint8_t lmp_ver, uint16_t lmp_sub_ver) {
1231 log::assert_that(addr != nullptr, "assert failed: addr != nullptr");
1232
1233 interop_db_entry_t* entry = (interop_db_entry_t*)osi_calloc(sizeof(interop_db_entry_t));
1234 entry->bl_type = INTEROP_BL_TYPE_LMP_VERSION;
1235 entry->bl_entry_type = INTEROP_ENTRY_TYPE_DYNAMIC;
1236 entry->entry_type.lmp_version_entry.addr = *addr;
1237 entry->entry_type.lmp_version_entry.feature = feature;
1238 entry->entry_type.lmp_version_entry.lmp_ver = lmp_ver;
1239 entry->entry_type.lmp_version_entry.lmp_sub_ver = lmp_sub_ver;
1240 interop_database_add_(entry, true);
1241 }
1242
interop_database_match_manufacturer(const interop_feature_t feature,uint16_t manufacturer)1243 bool interop_database_match_manufacturer(const interop_feature_t feature, uint16_t manufacturer) {
1244 interop_db_entry_t entry;
1245
1246 entry.bl_type = INTEROP_BL_TYPE_MANUFACTURE;
1247 entry.entry_type.mnfr_entry.feature = feature;
1248 entry.entry_type.mnfr_entry.manufacturer = manufacturer;
1249
1250 if (interop_database_match(
1251 &entry, NULL,
1252 (interop_entry_type)(INTEROP_ENTRY_TYPE_STATIC | INTEROP_ENTRY_TYPE_DYNAMIC))) {
1253 log::warn("Device with manufacturer id: {} is a match for interop workaround {}", manufacturer,
1254 interop_feature_string_(feature));
1255 return true;
1256 }
1257
1258 return false;
1259 }
1260
interop_database_match_name(const interop_feature_t feature,const char * name)1261 bool interop_database_match_name(const interop_feature_t feature, const char* name) {
1262 char trim_name[KEY_MAX_LENGTH] = {'\0'};
1263 log::assert_that(name != nullptr, "assert failed: name != nullptr");
1264
1265 osi_strlcpy(trim_name, name, KEY_MAX_LENGTH);
1266 interop_db_entry_t entry;
1267
1268 entry.bl_type = INTEROP_BL_TYPE_NAME;
1269 osi_strlcpy(entry.entry_type.name_entry.name, trim(trim_name), KEY_MAX_LENGTH);
1270 entry.entry_type.name_entry.feature = (interop_feature_t)feature;
1271 entry.entry_type.name_entry.length = strlen(entry.entry_type.name_entry.name);
1272
1273 if (interop_database_match(
1274 &entry, NULL,
1275 (interop_entry_type)(INTEROP_ENTRY_TYPE_STATIC | INTEROP_ENTRY_TYPE_DYNAMIC))) {
1276 log::warn("Device with name: {} is a match for interop workaround {}", name,
1277 interop_feature_string_(feature));
1278 return true;
1279 }
1280
1281 return false;
1282 }
1283
interop_database_match_addr(const interop_feature_t feature,const RawAddress * addr)1284 bool interop_database_match_addr(const interop_feature_t feature, const RawAddress* addr) {
1285 log::assert_that(addr != nullptr, "assert failed: addr != nullptr");
1286
1287 interop_db_entry_t entry;
1288
1289 entry.bl_type = INTEROP_BL_TYPE_ADDR;
1290 entry.entry_type.addr_entry.addr = *addr;
1291 entry.entry_type.addr_entry.feature = (interop_feature_t)feature;
1292 entry.entry_type.addr_entry.length = sizeof(RawAddress);
1293
1294 if (interop_database_match(
1295 &entry, NULL,
1296 (interop_entry_type)(INTEROP_ENTRY_TYPE_STATIC | INTEROP_ENTRY_TYPE_DYNAMIC))) {
1297 log::warn("Device {} is a match for interop workaround {}.", *addr,
1298 interop_feature_string_(feature));
1299 return true;
1300 }
1301
1302 entry.bl_type = INTEROP_BL_TYPE_ADDR_RANGE;
1303 entry.bl_entry_type = INTEROP_ENTRY_TYPE_STATIC;
1304 entry.entry_type.addr_range_entry.addr_start = *addr;
1305 entry.entry_type.addr_range_entry.feature = (interop_feature_t)feature;
1306
1307 if (interop_database_match(&entry, NULL, (interop_entry_type)(INTEROP_ENTRY_TYPE_STATIC))) {
1308 log::warn("Device {} is a match for interop workaround {}.", *addr,
1309 interop_feature_string_(feature));
1310 return true;
1311 }
1312
1313 return false;
1314 }
1315
interop_database_match_vndr_prdt(const interop_feature_t feature,uint16_t vendor_id,uint16_t product_id)1316 bool interop_database_match_vndr_prdt(const interop_feature_t feature, uint16_t vendor_id,
1317 uint16_t product_id) {
1318 interop_db_entry_t entry;
1319
1320 entry.bl_type = INTEROP_BL_TYPE_VNDR_PRDT;
1321
1322 entry.entry_type.vnr_pdt_entry.feature = (interop_feature_t)feature;
1323 entry.entry_type.vnr_pdt_entry.vendor_id = vendor_id;
1324 entry.entry_type.vnr_pdt_entry.product_id = product_id;
1325 if (interop_database_match(
1326 &entry, NULL,
1327 (interop_entry_type)(INTEROP_ENTRY_TYPE_STATIC | INTEROP_ENTRY_TYPE_DYNAMIC))) {
1328 log::warn("Device with vendor_id: {} product_id: {} is a match for interop workaround {}",
1329 vendor_id, product_id, interop_feature_string_(feature));
1330 return true;
1331 }
1332
1333 return false;
1334 }
1335
interop_database_match_addr_get_max_lat(const interop_feature_t feature,const RawAddress * addr,uint16_t * max_lat)1336 bool interop_database_match_addr_get_max_lat(const interop_feature_t feature,
1337 const RawAddress* addr, uint16_t* max_lat) {
1338 interop_db_entry_t entry;
1339 interop_db_entry_t* ret_entry = NULL;
1340
1341 entry.bl_type = INTEROP_BL_TYPE_SSR_MAX_LAT;
1342
1343 entry.entry_type.ssr_max_lat_entry.feature = feature;
1344 entry.entry_type.ssr_max_lat_entry.addr = *addr;
1345 entry.entry_type.ssr_max_lat_entry.feature = feature;
1346 if (interop_database_match(
1347 &entry, &ret_entry,
1348 (interop_entry_type)(INTEROP_ENTRY_TYPE_STATIC | INTEROP_ENTRY_TYPE_DYNAMIC))) {
1349 log::warn("Device {} is a match for interop workaround {}.", *addr,
1350 interop_feature_string_(feature));
1351 *max_lat = ret_entry->entry_type.ssr_max_lat_entry.max_lat;
1352 return true;
1353 }
1354
1355 return false;
1356 }
1357
interop_database_match_version(const interop_feature_t feature,uint16_t version)1358 bool interop_database_match_version(const interop_feature_t feature, uint16_t version) {
1359 interop_db_entry_t entry;
1360
1361 entry.bl_type = INTEROP_BL_TYPE_VERSION;
1362
1363 entry.entry_type.version_entry.feature = (interop_feature_t)feature;
1364 entry.entry_type.version_entry.version = version;
1365 if (interop_database_match(
1366 &entry, NULL,
1367 (interop_entry_type)(INTEROP_ENTRY_TYPE_STATIC | INTEROP_ENTRY_TYPE_DYNAMIC))) {
1368 log::warn("Device with version: 0x{:04x} is a match for interop workaround {}", version,
1369 interop_feature_string_(feature));
1370 return true;
1371 }
1372
1373 return false;
1374 }
1375
interop_database_match_addr_get_lmp_ver(const interop_feature_t feature,const RawAddress * addr,uint8_t * lmp_ver,uint16_t * lmp_sub_ver)1376 bool interop_database_match_addr_get_lmp_ver(const interop_feature_t feature,
1377 const RawAddress* addr, uint8_t* lmp_ver,
1378 uint16_t* lmp_sub_ver) {
1379 interop_db_entry_t entry;
1380 interop_db_entry_t* ret_entry = NULL;
1381
1382 entry.bl_type = INTEROP_BL_TYPE_LMP_VERSION;
1383
1384 entry.entry_type.lmp_version_entry.feature = feature;
1385 entry.entry_type.lmp_version_entry.addr = *addr;
1386 entry.entry_type.lmp_version_entry.feature = feature;
1387 if (interop_database_match(
1388 &entry, &ret_entry,
1389 (interop_entry_type)(INTEROP_ENTRY_TYPE_STATIC | INTEROP_ENTRY_TYPE_DYNAMIC))) {
1390 log::warn("Device {} is a match for interop workaround {}.", *addr,
1391 interop_feature_string_(feature));
1392 *lmp_ver = ret_entry->entry_type.lmp_version_entry.lmp_ver;
1393 *lmp_sub_ver = ret_entry->entry_type.lmp_version_entry.lmp_sub_ver;
1394 return true;
1395 }
1396
1397 return false;
1398 }
1399
interop_database_remove_name(const interop_feature_t feature,const char * name)1400 bool interop_database_remove_name(const interop_feature_t feature, const char* name) {
1401 log::assert_that(name != nullptr, "assert failed: name != nullptr");
1402
1403 interop_db_entry_t entry;
1404
1405 entry.bl_type = INTEROP_BL_TYPE_NAME;
1406 entry.bl_entry_type = INTEROP_ENTRY_TYPE_DYNAMIC;
1407 osi_strlcpy(entry.entry_type.name_entry.name, name, 20);
1408 entry.entry_type.name_entry.feature = (interop_feature_t)feature;
1409 entry.entry_type.name_entry.length = strlen(entry.entry_type.name_entry.name);
1410 if (interop_database_remove_(&entry)) {
1411 log::warn("Device with name: {} is removed from interop workaround {}", name,
1412 interop_feature_string_(feature));
1413 return true;
1414 }
1415
1416 return false;
1417 }
1418
interop_database_remove_manufacturer(const interop_feature_t feature,uint16_t manufacturer)1419 bool interop_database_remove_manufacturer(const interop_feature_t feature, uint16_t manufacturer) {
1420 interop_db_entry_t entry;
1421
1422 entry.bl_type = INTEROP_BL_TYPE_MANUFACTURE;
1423 entry.bl_entry_type = INTEROP_ENTRY_TYPE_DYNAMIC;
1424 entry.entry_type.mnfr_entry.feature = feature;
1425 entry.entry_type.mnfr_entry.manufacturer = manufacturer;
1426 if (interop_database_remove_(&entry)) {
1427 log::warn("Device with manufacturer id: {} is removed from interop workaround {}", manufacturer,
1428 interop_feature_string_(feature));
1429 return true;
1430 }
1431
1432 return false;
1433 }
1434
interop_database_remove_addr(const interop_feature_t feature,const RawAddress * addr)1435 bool interop_database_remove_addr(const interop_feature_t feature, const RawAddress* addr) {
1436 log::assert_that(addr != nullptr, "assert failed: addr != nullptr");
1437
1438 interop_db_entry_t entry;
1439
1440 entry.bl_type = INTEROP_BL_TYPE_ADDR;
1441 entry.bl_entry_type = INTEROP_ENTRY_TYPE_DYNAMIC;
1442 entry.entry_type.addr_entry.addr = *addr;
1443 entry.entry_type.addr_entry.feature = (interop_feature_t)feature;
1444 entry.entry_type.addr_entry.length = sizeof(RawAddress);
1445 if (interop_database_remove_(&entry)) {
1446 log::warn("Device {} is a removed from interop workaround {}.", *addr,
1447 interop_feature_string_(feature));
1448 return true;
1449 }
1450
1451 return false;
1452 }
1453
interop_database_remove_feature(const interop_feature_t feature)1454 bool interop_database_remove_feature(const interop_feature_t feature) {
1455 if (interop_list == NULL || list_length(interop_list) == 0) {
1456 return false;
1457 }
1458
1459 list_node_t* node = list_begin(interop_list);
1460 while (node != list_end(interop_list)) {
1461 interop_db_entry_t* entry = static_cast<interop_db_entry_t*>(list_node(node));
1462 log::assert_that(entry != nullptr, "assert failed: entry != nullptr");
1463
1464 bool entry_match = false;
1465 if (entry->bl_entry_type == INTEROP_ENTRY_TYPE_DYNAMIC) {
1466 switch (entry->bl_type) {
1467 case INTEROP_BL_TYPE_ADDR:
1468 if (entry->entry_type.addr_entry.feature == feature) {
1469 entry_match = true;
1470 }
1471 break;
1472 case INTEROP_BL_TYPE_NAME:
1473 if (entry->entry_type.name_entry.feature == feature) {
1474 entry_match = true;
1475 }
1476 break;
1477 case INTEROP_BL_TYPE_MANUFACTURE:
1478 if (entry->entry_type.mnfr_entry.feature == feature) {
1479 entry_match = true;
1480 }
1481 break;
1482 case INTEROP_BL_TYPE_VNDR_PRDT:
1483 if (entry->entry_type.vnr_pdt_entry.feature == feature) {
1484 entry_match = true;
1485 }
1486 break;
1487 case INTEROP_BL_TYPE_SSR_MAX_LAT:
1488 if (entry->entry_type.ssr_max_lat_entry.feature == feature) {
1489 entry_match = true;
1490 }
1491 break;
1492 case INTEROP_BL_TYPE_VERSION:
1493 if (entry->entry_type.version_entry.feature == feature) {
1494 entry_match = true;
1495 }
1496 break;
1497 case INTEROP_BL_TYPE_LMP_VERSION:
1498 if (entry->entry_type.lmp_version_entry.feature == feature) {
1499 entry_match = true;
1500 }
1501 break;
1502 default:
1503 break;
1504 }
1505 }
1506
1507 node = list_next(node);
1508
1509 if (entry_match) {
1510 pthread_mutex_lock(&interop_list_lock);
1511 list_remove(interop_list, (void*)entry);
1512 pthread_mutex_unlock(&interop_list_lock);
1513 }
1514 }
1515
1516 for (const section_t& sec : config_dynamic.get()->sections) {
1517 if (feature == interop_feature_name_to_feature_id(sec.name.c_str())) {
1518 log::warn("found feature - {}", interop_feature_string_(feature));
1519 interop_config_remove_section(sec.name);
1520 return true;
1521 }
1522 }
1523
1524 return false;
1525 }
1526
interop_database_remove_vndr_prdt(const interop_feature_t feature,uint16_t vendor_id,uint16_t product_id)1527 bool interop_database_remove_vndr_prdt(const interop_feature_t feature, uint16_t vendor_id,
1528 uint16_t product_id) {
1529 interop_db_entry_t entry;
1530
1531 entry.bl_type = INTEROP_BL_TYPE_VNDR_PRDT;
1532 entry.bl_entry_type = INTEROP_ENTRY_TYPE_DYNAMIC;
1533
1534 entry.entry_type.vnr_pdt_entry.feature = (interop_feature_t)feature;
1535 entry.entry_type.vnr_pdt_entry.vendor_id = vendor_id;
1536 entry.entry_type.vnr_pdt_entry.product_id = product_id;
1537
1538 if (interop_database_remove_(&entry)) {
1539 log::warn("Device with vendor_id: {} product_id: {} is removed from interop workaround {}",
1540 vendor_id, product_id, interop_feature_string_(feature));
1541 return true;
1542 }
1543 return false;
1544 }
1545
interop_database_remove_addr_max_lat(const interop_feature_t feature,const RawAddress * addr,uint16_t max_lat)1546 bool interop_database_remove_addr_max_lat(const interop_feature_t feature, const RawAddress* addr,
1547 uint16_t max_lat) {
1548 interop_db_entry_t entry;
1549
1550 entry.bl_type = INTEROP_BL_TYPE_SSR_MAX_LAT;
1551 entry.bl_entry_type = INTEROP_ENTRY_TYPE_DYNAMIC;
1552
1553 entry.entry_type.ssr_max_lat_entry.addr = *addr;
1554 entry.entry_type.ssr_max_lat_entry.feature = feature;
1555 entry.entry_type.ssr_max_lat_entry.max_lat = max_lat;
1556
1557 if (interop_database_remove_(&entry)) {
1558 log::warn("Device {} is a removed from interop workaround {}.", *addr,
1559 interop_feature_string_(feature));
1560 return true;
1561 }
1562 return false;
1563 }
1564
interop_database_remove_version(const interop_feature_t feature,uint16_t version)1565 bool interop_database_remove_version(const interop_feature_t feature, uint16_t version) {
1566 interop_db_entry_t entry;
1567
1568 entry.bl_type = INTEROP_BL_TYPE_VERSION;
1569 entry.bl_entry_type = INTEROP_ENTRY_TYPE_DYNAMIC;
1570
1571 entry.entry_type.version_entry.feature = (interop_feature_t)feature;
1572 entry.entry_type.version_entry.version = version;
1573
1574 if (interop_database_remove_(&entry)) {
1575 log::warn("Device with version: 0x{:04x} is removed from interop workaround {}", version,
1576 interop_feature_string_(feature));
1577 return true;
1578 }
1579 return false;
1580 }
1581
interop_database_remove_addr_lmp_version(const interop_feature_t feature,const RawAddress * addr,uint8_t lmp_ver,uint16_t lmp_sub_ver)1582 bool interop_database_remove_addr_lmp_version(const interop_feature_t feature,
1583 const RawAddress* addr, uint8_t lmp_ver,
1584 uint16_t lmp_sub_ver) {
1585 interop_db_entry_t entry;
1586
1587 entry.bl_type = INTEROP_BL_TYPE_LMP_VERSION;
1588 entry.bl_entry_type = INTEROP_ENTRY_TYPE_DYNAMIC;
1589
1590 entry.entry_type.lmp_version_entry.addr = *addr;
1591 entry.entry_type.lmp_version_entry.feature = feature;
1592 entry.entry_type.lmp_version_entry.lmp_ver = lmp_ver;
1593 entry.entry_type.lmp_version_entry.lmp_sub_ver = lmp_sub_ver;
1594
1595 if (interop_database_remove_(&entry)) {
1596 log::warn("Device {} is a removed from interop workaround {}.", *addr,
1597 interop_feature_string_(feature));
1598 return true;
1599 }
1600 return false;
1601 }
1602