Merge "newblue: Add new BT members to the owner list"
diff --git a/uhid.c b/uhid.c
index 2e4f437..e24def0 100644
--- a/uhid.c
+++ b/uhid.c
@@ -42,6 +42,7 @@
 
     int fd;
     ble_hid_conn_t hidId;
+    uint8_t state;  /* indicating the device state such as UP or TEARDOWN */
     uint8_t reportIdPresent;
     uint8_t name[MAX_DEVICE_NAME_SIZE];
     uint16_t bus;
@@ -70,6 +71,10 @@
 
 pthread_t mOutputReportThread;                                         /* the thread polling output report events */
 
+/* fwd decls */
+static void hidDeinit(void);
+
+
 /*
  * FUNCTION: uhidWrite
  * USE:      Write hid event data to kernel
@@ -82,6 +87,11 @@
     size_t ev_size;
     int ret;
 
+    if (fd <= 0) {
+        loge("fd to write is invalid: %d\n", fd);
+        return -EBADR;
+    }
+
     ev_size = sizeof(*ev);
     while (1) {
         ret = write(fd, ev, ev_size);
@@ -134,10 +144,15 @@
  * RETURN:   0 on success or an error code
  * NOTES:
  */
-static int destroyDeviceInKernel(const struct UhidDeviceInfo *devInfo)
+static int destroyDeviceInKernel(struct UhidDeviceInfo *devInfo)
 {
     struct uhid_event ev = {.type = UHID_DESTROY};
 
+    if (!devInfo)
+        return -EFAULT;
+
+    devInfo->state = BTLE_HID_CONN_STATE_TEARDOWN;
+
     return uhidWrite(devInfo->fd, &ev);
 }
 
@@ -256,6 +271,7 @@
     logd("Extract device data.\n");
     devInfo->fd = fd;
     devInfo->hidId = hidId;
+    devInfo->state = BTLE_HID_CONN_STATE_UP;
     devInfo->bus = BUS_USB;
     devInfo->next = NULL;
     btleHidGetHidName(hidId, (char*) devInfo->name);
@@ -332,11 +348,18 @@
  * NOTES:
  */
 static void printmDevices() {
+    if (!mDevices)
+        return;
+
     logd("mDevices has %d devices\n", mDevices->numDevices);
+
     for (struct UhidDeviceInfo *d = mDevices->head; d; d = d->next)
-      logd("  %d\n", d->fd);
-    for (int i = 0; i <= MAX_SUPPORTED_HID_CONNECTIONS; i++)
-      logd("  pfds[%d].fd %d\n", i, mDevices->pfds[i].fd);
+        logd("  %d\n", d->fd);
+
+    for (int i = 0; i <= MAX_SUPPORTED_HID_CONNECTIONS; i++) {
+        if (mDevices->pfds[i].fd >= 0)
+            logd("  pfds[%d].fd %d\n", i, mDevices->pfds[i].fd);
+    }
 }
 
 /*
@@ -393,11 +416,10 @@
 static bool uhidDeviceDel(ble_hid_conn_t hidId) {
     struct UhidDeviceInfo *p = NULL;
     struct UhidDeviceInfo *d = NULL;
-    bool ret = false;
 
     if (!mDevices) {
         loge("mDevices is NULL.\n");
-        return ret;
+        return false;
     }
 
     d = mDevices->head;
@@ -417,9 +439,9 @@
 
         mDevices->numDevices--;
     }
-    printmDevices();
+    logd("Removed the device with conn id %" PRIu64 " from mDevices.\n", hidId);
 
-    return ret;
+    return true;
 }
 
 /*
@@ -456,13 +478,42 @@
 }
 
 /*
+ * FUNCTION: closeDevInfo
+ * USE:      close a device
+ * PARAMS:   devInfo - device data
+ * RETURN:   NONE
+ * NOTES:
+ */
+static void closeDevInfo(struct UhidDeviceInfo *devInfo) {
+    if (!devInfo)
+        return;
+
+    if (!uhidDeviceDel(devInfo->hidId))
+        logw("Cannot find HID conn id %" PRIu64 " to delete.\n", devInfo->hidId);
+
+    for (int i = 1; i <= MAX_SUPPORTED_HID_CONNECTIONS; i++) {
+        if (mDevices->pfds[i].fd == devInfo->fd) {
+            close(mDevices->pfds[i].fd);
+            mDevices->pfds[i].fd = -1;
+            break;
+        }
+    }
+    freeDeviceInfo(devInfo);
+
+    if (mDevices->numDevices <= 0)
+        hidDeinit();
+
+    printmDevices();
+}
+
+/*
  * FUNCTION: handleUhidOutputEvent
  * USE:      read output events and send output reports accordingly
  * PARAMS:   devInfo - device data
  * RETURN:   0 on success or an error code
  * NOTES:
  */
-static int handleUhidOutputEvent(const struct UhidDeviceInfo *devInfo)
+static int handleUhidOutputEvent(struct UhidDeviceInfo *devInfo)
 {
     struct uhid_event ev = {0,};
     ssize_t ret;
@@ -486,7 +537,12 @@
         logd("ev type: UHID_OPEN %d. The HID device is opened.\n", ev.type);
         break;
     case UHID_CLOSE:
-        logd("ev type: UHID_CLOSE %d. The HID device is closed.\n", ev.type);
+        if (devInfo->state == BTLE_HID_CONN_STATE_TEARDOWN) {
+            logd("ev type: UHID_CLOSE %d. The HID device is closed.\n", ev.type);
+            closeDevInfo((struct UhidDeviceInfo *)devInfo);
+        } else {
+            logd("ev type: UHID_CLOSE %d. Ignored.\n", ev.type);
+        }
         break;
     case UHID_OUTPUT:
         logv("ev type: UHID_OUTPUT %d\n", ev.type);
@@ -500,6 +556,29 @@
 }
 
 /*
+ * FUNCTION: closeAllDevices
+ * USE:      close all devices
+ * PARAMS:   NONE
+ * RETURN:   NONE
+ * NOTES:
+ */
+static void closeAllDevices(void) {
+    if (mDevices) {
+        logd("Closing all devices...\n");
+
+        // Close any fd that is still open.
+        for (int i = 1; i <= MAX_SUPPORTED_HID_CONNECTIONS; i++) {
+            if (mDevices->pfds[i].fd > 0)
+                close(mDevices->pfds[i].fd);
+        }
+
+        close(mDevices->control_pipe[0]);
+        free(mDevices);
+        mDevices = NULL;
+    }
+}
+
+/*
  * FUNCTION: outputReportPoller
  * USE:      poll file descriptors and handle the received events
  * PARAMS:   unused - unused
@@ -517,7 +596,7 @@
         } else {
             for (int i = 1; i <= MAX_SUPPORTED_HID_CONNECTIONS; i++) {
                 if (mDevices->pfds[i].revents & POLLIN) {
-                    logd("POLLIN for the %d-th device, fd: %u\n", i, mDevices->pfds[i].fd);
+                    logd("POLLIN for the device[%d].fd %u\n", i, mDevices->pfds[i].fd);
                     handleUhidOutputEvent(uhidFindDeviceByFd(mDevices->pfds[i].fd));
                 }
             }
@@ -527,6 +606,7 @@
 
             if (mDevices->pfds[0].revents & POLLHUP) {
                 logi("Received POLLHUP from pfds 0. Exiting.\n");
+                closeAllDevices();
                 break;
             }
         }
@@ -632,7 +712,7 @@
         if (mDevices->pfds[i].fd < 0) {
             mDevices->pfds[i].fd = fd;
             mDevices->pfds[i].events = POLLIN;
-            logd("devInfo created for id %" PRIu64 " fd %d.\n", hidId, fd);
+            logd("devInfo created for pfds[%d] id %" PRIu64 " fd %d.\n", i, hidId, fd);
             return devInfo;
         }
     }
@@ -650,51 +730,6 @@
 }
 
 /*
- * FUNCTION: closeDevInfo
- * USE:      close a device
- * PARAMS:   devInfo - device data
- * RETURN:   NONE
- * NOTES:
- */
-static void closeDevInfo(struct UhidDeviceInfo *devInfo) {
-    if (!devInfo)
-        return;
-
-    for (int i = 1; i <= MAX_SUPPORTED_HID_CONNECTIONS; i++) {
-        if (mDevices->pfds[i].fd == devInfo->fd) {
-            close(mDevices->pfds[i].fd);
-            mDevices->pfds[i].fd = -1;
-            break;
-        }
-    }
-    freeDeviceInfo(devInfo);
-    printmDevices();
-}
-
-/*
- * FUNCTION: closeAllDevices
- * USE:      close all devices
- * PARAMS:   NONE
- * RETURN:   NONE
- * NOTES:
- */
-static void closeAllDevices(void) {
-    if (mDevices) {
-        logd("Closing all devices...\n");
-
-        // Close any fd that is still open.
-        for (int i = 1; i <= MAX_SUPPORTED_HID_CONNECTIONS; i++) {
-            if (mDevices->pfds[i].fd > 0)
-                close(mDevices->pfds[i].fd);
-        }
-
-        close(mDevices->control_pipe[0]);
-        free(mDevices);
-        mDevices = NULL;
-    }
-}
-
-/*
  * FUNCTION: hidDeinit
  * USE:      close pipe and file descriptors and then free devices
  * PARAMS:   NONE
@@ -702,15 +737,11 @@
  * NOTES:
  */
 static void hidDeinit(void) {
+    if (!mDevices)
+        return;
+
     /* Close the control_pipe so that mOutputReportThread exits with POLLHUP. */
     close(mDevices->control_pipe[1]);
-
-    if (pthread_join(mOutputReportThread, NULL))
-        loge("Failed to join mOutputReportThread.\n");
-    else
-        logd("Joined with mOutputReportThread successfully.\n");
-
-    closeAllDevices();
 }
 
 /*
@@ -747,11 +778,9 @@
         }
 
         devInfo = createDevInfo(hidId);
-
-        ret = createDeviceInKernel(devInfo);
-        if (ret) {
-            loge("Failed to create uhid device: %d.\n", ret);
-            goto out_device;
+        if (!devInfo) {
+            loge("Failed to create devInfo for that hidId %" PRIu64 ".\n", hidId);
+            goto out;
         }
 
         if (!uhidDeviceAppend(devInfo)) {
@@ -759,6 +788,12 @@
             goto out_device;
         }
 
+        ret = createDeviceInKernel(devInfo);
+        if (ret) {
+            loge("Failed to create uhid device: %d.\n", ret);
+            goto del_device;
+        }
+
         logi("Created a uhid device. %u devices in total.\n", mDevices->numDevices);
         goto out;
     }
@@ -769,19 +804,15 @@
         }
 
         devInfo = uhidFindDeviceById(hidId);
+        if (!devInfo) {
+            loge("Failed to find the device with that hidId %" PRIu64 ".\n", hidId);
+            goto out;
+        }
+
         ret = destroyDeviceInKernel(devInfo);
         if (ret)
             loge("Error in destroying the device: %d\n", ret);
 
-        if (!uhidDeviceDel(hidId))
-            logw("Cannot find HID conn id %" PRIu64 " to delete.\n", hidId);
-
-        closeDevInfo(devInfo);
-        logi("uhid device destroyed: %u devices left.\n", mDevices->numDevices);
-
-        if (mDevices->numDevices <= 0)
-            hidDeinit();
-
         goto out;
     }
     else {
@@ -789,6 +820,9 @@
         goto out;
     }
 
+del_device:
+    uhidDeviceDel(devInfo->hidId);
+
 out_device:
     freeDeviceInfo(devInfo);