1/******************************************************************************
2 *
3 *  Copyright (C) 2015 Google, Inc.
4 *
5 *  Licensed under the Apache License, Version 2.0 (the "License");
6 *  you may not use this file except in compliance with the License.
7 *  You may obtain a copy of the License at:
8 *
9 *  http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 *
17 ******************************************************************************/
18
19#define LOG_TAG "bt_device_interop"
20
21#include <base/logging.h>
22#include <string.h>  // For memcmp
23
24#include "btcore/include/module.h"
25#include "device/include/interop.h"
26#include "device/include/interop_database.h"
27#include "osi/include/allocator.h"
28#include "osi/include/list.h"
29#include "osi/include/log.h"
30
31#define CASE_RETURN_STR(const) \
32  case const:                  \
33    return #const;
34
35static list_t* interop_list = NULL;
36
37static const char* interop_feature_string_(const interop_feature_t feature);
38static void interop_free_entry_(void* data);
39static void interop_lazy_init_(void);
40static bool interop_match_fixed_(const interop_feature_t feature,
41                                 const bt_bdaddr_t* addr);
42static bool interop_match_dynamic_(const interop_feature_t feature,
43                                   const bt_bdaddr_t* addr);
44
45// Interface functions
46
47bool interop_match_addr(const interop_feature_t feature,
48                        const bt_bdaddr_t* addr) {
49  CHECK(addr);
50
51  if (interop_match_fixed_(feature, addr) ||
52      interop_match_dynamic_(feature, addr)) {
53    char bdstr[20] = {0};
54    LOG_WARN(LOG_TAG, "%s() Device %s is a match for interop workaround %s.",
55             __func__, bdaddr_to_string(addr, bdstr, sizeof(bdstr)),
56             interop_feature_string_(feature));
57    return true;
58  }
59
60  return false;
61}
62
63bool interop_match_name(const interop_feature_t feature, const char* name) {
64  CHECK(name);
65
66  const size_t db_size =
67      sizeof(interop_name_database) / sizeof(interop_name_entry_t);
68  for (size_t i = 0; i != db_size; ++i) {
69    if (feature == interop_name_database[i].feature &&
70        strlen(name) >= interop_name_database[i].length &&
71        strncmp(name, interop_name_database[i].name,
72                interop_name_database[i].length) == 0) {
73      return true;
74    }
75  }
76
77  return false;
78}
79
80void interop_database_add(const uint16_t feature, const bt_bdaddr_t* addr,
81                          size_t length) {
82  CHECK(addr);
83  CHECK(length > 0);
84  CHECK(length < sizeof(bt_bdaddr_t));
85
86  interop_addr_entry_t* entry = static_cast<interop_addr_entry_t*>(
87      osi_calloc(sizeof(interop_addr_entry_t)));
88  memcpy(&entry->addr, addr, length);
89  entry->feature = static_cast<interop_feature_t>(feature);
90  entry->length = length;
91
92  interop_lazy_init_();
93  list_append(interop_list, entry);
94}
95
96void interop_database_clear() {
97  if (interop_list) list_clear(interop_list);
98}
99
100// Module life-cycle functions
101
102static future_t* interop_clean_up(void) {
103  list_free(interop_list);
104  interop_list = NULL;
105  return future_new_immediate(FUTURE_SUCCESS);
106}
107
108EXPORT_SYMBOL module_t interop_module = {
109    .name = INTEROP_MODULE,
110    .init = NULL,
111    .start_up = NULL,
112    .shut_down = NULL,
113    .clean_up = interop_clean_up,
114    .dependencies = {NULL},
115};
116
117// Local functions
118
119static const char* interop_feature_string_(const interop_feature_t feature) {
120  switch (feature) {
121    CASE_RETURN_STR(INTEROP_DISABLE_LE_SECURE_CONNECTIONS)
122    CASE_RETURN_STR(INTEROP_AUTO_RETRY_PAIRING)
123    CASE_RETURN_STR(INTEROP_DISABLE_ABSOLUTE_VOLUME)
124    CASE_RETURN_STR(INTEROP_DISABLE_AUTO_PAIRING)
125    CASE_RETURN_STR(INTEROP_KEYBOARD_REQUIRES_FIXED_PIN)
126    CASE_RETURN_STR(INTEROP_2MBPS_LINK_ONLY)
127    CASE_RETURN_STR(INTEROP_HID_PREF_CONN_SUP_TIMEOUT_3S)
128    CASE_RETURN_STR(INTEROP_GATTC_NO_SERVICE_CHANGED_IND)
129    CASE_RETURN_STR(INTEROP_DISABLE_AVDTP_RECONFIGURE)
130  }
131
132  return "UNKNOWN";
133}
134
135static void interop_free_entry_(void* data) {
136  interop_addr_entry_t* entry = (interop_addr_entry_t*)data;
137  osi_free(entry);
138}
139
140static void interop_lazy_init_(void) {
141  if (interop_list == NULL) {
142    interop_list = list_new(interop_free_entry_);
143  }
144}
145
146static bool interop_match_dynamic_(const interop_feature_t feature,
147                                   const bt_bdaddr_t* addr) {
148  if (interop_list == NULL || list_length(interop_list) == 0) return false;
149
150  const list_node_t* node = list_begin(interop_list);
151  while (node != list_end(interop_list)) {
152    interop_addr_entry_t* entry =
153        static_cast<interop_addr_entry_t*>(list_node(node));
154    CHECK(entry);
155
156    if (feature == entry->feature &&
157        memcmp(addr, &entry->addr, entry->length) == 0)
158      return true;
159
160    node = list_next(node);
161  }
162  return false;
163}
164
165static bool interop_match_fixed_(const interop_feature_t feature,
166                                 const bt_bdaddr_t* addr) {
167  CHECK(addr);
168
169  const size_t db_size =
170      sizeof(interop_addr_database) / sizeof(interop_addr_entry_t);
171  for (size_t i = 0; i != db_size; ++i) {
172    if (feature == interop_addr_database[i].feature &&
173        memcmp(addr, &interop_addr_database[i].addr,
174               interop_addr_database[i].length) == 0) {
175      return true;
176    }
177  }
178
179  return false;
180}
181