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