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/adb.h b/adb/adb.h
index 8d57bf2..4789edf 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -33,7 +33,7 @@
 #define ADB_VERSION_MAJOR 1         // Used for help/version information
 #define ADB_VERSION_MINOR 0         // Used for help/version information
 
-#define ADB_SERVER_VERSION    22    // Increment this when we want to force users to start a new adb server
+#define ADB_SERVER_VERSION    23    // Increment this when we want to force users to start a new adb server
 
 typedef struct amessage amessage;
 typedef struct apacket apacket;
@@ -263,14 +263,17 @@
 
 /* initialize a transport object's func pointers and state */
 int  init_socket_transport(atransport *t, int s, int port);
-void init_usb_transport(atransport *t, usb_handle *usb);
+void init_usb_transport(atransport *t, usb_handle *usb, int state);
 
 /* for MacOS X cleanup */
 void close_usb_devices();
 
 /* cause new transports to be init'd and added to the list */
 void register_socket_transport(int s, const char *serial, int  port);
-void register_usb_transport(usb_handle *h, const char *serial);
+void register_usb_transport(usb_handle *h, const char *serial, unsigned writeable);
+
+/* this should only be used for transports with connection_state == CS_NOPERM */
+void unregister_usb_transport(usb_handle *usb);
 
 int service_to_fd(const char *name);
 #if ADB_HOST
@@ -384,7 +387,7 @@
 #define CS_DEVICE     2
 #define CS_HOST       3
 #define CS_RECOVERY   4
-#define CS_ERROR      5
+#define CS_NOPERM     5 /* Insufficient permissions to communicate with the device */
 
 extern int HOST;
 
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
diff --git a/adb/transport_usb.c b/adb/transport_usb.c
index 3737c5c..2584163 100644
--- a/adb/transport_usb.c
+++ b/adb/transport_usb.c
@@ -110,7 +110,7 @@
     usb_kick(t->usb);
 }
 
-void init_usb_transport(atransport *t, usb_handle *h)
+void init_usb_transport(atransport *t, usb_handle *h, int state)
 {
     D("transport: usb\n");
     t->close = remote_close;
@@ -118,7 +118,7 @@
     t->read_from_remote = remote_read;
     t->write_to_remote = remote_write;
     t->sync_token = 1;
-    t->connection_state = CS_OFFLINE;
+    t->connection_state = state;
     t->type = kTransportUsb;
     t->usb = h;
 
diff --git a/adb/usb_linux.c b/adb/usb_linux.c
index 537122d..cf78d80 100644
--- a/adb/usb_linux.c
+++ b/adb/usb_linux.c
@@ -57,6 +57,7 @@
     unsigned char ep_out;
 
     unsigned zero_mask;
+    unsigned writeable;
 
     struct usbdevfs_urb urb_in;
     struct usbdevfs_urb urb_out;
@@ -115,7 +116,7 @@
 }
 
 static void register_device(const char *dev_name, unsigned char ep_in, unsigned char ep_out,
-                            int ifc, const char *serial, unsigned zero_mask);
+                            int ifc, int serial_index, unsigned zero_mask);
 
 static inline int badname(const char *name)
 {
@@ -125,19 +126,18 @@
     return 0;
 }
 
-static int find_usb_device(const char *base,
-                           void (*register_device_callback) (const char *, unsigned char, unsigned char, int, const char *, unsigned))
+static void find_usb_device(const char *base,
+        void (*register_device_callback)
+                (const char *, unsigned char, unsigned char, int, int, unsigned))
 {
     char busname[32], devname[32];
     unsigned char local_ep_in, local_ep_out;
     DIR *busdir , *devdir ;
     struct dirent *de;
     int fd ;
-    int found_device = 0;
-    char serial[256];
 
     busdir = opendir(base);
-    if(busdir == 0) return 0;
+    if(busdir == 0) return;
 
     while((de = readdir(busdir)) != 0) {
         if(badname(de->d_name)) continue;
@@ -168,7 +168,7 @@
             }
 
 //            DBGX("[ scanning %s ]\n", devname);
-            if((fd = unix_open(devname, O_RDWR)) < 0) {
+            if((fd = unix_open(devname, O_RDONLY)) < 0) {
                 continue;
             }
 
@@ -263,59 +263,9 @@
                         local_ep_out = ep1->bEndpointAddress;
                     }
 
-                        // read the device's serial number
-                    serial[0] = 0;
-                    memset(serial, 0, sizeof(serial));
-                    if (device->iSerialNumber) {
-                        struct usbdevfs_ctrltransfer  ctrl;
-                        __u16 buffer[128];
-                        __u16 languages[128];
-                        int i, result;
-                        int languageCount = 0;
-
-                        memset(languages, 0, sizeof(languages));
-                        memset(&ctrl, 0, sizeof(ctrl));
-
-                            // read list of supported languages
-                        ctrl.bRequestType = USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE;
-                        ctrl.bRequest = USB_REQ_GET_DESCRIPTOR;
-                        ctrl.wValue = (USB_DT_STRING << 8) | 0;
-                        ctrl.wIndex = 0;
-                        ctrl.wLength = sizeof(languages);
-                        ctrl.data = languages;
-
-                        result = ioctl(fd, USBDEVFS_CONTROL, &ctrl);
-                        if (result > 0)
-                            languageCount = (result - 2) / 2;
-
-                        for (i = 1; i <= languageCount; i++) {
-                            memset(buffer, 0, sizeof(buffer));
-                            memset(&ctrl, 0, sizeof(ctrl));
-
-                            ctrl.bRequestType = USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE;
-                            ctrl.bRequest = USB_REQ_GET_DESCRIPTOR;
-                            ctrl.wValue = (USB_DT_STRING << 8) | device->iSerialNumber;
-                            ctrl.wIndex = languages[i];
-                            ctrl.wLength = sizeof(buffer);
-                            ctrl.data = buffer;
-
-                            result = ioctl(fd, USBDEVFS_CONTROL, &ctrl);
-                            if (result > 0) {
-                                int i;
-                                    // skip first word, and copy the rest to the serial string, changing shorts to bytes.
-                                result /= 2;
-                                for (i = 1; i < result; i++)
-                                    serial[i - 1] = buffer[i];
-                                serial[i - 1] = 0;
-                                break;
-                            }
-                        }
-                    }
-
                     register_device_callback(devname, local_ep_in, local_ep_out,
-                            interface->bInterfaceNumber, serial, zero_mask);
+                            interface->bInterfaceNumber, device->iSerialNumber, zero_mask);
 
-                    found_device = 1;
                     break;
                 } else {
                     // seek next interface descriptor
@@ -332,8 +282,6 @@
         closedir(devdir);
     } //end of busdir while
     closedir(busdir);
-
-    return found_device;
 }
 
 void usb_cleanup()
@@ -537,26 +485,30 @@
     if(h->dead == 0) {
         h->dead = 1;
 
-        /* HACK ALERT!
-        ** Sometimes we get stuck in ioctl(USBDEVFS_REAPURB).
-        ** This is a workaround for that problem.
-        */
-        if (h->reaper_thread) {
-            pthread_kill(h->reaper_thread, SIGALRM);
-        }
+        if (h->writeable) {
+            /* HACK ALERT!
+            ** Sometimes we get stuck in ioctl(USBDEVFS_REAPURB).
+            ** This is a workaround for that problem.
+            */
+            if (h->reaper_thread) {
+                pthread_kill(h->reaper_thread, SIGALRM);
+            }
 
-        /* cancel any pending transactions
-        ** these will quietly fail if the txns are not active,
-        ** but this ensures that a reader blocked on REAPURB
-        ** will get unblocked
-        */
-        ioctl(h->desc, USBDEVFS_DISCARDURB, &h->urb_in);
-        ioctl(h->desc, USBDEVFS_DISCARDURB, &h->urb_out);
-        h->urb_in.status = -ENODEV;
-        h->urb_out.status = -ENODEV;
-        h->urb_in_busy = 0;
-        h->urb_out_busy = 0;
-        adb_cond_broadcast(&h->notify);
+            /* cancel any pending transactions
+            ** these will quietly fail if the txns are not active,
+            ** but this ensures that a reader blocked on REAPURB
+            ** will get unblocked
+            */
+            ioctl(h->desc, USBDEVFS_DISCARDURB, &h->urb_in);
+            ioctl(h->desc, USBDEVFS_DISCARDURB, &h->urb_out);
+            h->urb_in.status = -ENODEV;
+            h->urb_out.status = -ENODEV;
+            h->urb_in_busy = 0;
+            h->urb_out_busy = 0;
+            adb_cond_broadcast(&h->notify);
+        } else {
+            unregister_usb_transport(h);
+        }
     }
     adb_mutex_unlock(&h->lock);
 }
@@ -580,11 +532,11 @@
 
 static void register_device(const char *dev_name,
                             unsigned char ep_in, unsigned char ep_out,
-                            int interface,
-                            const char *serial, unsigned zero_mask)
+                            int interface, int serial_index, unsigned zero_mask)
 {
     usb_handle* usb = 0;
     int n = 0;
+    char serial[256];
 
         /* Since Linux will not reassign the device ID (and dev_name)
         ** as long as the device is open, we can add to the list here
@@ -610,6 +562,7 @@
     usb->ep_in = ep_in;
     usb->ep_out = ep_out;
     usb->zero_mask = zero_mask;
+    usb->writeable = 1;
 
     adb_cond_init(&usb->notify, 0);
     adb_mutex_init(&usb->lock, 0);
@@ -618,10 +571,66 @@
     usb->reaper_thread = 0;
 
     usb->desc = unix_open(usb->fname, O_RDWR);
-    if(usb->desc < 0) goto fail;
-    D("[ usb open %s fd = %d]\n", usb->fname, usb->desc);
-    n = ioctl(usb->desc, USBDEVFS_CLAIMINTERFACE, &interface);
-    if(n != 0) goto fail;
+    if(usb->desc < 0) {
+        /* if we fail, see if have read-only access */
+        usb->desc = unix_open(usb->fname, O_RDONLY);
+        if(usb->desc < 0) goto fail;
+        usb->writeable = 0;
+        D("[ usb open read-only %s fd = %d]\n", usb->fname, usb->desc);
+    } else {
+        D("[ usb open %s fd = %d]\n", usb->fname, usb->desc);
+        n = ioctl(usb->desc, USBDEVFS_CLAIMINTERFACE, &interface);
+        if(n != 0) goto fail;
+    }
+
+        /* read the device's serial number */
+    serial[0] = 0;
+    memset(serial, 0, sizeof(serial));
+    if (serial_index) {
+        struct usbdevfs_ctrltransfer  ctrl;
+        __u16 buffer[128];
+        __u16 languages[128];
+        int i, result;
+        int languageCount = 0;
+
+        memset(languages, 0, sizeof(languages));
+        memset(&ctrl, 0, sizeof(ctrl));
+
+            // read list of supported languages
+        ctrl.bRequestType = USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE;
+        ctrl.bRequest = USB_REQ_GET_DESCRIPTOR;
+        ctrl.wValue = (USB_DT_STRING << 8) | 0;
+        ctrl.wIndex = 0;
+        ctrl.wLength = sizeof(languages);
+        ctrl.data = languages;
+
+        result = ioctl(usb->desc, USBDEVFS_CONTROL, &ctrl);
+        if (result > 0)
+            languageCount = (result - 2) / 2;
+
+        for (i = 1; i <= languageCount; i++) {
+            memset(buffer, 0, sizeof(buffer));
+            memset(&ctrl, 0, sizeof(ctrl));
+
+            ctrl.bRequestType = USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE;
+            ctrl.bRequest = USB_REQ_GET_DESCRIPTOR;
+            ctrl.wValue = (USB_DT_STRING << 8) | serial_index;
+            ctrl.wIndex = languages[i];
+            ctrl.wLength = sizeof(buffer);
+            ctrl.data = buffer;
+
+            result = ioctl(usb->desc, USBDEVFS_CONTROL, &ctrl);
+            if (result > 0) {
+                int i;
+                // skip first word, and copy the rest to the serial string, changing shorts to bytes.
+                result /= 2;
+                for (i = 1; i < result; i++)
+                    serial[i - 1] = buffer[i];
+                serial[i - 1] = 0;
+                break;
+            }
+        }
+    }
 
         /* add to the end of the active handles */
     adb_mutex_lock(&usb_lock);
@@ -631,7 +640,7 @@
     usb->next->prev = usb;
     adb_mutex_unlock(&usb_lock);
 
-    register_usb_transport(usb, serial);
+    register_usb_transport(usb, serial, usb->writeable);
     return;
 
 fail:
diff --git a/adb/usb_linux_client.c b/adb/usb_linux_client.c
index 530bd04..0a21c6f 100644
--- a/adb/usb_linux_client.c
+++ b/adb/usb_linux_client.c
@@ -72,7 +72,7 @@
         usb->fd = fd;
 
         D("[ usb_thread - registering device ]\n");
-        register_usb_transport(usb, 0);
+        register_usb_transport(usb, 0, 1);
     }
 
     // never gets here