btif_config.c revision 47d68ee6229ccec3deeaaf694fa86438d97333a4
1/****************************************************************************** 2 * 3 * Copyright (C) 2014 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_btif_config" 20 21#include <assert.h> 22#include <ctype.h> 23#include <pthread.h> 24#include <stdio.h> 25#include <string.h> 26 27#include "osi/include/alarm.h" 28#include "btcore/include/bdaddr.h" 29#include "btif_config.h" 30#include "btif_config_transcode.h" 31#include "btif_util.h" 32#include "osi/include/compat.h" 33#include "osi/include/config.h" 34#include "btcore/include/module.h" 35#include "osi/include/osi.h" 36#include "osi/include/log.h" 37 38#include "bt_types.h" 39 40static const char *CONFIG_FILE_PATH = "/data/misc/bluedroid/bt_config.conf"; 41static const char *LEGACY_CONFIG_FILE_PATH = "/data/misc/bluedroid/bt_config.xml"; 42static const period_ms_t CONFIG_SETTLE_PERIOD_MS = 3000; 43 44static void timer_config_save(void *data); 45 46// TODO(zachoverflow): Move these two functions out, because they are too specific for this file 47// {grumpy-cat/no, monty-python/you-make-me-sad} 48bool btif_get_device_type(const BD_ADDR bd_addr, int *p_device_type) 49{ 50 if (p_device_type == NULL) 51 return FALSE; 52 53 bt_bdaddr_t bda; 54 bdcpy(bda.address, bd_addr); 55 56 bdstr_t bd_addr_str; 57 bdaddr_to_string(&bda, bd_addr_str, sizeof(bd_addr_str)); 58 59 if (!btif_config_get_int(bd_addr_str, "DevType", p_device_type)) 60 return FALSE; 61 62 LOG_DEBUG("%s: Device [%s] type %d", __FUNCTION__, bd_addr_str, *p_device_type); 63 return TRUE; 64} 65 66bool btif_get_address_type(const BD_ADDR bd_addr, int *p_addr_type) 67{ 68 if (p_addr_type == NULL) 69 return FALSE; 70 71 bt_bdaddr_t bda; 72 bdcpy(bda.address, bd_addr); 73 74 bdstr_t bd_addr_str; 75 bdaddr_to_string(&bda, bd_addr_str, sizeof(bd_addr_str)); 76 77 if (!btif_config_get_int(bd_addr_str, "AddrType", p_addr_type)) 78 return FALSE; 79 80 LOG_DEBUG("%s: Device [%s] address type %d", __FUNCTION__, bd_addr_str, *p_addr_type); 81 return TRUE; 82} 83 84static pthread_mutex_t lock; // protects operations on |config|. 85static config_t *config; 86static alarm_t *alarm_timer; 87 88// Module lifecycle functions 89 90static future_t *init(void) { 91 pthread_mutex_init(&lock, NULL); 92 config = config_new(CONFIG_FILE_PATH); 93 if (!config) { 94 LOG_WARN("%s unable to load config file; attempting to transcode legacy file.", __func__); 95 config = btif_config_transcode(LEGACY_CONFIG_FILE_PATH); 96 if (!config) { 97 LOG_WARN("%s unable to transcode legacy file, starting unconfigured.", __func__); 98 config = config_new_empty(); 99 if (!config) { 100 LOG_ERROR("%s unable to allocate a config object.", __func__); 101 goto error; 102 } 103 } 104 105 if (config_save(config, CONFIG_FILE_PATH)) 106 unlink(LEGACY_CONFIG_FILE_PATH); 107 } 108 109 // TODO(sharvil): use a non-wake alarm for this once we have 110 // API support for it. There's no need to wake the system to 111 // write back to disk. 112 alarm_timer = alarm_new(); 113 if (!alarm_timer) { 114 LOG_ERROR("%s unable to create alarm.", __func__); 115 goto error; 116 } 117 118 return future_new_immediate(FUTURE_SUCCESS); 119 120error:; 121 alarm_free(alarm_timer); 122 config_free(config); 123 pthread_mutex_destroy(&lock); 124 alarm_timer = NULL; 125 config = NULL; 126 return future_new_immediate(FUTURE_FAIL); 127} 128 129static future_t *shut_down(void) { 130 btif_config_flush(); 131 return future_new_immediate(FUTURE_SUCCESS); 132} 133 134static future_t *clean_up(void) { 135 btif_config_flush(); 136 137 alarm_free(alarm_timer); 138 config_free(config); 139 pthread_mutex_destroy(&lock); 140 alarm_timer = NULL; 141 config = NULL; 142 return future_new_immediate(FUTURE_SUCCESS); 143} 144 145const module_t btif_config_module = { 146 .name = BTIF_CONFIG_MODULE, 147 .init = init, 148 .start_up = NULL, 149 .shut_down = shut_down, 150 .clean_up = clean_up, 151 .dependencies = { 152 NULL 153 } 154}; 155 156bool btif_config_has_section(const char *section) { 157 assert(config != NULL); 158 assert(section != NULL); 159 160 pthread_mutex_lock(&lock); 161 bool ret = config_has_section(config, section); 162 pthread_mutex_unlock(&lock); 163 164 return ret; 165} 166 167bool btif_config_exist(const char *section, const char *key) { 168 assert(config != NULL); 169 assert(section != NULL); 170 assert(key != NULL); 171 172 pthread_mutex_lock(&lock); 173 bool ret = config_has_key(config, section, key); 174 pthread_mutex_unlock(&lock); 175 176 return ret; 177} 178 179bool btif_config_get_int(const char *section, const char *key, int *value) { 180 assert(config != NULL); 181 assert(section != NULL); 182 assert(key != NULL); 183 assert(value != NULL); 184 185 pthread_mutex_lock(&lock); 186 bool ret = config_has_key(config, section, key); 187 if (ret) 188 *value = config_get_int(config, section, key, *value); 189 pthread_mutex_unlock(&lock); 190 191 return ret; 192} 193 194bool btif_config_set_int(const char *section, const char *key, int value) { 195 assert(config != NULL); 196 assert(section != NULL); 197 assert(key != NULL); 198 199 pthread_mutex_lock(&lock); 200 config_set_int(config, section, key, value); 201 pthread_mutex_unlock(&lock); 202 203 return true; 204} 205 206bool btif_config_get_str(const char *section, const char *key, char *value, int *size_bytes) { 207 assert(config != NULL); 208 assert(section != NULL); 209 assert(key != NULL); 210 assert(value != NULL); 211 assert(size_bytes != NULL); 212 213 pthread_mutex_lock(&lock); 214 const char *stored_value = config_get_string(config, section, key, NULL); 215 pthread_mutex_unlock(&lock); 216 217 if (!stored_value) 218 return false; 219 220 strlcpy(value, stored_value, *size_bytes); 221 *size_bytes = strlen(value) + 1; 222 223 return true; 224} 225 226bool btif_config_set_str(const char *section, const char *key, const char *value) { 227 assert(config != NULL); 228 assert(section != NULL); 229 assert(key != NULL); 230 assert(value != NULL); 231 232 pthread_mutex_lock(&lock); 233 config_set_string(config, section, key, value); 234 pthread_mutex_unlock(&lock); 235 236 return true; 237} 238 239bool btif_config_get_bin(const char *section, const char *key, uint8_t *value, size_t *length) { 240 assert(config != NULL); 241 assert(section != NULL); 242 assert(key != NULL); 243 assert(value != NULL); 244 assert(length != NULL); 245 246 pthread_mutex_lock(&lock); 247 const char *value_str = config_get_string(config, section, key, NULL); 248 pthread_mutex_unlock(&lock); 249 250 if (!value_str) 251 return false; 252 253 size_t value_len = strlen(value_str); 254 if ((value_len % 2) != 0 || *length < (value_len / 2)) 255 return false; 256 257 for (size_t i = 0; i < value_len; ++i) 258 if (!isxdigit(value_str[i])) 259 return false; 260 261 for (*length = 0; *value_str; value_str += 2, *length += 1) 262 sscanf(value_str, "%02hhx", &value[*length]); 263 264 return true; 265} 266 267size_t btif_config_get_bin_length(const char *section, const char *key) { 268 assert(config != NULL); 269 assert(section != NULL); 270 assert(key != NULL); 271 272 pthread_mutex_lock(&lock); 273 const char *value_str = config_get_string(config, section, key, NULL); 274 pthread_mutex_unlock(&lock); 275 276 if (!value_str) 277 return 0; 278 279 size_t value_len = strlen(value_str); 280 return ((value_len % 2) != 0) ? 0 : (value_len / 2); 281} 282 283bool btif_config_set_bin(const char *section, const char *key, const uint8_t *value, size_t length) { 284 static const char *lookup = "0123456789abcdef"; 285 286 assert(config != NULL); 287 assert(section != NULL); 288 assert(key != NULL); 289 assert(value != NULL); 290 291 char *str = (char *)calloc(length * 2 + 1, 1); 292 if (!str) 293 return false; 294 295 for (size_t i = 0; i < length; ++i) { 296 str[(i * 2) + 0] = lookup[(value[i] >> 4) & 0x0F]; 297 str[(i * 2) + 1] = lookup[(value[i] >> 0) & 0x0F]; 298 } 299 300 pthread_mutex_lock(&lock); 301 config_set_string(config, section, key, str); 302 pthread_mutex_unlock(&lock); 303 304 free(str); 305 return true; 306} 307 308const btif_config_section_iter_t *btif_config_section_begin(void) { 309 assert(config != NULL); 310 return (const btif_config_section_iter_t *)config_section_begin(config); 311} 312 313const btif_config_section_iter_t *btif_config_section_end(void) { 314 assert(config != NULL); 315 return (const btif_config_section_iter_t *)config_section_end(config); 316} 317 318const btif_config_section_iter_t *btif_config_section_next(const btif_config_section_iter_t *section) { 319 assert(config != NULL); 320 assert(section != NULL); 321 return (const btif_config_section_iter_t *)config_section_next((const config_section_node_t *)section); 322} 323 324const char *btif_config_section_name(const btif_config_section_iter_t *section) { 325 assert(config != NULL); 326 assert(section != NULL); 327 return config_section_name((const config_section_node_t *)section); 328} 329 330bool btif_config_remove(const char *section, const char *key) { 331 assert(config != NULL); 332 assert(section != NULL); 333 assert(key != NULL); 334 335 pthread_mutex_lock(&lock); 336 bool ret = config_remove_key(config, section, key); 337 pthread_mutex_unlock(&lock); 338 339 return ret; 340} 341 342void btif_config_save(void) { 343 assert(alarm_timer != NULL); 344 assert(config != NULL); 345 346 alarm_set(alarm_timer, CONFIG_SETTLE_PERIOD_MS, timer_config_save, NULL); 347} 348 349void btif_config_flush(void) { 350 assert(config != NULL); 351 assert(alarm_timer != NULL); 352 353 alarm_cancel(alarm_timer); 354 355 pthread_mutex_lock(&lock); 356 config_save(config, CONFIG_FILE_PATH); 357 pthread_mutex_unlock(&lock); 358} 359 360static void timer_config_save(UNUSED_ATTR void *data) { 361 assert(config != NULL); 362 assert(alarm_timer != NULL); 363 364 // Garbage collection process: the config file accumulates 365 // cached information about remote devices during regular 366 // inquiry scans. We remove some of these junk entries 367 // so the file doesn't grow indefinitely. We have to take care 368 // to make sure we don't remove information about bonded 369 // devices (hence the check for link keys). 370 static const size_t CACHE_MAX = 256; 371 const char *keys[CACHE_MAX]; 372 size_t num_keys = 0; 373 size_t total_candidates = 0; 374 375 pthread_mutex_lock(&lock); 376 for (const config_section_node_t *snode = config_section_begin(config); snode != config_section_end(config); snode = config_section_next(snode)) { 377 const char *section = config_section_name(snode); 378 if (!string_is_bdaddr(section)) 379 continue; 380 381 if (config_has_key(config, section, "LinkKey") || 382 config_has_key(config, section, "LE_KEY_PENC") || 383 config_has_key(config, section, "LE_KEY_PID") || 384 config_has_key(config, section, "LE_KEY_PCSRK") || 385 config_has_key(config, section, "LE_KEY_LENC") || 386 config_has_key(config, section, "LE_KEY_LCSRK")) 387 continue; 388 389 if (num_keys < CACHE_MAX) 390 keys[num_keys++] = section; 391 392 ++total_candidates; 393 } 394 395 if (total_candidates > CACHE_MAX * 2) 396 while (num_keys > 0) 397 config_remove_section(config, keys[--num_keys]); 398 399 config_save(config, CONFIG_FILE_PATH); 400 pthread_mutex_unlock(&lock); 401} 402