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 RawAddress* addr);
42static bool interop_match_dynamic_(const interop_feature_t feature,
43                                   const RawAddress* addr);
44
45// Interface functions
46
47bool interop_match_addr(const interop_feature_t feature,
48                        const RawAddress* addr) {
49  CHECK(addr);
50
51  if (interop_match_fixed_(feature, addr) ||
52      interop_match_dynamic_(feature, addr)) {
53    LOG_WARN(LOG_TAG, "%s() Device %s is a match for interop workaround %s.",
54             __func__, addr->ToString().c_str(),
55             interop_feature_string_(feature));
56    return true;
57  }
58
59  return false;
60}
61
62bool interop_match_name(const interop_feature_t feature, const char* name) {
63  CHECK(name);
64
65  const size_t db_size =
66      sizeof(interop_name_database) / sizeof(interop_name_entry_t);
67  for (size_t i = 0; i != db_size; ++i) {
68    if (feature == interop_name_database[i].feature &&
69        strlen(name) >= interop_name_database[i].length &&
70        strncmp(name, interop_name_database[i].name,
71                interop_name_database[i].length) == 0) {
72      return true;
73    }
74  }
75
76  return false;
77}
78
79void interop_database_add(const uint16_t feature, const RawAddress* addr,
80                          size_t length) {
81  CHECK(addr);
82  CHECK(length > 0);
83  CHECK(length < RawAddress::kLength);
84
85  interop_addr_entry_t* entry = static_cast<interop_addr_entry_t*>(
86      osi_calloc(sizeof(interop_addr_entry_t)));
87  memcpy(&entry->addr, addr, length);
88  entry->feature = static_cast<interop_feature_t>(feature);
89  entry->length = length;
90
91  interop_lazy_init_();
92  list_append(interop_list, entry);
93}
94
95void interop_database_clear() {
96  if (interop_list) list_clear(interop_list);
97}
98
99// Module life-cycle functions
100
101static future_t* interop_clean_up(void) {
102  list_free(interop_list);
103  interop_list = NULL;
104  return future_new_immediate(FUTURE_SUCCESS);
105}
106
107EXPORT_SYMBOL module_t interop_module = {
108    .name = INTEROP_MODULE,
109    .init = NULL,
110    .start_up = NULL,
111    .shut_down = NULL,
112    .clean_up = interop_clean_up,
113    .dependencies = {NULL},
114};
115
116// Local functions
117
118static const char* interop_feature_string_(const interop_feature_t feature) {
119  switch (feature) {
120    CASE_RETURN_STR(INTEROP_DISABLE_LE_SECURE_CONNECTIONS)
121    CASE_RETURN_STR(INTEROP_AUTO_RETRY_PAIRING)
122    CASE_RETURN_STR(INTEROP_DISABLE_ABSOLUTE_VOLUME)
123    CASE_RETURN_STR(INTEROP_DISABLE_AUTO_PAIRING)
124    CASE_RETURN_STR(INTEROP_KEYBOARD_REQUIRES_FIXED_PIN)
125    CASE_RETURN_STR(INTEROP_2MBPS_LINK_ONLY)
126    CASE_RETURN_STR(INTEROP_HID_PREF_CONN_SUP_TIMEOUT_3S)
127    CASE_RETURN_STR(INTEROP_GATTC_NO_SERVICE_CHANGED_IND)
128    CASE_RETURN_STR(INTEROP_DISABLE_AVDTP_RECONFIGURE)
129    CASE_RETURN_STR(INTEROP_DYNAMIC_ROLE_SWITCH)
130    CASE_RETURN_STR(INTEROP_DISABLE_ROLE_SWITCH)
131  }
132
133  return "UNKNOWN";
134}
135
136static void interop_free_entry_(void* data) {
137  interop_addr_entry_t* entry = (interop_addr_entry_t*)data;
138  osi_free(entry);
139}
140
141static void interop_lazy_init_(void) {
142  if (interop_list == NULL) {
143    interop_list = list_new(interop_free_entry_);
144  }
145}
146
147static bool interop_match_dynamic_(const interop_feature_t feature,
148                                   const RawAddress* addr) {
149  if (interop_list == NULL || list_length(interop_list) == 0) return false;
150
151  const list_node_t* node = list_begin(interop_list);
152  while (node != list_end(interop_list)) {
153    interop_addr_entry_t* entry =
154        static_cast<interop_addr_entry_t*>(list_node(node));
155    CHECK(entry);
156
157    if (feature == entry->feature &&
158        memcmp(addr, &entry->addr, entry->length) == 0)
159      return true;
160
161    node = list_next(node);
162  }
163  return false;
164}
165
166static bool interop_match_fixed_(const interop_feature_t feature,
167                                 const RawAddress* addr) {
168  CHECK(addr);
169
170  const size_t db_size =
171      sizeof(interop_addr_database) / sizeof(interop_addr_entry_t);
172  for (size_t i = 0; i != db_size; ++i) {
173    if (feature == interop_addr_database[i].feature &&
174        memcmp(addr, &interop_addr_database[i].addr,
175               interop_addr_database[i].length) == 0) {
176      return true;
177    }
178  }
179
180  return false;
181}
182