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