Bluetooth: Implement dynamic blacklist method for role switch
- Blacklist the device if it's rejected the role switch for
max number of times.Same is added to the iot_devlist.conf
file.
- Maximum number of failed (BTM_MAX_SW_ROLE_FAILED_ATTEMPTS)
attempts set to 3 and it is configurable.
- Same blacklisted device is referred by the BTM module when
any other module like profile is requesting for role switch
and BTM module returns BTM_REPEATED_ATTEMPTS as the switch
role status.
- There is option to retry for role switch even though the
device is blacklisted and it can be controlled by feature
flag BTM_SAFE_REATTEMPT_ROLE_SWITCH. And by default it is
enabled.
CRs-Fixed: 831542
Change-Id: I570e1539578e60901794941ca46f0722e368a954
diff --git a/bta/dm/bta_dm_act.c b/bta/dm/bta_dm_act.c
index 1897ff2..0ef7ff2 100644
--- a/bta/dm/bta_dm_act.c
+++ b/bta/dm/bta_dm_act.c
@@ -681,6 +681,7 @@
/* If ACL exists for the device in the remove_bond message*/
BOOLEAN continue_delete_dev = FALSE;
UINT8 other_transport = BT_TRANSPORT_INVALID;
+ remove_iot_device(IOT_DEV_CONF_FILE, IOT_ROLE_CHANGE_BLACKLIST,p_dev->bd_addr, METHOD_BD);
if (BTM_IsAclConnectionUp(p_dev->bd_addr, BT_TRANSPORT_LE) ||
BTM_IsAclConnectionUp(p_dev->bd_addr, BT_TRANSPORT_BR_EDR))
diff --git a/btif/src/btif_dm.c b/btif/src/btif_dm.c
index 20fb5e8..3b62c9a 100644
--- a/btif/src/btif_dm.c
+++ b/btif/src/btif_dm.c
@@ -1830,6 +1830,7 @@
btif_storage_load_bonded_devices();
btif_storage_load_autopair_device_list();
+ load_iot_devlist(IOT_DEV_CONF_FILE);
btif_enable_bluetooth_evt(p_data->enable.status);
}
@@ -1847,6 +1848,7 @@
btif_in_execute_service_request(i, FALSE);
}
}
+ unload_iot_devlist();
btif_disable_bluetooth_evt();
break;
diff --git a/conf/Android.mk b/conf/Android.mk
index 22d03d7..1869c4e 100644
--- a/conf/Android.mk
+++ b/conf/Android.mk
@@ -26,3 +26,10 @@
LOCAL_SRC_FILES := $(LOCAL_MODULE)
include $(BUILD_PREBUILT)
+include $(CLEAR_VARS)
+LOCAL_MODULE := iot_devlist.conf
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT)/etc/bluetooth
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+include $(BUILD_PREBUILT)
diff --git a/conf/iot_devlist.conf b/conf/iot_devlist.conf
new file mode 100755
index 0000000..db09b90
--- /dev/null
+++ b/conf/iot_devlist.conf
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+# Place holder for all IOT devices to be blacklisted
+# Blacklist mechanism will detect the device in runtime based on issue condition
+# and black list the device
diff --git a/include/bt_target.h b/include/bt_target.h
index ce0baaa..029b0a6 100644
--- a/include/bt_target.h
+++ b/include/bt_target.h
@@ -598,6 +598,11 @@
#define BTM_MAX_VSE_CALLBACKS 3
#endif
+/* Safe reattempt even after device is blacklisted for role switch */
+#ifndef BTM_SAFE_REATTEMPT_ROLE_SWITCH
+#define BTM_SAFE_REATTEMPT_ROLE_SWITCH TRUE
+#endif
+
/******************************************
** Lisbon Features
*******************************************/
diff --git a/osi/include/list.h b/osi/include/list.h
index da38c2f..4c9c1d8 100644
--- a/osi/include/list.h
+++ b/osi/include/list.h
@@ -11,6 +11,7 @@
typedef void (*list_free_cb)(void *data);
typedef bool (*list_iter_cb)(void *data);
+typedef bool (*list_iter_cb_ext)(void *data, void *cb_data);
// Returns a new, empty list. Returns NULL if not enough memory could be allocated
// for the list structure. The returned list must be freed with |list_free|. The
@@ -79,6 +80,7 @@
// there will be no callback for the newly-inserted node. Neither |list| nor
// |callback| may be NULL.
void list_foreach(const list_t *list, list_iter_cb callback);
+void list_foreach_ext(const list_t *list, list_iter_cb_ext callback, void *cb_data);
// Returns an iterator to the first element in |list|. |list| may not be NULL.
// The returned iterator is valid as long as it does not equal the value returned
diff --git a/osi/src/list.c b/osi/src/list.c
index 90326e9..6fbb69c 100644
--- a/osi/src/list.c
+++ b/osi/src/list.c
@@ -193,6 +193,23 @@
}
}
+// Iterates through the entire |list| and calls |callback| for each data element.
+// Passes the caller provided data along with node
+// If the list is empty, |callback| will never be called. It is safe to mutate the
+// list inside the callback. If an element is added before the node being visited,
+// there will be no callback for the newly-inserted node. Neither |list| nor
+// |callback| may be NULL.
+void list_foreach_ext(const list_t *list, list_iter_cb_ext callback, void *cb_data) {
+ assert(list != NULL);
+ assert(callback != NULL);
+ if (list)
+ for (list_node_t *node = list->head; node; ) {
+ list_node_t *next = node->next;
+ callback(node->data, cb_data);
+ node = next;
+ }
+}
+
list_node_t *list_begin(const list_t *list) {
assert(list != NULL);
if (list)
diff --git a/stack/btm/btm_acl.c b/stack/btm/btm_acl.c
index 32525b3..049fbe8 100644
--- a/stack/btm/btm_acl.c
+++ b/stack/btm/btm_acl.c
@@ -286,6 +286,7 @@
#endif
#endif
+ p->switch_role_failed_attempts = 0;
p->switch_role_state = BTM_ACL_SWKEY_STATE_IDLE;
btm_pm_sm_alloc(xx);
@@ -676,6 +677,32 @@
return(BTM_BUSY);
}
+ if (is_device_present(IOT_ROLE_CHANGE_BLACKLIST, remote_bd_addr))
+ {
+#if (defined(BTM_SAFE_REATTEMPT_ROLE_SWITCH) && BTM_SAFE_REATTEMPT_ROLE_SWITCH == TRUE)
+ p_dev_rec = btm_find_dev (remote_bd_addr);
+ if(!p_dev_rec || (p_dev_rec->switch_role_attempts >= BTM_MAX_BL_SW_ROLE_ATTEMPTS))
+ {
+ BTM_TRACE_DEBUG (" Below device is Blacklisted ....");
+ BTM_TRACE_DEBUG (" SwitchRole can't be initiated for 0x%02x%02x%02x%02x%02x%02x",
+ remote_bd_addr[0], remote_bd_addr[1], remote_bd_addr[2],
+ remote_bd_addr[3], remote_bd_addr[4], remote_bd_addr[5]);
+ return BTM_REPEATED_ATTEMPTS;
+ }
+ else
+ {
+ BTM_TRACE_DEBUG (" Device blacklisted, trying for role change again");
+ p_dev_rec->switch_role_attempts++;
+ }
+#else
+ BTM_TRACE_DEBUG (" Below device is Blacklisted ....");
+ BTM_TRACE_DEBUG (" SwitchRole can't be initiated for 0x%02x%02x%02x%02x%02x%02x",
+ remote_bd_addr[0], remote_bd_addr[1], remote_bd_addr[2],
+ remote_bd_addr[3], remote_bd_addr[4], remote_bd_addr[5]);
+ return BTM_REPEATED_ATTEMPTS;
+#endif
+ }
+
if ((status = BTM_ReadPowerMode(p->remote_addr, &pwr_mode)) != BTM_SUCCESS)
return(status);
@@ -1483,6 +1510,62 @@
/*******************************************************************************
**
+** Function btm_blacklist_role_change_device
+**
+** Description This function is used to blacklist the device if the role
+** switch fails for maximum number of times. It also removes
+** the device from black list if the role switch succedes.
+
+** Input Parms bd_addr - remote BD addr
+** hci_status - role switch status
+**
+** Returns void
+**
+*******************************************************************************/
+void btm_blacklist_role_change_device (BD_ADDR bd_addr, UINT8 hci_status)
+{
+ tACL_CONN *p = btm_bda_to_acl(bd_addr, BT_TRANSPORT_BR_EDR);
+ tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr);
+ UINT32 cod = 0;
+
+ if(!p || !p_dev_rec)
+ {
+ return;
+ }
+ cod = (p_dev_rec->dev_class[2]) | (p_dev_rec->dev_class[1] << 8) |
+ (p_dev_rec->dev_class[0] << 16);
+
+ /* check for carkits */
+ if ((hci_status != HCI_SUCCESS) &&
+ ((p->switch_role_state == BTM_ACL_SWKEY_STATE_SWITCHING) ||
+ (p->switch_role_state == BTM_ACL_SWKEY_STATE_IN_PROGRESS)) &&
+ ((cod & COD_AUDIO_DEVICE) == COD_AUDIO_DEVICE) &&
+ (!is_device_present(IOT_ROLE_CHANGE_BLACKLIST, bd_addr)))
+ {
+ p->switch_role_failed_attempts++;
+ if(p->switch_role_failed_attempts == BTM_MAX_SW_ROLE_FAILED_ATTEMPTS)
+ {
+ BTM_TRACE_WARNING ("btm_blacklist_device: BDA: %02x-%02x-%02x-%02x-%02x-%02x",
+ bd_addr[0], bd_addr[1], bd_addr[2], bd_addr[3], bd_addr[4], bd_addr[5]);
+ add_iot_device(IOT_DEV_CONF_FILE, IOT_ROLE_CHANGE_BLACKLIST,
+ bd_addr, METHOD_BD);
+ }
+ }
+ else if(hci_status == HCI_SUCCESS)
+ {
+#if (defined(BTM_SAFE_REATTEMPT_ROLE_SWITCH) && BTM_SAFE_REATTEMPT_ROLE_SWITCH == TRUE)
+ if (is_device_present(IOT_ROLE_CHANGE_BLACKLIST, bd_addr))
+ {
+ remove_iot_device(IOT_DEV_CONF_FILE, IOT_ROLE_CHANGE_BLACKLIST,
+ bd_addr, METHOD_BD);
+ }
+#endif
+ p->switch_role_failed_attempts = 0;
+ }
+}
+
+/*******************************************************************************
+**
** Function btm_acl_role_changed
**
** Description This function is called whan a link's master/slave role change
diff --git a/stack/btm/btm_int.h b/stack/btm/btm_int.h
index 76d70df..a7e40e0 100644
--- a/stack/btm/btm_int.h
+++ b/stack/btm/btm_int.h
@@ -101,6 +101,8 @@
#define BTM_ACL_SWKEY_STATE_ENCRYPTION_ON 4
#define BTM_ACL_SWKEY_STATE_IN_PROGRESS 5
UINT8 switch_role_state;
+#define BTM_MAX_SW_ROLE_FAILED_ATTEMPTS 3
+ UINT8 switch_role_failed_attempts;
#define BTM_ACL_ENCRYPT_STATE_IDLE 0
#define BTM_ACL_ENCRYPT_STATE_ENCRYPT_OFF 1 /* encryption turning off */
@@ -624,6 +626,11 @@
#define BTM_SEC_NO_LAST_SERVICE_ID 0
UINT8 last_author_service_id; /* ID of last serviced authorized: Reset after each l2cap connection */
+#if (defined(BTM_SAFE_REATTEMPT_ROLE_SWITCH) && BTM_SAFE_REATTEMPT_ROLE_SWITCH == TRUE)
+#define BTM_MAX_BL_SW_ROLE_ATTEMPTS 1
+ UINT8 switch_role_attempts;
+#endif
+
} tBTM_SEC_DEV_REC;
#define BTM_SEC_IS_SM4(sm) ((BOOLEAN)(BTM_SM4_TRUE == ((sm)&BTM_SM4_TRUE)))
@@ -968,6 +975,7 @@
extern tBTM_STATUS btm_set_packet_types (tACL_CONN *p, UINT16 pkt_types);
extern void btm_process_clk_off_comp_evt (UINT16 hci_handle, UINT16 clock_offset);
extern void btm_acl_role_changed (UINT8 hci_status, BD_ADDR bd_addr, UINT8 new_role);
+extern void btm_blacklist_role_change_device (BD_ADDR bd_addr, UINT8 hci_status);
extern void btm_acl_encrypt_change (UINT16 handle, UINT8 status, UINT8 encr_enable);
extern UINT16 btm_get_acl_disc_reason_code (void);
extern tBTM_STATUS btm_remove_acl (BD_ADDR bd_addr, tBT_TRANSPORT transport);
diff --git a/stack/btu/btu_hcif.c b/stack/btu/btu_hcif.c
index bcc7fcf..e70def2 100644
--- a/stack/btu/btu_hcif.c
+++ b/stack/btu/btu_hcif.c
@@ -1254,7 +1254,7 @@
STREAM_TO_UINT8 (status, p);
STREAM_TO_BDADDR (bda, p);
STREAM_TO_UINT8 (role, p);
-
+ btm_blacklist_role_change_device(bda, status);
l2c_link_role_changed (bda, role, status);
btm_acl_role_changed(status, bda, role);
}
diff --git a/utils/Android.mk b/utils/Android.mk
index 8e3fe39..884df6d 100644
--- a/utils/Android.mk
+++ b/utils/Android.mk
@@ -7,13 +7,18 @@
$(LOCAL_PATH)/../btcore/include \
$(LOCAL_PATH)/../osi/include \
$(LOCAL_PATH)/../stack/include \
+ $(LOCAL_PATH)/../gki/common \
+ $(LOCAL_PATH)/../include \
$(LOCAL_PATH)/../ \
$(bdroid_C_INCLUDES)
LOCAL_CFLAGS += $(bdroid_CFLAGS) -std=c99
LOCAL_SRC_FILES := \
- ./src/bt_utils.c
+ ./src/bt_utils.c
+
+LOCAL_STATIC_LIBRARIES := \
+ libosi
LOCAL_MODULE := libbt-utils
LOCAL_MODULE_TAGS := optional
diff --git a/utils/include/bt_utils.h b/utils/include/bt_utils.h
index e02aa93..80298d7 100644
--- a/utils/include/bt_utils.h
+++ b/utils/include/bt_utils.h
@@ -21,6 +21,7 @@
static const char BT_UTILS_MODULE[] = "bt_utils_module";
+#include <stdbool.h>
/*******************************************************************************
** Type definitions
********************************************************************************/
@@ -45,7 +46,25 @@
** Functions
********************************************************************************/
+typedef enum {
+ METHOD_BD = 0,
+ METHOD_NAME
+} tBLACKLIST_METHOD;
+
+#define MAX_NAME_LEN (50)
+#define IOT_DEV_BASE_CONF_FILE "/etc/bluetooth/iot_devlist.conf"
+#define IOT_DEV_CONF_FILE "/data/misc/bluedroid/iot_devlist.conf"
+#define IOT_DEV_CONF_BKP_FILE "/data/misc/bluedroid/iot_devlist_bkp.conf"
+#define IOT_ROLE_CHANGE_BLACKLIST "RoleChangeBlacklistAddr"
+#define COD_AUDIO_DEVICE (0x200400)
void raise_priority_a2dp(tHIGH_PRIORITY_TASK high_task);
void adjust_priority_a2dp(int start);
+void load_iot_devlist(const char *filename);
+void unload_iot_devlist();
+bool is_device_present(char* header, unsigned char* device_details);
+bool add_iot_device(const char *filename, char* header,
+ unsigned char* device_details, tBLACKLIST_METHOD method_type);
+bool remove_iot_device(const char *filename, char* header,
+ unsigned char* device_details, tBLACKLIST_METHOD method_type);
#define UNUSED(x) (void)(x)
#endif /* BT_UTILS_H */
diff --git a/utils/src/bt_utils.c b/utils/src/bt_utils.c
index 4a73c80..4bd4abd 100644
--- a/utils/src/bt_utils.c
+++ b/utils/src/bt_utils.c
@@ -42,8 +42,38 @@
#include "btcore/include/module.h"
#include "osi/include/compat.h"
#include "osi/include/log.h"
+#include "gki.h"
+#include "list.h"
+#include <string.h>
/*******************************************************************************
+** Local type definitions
+*******************************************************************************/
+typedef struct {
+ char header_name[MAX_NAME_LEN]; // name of header in iot_devlist_conf file
+ list_t *devlist; // list of BD addresses
+ tBLACKLIST_METHOD method_type;
+} iot_header_node_t;
+
+typedef struct {
+ char dev_bd[3]; // BD address of blacklisted device
+} iot_devlist_bd_node_t;
+
+typedef struct {
+ char dev_name[MAX_NAME_LEN]; // Name of blacklisted device
+} iot_devlist_name_node_t;
+
+typedef struct {
+ char *header; // header name
+ unsigned char *dev_details; // details of blacklisted device
+ bool device_found;
+} iot_input_param;
+
+static list_t *iot_header_queue = NULL;
+#define MAX_LINE 2048
+#define MAX_ADDR_STR_LEN 9
+static pthread_mutex_t iot_mutex_lock;
+/*******************************************************************************
** Type definitions for callback functions
********************************************************************************/
static pthread_once_t g_DoSchedulingGroupOnce[TASK_HIGH_MAX];
@@ -65,11 +95,13 @@
pthread_mutexattr_init(&lock_attr);
pthread_mutex_init(&gIdxLock, &lock_attr);
+ pthread_mutex_init(&iot_mutex_lock, NULL);
return NULL;
}
static future_t *clean_up(void) {
pthread_mutex_destroy(&gIdxLock);
+ pthread_mutex_destroy(&iot_mutex_lock);
return NULL;
}
@@ -174,3 +206,855 @@
}
}
}
+
+/*****************************************************************************
+**
+** Function check_bd_cb
+**
+** Description Compares the BD address.
+**
+** Returns returns true if the BD address matches otherwise false
+**
+*******************************************************************************/
+static bool check_bd_cb(void* node, void* cb_data)
+{
+ iot_devlist_bd_node_t *bd_node = (iot_devlist_bd_node_t*)node;
+ iot_input_param *input_param = (iot_input_param*)cb_data;
+
+ if (input_param->device_found == true)
+ return true;
+
+ if ((bd_node->dev_bd[0] == input_param->dev_details[0]) &&
+ (bd_node->dev_bd[1] == input_param->dev_details[1]) &&
+ (bd_node->dev_bd[2] == input_param->dev_details[2])) {
+ input_param->device_found = true;
+ return true;
+ }
+ return false;
+}
+
+/*****************************************************************************
+**
+** Function check_name_cb
+**
+** Description Compares the Device name.
+**
+** Returns returns true if the name matches otherwise false
+**
+*******************************************************************************/
+static bool check_name_cb(void* node, void* cb_data)
+{
+ iot_devlist_name_node_t *name_node = (iot_devlist_name_node_t*)node;
+ iot_input_param *input_param = (iot_input_param*)cb_data;
+
+ if (input_param->device_found == true)
+ return true;
+
+ if (!strncmp(name_node->dev_name, (const char*)input_param->dev_details,
+ strlen((char *)input_param->dev_details))) {
+ input_param->device_found = true;
+ return true;
+ }
+ return false;
+}
+
+/*****************************************************************************
+**
+** Function check_header_cb
+**
+** Description Iterates through the each entry in the header list and
+** calls the callback associated to each entry.
+**
+** Returns boolean
+**
+*******************************************************************************/
+static bool check_header_cb(void* node, void* cb_data)
+{
+ iot_header_node_t *header_node = (iot_header_node_t*)node;
+ iot_input_param *input_param = (iot_input_param*)cb_data;
+ if (!strcmp(header_node->header_name, input_param->header)) {
+ if(header_node->devlist) {
+ if (header_node->method_type == METHOD_BD)
+ list_foreach_ext(header_node->devlist, check_bd_cb, cb_data);
+ else if (header_node->method_type == METHOD_NAME)
+ list_foreach_ext(header_node->devlist, check_name_cb, cb_data);
+ }
+ }
+ return true;
+}
+
+/*****************************************************************************
+**
+** Function is_device_present
+**
+** Description Checks if the device is already present in the blacklisted
+** device list or not.The input can be address based or name
+** based.
+**
+** Returns true incase device is present false otherwise.
+**
+*******************************************************************************/
+bool is_device_present(char* header, unsigned char* device_details)
+{
+ int device_present = false;
+ iot_input_param input_param;
+ input_param.dev_details = device_details;
+ input_param.header = header;
+ input_param.device_found = false;
+
+ pthread_mutex_lock(&iot_mutex_lock);
+ if (!iot_header_queue) {
+ pthread_mutex_unlock(&iot_mutex_lock);
+ return false;
+ }
+ list_foreach_ext(iot_header_queue, check_header_cb, &input_param);
+ pthread_mutex_unlock(&iot_mutex_lock);
+
+ if (input_param.device_found)
+ return true;
+ else
+ return false;
+}
+
+/*****************************************************************************
+**
+** Function parse_bd
+**
+** Description It will read 3 bytes and copy them into node. It also
+** increments header pointer.
+**
+** Returns void.
+**
+*******************************************************************************/
+static void parse_bd(char **start_ptr, iot_devlist_bd_node_t *bd)
+{
+ char *p_end;
+ bd->dev_bd[0] = (unsigned char)strtol(*start_ptr, &p_end, 16);
+ (*start_ptr) = p_end + 1;
+ bd->dev_bd[1] = (unsigned char)strtol(*start_ptr, &p_end, 16);
+ (*start_ptr) = p_end + 1;
+ bd->dev_bd[2] = (unsigned char)strtol(*start_ptr, &p_end, 16);
+ (*start_ptr) = p_end;
+}
+
+/*****************************************************************************
+**
+** Function parse_name
+**
+** Description It will read name and copy them into node. It also
+** increments header pointer.
+**
+** Returns void.
+**
+*******************************************************************************/
+static void parse_name(char **start_ptr, iot_devlist_name_node_t *name)
+{
+ char *split = strchr(*start_ptr, ','); // split point to first occurrence of ,
+ int len = 0;
+ if (split == NULL) {
+ // check once for end of line, for the last name in list
+ split = strchr(*start_ptr, '\n');
+ if (split == NULL)
+ return;
+ }
+ len = (((split - (*start_ptr)) >= MAX_NAME_LEN) ? MAX_NAME_LEN - 1 :
+ (split - (*start_ptr)));
+ memcpy(name->dev_name, *start_ptr, len);
+ name->dev_name[len] = '\0';
+ *start_ptr = split;
+}
+
+/*****************************************************************************
+**
+** Function is_device_node_exist
+**
+** Description Checks if the device node is already present in the queue
+** or not.The input can be address based or name based.
+**
+** Returns true if the entry found else false.
+**
+*******************************************************************************/
+static bool is_device_node_exist(iot_header_node_t *header_entry, char* device_details,
+ tBLACKLIST_METHOD method_type)
+{
+ if(!header_entry || !header_entry->devlist)
+ return false;
+
+ for (const list_node_t *device_node = list_begin(header_entry->devlist);
+ device_node != list_end(header_entry->devlist);
+ device_node = list_next(device_node)) {
+ if(method_type == METHOD_BD) {
+ iot_devlist_bd_node_t *bd_addr_entry = list_node(device_node);
+ if(!memcmp(device_details, bd_addr_entry->dev_bd, 3)) {
+ return true;
+ }
+ }
+ else if(method_type == METHOD_NAME) {
+ iot_devlist_name_node_t *bd_name_entry = list_node(device_node);
+ if(!strcmp((char *)device_details, bd_name_entry->dev_name)) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+/*****************************************************************************
+**
+** Function populate_list
+**
+** Description It goes through the input buffer and add device node to the
+** header list if the valid entry is found.It ignores the
+** duplicated entries.
+**
+** Returns void.
+**
+*******************************************************************************/
+static void populate_list(char *header_end, iot_header_node_t *node)
+{
+ if(node->devlist == NULL)
+ node->devlist = list_new(GKI_freebuf);
+ while(header_end && (*header_end != '\n')&&(*header_end != '\0')) // till end of line reached
+ {
+ // read from line buffer and copy to list
+ if (node->method_type == METHOD_BD) {
+ iot_devlist_bd_node_t *bd = GKI_getbuf(sizeof(iot_devlist_bd_node_t));
+ if(bd == NULL) {
+ ALOGE(" Unable to allocate memory for addr entry");
+ return;
+ }
+ header_end++;
+ parse_bd(&header_end, bd);
+ if(is_device_node_exist(node, (char *) bd, node->method_type)) {
+ GKI_freebuf(bd);
+ }
+ else {
+ list_append(node->devlist, bd);
+ }
+ }
+ else if (node->method_type == METHOD_NAME) {
+ iot_devlist_name_node_t *name = GKI_getbuf(sizeof(iot_devlist_name_node_t));
+ if(name == NULL) {
+ ALOGE(" Unable to allocate memory for name entry");
+ return;
+ }
+ header_end++;
+ parse_name(&header_end, name);
+ if(is_device_node_exist(node, (char *)name, node->method_type)) {
+ GKI_freebuf(name);
+ }
+ else {
+ list_append(node->devlist, name);
+ }
+ }
+ }
+}
+
+/*****************************************************************************
+**
+** Function create_header_node
+**
+** Description This function is used to create the header node.
+**
+** Returns valid pointer incase the node is created otherwise NULL.
+**
+*******************************************************************************/
+static iot_header_node_t *create_header_node(char* name, unsigned int len,
+ tBLACKLIST_METHOD method_type)
+{
+ iot_header_node_t *node = NULL;
+ if(len >= MAX_NAME_LEN) {
+ return NULL;
+ }
+ node = GKI_getbuf(sizeof(iot_header_node_t));
+ if (node == NULL) {
+ ALOGE(" Not enough memory to create the header node");
+ return NULL;
+ }
+ memcpy(node->header_name, name, len);
+ node->header_name[len] = '\0'; // header copied
+ node->method_type = method_type;
+ node->devlist = NULL;
+ return node;
+}
+
+/*****************************************************************************
+**
+** Function get_existing_header_node
+**
+** Description This function is used to get exisiting header node if present.
+**
+** Returns valid pointer incase the node is already prsent otherwise NULL.
+**
+*******************************************************************************/
+static iot_header_node_t *get_existing_header_node(char* name, unsigned int len)
+{
+ for (const list_node_t *node = list_begin(iot_header_queue);
+ node != list_end(iot_header_queue); node = list_next(node)) {
+ iot_header_node_t *entry = list_node(node);
+ if (!strncmp(entry->header_name, name, len)) {
+ return entry;
+ }
+ }
+ return NULL;
+}
+
+/*****************************************************************************
+**
+** Function populate_header
+**
+** Description It goes through the input buffer and add header node to the
+** main queue if the valid entry is found.It ignores the
+** duplicated entries.
+**
+** Returns void.
+**
+*******************************************************************************/
+static void populate_header(char* line_start, char *header_end)
+{
+ tBLACKLIST_METHOD method_type;
+ iot_header_node_t *node = NULL;
+
+ if (*(header_end + 3) == ':')
+ method_type = METHOD_BD;
+ else
+ method_type = METHOD_NAME;
+
+ if (!iot_header_queue) {
+ iot_header_queue = list_new(GKI_freebuf);
+ if (iot_header_queue == NULL) {
+ ALOGE(" Not enough memory to create the queue");
+ return;
+ }
+ }
+
+ if( (node = get_existing_header_node(line_start, header_end - line_start)) == NULL) {
+ node = create_header_node(line_start, header_end - line_start, method_type);
+ if(node)
+ list_append(iot_header_queue, node);
+ }
+ if(node)
+ populate_list(header_end, node);
+}
+
+/*****************************************************************************
+**
+** Function free_header_list
+**
+** Description This function is used to free all entries under blacklist
+** queue.
+**
+** Returns boolean
+**
+*******************************************************************************/
+static bool free_header_list(void* node)
+{
+ iot_header_node_t *header_node = (iot_header_node_t*)node;
+ list_free(header_node->devlist);
+ return true;
+}
+
+/*****************************************************************************
+**
+** Function unload_iot_devlist
+**
+** Description This function is used to free the IOT blacklist queue.
+**
+** Returns void
+**
+*******************************************************************************/
+void unload_iot_devlist()
+{
+ pthread_mutex_lock(&iot_mutex_lock);
+ if (!iot_header_queue) {
+ ALOGV(" Blacklist queue is not initialized ");
+ pthread_mutex_unlock(&iot_mutex_lock);
+ return;
+ }
+ list_foreach(iot_header_queue, free_header_list);
+ list_free(iot_header_queue);
+ iot_header_queue = NULL;
+ pthread_mutex_unlock(&iot_mutex_lock);
+}
+
+/*****************************************************************************
+**
+** Function copy_file
+**
+** Description This function is used to copy one file to other.
+**
+** Returns true incase copy is successful otherwise false.
+**
+*******************************************************************************/
+static bool copy_file(const char *src, const char *dst)
+{
+ FILE *src_fp = NULL, *dst_fp = NULL;
+ int ch;
+
+ if( !src || !dst) {
+ return false;
+ }
+ src_fp = fopen(src, "rt");
+ if(src_fp)
+ dst_fp = fopen(dst, "wt");
+ if(src_fp && dst_fp) {
+ while( ( ch = fgetc(src_fp) ) != EOF ) {
+ fputc(ch, dst_fp);
+ }
+ fclose(dst_fp);
+ fclose(src_fp);
+ return true;
+ }
+ else {
+ if(src_fp)
+ fclose(src_fp);
+ if(dst_fp)
+ fclose(dst_fp);
+ return false;
+ }
+}
+
+/*****************************************************************************
+**
+** Function dump_all_iot_devices
+**
+** Description This function is used to print all blacklisted devices
+** which are loaded from iot_devlist.conf file..
+**
+** Returns void.
+**
+*******************************************************************************/
+static void dump_all_iot_devices(void)
+{
+ tBLACKLIST_METHOD method_type;
+
+ if(!iot_header_queue)
+ return;
+
+ for (const list_node_t *header_node = list_begin(iot_header_queue);
+ header_node != list_end(iot_header_queue);
+ header_node = list_next(header_node)) {
+ iot_header_node_t *header_entry = list_node(header_node);
+ method_type = header_entry->method_type;
+
+ if(!header_entry->devlist)
+ continue;
+
+ ALOGW(" ########### Blacklisted Device summary ##############");
+ for (const list_node_t *device_node = list_begin(header_entry->devlist);
+ device_node != list_end(header_entry->devlist);
+ device_node = list_next(device_node)) {
+ if(method_type == METHOD_BD) {
+ iot_devlist_bd_node_t *bd_addr_entry = list_node(device_node);
+ ALOGW(" Device %02X:%02X:%02X Blacklisted under %s",
+ bd_addr_entry->dev_bd[0], bd_addr_entry->dev_bd[1],
+ bd_addr_entry->dev_bd[2], header_entry->header_name);
+ }
+ else if(method_type == METHOD_NAME) {
+ iot_devlist_name_node_t *bd_name_entry = list_node(device_node);
+ ALOGW(" Device %s Blacklisted under %s", bd_name_entry->dev_name,
+ header_entry->header_name);
+ }
+ }
+ }
+}
+
+/*****************************************************************************
+**
+** Function load_iot_devlist_from_file
+**
+** Description This function is used to initialize the queue and load the
+** load the devices from file.
+**
+** Returns void.
+**
+*******************************************************************************/
+void load_iot_devlist_from_file(const char *filename)
+{
+ if (!filename) {
+ ALOGE(" Invalid IOT blacklist filename");
+ return;
+ }
+ char line_start[MAX_LINE];
+ int line_number = 0;
+ char *header_end = NULL;
+ FILE *iot_devlist_fp = fopen(filename, "rt");
+ if (iot_devlist_fp == NULL) {
+ if(!strcmp(filename, IOT_DEV_CONF_FILE)) {
+ //load it from system partition
+ if(copy_file(IOT_DEV_BASE_CONF_FILE, IOT_DEV_CONF_FILE) == false) {
+ ALOGE(" Can't copy it from Base file %s", IOT_DEV_BASE_CONF_FILE);
+ return;
+ }
+ else {
+ if((iot_devlist_fp = fopen(filename, "rt")) == NULL)
+ return;
+ }
+ }
+ else {
+ ALOGE(" File %s does not exist ",filename);
+ return;
+ }
+ }
+ while(fgets(line_start, MAX_LINE, iot_devlist_fp)) {
+ line_number++;
+ if((*line_start == '\n') ||(*line_start == '#')) {
+ ALOGV("line %d is empty",line_number);
+ continue;
+ }
+ header_end = strchr(line_start, '=');
+ if (header_end == NULL) {
+ ALOGV(" NOT A valid line %d", line_number);
+ continue;
+ }
+ populate_header(line_start, header_end);
+ }
+ dump_all_iot_devices();
+ fclose(iot_devlist_fp);
+}
+
+/*****************************************************************************
+**
+** Function load_iot_devlist
+**
+** Description This function is used to initialize the queue.
+**
+** Returns void.
+**
+*******************************************************************************/
+void load_iot_devlist(const char *filename)
+{
+ pthread_mutex_lock(&iot_mutex_lock);
+ load_iot_devlist_from_file(filename);
+ pthread_mutex_unlock(&iot_mutex_lock);
+}
+
+/*****************************************************************************
+**
+** Function add_iot_device
+**
+** Description This function is used to add the device to the blacklist file
+** as well as queue.
+**
+** Returns true incase the device is blacklisted otherwise fasle.
+**
+*******************************************************************************/
+bool add_iot_device(const char *filename, char* header,
+ unsigned char* device_details, tBLACKLIST_METHOD method_type)
+{
+ char line_start[MAX_LINE];
+ FILE *iot_devlist_fp;
+ char *header_end = NULL;
+ int index = 0, i, len = 0;
+
+ if((header == NULL) || (device_details == NULL)) {
+ ALOGE("Error adding device to the list: Invalid input data");
+ return false;
+ }
+ if (is_device_present (header , device_details)) {
+ ALOGW("Device already present in the blacklist");
+ return true;
+ }
+
+ pthread_mutex_lock(&iot_mutex_lock);
+ iot_devlist_fp = fopen(filename, "a");
+
+ if (iot_devlist_fp == NULL) {
+ ALOGE(" File %s does not exist ", filename);
+ pthread_mutex_unlock(&iot_mutex_lock);
+ return false;
+ }
+ /* first copy the header */
+ len = strlcpy(&line_start[index], header, strlen(header)+ 1);
+ index += len;
+
+ line_start[index++] = '=';
+ /* then copy the device addr/device name */
+ if(method_type == METHOD_BD) {
+ /* for addr take first 3 bytes */
+ for(i = 0; i < 3; i++) {
+ if(i < 2) {
+ len = snprintf(&line_start[index], MAX_LINE - index, "%02X:",
+ *(device_details + i));
+ }
+ else {
+ len = snprintf(&line_start[index], MAX_LINE - index, "%02X",
+ *(device_details + i));
+ }
+ index += len;
+ }
+ }
+ else if(method_type == METHOD_NAME) {
+ len = strlcpy(&line_start[index], (const char*) device_details,
+ strlen((const char*)device_details) + 1);
+ index += len;
+ }
+ /* append the new line characer at the end */
+ line_start[index++] = '\n';
+ line_start[index++] = '\0';
+
+ header_end = strchr(line_start,'=');
+ if(header_end) {
+ populate_header(line_start, header_end);
+ }
+ if(fputs(line_start, iot_devlist_fp)) {
+ fclose(iot_devlist_fp);
+ pthread_mutex_unlock(&iot_mutex_lock);
+ return true;
+ }
+ else {
+ fclose(iot_devlist_fp);
+ pthread_mutex_unlock(&iot_mutex_lock);
+ return false;
+ }
+}
+
+/*****************************************************************************
+**
+** Function form_bd_addr
+**
+** Description Adds the colon after 2 bytes to form valid BD address to
+** compare the entry prsent in file.
+**
+** Returns void
+**
+*******************************************************************************/
+static void form_bd_addr(char *addr, char *new_addr, int max_len)
+{
+ int i = 0, index = 0, len =0;
+ /* for addr take first 3 bytes */
+ for(i = 0; i < 3; i++) {
+ if(i < 2) {
+ len = snprintf(&new_addr[index], max_len - index, "%02X:",
+ *(addr + i));
+ }
+ else {
+ len = snprintf(&new_addr[index], max_len - index, "%02X",
+ *(addr + i));
+ }
+ index += len;
+ }
+ new_addr[max_len - 1]= '\0';
+}
+
+/*****************************************************************************
+**
+** Function remove_iot_device_from_queue
+**
+** Description This function is used remove the entry from internal queue.
+**
+** Returns true if the entry removed from queue else false.
+**
+*******************************************************************************/
+bool remove_iot_device_from_queue(unsigned char* device_details, char* header,
+ tBLACKLIST_METHOD method_type)
+{
+ if(!iot_header_queue)
+ return false;
+ for (const list_node_t *header_node = list_begin(iot_header_queue);
+ header_node != list_end(iot_header_queue);
+ header_node = list_next(header_node)) {
+ iot_header_node_t *header_entry = list_node(header_node);
+
+ if(!header_entry->devlist)
+ continue;
+
+ if((!strcmp(header, header_entry->header_name)) &&
+ method_type == header_entry->method_type) {
+
+ for (const list_node_t *device_node = list_begin(header_entry->devlist);
+ device_node != list_end(header_entry->devlist);
+ device_node = list_next(device_node)) {
+ if(method_type == METHOD_BD) {
+ iot_devlist_bd_node_t *bd_addr_entry = list_node(device_node);
+ if(!memcmp(device_details, bd_addr_entry->dev_bd, 3)) {
+ list_remove(header_entry->devlist, bd_addr_entry);
+ return true;
+ }
+ }
+ else if(method_type == METHOD_NAME) {
+ iot_devlist_name_node_t *bd_name_entry = list_node(device_node);
+ if(!strcmp((char *)device_details, bd_name_entry->dev_name)) {
+ list_remove(header_entry->devlist, bd_name_entry);
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+/*****************************************************************************
+**
+** Function edit_line
+**
+** Description This function is used to remove the device entry from the
+** inputted line buffer if the entry present.
+**
+** Returns true if the entry removed from line else false.
+**
+*******************************************************************************/
+static void edit_line(char *line_start, char *dev_info, int line_len)
+{
+ char *dev_ptr = strstr(line_start, dev_info);
+ char *comma_ptr = NULL;
+ int len_to_copy = 0;
+ if(dev_ptr) {
+ comma_ptr = strchr(dev_ptr, ',');
+ if(comma_ptr) {
+ len_to_copy = line_len - (comma_ptr - line_start + 1);
+ }
+ else {
+ *(dev_ptr - 1) = '\n';
+ *(dev_ptr) = '\0';
+ }
+ }
+ if(len_to_copy) {
+ memmove(dev_ptr, comma_ptr + 1, len_to_copy);
+ }
+}
+
+/*****************************************************************************
+**
+** Function is_single_entry_line
+**
+** Description This function is used to check the line consists of single
+** input line if the entry present.
+**
+** Returns true if the single entry present else false.
+**
+*******************************************************************************/
+static bool is_single_entry_line(char *line_start)
+{
+ char *comma_ptr = strchr(line_start, ',');
+ // check the char next to ,
+ if( !comma_ptr || (*(comma_ptr + 1) == '\n')) {
+ return true;
+ }
+ else {
+ return false;
+ }
+}
+
+/*****************************************************************************
+**
+** Function get_header_from_line
+**
+** Description This function is used to get the header from line buffer.
+**
+** Returns true if the header found else false.
+**
+*******************************************************************************/
+bool get_header_from_line(char *line_start, char* header)
+{
+ int i = 0;
+ if(!line_start || !header || !strchr(line_start, '=')) {
+ return false;
+ }
+ while (line_start[i] != '=') {
+ header[i] = line_start[i];
+ i++;
+ }
+ header[i] = '\0';
+ return true;
+}
+
+/*****************************************************************************
+**
+** Function remove_iot_device
+**
+** Description This function is used to remove the device from internal
+** blacklisted queue as well as black list file.
+**
+** Returns true if the device is removed else false.
+**
+*******************************************************************************/
+bool remove_iot_device(const char *filename, char* header,
+ unsigned char* device_details, tBLACKLIST_METHOD method_type)
+{
+ char line_start[MAX_LINE];
+ FILE *iot_devlist_fp, *iot_devlist_new_fp;
+ char *header_end = NULL;
+ char bd_addr[MAX_ADDR_STR_LEN];
+ char header_name[MAX_NAME_LEN] = { 0 };
+ char *dev = NULL;
+ int index = 0, i, len = 0;
+
+ if((header == NULL) || (device_details == NULL)) {
+ ALOGE("Invalid input data to add the device");
+ return false;
+ }
+ if (!is_device_present (header , device_details)) {
+ ALOGW("Device doesn't exist in the list");
+ return false;
+ }
+ pthread_mutex_lock(&iot_mutex_lock);
+ iot_devlist_fp = fopen(filename, "rt");
+
+ if (iot_devlist_fp == NULL) {
+ ALOGE(" File %s does not exist ", filename);
+ pthread_mutex_unlock(&iot_mutex_lock);
+ return false;
+ }
+ iot_devlist_new_fp = fopen(IOT_DEV_CONF_BKP_FILE, "wt");
+
+ if (iot_devlist_new_fp == NULL) {
+ ALOGE(" Unable to create backup file %s", IOT_DEV_CONF_BKP_FILE);
+ fclose(iot_devlist_fp);
+ pthread_mutex_unlock(&iot_mutex_lock);
+ return false;
+ }
+
+ /* then copy the device addr/device name */
+ while (fgets(line_start, sizeof line_start, iot_devlist_fp)) {
+ len = strlen(line_start);
+
+ if (len) {
+ get_header_from_line(line_start, header_name);
+ if(method_type == METHOD_BD) {
+ form_bd_addr((char*)device_details, bd_addr, MAX_ADDR_STR_LEN);
+ dev = bd_addr;
+ }
+ else if(method_type == METHOD_NAME) {
+ dev = (char *) device_details;
+ }
+ // copy as it is if the line consists comments
+ if( (line_start[0] == '#') || (line_start[0] == '/') ||
+ (line_start[0] == ' ') || (line_start[0] == '\n')) {
+ fputs(line_start, iot_devlist_new_fp);
+ }
+ else if((!strcmp(header_name, header)) && (strstr(line_start, dev))) {
+ if(is_single_entry_line(line_start)) {
+ if(!remove_iot_device_from_queue(device_details, header, method_type)) {
+ // if unable to remove from queue put the same line as it is
+ fputs(line_start, iot_devlist_new_fp);
+ }
+ else
+ ALOGE(" Removed %s device from blacklist file %s", dev, IOT_DEV_CONF_FILE);
+ }
+ else {
+ // multi line entry
+ if(remove_iot_device_from_queue(device_details, header, method_type)) {
+ edit_line(line_start, dev, len + 1);
+ fputs(line_start, iot_devlist_new_fp);
+ ALOGE(" Removed %s device from blacklist file %s", dev, IOT_DEV_CONF_FILE);
+ }
+ else {
+ fputs(line_start, iot_devlist_new_fp);
+ }
+ }
+ }
+ else {
+ fputs(line_start, iot_devlist_new_fp);
+ }
+ }
+ }
+
+ fclose(iot_devlist_fp);
+ fclose(iot_devlist_new_fp);
+ remove(filename);
+ rename(IOT_DEV_CONF_BKP_FILE, filename);
+ pthread_mutex_unlock(&iot_mutex_lock);
+ return true;
+}