/****************************************************************************** * * Copyright (C) 2009-2012 Broadcom Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ******************************************************************************/ /************************************************************************************ * * Filename: btif_config.c * * Description: Stores the local BT adapter and remote device properties in * NVRAM storage, typically as xml file in the * mobile's filesystem * * ***********************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define LOG_TAG "btif_config" #include #include "data_types.h" #include "bd.h" #include "btif_api.h" #include "btif_config.h" #include "btif_config_util.h" #include "btif_sock_thread.h" #include "btif_sock_util.h" #include "btif_util.h" //#define UNIT_TEST #define CFG_PATH "/data/misc/bluedroid/" #define CFG_FILE_NAME "bt_config" #define CFG_FILE_EXT ".xml" #define CFG_FILE_EXT_OLD ".old" #define CFG_FILE_EXT_NEW ".new" #define CFG_GROW_SIZE (10*sizeof(cfg_node)) #define GET_CHILD_MAX_COUNT(node) (short)((int)(node)->bytes / sizeof(cfg_node)) #define GET_CHILD_COUNT(p) (short)((int)(p)->used / sizeof(cfg_node)) #define ADD_CHILD_COUNT(p, c) (p)->used += (short)((c)*sizeof(cfg_node)) #define DEC_CHILD_COUNT(p, c) (p)->used -= (short)((c)*sizeof(cfg_node)) #define GET_NODE_COUNT(bytes) (bytes / sizeof(cfg_node)) #define GET_NODE_BYTES(c) (c * sizeof(cfg_node)) #define MAX_NODE_BYTES 32000 #define CFG_CMD_SAVE 1 #ifndef FALSE #define TRUE 1 #define FALSE 0 #endif typedef struct cfg_node_s { const char* name; union { struct cfg_node_s* child; char* value; }; short bytes; short type; short used; short flag; } cfg_node; static pthread_mutex_t slot_lock; static int pth = -1; //poll thread handle static cfg_node root; static int cached_change; static int save_cmds_queued; static void cfg_cmd_callback(int cmd_fd, int type, int flags, uint32_t user_id); static inline short alloc_node(cfg_node* p, short grow); static inline void free_node(cfg_node* p); static inline short find_inode(const cfg_node* p, const char* name); static cfg_node* find_node(const char* section, const char* key, const char* name); static int remove_node(const char* section, const char* key, const char* name); static int remove_filter_node(const char* section, const char* filter[], int filter_count, int max_allowed); static inline cfg_node* find_free_node(cfg_node* p); static int set_node(const char* section, const char* key, const char* name, const char* value, short bytes, short type); static int save_cfg(); static void load_cfg(); static short find_next_node(const cfg_node* p, short start, char* name, int* bytes); #ifdef UNIT_TEST static void cfg_test_load(); static void cfg_test_write(); static void cfg_test_read(); #endif #define MY_LOG_LEVEL appl_trace_level #define MY_LOG_LAYER TRACE_LAYER_NONE | TRACE_ORG_APPL static inline void dump_node(const char* title, const cfg_node* p) { if(p) { bdld("%s, p->name:%s, child/value:%p, bytes:%d", title, p->name, p->child, p->bytes); bdld("p->used:%d, type:%x, p->flag:%d", p->used, p->type, p->flag); } else bdld("%s is NULL", title); } //////////////////////////////////////////////////////////////////////////////////////////////////////////// int btif_config_init() { static int initialized; bdld("in initialized:%d", initialized); if(!initialized) { initialized = 1; struct stat st; if(stat(CFG_PATH, &st) != 0) bdle("%s does not exist, need provision", CFG_PATH); btsock_thread_init(); init_slot_lock(&slot_lock); lock_slot(&slot_lock); root.name = "Bluedroid"; alloc_node(&root, CFG_GROW_SIZE); dump_node("root", &root); pth = btsock_thread_create(NULL, cfg_cmd_callback); load_cfg(); unlock_slot(&slot_lock); #ifdef UNIT_TEST cfg_test_write(); //cfg_test_read(); exit(0); #endif } return pth >= 0; } int btif_config_get_int(const char* section, const char* key, const char* name, int* value) { int size = sizeof(*value); int type = BTIF_CFG_TYPE_INT; return btif_config_get(section, key, name, (char*)value, &size, &type); } int btif_config_set_int(const char* section, const char* key, const char* name, int value) { return btif_config_set(section, key, name, (char*)&value, sizeof(value), BTIF_CFG_TYPE_INT); } int btif_config_get_str(const char* section, const char* key, const char* name, char* value, int* size) { int type = BTIF_CFG_TYPE_STR; if(value) *value = 0; return btif_config_get(section, key, name, value, size, &type); } int btif_config_set_str(const char* section, const char* key, const char* name, const char* value) { value = value ? value : ""; return btif_config_set(section, key, name, value, strlen(value) + 1, BTIF_CFG_TYPE_STR); } int btif_config_exist(const char* section, const char* key, const char* name) { int ret = FALSE; if(section && *section && key && *key) { lock_slot(&slot_lock); ret = find_node(section, key, name) != NULL; unlock_slot(&slot_lock); } return ret; } int btif_config_get(const char* section, const char* key, const char* name, char* value, int* bytes, int* type) { int ret = FALSE; bdla(section && *section && key && *key && name && *name && bytes && type); bdld("section:%s, key:%s, name:%s, value:%p, bytes:%d, type:%d", section, key, name, value, *bytes, *type); if(section && *section && key && *key && name && *name && bytes && type) { lock_slot(&slot_lock); const cfg_node* node = find_node(section, key, name); dump_node("found node", node); if(node) { if(*type == node->type && value && *bytes >= node->used) { if(node->used > 0) memcpy(value, node->value, node->used); ret = TRUE; } *type = node->type; *bytes = node->used; if(ret != TRUE) { if(*type != node->type) bdle("value:%s, wrong type:%d, need to be type: %d", name, *type, node->type); if(value && *bytes < node->used) bdle("value:%s, not enough size: %d bytes, need %d bytes", name, node->used, *bytes); } } unlock_slot(&slot_lock); } return ret; } int btif_config_set(const char* section, const char* key, const char* name, const char* value, int bytes, int type) { int ret = FALSE; bdla(section && *section && key && *key && name && *name); bdla(bytes < MAX_NODE_BYTES); if(section && *section && key && *key && name && *name && bytes < MAX_NODE_BYTES) { lock_slot(&slot_lock); ret = set_node(section, key, name, value, (short)bytes, (short)type); if(ret && !(type & BTIF_CFG_TYPE_VOLATILE)) cached_change++; unlock_slot(&slot_lock); } return ret; } int btif_config_remove(const char* section, const char* key, const char* name) { bdla(section && *section && key && *key); bdld("section:%s, key:%s, name:%s", section, key, name); int ret = FALSE; if(section && *section && key && *key) { lock_slot(&slot_lock); ret = remove_node(section, key, name); if(ret) cached_change++; unlock_slot(&slot_lock); } return ret; } int btif_config_filter_remove(const char* section, const char* filter[], int filter_count, int max_allowed) { bdla(section && *section && max_allowed > 0); bdld("section:%s, filter:%s, filter count:%d, max allowed:%d", section, filter[0], filter_count, max_allowed); int ret = FALSE; if(section && *section && max_allowed > 0) { lock_slot(&slot_lock); ret = remove_filter_node(section, filter, filter_count, max_allowed); if(ret) cached_change++; unlock_slot(&slot_lock); } return ret; } typedef struct { short si; short ki; short vi; short reserved; } cfg_node_pos; short btif_config_next_key(short pos, const char* section, char * name, int* bytes) { int next = -1; lock_slot(&slot_lock); short si = find_inode(&root, section); if(si >= 0) { const cfg_node* section_node = &root.child[si]; next = find_next_node(section_node, pos, name, bytes); } unlock_slot(&slot_lock); return next; } short btif_config_next_value(short pos, const char* section, const char* key, char* name, int* bytes) { int next = -1; lock_slot(&slot_lock); short si = find_inode(&root, section); if(si >= 0) { const cfg_node* section_node = &root.child[si]; short ki = find_inode(section_node, key); if(ki >= 0) { const cfg_node* key_node = §ion_node->child[ki]; next = find_next_node(key_node, pos, name, bytes); } } unlock_slot(&slot_lock); return next; } int btif_config_enum(btif_config_enum_callback cb, void* user_data) { bdla(cb); if(!cb) return FALSE; lock_slot(&slot_lock); int si, ki, vi; cfg_node *section_node, *key_node, *value_node; for(si = 0; si < GET_CHILD_COUNT(&root); si++) { section_node = &root.child[si]; if(section_node->name && *section_node->name) { for(ki = 0; ki < GET_CHILD_COUNT(section_node); ki++) { key_node = §ion_node->child[ki]; if(key_node->name && *key_node->name) { for(vi = 0; vi < GET_CHILD_COUNT(key_node); vi++) { value_node = &key_node->child[vi]; if(value_node->name && *value_node->name) { cb(user_data, section_node->name, key_node->name, value_node->name, value_node->value, value_node->used, value_node->type); } } } } } } unlock_slot(&slot_lock); return TRUE; } int btif_config_save() { int post_cmd = 0; lock_slot(&slot_lock); bdld("save_cmds_queued:%d, cached_change:%d", save_cmds_queued, cached_change); if((save_cmds_queued == 0) && (cached_change > 0)) { post_cmd = 1; save_cmds_queued++; bdld("post_cmd set to 1, save_cmds_queued:%d", save_cmds_queued); } unlock_slot(&slot_lock); /* don't hold lock when invoking send or else a deadlock could * occur when the socket thread tries to do the actual saving. */ if (post_cmd) btsock_thread_post_cmd(pth, CFG_CMD_SAVE, NULL, 0, 0); return TRUE; } void btif_config_flush() { lock_slot(&slot_lock); if(cached_change > 0) save_cfg(); unlock_slot(&slot_lock); } /******************************************************************************* * Device information *******************************************************************************/ BOOLEAN btif_get_device_type(const BD_ADDR bd_addr, int *p_device_type) { if (p_device_type == NULL) return FALSE; bt_bdaddr_t bda; bdcpy(bda.address, bd_addr); char bd_addr_str[18] = {0}; bd2str(&bda, &bd_addr_str); if (!btif_config_get_int("Remote", bd_addr_str, "DevType", p_device_type)) return FALSE; ALOGD("%s: Device [%s] type %d", __FUNCTION__, bd_addr_str, *p_device_type); return TRUE; } BOOLEAN btif_get_address_type(const BD_ADDR bd_addr, int *p_addr_type) { if (p_addr_type == NULL) return FALSE; bt_bdaddr_t bda; bdcpy(bda.address, bd_addr); char bd_addr_str[18] = {0}; bd2str(&bda, &bd_addr_str); if (!btif_config_get_int("Remote", bd_addr_str, "AddrType", p_addr_type)) return FALSE; ALOGD("%s: Device [%s] address type %d", __FUNCTION__, bd_addr_str, *p_addr_type); return TRUE; } ///////////////////////////////////////////////////////////////////////////////////////////// static inline short alloc_node(cfg_node* p, short grow) { int new_bytes = p->bytes + grow; if(grow > 0 && new_bytes < MAX_NODE_BYTES) { char* value = (char*)realloc(p->value, new_bytes); if(value) { short old_bytes = p->bytes; //clear to zero memset(value + old_bytes, 0, grow); p->bytes = old_bytes + grow; p->value = value; return old_bytes;//return the previous size } else bdle("realloc failed, old_bytes:%d, grow:%d, total:%d", p->bytes, grow, p->bytes + grow); } return -1; } static inline void free_node(cfg_node* p) { if(p) { if(p->child) { free(p->child); p->child = NULL; } if(p->name) { free((void*)p->name); p->name = 0; } p->used = p->bytes = p->flag = p->type = 0; } } static inline short find_inode(const cfg_node* p, const char* name) { if(p && p->child && name && *name) { int i; int count = GET_CHILD_COUNT(p); //bdld("parent name:%s, child name:%s, child count:%d", p->name, name, count); for(i = 0; i < count; i++) { if(p->child[i].name && *p->child[i].name && strcmp(p->child[i].name, name) == 0) { return (short)i; } } } return -1; } static inline cfg_node* find_free_node(cfg_node* p) { if(p && p->child) { int count = GET_CHILD_COUNT(p); if(count < GET_CHILD_MAX_COUNT(p)) return p->child + count; } return NULL; } static cfg_node* find_add_node(cfg_node* p, const char* name) { int i = -1; cfg_node* node = NULL; if((i = find_inode(p, name)) < 0) { if(!(node = find_free_node(p))) { int old_size = alloc_node(p, CFG_GROW_SIZE); if(old_size >= 0) { i = GET_NODE_COUNT(old_size); node = &p->child[i]; ADD_CHILD_COUNT(p, 1); } } else ADD_CHILD_COUNT(p, 1); } else node = &p->child[i]; if(node && (!node->name)) node->name = strdup(name); return node; } static int set_node(const char* section, const char* key, const char* name, const char* value, short bytes, short type) { int si = -1, ki = -1, vi = -1; cfg_node* section_node = NULL; if((section_node = find_add_node(&root, section))) { cfg_node* key_node; if((key_node = find_add_node(section_node, key))) { cfg_node* value_node; if((value_node = find_add_node(key_node, name))) { if(value_node->bytes < bytes) { if(value_node->value) free(value_node->value); value_node->value = (char*)malloc(bytes); if(value_node->value) value_node->bytes = bytes; else { bdle("not enough memory!"); value_node->bytes = 0; return FALSE; } } if(value_node->value && value != NULL && bytes > 0) memcpy(value_node->value, value, bytes); value_node->type = type; value_node->used = bytes; return TRUE; } } } return FALSE; } static cfg_node* find_node(const char* section, const char* key, const char* name) { int si = -1, ki = -1, vi = -1; if((si = find_inode(&root, section)) >= 0) { cfg_node* section_node = &root.child[si]; if(key) { if((ki = find_inode(section_node, key)) >= 0) { cfg_node* key_node = §ion_node->child[ki]; if(name) { if((vi = find_inode(key_node, name)) >= 0) { return &key_node->child[vi]; } return NULL; } return key_node; } return NULL; } return section_node; } return NULL; } static short find_next_node(const cfg_node* p, short start, char* name, int* bytes) { bdla(0 <= start && start < GET_CHILD_COUNT(p)); bdld("in, start:%d, child count:%d, max count:%d", start, GET_CHILD_COUNT(p), GET_CHILD_MAX_COUNT(p)); short next = -1; if(name) *name = 0; if(0 <= start && start < GET_CHILD_COUNT(p)) { int i; for(i = start; i < GET_CHILD_COUNT(p); i++) { cfg_node* child = &p->child[i]; if(child->name) { int name_bytes = strlen(child->name) + 1; if(name && bytes && *bytes >= name_bytes) { memcpy(name, child->name, name_bytes); if(i + 1 < GET_CHILD_COUNT(p)) next = (short)(i + 1); *bytes = name_bytes; } else if(bytes) { *bytes = name_bytes; } break; } } } return next; } static void free_child(cfg_node* p, int ichild, int count) { int child_count = GET_CHILD_COUNT(p); bdla(p && ichild + count <= child_count && count > 0); int icount = ichild + count; icount = icount <= child_count ? icount : child_count; int i; for(i = ichild; i < icount; i++) free_node(p->child + i); if(i < child_count) { int mv_count = child_count - i; memmove(p->child + ichild, p->child + i, GET_NODE_BYTES(mv_count)); //cleanup the buffer of already moved children memset(p->child + i, 0, GET_NODE_BYTES(mv_count)); } DEC_CHILD_COUNT(p, i - ichild); } static int remove_node(const char* section, const char* key, const char* name) { short si = -1, ki = -1, vi = -1; if((si = find_inode(&root, section)) >= 0) { cfg_node* section_node = &root.child[si]; if((ki = find_inode(section_node, key)) >= 0) { cfg_node* key_node = §ion_node->child[ki]; if(name == NULL) { int count = GET_CHILD_COUNT(key_node); int i; free_child(key_node, 0, count); free_child(section_node, ki, 1); return TRUE; } else if((vi = find_inode(key_node, name)) >= 0) { free_child(key_node, vi, 1); return TRUE; } } } return FALSE; } static inline int find_first_empty(cfg_node*p, int start, int count) { int i; for(i = start; i < count; i++) { if(p->child[i].name == NULL) return i; } return -1; } static inline int find_first_occupy(cfg_node*p, int start, int count) { int i; for(i = start; i < count; i++) if(p->child[i].name) return i; return -1; } static void pack_child(cfg_node* p) { int child_count = GET_CHILD_COUNT(p); int occupy = 1; int empty = 0; int i; for(;;) { empty = find_first_empty(p, empty, child_count); if(empty >= 0) { if(occupy <= empty) occupy = empty + 1; occupy = find_first_occupy(p, occupy, child_count); bdla(occupy != 0); if(occupy > 0) {//move p->child[empty] = p->child[occupy]; memset(&p->child[occupy], 0, sizeof(cfg_node)); empty++; occupy++; } else break; } else break; } } static inline int value_in_filter(cfg_node* key, const char* filter[], int filter_count) { int i, j; int child_count = GET_CHILD_COUNT(key); for(i = 0; i < child_count; i++) { if(key->child[i].name && *key->child[i].name) { for(j = 0; j < filter_count; j++) if(strcmp(filter[j], key->child[i].name) == 0) return TRUE; } } return FALSE; } static int remove_filter_node(const char* section, const char* filter[], int filter_count, int max_allowed) { int si = -1; if((si = find_inode(&root, section)) < 0) { bdle("cannot find section:%s", section); return FALSE; } cfg_node* s = &root.child[si]; int child_count = GET_CHILD_COUNT(s); bdld("section:%s, curr child count:%d, filter count:%d", section, child_count, filter_count); if(child_count < max_allowed) return FALSE; //remove until half of max allowance left int total_rm = child_count - max_allowed / 2; int rm_count = 0; int i; for(i = 0; i < child_count; i++) { if(!value_in_filter(&s->child[i], filter, filter_count)) { free_child(&s->child[i], 0, GET_CHILD_COUNT(&s->child[i])); free_node(&s->child[i]); rm_count++; if(rm_count >= total_rm) break; } } if(rm_count) { pack_child(s); DEC_CHILD_COUNT(s, rm_count); return TRUE; } return FALSE; } static int save_cfg() { const char* file_name = CFG_PATH CFG_FILE_NAME CFG_FILE_EXT; const char* file_name_new = CFG_PATH CFG_FILE_NAME CFG_FILE_EXT_NEW; const char* file_name_old = CFG_PATH CFG_FILE_NAME CFG_FILE_EXT_OLD; int ret = FALSE; if(access(file_name_old, F_OK) == 0) unlink(file_name_old); if(access(file_name_new, F_OK) == 0) unlink(file_name_new); if(btif_config_save_file(file_name_new)) { cached_change = 0; chown(file_name_new, -1, AID_NET_BT_STACK); chmod(file_name_new, 0660); rename(file_name, file_name_old); rename(file_name_new, file_name); ret = TRUE; } else bdle("btif_config_save_file failed"); return ret; } static int load_bluez_cfg() { char adapter_path[256]; if(load_bluez_adapter_info(adapter_path, sizeof(adapter_path))) { if(load_bluez_linkkeys(adapter_path)) return TRUE; } return FALSE; } static void remove_bluez_cfg() { rename(BLUEZ_PATH, BLUEZ_PATH_BAK); } static void clean_newline_char() { char kname[128], vname[128]; short kpos = 0; int kname_size, vname_size; vname[0] = 0; vname_size = sizeof(vname); //bdld("removing newline at the end of the adapter and device name"); if(btif_config_get_str("Local", "Adapter", "Name", vname, &vname_size) && vname_size > 2) { if(vname[vname_size - 2] == '\n') { bdld("remove newline at the end of the adapter name:%s", vname); vname[vname_size - 2] = 0; btif_config_set_str("Local", "Adapter", "Name", vname); } } do { kname_size = sizeof(kname); kname[0] = 0; kpos = btif_config_next_key(kpos, "Remote", kname, &kname_size); //bdld("Remote device:%s, size:%d", kname, kname_size); vname_size = sizeof(vname); vname[0] = 0; if(btif_config_get_str("Remote", kname, "Name", vname, &vname_size) && vname_size > 2) { bdld("remote device name:%s", vname); if(vname[vname_size - 2] == '\n') { bdld("remove newline at the end of the device name:%s", vname); vname[vname_size - 2] = 0; btif_config_set_str("Remote", kname, "Name", vname); } } } while(kpos != -1); } static void load_cfg() { const char* file_name = CFG_PATH CFG_FILE_NAME CFG_FILE_EXT; const char* file_name_new = CFG_PATH CFG_FILE_NAME CFG_FILE_EXT_NEW; const char* file_name_old = CFG_PATH CFG_FILE_NAME CFG_FILE_EXT_OLD; if(!btif_config_load_file(file_name)) { unlink(file_name); if(!btif_config_load_file(file_name_old)) { unlink(file_name_old); if(load_bluez_cfg() && save_cfg()) remove_bluez_cfg(); } } int bluez_migration_done = 0; btif_config_get_int("Local", "Adapter", "BluezMigrationDone", &bluez_migration_done); if(!bluez_migration_done) { //clean the new line char at the end of the device name. Caused by bluez config import bug clean_newline_char(); btif_config_set_int("Local", "Adapter", "BluezMigrationDone", 1); btif_config_save(); } } static void cfg_cmd_callback(int cmd_fd, int type, int size, uint32_t user_id) { UNUSED(cmd_fd); UNUSED(size); UNUSED(user_id); switch(type) { case CFG_CMD_SAVE: { int i; int last_cached_change; // grab lock while accessing cached_change. lock_slot(&slot_lock); bdla(save_cmds_queued > 0); save_cmds_queued--; last_cached_change = cached_change; //hold the file saving until no more change in last 3 seconds. bdld("wait until no more changes in short time, cached change:%d", cached_change); for(i = 0; i < 100; i ++) //5 minutes max waiting { // don't sleep if there is nothing to do if(cached_change == 0) break; // release lock during sleep unlock_slot(&slot_lock); sleep(3); lock_slot(&slot_lock); if(last_cached_change == cached_change) break; last_cached_change = cached_change; } bdld("writing the bt_config.xml now, cached change:%d", cached_change); if(cached_change > 0) save_cfg(); unlock_slot(&slot_lock); break; } } } #ifdef UNIT_TEST static void cfg_test_load() { load_cfg(); char kname[128], vname[128]; short kpos, vpos; int kname_size, vname_size; bdld("list all remote devices values:"); kname_size = sizeof(kname); kname[0] = 0; kpos = 0; do { kpos = btif_config_next_key(kpos, "Remote Devices", kname, &kname_size); bdld("Remote devices:%s, size:%d", kname, kname_size); vpos = 0; vname[0] = 0; vname_size = sizeof(vname); while((vpos = btif_config_next_value(vpos, "Remote Devices", kname, vname, &vname_size)) != -1) { char v[128] = {0}; int vtype = BTIF_CFG_TYPE_STR; int vsize = sizeof(v); int ret = btif_config_get("Remote Devices", kname, vname, v, &vsize, &vtype); bdld("btif_config_get return:%d, Remote devices:%s, value name:%s, value:%s, value size:%d, type:0x%x", ret, kname, vname, v, vsize, vtype); vname[0] = 0; vname_size = sizeof(vname); } kname[0] = 0; kname_size = sizeof(kname); } while(kpos != -1); } static void cfg_test_write() { int i; char key[128]; const char* section = "Remote"; char link_key[64]; for(i = 0; i < (int)sizeof(link_key); i++) link_key[i] = i; bdld("[start write testing"); if(btif_config_exist("test", "test cfg", "write")) return; btif_config_set_int("test", "test cfg", "write", 1); for(i = 0; i < 50; i++) { if(i % 3 == 0) sprintf(key, "Remote paired %d", i); else sprintf(key, "Remote %d", i); link_key[0] = i; btif_config_set_str(section, key, "class", "smart phone"); if(i % 3 == 0) { if(i % 6 == 0) btif_config_set(section, key, "LinkKey", link_key, sizeof(link_key), BTIF_CFG_TYPE_BIN); else btif_config_set(section, key, "LE_KEY_LCSRK", link_key, sizeof(link_key), BTIF_CFG_TYPE_BIN); } btif_config_set_int(section, key, "count", i); if(!btif_config_exist(section, key, "time stamp")) btif_config_set_int(section, key, "time stamp", time(NULL)); } static const char* exclude_filter[] = {"LinkKey", "LE_KEY_PENC", "LE_KEY_PID", "LE_KEY_PCSRK", "LE_KEY_LENC", "LE_KEY_LCSRK"}; const int max_allowed_remote_device = 40; btif_config_filter_remove("Remote", exclude_filter, sizeof(exclude_filter)/sizeof(char*), max_allowed_remote_device); bdld("]end write testing"); btif_config_flush(); } static void cfg_test_read() { //debug("in"); char class[128] = {0}; char link_key[128] = {0}; int size, type; char key[128]; const char* section; int ret, i; for(i = 0; i < 100; i++) { sprintf(key, "00:22:5F:97:56:%02d", i); section = "Remote"; size = sizeof(class); ret = btif_config_get_str(section, key, "class", class, &size); bdld("btif_config_get_str return:%d, Remote devices:%s, class:%s", ret, key, class); size = sizeof(link_key); type = BTIF_CFG_TYPE_BIN; ret = btif_config_get(section, key, "link keys", link_key, &size, &type); //debug("btif_config_get return:%d, Remote devices:%s, link key:%x, %x", // ret, key, *(int *)link_key, *((int *)link_key + 1)); int timeout; ret = btif_config_get_int(section, key, "connect time out", &timeout); //debug("btif_config_get_int return:%d, Remote devices:%s, connect time out:%d", ret, key, timeout); } // debug("testing btif_config_remove"); size = sizeof(class); type = BTIF_CFG_TYPE_STR; btif_config_set("Remote", "00:22:5F:97:56:04", "Class Delete", class, strlen(class) + 1, BTIF_CFG_TYPE_STR); btif_config_get("Remote", "00:22:5F:97:56:04", "Class Delete", class, &size, &type); // debug("Remote devices, 00:22:5F:97:56:04 Class Delete:%s", class); btif_config_remove("Remote", "00:22:5F:97:56:04", "Class Delete"); size = sizeof(class); type = BTIF_CFG_TYPE_STR; ret = btif_config_get("Remote", "00:22:5F:97:56:04", "Class Delete", class, &size, &type); // debug("after removed, btif_config_get ret:%d, Remote devices, 00:22:5F:97:56:04 Class Delete:%s", ret, class); // debug("out"); } #endif