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;
+}