adb: On Linux, detect USB devices for which adb does not have permissions to communicate with.

adb devices will now list devices without adequate file system permissions in /dev/bus/usb as:

List of devices attached
????????????	no permissions

Signed-off-by: Mike Lockwood <lockwood@android.com>
diff --git a/adb/transport.c b/adb/transport.c
index c76f1a5..831b6fc 100644
--- a/adb/transport.c
+++ b/adb/transport.c
@@ -584,18 +584,37 @@
         return;
     }
 
+    /* don't create transport threads for inaccessible devices */
+    if (t->connection_state != CS_NOPERM) {
         /* initial references are the two threads */
-    t->ref_count = 2;
+        t->ref_count = 2;
 
-    if(adb_socketpair(s)) {
-        fatal_errno("cannot open transport socketpair");
+        if(adb_socketpair(s)) {
+            fatal_errno("cannot open transport socketpair");
+        }
+
+        D("transport: %p (%d,%d) starting\n", t, s[0], s[1]);
+
+        t->transport_socket = s[0];
+        t->fd = s[1];
+
+        D("transport: %p install %d\n", t, t->transport_socket );
+        fdevent_install(&(t->transport_fde),
+                        t->transport_socket,
+                        transport_socket_events,
+                        t);
+
+        fdevent_set(&(t->transport_fde), FDE_READ);
+
+        if(adb_thread_create(&input_thread_ptr, input_thread, t)){
+            fatal_errno("cannot create input thread");
+        }
+
+        if(adb_thread_create(&output_thread_ptr, output_thread, t)){
+            fatal_errno("cannot create output thread");
+        }
     }
 
-    D("transport: %p (%d,%d) starting\n", t, s[0], s[1]);
-
-    t->transport_socket = s[0];
-    t->fd = s[1];
-
         /* put us on the master device list */
     adb_mutex_lock(&transport_lock);
     t->next = &transport_list;
@@ -604,22 +623,6 @@
     t->prev->next = t;
     adb_mutex_unlock(&transport_lock);
 
-    D("transport: %p install %d\n", t, t->transport_socket );
-    fdevent_install(&(t->transport_fde),
-                    t->transport_socket,
-                    transport_socket_events,
-                    t);
-
-    fdevent_set(&(t->transport_fde), FDE_READ);
-
-    if(adb_thread_create(&input_thread_ptr, input_thread, t)){
-        fatal_errno("cannot create input thread");
-    }
-
-    if(adb_thread_create(&output_thread_ptr, output_thread, t)){
-        fatal_errno("cannot create output thread");
-    }
-
     t->disconnects.next = t->disconnects.prev = &t->disconnects;
 
     update_transports();
@@ -717,6 +720,8 @@
 
     adb_mutex_lock(&transport_lock);
     for (t = transport_list.next; t != &transport_list; t = t->next) {
+        if (t->connection_state == CS_NOPERM) continue;
+
         /* check for matching serial number */
         if (serial) {
             if (t->serial && !strcmp(serial, t->serial)) {
@@ -763,6 +768,11 @@
                 *error_out = "device offline";
             result = NULL;
         }
+        if (result && result->connection_state == CS_NOPERM) {
+            if (error_out)
+                *error_out = "no permissions for device";
+            result = NULL;
+        }
 
          /* check for required connection state */
         if (result && state != CS_ANY && result->connection_state != state) {
@@ -793,6 +803,7 @@
     case CS_DEVICE: return "device";
     case CS_HOST: return "host";
     case CS_RECOVERY: return "recovery";
+    case CS_NOPERM: return "no permissions";
     default: return "unknown";
     }
 }
@@ -807,9 +818,10 @@
         /* XXX OVERRUN PROBLEMS XXX */
     adb_mutex_lock(&transport_lock);
     for(t = transport_list.next; t != &transport_list; t = t->next) {
-        len = snprintf(p, end - p, "%s\t%s\n",
-                t->serial ? t->serial : "",
-                statename(t));
+        const char* serial = t->serial;
+        if (!serial || !serial[0])
+            serial = "????????????";
+        len = snprintf(p, end - p, "%s\t%s\n", serial, statename(t));
 
         if (p + len >= end) {
             /* discard last line if buffer is too short */
@@ -854,18 +866,32 @@
     register_transport(t);
 }
 
-void register_usb_transport(usb_handle *usb, const char *serial)
+void register_usb_transport(usb_handle *usb, const char *serial, unsigned writeable)
 {
     atransport *t = calloc(1, sizeof(atransport));
     D("transport: %p init'ing for usb_handle %p (sn='%s')\n", t, usb,
       serial ? serial : "");
-    init_usb_transport(t, usb);
+    init_usb_transport(t, usb, (writeable ? CS_OFFLINE : CS_NOPERM));
     if(serial) {
         t->serial = strdup(serial);
     }
     register_transport(t);
 }
 
+/* this should only be used for transports with connection_state == CS_NOPERM */
+void unregister_usb_transport(usb_handle *usb)
+{
+    atransport *t;
+    adb_mutex_lock(&transport_lock);
+    for(t = transport_list.next; t != &transport_list; t = t->next) {
+        if (t->usb == usb && t->connection_state == CS_NOPERM) {
+            t->next->prev = t->prev;
+            t->prev->next = t->next;
+            break;
+        }
+     }
+    adb_mutex_unlock(&transport_lock);
+}
 
 #undef TRACE_TAG
 #define TRACE_TAG  TRACE_RWX