am 6f523cda: am 7bb1b958: am ff856a2b: Merge "Add error handling to SocketClient::sendData"

* commit '6f523cda13094bf493cdf37e7ecc480bb5a37a2b':
  Add error handling to SocketClient::sendData
diff --git a/adb/SERVICES.TXT b/adb/SERVICES.TXT
index be4d50b..d9aa09c 100644
--- a/adb/SERVICES.TXT
+++ b/adb/SERVICES.TXT
@@ -17,8 +17,10 @@
     upgrade.
 
 host:devices
+host:devices-l
     Ask to return the list of available Android devices and their
-    state. After the OKAY, this is followed by a 4-byte hex len,
+    state. devices-l includes the device paths in the state.
+    After the OKAY, this is followed by a 4-byte hex len,
     and a string that will be dumped as-is by the client, then
     the connection is closed
 
@@ -88,6 +90,9 @@
     Returns the serial number of the corresponding device/emulator.
     Note that emulator serial numbers are of the form "emulator-5554"
 
+<host-prefix>:get-devpath
+    Returns the device path of the corresponding device/emulator.
+
 <host-prefix>:get-state
     Returns the state of a given device as a string.
 
diff --git a/adb/adb.c b/adb/adb.c
index f4ee448..44eaebb 100644
--- a/adb/adb.c
+++ b/adb/adb.c
@@ -1201,16 +1201,19 @@
     }
 
     // return a list of all connected devices
-    if (!strcmp(service, "devices")) {
+    if (!strncmp(service, "devices", 7)) {
         char buffer[4096];
-        memset(buf, 0, sizeof(buf));
-        memset(buffer, 0, sizeof(buffer));
-        D("Getting device list \n");
-        list_transports(buffer, sizeof(buffer));
-        snprintf(buf, sizeof(buf), "OKAY%04x%s",(unsigned)strlen(buffer),buffer);
-        D("Wrote device list \n");
-        writex(reply_fd, buf, strlen(buf));
-        return 0;
+        int use_long = !strcmp(service+7, "-l");
+        if (use_long || service[7] == 0) {
+            memset(buf, 0, sizeof(buf));
+            memset(buffer, 0, sizeof(buffer));
+            D("Getting device list \n");
+            list_transports(buffer, sizeof(buffer), use_long);
+            snprintf(buf, sizeof(buf), "OKAY%04x%s",(unsigned)strlen(buffer),buffer);
+            D("Wrote device list \n");
+            writex(reply_fd, buf, strlen(buf));
+            return 0;
+        }
     }
 
     // add a new TCP transport, device or emulator
@@ -1276,6 +1279,16 @@
         writex(reply_fd, buf, strlen(buf));
         return 0;
     }
+    if(!strncmp(service,"get-devpath",strlen("get-devpath"))) {
+        char *out = "unknown";
+         transport = acquire_one_transport(CS_ANY, ttype, serial, NULL);
+       if (transport && transport->devpath) {
+            out = transport->devpath;
+        }
+        snprintf(buf, sizeof buf, "OKAY%04x%s",(unsigned)strlen(out),out);
+        writex(reply_fd, buf, strlen(buf));
+        return 0;
+    }
     // indicates a new emulator instance has started
     if (!strncmp(service,"emulator:",9)) {
         int  port = atoi(service+9);
diff --git a/adb/adb.h b/adb/adb.h
index 03a7393..4a9b53c 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -190,6 +190,7 @@
         /* used to identify transports for clients */
     char *serial;
     char *product;
+    char *devpath;
     int adb_port; // Use for emulators (local transport)
 
         /* a list of adisconnect callbacks called when the transport is kicked */
@@ -253,7 +254,7 @@
 ** get_device_transport does an acquire on your behalf before returning
 */
 void init_transport_registration(void);
-int  list_transports(char *buf, size_t  bufsize);
+int  list_transports(char *buf, size_t  bufsize, int show_devpath);
 void update_transports(void);
 
 asocket*  create_device_tracker(void);
@@ -286,7 +287,7 @@
 void unregister_transport(atransport *t);
 void unregister_all_tcp_transports();
 
-void register_usb_transport(usb_handle *h, const char *serial, unsigned writeable);
+void register_usb_transport(usb_handle *h, const char *serial, const char *devpath, unsigned writeable);
 
 /* this should only be used for transports with connection_state == CS_NOPERM */
 void unregister_usb_transport(usb_handle *usb);
diff --git a/adb/commandline.c b/adb/commandline.c
index 47c9bec..a6f1ce2 100644
--- a/adb/commandline.c
+++ b/adb/commandline.c
@@ -84,8 +84,8 @@
         "                                 returns an error if more than one USB device is present.\n"
         " -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. Overrides ANDROID_SERIAL\n"
+        " -s <specific device>          - directs command to the device or emulator with the given\n"
+        "                                 serial number or device path. Overrides ANDROID_SERIAL\n"
         "                                 environment variable.\n"
         " -p <product name or path>     - simple product name like 'sooner', or\n"
         "                                 a relative/absolute path to a product\n"
@@ -93,7 +93,8 @@
         "                                 If -p is not specified, the ANDROID_PRODUCT_OUT\n"
         "                                 environment variable is used, which must\n"
         "                                 be an absolute path.\n"
-        " devices                       - list all connected devices\n"
+        " devices [-l]                  - list all connected devices\n"
+        "                                 ('-l' means list device paths)\n"
         " connect <host>[:<port>]       - connect to a device via TCP/IP\n"
         "                                 Port 5555 is used by default if no port number is specified.\n"
         " disconnect [<host>[:<port>]]  - disconnect from a TCP/IP device.\n"
@@ -159,6 +160,7 @@
         "  adb kill-server              - kill the server if it is running\n"
         "  adb get-state                - prints: offline | bootloader | device\n"
         "  adb get-serialno             - prints: <serial-number>\n"
+        "  adb get-devpath              - prints: <device-path>\n"
         "  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"
@@ -1016,7 +1018,16 @@
 
     if(!strcmp(argv[0], "devices")) {
         char *tmp;
-        snprintf(buf, sizeof buf, "host:%s", argv[0]);
+        char *listopt;
+        if (argc < 2)
+            listopt = "";
+        else if (argc == 2 && !strcmp(argv[1], "-l"))
+            listopt = argv[1];
+        else {
+            fprintf(stderr, "Usage: adb devices [-l]\n");
+            return 1;
+        }
+        snprintf(buf, sizeof buf, "host:%s%s", argv[0], listopt);
         tmp = adb_query(buf);
         if(tmp) {
             printf("List of devices attached \n");
@@ -1298,7 +1309,8 @@
     /* passthrough commands */
 
     if(!strcmp(argv[0],"get-state") ||
-        !strcmp(argv[0],"get-serialno"))
+        !strcmp(argv[0],"get-serialno") ||
+        !strcmp(argv[0],"get-devpath"))
     {
         char *tmp;
 
diff --git a/adb/transport.c b/adb/transport.c
index 2f7bd27..29cb582 100644
--- a/adb/transport.c
+++ b/adb/transport.c
@@ -370,7 +370,7 @@
     char  head[5];
     int   len;
 
-    len = list_transports(buffer+4, bufferlen-4);
+    len = list_transports(buffer+4, bufferlen-4, 0);
     snprintf(head, sizeof(head), "%04x", len);
     memcpy(buffer, head, 4);
     len += 4;
@@ -601,6 +601,8 @@
             free(t->product);
         if (t->serial)
             free(t->serial);
+        if (t->devpath)
+            free(t->devpath);
 
         memset(t,0xee,sizeof(atransport));
         free(t);
@@ -762,6 +764,10 @@
                 result = t;
                 break;
             }
+            if (t->devpath && !strcmp(serial, t->devpath)) {
+                result = t;
+                break;
+            }
         } else {
             if (ttype == kTransportUsb && t->type == kTransportUsb) {
                 if (result) {
@@ -837,7 +843,7 @@
     }
 }
 
-int list_transports(char *buf, size_t  bufsize)
+int list_transports(char *buf, size_t  bufsize, int show_devpath)
 {
     char*       p   = buf;
     char*       end = buf + bufsize;
@@ -850,7 +856,13 @@
         const char* serial = t->serial;
         if (!serial || !serial[0])
             serial = "????????????";
-        len = snprintf(p, end - p, "%s\t%s\n", serial, statename(t));
+        if (show_devpath) {
+            const char* devpath = t->devpath;
+            if (!devpath || !devpath[0])
+                devpath = "????????????";
+            len = snprintf(p, end - p, "%s\t%s\t%s\n", serial, devpath, statename(t));
+        } else
+            len = snprintf(p, end - p, "%s\t%s\n", serial, statename(t));
 
         if (p + len >= end) {
             /* discard last line if buffer is too short */
@@ -911,6 +923,9 @@
         if (t->serial && !strcmp(serial, t->serial)) {
             break;
         }
+        if (t->devpath && !strcmp(serial, t->devpath)) {
+            break;
+        }
      }
     adb_mutex_unlock(&transport_lock);
 
@@ -956,7 +971,7 @@
 
 #endif
 
-void register_usb_transport(usb_handle *usb, const char *serial, unsigned writeable)
+void register_usb_transport(usb_handle *usb, const char *serial, const char *devpath, unsigned writeable)
 {
     atransport *t = calloc(1, sizeof(atransport));
     D("transport: %p init'ing for usb_handle %p (sn='%s')\n", t, usb,
@@ -965,6 +980,9 @@
     if(serial) {
         t->serial = strdup(serial);
     }
+    if(devpath) {
+        t->devpath = strdup(devpath);
+    }
     register_transport(t);
 }
 
diff --git a/adb/usb_libusb.c b/adb/usb_libusb.c
index 8c75266..06ff5dc 100644
--- a/adb/usb_libusb.c
+++ b/adb/usb_libusb.c
@@ -347,7 +347,7 @@
 
     adb_mutex_unlock(&usb_lock);
 
-    register_usb_transport(usb, serial, 1); 
+    register_usb_transport(usb, serial, NULL, 1); 
 
     return (1);
 }
diff --git a/adb/usb_linux.c b/adb/usb_linux.c
index 4d55b74..7bf2057 100644
--- a/adb/usb_linux.c
+++ b/adb/usb_linux.c
@@ -116,7 +116,8 @@
 
 }
 
-static void register_device(const char *dev_name, unsigned char ep_in, unsigned char ep_out,
+static void register_device(const char *dev_name, const char *devpath,
+                            unsigned char ep_in, unsigned char ep_out,
                             int ifc, int serial_index, unsigned zero_mask);
 
 static inline int badname(const char *name)
@@ -129,7 +130,7 @@
 
 static void find_usb_device(const char *base,
         void (*register_device_callback)
-                (const char *, unsigned char, unsigned char, int, int, unsigned))
+                (const char *, const char *, unsigned char, unsigned char, int, int, unsigned))
 {
     char busname[32], devname[32];
     unsigned char local_ep_in, local_ep_out;
@@ -227,6 +228,11 @@
                             is_adb_interface(vid, pid, interface->bInterfaceClass,
                             interface->bInterfaceSubClass, interface->bInterfaceProtocol))  {
 
+                        struct stat st;
+                        char pathbuf[128];
+                        char link[256];
+                        char *devpath = NULL;
+
                         DBGX("looking for bulk endpoints\n");
                             // looks like ADB...
                         ep1 = (struct usb_endpoint_descriptor *)bufptr;
@@ -263,7 +269,26 @@
                             local_ep_out = ep1->bEndpointAddress;
                         }
 
-                        register_device_callback(devname, local_ep_in, local_ep_out,
+                            // Determine the device path
+                        if (!fstat(fd, &st) && S_ISCHR(st.st_mode)) {
+                            char *slash;
+                            ssize_t link_len;
+                            snprintf(pathbuf, sizeof(pathbuf), "/sys/dev/char/%d:%d",
+                                     major(st.st_rdev), minor(st.st_rdev));
+                            link_len = readlink(pathbuf, link, sizeof(link) - 1);
+                            if (link_len > 0) {
+                                link[link_len] = '\0';
+                                slash = strrchr(link, '/');
+                                if (slash) {
+                                    snprintf(pathbuf, sizeof(pathbuf),
+                                             "usb:%s", slash + 1);
+                                    devpath = pathbuf;
+                                }
+                            }
+                        }
+
+                        register_device_callback(devname, devpath,
+                                local_ep_in, local_ep_out,
                                 interface->bInterfaceNumber, device->iSerialNumber, zero_mask);
                         break;
                     }
@@ -532,7 +557,7 @@
     return 0;
 }
 
-static void register_device(const char *dev_name,
+static void register_device(const char *dev_name, const char *devpath,
                             unsigned char ep_in, unsigned char ep_out,
                             int interface, int serial_index, unsigned zero_mask)
 {
@@ -644,7 +669,7 @@
     usb->next->prev = usb;
     adb_mutex_unlock(&usb_lock);
 
-    register_usb_transport(usb, serial, usb->writeable);
+    register_usb_transport(usb, serial, devpath, usb->writeable);
     return;
 
 fail:
diff --git a/adb/usb_linux_client.c b/adb/usb_linux_client.c
index 635fa4b..b110ed8 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, 1);
+        register_usb_transport(usb, 0, 0, 1);
     }
 
     // never gets here
diff --git a/adb/usb_osx.c b/adb/usb_osx.c
index 00d02da..45ce444 100644
--- a/adb/usb_osx.c
+++ b/adb/usb_osx.c
@@ -125,10 +125,13 @@
     IOUSBDeviceInterface197  **dev = NULL;
     HRESULT                  result;
     SInt32                   score;
+    UInt32                   locationId;
     UInt16                   vendor;
     UInt16                   product;
     UInt8                    serialIndex;
     char                     serial[256];
+    char                     devpathBuf[64];
+    char                     *devpath = NULL;
 
     while ((usbInterface = IOIteratorNext(iterator))) {
         //* Create an intermediate interface plugin
@@ -192,6 +195,11 @@
 
         kr = (*dev)->GetDeviceVendor(dev, &vendor);
         kr = (*dev)->GetDeviceProduct(dev, &product);
+        kr = (*dev)->GetLocationID(dev, &locationId);
+        if (kr == 0) {
+            snprintf(devpathBuf, sizeof(devpathBuf), "usb:%lX", locationId);
+            devpath = devpathBuf;
+        }
         kr = (*dev)->USBGetSerialNumberStringIndex(dev, &serialIndex);
 
 	if (serialIndex > 0) {
@@ -256,7 +264,7 @@
         }
 
         DBG("AndroidDeviceAdded calling register_usb_transport\n");
-        register_usb_transport(handle, (serial[0] ? serial : NULL), 1);
+        register_usb_transport(handle, (serial[0] ? serial : NULL), devpath, 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_windows.c b/adb/usb_windows.c
index 251bf17..4936b77 100644
--- a/adb/usb_windows.c
+++ b/adb/usb_windows.c
@@ -490,7 +490,7 @@
                                 true)) {
             // Lets make sure that we don't duplicate this device
             if (register_new_device(handle)) {
-              register_usb_transport(handle, serial_number, 1);
+              register_usb_transport(handle, serial_number, NULL, 1);
             } else {
               D("register_new_device failed for %s\n", interf_name);
               usb_cleanup_handle(handle);
diff --git a/fastboot/engine.c b/fastboot/engine.c
index 46b0828..f5215cf 100644
--- a/fastboot/engine.c
+++ b/fastboot/engine.c
@@ -542,6 +542,8 @@
     int status = 0;
 
     a = action_list;
+    if (!a)
+        return status;
     resp[FB_RESPONSE_SZ] = 0;
 
     double start = -1;
@@ -578,3 +580,8 @@
     fprintf(stderr,"finished. total time: %.3fs\n", (now() - start));
     return status;
 }
+
+int fb_queue_is_empty(void)
+{
+    return (action_list == NULL);
+}
diff --git a/fastboot/fastboot.c b/fastboot/fastboot.c
index 848cea3..5858e23 100644
--- a/fastboot/fastboot.c
+++ b/fastboot/fastboot.c
@@ -58,6 +58,7 @@
 static const char *cmdline = 0;
 static int wipe_data = 0;
 static unsigned short vendor_id = 0;
+static int long_listing = 0;
 
 static unsigned base_addr = 0x10000000;
 
@@ -167,9 +168,10 @@
     if(info->ifc_class != 0xff) return -1;
     if(info->ifc_subclass != 0x42) return -1;
     if(info->ifc_protocol != 0x03) return -1;
-    // require matching serial number if a serial number is specified
+    // require matching serial number or device path if requested
     // at the command line with the -s option.
-    if (serial && strcmp(serial, info->serial_number) != 0) return -1;
+    if (serial && (strcmp(serial, info->serial_number) != 0 &&
+                   strcmp(serial, info->device_path) != 0)) return -1;
     return 0;
 }
 
@@ -183,8 +185,16 @@
         if (!serial[0]) {
             serial = "????????????";
         }
-        // output compatible with "adb devices"
-        printf("%s\tfastboot\n", serial);
+        if (!long_listing) {
+            // output compatible with "adb devices"
+            printf("%s\tfastboot\n", serial);
+        } else {
+            char* device_path = info->device_path;
+            if (!device_path[0]) {
+                device_path = "????????????";
+            }
+            printf("%s\t%s\tfastboot\n", serial, device_path);
+        }
     }
 
     return -1;
@@ -238,7 +248,9 @@
             "\n"
             "options:\n"
             "  -w                                       erase userdata and cache\n"
-            "  -s <serial number>                       specify device serial number\n"
+            "  -s <specific device>                     specify device serial number\n"
+            "                                           or path to device port\n"
+            "  -l                                       with \"devices\", lists device paths\n"
             "  -p <product>                             specify product name\n"
             "  -c <cmdline>                             override kernel commandline\n"
             "  -i <vendor id>                           specify a custom USB vendor id\n"
@@ -571,6 +583,7 @@
     int wants_wipe = 0;
     int wants_reboot = 0;
     int wants_reboot_bootloader = 0;
+    int wants_device_list = 0;
     void *data;
     unsigned sz;
     unsigned page_size = 2048;
@@ -582,11 +595,6 @@
         return 1;
     }
 
-    if (!strcmp(*argv, "devices")) {
-        list_devices();
-        return 0;
-    }
-
     if (!strcmp(*argv, "help")) {
         usage();
         return 0;
@@ -612,6 +620,9 @@
             require(2);
             serial = argv[1];
             skip(2);
+        } else if(!strcmp(*argv, "-l")) {
+            long_listing = 1;
+            skip(1);
         } else if(!strcmp(*argv, "-p")) {
             require(2);
             product = argv[1];
@@ -630,6 +641,9 @@
                 die("invalid vendor id '%s'", argv[1]);
             vendor_id = (unsigned short)val;
             skip(2);
+        } else if (!strcmp(*argv, "devices")) {
+            skip(1);
+            wants_device_list = 1;
         } else if(!strcmp(*argv, "getvar")) {
             require(2);
             fb_queue_display(argv[1], argv[1]);
@@ -725,6 +739,9 @@
         }
     }
 
+    if (wants_device_list)
+        list_devices();
+
     if (wants_wipe) {
         fb_queue_erase("userdata");
         fb_queue_format("userdata", 1);
@@ -737,6 +754,9 @@
         fb_queue_command("reboot-bootloader", "rebooting into bootloader");
     }
 
+    if (fb_queue_is_empty())
+        return 0;
+
     usb = open_device();
 
     status = fb_execute_queue(usb);
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
index 1d3e2b8..90d8a6a 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/fastboot.h
@@ -53,6 +53,7 @@
 void fb_queue_download(const char *name, void *data, unsigned size);
 void fb_queue_notice(const char *notice);
 int fb_execute_queue(usb_handle *usb);
+int fb_queue_is_empty(void);
 
 /* util stuff */
 void die(const char *fmt, ...);
diff --git a/fastboot/usb.h b/fastboot/usb.h
index df9efde..d504ee2 100644
--- a/fastboot/usb.h
+++ b/fastboot/usb.h
@@ -53,6 +53,7 @@
     unsigned char writable;
 
     char serial_number[256];
+    char device_path[256];
 };
 
 typedef int (*ifc_match_func)(usb_ifc_info *ifc);
diff --git a/fastboot/usb_linux.c b/fastboot/usb_linux.c
index 83c6de9..b7a9ca3 100644
--- a/fastboot/usb_linux.c
+++ b/fastboot/usb_linux.c
@@ -32,6 +32,7 @@
 #include <string.h>
 
 #include <sys/ioctl.h>
+#include <sys/stat.h>
 #include <sys/types.h>
 #include <dirent.h>
 #include <fcntl.h>
@@ -107,6 +108,9 @@
     int in, out;
     unsigned i;
     unsigned e;
+    
+    struct stat st;
+    int result;
 
     if(check(ptr, len, USB_DT_DEVICE, USB_DT_DEVICE_SIZE))
         return -1;
@@ -134,7 +138,6 @@
         // Keep it short enough because some bootloaders are borked if the URB len is > 255
         // 128 is too big by 1.
         __u16 buffer[127];
-        int result;
 
         memset(buffer, 0, sizeof(buffer));
 
@@ -158,6 +161,42 @@
         }
     }
 
+    /* We need to get a path that represents a particular port on a particular
+     * hub.  We are passed an fd that was obtained by opening an entry under
+     * /dev/bus/usb.  Unfortunately, the names of those entries change each
+     * time devices are plugged and unplugged.  So how to get a repeatable
+     * path?  udevadm provided the inspiration.  We can get the major and
+     * minor of the device file, read the symlink that can be found here:
+     *   /sys/dev/char/<major>:<minor>
+     * and then use the last element of that path.  As a concrete example, I
+     * have an Android device at /dev/bus/usb/001/027 so working with bash:
+     *   $ ls -l /dev/bus/usb/001/027
+     *   crw-rw-r-- 1 root plugdev 189, 26 Apr  9 11:03 /dev/bus/usb/001/027
+     *   $ ls -l /sys/dev/char/189:26
+     *   lrwxrwxrwx 1 root root 0 Apr  9 11:03 /sys/dev/char/189:26 ->
+     *           ../../devices/pci0000:00/0000:00:1a.7/usb1/1-4/1-4.2/1-4.2.3
+     * So our device_path would be 1-4.2.3 which says my device is connected
+     * to port 3 of a hub on port 2 of a hub on port 4 of bus 1 (per
+     * http://www.linux-usb.org/FAQ.html).
+     */
+    info.device_path[0] = '\0';
+    result = fstat(fd, &st);
+    if (!result && S_ISCHR(st.st_mode)) {
+        char cdev[128];
+        char link[256];
+        char *slash;
+        ssize_t link_len;
+        snprintf(cdev, sizeof(cdev), "/sys/dev/char/%d:%d",
+                 major(st.st_rdev), minor(st.st_rdev));
+        link_len = readlink(cdev, link, sizeof(link) - 1);
+        if (link_len > 0) {
+            link[link_len] = '\0';
+            slash = strrchr(link, '/');
+            if (slash)
+                snprintf(info.device_path, sizeof(info.device_path), "usb:%s", slash+1);
+        }
+    }
+
     for(i = 0; i < cfg->bNumInterfaces; i++) {
         if(check(ptr, len, USB_DT_INTERFACE, USB_DT_INTERFACE_SIZE))
             return -1;
diff --git a/fastboot/usb_osx.c b/fastboot/usb_osx.c
index cbce9bd..1548ba8 100644
--- a/fastboot/usb_osx.c
+++ b/fastboot/usb_osx.c
@@ -264,6 +264,7 @@
     SInt32 score;
     HRESULT result;
     UInt8 serialIndex;
+    UInt32 locationId;
 
     // Create an intermediate plugin.
     kr = IOCreatePlugInInterfaceForService(device,
@@ -322,6 +323,13 @@
         goto error;
     }
 
+    kr = (*dev)->GetLocationID(dev, &locationId);
+    if (kr != 0) {
+        ERR("GetLocationId");
+        goto error;
+    }
+    snprintf(handle->info.device_path, sizeof(handle->info.device_path), "usb:%lX", locationId);
+
     kr = (*dev)->USBGetSerialNumberStringIndex(dev, &serialIndex);
 
     if (serialIndex > 0) {
diff --git a/fastboot/usb_windows.c b/fastboot/usb_windows.c
index 99027cc..7aa36b2 100644
--- a/fastboot/usb_windows.c
+++ b/fastboot/usb_windows.c
@@ -311,6 +311,8 @@
         info.serial_number[0] = 0;
     }
 
+    info.device_path[0] = 0;
+
     if (callback(&info) == 0) {
         return 1;
     }
diff --git a/include/arch/darwin-x86/AndroidConfig.h b/include/arch/darwin-x86/AndroidConfig.h
index 48f8d9a..9da01c5 100644
--- a/include/arch/darwin-x86/AndroidConfig.h
+++ b/include/arch/darwin-x86/AndroidConfig.h
@@ -175,7 +175,7 @@
  * with a memory address.  If not defined, stack crawls will not have symbolic
  * information.
  */
-#define HAVE_DLADDR 0
+#define HAVE_DLADDR 1
 
 /*
  * Defined if we have the cxxabi.h header for demangling C++ symbols.  If
diff --git a/include/corkscrew/map_info.h b/include/corkscrew/map_info.h
index c5cd8f8..ea1d35f 100644
--- a/include/corkscrew/map_info.h
+++ b/include/corkscrew/map_info.h
@@ -21,6 +21,7 @@
 
 #include <sys/types.h>
 #include <stdbool.h>
+#include <stdint.h>
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/include/corkscrew/ptrace.h b/include/corkscrew/ptrace.h
index 172e348..0040c22 100644
--- a/include/corkscrew/ptrace.h
+++ b/include/corkscrew/ptrace.h
@@ -24,6 +24,7 @@
 
 #include <sys/types.h>
 #include <stdbool.h>
+#include <stdint.h>
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/include/corkscrew/symbol_table.h b/include/corkscrew/symbol_table.h
index 020c8b8..4998750 100644
--- a/include/corkscrew/symbol_table.h
+++ b/include/corkscrew/symbol_table.h
@@ -17,6 +17,7 @@
 #ifndef _CORKSCREW_SYMBOL_TABLE_H
 #define _CORKSCREW_SYMBOL_TABLE_H
 
+#include <stdint.h>
 #include <sys/types.h>
 
 #ifdef __cplusplus
diff --git a/libcorkscrew/Android.mk b/libcorkscrew/Android.mk
index 433f93c..9019986 100644
--- a/libcorkscrew/Android.mk
+++ b/libcorkscrew/Android.mk
@@ -14,9 +14,7 @@
 
 LOCAL_PATH:= $(call my-dir)
 
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
+generic_src_files := \
 	backtrace.c \
 	backtrace-helper.c \
 	demangle.c \
@@ -24,16 +22,24 @@
 	ptrace.c \
 	symbol_table.c
 
-ifeq ($(TARGET_ARCH),arm)
-LOCAL_SRC_FILES += \
+arm_src_files := \
 	arch-arm/backtrace-arm.c \
 	arch-arm/ptrace-arm.c
+
+x86_src_files := \
+	arch-x86/backtrace-x86.c \
+	arch-x86/ptrace-x86.c
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(generic_src_files)
+
+ifeq ($(TARGET_ARCH),arm)
+LOCAL_SRC_FILES += $(arm_src_files)
 LOCAL_CFLAGS += -DCORKSCREW_HAVE_ARCH
 endif
 ifeq ($(TARGET_ARCH),x86)
-LOCAL_SRC_FILES += \
-	arch-x86/backtrace-x86.c \
-	arch-x86/ptrace-x86.c
+LOCAL_SRC_FILES += $(x86_src_files)
 LOCAL_CFLAGS += -DCORKSCREW_HAVE_ARCH
 endif
 
@@ -44,3 +50,38 @@
 LOCAL_MODULE_TAGS := optional
 
 include $(BUILD_SHARED_LIBRARY)
+
+# Build test.
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := test.c
+LOCAL_CFLAGS += -std=gnu99 -Werror -fno-inline-small-functions
+LOCAL_SHARED_LIBRARIES := libcorkscrew
+LOCAL_MODULE := libcorkscrew_test
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_EXECUTABLE)
+
+
+ifeq ($(HOST_OS)-$(HOST_ARCH),linux-x86)
+
+# Build libcorkscrew.
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES += $(generic_src_files) $(x86_src_files)
+LOCAL_CFLAGS += -DCORKSCREW_HAVE_ARCH
+LOCAL_SHARED_LIBRARIES += libgccdemangle
+LOCAL_STATIC_LIBRARIES += libcutils
+LOCAL_LDLIBS += -ldl -lrt
+LOCAL_CFLAGS += -std=gnu99 -Werror
+LOCAL_MODULE := libcorkscrew
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_HOST_SHARED_LIBRARY)
+
+# Build test.
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := test.c
+LOCAL_CFLAGS += -std=gnu99 -Werror -fno-inline-small-functions
+LOCAL_SHARED_LIBRARIES := libcorkscrew
+LOCAL_MODULE := libcorkscrew_test
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_HOST_EXECUTABLE)
+
+endif # linux-x86
diff --git a/libcorkscrew/arch-x86/backtrace-x86.c b/libcorkscrew/arch-x86/backtrace-x86.c
index 24fadcb..01d9ed5 100644
--- a/libcorkscrew/arch-x86/backtrace-x86.c
+++ b/libcorkscrew/arch-x86/backtrace-x86.c
@@ -31,40 +31,22 @@
 #include <limits.h>
 #include <errno.h>
 #include <sys/ptrace.h>
-#include <sys/exec_elf.h>
 #include <cutils/log.h>
 
-/* Machine context at the time a signal was raised. */
-typedef struct ucontext {
-    uint32_t uc_flags;
-    struct ucontext* uc_link;
-    stack_t uc_stack;
-    struct sigcontext {
-        uint32_t gs;
-        uint32_t fs;
-        uint32_t es;
-        uint32_t ds;
-        uint32_t edi;
-        uint32_t esi;
-        uint32_t ebp;
-        uint32_t esp;
-        uint32_t ebx;
-        uint32_t edx;
-        uint32_t ecx;
-        uint32_t eax;
-        uint32_t trapno;
-        uint32_t err;
-        uint32_t eip;
-        uint32_t cs;
-        uint32_t efl;
-        uint32_t uesp;
-        uint32_t ss;
-        void* fpregs;
-        uint32_t oldmask;
-        uint32_t cr2;
-    } uc_mcontext;
-    uint32_t uc_sigmask;
-} ucontext_t;
+#if defined(__BIONIC__)
+
+// Bionic offers the Linux kernel headers.
+#include <asm/sigcontext.h>
+#include <asm/ucontext.h>
+typedef struct ucontext ucontext_t;
+
+#else
+
+// glibc has its own renaming of the Linux kernel's structures.
+#define __USE_GNU // For REG_EBP, REG_ESP, and REG_EIP.
+#include <ucontext.h>
+
+#endif
 
 /* Unwind state. */
 typedef struct {
@@ -114,9 +96,15 @@
     const ucontext_t* uc = (const ucontext_t*)sigcontext;
 
     unwind_state_t state;
+#if defined(__BIONIC__)
     state.ebp = uc->uc_mcontext.ebp;
-    state.eip = uc->uc_mcontext.eip;
     state.esp = uc->uc_mcontext.esp;
+    state.eip = uc->uc_mcontext.eip;
+#else
+    state.ebp = uc->uc_mcontext.gregs[REG_EBP];
+    state.esp = uc->uc_mcontext.gregs[REG_ESP];
+    state.eip = uc->uc_mcontext.gregs[REG_EIP];
+#endif
 
     memory_t memory;
     init_memory(&memory, map_info_list);
diff --git a/libcorkscrew/backtrace.c b/libcorkscrew/backtrace.c
index fa21574..eec53a2 100644
--- a/libcorkscrew/backtrace.c
+++ b/libcorkscrew/backtrace.c
@@ -27,16 +27,41 @@
 
 #include <unistd.h>
 #include <signal.h>
+#include <stdlib.h>
+#include <string.h>
 #include <pthread.h>
 #include <unwind.h>
-#include <sys/exec_elf.h>
 #include <cutils/log.h>
 #include <cutils/atomic.h>
+#include <elf.h>
 
 #if HAVE_DLADDR
+#define __USE_GNU // For dladdr(3) in glibc.
 #include <dlfcn.h>
 #endif
 
+#if defined(__BIONIC__)
+
+// Bionic implements and exports gettid but only implements tgkill.
+extern int tgkill(int tgid, int tid, int sig);
+
+#else
+
+// glibc doesn't implement or export either gettid or tgkill.
+
+#include <unistd.h>
+#include <sys/syscall.h>
+
+static pid_t gettid() {
+  return syscall(__NR_gettid);
+}
+
+static int tgkill(int tgid, int tid, int sig) {
+  return syscall(__NR_tgkill, tgid, tid, sig);
+}
+
+#endif
+
 typedef struct {
     backtrace_frame_t* backtrace;
     size_t ignore_depth;
@@ -115,8 +140,6 @@
 }
 #endif
 
-extern int tgkill(int tgid, int tid, int sig);
-
 ssize_t unwind_backtrace_thread(pid_t tid, backtrace_frame_t* backtrace,
         size_t ignore_depth, size_t max_depth) {
     if (tid == gettid()) {
diff --git a/libcorkscrew/map_info.c b/libcorkscrew/map_info.c
index f33378f..3c52854 100644
--- a/libcorkscrew/map_info.c
+++ b/libcorkscrew/map_info.c
@@ -21,6 +21,7 @@
 
 #include <ctype.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <limits.h>
 #include <pthread.h>
diff --git a/libcorkscrew/ptrace.c b/libcorkscrew/ptrace.c
index cbea8ca..6496d5e 100644
--- a/libcorkscrew/ptrace.c
+++ b/libcorkscrew/ptrace.c
@@ -21,6 +21,7 @@
 #include <corkscrew/ptrace.h>
 
 #include <errno.h>
+#include <stdlib.h>
 #include <sys/ptrace.h>
 #include <cutils/log.h>
 
diff --git a/libcorkscrew/symbol_table.c b/libcorkscrew/symbol_table.c
index 1b97180..29e4a79 100644
--- a/libcorkscrew/symbol_table.c
+++ b/libcorkscrew/symbol_table.c
@@ -19,14 +19,22 @@
 
 #include <corkscrew/symbol_table.h>
 
+#include <stdbool.h>
 #include <stdlib.h>
+#include <elf.h>
 #include <fcntl.h>
 #include <string.h>
 #include <sys/stat.h>
 #include <sys/mman.h>
-#include <sys/exec_elf.h>
 #include <cutils/log.h>
 
+static bool is_elf(Elf32_Ehdr* e) {
+    return (e->e_ident[EI_MAG0] == ELFMAG0 &&
+            e->e_ident[EI_MAG1] == ELFMAG1 &&
+            e->e_ident[EI_MAG2] == ELFMAG2 &&
+            e->e_ident[EI_MAG3] == ELFMAG3);
+}
+
 // Compare function for qsort
 static int qcompar(const void *a, const void *b) {
     const symbol_t* asym = (const symbol_t*)a;
@@ -67,7 +75,7 @@
 
     // Parse the file header
     Elf32_Ehdr *hdr = (Elf32_Ehdr*)base;
-    if (!IS_ELF(*hdr)) {
+    if (!is_elf(hdr)) {
         goto out_close;
     }
     Elf32_Shdr *shdr = (Elf32_Shdr*)(base + hdr->e_shoff);
diff --git a/libcorkscrew/test.c b/libcorkscrew/test.c
new file mode 100644
index 0000000..af34c03
--- /dev/null
+++ b/libcorkscrew/test.c
@@ -0,0 +1,64 @@
+#include <corkscrew/backtrace.h>
+#include <corkscrew/symbol_table.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+void do_backtrace() {
+  const size_t MAX_DEPTH = 32;
+  backtrace_frame_t* frames = (backtrace_frame_t*) malloc(sizeof(backtrace_frame_t) * MAX_DEPTH);
+  ssize_t frame_count = unwind_backtrace(frames, 0, MAX_DEPTH);
+  fprintf(stderr, "frame_count=%d\n", (int) frame_count);
+
+  backtrace_symbol_t* backtrace_symbols = (backtrace_symbol_t*) malloc(sizeof(backtrace_symbol_t) * frame_count);
+  get_backtrace_symbols(frames, frame_count, backtrace_symbols);
+
+  for (size_t i = 0; i < (size_t) frame_count; ++i) {
+    char line[MAX_BACKTRACE_LINE_LENGTH];
+    format_backtrace_line(i, &frames[i], &backtrace_symbols[i],
+                          line, MAX_BACKTRACE_LINE_LENGTH);
+    if (backtrace_symbols[i].symbol_name != NULL) {
+      // get_backtrace_symbols found the symbol's name with dladdr(3).
+      fprintf(stderr, "  %s\n", line);
+    } else {
+      // We don't have a symbol. Maybe this is a static symbol, and
+      // we can look it up?
+      symbol_table_t* symbols = NULL;
+      if (backtrace_symbols[i].map_name != NULL) {
+        symbols = load_symbol_table(backtrace_symbols[i].map_name);
+      }
+      const symbol_t* symbol = NULL;
+      if (symbols != NULL) {
+        symbol = find_symbol(symbols, frames[i].absolute_pc);
+      }
+      if (symbol != NULL) {
+        uintptr_t offset = frames[i].absolute_pc - symbol->start;
+        fprintf(stderr, "  %s (%s%+d)\n", line, symbol->name, offset);
+      } else {
+        fprintf(stderr, "  %s (\?\?\?)\n", line);
+      }
+      free_symbol_table(symbols);
+    }
+  }
+
+  free_backtrace_symbols(backtrace_symbols, frame_count);
+  free(backtrace_symbols);
+  free(frames);
+}
+
+__attribute__ ((noinline)) void g() {
+  fprintf(stderr, "g()\n");
+  do_backtrace();
+}
+
+__attribute__ ((noinline)) int f(int i) {
+  fprintf(stderr, "f(%i)\n", i);
+  if (i == 0) {
+    g();
+    return 0;
+  }
+  return f(i - 1);
+}
+
+int main() {
+  return f(5);
+}
diff --git a/toolbox/dd.c b/toolbox/dd.c
index c6af3ea..53a6206 100644
--- a/toolbox/dd.c
+++ b/toolbox/dd.c
@@ -64,7 +64,7 @@
 
 #include "dd.h"
 
-#define NO_CONV
+//#define NO_CONV
 
 //#include "extern.h"
 void block(void);
@@ -91,12 +91,9 @@
 extern u_int		files_cnt;
 extern int		progress;
 extern const u_char	*ctab;
-extern const u_char	a2e_32V[], a2e_POSIX[];
-extern const u_char	e2a_32V[], e2a_POSIX[];
-extern const u_char	a2ibm_32V[], a2ibm_POSIX[];
-extern u_char		casetab[];
 
 
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
 #define MAX(a, b) ((a) > (b) ? (a) : (b))
 
 static void dd_close(void);
@@ -243,42 +240,6 @@
 	if ((ddflags & (C_OF | C_SEEK | C_NOTRUNC)) == (C_OF | C_SEEK))
 		(void)ftruncate(out.fd, (off_t)out.offset * out.dbsz);
 
-	/*
-	 * If converting case at the same time as another conversion, build a
-	 * table that does both at once.  If just converting case, use the
-	 * built-in tables.
-	 */
-	if (ddflags & (C_LCASE|C_UCASE)) {
-#ifdef	NO_CONV
-		/* Should not get here, but just in case... */
-		fprintf(stderr, "case conv and -DNO_CONV\n");
-		exit(1);
-		/* NOTREACHED */
-#else	/* NO_CONV */
-		u_int cnt;
-
-		if (ddflags & C_ASCII || ddflags & C_EBCDIC) {
-			if (ddflags & C_LCASE) {
-				for (cnt = 0; cnt < 0377; ++cnt)
-					casetab[cnt] = tolower(ctab[cnt]);
-			} else {
-				for (cnt = 0; cnt < 0377; ++cnt)
-					casetab[cnt] = toupper(ctab[cnt]);
-			}
-		} else {
-			if (ddflags & C_LCASE) {
-				for (cnt = 0; cnt < 0377; ++cnt)
-					casetab[cnt] = tolower(cnt);
-			} else {
-				for (cnt = 0; cnt < 0377; ++cnt)
-					casetab[cnt] = toupper(cnt);
-			}
-		}
-
-		ctab = casetab;
-#endif	/* NO_CONV */
-	}
-
 	(void)gettimeofday(&st.start, NULL);	/* Statistics timestamp. */
 }
 
@@ -796,6 +757,9 @@
 void
 def_close(void)
 {
+	if (ddflags & C_FDATASYNC) {
+		fdatasync(out.fd);
+	}
 
 	/* Just update the count, everything is already in the buffer. */
 	if (in.dbcnt)
@@ -1301,21 +1265,14 @@
 	u_int set, noset;
 	const u_char *ctab;
 } clist[] = {
-	{ "ascii",	C_ASCII,	C_EBCDIC,	e2a_POSIX },
 	{ "block",	C_BLOCK,	C_UNBLOCK,	NULL },
-	{ "ebcdic",	C_EBCDIC,	C_ASCII,	a2e_POSIX },
-	{ "ibm",	C_EBCDIC,	C_ASCII,	a2ibm_POSIX },
-	{ "lcase",	C_LCASE,	C_UCASE,	NULL },
+	{ "fdatasync",	C_FDATASYNC,	0,		NULL },
 	{ "noerror",	C_NOERROR,	0,		NULL },
 	{ "notrunc",	C_NOTRUNC,	0,		NULL },
-	{ "oldascii",	C_ASCII,	C_EBCDIC,	e2a_32V },
-	{ "oldebcdic",	C_EBCDIC,	C_ASCII,	a2e_32V },
-	{ "oldibm",	C_EBCDIC,	C_ASCII,	a2ibm_32V },
 	{ "osync",	C_OSYNC,	C_BS,		NULL },
 	{ "sparse",	C_SPARSE,	0,		NULL },
 	{ "swab",	C_SWAB,		0,		NULL },
 	{ "sync",	C_SYNC,		0,		NULL },
-	{ "ucase",	C_UCASE,	C_LCASE,	NULL },
 	{ "unblock",	C_UNBLOCK,	C_BLOCK,	NULL },
 	/* If you add items to this table, be sure to add the
 	 * conversions to the C_BS check in the jcl routine above.
diff --git a/toolbox/dd.h b/toolbox/dd.h
index cca1024..89f2833 100644
--- a/toolbox/dd.h
+++ b/toolbox/dd.h
@@ -91,3 +91,4 @@
 #define	C_UNBLOCK	0x80000
 #define	C_OSYNC		0x100000
 #define	C_SPARSE	0x200000
+#define	C_FDATASYNC	0x400000