eclair snapshot
diff --git a/adb/MODULE_LICENSE_APACHE2 b/adb/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/adb/MODULE_LICENSE_APACHE2
diff --git a/adb/NOTICE b/adb/NOTICE
new file mode 100644
index 0000000..9ffcc08
--- /dev/null
+++ b/adb/NOTICE
@@ -0,0 +1,191 @@
+
+   Copyright (c) 2006-2009, The Android Open Source Project
+   Copyright 2006, Brian Swetland <swetland@frotz.net>
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/adb/adb.c b/adb/adb.c
index e8d2c8f..c1646b8 100644
--- a/adb/adb.c
+++ b/adb/adb.c
@@ -832,6 +832,7 @@
 {
 #if !ADB_HOST
     int secure = 0;
+    int port;
     char value[PROPERTY_VALUE_MAX];
 #endif
 
@@ -850,7 +851,7 @@
     HOST = 1;
     usb_vendors_init();
     usb_init();
-    local_init();
+    local_init(ADB_LOCAL_TRANSPORT_PORT);
 
     if(install_listener("tcp:5037", "*smartsocket*", NULL)) {
         exit(1);
@@ -918,14 +919,19 @@
     }
 
         /* for the device, start the usb transport if the
-        ** android usb device exists, otherwise start the
-        ** network transport.
+        ** android usb device exists and "service.adb.tcp"
+        ** is not set, otherwise start the network transport.
         */
-    if(access("/dev/android_adb", F_OK) == 0 ||
-       access("/dev/android", F_OK) == 0) {
+    property_get("service.adb.tcp.port", value, "0");
+    if (sscanf(value, "%d", &port) == 1 && port > 0) {
+        // listen on TCP port specified by service.adb.tcp.port property
+        local_init(port);
+    } else if (access("/dev/android_adb", F_OK) == 0) {
+        // listen on USB
         usb_init();
     } else {
-        local_init();
+        // listen on default port
+        local_init(ADB_LOCAL_TRANSPORT_PORT);
     }
     init_jdwp();
 #endif
@@ -1006,6 +1012,66 @@
         return 0;
     }
 
+    // add a new TCP transport
+    if (!strncmp(service, "connect:", 8)) {
+        char buffer[4096];
+        int port, fd;
+        char* host = service + 8;
+        char* portstr = strchr(host, ':');
+
+        if (!portstr) {
+            snprintf(buffer, sizeof(buffer), "unable to parse %s as <host>:<port>", host);
+            goto done;
+        }
+        if (find_transport(host)) {
+            snprintf(buffer, sizeof(buffer), "Already connected to %s", host);
+            goto done;
+        }
+
+        // zero terminate host by overwriting the ':'
+        *portstr++ = 0;
+        if (sscanf(portstr, "%d", &port) == 0) {
+            snprintf(buffer, sizeof(buffer), "bad port number %s", portstr);
+            goto done;
+        }
+
+        fd = socket_network_client(host, port, SOCK_STREAM);
+        if (fd < 0) {
+            snprintf(buffer, sizeof(buffer), "unable to connect to %s:%d", host, port);
+            goto done;
+        }
+
+        D("client: connected on remote on fd %d\n", fd);
+        close_on_exec(fd);
+        disable_tcp_nagle(fd);
+        snprintf(buf, sizeof buf, "%s:%d", host, port);
+        register_socket_transport(fd, buf, port, 0);
+        snprintf(buffer, sizeof(buffer), "connected to %s:%d", host, port);
+
+done:
+        snprintf(buf, sizeof(buf), "OKAY%04x%s",(unsigned)strlen(buffer), buffer);
+        writex(reply_fd, buf, strlen(buf));
+        return 0;
+    }
+
+    // remove TCP transport
+    if (!strncmp(service, "disconnect:", 11)) {
+        char buffer[4096];
+        memset(buffer, 0, sizeof(buffer));
+        char* serial = service + 11;
+        atransport *t = find_transport(serial);
+
+        if (t) {
+            unregister_transport(t);
+        } else {
+            snprintf(buffer, sizeof(buffer), "No such device %s", serial);
+        }
+
+        snprintf(buf, sizeof(buf), "OKAY%04x%s",(unsigned)strlen(buffer), buffer);
+        writex(reply_fd, buf, strlen(buf));
+        return 0;
+    }
+
     // returns our value for ADB_SERVER_VERSION
     if (!strcmp(service, "version")) {
         char version[12];
diff --git a/adb/adb.h b/adb/adb.h
index 8d57bf2..b958682 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    25    // Increment this when we want to force users to start a new adb server
 
 typedef struct amessage amessage;
 typedef struct apacket apacket;
@@ -262,15 +262,24 @@
 void   kick_transport( atransport*  t );
 
 /* 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);
+int  init_socket_transport(atransport *t, int s, int port, int local);
+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_socket_transport(int s, const char *serial, int port, int local);
+
+/* this should only be used for the "adb disconnect" command */
+void unregister_transport(atransport *t);
+
+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);
+
+atransport *find_transport(const char *serial);
 
 int service_to_fd(const char *name);
 #if ADB_HOST
@@ -357,7 +366,7 @@
 #define ADB_PROTOCOL           0x1
 
 
-void local_init();
+void local_init(int port);
 int  local_connect(int  port);
 
 /* usb host/client interface */
@@ -384,7 +393,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/commandline.c b/adb/commandline.c
index 5414e5e..055aa10 100644
--- a/adb/commandline.c
+++ b/adb/commandline.c
@@ -96,7 +96,8 @@
         " -e                            - directs command to the only running emulator.\n"
         "                                 returns an error if more than one emulator is running.\n"
         " -s <serial number>            - directs command to the USB device or emulator with\n"
-        "                                 the given serial number\n"
+        "                                 the given serial number. Overrides ANDROID_SERIAL\n"
+        "                                 envivornment variable.\n"
         " -p <product name or path>     - simple product name like 'sooner', or\n"
         "                                 a relative/absolute path to a product\n"
         "                                 out directory like 'out/target/product/sooner'.\n"
@@ -104,6 +105,8 @@
         "                                 environment variable is used, which must\n"
         "                                 be an absolute path.\n"
         " devices                       - list all connected devices\n"
+        " connect <host>:<port>         - connect to a device via TCP/IP"
+        " disconnect <host>:<port>      - disconnect from a TCP/IP device"
         "\n"
         "device commands:\n"
         "  adb push <local> <remote>    - copy file/dir to device\n"
@@ -148,7 +151,9 @@
         "  adb status-window            - continuously print device status for a specified device\n"
         "  adb remount                  - remounts the /system partition on the device read-write\n"
         "  adb reboot [bootloader|recovery] - reboots the device, optionally into the bootloader or recovery program\n"
-        "  adb root                     - restarts adb with root permissions\n"
+        "  adb root                     - restarts the adbd daemon with root permissions\n"
+        "  adb usb                      - restarts the adbd daemon listening on USB"
+        "  adb tcpip <port>             - restarts the adbd daemon listening on TCP on the specified port"
         "\n"
         "networking:\n"
         "  adb ppp <tty> [parameters]   - Run PPP over USB.\n"
@@ -767,6 +772,8 @@
     }
     // TODO: also try TARGET_PRODUCT/TARGET_DEVICE as a hint
 
+    serial = getenv("ANDROID_SERIAL");
+
         /* modifiers and flags */
     while(argc > 0) {
         if(!strcmp(argv[0],"nodaemon")) {
@@ -847,6 +854,22 @@
         }
     }
 
+    if(!strcmp(argv[0], "connect") || !strcmp(argv[0], "disconnect")) {
+        char *tmp;
+        if (argc != 2) {
+            fprintf(stderr, "Usage: adb %s <host>:<port>\n", argv[0]);
+            return 1;
+        }
+        snprintf(buf, sizeof buf, "host:%s:%s", argv[0], argv[1]);
+        tmp = adb_query(buf);
+        if(tmp) {
+            printf("%s\n", tmp);
+            return 0;
+        } else {
+            return 1;
+        }
+    }
+
     if (!strcmp(argv[0], "emu")) {
         return adb_send_emulator_command(argc, argv);
     }
@@ -905,35 +928,15 @@
         return 0;
     }
 
-    if(!strcmp(argv[0], "remount")) {
-        int fd = adb_connect("remount:");
-        if(fd >= 0) {
-            read_and_dump(fd);
-            adb_close(fd);
-            return 0;
-        }
-        fprintf(stderr,"error: %s\n", adb_error());
-        return 1;
-    }
-
-    if(!strcmp(argv[0], "reboot")) {
-        int fd;
+    if(!strcmp(argv[0], "remount") || !strcmp(argv[0], "reboot")
+            || !strcmp(argv[0], "tcpip") || !strcmp(argv[0], "usb")
+            || !strcmp(argv[0], "root")) {
+        char command[100];
         if (argc > 1)
-            snprintf(buf, sizeof(buf), "reboot:%s", argv[1]);
+            snprintf(command, sizeof(command), "%s:%s", argv[0], argv[1]);
         else
-            snprintf(buf, sizeof(buf), "reboot:");
-        fd = adb_connect(buf);
-        if(fd >= 0) {
-            read_and_dump(fd);
-            adb_close(fd);
-            return 0;
-        }
-        fprintf(stderr,"error: %s\n", adb_error());
-        return 1;
-    }
-
-    if(!strcmp(argv[0], "root")) {
-        int fd = adb_connect("root:");
+            snprintf(command, sizeof(command), "%s:", argv[0]);
+        int fd = adb_connect(command);
         if(fd >= 0) {
             read_and_dump(fd);
             adb_close(fd);
diff --git a/adb/framebuffer_service.c b/adb/framebuffer_service.c
index 65cb20a..2f45694 100644
--- a/adb/framebuffer_service.c
+++ b/adb/framebuffer_service.c
@@ -28,18 +28,35 @@
 #include <sys/mman.h>
 
 /* TODO:
-** - grab the current buffer, not the first buffer
 ** - sync with vsync to avoid tearing
 */
+/* This version number defines the format of the fbinfo struct.
+   It must match versioning in ddms where this data is consumed. */
+#define DDMS_RAWIMAGE_VERSION 1
+struct fbinfo {
+    unsigned int version;
+    unsigned int bpp;
+    unsigned int size;
+    unsigned int width;
+    unsigned int height;
+    unsigned int red_offset;
+    unsigned int red_length;
+    unsigned int blue_offset;
+    unsigned int blue_length;
+    unsigned int green_offset;
+    unsigned int green_length;
+    unsigned int alpha_offset;
+    unsigned int alpha_length;
+} __attribute__((packed));
 
 void framebuffer_service(int fd, void *cookie)
 {
     struct fb_var_screeninfo vinfo;
-    int fb;
-    void *ptr = MAP_FAILED;
-    char x;
+    int fb, offset;
+    char x[256];
 
-    unsigned fbinfo[4];
+    struct fbinfo fbinfo;
+    unsigned i, bytespp;
 
     fb = open("/dev/graphics/fb0", O_RDONLY);
     if(fb < 0) goto done;
@@ -47,24 +64,43 @@
     if(ioctl(fb, FBIOGET_VSCREENINFO, &vinfo) < 0) goto done;
     fcntl(fb, F_SETFD, FD_CLOEXEC);
 
-    fbinfo[0] = 16;
-    fbinfo[1] = vinfo.xres * vinfo.yres * 2;
-    fbinfo[2] = vinfo.xres;
-    fbinfo[3] = vinfo.yres;
+    bytespp = vinfo.bits_per_pixel / 8;
 
-    ptr = mmap(0, fbinfo[1], PROT_READ, MAP_SHARED, fb, 0);
-    if(ptr == MAP_FAILED) goto done;
+    fbinfo.version = DDMS_RAWIMAGE_VERSION;
+    fbinfo.bpp = vinfo.bits_per_pixel;
+    fbinfo.size = vinfo.xres * vinfo.yres * bytespp;
+    fbinfo.width = vinfo.xres;
+    fbinfo.height = vinfo.yres;
+    fbinfo.red_offset = vinfo.red.offset;
+    fbinfo.red_length = vinfo.red.length;
+    fbinfo.green_offset = vinfo.green.offset;
+    fbinfo.green_length = vinfo.green.length;
+    fbinfo.blue_offset = vinfo.blue.offset;
+    fbinfo.blue_length = vinfo.blue.length;
+    fbinfo.alpha_offset = vinfo.transp.offset;
+    fbinfo.alpha_length = vinfo.transp.length;
 
-    if(writex(fd, fbinfo, sizeof(unsigned) * 4)) goto done;
+    /* HACK: for several of our 3d cores a specific alignment
+     * is required so the start of the fb may not be an integer number of lines
+     * from the base.  As a result we are storing the additional offset in
+     * xoffset. This is not the correct usage for xoffset, it should be added
+     * to each line, not just once at the beginning */
+    offset = vinfo.xoffset * bytespp;
 
-    for(;;) {
-        if(readx(fd, &x, 1)) goto done;
-        if(writex(fd, ptr, fbinfo[1])) goto done;
+    offset += vinfo.xres * vinfo.yoffset * bytespp;
+
+    if(writex(fd, &fbinfo, sizeof(fbinfo))) goto done;
+
+    lseek(fb, offset, SEEK_SET);
+    for(i = 0; i < fbinfo.size; i += 256) {
+      if(readx(fb, &x, 256)) goto done;
+      if(writex(fd, &x, 256)) goto done;
     }
 
+    if(readx(fb, &x, fbinfo.size % 256)) goto done;
+    if(writex(fd, &x, fbinfo.size % 256)) goto done;
+
 done:
-    if(ptr != MAP_FAILED) munmap(ptr, fbinfo[1]);
     if(fb >= 0) close(fb);
     close(fd);
 }
-
diff --git a/adb/get_my_path_darwin.c b/adb/get_my_path_darwin.c
index 00dfee4..6125cb4 100644
--- a/adb/get_my_path_darwin.c
+++ b/adb/get_my_path_darwin.c
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-#include <utils/executablepath.h>
 #import <Carbon/Carbon.h>
 #include <unistd.h>
 
diff --git a/adb/jdwp_service.c b/adb/jdwp_service.c
index ae7f12d..0c26f7b 100644
--- a/adb/jdwp_service.c
+++ b/adb/jdwp_service.c
@@ -164,7 +164,7 @@
         proc->next->prev = proc->prev;
 
         if (proc->socket >= 0) {
-            shutdown(proc->socket, SHUT_RDWR);
+            adb_shutdown(proc->socket);
             adb_close(proc->socket);
             proc->socket = -1;
         }
diff --git a/adb/services.c b/adb/services.c
index 517da55..b5df554 100644
--- a/adb/services.c
+++ b/adb/services.c
@@ -32,8 +32,7 @@
 #    include <netdb.h>
 #  endif
 #else
-#include <sys/poll.h>
-#include <sys/reboot.h>
+#  include <sys/reboot.h>
 #endif
 
 typedef struct stinfo stinfo;
@@ -64,6 +63,7 @@
 
     adb_mutex_lock(&dns_lock);
     hp = gethostbyname(hostname);
+    free(cookie);
     if(hp == 0) {
         writex(fd, &zero, 4);
     } else {
@@ -120,6 +120,7 @@
         if (strcmp(value, "1") != 0) {
             snprintf(buf, sizeof(buf), "adbd cannot run as root in production builds\n");
             writex(fd, buf, strlen(buf));
+            adb_close(fd);
             return;
         }
 
@@ -134,17 +135,57 @@
     }
 }
 
-void reboot_service(int fd, char *arg)
+void restart_tcp_service(int fd, void *cookie)
+{
+    char buf[100];
+    char value[PROPERTY_VALUE_MAX];
+    int port = (int)cookie;
+
+    if (port <= 0) {
+        snprintf(buf, sizeof(buf), "invalid port\n");
+        writex(fd, buf, strlen(buf));
+        adb_close(fd);
+        return;
+    }
+
+    snprintf(value, sizeof(value), "%d", port);
+    property_set("service.adb.tcp.port", value);
+    snprintf(buf, sizeof(buf), "restarting in TCP mode port: %d\n", port);
+    writex(fd, buf, strlen(buf));
+    adb_close(fd);
+
+    // quit, and init will restart us in TCP mode
+    sleep(1);
+    exit(1);
+}
+
+void restart_usb_service(int fd, void *cookie)
+{
+    char buf[100];
+
+    property_set("service.adb.tcp.port", "0");
+    snprintf(buf, sizeof(buf), "restarting in USB mode\n");
+    writex(fd, buf, strlen(buf));
+    adb_close(fd);
+
+    // quit, and init will restart us in USB mode
+    sleep(1);
+    exit(1);
+}
+
+void reboot_service(int fd, void *arg)
 {
     char buf[100];
     int ret;
 
     sync();
-    ret = __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, arg);
+    ret = __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
+                    LINUX_REBOOT_CMD_RESTART2, (char *)arg);
     if (ret < 0) {
         snprintf(buf, sizeof(buf), "reboot failed: %s\n", strerror(errno));
         writex(fd, buf, strlen(buf));
     }
+    free(arg);
     adb_close(fd);
 }
 
@@ -213,9 +254,12 @@
     return s[0];
 }
 
-#if !ADB_HOST
 static int create_subprocess(const char *cmd, const char *arg0, const char *arg1)
 {
+#ifdef HAVE_WIN32_PROC
+	fprintf(stderr, "error: create_subprocess not implemented on Win32 (%s %s %s)\n", cmd, arg0, arg1);
+	return -1;
+#else /* !HAVE_WIN32_PROC */
     char *devname;
     int ptm;
     pid_t pid;
@@ -258,6 +302,7 @@
                 cmd, strerror(errno), errno);
         exit(-1);
     } else {
+#if !ADB_HOST
         // set child's OOM adjustment to zero
         char text[64];
         snprintf(text, sizeof text, "/proc/%d/oom_adj", pid);
@@ -268,11 +313,11 @@
         } else {
            D("adb: unable to open %s\n", text);
         }
-
+#endif
         return ptm;
     }
+#endif /* !HAVE_WIN32_PROC */
 }
-#endif /* !ADB_HOST */
 
 #if ADB_HOST
 #define SHELL_COMMAND "/bin/sh"
@@ -280,76 +325,6 @@
 #define SHELL_COMMAND "/system/bin/sh"
 #endif
 
-#if !ADB_HOST
-static void shell_service(int s, void *command)
-{
-    char    buffer[MAX_PAYLOAD];
-    char    buffer2[MAX_PAYLOAD];
-    struct pollfd ufds[2];
-    int     fd, ret = 0;
-    unsigned count = 0;
-    char** args = (char **)command;
-    fd = create_subprocess(SHELL_COMMAND, args[0], args[1]);
-
-    while (1) {
-        while (count < sizeof(buffer)) {
-            ufds[0].fd = fd;
-            ufds[0].events = POLLIN | POLLHUP;
-            ufds[0].revents = 0;
-            ufds[1].fd = s;
-            ufds[1].events = POLLIN | POLLHUP;
-            ufds[1].revents = 0;
-            // use a 100ms timeout so we don't block indefinitely with our
-            // buffer partially filled.
-            ret = poll(ufds, 2, 100);
-            if (ret <= 0) {
-                D("poll returned %d\n", ret);
-                // file has closed or we timed out
-                // set ret to 1 so we don't exit the outer loop
-                ret = 1;
-                break;
-            }
-
-            if (ufds[0].revents & POLLIN) {
-                ret = adb_read(fd, buffer + count, sizeof(buffer) - count);
-                D("read fd ret: %d, count: %d\n", ret, count);
-                if (ret > 0)
-                    count += ret;
-                else
-                    break;
-            }
-            if (ufds[1].revents & POLLIN) {
-                ret = adb_read(s, buffer2, sizeof(buffer2));
-                D("read s ret: %d\n", ret);
-                if (ret > 0)
-                    adb_write(fd, buffer2, ret);
-                else
-                    break;
-            }
-
-            if ((ufds[0].revents & POLLHUP) || (ufds[1].revents & POLLHUP)) {
-                // set flag to exit after flushing the buffer
-                ret = -1;
-                break;
-            }
-        }
-
-        D("writing: %d\n", count);
-        if (count > 0) {
-            adb_write(s, buffer, count);
-            count = 0;
-        }
-        if (ret <= 0)
-            break;
-    }
-
-    D("shell_service done\n");
-
-    adb_close(fd);
-    adb_close(s);
-}
-#endif // !ADB_HOST
-
 int service_to_fd(const char *name)
 {
     int ret = -1;
@@ -400,27 +375,32 @@
         ret = create_jdwp_connection_fd(atoi(name+5));
     } else if (!strncmp(name, "log:", 4)) {
         ret = create_service_thread(log_service, get_log_file_path(name + 4));
+#endif
     } else if(!HOST && !strncmp(name, "shell:", 6)) {
-        const char* args[2];
         if(name[6]) {
-            args[0] = "-c";
-            args[1] = name + 6;
+            ret = create_subprocess(SHELL_COMMAND, "-c", name + 6);
         } else {
-            args[0] = "-";
-            args[1] = 0;
+            ret = create_subprocess(SHELL_COMMAND, "-", 0);
         }
-        ret = create_service_thread(shell_service, (void *)args);
+#if !ADB_HOST
     } else if(!strncmp(name, "sync:", 5)) {
         ret = create_service_thread(file_sync_service, NULL);
     } else if(!strncmp(name, "remount:", 8)) {
         ret = create_service_thread(remount_service, NULL);
     } else if(!strncmp(name, "reboot:", 7)) {
-        char* arg = name + 7;
-        if (*name == 0)
-            arg = NULL;
+        void* arg = strdup(name + 7);
+        if(arg == 0) return -1;
         ret = create_service_thread(reboot_service, arg);
     } else if(!strncmp(name, "root:", 5)) {
         ret = create_service_thread(restart_root_service, NULL);
+    } else if(!strncmp(name, "tcpip:", 6)) {
+        int port;
+        if (sscanf(name + 6, "%d", &port) == 0) {
+            port = 0;
+        }
+        ret = create_service_thread(restart_tcp_service, (void *)port);
+    } else if(!strncmp(name, "usb:", 4)) {
+        ret = create_service_thread(restart_usb_service, NULL);
 #endif
 #if 0
     } else if(!strncmp(name, "echo:", 5)){
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 389fbd2..6372649 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -113,6 +113,7 @@
 extern int  adb_read(int  fd, void* buf, int len);
 extern int  adb_write(int  fd, const void*  buf, int  len);
 extern int  adb_lseek(int  fd, int  pos, int  where);
+extern int  adb_shutdown(int  fd);
 extern int  adb_close(int  fd);
 
 static __inline__ int  unix_close(int fd)
@@ -327,6 +328,13 @@
 #undef   open
 #define  open    ___xxx_open
 
+static __inline__ int  adb_shutdown(int fd)
+{
+    return shutdown(fd, SHUT_RDWR);
+}
+#undef   shutdown
+#define  shutdown   ____xxx_shutdown
+
 static __inline__ int  adb_close(int fd)
 {
     return close(fd);
diff --git a/adb/sysdeps_win32.c b/adb/sysdeps_win32.c
index a8e3bb9..ced91e8 100644
--- a/adb/sysdeps_win32.c
+++ b/adb/sysdeps_win32.c
@@ -435,6 +435,20 @@
 }
 
 
+int  adb_shutdown(int  fd)
+{
+    FH   f = _fh_from_int(fd);
+
+    if (!f) {
+        return -1;
+    }
+
+    D( "adb_shutdown: %s\n", f->name);
+    shutdown( f->fh_socket, SD_BOTH );
+    return 0;
+}
+
+
 int  adb_close(int  fd)
 {
     FH   f = _fh_from_int(fd);
diff --git a/adb/transport.c b/adb/transport.c
index c76f1a5..c2877d2 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,12 @@
 
     adb_mutex_lock(&transport_lock);
     for (t = transport_list.next; t != &transport_list; t = t->next) {
+        if (t->connection_state == CS_NOPERM) {
+        if (error_out)
+            *error_out = "insufficient permissions for device";
+            continue;
+        }
+
         /* check for matching serial number */
         if (serial) {
             if (t->serial && !strcmp(serial, t->serial)) {
@@ -763,7 +772,6 @@
                 *error_out = "device offline";
             result = NULL;
         }
-
          /* check for required connection state */
         if (result && state != CS_ANY && result->connection_state != state) {
             if (error_out)
@@ -793,6 +801,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 +816,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 */
@@ -839,11 +849,11 @@
 }
 #endif // ADB_HOST
 
-void register_socket_transport(int s, const char *serial, int  port)
+void register_socket_transport(int s, const char *serial, int port, int local)
 {
     atransport *t = calloc(1, sizeof(atransport));
     D("transport: %p init'ing for socket %d, on port %d\n", t, s, port);
-    if ( init_socket_transport(t, s, port) < 0 ) {
+    if ( init_socket_transport(t, s, port, local) < 0 ) {
         adb_close(s);
         free(t);
         return;
@@ -854,18 +864,64 @@
     register_transport(t);
 }
 
-void register_usb_transport(usb_handle *usb, const char *serial)
+#if ADB_HOST
+atransport *find_transport(const char *serial)
+{
+    atransport *t;
+
+    adb_mutex_lock(&transport_lock);
+    for(t = transport_list.next; t != &transport_list; t = t->next) {
+        if (t->serial && !strcmp(serial, t->serial)) {
+            break;
+        }
+     }
+    adb_mutex_unlock(&transport_lock);
+
+    if (t != &transport_list)
+        return t;
+    else
+        return 0;
+}
+
+void unregister_transport(atransport *t)
+{
+    adb_mutex_lock(&transport_lock);
+    t->next->prev = t->prev;
+    t->prev->next = t->next;
+    adb_mutex_unlock(&transport_lock);
+
+    kick_transport(t);
+    transport_unref(t);
+}
+
+#endif
+
+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_local.c b/adb/transport_local.c
index be01f29..81d120e 100644
--- a/adb/transport_local.c
+++ b/adb/transport_local.c
@@ -122,7 +122,7 @@
         close_on_exec(fd);
         disable_tcp_nagle(fd);
         snprintf(buf, sizeof buf, "%s%d", LOCAL_CLIENT_PREFIX, port - 1);
-        register_socket_transport(fd, buf, port);
+        register_socket_transport(fd, buf, port, 1);
         return 0;
     }
     return -1;
@@ -147,17 +147,18 @@
     return 0;
 }
 
-static void *server_socket_thread(void *x)
+static void *server_socket_thread(void * arg)
 {
     int serverfd, fd;
     struct sockaddr addr;
     socklen_t alen;
+    int port = (int)arg;
 
     D("transport: server_socket_thread() starting\n");
     serverfd = -1;
     for(;;) {
         if(serverfd == -1) {
-            serverfd = socket_inaddr_any_server(ADB_LOCAL_TRANSPORT_PORT, SOCK_STREAM);
+            serverfd = socket_inaddr_any_server(port, SOCK_STREAM);
             if(serverfd < 0) {
                 D("server: cannot bind socket yet\n");
                 adb_sleep_ms(1000);
@@ -167,20 +168,20 @@
         }
 
         alen = sizeof(addr);
-        D("server: trying to get new connection from %d\n", ADB_LOCAL_TRANSPORT_PORT);
+        D("server: trying to get new connection from %d\n", port);
         fd = adb_socket_accept(serverfd, &addr, &alen);
         if(fd >= 0) {
             D("server: new connection on fd %d\n", fd);
             close_on_exec(fd);
             disable_tcp_nagle(fd);
-            register_socket_transport(fd,"host",ADB_LOCAL_TRANSPORT_PORT);
+            register_socket_transport(fd, "host", port, 1);
         }
     }
     D("transport: server_socket_thread() exiting\n");
     return 0;
 }
 
-void local_init(void)
+void local_init(int port)
 {
     adb_thread_t thr;
     void* (*func)(void *);
@@ -193,7 +194,7 @@
 
     D("transport: local %s init\n", HOST ? "client" : "server");
 
-    if(adb_thread_create(&thr, func, 0)) {
+    if(adb_thread_create(&thr, func, (void *)port)) {
         fatal_errno("cannot create local socket %s thread",
                     HOST ? "client" : "server");
     }
@@ -203,6 +204,7 @@
 {
     int fd = t->sfd;
     t->sfd = -1;
+    adb_shutdown(fd);
     adb_close(fd);
 
 #if ADB_HOST
@@ -225,7 +227,7 @@
     adb_close(t->fd);
 }
 
-int init_socket_transport(atransport *t, int s, int  port)
+int init_socket_transport(atransport *t, int s, int port, int local)
 {
     int  fail = 0;
 
@@ -239,7 +241,7 @@
     t->type = kTransportLocal;
 
 #if ADB_HOST
-    if (HOST) {
+    if (HOST && local) {
         adb_mutex_lock( &local_transports_lock );
         {
             int  index = (port - ADB_LOCAL_TRANSPORT_PORT)/2;
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..863af1d 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,67 +263,13 @@
                         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
-                    if (i < interfaces - 1) {
-                        while (bufptr[1] != USB_DT_INTERFACE) {
-                            bufptr += bufptr[0];
-                        }
-                    }
+                    bufptr += (USB_DT_ENDPOINT_SIZE * interface->bNumEndpoints);
                  }
             } // end of for
 
@@ -332,8 +278,6 @@
         closedir(devdir);
     } //end of busdir while
     closedir(busdir);
-
-    return found_device;
 }
 
 void usb_cleanup()
@@ -537,26 +481,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 +528,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 +558,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 +567,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 +636,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
diff --git a/adb/usb_osx.c b/adb/usb_osx.c
index 4892c38..00d02da 100644
--- a/adb/usb_osx.c
+++ b/adb/usb_osx.c
@@ -194,30 +194,54 @@
         kr = (*dev)->GetDeviceProduct(dev, &product);
         kr = (*dev)->USBGetSerialNumberStringIndex(dev, &serialIndex);
 
-        if (serialIndex > 0) {
-            IOUSBDevRequest req;
-            UInt16          buffer[256];
+	if (serialIndex > 0) {
+		IOUSBDevRequest req;
+		UInt16          buffer[256];
+		UInt16          languages[128];
 
-            req.bmRequestType =
-                USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
-            req.bRequest = kUSBRqGetDescriptor;
-            req.wValue = (kUSBStringDesc << 8) | serialIndex;
-            req.wIndex = 0;
-            req.pData = buffer;
-            req.wLength = sizeof(buffer);
-            kr = (*dev)->DeviceRequest(dev, &req);
+		memset(languages, 0, sizeof(languages));
 
-            if (kr == kIOReturnSuccess && req.wLenDone > 0) {
-                int i, count;
+		req.bmRequestType =
+			USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
+		req.bRequest = kUSBRqGetDescriptor;
+		req.wValue = (kUSBStringDesc << 8) | 0;
+		req.wIndex = 0;
+		req.pData = languages;
+		req.wLength = sizeof(languages);
+		kr = (*dev)->DeviceRequest(dev, &req);
 
-                // skip first word, and copy the rest to the serial string,
-                // changing shorts to bytes.
-                count = (req.wLenDone - 1) / 2;
-                for (i = 0; i < count; i++)
-                  serial[i] = buffer[i + 1];
-                serial[i] = 0;
-            }
-        }
+		if (kr == kIOReturnSuccess && req.wLenDone > 0) {
+
+			int langCount = (req.wLenDone - 2) / 2, lang;
+
+			for (lang = 1; lang <= langCount; lang++) {
+
+                                memset(buffer, 0, sizeof(buffer));
+                                memset(&req, 0, sizeof(req));
+
+				req.bmRequestType =
+					USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
+				req.bRequest = kUSBRqGetDescriptor;
+				req.wValue = (kUSBStringDesc << 8) | serialIndex;
+				req.wIndex = languages[lang];
+				req.pData = buffer;
+				req.wLength = sizeof(buffer);
+				kr = (*dev)->DeviceRequest(dev, &req);
+
+				if (kr == kIOReturnSuccess && req.wLenDone > 0) {
+					int i, count;
+
+					// skip first word, and copy the rest to the serial string,
+					// changing shorts to bytes.
+					count = (req.wLenDone - 1) / 2;
+					for (i = 0; i < count; i++)
+						serial[i] = buffer[i + 1];
+					serial[i] = 0;
+                                        break;
+				}
+			}
+		}
+	}
         (*dev)->Release(dev);
 
         DBG("INFO: Found vid=%04x pid=%04x serial=%s\n", vendor, product,
@@ -232,7 +256,7 @@
         }
 
         DBG("AndroidDeviceAdded calling register_usb_transport\n");
-        register_usb_transport(handle, (serial[0] ? serial : NULL));
+        register_usb_transport(handle, (serial[0] ? serial : NULL), 1);
 
         // Register for an interest notification of this device being removed.
         // Pass the reference to our private data as the refCon for the
diff --git a/adb/usb_vendors.c b/adb/usb_vendors.c
index f8c54d7..064abc0 100644
--- a/adb/usb_vendors.c
+++ b/adb/usb_vendors.c
@@ -43,6 +43,14 @@
 #define VENDOR_ID_SAMSUNG       0x04e8
 // Motorola's USB Vendor ID
 #define VENDOR_ID_MOTOROLA      0x22b8
+// LG's USB Vendor ID
+#define VENDOR_ID_LGE           0x1004
+// Huawei's USB Vendor ID
+#define VENDOR_ID_HUAWEI        0x12D1
+// Acer's USB Vendor ID
+#define VENDOR_ID_ACER          0x0502
+// Sony Ericsson's USB Vendor ID
+#define VENDOR_ID_SONY_ERICSSON 0x0FCE
 
 /** built-in vendor list */
 int builtInVendorIds[] = {
@@ -50,6 +58,10 @@
     VENDOR_ID_HTC,
     VENDOR_ID_SAMSUNG,
     VENDOR_ID_MOTOROLA,
+    VENDOR_ID_LGE,
+    VENDOR_ID_HUAWEI,
+    VENDOR_ID_ACER,
+    VENDOR_ID_SONY_ERICSSON,
 };
 
 #define BUILT_IN_VENDOR_COUNT    (sizeof(builtInVendorIds)/sizeof(builtInVendorIds[0]))
diff --git a/adb/usb_windows.c b/adb/usb_windows.c
index 0951f67..38c4cf4 100644
--- a/adb/usb_windows.c
+++ b/adb/usb_windows.c
@@ -488,7 +488,7 @@
                                 true)) {
             // Lets make sure that we don't duplicate this device
             if (register_new_device(handle)) {
-              register_usb_transport(handle, serial_number);
+              register_usb_transport(handle, serial_number, 1);
             } else {
               D("register_new_device failed for %s\n", interf_name);
               usb_cleanup_handle(handle);