Merge "Fix tracing on user builds"
diff --git a/adb/Android.mk b/adb/Android.mk
index 721b48d..155c6e5 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -130,7 +130,7 @@
 LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN)
 LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED)
 
-LOCAL_STATIC_LIBRARIES := liblog libcutils libc libmincrypt
+LOCAL_STATIC_LIBRARIES := liblog libcutils libc libmincrypt libselinux
 include $(BUILD_EXECUTABLE)
 
 
diff --git a/adb/adb.c b/adb/adb.c
index 72b7484..41270f9 100644
--- a/adb/adb.c
+++ b/adb/adb.c
@@ -562,7 +562,7 @@
         break;
 
     case A_OPEN: /* OPEN(local-id, 0, "destination") */
-        if (t->online) {
+        if (t->online && p->msg.arg0 != 0 && p->msg.arg1 == 0) {
             char *name = (char*) p->data;
             name[p->msg.data_length > 0 ? p->msg.data_length - 1 : 0] = 0;
             s = create_local_service_socket(name);
@@ -578,28 +578,50 @@
         break;
 
     case A_OKAY: /* READY(local-id, remote-id, "") */
-        if (t->online) {
-            if((s = find_local_socket(p->msg.arg1))) {
+        if (t->online && p->msg.arg0 != 0 && p->msg.arg1 != 0) {
+            if((s = find_local_socket(p->msg.arg1, 0))) {
                 if(s->peer == 0) {
+                    /* On first READY message, create the connection. */
                     s->peer = create_remote_socket(p->msg.arg0, t);
                     s->peer->peer = s;
+                    s->ready(s);
+                } else if (s->peer->id == p->msg.arg0) {
+                    /* Other READY messages must use the same local-id */
+                    s->ready(s);
+                } else {
+                    D("Invalid A_OKAY(%d,%d), expected A_OKAY(%d,%d) on transport %s\n",
+                      p->msg.arg0, p->msg.arg1, s->peer->id, p->msg.arg1, t->serial);
                 }
-                s->ready(s);
             }
         }
         break;
 
-    case A_CLSE: /* CLOSE(local-id, remote-id, "") */
-        if (t->online) {
-            if((s = find_local_socket(p->msg.arg1))) {
-                s->close(s);
+    case A_CLSE: /* CLOSE(local-id, remote-id, "") or CLOSE(0, remote-id, "") */
+        if (t->online && p->msg.arg1 != 0) {
+            if((s = find_local_socket(p->msg.arg1, p->msg.arg0))) {
+                /* According to protocol.txt, p->msg.arg0 might be 0 to indicate
+                 * a failed OPEN only. However, due to a bug in previous ADB
+                 * versions, CLOSE(0, remote-id, "") was also used for normal
+                 * CLOSE() operations.
+                 *
+                 * This is bad because it means a compromised adbd could
+                 * send packets to close connections between the host and
+                 * other devices. To avoid this, only allow this if the local
+                 * socket has a peer on the same transport.
+                 */
+                if (p->msg.arg0 == 0 && s->peer && s->peer->transport != t) {
+                    D("Invalid A_CLSE(0, %u) from transport %s, expected transport %s\n",
+                      p->msg.arg1, t->serial, s->peer->transport->serial);
+                } else {
+                    s->close(s);
+                }
             }
         }
         break;
 
-    case A_WRTE:
-        if (t->online) {
-            if((s = find_local_socket(p->msg.arg1))) {
+    case A_WRTE: /* WRITE(local-id, remote-id, <data>) */
+        if (t->online && p->msg.arg0 != 0 && p->msg.arg1 != 0) {
+            if((s = find_local_socket(p->msg.arg1, p->msg.arg0))) {
                 unsigned rid = p->msg.arg0;
                 p->len = p->msg.data_length;
 
diff --git a/adb/adb.h b/adb/adb.h
index 622ca70..c85b02a 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -122,6 +122,12 @@
         */
     void (*ready)(asocket *s);
 
+        /* shutdown is called by the peer before it goes away.
+        ** the socket should not do any further calls on its peer.
+        ** Always followed by a call to close. Optional, i.e. can be NULL.
+        */
+    void (*shutdown)(asocket *s);
+
         /* close is called by the peer when it has gone away.
         ** we are not allowed to make any further calls on the
         ** peer once our close method is called.
@@ -233,7 +239,7 @@
 
 void print_packet(const char *label, apacket *p);
 
-asocket *find_local_socket(unsigned id);
+asocket *find_local_socket(unsigned local_id, unsigned remote_id);
 void install_local_socket(asocket *s);
 void remove_socket(asocket *s);
 void close_all_sockets(atransport *t);
diff --git a/adb/file_sync_service.c b/adb/file_sync_service.c
index f24f14c..c30f9fb 100644
--- a/adb/file_sync_service.c
+++ b/adb/file_sync_service.c
@@ -22,19 +22,32 @@
 #include <sys/types.h>
 #include <dirent.h>
 #include <utime.h>
+#include <unistd.h>
 
 #include <errno.h>
-
+#include <private/android_filesystem_config.h>
+#include <selinux/android.h>
 #include "sysdeps.h"
 
 #define TRACE_TAG  TRACE_SYNC
 #include "adb.h"
 #include "file_sync_service.h"
 
+/* TODO: use fs_config to configure permissions on /data */
+static bool is_on_system(const char *name) {
+    const char *SYSTEM = "/system/";
+    return (strncmp(SYSTEM, name, strlen(SYSTEM)) == 0);
+}
+
 static int mkdirs(char *name)
 {
     int ret;
     char *x = name + 1;
+    unsigned int uid, gid;
+    unsigned int mode = 0775;
+    uint64_t cap = 0;
+    uid = getuid();
+    gid = getgid();
 
     if(name[0] != '/') return -1;
 
@@ -42,11 +55,21 @@
         x = adb_dirstart(x);
         if(x == 0) return 0;
         *x = 0;
-        ret = adb_mkdir(name, 0775);
+        if (is_on_system(name)) {
+            fs_config(name, 1, &uid, &gid, &mode, &cap);
+        }
+        ret = adb_mkdir(name, mode);
         if((ret < 0) && (errno != EEXIST)) {
             D("mkdir(\"%s\") -> %s\n", name, strerror(errno));
             *x = '/';
             return ret;
+        } else if(ret == 0) {
+            ret = chown(name, uid, gid);
+            if (ret < 0) {
+                *x = '/';
+                return ret;
+            }
+            selinux_android_restorecon(name);
         }
         *x++ = '/';
     }
@@ -149,7 +172,8 @@
     return fail_message(s, strerror(errno));
 }
 
-static int handle_send_file(int s, char *path, mode_t mode, char *buffer)
+static int handle_send_file(int s, char *path, unsigned int uid,
+        unsigned int gid, mode_t mode, char *buffer)
 {
     syncmsg msg;
     unsigned int timestamp = 0;
@@ -157,8 +181,13 @@
 
     fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL, mode);
     if(fd < 0 && errno == ENOENT) {
-        mkdirs(path);
-        fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL, mode);
+        if(mkdirs(path) != 0) {
+            if(fail_errno(s))
+                return -1;
+            fd = -1;
+        } else {
+            fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL, mode);
+        }
     }
     if(fd < 0 && errno == EEXIST) {
         fd = adb_open_mode(path, O_WRONLY, mode);
@@ -167,6 +196,16 @@
         if(fail_errno(s))
             return -1;
         fd = -1;
+    } else {
+        if(fchown(fd, uid, gid) != 0) {
+            fail_errno(s);
+            errno = 0;
+        }
+        /* fchown clears the setuid bit - restore it if present */
+        if(fchmod(fd, mode) != 0) {
+            fail_errno(s);
+            errno = 0;
+        }
     }
 
     for(;;) {
@@ -206,6 +245,7 @@
     if(fd >= 0) {
         struct utimbuf u;
         adb_close(fd);
+        selinux_android_restorecon(path);
         u.actime = timestamp;
         u.modtime = timestamp;
         utime(path, &u);
@@ -249,7 +289,10 @@
 
     ret = symlink(buffer, path);
     if(ret && errno == ENOENT) {
-        mkdirs(path);
+        if(mkdirs(path) != 0) {
+            fail_errno(s);
+            return -1;
+        }
         ret = symlink(buffer, path);
     }
     if(ret) {
@@ -277,7 +320,7 @@
 static int do_send(int s, char *path, char *buffer)
 {
     char *tmp;
-    mode_t mode;
+    unsigned int mode;
     int is_link, ret;
 
     tmp = strrchr(path,',');
@@ -288,7 +331,7 @@
 #ifndef HAVE_SYMLINKS
         is_link = 0;
 #else
-        is_link = S_ISLNK(mode);
+        is_link = S_ISLNK((mode_t) mode);
 #endif
         mode &= 0777;
     }
@@ -307,11 +350,23 @@
 #else
     {
 #endif
+        unsigned int uid, gid;
+        uint64_t cap = 0;
+        uid = getuid();
+        gid = getgid();
+
         /* copy user permission bits to "group" and "other" permissions */
         mode |= ((mode >> 3) & 0070);
         mode |= ((mode >> 3) & 0007);
 
-        ret = handle_send_file(s, path, mode, buffer);
+        tmp = path;
+        if(*tmp == '/') {
+            tmp++;
+        }
+        if (is_on_system(path)) {
+            fs_config(tmp, 0, &uid, &gid, &mode, &cap);
+        }
+        ret = handle_send_file(s, path, uid, gid, mode, buffer);
     }
 
     return ret;
diff --git a/adb/services.c b/adb/services.c
index f0d5878..951048e 100644
--- a/adb/services.c
+++ b/adb/services.c
@@ -144,7 +144,11 @@
     if (ret < 0) {
         snprintf(buf, sizeof(buf), "reboot failed: %d\n", ret);
         writex(fd, buf, strlen(buf));
+        goto cleanup;
     }
+    // Don't return early. Give the reboot command time to take effect
+    // to avoid messing up scripts which do "adb reboot && adb wait-for-device"
+    while(1) { pause(); }
 cleanup:
     free(arg);
     adb_close(fd);
diff --git a/adb/sockets.c b/adb/sockets.c
index f17608b..7f54ad9 100644
--- a/adb/sockets.c
+++ b/adb/sockets.c
@@ -59,17 +59,22 @@
     .prev = &local_socket_closing_list,
 };
 
-asocket *find_local_socket(unsigned id)
+// Parse the global list of sockets to find one with id |local_id|.
+// If |peer_id| is not 0, also check that it is connected to a peer
+// with id |peer_id|. Returns an asocket handle on success, NULL on failure.
+asocket *find_local_socket(unsigned local_id, unsigned peer_id)
 {
     asocket *s;
     asocket *result = NULL;
 
     adb_mutex_lock(&socket_list_lock);
     for (s = local_socket_list.next; s != &local_socket_list; s = s->next) {
-        if (s->id == id) {
+        if (s->id != local_id)
+            continue;
+        if (peer_id == 0 || (s->peer && s->peer->id == peer_id)) {
             result = s;
-            break;
         }
+        break;
     }
     adb_mutex_unlock(&socket_list_lock);
 
@@ -91,6 +96,11 @@
     adb_mutex_lock(&socket_list_lock);
 
     s->id = local_socket_next_id++;
+
+    // Socket ids should never be 0.
+    if (local_socket_next_id == 0)
+      local_socket_next_id = 1;
+
     insert_local_socket(s, &local_socket_list);
 
     adb_mutex_unlock(&socket_list_lock);
@@ -230,6 +240,12 @@
     if(s->peer) {
         D("LS(%d): closing peer. peer->id=%d peer->fd=%d\n",
           s->id, s->peer->id, s->peer->fd);
+        /* Note: it's important to call shutdown before disconnecting from
+         * the peer, this ensures that remote sockets can still get the id
+         * of the local socket they're connected to, to send a CLOSE()
+         * protocol event. */
+        if (s->peer->shutdown)
+          s->peer->shutdown(s->peer);
         s->peer->peer = 0;
         // tweak to avoid deadlock
         if (s->peer->close == local_socket_close) {
@@ -397,6 +413,7 @@
     s->fd = fd;
     s->enqueue = local_socket_enqueue;
     s->ready = local_socket_ready;
+    s->shutdown = NULL;
     s->close = local_socket_close;
     install_local_socket(s);
 
@@ -485,21 +502,29 @@
     send_packet(p, s->transport);
 }
 
-static void remote_socket_close(asocket *s)
+static void remote_socket_shutdown(asocket *s)
 {
-    D("entered remote_socket_close RS(%d) CLOSE fd=%d peer->fd=%d\n",
+    D("entered remote_socket_shutdown RS(%d) CLOSE fd=%d peer->fd=%d\n",
       s->id, s->fd, s->peer?s->peer->fd:-1);
     apacket *p = get_apacket();
     p->msg.command = A_CLSE;
     if(s->peer) {
         p->msg.arg0 = s->peer->id;
+    }
+    p->msg.arg1 = s->id;
+    send_packet(p, s->transport);
+}
+
+static void remote_socket_close(asocket *s)
+{
+    if (s->peer) {
         s->peer->peer = 0;
         D("RS(%d) peer->close()ing peer->id=%d peer->fd=%d\n",
           s->id, s->peer->id, s->peer->fd);
         s->peer->close(s->peer);
     }
-    p->msg.arg1 = s->id;
-    send_packet(p, s->transport);
+    D("entered remote_socket_close RS(%d) CLOSE fd=%d peer->fd=%d\n",
+      s->id, s->fd, s->peer?s->peer->fd:-1);
     D("RS(%d): closed\n", s->id);
     remove_transport_disconnect( s->transport, &((aremotesocket*)s)->disconnect );
     free(s);
@@ -519,15 +544,24 @@
     free(s);
 }
 
+/* Create an asocket to exchange packets with a remote service through transport
+  |t|. Where |id| is the socket id of the corresponding service on the other
+   side of the transport (it is allocated by the remote side and _cannot_ be 0).
+   Returns a new non-NULL asocket handle. */
 asocket *create_remote_socket(unsigned id, atransport *t)
 {
-    asocket *s = calloc(1, sizeof(aremotesocket));
-    adisconnect*  dis = &((aremotesocket*)s)->disconnect;
+    asocket* s;
+    adisconnect* dis;
+
+    if (id == 0) fatal("invalid remote socket id (0)");
+    s = calloc(1, sizeof(aremotesocket));
+    dis = &((aremotesocket*)s)->disconnect;
 
     if (s == NULL) fatal("cannot allocate socket");
     s->id = id;
     s->enqueue = remote_socket_enqueue;
     s->ready = remote_socket_ready;
+    s->shutdown = remote_socket_shutdown;
     s->close = remote_socket_close;
     s->transport = t;
 
@@ -562,6 +596,7 @@
 static void local_socket_ready_notify(asocket *s)
 {
     s->ready = local_socket_ready;
+    s->shutdown = NULL;
     s->close = local_socket_close;
     adb_write(s->fd, "OKAY", 4);
     s->ready(s);
@@ -573,6 +608,7 @@
 static void local_socket_close_notify(asocket *s)
 {
     s->ready = local_socket_ready;
+    s->shutdown = NULL;
     s->close = local_socket_close;
     sendfailmsg(s->fd, "closed");
     s->close(s);
@@ -767,6 +803,7 @@
         adb_write(s->peer->fd, "OKAY", 4);
 
         s->peer->ready = local_socket_ready;
+        s->peer->shutdown = NULL;
         s->peer->close = local_socket_close;
         s->peer->peer = s2;
         s2->peer = s->peer;
@@ -806,6 +843,7 @@
         ** tear down
         */
     s->peer->ready = local_socket_ready_notify;
+    s->peer->shutdown = NULL;
     s->peer->close = local_socket_close_notify;
     s->peer->peer = 0;
         /* give him our transport and upref it */
@@ -851,6 +889,7 @@
     if (s == NULL) fatal("cannot allocate socket");
     s->enqueue = smart_socket_enqueue;
     s->ready = smart_socket_ready;
+    s->shutdown = NULL;
     s->close = smart_socket_close;
 
     D("SS(%d)\n", s->id);
diff --git a/adb/usb_vendors.c b/adb/usb_vendors.c
old mode 100644
new mode 100755
index 19b3022..df1244f
--- a/adb/usb_vendors.c
+++ b/adb/usb_vendors.c
@@ -35,196 +35,221 @@
 
 #define TRACE_TAG               TRACE_USB
 
-// Google's USB Vendor ID
-#define VENDOR_ID_GOOGLE        0x18d1
-// Intel's USB Vendor ID
-#define VENDOR_ID_INTEL         0x8087
-// HTC's USB Vendor ID
-#define VENDOR_ID_HTC           0x0bb4
-// Samsung's USB Vendor ID
-#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
+/* Keep the list below sorted alphabetically by #define name */
 // Acer's USB Vendor ID
 #define VENDOR_ID_ACER          0x0502
-// Sony Ericsson's USB Vendor ID
-#define VENDOR_ID_SONY_ERICSSON 0x0FCE
-// Foxconn's USB Vendor ID
-#define VENDOR_ID_FOXCONN       0x0489
-// Dell's USB Vendor ID
-#define VENDOR_ID_DELL          0x413c
-// Nvidia's USB Vendor ID
-#define VENDOR_ID_NVIDIA        0x0955
-// Garmin-Asus's USB Vendor ID
-#define VENDOR_ID_GARMIN_ASUS   0x091E
-// Sharp's USB Vendor ID
-#define VENDOR_ID_SHARP         0x04dd
-// ZTE's USB Vendor ID
-#define VENDOR_ID_ZTE           0x19D2
-// Kyocera's USB Vendor ID
-#define VENDOR_ID_KYOCERA       0x0482
-// Pantech's USB Vendor ID
-#define VENDOR_ID_PANTECH       0x10A9
-// Qualcomm's USB Vendor ID
-#define VENDOR_ID_QUALCOMM      0x05c6
-// On-The-Go-Video's USB Vendor ID
-#define VENDOR_ID_OTGV          0x2257
-// NEC's USB Vendor ID
-#define VENDOR_ID_NEC           0x0409
-// Panasonic Mobile Communication's USB Vendor ID
-#define VENDOR_ID_PMC           0x04DA
-// Toshiba's USB Vendor ID
-#define VENDOR_ID_TOSHIBA       0x0930
-// SK Telesys's USB Vendor ID
-#define VENDOR_ID_SK_TELESYS    0x1F53
-// KT Tech's USB Vendor ID
-#define VENDOR_ID_KT_TECH       0x2116
-// Asus's USB Vendor ID
-#define VENDOR_ID_ASUS          0x0b05
-// Philips's USB Vendor ID
-#define VENDOR_ID_PHILIPS       0x0471
-// Texas Instruments's USB Vendor ID
-#define VENDOR_ID_TI            0x0451
-// Funai's USB Vendor ID
-#define VENDOR_ID_FUNAI         0x0F1C
-// Gigabyte's USB Vendor ID
-#define VENDOR_ID_GIGABYTE      0x0414
-// IRiver's USB Vendor ID
-#define VENDOR_ID_IRIVER        0x2420
-// Compal's USB Vendor ID
-#define VENDOR_ID_COMPAL        0x1219
-// T & A Mobile Phones' USB Vendor ID
-#define VENDOR_ID_T_AND_A       0x1BBB
-// LenovoMobile's USB Vendor ID
-#define VENDOR_ID_LENOVOMOBILE  0x2006
-// Lenovo's USB Vendor ID
-#define VENDOR_ID_LENOVO        0x17EF
-// Vizio's USB Vendor ID
-#define VENDOR_ID_VIZIO         0xE040
-// K-Touch's USB Vendor ID
-#define VENDOR_ID_K_TOUCH       0x24E3
-// Pegatron's USB Vendor ID
-#define VENDOR_ID_PEGATRON      0x1D4D
-// Archos's USB Vendor ID
-#define VENDOR_ID_ARCHOS        0x0E79
-// Positivo's USB Vendor ID
-#define VENDOR_ID_POSITIVO      0x1662
-// Fujitsu's USB Vendor ID
-#define VENDOR_ID_FUJITSU       0x04C5
-// Lumigon's USB Vendor ID
-#define VENDOR_ID_LUMIGON       0x25E3
-// Quanta's USB Vendor ID
-#define VENDOR_ID_QUANTA        0x0408
-// INQ Mobile's USB Vendor ID
-#define VENDOR_ID_INQ_MOBILE    0x2314
-// Sony's USB Vendor ID
-#define VENDOR_ID_SONY          0x054C
-// Lab126's USB Vendor ID
-#define VENDOR_ID_LAB126        0x1949
-// Yulong Coolpad's USB Vendor ID
-#define VENDOR_ID_YULONG_COOLPAD 0x1EBF
-// Kobo's USB Vendor ID
-#define VENDOR_ID_KOBO          0x2237
-// Teleepoch's USB Vendor ID
-#define VENDOR_ID_TELEEPOCH     0x2340
+// Allwinner's USB Vendor ID
+#define VENDOR_ID_ALLWINNER     0x1F3A
+// Amlogic's USB Vendor ID
+#define VENDOR_ID_AMLOGIC       0x1b8e
 // AnyDATA's USB Vendor ID
 #define VENDOR_ID_ANYDATA       0x16D5
-// Harris's USB Vendor ID
-#define VENDOR_ID_HARRIS        0x19A5
-// OPPO's USB Vendor ID
-#define VENDOR_ID_OPPO          0x22D9
-// Xiaomi's USB Vendor ID
-#define VENDOR_ID_XIAOMI        0x2717
+// Archos's USB Vendor ID
+#define VENDOR_ID_ARCHOS        0x0E79
+// Asus's USB Vendor ID
+#define VENDOR_ID_ASUS          0x0b05
 // BYD's USB Vendor ID
-#define VENDOR_ID_BYD           0x19D1
-// OUYA's USB Vendor ID
-#define VENDOR_ID_OUYA          0x2836
-// Haier's USB Vendor ID
-#define VENDOR_ID_HAIER         0x201E
-// Hisense's USB Vendor ID
-#define VENDOR_ID_HISENSE       0x109b
-// MTK's USB Vendor ID
-#define VENDOR_ID_MTK           0x0e8d
-// B&N Nook's USB Vendor ID
-#define VENDOR_ID_NOOK          0x2080
-// Qisda's USB Vendor ID
-#define VENDOR_ID_QISDA         0x1D45
+#define VENDOR_ID_BYD           0x1D91
+// Compal's USB Vendor ID
+#define VENDOR_ID_COMPAL        0x1219
+// Dell's USB Vendor ID
+#define VENDOR_ID_DELL          0x413c
 // ECS's USB Vendor ID
 #define VENDOR_ID_ECS           0x03fc
+// EMERGING_TECH's USB Vendor ID
+#define VENDOR_ID_EMERGING_TECH 0x297F
+// Foxconn's USB Vendor ID
+#define VENDOR_ID_FOXCONN       0x0489
+// Fujitsu's USB Vendor ID
+#define VENDOR_ID_FUJITSU       0x04C5
+// Funai's USB Vendor ID
+#define VENDOR_ID_FUNAI         0x0F1C
+// Garmin-Asus's USB Vendor ID
+#define VENDOR_ID_GARMIN_ASUS   0x091E
+// Gigabyte's USB Vendor ID
+#define VENDOR_ID_GIGABYTE      0x0414
+// Gigaset's USB Vendor ID
+#define VENDOR_ID_GIGASET       0x1E85
+// Google's USB Vendor ID
+#define VENDOR_ID_GOOGLE        0x18d1
+// Haier's USB Vendor ID
+#define VENDOR_ID_HAIER         0x201E
+// Harris's USB Vendor ID
+#define VENDOR_ID_HARRIS        0x19A5
+// Hisense's USB Vendor ID
+#define VENDOR_ID_HISENSE       0x109b
+// HTC's USB Vendor ID
+#define VENDOR_ID_HTC           0x0bb4
+// Huawei's USB Vendor ID
+#define VENDOR_ID_HUAWEI        0x12D1
+// INQ Mobile's USB Vendor ID
+#define VENDOR_ID_INQ_MOBILE    0x2314
+// Intel's USB Vendor ID
+#define VENDOR_ID_INTEL         0x8087
+// IRiver's USB Vendor ID
+#define VENDOR_ID_IRIVER        0x2420
+// K-Touch's USB Vendor ID
+#define VENDOR_ID_K_TOUCH       0x24E3
+// KT Tech's USB Vendor ID
+#define VENDOR_ID_KT_TECH       0x2116
+// Kobo's USB Vendor ID
+#define VENDOR_ID_KOBO          0x2237
+// Kyocera's USB Vendor ID
+#define VENDOR_ID_KYOCERA       0x0482
+// Lab126's USB Vendor ID
+#define VENDOR_ID_LAB126        0x1949
+// Lenovo's USB Vendor ID
+#define VENDOR_ID_LENOVO        0x17EF
+// LenovoMobile's USB Vendor ID
+#define VENDOR_ID_LENOVOMOBILE  0x2006
+// LG's USB Vendor ID
+#define VENDOR_ID_LGE           0x1004
+// Lumigon's USB Vendor ID
+#define VENDOR_ID_LUMIGON       0x25E3
+// Motorola's USB Vendor ID
+#define VENDOR_ID_MOTOROLA      0x22b8
 // MSI's USB Vendor ID
 #define VENDOR_ID_MSI           0x0DB0
+// MTK's USB Vendor ID
+#define VENDOR_ID_MTK           0x0e8d
+// NEC's USB Vendor ID
+#define VENDOR_ID_NEC           0x0409
+// B&N Nook's USB Vendor ID
+#define VENDOR_ID_NOOK          0x2080
+// Nvidia's USB Vendor ID
+#define VENDOR_ID_NVIDIA        0x0955
+// OPPO's USB Vendor ID
+#define VENDOR_ID_OPPO          0x22D9
+// On-The-Go-Video's USB Vendor ID
+#define VENDOR_ID_OTGV          0x2257
+// OUYA's USB Vendor ID
+#define VENDOR_ID_OUYA          0x2836
+// Pantech's USB Vendor ID
+#define VENDOR_ID_PANTECH       0x10A9
+// Pegatron's USB Vendor ID
+#define VENDOR_ID_PEGATRON      0x1D4D
+// Philips's USB Vendor ID
+#define VENDOR_ID_PHILIPS       0x0471
+// Panasonic Mobile Communication's USB Vendor ID
+#define VENDOR_ID_PMC           0x04DA
+// Positivo's USB Vendor ID
+#define VENDOR_ID_POSITIVO      0x1662
+// Qisda's USB Vendor ID
+#define VENDOR_ID_QISDA         0x1D45
+// Qualcomm's USB Vendor ID
+#define VENDOR_ID_QUALCOMM      0x05c6
+// Quanta's USB Vendor ID
+#define VENDOR_ID_QUANTA        0x0408
+// Rockchip's USB Vendor ID
+#define VENDOR_ID_ROCKCHIP      0x2207
+// Samsung's USB Vendor ID
+#define VENDOR_ID_SAMSUNG       0x04e8
+// Sharp's USB Vendor ID
+#define VENDOR_ID_SHARP         0x04dd
+// SK Telesys's USB Vendor ID
+#define VENDOR_ID_SK_TELESYS    0x1F53
+// Sony's USB Vendor ID
+#define VENDOR_ID_SONY          0x054C
+// Sony Ericsson's USB Vendor ID
+#define VENDOR_ID_SONY_ERICSSON 0x0FCE
+// T & A Mobile Phones' USB Vendor ID
+#define VENDOR_ID_T_AND_A       0x1BBB
+// TechFaith's USB Vendor ID
+#define VENDOR_ID_TECHFAITH     0x1d09
+// Teleepoch's USB Vendor ID
+#define VENDOR_ID_TELEEPOCH     0x2340
+// Texas Instruments's USB Vendor ID
+#define VENDOR_ID_TI            0x0451
+// Toshiba's USB Vendor ID
+#define VENDOR_ID_TOSHIBA       0x0930
+// Vizio's USB Vendor ID
+#define VENDOR_ID_VIZIO         0xE040
 // Wacom's USB Vendor ID
 #define VENDOR_ID_WACOM         0x0531
+// Xiaomi's USB Vendor ID
+#define VENDOR_ID_XIAOMI        0x2717
+// YotaDevices's USB Vendor ID
+#define VENDOR_ID_YOTADEVICES   0x2916
+// Yulong Coolpad's USB Vendor ID
+#define VENDOR_ID_YULONG_COOLPAD 0x1EBF
+// ZTE's USB Vendor ID
+#define VENDOR_ID_ZTE           0x19D2
+/* Keep the list above sorted alphabetically by #define name */
 
 /** built-in vendor list */
+/* Keep the list below sorted alphabetically */
 int builtInVendorIds[] = {
-    VENDOR_ID_GOOGLE,
-    VENDOR_ID_INTEL,
-    VENDOR_ID_HTC,
-    VENDOR_ID_SAMSUNG,
-    VENDOR_ID_MOTOROLA,
-    VENDOR_ID_LGE,
-    VENDOR_ID_HUAWEI,
     VENDOR_ID_ACER,
-    VENDOR_ID_SONY_ERICSSON,
-    VENDOR_ID_FOXCONN,
-    VENDOR_ID_DELL,
-    VENDOR_ID_NVIDIA,
-    VENDOR_ID_GARMIN_ASUS,
-    VENDOR_ID_SHARP,
-    VENDOR_ID_ZTE,
-    VENDOR_ID_KYOCERA,
-    VENDOR_ID_PANTECH,
-    VENDOR_ID_QUALCOMM,
-    VENDOR_ID_OTGV,
-    VENDOR_ID_NEC,
-    VENDOR_ID_PMC,
-    VENDOR_ID_TOSHIBA,
-    VENDOR_ID_SK_TELESYS,
-    VENDOR_ID_KT_TECH,
-    VENDOR_ID_ASUS,
-    VENDOR_ID_PHILIPS,
-    VENDOR_ID_TI,
-    VENDOR_ID_FUNAI,
-    VENDOR_ID_GIGABYTE,
-    VENDOR_ID_IRIVER,
-    VENDOR_ID_COMPAL,
-    VENDOR_ID_T_AND_A,
-    VENDOR_ID_LENOVOMOBILE,
-    VENDOR_ID_LENOVO,
-    VENDOR_ID_VIZIO,
-    VENDOR_ID_K_TOUCH,
-    VENDOR_ID_PEGATRON,
-    VENDOR_ID_ARCHOS,
-    VENDOR_ID_POSITIVO,
-    VENDOR_ID_FUJITSU,
-    VENDOR_ID_LUMIGON,
-    VENDOR_ID_QUANTA,
-    VENDOR_ID_INQ_MOBILE,
-    VENDOR_ID_SONY,
-    VENDOR_ID_LAB126,
-    VENDOR_ID_YULONG_COOLPAD,
-    VENDOR_ID_KOBO,
-    VENDOR_ID_TELEEPOCH,
+    VENDOR_ID_ALLWINNER,
+    VENDOR_ID_AMLOGIC,
     VENDOR_ID_ANYDATA,
-    VENDOR_ID_HARRIS,
-    VENDOR_ID_OPPO,
-    VENDOR_ID_XIAOMI,
+    VENDOR_ID_ARCHOS,
+    VENDOR_ID_ASUS,
     VENDOR_ID_BYD,
-    VENDOR_ID_OUYA,
-    VENDOR_ID_HAIER,
-    VENDOR_ID_HISENSE,
-    VENDOR_ID_MTK,
-    VENDOR_ID_NOOK,
-    VENDOR_ID_QISDA,
+    VENDOR_ID_COMPAL,
+    VENDOR_ID_DELL,
     VENDOR_ID_ECS,
+    VENDOR_ID_EMERGING_TECH,
+    VENDOR_ID_FOXCONN,
+    VENDOR_ID_FUJITSU,
+    VENDOR_ID_FUNAI,
+    VENDOR_ID_GARMIN_ASUS,
+    VENDOR_ID_GIGABYTE,
+    VENDOR_ID_GIGASET,
+    VENDOR_ID_GOOGLE,
+    VENDOR_ID_HAIER,
+    VENDOR_ID_HARRIS,
+    VENDOR_ID_HISENSE,
+    VENDOR_ID_HTC,
+    VENDOR_ID_HUAWEI,
+    VENDOR_ID_INQ_MOBILE,
+    VENDOR_ID_INTEL,
+    VENDOR_ID_IRIVER,
+    VENDOR_ID_KOBO,
+    VENDOR_ID_K_TOUCH,
+    VENDOR_ID_KT_TECH,
+    VENDOR_ID_KYOCERA,
+    VENDOR_ID_LAB126,
+    VENDOR_ID_LENOVO,
+    VENDOR_ID_LENOVOMOBILE,
+    VENDOR_ID_LGE,
+    VENDOR_ID_LUMIGON,
+    VENDOR_ID_MOTOROLA,
     VENDOR_ID_MSI,
+    VENDOR_ID_MTK,
+    VENDOR_ID_NEC,
+    VENDOR_ID_NOOK,
+    VENDOR_ID_NVIDIA,
+    VENDOR_ID_OPPO,
+    VENDOR_ID_OTGV,
+    VENDOR_ID_OUYA,
+    VENDOR_ID_PANTECH,
+    VENDOR_ID_PEGATRON,
+    VENDOR_ID_PHILIPS,
+    VENDOR_ID_PMC,
+    VENDOR_ID_POSITIVO,
+    VENDOR_ID_QISDA,
+    VENDOR_ID_QUALCOMM,
+    VENDOR_ID_QUANTA,
+    VENDOR_ID_ROCKCHIP,
+    VENDOR_ID_SAMSUNG,
+    VENDOR_ID_SHARP,
+    VENDOR_ID_SK_TELESYS,
+    VENDOR_ID_SONY,
+    VENDOR_ID_SONY_ERICSSON,
+    VENDOR_ID_T_AND_A,
+    VENDOR_ID_TECHFAITH,
+    VENDOR_ID_TELEEPOCH,
+    VENDOR_ID_TI,
+    VENDOR_ID_TOSHIBA,
+    VENDOR_ID_VIZIO,
     VENDOR_ID_WACOM,
+    VENDOR_ID_XIAOMI,
+    VENDOR_ID_YOTADEVICES,
+    VENDOR_ID_YULONG_COOLPAD,
+    VENDOR_ID_ZTE,
 };
+/* Keep the list above sorted alphabetically */
 
 #define BUILT_IN_VENDOR_COUNT    (sizeof(builtInVendorIds)/sizeof(builtInVendorIds[0]))
 
diff --git a/adf/Android.mk b/adf/Android.mk
new file mode 100644
index 0000000..64d486e
--- /dev/null
+++ b/adf/Android.mk
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2013 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# 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.
+#
+LOCAL_PATH := $(my-dir)
+
+include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/adf/libadf/Android.mk b/adf/libadf/Android.mk
new file mode 100644
index 0000000..908aa6c
--- /dev/null
+++ b/adf/libadf/Android.mk
@@ -0,0 +1,23 @@
+# Copyright (C) 2013 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := adf.c
+LOCAL_MODULE := libadf
+LOCAL_MODULE_TAGS := optional
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES += $(LOCAL_EXPORT_C_INCLUDE_DIRS)
+include $(BUILD_STATIC_LIBRARY)
diff --git a/adf/libadf/adf.c b/adf/libadf/adf.c
new file mode 100644
index 0000000..871629e
--- /dev/null
+++ b/adf/libadf/adf.c
@@ -0,0 +1,810 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ */
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <linux/limits.h>
+
+#include <sys/ioctl.h>
+
+#include <adf/adf.h>
+
+#define ADF_BASE_PATH "/dev/"
+
+static ssize_t adf_find_nodes(const char *pattern, adf_id_t **ids)
+{
+    DIR *dir;
+    struct dirent *dirent;
+    size_t n = 0;
+    ssize_t ret;
+    adf_id_t *ids_ret = NULL;
+
+    dir = opendir(ADF_BASE_PATH);
+    if (!dir)
+        return -errno;
+
+    errno = 0;
+    while ((dirent = readdir(dir))) {
+        adf_id_t id;
+        int matched = sscanf(dirent->d_name, pattern, &id);
+
+        if (matched < 0) {
+            ret = -errno;
+            goto done;
+        } else if (matched != 1) {
+            continue;
+        }
+
+        adf_id_t *new_ids = realloc(ids_ret, (n + 1) * sizeof(ids_ret[0]));
+        if (!new_ids) {
+            ret = -ENOMEM;
+            goto done;
+        }
+
+        ids_ret = new_ids;
+        ids_ret[n] = id;
+        n++;
+    }
+    if (errno)
+        ret = -errno;
+    else
+        ret = n;
+
+done:
+    closedir(dir);
+    if (ret < 0)
+        free(ids_ret);
+    else
+        *ids = ids_ret;
+    return ret;
+}
+
+ssize_t adf_devices(adf_id_t **ids)
+{
+    return adf_find_nodes("adf%u", ids);
+}
+
+int adf_device_open(adf_id_t id, int flags, struct adf_device *dev)
+{
+    char filename[64];
+    int err;
+
+    dev->id = id;
+
+    snprintf(filename, sizeof(filename), ADF_BASE_PATH "adf%u", id);
+    dev->fd = open(filename, flags);
+    if (dev->fd < 0)
+        return -errno;
+
+    return 0;
+}
+
+void adf_device_close(struct adf_device *dev)
+{
+    if (dev->fd >= 0)
+        close(dev->fd);
+}
+
+int adf_get_device_data(struct adf_device *dev, struct adf_device_data *data)
+{
+    int err;
+    int ret = 0;
+
+    memset(data, 0, sizeof(*data));
+
+    err = ioctl(dev->fd, ADF_GET_DEVICE_DATA, data);
+    if (err < 0)
+        return -ENOMEM;
+
+    if (data->n_attachments) {
+        data->attachments = malloc(sizeof(data->attachments[0]) *
+                data->n_attachments);
+        if (!data->attachments)
+            return -ENOMEM;
+    }
+
+    if (data->n_allowed_attachments) {
+        data->allowed_attachments =
+                malloc(sizeof(data->allowed_attachments[0]) *
+                        data->n_allowed_attachments);
+        if (!data->allowed_attachments) {
+            ret = -ENOMEM;
+            goto done;
+        }
+    }
+
+    if (data->custom_data_size) {
+        data->custom_data = malloc(data->custom_data_size);
+        if (!data->custom_data) {
+            ret = -ENOMEM;
+            goto done;
+        }
+    }
+
+    err = ioctl(dev->fd, ADF_GET_DEVICE_DATA, data);
+    if (err < 0)
+        ret = -errno;
+
+done:
+    if (ret < 0)
+        adf_free_device_data(data);
+    return ret;
+}
+
+void adf_free_device_data(struct adf_device_data *data)
+{
+    free(data->attachments);
+    free(data->allowed_attachments);
+    free(data->custom_data);
+}
+
+int adf_device_post(struct adf_device *dev,
+        adf_id_t *interfaces, size_t n_interfaces,
+        struct adf_buffer_config *bufs, size_t n_bufs,
+        void *custom_data, size_t custom_data_size)
+{
+    int err;
+    struct adf_post_config data;
+
+    memset(&data, 0, sizeof(data));
+    data.interfaces = interfaces;
+    data.n_interfaces = n_interfaces;
+    data.bufs = bufs;
+    data.n_bufs = n_bufs;
+    data.custom_data = custom_data;
+    data.custom_data_size = custom_data_size;
+
+    err = ioctl(dev->fd, ADF_POST_CONFIG, &data);
+    if (err < 0)
+        return -errno;
+
+    return (int)data.complete_fence;
+}
+
+static int adf_device_attachment(struct adf_device *dev,
+        adf_id_t overlay_engine, adf_id_t interface, bool attach)
+{
+    int err;
+    struct adf_attachment_config data;
+
+    memset(&data, 0, sizeof(data));
+    data.overlay_engine = overlay_engine;
+    data.interface = interface;
+
+    err = ioctl(dev->fd, attach ? ADF_ATTACH : ADF_DETACH, &data);
+    if (err < 0)
+        return -errno;
+
+    return 0;
+}
+
+int adf_device_attach(struct adf_device *dev, adf_id_t overlay_engine,
+                      adf_id_t interface)
+{
+   return adf_device_attachment(dev, overlay_engine, interface, true);
+}
+
+int adf_device_detach(struct adf_device *dev, adf_id_t overlay_engine,
+                      adf_id_t interface)
+{
+   return adf_device_attachment(dev, overlay_engine, interface, false);
+}
+
+ssize_t adf_interfaces(struct adf_device *dev, adf_id_t **interfaces)
+{
+    char pattern[64];
+
+    snprintf(pattern, sizeof(pattern), "adf-interface%u.%%u", dev->id);
+    return adf_find_nodes(pattern, interfaces);
+}
+
+ssize_t adf_interfaces_for_overlay_engine(struct adf_device *dev,
+        adf_id_t overlay_engine, adf_id_t **interfaces)
+{
+    struct adf_device_data data;
+    ssize_t n = 0;
+    ssize_t ret;
+    adf_id_t *ids_ret = NULL;
+
+    ret = adf_get_device_data(dev, &data);
+    if (ret < 0)
+        return ret;
+
+    size_t i;
+    for (i = 0; i < data.n_allowed_attachments; i++) {
+        if (data.allowed_attachments[i].overlay_engine != overlay_engine)
+            continue;
+
+        adf_id_t *new_ids = realloc(ids_ret, (n + 1) * sizeof(ids_ret[0]));
+        if (!new_ids) {
+            ret = -ENOMEM;
+            goto done;
+        }
+
+        ids_ret = new_ids;
+        ids_ret[n] = data.allowed_attachments[i].interface;
+        n++;
+    }
+
+    ret = n;
+
+done:
+    adf_free_device_data(&data);
+    if (ret < 0)
+        free(ids_ret);
+    else
+        *interfaces = ids_ret;
+    return ret;
+}
+
+static ssize_t adf_interfaces_filter(struct adf_device *dev,
+        adf_id_t *in, size_t n_in, adf_id_t **out,
+        bool (*filter)(struct adf_interface_data *data, __u32 match),
+        __u32 match)
+{
+    size_t n = 0;
+    ssize_t ret;
+    adf_id_t *ids_ret = NULL;
+
+    size_t i;
+    for (i = 0; i < n_in; i++) {
+        int fd = adf_interface_open(dev, in[i], O_RDONLY);
+        if (fd < 0) {
+            ret = fd;
+            goto done;
+        }
+
+        struct adf_interface_data data;
+        ret = adf_get_interface_data(fd, &data);
+        close(fd);
+        if (ret < 0)
+            goto done;
+
+        if (!filter(&data, match))
+            continue;
+
+        adf_id_t *new_ids = realloc(ids_ret, (n + 1) * sizeof(ids_ret[0]));
+        if (!new_ids) {
+            ret = -ENOMEM;
+            goto done;
+        }
+
+        ids_ret = new_ids;
+        ids_ret[n] = in[i];
+        n++;
+    }
+
+    ret = n;
+
+done:
+    if (ret < 0)
+        free(ids_ret);
+    else
+        *out = ids_ret;
+    return ret;
+}
+
+static bool adf_interface_type_filter(struct adf_interface_data *data,
+        __u32 type)
+{
+    return data->type == (enum adf_interface_type)type;
+}
+
+ssize_t adf_interfaces_filter_by_type(struct adf_device *dev,
+        enum adf_interface_type type,
+        adf_id_t *in, size_t n_in, adf_id_t **out)
+{
+    return adf_interfaces_filter(dev, in, n_in, out, adf_interface_type_filter,
+            type);
+}
+
+static bool adf_interface_flags_filter(struct adf_interface_data *data,
+        __u32 flag)
+{
+    return !!(data->flags & flag);
+}
+
+ssize_t adf_interfaces_filter_by_flag(struct adf_device *dev, __u32 flag,
+        adf_id_t *in, size_t n_in, adf_id_t **out)
+{
+    return adf_interfaces_filter(dev, in, n_in, out, adf_interface_flags_filter,
+            flag);
+}
+
+int adf_interface_open(struct adf_device *dev, adf_id_t id, int flags)
+{
+    char filename[64];
+
+    snprintf(filename, sizeof(filename), ADF_BASE_PATH "adf-interface%u.%u",
+            dev->id, id);
+
+    int fd = open(filename, flags);
+    if (fd < 0)
+        return -errno;
+    return fd;
+}
+
+int adf_get_interface_data(int fd, struct adf_interface_data *data)
+{
+    int err;
+    int ret = 0;
+
+    memset(data, 0, sizeof(*data));
+
+    err = ioctl(fd, ADF_GET_INTERFACE_DATA, data);
+    if (err < 0)
+        return -errno;
+
+    if (data->n_available_modes) {
+        data->available_modes = malloc(sizeof(data->available_modes[0]) *
+                data->n_available_modes);
+        if (!data->available_modes)
+            return -ENOMEM;
+    }
+
+    if (data->custom_data_size) {
+        data->custom_data = malloc(data->custom_data_size);
+        if (!data->custom_data) {
+            ret = -ENOMEM;
+            goto done;
+        }
+    }
+
+    err = ioctl(fd, ADF_GET_INTERFACE_DATA, data);
+    if (err < 0)
+        ret = -errno;
+
+done:
+    if (ret < 0)
+        adf_free_interface_data(data);
+    return ret;
+}
+
+void adf_free_interface_data(struct adf_interface_data *data)
+{
+    free(data->available_modes);
+    free(data->custom_data);
+}
+
+int adf_interface_blank(int fd, __u8 mode)
+{
+    int err = ioctl(fd, ADF_BLANK, mode);
+    if (err < 0)
+        return -errno;
+    return 0;
+}
+
+int adf_interface_set_mode(int fd, struct drm_mode_modeinfo *mode)
+{
+    int err = ioctl(fd, ADF_SET_MODE, mode);
+    if (err < 0)
+        return -errno;
+    return 0;
+}
+
+int adf_interface_simple_buffer_alloc(int fd, __u32 w, __u32 h,
+        __u32 format, __u32 *offset, __u32 *pitch)
+{
+    int err;
+    struct adf_simple_buffer_alloc data;
+
+    memset(&data, 0, sizeof(data));
+    data.w = w;
+    data.h = h;
+    data.format = format;
+
+    err = ioctl(fd, ADF_SIMPLE_BUFFER_ALLOC, &data);
+    if (err < 0)
+        return -errno;
+
+    *offset = data.offset;
+    *pitch = data.pitch;
+    return (int)data.fd;
+}
+
+int adf_interface_simple_post(int fd, __u32 overlay_engine,
+        __u32 w, __u32 h, __u32 format, int buf_fd, __u32 offset,
+        __u32 pitch, int acquire_fence)
+{
+    int ret;
+    struct adf_simple_post_config data;
+
+    memset(&data, 0, sizeof(data));
+    data.buf.overlay_engine = overlay_engine;
+    data.buf.w = w;
+    data.buf.h = h;
+    data.buf.format = format;
+    data.buf.fd[0] = buf_fd;
+    data.buf.offset[0] = offset;
+    data.buf.pitch[0] = pitch;
+    data.buf.n_planes = 1;
+    data.buf.acquire_fence = acquire_fence;
+
+    ret = ioctl(fd, ADF_SIMPLE_POST_CONFIG, &data);
+    if (ret < 0)
+        return -errno;
+
+    return (int)data.complete_fence;
+}
+
+ssize_t adf_overlay_engines(struct adf_device *dev, adf_id_t **overlay_engines)
+{
+    char pattern[64];
+
+    snprintf(pattern, sizeof(pattern), "adf-overlay-engine%u.%%u", dev->id);
+    return adf_find_nodes(pattern, overlay_engines);
+}
+
+ssize_t adf_overlay_engines_for_interface(struct adf_device *dev,
+        adf_id_t interface, adf_id_t **overlay_engines)
+{
+    struct adf_device_data data;
+    ssize_t n = 0;
+    ssize_t ret;
+    adf_id_t *ids_ret = NULL;
+
+    ret = adf_get_device_data(dev, &data);
+    if (ret < 0)
+        return ret;
+
+    size_t i;
+    for (i = 0; i < data.n_allowed_attachments; i++) {
+        if (data.allowed_attachments[i].interface != interface)
+            continue;
+
+        adf_id_t *new_ids = realloc(ids_ret, (n + 1) * sizeof(ids_ret[0]));
+        if (!new_ids) {
+            ret = -ENOMEM;
+            goto done;
+        }
+
+        ids_ret = new_ids;
+        ids_ret[n] = data.allowed_attachments[i].overlay_engine;
+        n++;
+    }
+
+    ret = n;
+
+done:
+    adf_free_device_data(&data);
+    if (ret < 0)
+        free(ids_ret);
+    else
+        *overlay_engines = ids_ret;
+    return ret;
+}
+
+static ssize_t adf_overlay_engines_filter(struct adf_device *dev,
+        adf_id_t *in, size_t n_in, adf_id_t **out,
+        bool (*filter)(struct adf_overlay_engine_data *data, void *cookie),
+        void *cookie)
+{
+    size_t n = 0;
+    ssize_t ret;
+    adf_id_t *ids_ret = NULL;
+
+    size_t i;
+    for (i = 0; i < n_in; i++) {
+        int fd = adf_overlay_engine_open(dev, in[i], O_RDONLY);
+        if (fd < 0) {
+            ret = fd;
+            goto done;
+        }
+
+        struct adf_overlay_engine_data data;
+        ret = adf_get_overlay_engine_data(fd, &data);
+        close(fd);
+        if (ret < 0)
+            goto done;
+
+        if (!filter(&data, cookie))
+            continue;
+
+        adf_id_t *new_ids = realloc(ids_ret, (n + 1) * sizeof(ids_ret[0]));
+        if (!new_ids) {
+            ret = -ENOMEM;
+            goto done;
+        }
+
+        ids_ret = new_ids;
+        ids_ret[n] = in[i];
+        n++;
+    }
+
+    ret = n;
+
+done:
+    if (ret < 0)
+        free(ids_ret);
+    else
+        *out = ids_ret;
+    return ret;
+}
+
+struct format_filter_cookie {
+    const __u32 *formats;
+    size_t n_formats;
+};
+
+static bool adf_overlay_engine_format_filter(
+        struct adf_overlay_engine_data *data, void *cookie)
+{
+    struct format_filter_cookie *c = cookie;
+    size_t i;
+    for (i = 0; i < data->n_supported_formats; i++) {
+        size_t j;
+        for (j = 0; j < c->n_formats; j++)
+            if (data->supported_formats[i] == c->formats[j])
+                return true;
+    }
+    return false;
+}
+
+ssize_t adf_overlay_engines_filter_by_format(struct adf_device *dev,
+        const __u32 *formats, size_t n_formats, adf_id_t *in, size_t n_in,
+        adf_id_t **out)
+{
+    struct format_filter_cookie cookie = { formats, n_formats };
+    return adf_overlay_engines_filter(dev, in, n_in, out,
+            adf_overlay_engine_format_filter, &cookie);
+}
+
+int adf_overlay_engine_open(struct adf_device *dev, adf_id_t id, int flags)
+{
+    char filename[64];
+
+    snprintf(filename, sizeof(filename),
+            ADF_BASE_PATH "adf-overlay-engine%u.%u", dev->id, id);
+
+    int fd = open(filename, flags);
+    if (fd < 0)
+        return -errno;
+    return fd;
+}
+
+int adf_get_overlay_engine_data(int fd, struct adf_overlay_engine_data *data)
+{
+    int err;
+    int ret = 0;
+
+    memset(data, 0, sizeof(*data));
+
+    err = ioctl(fd, ADF_GET_OVERLAY_ENGINE_DATA, data);
+    if (err < 0)
+        return -errno;
+
+    if (data->n_supported_formats) {
+        data->supported_formats = malloc(sizeof(data->supported_formats[0]) *
+              data->n_supported_formats);
+        if (!data->supported_formats)
+            return -ENOMEM;
+    }
+
+    if (data->custom_data_size) {
+      data->custom_data = malloc(data->custom_data_size);
+      if (!data->custom_data) {
+          ret = -ENOMEM;
+          goto done;
+      }
+    }
+
+    err = ioctl(fd, ADF_GET_OVERLAY_ENGINE_DATA, data);
+    if (err < 0)
+        ret = -errno;
+
+done:
+    if (ret < 0)
+        adf_free_overlay_engine_data(data);
+    return ret;
+}
+
+void adf_free_overlay_engine_data(struct adf_overlay_engine_data *data)
+{
+    free(data->supported_formats);
+    free(data->custom_data);
+}
+
+bool adf_overlay_engine_supports_format(int fd, __u32 format)
+{
+    struct adf_overlay_engine_data data;
+    bool ret = false;
+    size_t i;
+
+    int err = adf_get_overlay_engine_data(fd, &data);
+    if (err < 0)
+        return false;
+
+    for (i = 0; i < data.n_supported_formats; i++) {
+        if (data.supported_formats[i] == format) {
+            ret = true;
+            break;
+        }
+    }
+
+    adf_free_overlay_engine_data(&data);
+    return ret;
+}
+
+int adf_set_event(int fd, enum adf_event_type type, bool enabled)
+{
+    struct adf_set_event data;
+
+    data.type = type;
+    data.enabled = enabled;
+
+    int err = ioctl(fd, ADF_SET_EVENT, &data);
+    if (err < 0)
+        return -errno;
+    return 0;
+}
+
+int adf_read_event(int fd, struct adf_event **event)
+{
+    struct adf_event header;
+    struct {
+        struct adf_event base;
+        uint8_t data[0];
+    } *event_ret;
+    size_t data_size;
+    int ret = 0;
+
+    int err = read(fd, &header, sizeof(header));
+    if (err < 0)
+        return -errno;
+    if ((size_t)err < sizeof(header))
+        return -EIO;
+    if (header.length < sizeof(header))
+        return -EIO;
+
+    event_ret = malloc(header.length);
+    if (!event_ret)
+        return -ENOMEM;
+    data_size = header.length - sizeof(header);
+
+    memcpy(event_ret, &header, sizeof(header));
+    ssize_t read_size = read(fd, &event_ret->data, data_size);
+    if (read_size < 0) {
+        ret = -errno;
+        goto done;
+    }
+    if ((size_t)read_size < data_size) {
+        ret = -EIO;
+        goto done;
+    }
+
+    *event = &event_ret->base;
+
+done:
+    if (ret < 0)
+        free(event_ret);
+    return ret;
+}
+
+void adf_format_str(__u32 format, char buf[ADF_FORMAT_STR_SIZE])
+{
+    buf[0] = format & 0xFF;
+    buf[1] = (format >> 8) & 0xFF;
+    buf[2] = (format >> 16) & 0xFF;
+    buf[3] = (format >> 24) & 0xFF;
+    buf[4] = '\0';
+}
+
+static bool adf_find_simple_post_overlay_engine(struct adf_device *dev,
+        const __u32 *formats, size_t n_formats,
+        adf_id_t interface, adf_id_t *overlay_engine)
+{
+    adf_id_t *engs;
+    ssize_t n_engs = adf_overlay_engines_for_interface(dev, interface, &engs);
+
+    if (n_engs <= 0)
+        return false;
+
+    adf_id_t *filtered_engs;
+    ssize_t n_filtered_engs = adf_overlay_engines_filter_by_format(dev,
+            formats, n_formats, engs, n_engs, &filtered_engs);
+    free(engs);
+
+    if (n_filtered_engs <= 0)
+        return false;
+
+    *overlay_engine = filtered_engs[0];
+    free(filtered_engs);
+    return true;
+}
+
+static const __u32 any_rgb_format[] = {
+    DRM_FORMAT_C8,
+    DRM_FORMAT_RGB332,
+    DRM_FORMAT_BGR233,
+    DRM_FORMAT_XRGB1555,
+    DRM_FORMAT_XBGR1555,
+    DRM_FORMAT_RGBX5551,
+    DRM_FORMAT_BGRX5551,
+    DRM_FORMAT_ARGB1555,
+    DRM_FORMAT_ABGR1555,
+    DRM_FORMAT_RGBA5551,
+    DRM_FORMAT_BGRA5551,
+    DRM_FORMAT_RGB565,
+    DRM_FORMAT_BGR565,
+    DRM_FORMAT_RGB888,
+    DRM_FORMAT_BGR888,
+    DRM_FORMAT_XRGB8888,
+    DRM_FORMAT_XBGR8888,
+    DRM_FORMAT_RGBX8888,
+    DRM_FORMAT_BGRX8888,
+    DRM_FORMAT_XRGB2101010,
+    DRM_FORMAT_XBGR2101010,
+    DRM_FORMAT_RGBX1010102,
+    DRM_FORMAT_BGRX1010102,
+    DRM_FORMAT_ARGB2101010,
+    DRM_FORMAT_ABGR2101010,
+    DRM_FORMAT_RGBA1010102,
+    DRM_FORMAT_BGRA1010102,
+    DRM_FORMAT_ARGB8888,
+    DRM_FORMAT_ABGR8888,
+    DRM_FORMAT_RGBA8888,
+    DRM_FORMAT_BGRA8888,
+};
+
+int adf_find_simple_post_configuration(struct adf_device *dev,
+        const __u32 *formats, size_t n_formats,
+        adf_id_t *interface, adf_id_t *overlay_engine)
+{
+    adf_id_t *intfs;
+    ssize_t n_intfs = adf_interfaces(dev, &intfs);
+
+    if (n_intfs < 0)
+        return n_intfs;
+    else if (!n_intfs)
+        return -ENODEV;
+
+    adf_id_t *primary_intfs;
+    ssize_t n_primary_intfs = adf_interfaces_filter_by_flag(dev,
+            ADF_INTF_FLAG_PRIMARY, intfs, n_intfs, &primary_intfs);
+    free(intfs);
+
+    if (n_primary_intfs < 0)
+        return n_primary_intfs;
+    else if (!n_primary_intfs)
+        return -ENODEV;
+
+    if (!formats) {
+        formats = any_rgb_format;
+        n_formats = sizeof(any_rgb_format) / sizeof(any_rgb_format[0]);
+    }
+
+    bool found = false;
+    ssize_t i = 0;
+    for (i = 0; i < n_primary_intfs; i++) {
+        found = adf_find_simple_post_overlay_engine(dev, formats, n_formats,
+                primary_intfs[i], overlay_engine);
+        if (found) {
+            *interface = primary_intfs[i];
+            break;
+        }
+    }
+    free(primary_intfs);
+
+    if (!found)
+        return -ENODEV;
+
+    return 0;
+}
diff --git a/adf/libadf/include/adf/adf.h b/adf/libadf/include/adf/adf.h
new file mode 100644
index 0000000..b6bda34
--- /dev/null
+++ b/adf/libadf/include/adf/adf.h
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ */
+
+#ifndef _LIBADF_ADF_H_
+#define _LIBADF_ADF_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <video/adf.h>
+
+typedef __u32 adf_id_t;
+
+struct adf_device {
+    adf_id_t id;
+    int fd;
+};
+
+__BEGIN_DECLS
+
+/**
+ * Enumerates all ADF devices.
+ *
+ * Returns the number of ADF devices, and sets ids to a list of device IDs.
+ * The caller must free() the returned list of device IDs.
+ *
+ * On error, returns -errno.
+ */
+ssize_t adf_devices(adf_id_t **ids);
+
+/**
+ * Opens an ADF device.
+ *
+ * On error, returns -errno.
+ */
+int adf_device_open(adf_id_t id, int flags, struct adf_device *dev);
+/**
+ * Closes an ADF device.
+ */
+void adf_device_close(struct adf_device *dev);
+/**
+ * Reads the ADF device data.
+ *
+ * adf_get_device_data() allocates buffers inside data, which the caller
+ * must free by calling adf_free_device_data().  On error, returns -errno.
+ */
+int adf_get_device_data(struct adf_device *dev, struct adf_device_data *data);
+/**
+ * Frees the device data returned by adf_get_device_data().
+ */
+void adf_free_device_data(struct adf_device_data *data);
+
+/**
+ * Atomically posts a new display configuration to the specified interfaces.
+ *
+ * Returns a sync fence fd that will fire when the configuration is removed
+ * from the screen.  On error, returns -errno.
+ */
+int adf_device_post(struct adf_device *dev,
+        adf_id_t *interfaces, size_t n_interfaces,
+        struct adf_buffer_config *bufs, size_t n_bufs,
+        void *custom_data, size_t custom_data_size);
+/**
+ * Attaches the specified interface and overlay engine.
+ */
+int adf_device_attach(struct adf_device *dev, adf_id_t overlay_engine,
+                      adf_id_t interface);
+/**
+ * Detaches the specified interface and overlay engine.
+ */
+int adf_device_detach(struct adf_device *dev, adf_id_t overlay_engine,
+                      adf_id_t interface);
+
+/**
+ * Enumerates all interfaces belonging to an ADF device.
+ *
+ * The caller must free() the returned list of interface IDs.
+ */
+ssize_t adf_interfaces(struct adf_device *dev, adf_id_t **interfaces);
+
+/**
+ * Enumerates all interfaces which can be attached to the specified overlay
+ * engine.
+ *
+ * The caller must free() the returned list of interface IDs.
+ */
+ssize_t adf_interfaces_for_overlay_engine(struct adf_device *dev,
+        adf_id_t overlay_engine, adf_id_t **interfaces);
+/**
+ * Filters a list of interfaces by type.
+ *
+ * Returns the number of matching interfaces, and sets out to a list of matching
+ * interface IDs.  The caller must free() the returned list of interface IDs.
+ *
+ * On error, returns -errno.
+ */
+ssize_t adf_interfaces_filter_by_type(struct adf_device *dev,
+        enum adf_interface_type type,
+        adf_id_t *in, size_t n_in, adf_id_t **out);
+/**
+ * Filters a list of interfaces by flag.
+ *
+ * The caller must free() the returned list of interface IDs.
+ */
+ssize_t adf_interfaces_filter_by_flag(struct adf_device *dev, __u32 flag,
+        adf_id_t *in, size_t n_in, adf_id_t **out);
+
+/**
+ * Opens an ADF interface.
+ *
+ * Returns a file descriptor.  The caller must close() the fd when done.
+ * On error, returns -errno.
+ */
+int adf_interface_open(struct adf_device *dev, adf_id_t id, int flags);
+/**
+ * Reads the interface data.
+ *
+ * adf_get_interface_data() allocates buffers inside data, which the caller
+ * must free by calling adf_free_interface_data().  On error, returns -errno.
+ */
+int adf_get_interface_data(int fd, struct adf_interface_data *data);
+/**
+ * Frees the interface data returned by adf_get_interface_data().
+ */
+void adf_free_interface_data(struct adf_interface_data *data);
+
+/**
+ * Sets the interface's DPMS mode.
+ */
+int adf_interface_blank(int fd, __u8 mode);
+/**
+ * Sets the interface's display mode.
+ */
+int adf_interface_set_mode(int fd, struct drm_mode_modeinfo *mode);
+/**
+ * Allocates a single-plane RGB buffer of the specified size and format.
+ *
+ * Returns a dma-buf fd.  On error, returns -errno.
+ */
+int adf_interface_simple_buffer_alloc(int fd, __u32 w, __u32 h,
+        __u32 format, __u32 *offset, __u32 *pitch);
+/**
+ * Posts a single-plane RGB buffer to the display using the specified
+ * overlay engine.
+ *
+ * Returns a sync fence fd that will fire when the buffer is removed
+ * from the screen.  On error, returns -errno.
+ */
+int adf_interface_simple_post(int fd, adf_id_t overlay_engine,
+        __u32 w, __u32 h, __u32 format, int buf_fd, __u32 offset,
+        __u32 pitch, int acquire_fence);
+
+/**
+ * Enumerates all overlay engines belonging to an ADF device.
+ *
+ * The caller must free() the returned list of overlay engine IDs.
+ */
+ssize_t adf_overlay_engines(struct adf_device *dev, adf_id_t **overlay_engines);
+
+/**
+ * Enumerates all overlay engines which can be attached to the specified
+ * interface.
+ *
+ * The caller must free() the returned list of overlay engine IDs.
+ */
+ssize_t adf_overlay_engines_for_interface(struct adf_device *dev,
+        adf_id_t interface, adf_id_t **overlay_engines);
+/**
+ * Filters a list of overlay engines by supported buffer format.
+ *
+ * Returns the overlay engines which support at least one of the specified
+ * formats.  The caller must free() the returned list of overlay engine IDs.
+ */
+ssize_t adf_overlay_engines_filter_by_format(struct adf_device *dev,
+        const __u32 *formats, size_t n_formats, adf_id_t *in, size_t n_in,
+        adf_id_t **out);
+
+/**
+ * Opens an ADF overlay engine.
+ *
+ * Returns a file descriptor.  The caller must close() the fd when done.
+ * On error, returns -errno.
+ */
+int adf_overlay_engine_open(struct adf_device *dev, adf_id_t id, int flags);
+/**
+ * Reads the overlay engine data.
+ *
+ * adf_get_overlay_engine_data() allocates buffers inside data, which the caller
+ * must free by calling adf_free_overlay_engine_data().  On error, returns
+ * -errno.
+ */
+int adf_get_overlay_engine_data(int fd, struct adf_overlay_engine_data *data);
+/**
+ * Frees the overlay engine data returned by adf_get_overlay_engine_data().
+ */
+void adf_free_overlay_engine_data(struct adf_overlay_engine_data *data);
+
+/**
+ * Returns whether the overlay engine supports the specified format.
+ */
+bool adf_overlay_engine_supports_format(int fd, __u32 format);
+
+/**
+ * Subscribes or unsubscribes from the specified hardware event.
+ */
+int adf_set_event(int fd, enum adf_event_type type, bool enabled);
+/**
+ * Reads one event from the fd, blocking if needed.
+ *
+ * The caller must free() the returned buffer.  On error, returns -errno.
+ */
+int adf_read_event(int fd, struct adf_event **event);
+
+#define ADF_FORMAT_STR_SIZE 5
+/**
+ * Converts an ADF/DRM fourcc format to its string representation.
+ */
+void adf_format_str(__u32 format, char buf[ADF_FORMAT_STR_SIZE]);
+
+/**
+ * Finds an appropriate interface and overlay engine for a simple post.
+ *
+ * Specifically, finds the primary interface, and an overlay engine
+ * that can be attached to the primary interface and supports one of the
+ * specified formats.  The caller may pass a NULL formats list, to indicate that
+ * any RGB format is acceptable.
+ *
+ * On error, returns -errno.
+ */
+int adf_find_simple_post_configuration(struct adf_device *dev,
+        const __u32 *formats, size_t n_formats,
+        adf_id_t *interface, adf_id_t *overlay_engine);
+
+__END_DECLS
+
+#endif /* _LIBADF_ADF_H_ */
diff --git a/adf/libadfhwc/Android.mk b/adf/libadfhwc/Android.mk
new file mode 100644
index 0000000..acea322
--- /dev/null
+++ b/adf/libadfhwc/Android.mk
@@ -0,0 +1,25 @@
+# Copyright (C) 2013 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := adfhwc.cpp
+LOCAL_MODULE := libadfhwc
+LOCAL_MODULE_TAGS := optional
+LOCAL_STATIC_LIBRARIES := libadf liblog libutils
+LOCAL_CFLAGS += -DLOG_TAG=\"adfhwc\"
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES += $(LOCAL_EXPORT_C_INCLUDE_DIRS)
+include $(BUILD_STATIC_LIBRARY)
diff --git a/adf/libadfhwc/adfhwc.cpp b/adf/libadfhwc/adfhwc.cpp
new file mode 100644
index 0000000..dee3cae
--- /dev/null
+++ b/adf/libadfhwc/adfhwc.cpp
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ */
+
+#include <fcntl.h>
+#include <poll.h>
+#include <pthread.h>
+#include <sys/resource.h>
+
+#include <adf/adf.h>
+#include <adfhwc/adfhwc.h>
+
+#include <cutils/log.h>
+#include <utils/Vector.h>
+
+struct adf_hwc_helper {
+    adf_hwc_event_callbacks const *event_cb;
+    void *event_cb_data;
+
+    pthread_t event_thread;
+
+    android::Vector<int> intf_fds;
+    android::Vector<drm_mode_modeinfo> display_configs;
+};
+
+template<typename T> inline T min(T a, T b) { return (a < b) ? a : b; }
+
+int adf_eventControl(struct adf_hwc_helper *dev, int disp, int event,
+        int enabled)
+{
+    if (enabled != !!enabled)
+        return -EINVAL;
+
+    if ((size_t)disp >= dev->intf_fds.size())
+        return -EINVAL;
+
+    switch (event) {
+    case HWC_EVENT_VSYNC:
+        return adf_set_event(dev->intf_fds[disp], ADF_EVENT_VSYNC, enabled);
+    }
+
+    return -EINVAL;
+}
+
+static inline int32_t dpi(uint16_t res, uint16_t size_mm)
+{
+    if (size_mm)
+        return 1000 * (res * 25.4f) / size_mm;
+    return 0;
+}
+
+int adf_blank(struct adf_hwc_helper *dev, int disp, int blank)
+{
+    if ((size_t)disp >= dev->intf_fds.size())
+        return -EINVAL;
+
+    uint8_t dpms_mode = blank ? DRM_MODE_DPMS_OFF : DRM_MODE_DPMS_ON;
+    return adf_interface_blank(dev->intf_fds[disp], dpms_mode);
+}
+
+int adf_query_display_types_supported(struct adf_hwc_helper *dev, int *value)
+{
+    *value = 0;
+    if (dev->intf_fds.size() > 0)
+        *value |= HWC_DISPLAY_PRIMARY_BIT;
+    if (dev->intf_fds.size() > 1)
+        *value |= HWC_DISPLAY_EXTERNAL_BIT;
+
+    return 0;
+}
+
+int adf_getDisplayConfigs(struct adf_hwc_helper *dev, int disp,
+        uint32_t *configs, size_t *numConfigs)
+{
+    if ((size_t)disp >= dev->intf_fds.size())
+        return -EINVAL;
+
+    adf_interface_data data;
+    int err = adf_get_interface_data(dev->intf_fds[disp], &data);
+    if (err < 0) {
+        ALOGE("failed to get ADF interface data: %s", strerror(err));
+        return err;
+    }
+
+    if (!data.hotplug_detect)
+        return -ENODEV;
+
+    android::Vector<drm_mode_modeinfo *> unique_configs;
+    unique_configs.push_back(&data.current_mode);
+    for (size_t i = 0; i < data.n_available_modes; i++)
+        if (memcmp(&data.available_modes[i], &data.current_mode,
+                sizeof(data.current_mode)))
+            unique_configs.push_back(&data.available_modes[i]);
+
+    for (size_t i = 0; i < min(*numConfigs, unique_configs.size()); i++) {
+        configs[i] = dev->display_configs.size();
+        dev->display_configs.push_back(*unique_configs[i]);
+    }
+    *numConfigs = unique_configs.size();
+
+    adf_free_interface_data(&data);
+    return 0;
+}
+
+static int32_t adf_display_attribute(const adf_interface_data &data,
+        const drm_mode_modeinfo &mode, const uint32_t attribute)
+{
+    switch (attribute) {
+    case HWC_DISPLAY_VSYNC_PERIOD:
+        if (mode.vrefresh)
+            return 1000000000 / mode.vrefresh;
+        return 0;
+
+    case HWC_DISPLAY_WIDTH:
+        return mode.hdisplay;
+
+    case HWC_DISPLAY_HEIGHT:
+        return mode.vdisplay;
+
+    case HWC_DISPLAY_DPI_X:
+        return dpi(mode.hdisplay, data.width_mm);
+
+    case HWC_DISPLAY_DPI_Y:
+        return dpi(mode.vdisplay, data.height_mm);
+
+    default:
+        ALOGE("unknown display attribute %u", attribute);
+        return -EINVAL;
+    }
+}
+
+int adf_getDisplayAttributes(struct adf_hwc_helper *dev, int disp,
+        uint32_t config, const uint32_t *attributes, int32_t *values)
+{
+    if ((size_t)disp >= dev->intf_fds.size())
+        return -EINVAL;
+
+    if (config >= dev->display_configs.size())
+        return -EINVAL;
+
+    adf_interface_data data;
+    int err = adf_get_interface_data(dev->intf_fds[disp], &data);
+    if (err < 0) {
+        ALOGE("failed to get ADF interface data: %s", strerror(err));
+        return err;
+    }
+
+    for (int i = 0; attributes[i] != HWC_DISPLAY_NO_ATTRIBUTE; i++)
+        values[i] = adf_display_attribute(data, dev->display_configs[config],
+                attributes[i]);
+
+    adf_free_interface_data(&data);
+    return 0;
+}
+
+static void handle_adf_event(struct adf_hwc_helper *dev, int disp)
+{
+    adf_event *event;
+    int err = adf_read_event(dev->intf_fds[disp], &event);
+    if (err < 0) {
+        ALOGE("error reading event from display %d: %s", disp, strerror(err));
+        return;
+    }
+
+    void *vsync_temp;
+    adf_vsync_event *vsync;
+    adf_hotplug_event *hotplug;
+
+    switch (event->type) {
+    case ADF_EVENT_VSYNC:
+        vsync_temp = event;
+        vsync = static_cast<adf_vsync_event *>(vsync_temp);
+        // casting directly to adf_vsync_event * makes g++ warn about
+        // potential alignment issues that don't apply here
+        dev->event_cb->vsync(dev->event_cb_data, disp, vsync->timestamp);
+        break;
+    case ADF_EVENT_HOTPLUG:
+        hotplug = reinterpret_cast<adf_hotplug_event *>(event);
+        dev->event_cb->hotplug(dev->event_cb_data, disp, hotplug->connected);
+        break;
+    default:
+        if (event->type < ADF_EVENT_DEVICE_CUSTOM)
+            ALOGW("unrecognized event type %u", event->type);
+        else if (!dev->event_cb || !dev->event_cb->custom_event)
+            ALOGW("unhandled event type %u", event->type);
+        else
+            dev->event_cb->custom_event(dev->event_cb_data, disp, event);
+    }
+    free(event);
+}
+
+static void *adf_event_thread(void *data)
+{
+    adf_hwc_helper *dev = static_cast<adf_hwc_helper *>(data);
+
+    setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY);
+
+    pollfd *fds = new pollfd[dev->intf_fds.size()];
+    for (size_t i = 0; i < dev->intf_fds.size(); i++) {
+        fds[i].fd = dev->intf_fds[i];
+        fds[i].events = POLLIN | POLLPRI;
+    }
+
+    while (true) {
+        int err = poll(fds, dev->intf_fds.size(), -1);
+
+        if (err > 0) {
+            for (size_t i = 0; i < dev->intf_fds.size(); i++)
+                if (fds[i].revents & (POLLIN | POLLPRI))
+                    handle_adf_event(dev, i);
+        }
+        else if (err == -1) {
+            if (errno == EINTR)
+                break;
+            ALOGE("error in event thread: %s", strerror(errno));
+        }
+    }
+
+    delete [] fds;
+    return NULL;
+}
+
+int adf_hwc_open(int *intf_fds, size_t n_intfs,
+        const struct adf_hwc_event_callbacks *event_cb, void *event_cb_data,
+        struct adf_hwc_helper **dev)
+{
+    if (!n_intfs)
+        return -EINVAL;
+
+    adf_hwc_helper *dev_ret = new adf_hwc_helper;
+    dev_ret->event_cb = event_cb;
+    dev_ret->event_cb_data = event_cb_data;
+
+    int ret;
+
+    for (size_t i = 0; i < n_intfs; i++) {
+        int dup_intf_fd = dup(intf_fds[i]);
+        if (dup_intf_fd < 0) {
+            ALOGE("failed to dup interface fd: %s", strerror(errno));
+            ret = -errno;
+            goto err;
+        }
+
+        dev_ret->intf_fds.push_back(dup_intf_fd);
+
+        ret = adf_set_event(dup_intf_fd, ADF_EVENT_HOTPLUG, 1);
+        if (ret < 0 && ret != -EINVAL) {
+            ALOGE("failed to enable hotplug event on display %u: %s",
+                    i, strerror(errno));
+            goto err;
+        }
+    }
+
+    ret = pthread_create(&dev_ret->event_thread, NULL, adf_event_thread,
+            dev_ret);
+    if (ret) {
+        ALOGE("failed to create event thread: %s", strerror(ret));
+        goto err;
+    }
+
+    *dev = dev_ret;
+    return 0;
+
+err:
+    for (size_t i = 0; i < dev_ret->intf_fds.size(); i++)
+        close(dev_ret->intf_fds[i]);
+
+    delete dev_ret;
+    return ret;
+}
+
+void adf_hwc_close(struct adf_hwc_helper *dev)
+{
+    pthread_kill(dev->event_thread, SIGTERM);
+    pthread_join(dev->event_thread, NULL);
+
+    for (size_t i = 0; i < dev->intf_fds.size(); i++)
+        close(dev->intf_fds[i]);
+
+    delete dev;
+}
diff --git a/adf/libadfhwc/include/adfhwc/adfhwc.h b/adf/libadfhwc/include/adfhwc/adfhwc.h
new file mode 100644
index 0000000..71e7624
--- /dev/null
+++ b/adf/libadfhwc/include/adfhwc/adfhwc.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ */
+
+#ifndef _LIBADFHWC_ADFHWC_H_
+#define _LIBADFHWC_ADFHWC_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/cdefs.h>
+#include <video/adf.h>
+
+#include <hardware/hwcomposer.h>
+
+struct adf_hwc_helper;
+
+struct adf_hwc_event_callbacks {
+    /**
+     * Called on vsync (required)
+     */
+    void (*vsync)(void *data, int disp, uint64_t timestamp);
+    /**
+     * Called on hotplug (required)
+     */
+    void (*hotplug)(void *data, int disp, bool connected);
+    /**
+     * Called on hardware-custom ADF events (optional)
+     */
+    void (*custom_event)(void *data, int disp, struct adf_event *event);
+};
+
+/**
+ * Converts HAL pixel formats to equivalent ADF/DRM format FourCCs.
+ */
+static inline uint32_t adf_fourcc_for_hal_pixel_format(int format)
+{
+    switch (format) {
+    case HAL_PIXEL_FORMAT_RGBA_8888:
+        return DRM_FORMAT_RGBA8888;
+    case HAL_PIXEL_FORMAT_RGBX_8888:
+        return DRM_FORMAT_RGBX8888;
+    case HAL_PIXEL_FORMAT_RGB_888:
+        return DRM_FORMAT_RGB888;
+    case HAL_PIXEL_FORMAT_RGB_565:
+        return DRM_FORMAT_RGB565;
+    case HAL_PIXEL_FORMAT_BGRA_8888:
+        return DRM_FORMAT_BGRA8888;
+    case HAL_PIXEL_FORMAT_YV12:
+        return DRM_FORMAT_YVU420;
+    case HAL_PIXEL_FORMAT_YCbCr_422_SP:
+        return DRM_FORMAT_NV16;
+    case HAL_PIXEL_FORMAT_YCrCb_420_SP:
+        return DRM_FORMAT_NV21;
+    case HAL_PIXEL_FORMAT_YCbCr_422_I:
+        return DRM_FORMAT_YUYV;
+    default:
+        return 0;
+    }
+}
+
+/**
+ * Converts HAL display types to equivalent ADF interface flags.
+ */
+static inline uint32_t adf_hwc_interface_flag_for_disp(int disp)
+{
+    switch (disp) {
+    case HWC_DISPLAY_PRIMARY:
+        return ADF_INTF_FLAG_PRIMARY;
+    case HWC_DISPLAY_EXTERNAL:
+        return ADF_INTF_FLAG_EXTERNAL;
+    default:
+        return 0;
+    }
+}
+
+__BEGIN_DECLS
+
+/**
+ * Create a HWC helper for the specified ADF interfaces.
+ *
+ * intf_fds must be indexed by HWC display type: e.g.,
+ * intf_fds[HWC_DISPLAY_PRIMARY] is the fd for the primary display
+ * interface.  n_intfs must be >= 1.
+ *
+ * The caller retains ownership of the fds in intf_fds and must close()
+ * them when they are no longer needed.
+ *
+ * On error, returns -errno.
+ */
+int adf_hwc_open(int *intf_fds, size_t n_intfs,
+        const struct adf_hwc_event_callbacks *event_cb, void *event_cb_data,
+        struct adf_hwc_helper **dev);
+
+/**
+ * Destroys a HWC helper.
+ */
+void adf_hwc_close(struct adf_hwc_helper *dev);
+
+/**
+ * Generic implementations of common HWC ops.
+ *
+ * The HWC should not point its ops directly at these helpers.  Instead, the HWC
+ * should provide stub ops which call these helpers after converting the
+ * hwc_composer_device_1* to a struct adf_hwc_helper*.
+ */
+int adf_eventControl(struct adf_hwc_helper *dev, int disp, int event,
+        int enabled);
+int adf_blank(struct adf_hwc_helper *dev, int disp, int blank);
+int adf_query_display_types_supported(struct adf_hwc_helper *dev, int *value);
+int adf_getDisplayConfigs(struct adf_hwc_helper *dev, int disp,
+        uint32_t *configs, size_t *numConfigs);
+int adf_getDisplayAttributes(struct adf_hwc_helper *dev, int disp,
+        uint32_t config, const uint32_t *attributes, int32_t *values);
+
+__END_DECLS
+
+#endif /* _LIBADFHWC_ADFHWC_H_ */
diff --git a/debuggerd/tombstone.c b/debuggerd/tombstone.c
index aa63547..1b08e8e 100644
--- a/debuggerd/tombstone.c
+++ b/debuggerd/tombstone.c
@@ -415,7 +415,7 @@
 
 /* Return true if some thread is not detached cleanly */
 static bool dump_sibling_thread_report(
-        log_t* log, pid_t pid, pid_t tid, int* total_sleep_time_usec) {
+        log_t* log, pid_t pid, pid_t tid, int* total_sleep_time_usec, backtrace_map_info_t* map_info) {
     char task_path[64];
     snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid);
 
@@ -449,7 +449,7 @@
         _LOG(log, 0, "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n");
         dump_thread_info(log, pid, new_tid, 0);
         backtrace_context_t new_context;
-        if (backtrace_create_context(&new_context, pid, new_tid, 0)) {
+        if (backtrace_create_context_with_map(&new_context, pid, new_tid, 0, map_info)) {
             dump_thread(&new_context, log, 0, total_sleep_time_usec);
             backtrace_destroy_context(&new_context);
         }
@@ -676,7 +676,9 @@
     }
 
     backtrace_context_t context;
-    if (backtrace_create_context(&context, pid, tid, 0)) {
+    /* Gather the map info once for all this process' threads. */
+    backtrace_map_info_t* map_info = backtrace_create_map_info_list(pid);
+    if (backtrace_create_context_with_map(&context, pid, tid, 0, map_info)) {
         dump_abort_message(&context, log, abort_msg_address);
         dump_thread(&context, log, SCOPE_AT_FAULT, total_sleep_time_usec);
         backtrace_destroy_context(&context);
@@ -688,9 +690,12 @@
 
     bool detach_failed = false;
     if (dump_sibling_threads) {
-        detach_failed = dump_sibling_thread_report(log, pid, tid, total_sleep_time_usec);
+        detach_failed = dump_sibling_thread_report(log, pid, tid, total_sleep_time_usec, map_info);
     }
 
+    /* Destroy the previously created map info. */
+    backtrace_destroy_map_info_list(map_info);
+
     if (want_logs) {
         dump_logs(log, pid, false);
     }
diff --git a/fastbootd/Android.mk b/fastbootd/Android.mk
index 76b28e2..0f32dbf 100644
--- a/fastbootd/Android.mk
+++ b/fastbootd/Android.mk
@@ -16,23 +16,89 @@
 
 include $(CLEAR_VARS)
 
+LOCAL_C_INCLUDES := \
+    external/openssl/include \
+    external/mdnsresponder/mDNSShared \
+    $(LOCAL_PATH)/include \
+    external/zlib/ \
+
 LOCAL_SRC_FILES := \
     config.c \
     commands.c \
+    commands/boot.c \
+    commands/flash.c \
+    commands/partitions.c \
+    commands/virtual_partitions.c \
     fastbootd.c \
     protocol.c \
+    network_discovery.c \
+    socket_client.c \
+    secure.c \
     transport.c \
-    usb_linux_client.c
+    transport_socket.c \
+    trigger.c \
+    usb_linux_client.c \
+    utils.c \
 
 LOCAL_MODULE := fastbootd
 LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter -DFLASH_CERT
+LOCAL_LDFLAGS := -ldl
+
+LOCAL_SHARED_LIBRARIES := \
+    libhardware \
+    libcrypto \
+    libhardware_legacy \
+    libmdnssd
+
+LOCAL_STATIC_LIBRARIES := \
+    libsparse_static \
+    libc \
+    libcutils \
+    libz
+
+#LOCAL_FORCE_STATIC_EXECUTABLE := true
+
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_C_INCLUDES := \
+    external/zlib/
+
+LOCAL_SRC_FILES := \
+    commands/partitions.c \
+    other/gptedit.c \
+    utils.c
+
+LOCAL_MODULE := gptedit
+LOCAL_MODULE_TAGS := optional
 LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter
 
 LOCAL_STATIC_LIBRARIES := \
     libsparse_static \
     libc \
-    libcutils
+    libcutils \
+    libz
 
 LOCAL_FORCE_STATIC_EXECUTABLE := true
 
 include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+
+LOCAL_C_INCLUDES := \
+    $(LOCAL_PATH)/include \
+
+LOCAL_STATIC_LIBRARIES := \
+    $(EXTRA_STATIC_LIBS) \
+    libcutils
+
+LOCAL_SRC_FILES := \
+    other/vendor_trigger.c
+
+LOCAL_MODULE := libvendortrigger.default
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter
+
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/fastbootd/commands.c b/fastbootd/commands.c
index 252f655..d8a601f 100644
--- a/fastbootd/commands.c
+++ b/fastbootd/commands.c
@@ -32,114 +32,304 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/reboot.h>
+#include <fcntl.h>
 
 #include "bootimg.h"
+#include "commands/boot.h"
+#include "commands/flash.h"
+#include "commands/partitions.h"
+#include "commands/virtual_partitions.h"
 #include "debug.h"
 #include "protocol.h"
+#include "trigger.h"
+#include "utils.h"
+
+#define ATAGS_LOCATION "/proc/atags"
 
 static void cmd_boot(struct protocol_handle *phandle, const char *arg)
 {
-#if 0
+    int sz, atags_sz, new_atags_sz;
+    int rv;
     unsigned kernel_actual;
     unsigned ramdisk_actual;
-    static struct boot_img_hdr hdr;
-    char *ptr = ((char*) data);
+    unsigned second_actual;
+    void *kernel_ptr;
+    void *ramdisk_ptr;
+    void *second_ptr;
+    struct boot_img_hdr *hdr;
+    char *ptr = NULL;
+    char *atags_ptr = NULL;
+    char *new_atags = NULL;
+    int data_fd = 0;
 
-    if (sz < sizeof(hdr)) {
+    D(DEBUG, "cmd_boot %s\n", arg);
+
+    if (phandle->download_fd < 0) {
+        fastboot_fail(phandle, "no kernel file");
+        return;
+    }
+
+    atags_ptr = read_atags(ATAGS_LOCATION, &atags_sz);
+    if (atags_ptr == NULL) {
+        fastboot_fail(phandle, "atags read error");
+        goto error;
+    }
+
+    // TODO: With cms we can also verify partition name included as
+    // cms signed attribute
+    if (flash_validate_certificate(phandle->download_fd, &data_fd) != 1) {
+        fastboot_fail(phandle, "Access forbiden you need the certificate");
+        return;
+    }
+
+    sz = get_file_size(data_fd);
+
+    ptr = (char *) mmap(NULL, sz, PROT_READ,
+                        MAP_POPULATE | MAP_PRIVATE, data_fd, 0);
+
+    hdr = (struct boot_img_hdr *) ptr;
+
+    if (ptr == MAP_FAILED) {
+        fastboot_fail(phandle, "internal fastbootd error");
+        goto error;
+    }
+
+    if ((size_t) sz < sizeof(*hdr)) {
         fastboot_fail(phandle, "invalid bootimage header");
-        return;
+        goto error;
     }
 
-    memcpy(&hdr, data, sizeof(hdr));
+    kernel_actual = ROUND_TO_PAGE(hdr->kernel_size, hdr->page_size);
+    ramdisk_actual = ROUND_TO_PAGE(hdr->ramdisk_size, hdr->page_size);
+    second_actual = ROUND_TO_PAGE(hdr->second_size, hdr->page_size);
 
-    /* ensure commandline is terminated */
-    hdr.cmdline[BOOT_ARGS_SIZE-1] = 0;
+    new_atags = (char *) create_atags((unsigned *) atags_ptr, atags_sz, hdr, &new_atags_sz);
 
-    kernel_actual = ROUND_TO_PAGE(hdr.kernel_size);
-    ramdisk_actual = ROUND_TO_PAGE(hdr.ramdisk_size);
+    if (new_atags == NULL) {
+        fastboot_fail(phandle, "atags generate error");
+        goto error;
+    }
+    if (new_atags_sz > 0x4000) {
+        fastboot_fail(phandle, "atags file to large");
+        goto error;
+    }
 
-    if (2048 + kernel_actual + ramdisk_actual < sz) {
+    if ((int) (hdr->page_size + kernel_actual + ramdisk_actual) < sz) {
         fastboot_fail(phandle, "incomplete bootimage");
-        return;
+        goto error;
     }
 
-    /*memmove((void*) KERNEL_ADDR, ptr + 2048, hdr.kernel_size);
-    memmove((void*) RAMDISK_ADDR, ptr + 2048 + kernel_actual, hdr.ramdisk_size);*/
+    kernel_ptr = (void *)((unsigned) ptr + hdr->page_size);
+    ramdisk_ptr = (void *)((unsigned) kernel_ptr + kernel_actual);
+    second_ptr = (void *)((unsigned) ramdisk_ptr + ramdisk_actual);
+
+    D(INFO, "preparing to boot");
+    // Prepares boot physical address. Addresses from header are ignored
+    rv = prepare_boot_linux(hdr->kernel_addr, kernel_ptr, kernel_actual,
+                            hdr->ramdisk_addr, ramdisk_ptr, ramdisk_actual,
+                            hdr->second_addr, second_ptr, second_actual,
+                            hdr->tags_addr, new_atags, ROUND_TO_PAGE(new_atags_sz, hdr->page_size));
+    if (rv < 0) {
+        fastboot_fail(phandle, "kexec prepare failed");
+        goto error;
+    }
 
     fastboot_okay(phandle, "");
-    udc_stop();
 
+    free(atags_ptr);
+    munmap(ptr, sz);
+    free(new_atags);
+    close(data_fd);
 
-    /*boot_linux((void*) KERNEL_ADDR, (void*) TAGS_ADDR,
-           (const char*) hdr.cmdline, LINUX_MACHTYPE,
-           (void*) RAMDISK_ADDR, hdr.ramdisk_size);*/
-#endif
+    D(INFO, "Kexec going to reboot");
+    reboot(LINUX_REBOOT_CMD_KEXEC);
+
+    fastboot_fail(phandle, "reboot error");
+
+    return;
+
+error:
+
+    if (atags_ptr != NULL)
+        free(atags_ptr);
+    if (ptr != NULL)
+        munmap(ptr, sz);
+
 }
 
 static void cmd_erase(struct protocol_handle *phandle, const char *arg)
 {
-#if 0
-    struct ptentry *ptn;
-    struct ptable *ptable;
+    int partition_fd;
+    char path[PATH_MAX];
+    D(DEBUG, "cmd_erase %s\n", arg);
 
-    ptable = flash_get_ptable();
-    if (ptable == NULL) {
+    if (flash_find_entry(arg, path, PATH_MAX)) {
         fastboot_fail(phandle, "partition table doesn't exist");
         return;
     }
 
-    ptn = ptable_find(ptable, arg);
-    if (ptn == NULL) {
-        fastboot_fail(phandle, "unknown partition name");
+    if (path == NULL) {
+        fastboot_fail(phandle, "Couldn't find partition");
         return;
     }
 
-    if (flash_erase(ptn)) {
+    partition_fd = flash_get_partiton(path);
+    if (partition_fd < 0) {
+        fastboot_fail(phandle, "partiton file does not exists");
+    }
+
+    if (flash_erase(partition_fd)) {
+        fastboot_fail(phandle, "failed to erase partition");
+        flash_close(partition_fd);
+        return;
+    }
+
+    if (flash_close(partition_fd) < 0) {
+        D(ERR, "could not close device %s", strerror(errno));
         fastboot_fail(phandle, "failed to erase partition");
         return;
     }
     fastboot_okay(phandle, "");
-#endif
+}
+
+static int GPT_header_location() {
+    const char *location_str = fastboot_getvar("gpt_sector");
+    char *str;
+    int location;
+
+    if (!strcmp("", location_str)) {
+        D(INFO, "GPT location not specified using second sector");
+        return 1;
+    }
+    else {
+        location = strtoul(location_str, &str, 10);
+        D(INFO, "GPT location specified as %d", location);
+
+        if (*str != '\0')
+            return -1;
+
+        return location - 1;
+    }
+}
+
+static void cmd_gpt_layout(struct protocol_handle *phandle, const char *arg) {
+    struct GPT_entry_table *oldtable;
+    int location;
+    struct GPT_content content;
+    const char *device;
+    device = fastboot_getvar("blockdev");
+
+    if (!strcmp(device, "")) {
+        fastboot_fail(phandle, "blockdev not defined in config file");
+        return;
+    }
+
+    //TODO: add same verification as in cmd_flash
+    if (phandle->download_fd < 0) {
+        fastboot_fail(phandle, "no layout file");
+        return;
+    }
+
+    location = GPT_header_location();
+    oldtable = GPT_get_device(device, location);
+
+    GPT_default_content(&content, oldtable);
+    if (oldtable == NULL)
+        D(WARN, "Could not get old gpt table");
+    else
+        GPT_release_device(oldtable);
+
+    if (!GPT_parse_file(phandle->download_fd, &content)) {
+        fastboot_fail(phandle, "Could not parse partition config file");
+        return;
+    }
+
+    if (trigger_gpt_layout(&content)) {
+        fastboot_fail(phandle, "Vendor forbids this opperation");
+        GPT_release_content(&content);
+        return;
+    }
+
+    if (!GPT_write_content(device, &content)) {
+        fastboot_fail(phandle, "Unable to write gpt file");
+        GPT_release_content(&content);
+        return;
+    }
+
+    GPT_release_content(&content);
+    fastboot_okay(phandle, "");
 }
 
 static void cmd_flash(struct protocol_handle *phandle, const char *arg)
 {
-#if 0
-    struct ptentry *ptn;
-    struct ptable *ptable;
-    unsigned extra = 0;
+    int partition;
+    uint64_t sz;
+    char data[BOOT_MAGIC_SIZE];
+    char path[PATH_MAX];
+    ssize_t header_sz = 0;
+    int data_fd = 0;
 
-    ptable = flash_get_ptable();
-    if (ptable == NULL) {
+    D(DEBUG, "cmd_flash %s\n", arg);
+
+    if (try_handle_virtual_partition(phandle, arg)) {
+        return;
+    }
+
+    if (phandle->download_fd < 0) {
+        fastboot_fail(phandle, "no kernel file");
+        return;
+    }
+
+    if (flash_find_entry(arg, path, PATH_MAX)) {
         fastboot_fail(phandle, "partition table doesn't exist");
         return;
     }
 
-    ptn = ptable_find(ptable, arg);
-    if (ptn == NULL) {
-        fastboot_fail(phandle, "unknown partition name");
+    if (flash_validate_certificate(phandle->download_fd, &data_fd) != 1) {
+        fastboot_fail(phandle, "Access forbiden you need certificate");
         return;
     }
 
-    if (!strcmp(ptn->name, "boot") || !strcmp(ptn->name, "recovery")) {
+    // TODO: Maybe its goot idea to check whether the partition is bootable
+    if (!strcmp(arg, "boot") || !strcmp(arg, "recovery")) {
+        if (read_data_once(data_fd, data, BOOT_MAGIC_SIZE) < BOOT_MAGIC_SIZE) {
+            fastboot_fail(phandle, "incoming data read error, cannot read boot header");
+            return;
+        }
         if (memcmp((void *)data, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
             fastboot_fail(phandle, "image is not a boot image");
             return;
         }
     }
 
-    if (!strcmp(ptn->name, "system") || !strcmp(ptn->name, "userdata"))
-        extra = 64;
-    else
-        sz = ROUND_TO_PAGE(sz);
+    partition = flash_get_partiton(path);
 
-    D(INFO, "writing %d bytes to '%s'\n", sz, ptn->name);
-    if (flash_write(ptn, extra, data, sz)) {
+    sz = get_file_size64(data_fd);
+
+    sz -= header_sz;
+
+    if (sz > get_file_size64(partition)) {
+        flash_close(partition);
+        D(WARN, "size of file too large");
+        fastboot_fail(phandle, "size of file too large");
+        return;
+    }
+
+    D(INFO, "writing %lld bytes to '%s'\n", sz, arg);
+
+    if (flash_write(partition, phandle->download_fd, sz, header_sz)) {
         fastboot_fail(phandle, "flash write failure");
         return;
     }
-    D(INFO, "partition '%s' updated\n", ptn->name);
-#endif
+    D(INFO, "partition '%s' updated\n", arg);
+
+    flash_close(partition);
+    close(data_fd);
+
     fastboot_okay(phandle, "");
 }
 
@@ -184,7 +374,6 @@
 
     phandle->download_fd = protocol_handle_download(phandle, len);
     if (phandle->download_fd < 0) {
-        //handle->state = STATE_ERROR;
         fastboot_fail(phandle, "download failed");
         return;
     }
@@ -192,14 +381,27 @@
     fastboot_okay(phandle, "");
 }
 
+static void cmd_oem(struct protocol_handle *phandle, const char *arg) {
+    const char *response = "";
+
+    //TODO: Maybe it should get download descriptor also
+    if (trigger_oem_cmd(arg, &response))
+        fastboot_fail(phandle, response);
+    else
+        fastboot_okay(phandle, response);
+}
+
 void commands_init()
 {
+    virtual_partition_register("partition-table", cmd_gpt_layout);
+
     fastboot_register("boot", cmd_boot);
     fastboot_register("erase:", cmd_erase);
     fastboot_register("flash:", cmd_flash);
     fastboot_register("continue", cmd_continue);
     fastboot_register("getvar:", cmd_getvar);
     fastboot_register("download:", cmd_download);
+    fastboot_register("oem", cmd_oem);
     //fastboot_publish("version", "0.5");
     //fastboot_publish("product", "swordfish");
     //fastboot_publish("kernel", "lk");
diff --git a/fastbootd/commands/boot.c b/fastbootd/commands/boot.c
new file mode 100644
index 0000000..8da9a28
--- /dev/null
+++ b/fastbootd/commands/boot.c
@@ -0,0 +1,254 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/syscall.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "boot.h"
+#include "debug.h"
+#include "utils.h"
+#include "bootimg.h"
+
+
+#define KEXEC_ARM_ATAGS_OFFSET  0x1000
+#define KEXEC_ARM_ZIMAGE_OFFSET 0x8000
+
+#define MEMORY_SIZE 0x0800000
+#define START_ADDRESS 0x44000000
+#define KERNEL_START (START_ADDRESS + KEXEC_ARM_ZIMAGE_OFFSET)
+
+#define ATAG_NONE_TYPE      0x00000000
+#define ATAG_CORE_TYPE      0x54410001
+#define ATAG_RAMDISK_TYPE   0x54410004
+#define ATAG_INITRD2_TYPE   0x54420005
+#define ATAG_CMDLINE_TYPE   0x54410009
+
+#define MAX_ATAG_SIZE 0x4000
+
+struct atag_info {
+    unsigned size;
+    unsigned type;
+};
+
+struct atag_initrd2 {
+    unsigned start;
+    unsigned size;
+};
+
+struct atag_cmdline {
+    char cmdline[0];
+};
+
+struct atag {
+    struct atag_info info;
+    union {
+        struct atag_initrd2 initrd2;
+        struct atag_cmdline cmdline;
+    } data;
+};
+
+
+long kexec_load(unsigned int entry, unsigned long nr_segments,
+                struct kexec_segment *segment, unsigned long flags) {
+   return syscall(__NR_kexec_load, entry, nr_segments, segment, flags);
+}
+
+/*
+ * Prepares arguments for kexec
+ * Kernel address is not set into kernel_phys
+ * Ramdisk is set to position relative to kernel
+ */
+int prepare_boot_linux(unsigned kernel_phys, void *kernel_addr, int kernel_size,
+                       unsigned ramdisk_phys, void *ramdisk_addr, int ramdisk_size,
+                       unsigned second_phys, void *second_addr, int second_size,
+                       unsigned atags_phys, void *atags_addr, int atags_size) {
+    struct kexec_segment segment[4];
+    int segment_count = 2;
+    unsigned entry = START_ADDRESS + KEXEC_ARM_ZIMAGE_OFFSET;
+    int rv;
+    int page_size = getpagesize();
+
+    segment[0].buf = kernel_addr;
+    segment[0].bufsz = kernel_size;
+    segment[0].mem = (void *) KERNEL_START;
+    segment[0].memsz = ROUND_TO_PAGE(kernel_size, page_size);
+
+    if (kernel_size > MEMORY_SIZE - KEXEC_ARM_ZIMAGE_OFFSET) {
+        D(INFO, "Kernel image too big");
+        return -1;
+    }
+
+    segment[1].buf = atags_addr;
+    segment[1].bufsz = atags_size;
+    segment[1].mem = (void *) (START_ADDRESS + KEXEC_ARM_ATAGS_OFFSET);
+    segment[1].memsz = ROUND_TO_PAGE(atags_size, page_size);
+
+    D(INFO, "Ramdisk size is %d", ramdisk_size);
+
+    if (ramdisk_size != 0) {
+        segment[segment_count].buf = ramdisk_addr;
+        segment[segment_count].bufsz = ramdisk_size;
+        segment[segment_count].mem = (void *) (KERNEL_START + ramdisk_phys - kernel_phys);
+        segment[segment_count].memsz = ROUND_TO_PAGE(ramdisk_phys, page_size);
+        ++segment_count;
+    }
+
+    D(INFO, "Ramdisk size is %d", ramdisk_size);
+    if (second_size != 0) {
+        segment[segment_count].buf = second_addr;
+        segment[segment_count].bufsz = second_size;
+        segment[segment_count].mem = (void *) (KERNEL_START + second_phys - kernel_phys);
+        segment[segment_count].memsz = ROUND_TO_PAGE(second_size, page_size);
+        entry = second_phys;
+        ++segment_count;
+    }
+
+    rv = kexec_load(entry, segment_count, segment, KEXEC_ARCH_DEFAULT);
+
+    if (rv != 0) {
+        D(INFO, "Kexec_load returned non-zero exit code: %s\n", strerror(errno));
+        return -1;
+    }
+
+    return 1;
+
+}
+
+unsigned *create_atags(unsigned *atags_position, int atag_size, const struct boot_img_hdr *hdr, int *size) {
+    struct atag *current_tag = (struct atag *) atags_position;
+    unsigned *current_tag_raw = atags_position;
+    unsigned *new_atags = malloc(ROUND_TO_PAGE(atag_size + BOOT_ARGS_SIZE * sizeof(char),
+                                               hdr->page_size));
+    //This pointer will point into the beggining of buffer free space
+    unsigned *natags_raw_buff = new_atags;
+    int new_atags_size = 0;
+    int current_size;
+    int cmdl_length;
+
+    // copy tags from current atag file
+    while (current_tag->info.type != ATAG_NONE_TYPE) {
+        switch (current_tag->info.type) {
+            case ATAG_CMDLINE_TYPE:
+            case ATAG_RAMDISK_TYPE:
+            case ATAG_INITRD2_TYPE: break;
+            default:
+                memcpy((void *)natags_raw_buff, (void *)current_tag_raw, current_tag->info.size * sizeof(unsigned));
+                natags_raw_buff += current_tag->info.size;
+                new_atags_size += current_tag->info.size;
+        }
+
+        current_tag_raw += current_tag->info.size;
+        current_tag = (struct atag *) current_tag_raw;
+
+        if (current_tag_raw >= atags_position + atag_size) {
+            D(ERR, "Critical error in atags");
+            return NULL;
+        }
+    }
+
+    // set INITRD2 tag
+    if (hdr->ramdisk_size > 0) {
+        current_size = (sizeof(struct atag_info) + sizeof(struct atag_initrd2)) / sizeof(unsigned);
+        *((struct atag *) natags_raw_buff) = (struct atag) {
+            .info = {
+                .size = current_size,
+                .type = ATAG_INITRD2_TYPE
+            },
+            .data = {
+                .initrd2 = (struct atag_initrd2) {
+                    .start = hdr->ramdisk_addr,
+                    .size = hdr->ramdisk_size
+                }
+            }
+        };
+
+        new_atags_size += current_size;
+        natags_raw_buff += current_size;
+    }
+
+    // set ATAG_CMDLINE
+    cmdl_length = strnlen((char *) hdr->cmdline, BOOT_ARGS_SIZE - 1);
+    current_size = sizeof(struct atag_info) + (1 + cmdl_length);
+    current_size = (current_size + sizeof(unsigned) - 1) / sizeof(unsigned);
+    *((struct atag *) natags_raw_buff) = (struct atag) {
+        .info = {
+            .size = current_size,
+            .type = ATAG_CMDLINE_TYPE
+        },
+    };
+
+    //copy cmdline and ensure that there is null character
+    memcpy(((struct atag *) natags_raw_buff)->data.cmdline.cmdline,
+           (char *) hdr->cmdline, cmdl_length);
+    ((struct atag *) natags_raw_buff)->data.cmdline.cmdline[cmdl_length] = '\0';
+
+    new_atags_size += current_size;
+    natags_raw_buff += current_size;
+
+    // set ATAG_NONE
+    *((struct atag *) natags_raw_buff) = (struct atag) {
+        .info = {
+            .size = 0,
+            .type = ATAG_NONE_TYPE
+        },
+    };
+    new_atags_size += sizeof(struct atag_info) / sizeof(unsigned);
+    natags_raw_buff += sizeof(struct atag_info) / sizeof(unsigned);
+
+    *size = new_atags_size * sizeof(unsigned);
+    return new_atags;
+}
+
+char *read_atags(const char * path, int *atags_sz) {
+    int afd = -1;
+    char *atags_ptr = NULL;
+
+    afd = open(path, O_RDONLY);
+    if (afd < 0) {
+        D(ERR, "wrong atags file");
+        return 0;
+    }
+
+    atags_ptr = (char *) malloc(MAX_ATAG_SIZE);
+    if (atags_ptr == NULL) {
+        D(ERR, "insufficient memory");
+        return 0;
+    }
+
+    *atags_sz = read(afd, atags_ptr, MAX_ATAG_SIZE);
+
+    close(afd);
+    return atags_ptr;
+}
+
diff --git a/fastbootd/commands/boot.h b/fastbootd/commands/boot.h
new file mode 100644
index 0000000..901c38a
--- /dev/null
+++ b/fastbootd/commands/boot.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef __FASTBOOT_BOOT_H
+#define __FASTBOOT_BOOT_H
+
+#include <sys/cdefs.h>
+#include <linux/kexec.h>
+
+#include "bootimg.h"
+
+#define KEXEC_TYPE_DEFAULT 0
+#define KEXEC_TYPE_CRASH   1
+
+int prepare_boot_linux(unsigned, void *, int, unsigned, void *, int,
+                       unsigned, void *, int, unsigned, void *, int);
+unsigned *create_atags(unsigned *, int, const struct boot_img_hdr *, int *);
+long kexec_load(unsigned int, unsigned long, struct kexec_segment *, unsigned long);
+char *read_atags(const char *, int *);
+
+#endif /* _SYS_KEXEC_H */
+
diff --git a/fastbootd/commands/flash.c b/fastbootd/commands/flash.c
new file mode 100644
index 0000000..0954217
--- /dev/null
+++ b/fastbootd/commands/flash.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+
+#include "flash.h"
+#include "protocol.h"
+#include "debug.h"
+#include "utils.h"
+#include "commands/partitions.h"
+
+#ifdef FLASH_CERT
+#include "secure.h"
+#endif
+
+#define ALLOWED_CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-."
+#define BUFFER_SIZE 1024 * 1024
+#define MIN(a, b) (a > b ? b : a)
+
+
+int flash_find_entry(const char *name, char *out, size_t outlen)
+{
+//TODO: Assumption: All the partitions has they unique name
+
+    const char *path = fastboot_getvar("device-directory");
+    size_t length;
+    if (strcmp(path, "") == 0) {
+        D(ERR, "device-directory: not defined in config file");
+        return -1;
+    }
+
+    length = strspn(name, ALLOWED_CHARS);
+    if (length != strlen(name)) {
+        D(ERR, "Not allowed char in name: %c", name[length]);
+        return -1;
+    }
+
+    if (snprintf(out, outlen, "%s%s", path, name) >= (int) outlen) {
+        D(ERR, "Too long path to partition file");
+        return -1;
+    }
+
+    if (access(out, F_OK ) == -1) {
+        D(ERR, "could not find partition file %s", name);
+        return -1;
+    }
+
+    return 0;
+}
+
+int flash_erase(int fd)
+{
+    int64_t size;
+    size = get_block_device_size(fd);
+    D(DEBUG, "erase %llu data from %d\n", size, fd);
+
+    return wipe_block_device(fd, size);
+}
+
+int flash_write(int partition_fd, int data_fd, ssize_t size, ssize_t skip)
+{
+    ssize_t written = 0;
+    struct GPT_mapping input;
+    struct GPT_mapping output;
+
+    while (written < size) {
+        int current_size = MIN(size - written, BUFFER_SIZE);
+
+        if (gpt_mmap(&input, written + skip, current_size, data_fd)) {
+            D(ERR, "Error in writing data, unable to map data file %d at %d size %d", size, skip, current_size);
+            return -1;
+        }
+        if (gpt_mmap(&output, written, current_size, partition_fd)) {
+            D(ERR, "Error in writing data, unable to map output partition");
+            return -1;
+        }
+
+        memcpy(output.ptr, input.ptr, current_size);
+
+        gpt_unmap(&input);
+        gpt_unmap(&output);
+
+        written += current_size;
+    }
+
+    return 0;
+}
+
+#ifdef FLASH_CERT
+
+int flash_validate_certificate(int signed_fd, int *data_fd) {
+    int ret = 0;
+    const char *cert_path;
+    X509_STORE *store = NULL;
+    CMS_ContentInfo *content_info;
+    BIO *content;
+
+    cert_path = fastboot_getvar("certificate-path");
+    if (!strcmp(cert_path, "")) {
+        D(ERR, "could not find cert-key value in config file");
+        goto finish;
+    }
+
+    store = cert_store_from_path(cert_path);
+    if (store == NULL) {
+        D(ERR, "unable to create certification store");
+        goto finish;
+    }
+
+    if (cert_read(signed_fd, &content_info, &content)) {
+        D(ERR, "reading data failed");
+        goto finish;
+    }
+
+    ret = cert_verify(content, content_info, store, data_fd);
+    cert_release(content, content_info);
+
+    return ret;
+
+finish:
+    if (store != NULL)
+        cert_release_store(store);
+
+    return ret;
+}
+
+#else
+int flash_validate_certificate(int signed_fd, int *data_fd) {
+    return 1;
+}
+#endif
diff --git a/fastbootd/commands/flash.h b/fastbootd/commands/flash.h
new file mode 100644
index 0000000..5a64cab
--- /dev/null
+++ b/fastbootd/commands/flash.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _FASTBOOTD_ERASE_H
+#define _FASTBOOTD_ERASE_H
+
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include "debug.h"
+
+int flash_find_entry(const char *, char *, size_t);
+int flash_erase(int fd);
+
+static inline int flash_get_partiton(const char *path) {
+    return open(path, O_RDWR);
+}
+
+static inline int flash_close(int fd) {
+    return close(fd);
+}
+
+int flash_write(int partition, int data, ssize_t size, ssize_t skip);
+
+static inline ssize_t read_data_once(int fd, char *buffer, ssize_t size) {
+    ssize_t readcount = 0;
+    ssize_t len;
+
+    while ((len = TEMP_FAILURE_RETRY(read(fd, (void *) &buffer[readcount], size - readcount))) > 0) {
+        readcount += len;
+    }
+    if (len < 0) {
+        D(ERR, "Read error:%s", strerror(errno));
+        return len;
+    }
+
+    return readcount;
+}
+
+int flash_validate_certificate(int signed_fd, int *data_fd);
+
+#endif
+
diff --git a/fastbootd/commands/partitions.c b/fastbootd/commands/partitions.c
new file mode 100644
index 0000000..de80ea3
--- /dev/null
+++ b/fastbootd/commands/partitions.c
@@ -0,0 +1,771 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <endian.h>
+#include <zlib.h>
+#include <linux/hdreg.h>
+#include <sys/ioctl.h>
+#include <stdlib.h>
+#include <cutils/config_utils.h>
+
+#include "partitions.h"
+#include "debug.h"
+#include "utils.h"
+#include "protocol.h"
+
+#define BLKRRPART  _IO(0x12,95) /* re-read partition table */
+#define BLKSSZGET  _IO(0x12,104)
+
+#define DIV_ROUND_UP(x, y) (((x) + (y) - 1)/(y))
+#define ALIGN(x, y) ((y) * DIV_ROUND_UP((x), (y)))
+#define ALIGN_DOWN(x, y) ((y) * ((x) / (y)))
+
+
+const uint8_t partition_type_uuid[16] = {
+    0xa2, 0xa0, 0xd0, 0xeb, 0xe5, 0xb9, 0x33, 0x44,
+    0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7,
+};
+
+//TODO: There is assumption that we are using little endian
+
+static void GPT_entry_clear(struct GPT_entry_raw *entry)
+{
+    memset(entry, 0, sizeof(*entry));
+}
+
+/*
+ * returns mapped location to choosen area
+ * mapped_ptr is pointer to whole area mapped (it can be bigger then requested)
+ */
+int gpt_mmap(struct GPT_mapping *mapping, uint64_t location, int size, int fd)
+{
+    unsigned int location_diff = location & ~PAGE_MASK;
+
+    mapping->size = ALIGN(size + location_diff, PAGE_SIZE);
+
+    uint64_t sz = get_file_size64(fd);
+    if (sz < size + location) {
+        D(ERR, "the location of mapping area is outside of the device size %lld", sz);
+        return 1;
+    }
+    location = ALIGN_DOWN(location, PAGE_SIZE);
+
+    mapping->map_ptr = mmap64(NULL, mapping->size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, location);
+
+    if (mapping->map_ptr == MAP_FAILED) {
+        mapping->ptr = MAP_FAILED;
+        D(ERR, "map failed %d", (int) mapping->map_ptr);
+        return 1;
+    }
+
+    mapping->ptr = (void *)((char *) mapping->map_ptr + location_diff);
+    return 0;
+}
+
+void gpt_unmap(struct GPT_mapping *mapping) {
+    munmap(mapping->map_ptr, mapping->size);
+}
+
+
+#define LBA_ADDR(table, value)   ((uint64_t) (table)->sector_size * (value))
+
+int GPT_map_from_content(struct GPT_entry_table *table, const struct GPT_content *content)
+{
+
+    // Mapping header
+    if (gpt_mmap(&table->header_map, LBA_ADDR(table, content->header.current_lba),
+                 table->sector_size, table->fd)) {
+        D(ERR, "unable to map header:%s\n", strerror(errno));
+        goto error_header;
+    }
+
+    table->header = (struct GPT_header *) table->header_map.ptr;
+
+    table->partition_table_size = ROUND_UP(content->header.entries_count * sizeof(*table->entries),
+                                           table->sector_size);
+
+    // Mapping entry table
+    if (gpt_mmap(&table->entries_map, LBA_ADDR(table, content->header.entries_lba),
+                 table->partition_table_size, table->fd)) {
+        D(ERR, "unable to map entries");
+        goto error_signature;
+    }
+
+    table->entries = (struct GPT_entry_raw *) table->entries_map.ptr;
+
+    // Mapping secondary header
+    if (gpt_mmap(&table->sec_header_map, LBA_ADDR(table, content->header.backup_lba),
+                 table->sector_size, table->fd)) {
+        D(ERR, "unable to map backup gpt header");
+        goto error_sec_header;
+    }
+
+    // Mapping secondary entries table
+    if (gpt_mmap(&table->sec_entries_map,
+                 LBA_ADDR(table, content->header.backup_lba) - table->partition_table_size,
+                 table->partition_table_size, table->fd)) {
+        D(ERR, "unable to map secondary gpt table");
+        goto error_sec_entries;
+    }
+
+    table->second_header = (struct GPT_header *) table->sec_header_map.ptr;
+    table->second_entries = (struct GPT_entry_raw *) table->sec_entries_map.ptr;
+    table->second_valid = strcmp("EFI PART", (char *) table->second_header->signature) == 0;
+
+    return 0;
+
+error_sec_entries:
+    gpt_unmap(&table->sec_header_map);
+error_sec_header:
+    gpt_unmap(&table->entries_map);
+error_signature:
+    gpt_unmap(&table->header_map);
+error_header:
+    return 1;
+}
+
+int GPT_map(struct GPT_entry_table *table, unsigned header_lba)
+{
+    struct GPT_content content;
+    struct GPT_mapping mapping;
+    struct GPT_header *header;
+
+    if (gpt_mmap(&mapping, LBA_ADDR(table, header_lba), table->sector_size, table->fd)) {
+        D(ERR, "unable to map header: %s", strerror(errno));
+        goto error_header;
+    }
+
+    header = (struct GPT_header *) mapping.ptr;
+
+    if (strcmp("EFI PART", (char *) header->signature)) {
+        D(ERR, "GPT entry not valid");
+        goto error_signature;
+    }
+
+    content.header = *header;
+
+    gpt_unmap(&mapping);
+
+    return GPT_map_from_content(table, &content);
+
+error_signature:
+    gpt_unmap(&table->header_map);
+error_header:
+    return 1;
+}
+
+struct GPT_entry_table* GPT_get_device(const char *path, unsigned header_lba)
+{
+    struct GPT_entry_table *table;
+    size_t sector_bytes;
+
+    table = (struct GPT_entry_table *) malloc(sizeof(*table));
+    table->fd = open(path, O_RDWR);
+
+    if (table->fd < 0) {
+        D(ERR, "unable to open file %s:%s\n", path, strerror(errno));
+        return NULL;
+    }
+
+    if (!ioctl(table->fd, BLKSSZGET, &sector_bytes)) {
+        table->sector_size = (unsigned) sector_bytes;
+        D(INFO, "Got sector size %d", table->sector_size);
+    } else {
+        D(WARN, "unable to get sector size, assuming 512");
+        table->sector_size = 512;
+    }
+
+    if (GPT_map(table, header_lba)) {
+        D(ERR, "Could not map gpt");
+        return NULL;
+    }
+
+    return table;
+}
+
+static struct GPT_entry_table* GPT_get_from_content(const char *path, const struct GPT_content *content)
+{
+    struct GPT_entry_table *table;
+    size_t sector_bytes;
+
+    table = (struct GPT_entry_table *) malloc(sizeof(*table));
+    table->fd = open(path, O_RDWR);
+
+    if (table->fd < 0) {
+        D(ERR, "unable to open file %s:%s\n", path, strerror(errno));
+        return NULL;
+    }
+
+    if (!ioctl(table->fd, BLKSSZGET, &sector_bytes)) {
+        table->sector_size = (unsigned) sector_bytes;
+        D(INFO, "Got sector size %d", table->sector_size);
+    } else {
+        D(WARN, "unable to get sector size %s, assuming 512", strerror(errno));
+        table->sector_size = 512;
+    }
+
+    if (GPT_map_from_content(table, content)) {
+        D(ERR, "Could not map gpt");
+        return NULL;
+    }
+
+    return table;
+}
+
+
+void GPT_release_device(struct GPT_entry_table *table)
+{
+    gpt_unmap(&table->header_map);
+    gpt_unmap(&table->entries_map);
+    gpt_unmap(&table->sec_header_map);
+    gpt_unmap(&table->sec_entries_map);
+    close(table->fd);
+    free(table);
+}
+
+static int GPT_check_overlap(struct GPT_entry_table *table, struct GPT_entry_raw *entry);
+static int GPT_check_overlap_except(struct GPT_entry_table *table,
+                                    struct GPT_entry_raw *entry,
+                                    struct GPT_entry_raw *exclude);
+
+void GPT_edit_entry(struct GPT_entry_table *table,
+                    struct GPT_entry_raw *old_entry,
+                    struct GPT_entry_raw *new_entry)
+{
+    struct GPT_entry_raw *current_entry = GPT_get_pointer(table, old_entry);
+
+    if (GPT_check_overlap_except(table, new_entry, current_entry)) {
+        D(ERR, "Couldn't add overlaping partition");
+        return;
+    }
+
+    if (current_entry == NULL) {
+        D(ERR, "Couldn't find entry");
+        return;
+    }
+
+    *current_entry = *new_entry;
+}
+
+int GPT_delete_entry(struct GPT_entry_table *table, struct GPT_entry_raw *entry)
+{
+    struct GPT_entry_raw *raw = GPT_get_pointer(table, entry);
+
+    if (raw == NULL) {
+        D(ERR, "could not find entry");
+        return 1;
+    }
+    D(DEBUG, "Deleting gpt entry '%s'\n", raw->partition_guid);
+
+    // Entry in the middle of table may become empty
+    GPT_entry_clear(raw);
+
+    return 0;
+}
+
+void GPT_add_entry(struct GPT_entry_table *table, struct GPT_entry_raw *entry)
+{
+    unsigned i;
+    int inserted = 0;
+    if (GPT_check_overlap(table, entry)) {
+        D(ERR, "Couldn't add overlaping partition");
+        return;
+    }
+
+    if (GPT_get_pointer(table, entry) != NULL) {
+        D(WARN, "Add entry fault, this entry already exists");
+        return;
+    }
+
+    struct GPT_entry_raw *entries = table->entries;
+
+    for (i = 0; i < table->header->entries_count; ++i) {
+        if (!entries[i].type_guid[0]) {
+            inserted = 1;
+            D(DEBUG, "inserting");
+            memcpy(&entries[i], entry, sizeof(entries[i]));
+            break;
+        }
+    }
+
+    if (!inserted) {
+        D(ERR, "Unable to find empty partion entry");
+    }
+}
+
+struct GPT_entry_raw *GPT_get_pointer_by_UTFname(struct GPT_entry_table *table, const uint16_t *name);
+
+struct GPT_entry_raw *GPT_get_pointer(struct GPT_entry_table *table, struct GPT_entry_raw *entry)
+{
+    if (entry->partition_guid[0] != 0)
+        return GPT_get_pointer_by_guid(table, (const char *) entry->partition_guid);
+    else if (entry->name[0] != 0)
+        return GPT_get_pointer_by_UTFname(table, entry->name);
+
+    D(WARN, "Name or guid needed to find entry");
+    return NULL;
+}
+
+struct GPT_entry_raw *GPT_get_pointer_by_guid(struct GPT_entry_table *table, const char *name)
+{
+    int current = (int) table->header->entries_count;
+
+    for (current = current - 1; current >= 0; --current) {
+        if (strncmp((char *) name,
+                    (char *) table->entries[current].partition_guid, 16) == 0) {
+                return &table->entries[current];
+        }
+    }
+
+    return NULL;
+}
+
+int strncmp_UTF16_char(const uint16_t *s1, const char *s2, size_t n)
+{
+    if (n == 0)
+        return (0);
+    do {
+        if (((*s1) & 127) != *s2++)
+            return (((unsigned char) ((*s1) & 127)) - *(unsigned char *)--s2);
+        if (*s1++ == 0)
+            break;
+    } while (--n != 0);
+    return (0);
+}
+
+int strncmp_UTF16(const uint16_t *s1, const uint16_t *s2, size_t n)
+{
+    if (n == 0)
+        return (0);
+    do {
+        if ((*s1) != *s2++)
+            return (*s1 - *--s2);
+        if (*s1++ == 0)
+            break;
+    } while (--n != 0);
+    return (0);
+}
+
+struct GPT_entry_raw *GPT_get_pointer_by_name(struct GPT_entry_table *table, const char *name)
+{
+    int count = (int) table->header->entries_count;
+    int current;
+
+    for (current = 0; current < count; ++current) {
+        if (strncmp_UTF16_char(table->entries[current].name,
+                         (char *) name, 16) == 0) {
+                    return &table->entries[current];
+        }
+    }
+
+    return NULL;
+}
+
+struct GPT_entry_raw *GPT_get_pointer_by_UTFname(struct GPT_entry_table *table, const uint16_t *name)
+{
+    int count = (int) table->header->entries_count;
+    int current;
+
+    for (current = 0; current < count; ++current) {
+        if (strncmp_UTF16(table->entries[current].name,
+                          name, GPT_NAMELEN) == 0) {
+                return &table->entries[current];
+        }
+    }
+
+    return NULL;
+}
+
+void GPT_sync(struct GPT_entry_table *table)
+{
+    uint32_t crc;
+
+    //calculate crc32
+    crc = crc32(0, Z_NULL, 0);
+    crc = crc32(crc, (void*) table->entries, table->header->entries_count * sizeof(*table->entries));
+    table->header->partition_array_checksum = crc;
+
+    table->header->header_checksum = 0;
+    crc = crc32(0, Z_NULL, 0);
+    crc = crc32(crc, (void*) table->header, table->header->header_size);
+    table->header->header_checksum = crc;
+
+    //sync secondary partion
+    if (table->second_valid) {
+        memcpy((void *)table->second_entries, (void *) table->entries, table->partition_table_size);
+        memcpy((void *)table->second_header, (void *)table->header, sizeof(*table->header));
+    }
+
+    if(!ioctl(table->fd, BLKRRPART, NULL)) {
+        D(WARN, "Unable to force kernel to refresh partition table");
+    }
+}
+
+void GPT_to_UTF16(uint16_t *to, const char *from, int n)
+{
+    int i;
+    for (i = 0; i < (n - 1) && (to[i] = from[i]) != '\0'; ++i);
+    to[i] = '\0';
+}
+
+void GPT_from_UTF16(char *to, const uint16_t *from, int n)
+{
+    int i;
+    for (i = 0; i < (n - 1) && (to[i] = from[i] & 127) != '\0'; ++i);
+    to[i] = '\0';
+}
+
+static int GPT_check_overlap_except(struct GPT_entry_table *table,
+                                    struct GPT_entry_raw *entry,
+                                    struct GPT_entry_raw *exclude) {
+    int current = (int) table->header->entries_count;
+    int dontcheck;
+    struct GPT_entry_raw *current_entry;
+    if (entry->last_lba < entry->first_lba) {
+        D(WARN, "Start address have to be less than end address");
+        return 1;
+    }
+
+    for (current = current - 1; current >= 0; --current) {
+        current_entry = &table->entries[current];
+        dontcheck = strncmp((char *) entry->partition_guid,
+                           (char *) current_entry->partition_guid , 16) == 0;
+        dontcheck |= current_entry->type_guid[0] == 0;
+        dontcheck |= current_entry == exclude;
+
+        if (!dontcheck && ((entry->last_lba >= current_entry->first_lba &&
+                            entry->first_lba < current_entry->last_lba ))) {
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+static int GPT_check_overlap(struct GPT_entry_table *table, struct GPT_entry_raw *entry)
+{
+    return GPT_check_overlap_except(table, entry, NULL);
+}
+
+static char *get_key_value(char *ptr, char **key, char **value)
+{
+    *key = ptr;
+    ptr = strchr(ptr, '=');
+
+    if (ptr == NULL)
+        return NULL;
+
+    *ptr++ = '\0';
+    *value = ptr;
+    ptr = strchr(ptr, ';');
+
+    if (ptr == NULL)
+        ptr = *value + strlen(*value);
+    else
+        *ptr = '\0';
+
+    *key = strip(*key);
+    *value = strip(*value);
+
+    return ptr;
+}
+
+//TODO: little endian?
+static int add_key_value(const char *key, const char *value, struct GPT_entry_raw *entry)
+{
+    char *endptr;
+    if (!strcmp(key, "type")) {
+        strncpy((char *) entry->type_guid, value, 16);
+        entry->type_guid[15] = 0;
+    }
+    else if (!strcmp(key, "guid")) {
+        strncpy((char *) entry->partition_guid, value, 16);
+        entry->type_guid[15] = 0;
+    }
+    else if (!strcmp(key, "firstlba")) {
+        entry->first_lba = strtoul(value, &endptr, 10);
+        if (*endptr != '\0') goto error;
+    }
+    else if (!strcmp(key, "lastlba")) {
+        entry->last_lba = strtoul(value, &endptr, 10);
+        if (*endptr != '\0') goto error;
+    }
+    else if (!strcmp(key, "flags")) {
+        entry->flags = strtoul(value, &endptr, 16);
+        if (*endptr != '\0') goto error;
+    }
+    else if (!strcmp(key, "name")) {
+        GPT_to_UTF16(entry->name, value, GPT_NAMELEN);
+    }
+    else {
+        goto error;
+    }
+
+    return 0;
+
+error:
+    D(ERR, "Could not find key or parse value: %s,%s", key, value);
+    return 1;
+}
+
+int GPT_parse_entry(char *string, struct GPT_entry_raw *entry)
+{
+    char *ptr = string;
+    char *key, *value;
+
+    while ((ptr = get_key_value(ptr, &key, &value)) != NULL) {
+        if (add_key_value(key, value, entry)) {
+            D(WARN, "key or value not valid: %s %s", key, value);
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+void entry_set_guid(int n, uint8_t *guid)
+{
+    int fd;
+    fd = open("/dev/urandom", O_RDONLY);
+    read(fd, guid, 16);
+    close(fd);
+
+    //rfc4122
+    guid[8] = (guid[8] & 0x3F) | 0x80;
+    guid[7] = (guid[7] & 0x0F) | 0x40;
+}
+
+void GPT_default_content(struct GPT_content *content, struct GPT_entry_table *table)
+{
+    if (table != NULL) {
+        memcpy(&content->header, table->header, sizeof(content->header));
+        content->header.header_size = sizeof(content->header);
+        content->header.entry_size = sizeof(struct GPT_entry_raw);
+    }
+    else {
+        D(WARN, "Could not locate old gpt table, using default values");
+        memset(&content->header, 0, sizeof(content->header) / sizeof(int));
+        content->header = (struct GPT_header) {
+            .revision = 0x10000,
+            .header_size = sizeof(content->header),
+            .header_checksum = 0,
+            .reserved_zeros = 0,
+            .current_lba = 1,
+            .backup_lba = 1,
+            .entry_size = sizeof(struct GPT_entry_raw),
+            .partition_array_checksum = 0
+        };
+        strncpy((char *)content->header.signature, "EFI PART", 8);
+        strncpy((char *)content->header.disk_guid, "ANDROID MMC DISK", 16);
+    }
+}
+
+static int get_config_uint64(cnode *node, uint64_t *ptr, const char *name)
+{
+    const char *tmp;
+    uint64_t val;
+    char *endptr;
+    if ((tmp = config_str(node, name, NULL))) {
+        val = strtoull(tmp, &endptr, 10);
+        if (*endptr != '\0') {
+            D(WARN, "Value for %s is not a number: %s", name, tmp);
+            return 1;
+        }
+        *ptr = val;
+        return 0;
+    }
+    return 1;
+}
+
+static int get_config_string(cnode *node, char *ptr, int max_len, const char *name)
+{
+    size_t begin, end;
+    const char *value = config_str(node, name, NULL);
+    if (!value)
+        return -1;
+
+    begin = strcspn(value, "\"") + 1;
+    end = strcspn(&value[begin], "\"");
+
+    if ((int) end > max_len) {
+        D(WARN, "Identifier \"%s\" too long", value);
+        return -1;
+    }
+
+    strncpy(ptr, &value[begin], end);
+    if((int) end < max_len)
+        ptr[end] = 0;
+    return 0;
+}
+
+static void GPT_parse_header(cnode *node, struct GPT_content *content)
+{
+    get_config_uint64(node, &content->header.current_lba, "header_lba");
+    get_config_uint64(node, &content->header.backup_lba, "backup_lba");
+    get_config_uint64(node, &content->header.first_usable_lba, "first_lba");
+    get_config_uint64(node, &content->header.last_usable_lba, "last_lba");
+    get_config_uint64(node, &content->header.entries_lba, "entries_lba");
+    get_config_string(node, (char *) content->header.disk_guid, 16, "guid");
+}
+
+static int GPT_parse_partitions(cnode *node, struct GPT_content *content)
+{
+    cnode *current;
+    int i;
+    uint64_t partition_size;
+    struct GPT_entry_raw *entry;
+    for (i = 0, current = node->first_child; current; current = current->next, ++i) {
+        entry = &content->entries[i];
+        entry_set_guid(i, content->entries[i].partition_guid);
+        memcpy(&content->entries[i].type_guid, partition_type_uuid, 16);
+        if (get_config_uint64(current, &entry->first_lba, "first_lba")) {
+            D(ERR, "first_lba not specified");
+            return 1;
+        }
+        if (get_config_uint64(current, &partition_size, "partition_size")) {
+            D(ERR, "partition_size not specified");
+            return 1;
+        }
+        if (config_str(current, "system", NULL)) {
+            entry->flags |= GPT_FLAG_SYSTEM;
+        }
+        if (config_str(current, "bootable", NULL)) {
+            entry->flags |= GPT_FLAG_BOOTABLE;
+        }
+        if (config_str(current, "readonly", NULL)) {
+            entry->flags |= GPT_FLAG_READONLY;
+        }
+        if (config_str(current, "automount", NULL)) {
+            entry->flags |= GPT_FLAG_DOAUTOMOUNT;
+        }
+
+        get_config_uint64(current, &content->entries[i].flags, "flags");
+        content->entries[i].last_lba = content->entries[i].first_lba + partition_size - 1;
+        GPT_to_UTF16(content->entries[i].name, current->name, 16);
+    }
+    return 0;
+}
+
+static inline int cnode_count(cnode *node)
+{
+    int i;
+    cnode *current;
+    for (i = 0, current = node->first_child; current; current = current->next, ++i)
+        ;
+    return i;
+}
+
+
+static int GPT_parse_cnode(cnode *root, struct GPT_content *content)
+{
+    cnode *partnode;
+
+    if (!(partnode = config_find(root, "partitions"))) {
+        D(ERR, "Could not find partition table");
+        return 0;
+    }
+
+    GPT_parse_header(root, content);
+
+    content->header.entries_count = cnode_count(partnode);
+    content->entries = malloc(content->header.entries_count * sizeof(struct GPT_entry_raw));
+
+    if (GPT_parse_partitions(partnode, content)) {
+        D(ERR, "Could not parse partitions");
+        return 0;
+    }
+
+    return 1;
+}
+
+int GPT_parse_file(int fd, struct GPT_content *content)
+{
+    char *data;
+    int size;
+    int ret;
+    cnode *root = config_node("", "");
+
+    size = get_file_size(fd);
+    data = (char *) mmap(NULL, size + 1, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
+
+    if (data == NULL) {
+        if (size == 0)
+            D(ERR, "config file empty");
+        else
+            D(ERR, "Out of memory");
+        return 0;
+    }
+
+    data[size - 1] = 0;
+    config_load(root, data);
+
+    if (root->first_child == NULL) {
+        D(ERR, "Could not read config file");
+        return 0;
+    }
+
+    ret = GPT_parse_cnode(root, content);
+    munmap(data, size);
+    return ret;
+}
+
+void GPT_release_content(struct GPT_content *content)
+{
+    free(content->entries);
+}
+
+int GPT_write_content(const char *device, struct GPT_content *content)
+{
+    struct GPT_entry_table *maptable;
+
+    maptable = GPT_get_from_content(device, content);
+    if (maptable == NULL) {
+        D(ERR, "could not map device");
+        return 0;
+    }
+
+    memcpy(maptable->header, &content->header, sizeof(*maptable->header));
+    memcpy(maptable->entries, content->entries,
+           content->header.entries_count * sizeof(*maptable->entries));
+
+    GPT_sync(maptable);
+    GPT_release_device(maptable);
+
+    return 1;
+}
+
diff --git a/fastbootd/commands/partitions.h b/fastbootd/commands/partitions.h
new file mode 100644
index 0000000..9a6a88d
--- /dev/null
+++ b/fastbootd/commands/partitions.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef __FASTBOOTD_PATITIONS_
+#define __FASTBOOTD_PATITIONS_
+
+#include <stdint.h>
+
+#define GPT_ENTRIES 128
+#define GPT_NAMELEN 36
+
+#define GPT_FLAG_SYSTEM (1ULL << 0)
+#define GPT_FLAG_BOOTABLE (1ULL << 2)
+#define GPT_FLAG_READONLY (1ULL << 60)
+#define GPT_FLAG_DOAUTOMOUNT (1ULL << 63)
+
+// it should be passed in little endian order
+struct GPT_entry_raw {
+    uint8_t type_guid[16];
+    uint8_t partition_guid[16];
+    uint64_t first_lba; // little endian
+    uint64_t last_lba;
+    uint64_t flags;
+    uint16_t name[GPT_NAMELEN]; // UTF-16 LE
+};
+
+struct GPT_mapping {
+    void *map_ptr;
+    void *ptr;
+    unsigned size;
+};
+
+struct GPT_entry_table {
+    int fd;
+
+    struct GPT_mapping header_map;
+    struct GPT_mapping entries_map;
+    struct GPT_mapping sec_header_map;
+    struct GPT_mapping sec_entries_map;
+
+    struct GPT_header *header;
+    struct GPT_entry_raw *entries;
+    struct GPT_header *second_header;
+    struct GPT_entry_raw *second_entries;
+
+    unsigned sector_size;
+    unsigned partition_table_size;
+    int second_valid;
+};
+
+struct GPT_header {
+    uint8_t signature[8];
+    uint32_t revision;
+    uint32_t header_size;
+    uint32_t header_checksum;
+    uint32_t reserved_zeros;
+    uint64_t current_lba;
+    uint64_t backup_lba;
+    uint64_t first_usable_lba;
+    uint64_t last_usable_lba;
+    uint8_t disk_guid[16];
+    uint64_t entries_lba;
+    uint32_t entries_count;
+    uint32_t entry_size;
+    uint32_t partition_array_checksum;
+    // the rest should be filled with zeros
+} __attribute__((packed));
+
+struct GPT_content {
+    struct GPT_header header;
+    struct GPT_entry_raw *entries;
+};
+
+
+struct GPT_entry_table* GPT_get_device(const char *, unsigned lba);
+
+void GPT_release_device(struct GPT_entry_table *);
+
+void GPT_edit_entry(struct GPT_entry_table *table,
+                    struct GPT_entry_raw *old_entry,
+                    struct GPT_entry_raw *new_entry);
+
+int GPT_delete_entry(struct GPT_entry_table *table, struct GPT_entry_raw *entry);
+
+void GPT_add_entry(struct GPT_entry_table *table, struct GPT_entry_raw *entry);
+
+struct GPT_entry_raw *GPT_get_pointer(struct GPT_entry_table *table, struct GPT_entry_raw *entry);
+struct GPT_entry_raw *GPT_get_pointer_by_guid(struct GPT_entry_table *, const char *);
+struct GPT_entry_raw *GPT_get_pointer_by_name(struct GPT_entry_table *, const char *);
+
+//Use after every edit operation
+void GPT_sync();
+
+void GPT_to_UTF16(uint16_t *, const char *, int );
+void GPT_from_UTF16(char *, const uint16_t *, int);
+
+int GPT_parse_entry(char *string, struct GPT_entry_raw *entry);
+
+void GPT_default_content(struct GPT_content *content, struct GPT_entry_table *table);
+
+void GPT_release_content(struct GPT_content *content);
+
+int GPT_parse_file(int fd, struct GPT_content *content);
+
+int GPT_write_content(const char *device, struct GPT_content *content);
+
+int gpt_mmap(struct GPT_mapping *mapping, uint64_t location, int size, int fd);
+
+void gpt_unmap(struct GPT_mapping *mapping);
+
+#endif
diff --git a/fastbootd/commands/virtual_partitions.c b/fastbootd/commands/virtual_partitions.c
new file mode 100644
index 0000000..813f485
--- /dev/null
+++ b/fastbootd/commands/virtual_partitions.c
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "commands/virtual_partitions.h"
+#include "debug.h"
+
+static struct virtual_partition *partitions = NULL;
+
+int try_handle_virtual_partition(struct protocol_handle *handle, const char *arg)
+{
+    struct virtual_partition *current;
+
+    for (current = partitions; current != NULL; current = current->next) {
+        if (!strcmp(current->name, arg)) {
+            current->handler(handle, arg);
+        }
+    }
+
+    return 0;
+}
+
+void virtual_partition_register(
+        const char * name,
+        void (*handler)(struct protocol_handle *phandle, const char *arg))
+{
+    struct virtual_partition *new;
+    new = malloc(sizeof(*new));
+    if (new) {
+        new->name = name;
+        new->handler = handler;
+        new->next = partitions;
+        partitions = new;
+    }
+    else {
+        D(ERR, "Out of memory");
+    }
+}
diff --git a/fastbootd/commands/virtual_partitions.h b/fastbootd/commands/virtual_partitions.h
new file mode 100644
index 0000000..88df71e
--- /dev/null
+++ b/fastbootd/commands/virtual_partitions.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef FASTBOOTD_VIRTUAL_PARTITIONS_H
+#define FASTBOOTD_VIRTUAL_PARTITIONS_H
+
+#include "protocol.h"
+
+struct virtual_partition {
+    struct virtual_partition *next;
+    const char *name;
+    void (*handler)(struct protocol_handle *phandle, const char *arg);
+};
+
+int try_handle_virtual_partition(struct protocol_handle *handle, const char *arg);
+
+void virtual_partition_register(
+        const char * name,
+        void (*handler)(struct protocol_handle *phandle, const char *arg));
+
+#endif
diff --git a/fastbootd/config.c b/fastbootd/config.c
index b8503fd..fe6da69 100644
--- a/fastbootd/config.c
+++ b/fastbootd/config.c
@@ -35,29 +35,13 @@
 #include <sys/types.h>
 
 #include "protocol.h"
+#include "utils.h"
 
 #include "debug.h"
 
 // TODO: change config path
 #define CONFIG_PATH "/data/fastboot.cfg"
 
-static char *strip(char *str)
-{
-    int n;
-
-    n = strspn(str, " \t");
-    str += n;
-
-    for (n = strlen(str) - 1; n >= 0; n--) {
-        if (str[n] == ' ' || str[n] == '\t')
-            str[n] = '\0';
-        else
-            break;
-    }
-
-    return str;
-}
-
 static int config_parse_line(char *line)
 {
     char *c;
diff --git a/fastbootd/fastbootd.c b/fastbootd/fastbootd.c
index 98df0db..2b51b33 100644
--- a/fastbootd/fastbootd.c
+++ b/fastbootd/fastbootd.c
@@ -16,30 +16,85 @@
 
 #include <stdio.h>
 #include <unistd.h>
-
 #include <cutils/klog.h>
+#include <getopt.h>
+#include <stdlib.h>
 
 #include "debug.h"
+#include "trigger.h"
+#include "socket_client.h"
+#include "secure.h"
 
 unsigned int debug_level = DEBUG;
 
 void commands_init();
 void usb_init();
 void config_init();
+int transport_socket_init();
+int network_discovery_init();
+void ssh_server_start();
 
 int main(int argc, char **argv)
 {
+    int socket_client = 0;
+    int c;
+    int network = 1;
+
+    klog_init();
+    klog_set_level(6);
+
+    const struct option longopts[] = {
+        {"socket", no_argument, 0, 'S'},
+        {"nonetwork", no_argument, 0, 'n'},
+        {0, 0, 0, 0}
+    };
+
+    while (1) {
+        c = getopt_long(argc, argv, "Sn", longopts, NULL);
+        /* Alphabetical cases */
+        if (c < 0)
+            break;
+        switch (c) {
+        case 'S':
+            socket_client = 1;
+            break;
+        case 'n':
+            network = 0;
+            break;
+        case '?':
+            return 1;
+        default:
+            return 0;
+        }
+    }
+
     (void)argc;
     (void)argv;
 
     klog_init();
     klog_set_level(6);
 
-    config_init();
-    commands_init();
-    usb_init();
-    while (1) {
-        sleep(1);
+    if (socket_client) {
+        //TODO: Shouldn't we change current tty into raw mode?
+        run_socket_client();
+    }
+    else {
+        cert_init_crypto();
+        config_init();
+        load_trigger();
+        commands_init();
+        usb_init();
+
+        if (network) {
+            if (!transport_socket_init())
+                exit(1);
+            ssh_server_start();
+            network_discovery_init();
+        }
+
+        while (1) {
+            sleep(1);
+        }
     }
     return 0;
 }
diff --git a/fastbootd/include/vendor_trigger.h b/fastbootd/include/vendor_trigger.h
new file mode 100644
index 0000000..51204fa
--- /dev/null
+++ b/fastbootd/include/vendor_trigger.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef __VENDOR_TRIGGER_H_
+#define __VENDOR_TRIGGER_H_
+
+#define TRIGGER_MODULE_ID "fastbootd"
+#include <hardware/hardware.h>
+
+__BEGIN_DECLS
+
+struct GPT_entry_raw;
+struct GPT_content;
+
+/*
+ * Structer with function pointers may become longer in the future
+ */
+
+struct vendor_trigger_t {
+    struct hw_device_t common;
+
+    /*
+     * This function runs at the beggining and shoud never be changed
+     *
+     * version is number parameter indicating version on the fastbootd side
+     * libversion is version indicateing version of the library version
+     *
+     * returns 0 if it can cooperate with the current version and 1 in opposite
+     */
+    int (*check_version)(const int version, int *libversion);
+
+
+    /*
+     * Return value -1 forbid the action from the vendor site and sets errno
+     */
+    int (* gpt_layout)(struct GPT_content *);
+    int (* oem_cmd)(const char *arg, const char **response);
+};
+
+__END_DECLS
+
+#endif
diff --git a/fastbootd/network_discovery.c b/fastbootd/network_discovery.c
new file mode 100644
index 0000000..1cd3e48
--- /dev/null
+++ b/fastbootd/network_discovery.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <dns_sd.h>
+#include <cutils/properties.h>
+#include <unistd.h>
+
+#include "debug.h"
+#include "network_discovery.h"
+#include "utils.h"
+
+#define MDNS_SERVICE_NAME "mdnsd"
+#define MDNS_SERVICE_STATUS "init.svc.mdnsd"
+#define FASTBOOTD_TYPE "_fastbootd._tcp"
+#define FASTBOOTD_DOMAIN "local."
+#define FASTBOOTD_NAME "fastbootd"
+
+
+static void reg_reply(DNSServiceRef sdref, const DNSServiceFlags flags, DNSServiceErrorType errorCode,
+    const char *name, const char *regtype, const char *domain, void *context)
+{
+    (void)sdref;    // Unused
+    (void)flags;    // Unused
+    (void)context;  // Unused
+    if (errorCode == kDNSServiceErr_ServiceNotRunning) {
+        fprintf(stderr, "Error code %d\n", errorCode);
+    }
+
+
+    printf("Got a reply for service %s.%s%s: ", name, regtype, domain);
+
+    if (errorCode == kDNSServiceErr_NoError)
+    {
+        if (flags & kDNSServiceFlagsAdd)
+            printf("Name now registered and active\n");
+        else
+            printf("Name registration removed\n");
+        if (errorCode == kDNSServiceErr_NameConflict)
+            printf("Name in use, please choose another\n");
+        else
+            printf("Error %d\n", errorCode);
+
+        if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout);
+    }
+}
+
+static int register_service() {
+    DNSServiceRef sdref = NULL;
+    const char *domain = FASTBOOTD_DOMAIN;
+    const char *type = FASTBOOTD_TYPE;
+    const char *host = NULL;
+    char name[PROP_VALUE_MAX];
+    uint16_t port = 22;
+    int flags = 0;
+    DNSServiceErrorType result;
+    property_get("ro.serialno", name, "");
+    if (!strcmp(name, "")) {
+        D(ERR, "No property serialno");
+        return -1;
+    }
+
+    result = DNSServiceRegister(&sdref, flags, kDNSServiceInterfaceIndexAny,
+                       name, type, domain, host, port,
+                       0, NULL, reg_reply, NULL);
+    if (result != kDNSServiceErr_NoError) {
+        D(ERR, "Unable to register service");
+        return -1;
+    }
+    return 0;
+}
+
+
+int network_discovery_init()
+{
+    D(INFO, "Starting discovery");
+    if (service_start(MDNS_SERVICE_NAME)) {
+        D(ERR, "Unable to start discovery");
+        return -1;
+    }
+
+    if (register_service()) {
+        D(ERR, "Unable to register service");
+        return -1;
+    }
+
+    return 0;
+}
+
diff --git a/fastbootd/network_discovery.h b/fastbootd/network_discovery.h
new file mode 100644
index 0000000..75fda63
--- /dev/null
+++ b/fastbootd/network_discovery.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _FASTBOOTD_NETWORK_DISCOVERY_H
+#define _FASTBOOTD_NETWORK_DISCOVERY_H
+
+int network_discovery_init();
+
+#endif
diff --git a/fastbootd/other/gptedit.c b/fastbootd/other/gptedit.c
new file mode 100644
index 0000000..16d34a5
--- /dev/null
+++ b/fastbootd/other/gptedit.c
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <unistd.h>
+
+#include <cutils/klog.h>
+
+#include "commands/partitions.h"
+#include "debug.h"
+
+unsigned int debug_level = DEBUG;
+//TODO: add tool to generate config file
+
+void usage() {
+    fprintf(stderr,
+            "usage: test_gpt [ <option> ] <file>\n"
+            "\n"
+            "options:\n"
+            "  -p                                       print partitions\n"
+            "  -c                                       print config file\n"
+            "  -a                                       adds new partition\n"
+            "  -d                                       deletes partition (-o needed)\n"
+            "\n"
+            "  -n name@startlba,endlba                  new partition detail\n"
+            "  -o                                       old partition name\n"
+            "  -t                                       type guid\n"
+            "  -g                                       partition guid\n"
+            "  -l gpt_location                          specyfies gpt secto\n"
+    );
+
+}
+
+void printGPT(struct GPT_entry_table *table);
+void addGPT(struct GPT_entry_table *table, const char *arg, const char *guid, const char *tguid);
+void deleteGPT(struct GPT_entry_table *table, const char *name);
+void configPrintGPT(struct GPT_entry_table *table);
+
+int main(int argc, char *argv[]) {
+    int print_cmd = 0;
+    int config_cmd = 0;
+    int add_cmd = 0;
+    int del_cmd = 0;
+    int sync_cmd = 0;
+    int c;
+    const char *new_partition = NULL;
+    const char *old_partition = NULL;
+    const char *type_guid = NULL;
+    const char *partition_guid = NULL;
+    unsigned gpt_location = 1;
+
+    klog_init();
+    klog_set_level(6);
+
+    const struct option longopts[] = {
+        {"print", no_argument, 0, 'p'},
+        {"config-print", no_argument, 0, 'c'},
+        {"add", no_argument, 0, 'a'},
+        {"del", no_argument, 0, 'd'},
+        {"new", required_argument, 0, 'n'},
+        {"old", required_argument, 0, 'o'},
+        {"type", required_argument, 0, 't'},
+        {"sync", required_argument, 0, 's'},
+        {"guid", required_argument, 0, 'g'},
+        {"location", required_argument, 0, 'l'},
+        {0, 0, 0, 0}
+    };
+
+    while (1) {
+        c = getopt_long(argc, argv, "pcadt:g:n:o:sl:", longopts, NULL);
+        /* Alphabetical cases */
+        if (c < 0)
+            break;
+        switch (c) {
+        case 'p':
+            print_cmd = 1;
+            break;
+        case 'c':
+            config_cmd = 1;
+            break;
+        case 'a':
+            add_cmd = 1;
+            break;
+        case 'd':
+            del_cmd = 1;
+            break;
+        case 'n':
+            new_partition = optarg;
+            break;
+        case 'o':
+            old_partition = optarg;
+            break;
+        case 't':
+            type_guid = optarg;
+        case 'g':
+            partition_guid = optarg;
+            break;
+        case 's':
+            sync_cmd = 1;
+            break;
+        case 'l':
+            gpt_location = strtoul(optarg, NULL, 10);
+            fprintf(stderr, "Got offset as %d", gpt_location);
+            break;
+        case '?':
+            return 1;
+        default:
+            abort();
+        }
+    }
+
+    argc -= optind;
+    argv += optind;
+
+    if (argc < 1) {
+        usage();
+        return 1;
+    }
+
+    const char *path = argv[0];
+    struct GPT_entry_table *table = GPT_get_device(path, gpt_location);
+    if (table == NULL) {
+        fprintf(stderr, "unable to get GPT table from %s\n", path);
+        return 1;
+    }
+
+    if (add_cmd)
+        addGPT(table, new_partition, partition_guid, type_guid);
+    if (del_cmd)
+        deleteGPT(table, old_partition);
+    if (print_cmd)
+        printGPT(table);
+    if (config_cmd)
+        configPrintGPT(table);
+    if (sync_cmd)
+        GPT_sync(table);
+
+    GPT_release_device(table);
+
+    return 0;
+}
+
+void printGPT(struct GPT_entry_table *table) {
+    struct GPT_entry_raw *entry = table->entries;
+    unsigned n, m;
+    char name[GPT_NAMELEN + 1];
+
+    printf("ptn  start block   end block     name\n");
+    printf("---- ------------- -------------\n");
+
+    for (n = 0; n < table->header->entries_count; n++, entry++) {
+        if (entry->type_guid[0] == 0)
+            continue;
+        for (m = 0; m < GPT_NAMELEN; m++) {
+            name[m] = entry->name[m] & 127;
+        }
+        name[m] = 0;
+        printf("#%03d %13lld %13lld %s\n",
+            n + 1, entry->first_lba, entry->last_lba, name);
+    }
+}
+
+void configPrintGPT(struct GPT_entry_table *table) {
+    struct GPT_entry_raw *entry = table->entries;
+    unsigned n, m;
+    char name[GPT_NAMELEN + 1];
+    char temp_guid[17];
+    temp_guid[16] = 0;
+
+    printf("header_lba %lld\n", table->header->current_lba);
+    printf("backup_lba %lld\n", table->header->backup_lba);
+    printf("first_lba %lld\n", table->header->first_usable_lba);
+    printf("last_lba %lld\n", table->header->last_usable_lba);
+    printf("entries_lba %lld\n", table->header->entries_lba);
+    snprintf(temp_guid, 17, "%s", table->header->disk_guid);
+    printf("guid \"%s\"", temp_guid);
+
+    printf("\npartitions {\n");
+
+    for (n = 0; n < table->header->entries_count; n++, entry++) {
+        uint64_t size = entry->last_lba - entry->first_lba + 1;
+
+        if (entry->type_guid[0] == 0)
+            continue;
+        for (m = 0; m < GPT_NAMELEN; m++) {
+            name[m] = entry->name[m] & 127;
+        }
+        name[m] = 0;
+
+        printf("    %s {\n", name);
+        snprintf(temp_guid, 17, "%s", entry->partition_guid);
+        printf("        guid \"%s\"\n", temp_guid);
+        printf("        first_lba %lld\n", entry->first_lba);
+        printf("        partition_size %lld\n", size);
+        if (entry->flags & GPT_FLAG_SYSTEM)
+            printf("        system\n");
+        if (entry->flags & GPT_FLAG_BOOTABLE)
+            printf("        bootable\n");
+        if (entry->flags & GPT_FLAG_READONLY)
+            printf("        readonly\n");
+        if (entry->flags & GPT_FLAG_DOAUTOMOUNT)
+            printf("        automount\n");
+        printf("    }\n\n");
+    }
+    printf("}\n");
+}
+
+void addGPT(struct GPT_entry_table *table, const char *str  , const char *guid, const char *tguid) {
+    char *c, *c2;
+    char *arg = malloc(strlen(str));
+    char *name = arg;
+    unsigned start, end;
+    strcpy(arg, str);
+    if (guid == NULL || tguid == NULL) {
+        fprintf(stderr, "Type guid and partion guid needed");
+        free(arg);
+        return;
+    }
+
+    c = strchr(arg, '@');
+
+    if (c == NULL) {
+        fprintf(stderr, "Wrong entry format");
+        free(arg);
+        return;
+    }
+
+    *c++ = '\0';
+
+    c2 = strchr(c, ',');
+
+    if (c2 == NULL) {
+        fprintf(stderr, "Wrong entry format");
+        free(arg);
+        return;
+    }
+
+    start = strtoul(c, NULL, 10);
+    *c2++ = '\0';
+    end = strtoul(c2, NULL, 10);
+
+    struct GPT_entry_raw data;
+    strncpy((char *)data.partition_guid, guid, 15);
+    data.partition_guid[15] = '\0';
+    strncpy((char *)data.type_guid, tguid, 15);
+    data.type_guid[15] = '\0';
+    GPT_to_UTF16(data.name, name, GPT_NAMELEN);
+    data.first_lba = start;
+    data.last_lba = end;
+
+    fprintf(stderr, "Adding (%d,%d) %s as, [%s, %s]", start, end, name, (char *) data.type_guid, (char *) data.partition_guid);
+    GPT_add_entry(table, &data);
+    free(arg);
+}
+
+void deleteGPT(struct GPT_entry_table *table, const char *name) {
+    struct GPT_entry_raw *entry;
+
+    if (name == NULL) {
+        fprintf(stderr, "Need partition name");
+        return;
+    }
+
+    entry = GPT_get_pointer_by_name(table, name);
+
+    if (!entry) {
+        fprintf(stderr, "Unable to find partition: %s", name);
+        return;
+    }
+    GPT_delete_entry(table, entry);
+}
+
diff --git a/fastbootd/other/partitions.sample.cfg b/fastbootd/other/partitions.sample.cfg
new file mode 100644
index 0000000..49562cf
--- /dev/null
+++ b/fastbootd/other/partitions.sample.cfg
@@ -0,0 +1,60 @@
+
+header_lba 1
+backup_lba 101
+first_lba 43
+last_lba 100
+entries_lba 2
+
+partitions {
+    #You can generate this as output from gptedit -c
+    SOS {
+        first_lba 28672
+        partition_size 16384
+    }
+
+    DTB {
+        first_lba 45056
+        partition_size 8192
+    }
+
+    LNX {
+        first_lba 53248
+        partition_size 16384
+    }
+
+    APP {
+        first_lba 69632
+        partition_size 1048576
+    }
+
+    CAC {
+        first_lba 1118208
+        partition_size 1572864
+    }
+
+    MSC {
+        first_lba 2691072
+        partition_size 4096
+    }
+
+    USP {
+        first_lba 2695168
+        partition_size 65536
+    }
+
+    MDA {
+        first_lba 2760704
+        partition_size 4096
+    }
+
+    FCT {
+        first_lba 2764800
+        partition_size 32768
+    }
+
+    UDA {
+        first_lba 2797568
+        partition_size 27975680
+    }
+}
+
diff --git a/fastbootd/other/sign/src/SignImg.java b/fastbootd/other/sign/src/SignImg.java
new file mode 100644
index 0000000..338d427
--- /dev/null
+++ b/fastbootd/other/sign/src/SignImg.java
@@ -0,0 +1,181 @@
+package signtool;
+
+import java.io.*;
+import java.util.Properties;
+import java.util.ArrayList;
+
+import javax.mail.internet.*;
+import javax.mail.MessagingException;
+import javax.mail.Session;
+import javax.activation.MailcapCommandMap;
+import javax.activation.CommandMap;
+
+import java.security.PrivateKey;
+import java.security.Security;
+import java.security.KeyFactory;
+import java.security.KeyStore;
+import java.security.NoSuchAlgorithmException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.cert.X509Certificate;
+import java.security.cert.CertificateFactory;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateEncodingException;
+
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
+import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+import org.bouncycastle.cms.CMSProcessableByteArray;
+import org.bouncycastle.cms.CMSSignedGenerator;
+import org.bouncycastle.cms.CMSSignedDataGenerator;
+import org.bouncycastle.cms.CMSSignedGenerator;
+import org.bouncycastle.cms.CMSProcessable;
+import org.bouncycastle.cms.CMSSignedData;
+import org.bouncycastle.cms.CMSTypedData;
+import org.bouncycastle.cert.jcajce.JcaCertStore;
+import org.bouncycastle.util.Store;
+import org.bouncycastle.asn1.ASN1InputStream;    
+import org.bouncycastle.asn1.DEROutputStream;
+import org.bouncycastle.asn1.ASN1Object;
+
+
+public class SignImg {
+
+    /* It reads private key in pkcs#8 formate
+     * Conversion:
+     * openssl pkcs8 -topk8 -nocrypt -outform DER < inkey.pem > outkey.pk8
+     */
+    private static PrivateKey getPrivateKey(String path) throws IOException, FileNotFoundException, NoSuchAlgorithmException, InvalidKeySpecException {
+        File file = new File(path);
+        FileInputStream fis = new FileInputStream(file);
+        byte[] data = new byte[(int)file.length()];
+        fis.read(data);
+        fis.close();
+
+        PKCS8EncodedKeySpec kspec = new PKCS8EncodedKeySpec(data);
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        PrivateKey privateKey = kf.generatePrivate(kspec);
+
+        return privateKey;
+    }
+
+    private static MimeBodyPart getContent(String path) throws IOException, FileNotFoundException, MessagingException {
+        MimeBodyPart body = new MimeBodyPart();
+
+        File file = new File(path);
+        FileInputStream fis = new FileInputStream(file);
+        byte[] data = new byte[(int)file.length()];
+        fis.read(data);
+        fis.close();
+
+        body.setContent(data, "application/octet-stream");
+
+        return body;
+    }
+
+    private static CMSProcessableByteArray getCMSContent(String path) throws IOException, FileNotFoundException, MessagingException {
+        File file = new File(path);
+        FileInputStream fis = new FileInputStream(file);
+        byte[] data = new byte[(int)file.length()];
+        fis.read(data);
+        fis.close();
+        CMSProcessableByteArray cms = new CMSProcessableByteArray(data);
+
+        return cms;
+    }
+
+    private static X509Certificate readCert(String path) throws IOException, FileNotFoundException, CertificateException {
+        File file = new File(path);
+        FileInputStream is = new FileInputStream(file);
+
+        CertificateFactory cf = CertificateFactory.getInstance("X.509");
+        Certificate cert = cf.generateCertificate(is);
+        is.close();
+
+        return (X509Certificate) cert;
+    }
+
+    private static void save(MimeBodyPart content, String path) throws IOException, FileNotFoundException, MessagingException {
+        File file = new File(path);
+        FileOutputStream os = new FileOutputStream(file);
+
+        content.writeTo(os);
+
+        os.close();
+    }
+
+    private static Store certToStore(X509Certificate certificate) throws CertificateEncodingException {
+        ArrayList<X509Certificate> certList = new ArrayList<X509Certificate>();
+        certList.add(certificate);
+        return new JcaCertStore(certList);
+    }
+
+    public static void setDefaultMailcap()
+    {
+        MailcapCommandMap _mailcap =
+            (MailcapCommandMap)CommandMap.getDefaultCommandMap();
+
+        _mailcap.addMailcap("application/pkcs7-signature;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.pkcs7_signature");
+        _mailcap.addMailcap("application/pkcs7-mime;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.pkcs7_mime");
+        _mailcap.addMailcap("application/x-pkcs7-signature;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.x_pkcs7_signature");
+        _mailcap.addMailcap("application/x-pkcs7-mime;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.x_pkcs7_mime");
+        _mailcap.addMailcap("multipart/signed;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.multipart_signed");
+
+        CommandMap.setDefaultCommandMap(_mailcap);
+    } 
+
+    public static void main(String[] args) {
+        try {
+            if (args.length < 4) {
+                System.out.println("Usage: signimg data private_key certificate output");
+                return;
+            }
+            System.out.println("Signing the image");
+            setDefaultMailcap();
+
+            Security.addProvider(new BouncyCastleProvider());
+
+            PrivateKey key = getPrivateKey(args[1]);
+            System.out.println("File read sucessfully");
+
+            CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
+
+            CMSTypedData body = getCMSContent(args[0]);
+            System.out.println("Content read sucessfully");
+
+            X509Certificate cert = (X509Certificate) readCert(args[2]);
+            System.out.println("Certificate read sucessfully");
+
+            ContentSigner sha256Signer = new JcaContentSignerBuilder("SHA256withRSA").setProvider("BC").build(key);
+
+            Store certs = certToStore(cert);
+
+            generator.addCertificates(certs);
+            generator.addSignerInfoGenerator(
+                          new JcaSignerInfoGeneratorBuilder(
+                                new JcaDigestCalculatorProviderBuilder().setProvider("BC").build())
+                          .build(sha256Signer, cert));
+
+            CMSSignedData signed = generator.generate(body, true);
+            System.out.println("Signed");
+
+            Properties props = System.getProperties();
+            Session session = Session.getDefaultInstance(props, null);
+            
+            File file = new File(args[3]);
+            FileOutputStream os = new FileOutputStream(file);
+
+            ASN1InputStream asn1 = new ASN1InputStream(signed.getEncoded());
+            ByteArrayOutputStream out = new ByteArrayOutputStream(); 
+            DEROutputStream dOut = new DEROutputStream(os); 
+            dOut.writeObject(ASN1Object.fromByteArray(signed.getEncoded()));
+
+        }
+        catch (Exception ex) {
+            System.out.println("Exception during programm execution: " + ex.getMessage());
+        }
+    }
+}
diff --git a/fastbootd/other/vendor_trigger.c b/fastbootd/other/vendor_trigger.c
new file mode 100644
index 0000000..101959b
--- /dev/null
+++ b/fastbootd/other/vendor_trigger.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+
+#include "vendor_trigger.h"
+#include "debug.h"
+
+unsigned int debug_level = DEBUG;
+
+static const int version = 1;
+
+int check_version(const int fastboot_version, int *libversion) {
+    *libversion = version;
+    return !(fastboot_version == version);
+}
+
+int gpt_layout(struct GPT_content *table) {
+    D(DEBUG, "message from libvendor");
+    return 0;
+}
+
+int oem_cmd(const char *arg, const char **response) {
+    D(DEBUG, "message from libvendor, oem catched request %s", arg);
+    return 0;
+}
+
+static int close_triggers(struct vendor_trigger_t *dev)
+{
+    if (dev)
+        free(dev);
+
+    return 0;
+}
+
+static int open_triggers(const struct hw_module_t *module, char const *name,
+                         struct hw_device_t **device) {
+    struct vendor_trigger_t *dev = malloc(sizeof(struct vendor_trigger_t));
+    klog_init();
+    klog_set_level(6);
+
+    memset(dev, 0, sizeof(*dev));
+    dev->common.module = (struct hw_module_t *) module;
+    dev->common.close  = (int (*)(struct hw_device_t *)) close_triggers;
+
+    dev->gpt_layout = gpt_layout;
+    dev->oem_cmd = oem_cmd;
+
+    *device = (struct hw_device_t *) dev;
+
+    return 0;
+}
+
+
+static struct hw_module_methods_t trigger_module_methods = {
+    .open = open_triggers,
+};
+
+struct hw_module_t HAL_MODULE_INFO_SYM = {
+    .tag = HARDWARE_MODULE_TAG,
+    .version_major = 1,
+    .version_minor = 0,
+    .id = TRIGGER_MODULE_ID,
+    .name = "vendor trigger library for fastbootd",
+    .author = "Google, Inc.",
+    .methods = &trigger_module_methods,
+};
+
diff --git a/fastbootd/secure.c b/fastbootd/secure.c
new file mode 100644
index 0000000..a657ad4
--- /dev/null
+++ b/fastbootd/secure.c
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include <openssl/pem.h>
+#include <openssl/engine.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+#include <openssl/pem.h>
+#include <openssl/cms.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "secure.h"
+#include "debug.h"
+#include "utils.h"
+
+
+void cert_init_crypto() {
+    CRYPTO_malloc_init();
+    ERR_load_crypto_strings();
+    OpenSSL_add_all_algorithms();
+    ENGINE_load_builtin_engines();
+}
+
+X509_STORE *cert_store_from_path(const char *path) {
+
+    X509_STORE *store;
+    struct stat st;
+    X509_LOOKUP *lookup;
+
+    if (stat(path, &st)) {
+        D(ERR, "Unable to stat cert path");
+        goto error;
+    }
+
+    if (!(store = X509_STORE_new())) {
+        goto error;
+    }
+
+    if (S_ISDIR(st.st_mode)) {
+        lookup = X509_STORE_add_lookup(store,X509_LOOKUP_hash_dir());
+        if (lookup == NULL)
+            goto error;
+        if (!X509_LOOKUP_add_dir(lookup, path, X509_FILETYPE_PEM)) {
+            D(ERR, "Error loading cert directory %s", path);
+            goto error;
+        }
+    }
+    else if(S_ISREG(st.st_mode)) {
+        lookup = X509_STORE_add_lookup(store,X509_LOOKUP_file());
+        if (lookup == NULL)
+            goto error;
+        if (!X509_LOOKUP_load_file(lookup, path, X509_FILETYPE_PEM)) {
+            D(ERR, "Error loading cert directory %s", path);
+            goto error;
+        }
+    }
+    else {
+        D(ERR, "cert path is not directory or regular file");
+        goto error;
+    }
+
+    return store;
+
+error:
+    return NULL;
+}
+
+
+int cert_read(int fd, CMS_ContentInfo **content, BIO **output) {
+    BIO *input;
+    *output = NULL;
+
+
+    input = BIO_new_fd(fd, BIO_NOCLOSE);
+    if (input == NULL) {
+        D(ERR, "Unable to open input");
+        goto error;
+    }
+
+    //TODO:
+    // read with d2i_CMS_bio to support DER
+    // with java or just encode data with base64
+    *content = SMIME_read_CMS(input, output);
+    if (*content == NULL) {
+        unsigned long err = ERR_peek_last_error();
+        D(ERR, "Unable to parse input file: %s", ERR_lib_error_string(err));
+        goto error_read;
+    }
+
+    BIO_free(input);
+
+    return 0;
+
+error_read:
+    BIO_free(input);
+error:
+    return 1;
+}
+
+int cert_verify(BIO *content, CMS_ContentInfo *content_info, X509_STORE *store, int *out_fd) {
+    BIO *output_temp;
+    int ret;
+
+    *out_fd = create_temp_file();
+    if (*out_fd < 0) {
+        D(ERR, "unable to create temporary file");
+        return -1;
+    }
+
+    output_temp = BIO_new_fd(*out_fd, BIO_NOCLOSE);
+    if (output_temp == NULL) {
+        D(ERR, "unable to create temporary bio");
+        close(*out_fd);
+        return -1;
+    }
+
+    ret = CMS_verify(content_info, NULL ,store, content, output_temp, 0);
+
+    if (ret == 0) {
+        char buf[256];
+        unsigned long err = ERR_peek_last_error();
+        D(ERR, "Verification failed with reason: %s, %s", ERR_lib_error_string(err),  ERR_error_string(err, buf));
+        D(ERR, "Data used: content %d", (int) content);
+    }
+
+    ERR_clear_error();
+    ERR_remove_state(0);
+
+    BIO_free(output_temp);
+
+    return ret;
+}
+
+void cert_release(BIO *content, CMS_ContentInfo *info) {
+    BIO_free(content);
+    CMS_ContentInfo_free(info);
+}
+
diff --git a/fastbootd/secure.h b/fastbootd/secure.h
new file mode 100644
index 0000000..878a643
--- /dev/null
+++ b/fastbootd/secure.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _FASTBOOTD_SECURE_H
+#define _FASTBOOTD_SECURE_H
+
+#include <openssl/ssl.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+#include <openssl/pem.h>
+#include <openssl/cms.h>
+
+void cert_init_crypto();
+
+X509_STORE *cert_store_from_path(const char*stream);
+
+static inline void cert_release_store(X509_STORE *store) {
+    X509_STORE_free(store);
+}
+
+int cert_read(int fd, CMS_ContentInfo **content, BIO **output);
+int cert_verify(BIO *content, CMS_ContentInfo *content_info, X509_STORE *store, int *out_fd);
+void cert_release(BIO *content, CMS_ContentInfo *info);
+
+#endif
diff --git a/fastbootd/socket_client.c b/fastbootd/socket_client.c
new file mode 100644
index 0000000..da636db
--- /dev/null
+++ b/fastbootd/socket_client.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <cutils/sockets.h>
+#include <poll.h>
+#include <unistd.h>
+
+#include "utils.h"
+
+#define BUFFER_SIZE 256
+
+#define STDIN_FD 0
+#define STDOUT_FD 1
+#define STDERR_FD 2
+
+void run_socket_client() {
+    int fd;
+    char buffer[BUFFER_SIZE];
+    int n;
+    struct pollfd fds[2];
+
+    fd = socket_local_client("fastbootd",
+                         ANDROID_SOCKET_NAMESPACE_RESERVED,
+                         SOCK_STREAM);
+
+    if (fd < 0) {
+        fprintf(stderr, "ERROR: Unable to open fastbootd socket\n");
+        return;
+    }
+
+    fds[0].fd = STDIN_FD;
+    fds[0].events = POLLIN;
+    fds[1].fd = fd;
+    fds[1].events = POLLIN;
+
+    while(true) {
+        if (poll(fds, 2, -1) <= 0) {
+            fprintf(stderr, "ERROR: socket error");
+            return;
+        }
+
+        if (fds[0].revents & POLLIN) {
+            if ((n = read(STDIN_FD, buffer, BUFFER_SIZE)) < 0) {
+                goto error;
+            }
+
+            if (bulk_write(fd, buffer, n) < 0) {
+                goto error;
+            }
+        }
+
+        if (fds[1].revents & POLLIN) {
+            if ((n = read(fd, buffer, BUFFER_SIZE)) < 0) {
+                goto error;
+            }
+
+            if (bulk_write(STDOUT_FD, buffer, n) < 0) {
+                goto error;
+            }
+        }
+    }
+
+error:
+    fprintf(stderr, "Transport error\n");
+}
diff --git a/fastbootd/socket_client.h b/fastbootd/socket_client.h
new file mode 100644
index 0000000..4481ff2
--- /dev/null
+++ b/fastbootd/socket_client.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _FASTBOOTD_SOCKET_CLIENT_H
+#define _FASTBOOTD_SOCKET_CLIENT_H
+
+void run_socket_client();
+
+#endif
diff --git a/fastbootd/transport.c b/fastbootd/transport.c
index 01a5a8a..19a705c 100644
--- a/fastbootd/transport.c
+++ b/fastbootd/transport.c
@@ -99,6 +99,7 @@
         }
         if (ret > 0) {
             buffer[ret] = 0;
+            //TODO: multiple threads
             protocol_handle_command(phandle, buffer);
         }
     }
diff --git a/fastbootd/transport_socket.c b/fastbootd/transport_socket.c
new file mode 100644
index 0000000..ff0f3bd
--- /dev/null
+++ b/fastbootd/transport_socket.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <cutils/sockets.h>
+
+#include "debug.h"
+#include "transport.h"
+#include "utils.h"
+
+
+#define container_of(ptr, type, member) \
+    ((type*)((char*)(ptr) - offsetof(type, member)))
+
+#define SOCKET_WORKING 0
+#define SOCKET_STOPPED -1
+
+
+struct socket_transport {
+    struct transport transport;
+
+    int fd;
+};
+
+struct socket_handle {
+    struct transport_handle handle;
+
+    int fd;
+};
+
+void socket_close(struct transport_handle *thandle)
+{
+    struct socket_handle * handle = container_of(thandle, struct socket_handle, handle);
+    close(handle->fd);
+}
+
+struct transport_handle *socket_connect(struct transport *transport)
+{
+    struct socket_handle *handle = calloc(sizeof(struct socket_handle), 1);
+    struct socket_transport *socket_transport = container_of(transport, struct socket_transport, transport);
+    struct sockaddr addr;
+    socklen_t alen = sizeof(addr);
+
+    handle->fd = accept(socket_transport->fd, &addr, &alen);
+
+    if (handle->fd < 0) {
+        D(WARN, "socket connect error");
+        return NULL;
+    }
+
+    D(DEBUG, "[ socket_thread - registering device ]");
+    return &handle->handle;
+}
+
+ssize_t socket_write(struct transport_handle *thandle, const void *data, size_t len)
+{
+    ssize_t ret;
+    struct socket_handle *handle = container_of(thandle, struct socket_handle, handle);
+
+    D(DEBUG, "about to write (fd=%d, len=%d)", handle->fd, len);
+    ret = bulk_write(handle->fd, data, len);
+    if (ret < 0) {
+        D(ERR, "ERROR: fd = %d, ret = %zd", handle->fd, ret);
+        return -1;
+    }
+    D(DEBUG, "[ socket_write done fd=%d ]", handle->fd);
+    return ret;
+}
+
+ssize_t socket_read(struct transport_handle *thandle, void *data, size_t len)
+{
+    ssize_t ret;
+    struct socket_handle *handle = container_of(thandle, struct socket_handle, handle);
+
+    D(DEBUG, "about to read (fd=%d, len=%d)", handle->fd, len);
+    ret = bulk_read(handle->fd, data, len);
+    if (ret < 0) {
+        D(ERR, "ERROR: fd = %d, ret = %zd", handle->fd, ret);
+        return -1;
+    }
+    D(DEBUG, "[ socket_read done fd=%d ret=%zd]", handle->fd, ret);
+    return ret;
+}
+
+static int listen_socket_init(struct socket_transport *socket_transport)
+{
+    int s = android_get_control_socket("fastbootd");
+
+    if (s < 0) {
+        D(WARN, "android_get_control_socket(fastbootd): %s\n", strerror(errno));
+        return 0;
+    }
+
+    if (listen(s, 4) < 0) {
+        D(WARN, "listen(control socket): %s\n", strerror(errno));
+        return 0;
+    }
+
+    socket_transport->fd = s;
+
+    return 1;
+}
+
+
+int transport_socket_init()
+{
+    struct socket_transport *socket_transport = malloc(sizeof(struct socket_transport));
+
+    socket_transport->transport.connect = socket_connect;
+    socket_transport->transport.close = socket_close;
+    socket_transport->transport.read = socket_read;
+    socket_transport->transport.write = socket_write;
+
+    if (!listen_socket_init(socket_transport)) {
+        D(ERR, "socket transport init failed");
+        free(socket_transport);
+        return 0;
+    }
+
+    transport_register(&socket_transport->transport);
+    return 1;
+}
+
diff --git a/fastbootd/trigger.c b/fastbootd/trigger.c
new file mode 100644
index 0000000..e63e64d
--- /dev/null
+++ b/fastbootd/trigger.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <dlfcn.h>
+
+#include <hardware/hardware.h>
+#include "debug.h"
+#include "trigger.h"
+#include "protocol.h"
+#include "vendor_trigger.h"
+
+static const int version = 1;
+
+static struct vendor_trigger_t *triggers = NULL;
+
+int load_trigger() {
+    int err;
+    hw_module_t* module;
+    hw_device_t* device;
+    int libversion;
+
+    err = hw_get_module(TRIGGER_MODULE_ID, (hw_module_t const**)&module);
+
+    if (err == 0) {
+        err = module->methods->open(module, NULL, &device);
+
+        if (err == 0) {
+            triggers = (struct vendor_trigger_t *) device;
+        } else {
+            D(WARN, "Libvendor load error");
+            return 1;
+        }
+    }
+    else {
+        D(WARN, "Libvendor not load: %s", strerror(-err));
+        return 0;
+    }
+
+    if (triggers->check_version != NULL &&
+        triggers->check_version(version, &libversion)) {
+
+        triggers = NULL;
+        D(ERR, "Library report incompability");
+        return 1;
+    }
+    D(INFO, "libvendortrigger loaded");
+
+    return 0;
+}
+
+int trigger_oem_cmd(const char *arg, const char **response) {
+    if (triggers != NULL && triggers->oem_cmd != NULL)
+        return triggers->oem_cmd(arg, response);
+    return 0;
+}
+
+int trigger_gpt_layout(struct GPT_content *table) {
+    if (triggers != NULL && triggers->gpt_layout != NULL)
+        return triggers->gpt_layout(table);
+    return 0;
+}
+
diff --git a/fastbootd/trigger.h b/fastbootd/trigger.h
new file mode 100644
index 0000000..404acb4
--- /dev/null
+++ b/fastbootd/trigger.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef __FASTBOOTD_TRIGGER_H_
+#define __FASTBOOTD_TRIGGER_H_
+
+#include "commands/partitions.h"
+#include "vendor_trigger.h"
+
+int load_trigger();
+
+/* same as in struct triggers */
+
+int trigger_gpt_layout(struct GPT_content *table);
+int trigger_oem_cmd(const char *arg, const char **response);
+
+#endif
diff --git a/fastbootd/usb_linux_client.c b/fastbootd/usb_linux_client.c
index 111cf35..7a8e46f 100644
--- a/fastbootd/usb_linux_client.c
+++ b/fastbootd/usb_linux_client.c
@@ -30,6 +30,7 @@
 
 #include "debug.h"
 #include "transport.h"
+#include "utils.h"
 
 #define   TRACE_TAG  TRACE_USB
 
@@ -50,8 +51,6 @@
 #define USB_FFS_FASTBOOT_OUT   USB_FFS_FASTBOOT_EP(ep1)
 #define USB_FFS_FASTBOOT_IN    USB_FFS_FASTBOOT_EP(ep2)
 
-#define READ_BUF_SIZE (16*1024)
-
 #define container_of(ptr, type, member) \
     ((type*)((char*)(ptr) - offsetof(type, member)))
 
@@ -212,26 +211,6 @@
     return -1;
 }
 
-static ssize_t bulk_write(int bulk_in, const char *buf, size_t length)
-{
-    size_t count = 0;
-    ssize_t ret;
-
-    do {
-        ret = TEMP_FAILURE_RETRY(write(bulk_in, buf + count, length - count));
-        if (ret < 0) {
-            D(WARN, "[ bulk_read failed fd=%d length=%d errno=%d %s ]",
-                    bulk_in, length, errno, strerror(errno));
-            return -1;
-        } else {
-            count += ret;
-        }
-    } while (count < length);
-
-    D(VERBOSE, "[ bulk_write done fd=%d ]", bulk_in);
-    return count;
-}
-
 static ssize_t usb_write(struct transport_handle *thandle, const void *data, size_t len)
 {
     ssize_t ret;
@@ -248,30 +227,6 @@
     return ret;
 }
 
-static ssize_t bulk_read(int bulk_out, char *buf, size_t length)
-{
-    ssize_t ret;
-    size_t n = 0;
-
-    while (n < length) {
-        size_t to_read = (length - n > READ_BUF_SIZE) ? READ_BUF_SIZE : length - n;
-        ret = TEMP_FAILURE_RETRY(read(bulk_out, buf + n, to_read));
-        if (ret < 0) {
-            D(WARN, "[ bulk_read failed fd=%d length=%d errno=%d %s ]",
-                    bulk_out, length, errno, strerror(errno));
-            return ret;
-        }
-        n += ret;
-        if (ret < (ssize_t)to_read) {
-            D(VERBOSE, "bulk_read short read, ret=%zd to_read=%u n=%u length=%u",
-                    ret, to_read, n, length);
-            break;
-        }
-    }
-
-    return n;
-}
-
 ssize_t usb_read(struct transport_handle *thandle, void *data, size_t len)
 {
     ssize_t ret;
diff --git a/fastbootd/utils.c b/fastbootd/utils.c
new file mode 100644
index 0000000..fe3f0f8
--- /dev/null
+++ b/fastbootd/utils.c
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <linux/fs.h>
+#include <stdlib.h>
+#include <cutils/properties.h>
+
+#include "utils.h"
+#include "debug.h"
+
+#ifndef BLKDISCARD
+#define BLKDISCARD _IO(0x12,119)
+#endif
+
+#ifndef BLKSECDISCARD
+#define BLKSECDISCARD _IO(0x12,125)
+#endif
+
+#define READ_BUF_SIZE (16*1024)
+
+int get_stream_size(FILE *stream) {
+    int size;
+    fseek(stream, 0, SEEK_END);
+    size = ftell(stream);
+    fseek(stream, 0, SEEK_SET);
+    return size;
+}
+
+uint64_t get_block_device_size(int fd)
+{
+    uint64_t size = 0;
+    int ret;
+
+    ret = ioctl(fd, BLKGETSIZE64, &size);
+
+    if (ret)
+        return 0;
+
+    return size;
+}
+
+uint64_t get_file_size(int fd)
+{
+    struct stat buf;
+    int ret;
+    int64_t computed_size;
+
+    ret = fstat(fd, &buf);
+    if (ret)
+        return 0;
+
+    if (S_ISREG(buf.st_mode))
+        computed_size = buf.st_size;
+    else if (S_ISBLK(buf.st_mode))
+        computed_size = get_block_device_size(fd);
+    else
+        computed_size = 0;
+
+    return computed_size;
+}
+
+uint64_t get_file_size64(int fd)
+{
+    struct stat64 buf;
+    int ret;
+    uint64_t computed_size;
+
+    ret = fstat64(fd, &buf);
+    if (ret)
+        return 0;
+
+    if (S_ISREG(buf.st_mode))
+        computed_size = buf.st_size;
+    else if (S_ISBLK(buf.st_mode))
+        computed_size = get_block_device_size(fd);
+    else
+        computed_size = 0;
+
+    return computed_size;
+}
+
+
+char *strip(char *str)
+{
+    int n;
+
+    n = strspn(str, " \t");
+    str += n;
+    n = strcspn(str, " \t");
+    str[n] = '\0';
+
+    return str;
+}
+
+int wipe_block_device(int fd, int64_t len)
+{
+    uint64_t range[2];
+    int ret;
+
+    range[0] = 0;
+    range[1] = len;
+    ret = ioctl(fd, BLKSECDISCARD, &range);
+    if (ret < 0) {
+        range[0] = 0;
+        range[1] = len;
+        ret = ioctl(fd, BLKDISCARD, &range);
+        if (ret < 0) {
+            D(WARN, "Discard failed\n");
+            return 1;
+        } else {
+            D(WARN, "Wipe via secure discard failed, used discard instead\n");
+            return 0;
+        }
+    }
+
+    return 0;
+}
+
+int create_temp_file() {
+    char tempname[] = "/dev/fastboot_data_XXXXXX";
+    int fd;
+
+    fd = mkstemp(tempname);
+    if (fd < 0)
+        return -1;
+
+    unlink(tempname);
+
+    return fd;
+}
+
+ssize_t bulk_write(int bulk_in, const char *buf, size_t length)
+{
+    size_t count = 0;
+    ssize_t ret;
+
+    do {
+        ret = TEMP_FAILURE_RETRY(write(bulk_in, buf + count, length - count));
+        if (ret < 0) {
+            D(WARN, "[ bulk_write failed fd=%d length=%d errno=%d %s ]",
+                    bulk_in, length, errno, strerror(errno));
+            return -1;
+        } else {
+            count += ret;
+        }
+    } while (count < length);
+
+    D(VERBOSE, "[ bulk_write done fd=%d ]", bulk_in);
+    return count;
+}
+
+ssize_t bulk_read(int bulk_out, char *buf, size_t length)
+{
+    ssize_t ret;
+    size_t n = 0;
+
+    while (n < length) {
+        size_t to_read = (length - n > READ_BUF_SIZE) ? READ_BUF_SIZE : length - n;
+        ret = TEMP_FAILURE_RETRY(read(bulk_out, buf + n, to_read));
+        if (ret < 0) {
+            D(WARN, "[ bulk_read failed fd=%d length=%d errno=%d %s ]",
+                    bulk_out, length, errno, strerror(errno));
+            return ret;
+        }
+        n += ret;
+        if (ret < (ssize_t)to_read) {
+            D(VERBOSE, "bulk_read short read, ret=%zd to_read=%u n=%u length=%u",
+                    ret, to_read, n, length);
+            break;
+        }
+    }
+
+    return n;
+}
+
+#define NAP_TIME 200  // 200 ms between polls
+static int wait_for_property(const char *name, const char *desired_value, int maxwait)
+{
+    char value[PROPERTY_VALUE_MAX] = {'\0'};
+    int maxnaps = (maxwait * 1000) / NAP_TIME;
+
+    if (maxnaps < 1) {
+        maxnaps = 1;
+    }
+
+    while (maxnaps-- > 0) {
+        usleep(NAP_TIME * 1000);
+        if (property_get(name, value, NULL)) {
+            if (desired_value == NULL || strcmp(value, desired_value) == 0) {
+                return 0;
+            }
+        }
+    }
+    return -1; /* failure */
+}
+
+int service_start(const char *service_name)
+{
+    int result = 0;
+    char property_value[PROPERTY_VALUE_MAX];
+
+    property_get(service_name, property_value, "");
+    if (strcmp("running", property_value) != 0) {
+        D(INFO, "Starting %s", service_name);
+        property_set("ctl.start", service_name);
+        if (wait_for_property(service_name, "running", 5))
+            result = -1;
+    }
+
+    return result;
+}
+
+int service_stop(const char *service_name)
+{
+    int result = 0;
+
+    D(INFO, "Stopping MDNSD");
+    property_set("ctl.stop", service_name);
+    if (wait_for_property(service_name, "stopped", 5))
+        result = -1;
+
+    return result;
+}
+
+int ssh_server_start()
+{
+    return service_start("sshd");
+}
diff --git a/fastbootd/utils.h b/fastbootd/utils.h
new file mode 100644
index 0000000..3d98699
--- /dev/null
+++ b/fastbootd/utils.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _FASTBOOT_UTLIS_H
+#define _FASTBOOT_UTILS_H
+
+#include <stdio.h>
+
+int get_stream_size(FILE *);
+
+char *strip(char *str);
+
+uint64_t get_file_size64(int fd);
+uint64_t get_file_size(int fd);
+uint64_t get_block_device_size(int fd);
+int wipe_block_device(int fd, int64_t len);
+int create_temp_file();
+ssize_t bulk_read(int bulk_out, char *buf, size_t length);
+ssize_t bulk_write(int bulk_in, const char *buf, size_t length);
+int service_start(const char *service_name);
+int service_stop(const char *service_name);
+int ssh_server_start();
+
+#define ROUND_TO_PAGE(address,pagesize) ((address + pagesize - 1) & (~(pagesize - 1)))
+
+#define ROUND_UP(number,size) (((number + size - 1) / size) * size)
+
+#endif
diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c
index 13b71ee..6d9d20c 100644
--- a/fs_mgr/fs_mgr.c
+++ b/fs_mgr/fs_mgr.c
@@ -28,11 +28,6 @@
 #include <libgen.h>
 #include <time.h>
 #include <sys/swap.h>
-/* XXX These need to be obtained from kernel headers. See b/9336527 */
-#define SWAP_FLAG_PREFER        0x8000
-#define SWAP_FLAG_PRIO_MASK     0x7fff
-#define SWAP_FLAG_PRIO_SHIFT    0
-#define SWAP_FLAG_DISCARD       0x10000
 
 #include <linux/loop.h>
 #include <private/android_filesystem_config.h>
diff --git a/include/backtrace/Backtrace.h b/include/backtrace/Backtrace.h
index b15678c..bf4efd3 100644
--- a/include/backtrace/Backtrace.h
+++ b/include/backtrace/Backtrace.h
@@ -33,7 +33,9 @@
   // If pid >= 0 and tid < 0, then the Backtrace object corresponds to a
   // different process.
   // Tracing a thread in a different process is not supported.
-  static Backtrace* Create(pid_t pid, pid_t tid);
+  // If map_info is NULL, then create the map and manage it internally.
+  // If map_info is not NULL, the map is still owned by the caller.
+  static Backtrace* Create(pid_t pid, pid_t tid, backtrace_map_info_t* map_info = NULL);
 
   virtual ~Backtrace();
 
@@ -70,7 +72,7 @@
   }
 
 protected:
-  Backtrace(BacktraceImpl* impl);
+  Backtrace(BacktraceImpl* impl, pid_t pid, backtrace_map_info_t* map_info);
 
   virtual bool VerifyReadWordArgs(uintptr_t ptr, uint32_t* out_value);
 
@@ -78,6 +80,8 @@
 
   backtrace_map_info_t* map_info_;
 
+  bool map_info_requires_delete_;
+
   backtrace_t backtrace_;
 
   friend class BacktraceImpl;
diff --git a/include/backtrace/backtrace.h b/include/backtrace/backtrace.h
index 8a45690..186d327 100644
--- a/include/backtrace/backtrace.h
+++ b/include/backtrace/backtrace.h
@@ -73,6 +73,14 @@
 bool backtrace_create_context(
     backtrace_context_t* context, pid_t pid, pid_t tid, size_t num_ignore_frames);
 
+/* The same as backtrace_create_context, except that it is assumed that
+ * the pid map has already been acquired and the caller will handle freeing
+ * the map data.
+ */
+bool backtrace_create_context_with_map(
+    backtrace_context_t* context, pid_t pid, pid_t tid, size_t num_ignore_frames,
+    backtrace_map_info_t* map_info);
+
 /* Gather the backtrace data for a pthread instead of a process. */
 bool backtrace_create_thread_context(
     backtrace_context_t* context, pid_t tid, size_t num_ignore_frames);
diff --git a/include/private/pixelflinger/ggl_context.h b/include/private/pixelflinger/ggl_context.h
index 4864d5a..d43655c 100644
--- a/include/private/pixelflinger/ggl_context.h
+++ b/include/private/pixelflinger/ggl_context.h
@@ -121,7 +121,7 @@
 template<> struct CTA<true> { };
 
 #define GGL_CONTEXT(con, c)         context_t *con = static_cast<context_t *>(c)
-#define GGL_OFFSETOF(field)         int(&(((context_t*)0)->field))
+#define GGL_OFFSETOF(field)         uintptr_t(&(((context_t*)0)->field))
 #define GGL_INIT_PROC(p, f)         p.f = ggl_ ## f;
 #define GGL_BETWEEN(x, L, H)        (uint32_t((x)-(L)) <= ((H)-(L)))
 
@@ -340,16 +340,18 @@
 
 struct surface_t {
     union {
-        GGLSurface      s;
+        GGLSurface          s;
+        // Keep the following struct field types in line with the corresponding
+        // GGLSurface fields to avoid mismatches leading to errors.
         struct {
-        uint32_t            reserved;
-        uint32_t			width;
-        uint32_t			height;
-        int32_t             stride;
-        uint8_t*			data;	
-        uint8_t				format;
-        uint8_t				dirty;
-        uint8_t				pad[2];
+            GGLsizei        reserved;
+            GGLuint         width;
+            GGLuint         height;
+            GGLint          stride;
+            GGLubyte*       data;
+            GGLubyte        format;
+            GGLubyte        dirty;
+            GGLubyte        pad[2];
         };
     };
     void                (*read) (const surface_t* s, context_t* c,
@@ -480,7 +482,7 @@
     uint32_t    width;
     uint32_t    height;
     uint32_t    stride;
-    int32_t     data;
+    uintptr_t   data;
     int32_t     dsdx;
     int32_t     dtdx;
     int32_t     spill[2];
diff --git a/include/private/pixelflinger/ggl_fixed.h b/include/private/pixelflinger/ggl_fixed.h
index 217ec04..d0493f3 100644
--- a/include/private/pixelflinger/ggl_fixed.h
+++ b/include/private/pixelflinger/ggl_fixed.h
@@ -457,6 +457,69 @@
     return u.res;
 }
 
+#elif defined(__aarch64__)
+
+// inline AArch64 implementations
+
+inline GGLfixed gglMulx(GGLfixed x, GGLfixed y, int shift) CONST;
+inline GGLfixed gglMulx(GGLfixed x, GGLfixed y, int shift)
+{
+    GGLfixed result;
+    GGLfixed round;
+
+    asm("mov    %x[round], #1                        \n"
+        "lsl    %x[round], %x[round], %x[shift]      \n"
+        "lsr    %x[round], %x[round], #1             \n"
+        "smaddl %x[result], %w[x], %w[y],%x[round]   \n"
+        "lsr    %x[result], %x[result], %x[shift]    \n"
+        : [round]"=&r"(round), [result]"=&r"(result) \
+        : [x]"r"(x), [y]"r"(y), [shift] "r"(shift)   \
+        :
+       );
+    return result;
+}
+inline GGLfixed gglMulAddx(GGLfixed x, GGLfixed y, GGLfixed a, int shift) CONST;
+inline GGLfixed gglMulAddx(GGLfixed x, GGLfixed y, GGLfixed a, int shift)
+{
+    GGLfixed result;
+    asm("smull  %x[result], %w[x], %w[y]                     \n"
+        "lsr    %x[result], %x[result], %x[shift]            \n"
+        "add    %w[result], %w[result], %w[a]                \n"
+        : [result]"=&r"(result)                               \
+        : [x]"r"(x), [y]"r"(y), [a]"r"(a), [shift] "r"(shift) \
+        :
+        );
+    return result;
+}
+
+inline GGLfixed gglMulSubx(GGLfixed x, GGLfixed y, GGLfixed a, int shift) CONST;
+inline GGLfixed gglMulSubx(GGLfixed x, GGLfixed y, GGLfixed a, int shift)
+{
+
+    GGLfixed result;
+    int rshift;
+
+    asm("smull  %x[result], %w[x], %w[y]                     \n"
+        "lsr    %x[result], %x[result], %x[shift]            \n"
+        "sub    %w[result], %w[result], %w[a]                \n"
+        : [result]"=&r"(result)                               \
+        : [x]"r"(x), [y]"r"(y), [a]"r"(a), [shift] "r"(shift) \
+        :
+        );
+    return result;
+}
+inline int64_t gglMulii(int32_t x, int32_t y) CONST;
+inline int64_t gglMulii(int32_t x, int32_t y)
+{
+    int64_t res;
+    asm("smull  %x0, %w1, %w2 \n"
+        : "=r"(res)
+        : "%r"(x), "r"(y)
+        :
+        );
+    return res;
+}
+
 #else // ----------------------------------------------------------------------
 
 inline GGLfixed gglMulx(GGLfixed a, GGLfixed b, int shift) CONST;
@@ -498,7 +561,7 @@
 inline int32_t gglClz(int32_t x) CONST;
 inline int32_t gglClz(int32_t x)
 {
-#if (defined(__arm__) && !defined(__thumb__)) || defined(__mips__)
+#if (defined(__arm__) && !defined(__thumb__)) || defined(__mips__) || defined(__aarch64__)
     return __builtin_clz(x);
 #else
     if (!x) return 32;
@@ -554,6 +617,8 @@
     // clamps to zero in one instruction, but gcc won't generate it and
     // replace it by a cmp + movlt (it's quite amazing actually).
     asm("bic %0, %1, %1, asr #31\n" : "=r"(c) : "r"(c));
+#elif defined(__aarch64__)
+    asm("bic %w0, %w1, %w1, asr #31\n" : "=r"(c) : "r"(c));
 #else
     c &= ~(c>>31);
 #endif
diff --git a/include/system/window.h b/include/system/window.h
index 649bd71..588f9c6 100644
--- a/include/system/window.h
+++ b/include/system/window.h
@@ -22,7 +22,6 @@
 #include <limits.h>
 #include <stdint.h>
 #include <string.h>
-#include <sync/sync.h>
 #include <sys/cdefs.h>
 #include <system/graphics.h>
 #include <unistd.h>
diff --git a/include/sysutils/NetlinkListener.h b/include/sysutils/NetlinkListener.h
index beb8bda..6e52c3b 100644
--- a/include/sysutils/NetlinkListener.h
+++ b/include/sysutils/NetlinkListener.h
@@ -21,7 +21,7 @@
 class NetlinkEvent;
 
 class NetlinkListener : public SocketListener {
-    char mBuffer[64 * 1024];
+    char mBuffer[64 * 1024] __attribute__((aligned(4)));
     int mFormat;
 
 public:
diff --git a/include/utils/CallStack.h b/include/utils/CallStack.h
index 61dc832..2056751 100644
--- a/include/utils/CallStack.h
+++ b/include/utils/CallStack.h
@@ -17,50 +17,72 @@
 #ifndef ANDROID_CALLSTACK_H
 #define ANDROID_CALLSTACK_H
 
-#include <stdint.h>
-#include <sys/types.h>
-
+#include <android/log.h>
 #include <utils/String8.h>
 #include <corkscrew/backtrace.h>
 
-// ---------------------------------------------------------------------------
+#include <stdint.h>
+#include <sys/types.h>
 
 namespace android {
 
-class CallStack
-{
+class Printer;
+
+// Collect/print the call stack (function, file, line) traces for a single thread.
+class CallStack {
 public:
     enum {
-        MAX_DEPTH = 31
+        // Prune the lowest-most stack frames until we have at most MAX_DEPTH.
+        MAX_DEPTH = 31,
+        // Placeholder for specifying the current thread when updating the stack.
+        CURRENT_THREAD = -1,
     };
 
+    // Create an empty call stack. No-op.
     CallStack();
+    // Create a callstack with the current thread's stack trace.
+    // Immediately dump it to logcat using the given logtag.
     CallStack(const char* logtag, int32_t ignoreDepth=1,
             int32_t maxDepth=MAX_DEPTH);
+    // Copy the existing callstack (no other side effects).
     CallStack(const CallStack& rhs);
     ~CallStack();
 
+    // Copy the existing callstack (no other side effects).
     CallStack& operator = (const CallStack& rhs);
-    
+
+    // Compare call stacks by their backtrace frame memory.
     bool operator == (const CallStack& rhs) const;
     bool operator != (const CallStack& rhs) const;
     bool operator < (const CallStack& rhs) const;
     bool operator >= (const CallStack& rhs) const;
     bool operator > (const CallStack& rhs) const;
     bool operator <= (const CallStack& rhs) const;
-    
+
+    // Get the PC address for the stack frame specified by index.
     const void* operator [] (int index) const;
-    
+
+    // Reset the stack frames (same as creating an empty call stack).
     void clear();
 
-    void update(int32_t ignoreDepth=1, int32_t maxDepth=MAX_DEPTH);
+    // Immediately collect the stack traces for the specified thread.
+    void update(int32_t ignoreDepth=1, int32_t maxDepth=MAX_DEPTH, pid_t tid=CURRENT_THREAD);
 
-    // Dump a stack trace to the log using the supplied logtag
-    void dump(const char* logtag, const char* prefix = 0) const;
+    // Dump a stack trace to the log using the supplied logtag.
+    void log(const char* logtag,
+             android_LogPriority priority = ANDROID_LOG_DEBUG,
+             const char* prefix = 0) const;
 
-    // Return a string (possibly very long) containing the complete stack trace
+    // Dump a stack trace to the specified file descriptor.
+    void dump(int fd, int indent = 0, const char* prefix = 0) const;
+
+    // Return a string (possibly very long) containing the complete stack trace.
     String8 toString(const char* prefix = 0) const;
-    
+
+    // Dump a serialized representation of the stack trace to the specified printer.
+    void print(Printer& printer) const;
+
+    // Get the count of stack frames that are in this call stack.
     size_t size() const { return mCount; }
 
 private:
@@ -70,7 +92,4 @@
 
 }; // namespace android
 
-
-// ---------------------------------------------------------------------------
-
 #endif // ANDROID_CALLSTACK_H
diff --git a/include/utils/Looper.h b/include/utils/Looper.h
index 2e0651a..15c9891 100644
--- a/include/utils/Looper.h
+++ b/include/utils/Looper.h
@@ -22,18 +22,28 @@
 #include <utils/KeyedVector.h>
 #include <utils/Timers.h>
 
-#include <android/looper.h>
-
 #include <sys/epoll.h>
 
-/*
- * Declare a concrete type for the NDK's looper forward declaration.
- */
-struct ALooper {
-};
-
 namespace android {
 
+/*
+ * NOTE: Since Looper is used to implement the NDK ALooper, the Looper
+ * enums and the signature of Looper_callbackFunc need to align with
+ * that implementation.
+ */
+
+/**
+ * For callback-based event loops, this is the prototype of the function
+ * that is called when a file descriptor event occurs.
+ * It is given the file descriptor it is associated with,
+ * a bitmask of the poll events that were triggered (typically EVENT_INPUT),
+ * and the data pointer that was originally supplied.
+ *
+ * Implementations should return 1 to continue receiving callbacks, or 0
+ * to have this file descriptor and callback unregistered from the looper.
+ */
+typedef int (*Looper_callbackFunc)(int fd, int events, void* data);
+
 /**
  * A message that can be posted to a Looper.
  */
@@ -93,7 +103,7 @@
     /**
      * Handles a poll event for the given file descriptor.
      * It is given the file descriptor it is associated with,
-     * a bitmask of the poll events that were triggered (typically ALOOPER_EVENT_INPUT),
+     * a bitmask of the poll events that were triggered (typically EVENT_INPUT),
      * and the data pointer that was originally supplied.
      *
      * Implementations should return 1 to continue receiving callbacks, or 0
@@ -102,34 +112,113 @@
     virtual int handleEvent(int fd, int events, void* data) = 0;
 };
 
-
 /**
- * Wraps a ALooper_callbackFunc function pointer.
+ * Wraps a Looper_callbackFunc function pointer.
  */
 class SimpleLooperCallback : public LooperCallback {
 protected:
     virtual ~SimpleLooperCallback();
 
 public:
-    SimpleLooperCallback(ALooper_callbackFunc callback);
+    SimpleLooperCallback(Looper_callbackFunc callback);
     virtual int handleEvent(int fd, int events, void* data);
 
 private:
-    ALooper_callbackFunc mCallback;
+    Looper_callbackFunc mCallback;
 };
 
-
 /**
  * A polling loop that supports monitoring file descriptor events, optionally
  * using callbacks.  The implementation uses epoll() internally.
  *
  * A looper can be associated with a thread although there is no requirement that it must be.
  */
-class Looper : public ALooper, public RefBase {
+class Looper : public RefBase {
 protected:
     virtual ~Looper();
 
 public:
+    enum {
+        /**
+         * Result from Looper_pollOnce() and Looper_pollAll():
+         * The poll was awoken using wake() before the timeout expired
+         * and no callbacks were executed and no other file descriptors were ready.
+         */
+        POLL_WAKE = -1,
+
+        /**
+         * Result from Looper_pollOnce() and Looper_pollAll():
+         * One or more callbacks were executed.
+         */
+        POLL_CALLBACK = -2,
+
+        /**
+         * Result from Looper_pollOnce() and Looper_pollAll():
+         * The timeout expired.
+         */
+        POLL_TIMEOUT = -3,
+
+        /**
+         * Result from Looper_pollOnce() and Looper_pollAll():
+         * An error occurred.
+         */
+        POLL_ERROR = -4,
+    };
+
+    /**
+     * Flags for file descriptor events that a looper can monitor.
+     *
+     * These flag bits can be combined to monitor multiple events at once.
+     */
+    enum {
+        /**
+         * The file descriptor is available for read operations.
+         */
+        EVENT_INPUT = 1 << 0,
+
+        /**
+         * The file descriptor is available for write operations.
+         */
+        EVENT_OUTPUT = 1 << 1,
+
+        /**
+         * The file descriptor has encountered an error condition.
+         *
+         * The looper always sends notifications about errors; it is not necessary
+         * to specify this event flag in the requested event set.
+         */
+        EVENT_ERROR = 1 << 2,
+
+        /**
+         * The file descriptor was hung up.
+         * For example, indicates that the remote end of a pipe or socket was closed.
+         *
+         * The looper always sends notifications about hangups; it is not necessary
+         * to specify this event flag in the requested event set.
+         */
+        EVENT_HANGUP = 1 << 3,
+
+        /**
+         * The file descriptor is invalid.
+         * For example, the file descriptor was closed prematurely.
+         *
+         * The looper always sends notifications about invalid file descriptors; it is not necessary
+         * to specify this event flag in the requested event set.
+         */
+        EVENT_INVALID = 1 << 4,
+    };
+
+    enum {
+        /**
+         * Option for Looper_prepare: this looper will accept calls to
+         * Looper_addFd() that do not have a callback (that is provide NULL
+         * for the callback).  In this case the caller of Looper_pollOnce()
+         * or Looper_pollAll() MUST check the return from these functions to
+         * discover when data is available on such fds and process it.
+         */
+        PREPARE_ALLOW_NON_CALLBACKS = 1<<0
+    };
+
     /**
      * Creates a looper.
      *
@@ -152,16 +241,16 @@
      * If the timeout is zero, returns immediately without blocking.
      * If the timeout is negative, waits indefinitely until an event appears.
      *
-     * Returns ALOOPER_POLL_WAKE if the poll was awoken using wake() before
+     * Returns POLL_WAKE if the poll was awoken using wake() before
      * the timeout expired and no callbacks were invoked and no other file
      * descriptors were ready.
      *
-     * Returns ALOOPER_POLL_CALLBACK if one or more callbacks were invoked.
+     * Returns POLL_CALLBACK if one or more callbacks were invoked.
      *
-     * Returns ALOOPER_POLL_TIMEOUT if there was no data before the given
+     * Returns POLL_TIMEOUT if there was no data before the given
      * timeout expired.
      *
-     * Returns ALOOPER_POLL_ERROR if an error occurred.
+     * Returns POLL_ERROR if an error occurred.
      *
      * Returns a value >= 0 containing an identifier if its file descriptor has data
      * and it has no callback function (requiring the caller here to handle it).
@@ -179,7 +268,7 @@
     /**
      * Like pollOnce(), but performs all pending callbacks until all
      * data has been consumed or a file descriptor is available with no callback.
-     * This function will never return ALOOPER_POLL_CALLBACK.
+     * This function will never return POLL_CALLBACK.
      */
     int pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outData);
     inline int pollAll(int timeoutMillis) {
@@ -200,8 +289,8 @@
      *
      * "fd" is the file descriptor to be added.
      * "ident" is an identifier for this event, which is returned from pollOnce().
-     * The identifier must be >= 0, or ALOOPER_POLL_CALLBACK if providing a non-NULL callback.
-     * "events" are the poll events to wake up on.  Typically this is ALOOPER_EVENT_INPUT.
+     * The identifier must be >= 0, or POLL_CALLBACK if providing a non-NULL callback.
+     * "events" are the poll events to wake up on.  Typically this is EVENT_INPUT.
      * "callback" is the function to call when there is an event on the file descriptor.
      * "data" is a private data pointer to supply to the callback.
      *
@@ -211,7 +300,7 @@
      * data on the file descriptor.  It should execute any events it has pending,
      * appropriately reading from the file descriptor.  The 'ident' is ignored in this case.
      *
-     * (2) If "callback" is NULL, the 'ident' will be returned by ALooper_pollOnce
+     * (2) If "callback" is NULL, the 'ident' will be returned by Looper_pollOnce
      * when its file descriptor has data available, requiring the caller to take
      * care of processing it.
      *
@@ -225,7 +314,7 @@
      * easier to avoid races when the callback is removed from a different thread.
      * See removeFd() for details.
      */
-    int addFd(int fd, int ident, int events, ALooper_callbackFunc callback, void* data);
+    int addFd(int fd, int ident, int events, Looper_callbackFunc callback, void* data);
     int addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data);
 
     /**
@@ -308,7 +397,7 @@
      * If the thread already has a looper, it is returned.  Otherwise, a new
      * one is created, associated with the thread, and returned.
      *
-     * The opts may be ALOOPER_PREPARE_ALLOW_NON_CALLBACKS or 0.
+     * The opts may be PREPARE_ALLOW_NON_CALLBACKS or 0.
      */
     static sp<Looper> prepare(int opts);
 
diff --git a/include/utils/Printer.h b/include/utils/Printer.h
new file mode 100644
index 0000000..bb66287
--- /dev/null
+++ b/include/utils/Printer.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ */
+
+#ifndef ANDROID_PRINTER_H
+#define ANDROID_PRINTER_H
+
+#include <android/log.h>
+
+namespace android {
+
+// Interface for printing to an arbitrary data stream
+class Printer {
+public:
+    // Print a new line specified by 'string'. \n is appended automatically.
+    // -- Assumes that the string has no new line in it.
+    virtual void printLine(const char* string = "") = 0;
+
+    // Print a new line specified by the format string. \n is appended automatically.
+    // -- Assumes that the resulting string has no new line in it.
+    virtual void printFormatLine(const char* format, ...) __attribute__((format (printf, 2, 3)));
+
+protected:
+    Printer();
+    virtual ~Printer();
+}; // class Printer
+
+// Print to logcat
+class LogPrinter : public Printer {
+public:
+    // Create a printer using the specified logcat and log priority
+    // - Unless ignoreBlankLines is false, print blank lines to logcat
+    // (Note that the default ALOG behavior is to ignore blank lines)
+    LogPrinter(const char* logtag,
+               android_LogPriority priority = ANDROID_LOG_DEBUG,
+               const char* prefix = 0,
+               bool ignoreBlankLines = false);
+
+    // Print the specified line to logcat. No \n at the end is necessary.
+    virtual void printLine(const char* string);
+
+private:
+    void printRaw(const char* string);
+
+    const char* mLogTag;
+    android_LogPriority mPriority;
+    const char* mPrefix;
+    bool mIgnoreBlankLines;
+}; // class LogPrinter
+
+// Print to a file descriptor
+class FdPrinter : public Printer {
+public:
+    // Create a printer using the specified file descriptor.
+    // - Each line will be prefixed with 'indent' number of blank spaces.
+    // - In addition, each line will be prefixed with the 'prefix' string.
+    FdPrinter(int fd, unsigned int indent = 0, const char* prefix = 0);
+
+    // Print the specified line to the file descriptor. \n is appended automatically.
+    virtual void printLine(const char* string);
+
+private:
+    enum {
+        MAX_FORMAT_STRING = 20,
+    };
+
+    int mFd;
+    unsigned int mIndent;
+    const char* mPrefix;
+    char mFormatString[MAX_FORMAT_STRING];
+}; // class FdPrinter
+
+class String8;
+
+// Print to a String8
+class String8Printer : public Printer {
+public:
+    // Create a printer using the specified String8 as the target.
+    // - In addition, each line will be prefixed with the 'prefix' string.
+    // - target's memory lifetime must be a superset of this String8Printer.
+    String8Printer(String8* target, const char* prefix = 0);
+
+    // Append the specified line to the String8. \n is appended automatically.
+    virtual void printLine(const char* string);
+
+private:
+    String8* mTarget;
+    const char* mPrefix;
+}; // class String8Printer
+
+// Print to an existing Printer by adding a prefix to each line
+class PrefixPrinter : public Printer {
+public:
+    // Create a printer using the specified printer as the target.
+    PrefixPrinter(Printer& printer, const char* prefix);
+
+    // Print the line (prefixed with prefix) using the printer.
+    virtual void printLine(const char* string);
+
+private:
+    Printer& mPrinter;
+    const char* mPrefix;
+};
+
+}; // namespace android
+
+#endif // ANDROID_PRINTER_H
diff --git a/include/utils/ProcessCallStack.h b/include/utils/ProcessCallStack.h
new file mode 100644
index 0000000..4a86869
--- /dev/null
+++ b/include/utils/ProcessCallStack.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ */
+
+#ifndef ANDROID_PROCESS_CALLSTACK_H
+#define ANDROID_PROCESS_CALLSTACK_H
+
+#include <utils/CallStack.h>
+#include <android/log.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+
+#include <time.h>
+#include <sys/types.h>
+
+namespace android {
+
+class Printer;
+
+// Collect/print the call stack (function, file, line) traces for all threads in a process.
+class ProcessCallStack {
+public:
+    // Create an empty call stack. No-op.
+    ProcessCallStack();
+    // Copy the existing process callstack (no other side effects).
+    ProcessCallStack(const ProcessCallStack& rhs);
+    ~ProcessCallStack();
+
+    // Immediately collect the stack traces for all threads.
+    void update(int32_t maxDepth = CallStack::MAX_DEPTH);
+
+    // Print all stack traces to the log using the supplied logtag.
+    void log(const char* logtag, android_LogPriority priority = ANDROID_LOG_DEBUG,
+             const char* prefix = 0) const;
+
+    // Dump all stack traces to the specified file descriptor.
+    void dump(int fd, int indent = 0, const char* prefix = 0) const;
+
+    // Return a string (possibly very long) containing all the stack traces.
+    String8 toString(const char* prefix = 0) const;
+
+    // Dump a serialized representation of all the stack traces to the specified printer.
+    void print(Printer& printer) const;
+
+    // Get the number of threads whose stack traces were collected.
+    size_t size() const;
+
+private:
+    void printInternal(Printer& printer, Printer& csPrinter) const;
+
+    // Reset the process's stack frames and metadata.
+    void clear();
+
+    struct ThreadInfo {
+        CallStack callStack;
+        String8 threadName;
+    };
+
+    // tid -> ThreadInfo
+    KeyedVector<pid_t, ThreadInfo> mThreadMap;
+    // Time that update() was last called
+    struct tm mTimeUpdated;
+};
+
+}; // namespace android
+
+#endif // ANDROID_PROCESS_CALLSTACK_H
diff --git a/include/ziparchive/zip_archive.h b/include/ziparchive/zip_archive.h
new file mode 100644
index 0000000..1877494
--- /dev/null
+++ b/include/ziparchive/zip_archive.h
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ */
+
+/*
+ * Read-only access to Zip archives, with minimal heap allocation.
+ */
+#ifndef LIBZIPARCHIVE_ZIPARCHIVE_H_
+#define LIBZIPARCHIVE_ZIPARCHIVE_H_
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <utils/Compat.h>
+
+__BEGIN_DECLS
+
+/* Zip compression methods we support */
+enum {
+  kCompressStored     = 0,        // no compression
+  kCompressDeflated   = 8,        // standard deflate
+};
+
+struct ZipEntryName {
+  const char* name;
+  uint16_t name_length;
+};
+
+/*
+ * Represents information about a zip entry in a zip file.
+ */
+struct ZipEntry {
+  // Compression method: One of kCompressStored or
+  // kCompressDeflated.
+  uint16_t method;
+
+  // Modification time. The zipfile format specifies
+  // that the first two little endian bytes contain the time
+  // and the last two little endian bytes contain the date.
+  uint32_t mod_time;
+
+  // 1 if this entry contains a data descriptor segment, 0
+  // otherwise.
+  uint8_t has_data_descriptor;
+
+  // Crc32 value of this ZipEntry. This information might
+  // either be stored in the local file header or in a special
+  // Data descriptor footer at the end of the file entry.
+  uint32_t crc32;
+
+  // Compressed length of this ZipEntry. Might be present
+  // either in the local file header or in the data descriptor
+  // footer.
+  uint32_t compressed_length;
+
+  // Uncompressed length of this ZipEntry. Might be present
+  // either in the local file header or in the data descriptor
+  // footer.
+  uint32_t uncompressed_length;
+
+  // The offset to the start of data for this ZipEntry.
+  off64_t offset;
+};
+
+typedef void* ZipArchiveHandle;
+
+/*
+ * Open a Zip archive, and sets handle to the value of the opaque
+ * handle for the file. This handle must be released by calling
+ * CloseArchive with this handle.
+ *
+ * Returns 0 on success, and negative values on failure.
+ */
+int32_t OpenArchive(const char* fileName, ZipArchiveHandle* handle);
+
+/*
+ * Like OpenArchive, but takes a file descriptor open for reading
+ * at the start of the file.  The descriptor must be mappable (this does
+ * not allow access to a stream).
+ *
+ * Sets handle to the value of the opaque handle for this file descriptor.
+ * This handle must be released by calling CloseArchive with this handle.
+ *
+ * This function maps and scans the central directory and builds a table
+ * of entries for future lookups.
+ *
+ * "debugFileName" will appear in error messages, but is not otherwise used.
+ *
+ * Returns 0 on success, and negative values on failure.
+ */
+int32_t OpenArchiveFd(const int fd, const char* debugFileName,
+                      ZipArchiveHandle *handle);
+
+/*
+ * Close archive, releasing resources associated with it. This will
+ * unmap the central directory of the zipfile and free all internal
+ * data structures associated with the file. It is an error to use
+ * this handle for any further operations without an intervening
+ * call to one of the OpenArchive variants.
+ */
+void CloseArchive(ZipArchiveHandle handle);
+
+/*
+ * Find an entry in the Zip archive, by name. |entryName| must be a null
+ * terminated string, and |data| must point to a writeable memory location.
+ *
+ * Returns 0 if an entry is found, and populates |data| with information
+ * about this entry. Returns negative values otherwise.
+ *
+ * It's important to note that |data->crc32|, |data->compLen| and
+ * |data->uncompLen| might be set to values from the central directory
+ * if this file entry contains a data descriptor footer. To verify crc32s
+ * and length, a call to VerifyCrcAndLengths must be made after entry data
+ * has been processed.
+ */
+int32_t FindEntry(const ZipArchiveHandle handle, const char* entryName,
+                  ZipEntry* data);
+
+/*
+ * Start iterating over all entries of a zip file. The order of iteration
+ * is not guaranteed to be the same as the order of elements
+ * in the central directory but is stable for a given zip file. |cookie|
+ * must point to a writeable memory location, and will be set to the value
+ * of an opaque cookie which can be used to make one or more calls to
+ * Next.
+ *
+ * This method also accepts an optional prefix to restrict iteration to
+ * entry names that start with |prefix|.
+ *
+ * Returns 0 on success and negative values on failure.
+ */
+int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr,
+                       const char* prefix);
+
+/*
+ * Advance to the next element in the zipfile in iteration order.
+ *
+ * Returns 0 on success, -1 if there are no more elements in this
+ * archive and lower negative values on failure.
+ */
+int32_t Next(void* cookie, ZipEntry* data, ZipEntryName *name);
+
+/*
+ * Uncompress and write an entry to an open file identified by |fd|.
+ * |entry->uncompressed_length| bytes will be written to the file at
+ * its current offset, and the file will be truncated at the end of
+ * the uncompressed data.
+ *
+ * Returns 0 on success and negative values on failure.
+ */
+int32_t ExtractEntryToFile(ZipArchiveHandle handle, ZipEntry* entry, int fd);
+
+/**
+ * Uncompress a given zip entry to the memory region at |begin| and of
+ * size |size|. This size is expected to be the same as the *declared*
+ * uncompressed length of the zip entry. It is an error if the *actual*
+ * number of uncompressed bytes differs from this number.
+ *
+ * Returns 0 on success and negative values on failure.
+ */
+int32_t ExtractToMemory(ZipArchiveHandle handle, ZipEntry* entry,
+                        uint8_t* begin, uint32_t size);
+
+int GetFileDescriptor(const ZipArchiveHandle handle);
+
+const char* ErrorCodeString(int32_t error_code);
+
+__END_DECLS
+
+#endif  // LIBZIPARCHIVE_ZIPARCHIVE_H_
diff --git a/init/init.c b/init/init.c
index 864fc6c..ab52749 100644
--- a/init/init.c
+++ b/init/init.c
@@ -221,6 +221,9 @@
             }
 
             rc = security_compute_create(mycon, fcon, string_to_security_class("process"), &scon);
+            if (rc == 0 && !strcmp(scon, mycon)) {
+                ERROR("Warning!  Service %s needs a SELinux domain defined; please fix!\n", svc->name);
+            }
             freecon(mycon);
             freecon(fcon);
             if (rc < 0) {
@@ -1129,7 +1132,7 @@
             continue;
 
         for (i = 0; i < fd_count; i++) {
-            if (ufds[i].revents == POLLIN) {
+            if (ufds[i].revents & POLLIN) {
                 if (ufds[i].fd == get_property_set_fd())
                     handle_property_set_fd();
                 else if (ufds[i].fd == get_keychord_fd())
diff --git a/init/ueventd.c b/init/ueventd.c
index a41c31e..3d01836 100644
--- a/init/ueventd.c
+++ b/init/ueventd.c
@@ -94,7 +94,7 @@
         nr = poll(&ufd, 1, -1);
         if (nr <= 0)
             continue;
-        if (ufd.revents == POLLIN)
+        if (ufd.revents & POLLIN)
                handle_device_fd();
     }
 }
diff --git a/libbacktrace/Android.mk b/libbacktrace/Android.mk
index eb55b47..a6b9c2b 100644
--- a/libbacktrace/Android.mk
+++ b/libbacktrace/Android.mk
@@ -22,8 +22,11 @@
 	libgccdemangle \
 	liblog \
 
-# To enable using libunwind on each arch, add it to the list below.
-ifeq ($(TARGET_ARCH),$(filter $(TARGET_ARCH),arm))
+# To enable using libunwind on each arch, add it to this list.
+libunwind_architectures :=
+#libunwind_architectures := arm
+
+ifeq ($(TARGET_ARCH),$(filter $(TARGET_ARCH),$(libunwind_architectures)))
 
 #----------------------------------------------------------------------------
 # The native libbacktrace library with libunwind.
diff --git a/libbacktrace/Backtrace.cpp b/libbacktrace/Backtrace.cpp
index b22d301..ccfce81 100644
--- a/libbacktrace/Backtrace.cpp
+++ b/libbacktrace/Backtrace.cpp
@@ -42,11 +42,18 @@
 //-------------------------------------------------------------------------
 // Backtrace functions.
 //-------------------------------------------------------------------------
-Backtrace::Backtrace(BacktraceImpl* impl) : impl_(impl), map_info_(NULL) {
+Backtrace::Backtrace(BacktraceImpl* impl, pid_t pid, backtrace_map_info_t* map_info)
+    : impl_(impl), map_info_(map_info), map_info_requires_delete_(false) {
   impl_->SetParent(this);
   backtrace_.num_frames = 0;
-  backtrace_.pid = -1;
+  backtrace_.pid = pid;
   backtrace_.tid = -1;
+
+  if (map_info_ == NULL) {
+    // Create the map and manage it internally.
+    map_info_ = backtrace_create_map_info_list(pid);
+    map_info_requires_delete_ = true;
+  }
 }
 
 Backtrace::~Backtrace() {
@@ -57,7 +64,7 @@
     }
   }
 
-  if (map_info_) {
+  if (map_info_ && map_info_requires_delete_) {
     backtrace_destroy_map_info_list(map_info_);
     map_info_ = NULL;
   }
@@ -151,8 +158,8 @@
 //-------------------------------------------------------------------------
 // BacktraceCurrent functions.
 //-------------------------------------------------------------------------
-BacktraceCurrent::BacktraceCurrent(BacktraceImpl* impl) : Backtrace(impl) {
-  map_info_ = backtrace_create_map_info_list(-1);
+BacktraceCurrent::BacktraceCurrent(
+    BacktraceImpl* impl, backtrace_map_info_t *map_info) : Backtrace(impl, getpid(), map_info) {
 
   backtrace_.pid = getpid();
 }
@@ -179,11 +186,9 @@
 //-------------------------------------------------------------------------
 // BacktracePtrace functions.
 //-------------------------------------------------------------------------
-BacktracePtrace::BacktracePtrace(BacktraceImpl* impl, pid_t pid, pid_t tid)
-    : Backtrace(impl) {
-  map_info_ = backtrace_create_map_info_list(tid);
-
-  backtrace_.pid = pid;
+BacktracePtrace::BacktracePtrace(
+    BacktraceImpl* impl, pid_t pid, pid_t tid, backtrace_map_info_t* map_info)
+    : Backtrace(impl, pid, map_info) {
   backtrace_.tid = tid;
 }
 
@@ -212,26 +217,27 @@
 #endif
 }
 
-Backtrace* Backtrace::Create(pid_t pid, pid_t tid) {
+Backtrace* Backtrace::Create(pid_t pid, pid_t tid, backtrace_map_info_t* map_info) {
   if (pid == BACKTRACE_CURRENT_PROCESS || pid == getpid()) {
     if (tid == BACKTRACE_NO_TID || tid == gettid()) {
-      return CreateCurrentObj();
+      return CreateCurrentObj(map_info);
     } else {
-      return CreateThreadObj(tid);
+      return CreateThreadObj(tid, map_info);
     }
   } else if (tid == BACKTRACE_NO_TID) {
-    return CreatePtraceObj(pid, pid);
+    return CreatePtraceObj(pid, pid, map_info);
   } else {
-    return CreatePtraceObj(pid, tid);
+    return CreatePtraceObj(pid, tid, map_info);
   }
 }
 
 //-------------------------------------------------------------------------
 // Common interface functions.
 //-------------------------------------------------------------------------
-bool backtrace_create_context(
-    backtrace_context_t* context, pid_t pid, pid_t tid, size_t num_ignore_frames) {
-  Backtrace* backtrace = Backtrace::Create(pid, tid);
+bool backtrace_create_context_with_map(
+    backtrace_context_t* context, pid_t pid, pid_t tid, size_t num_ignore_frames,
+    backtrace_map_info_t* map_info) {
+  Backtrace* backtrace = Backtrace::Create(pid, tid, map_info);
   if (!backtrace) {
     return false;
   }
@@ -245,6 +251,12 @@
   return true;
 }
 
+bool backtrace_create_context(
+    backtrace_context_t* context, pid_t pid, pid_t tid, size_t num_ignore_frames) {
+  return backtrace_create_context_with_map(context, pid, tid, num_ignore_frames, NULL);
+}
+
+
 void backtrace_destroy_context(backtrace_context_t* context) {
   if (context->data) {
     Backtrace* backtrace = reinterpret_cast<Backtrace*>(context->data);
diff --git a/libbacktrace/Backtrace.h b/libbacktrace/Backtrace.h
index 00f0a10..d741ef1 100644
--- a/libbacktrace/Backtrace.h
+++ b/libbacktrace/Backtrace.h
@@ -45,7 +45,7 @@
 
 class BacktraceCurrent : public Backtrace {
 public:
-  BacktraceCurrent(BacktraceImpl* impl);
+  BacktraceCurrent(BacktraceImpl* impl, backtrace_map_info_t* map_info);
   virtual ~BacktraceCurrent();
 
   bool ReadWord(uintptr_t ptr, uint32_t* out_value);
@@ -53,14 +53,14 @@
 
 class BacktracePtrace : public Backtrace {
 public:
-  BacktracePtrace(BacktraceImpl* impl, pid_t pid, pid_t tid);
+  BacktracePtrace(BacktraceImpl* impl, pid_t pid, pid_t tid, backtrace_map_info_t* map_info);
   virtual ~BacktracePtrace();
 
   bool ReadWord(uintptr_t ptr, uint32_t* out_value);
 };
 
-Backtrace* CreateCurrentObj();
-Backtrace* CreatePtraceObj(pid_t pid, pid_t tid);
-Backtrace* CreateThreadObj(pid_t tid);
+Backtrace* CreateCurrentObj(backtrace_map_info_t* map_info);
+Backtrace* CreatePtraceObj(pid_t pid, pid_t tid, backtrace_map_info_t* map_info);
+Backtrace* CreateThreadObj(pid_t tid, backtrace_map_info_t* map_info);
 
 #endif // _LIBBACKTRACE_BACKTRACE_H
diff --git a/libbacktrace/BacktraceThread.cpp b/libbacktrace/BacktraceThread.cpp
index 8e664c4..70616b0 100644
--- a/libbacktrace/BacktraceThread.cpp
+++ b/libbacktrace/BacktraceThread.cpp
@@ -114,8 +114,9 @@
 }
 
 BacktraceThread::BacktraceThread(
-    BacktraceImpl* impl, BacktraceThreadInterface* thread_intf, pid_t tid)
-    : BacktraceCurrent(impl), thread_intf_(thread_intf) {
+    BacktraceImpl* impl, BacktraceThreadInterface* thread_intf, pid_t tid,
+    backtrace_map_info_t* map_info)
+    : BacktraceCurrent(impl, map_info), thread_intf_(thread_intf) {
   backtrace_.tid = tid;
 }
 
diff --git a/libbacktrace/BacktraceThread.h b/libbacktrace/BacktraceThread.h
index 8ed1122..bcb56c9 100644
--- a/libbacktrace/BacktraceThread.h
+++ b/libbacktrace/BacktraceThread.h
@@ -71,7 +71,8 @@
   // the compiler to catch if an implementation does not properly
   // subclass both.
   BacktraceThread(
-      BacktraceImpl* impl, BacktraceThreadInterface* thread_intf, pid_t tid);
+      BacktraceImpl* impl, BacktraceThreadInterface* thread_intf, pid_t tid,
+      backtrace_map_info_t* map_info);
   virtual ~BacktraceThread();
 
   virtual bool Unwind(size_t num_ignore_frames);
diff --git a/libbacktrace/Corkscrew.cpp b/libbacktrace/Corkscrew.cpp
index 2be5930..eae13c6 100644
--- a/libbacktrace/Corkscrew.cpp
+++ b/libbacktrace/Corkscrew.cpp
@@ -204,15 +204,15 @@
 //-------------------------------------------------------------------------
 // C++ object creation functions.
 //-------------------------------------------------------------------------
-Backtrace* CreateCurrentObj() {
-  return new BacktraceCurrent(new CorkscrewCurrent());
+Backtrace* CreateCurrentObj(backtrace_map_info_t* map_info) {
+  return new BacktraceCurrent(new CorkscrewCurrent(), map_info);
 }
 
-Backtrace* CreatePtraceObj(pid_t pid, pid_t tid) {
-  return new BacktracePtrace(new CorkscrewPtrace(), pid, tid);
+Backtrace* CreatePtraceObj(pid_t pid, pid_t tid, backtrace_map_info_t* map_info) {
+  return new BacktracePtrace(new CorkscrewPtrace(), pid, tid, map_info);
 }
 
-Backtrace* CreateThreadObj(pid_t tid) {
+Backtrace* CreateThreadObj(pid_t tid, backtrace_map_info_t* map_info) {
   CorkscrewThread* thread_obj = new CorkscrewThread();
-  return new BacktraceThread(thread_obj, thread_obj, tid);
+  return new BacktraceThread(thread_obj, thread_obj, tid, map_info);
 }
diff --git a/libbacktrace/UnwindCurrent.cpp b/libbacktrace/UnwindCurrent.cpp
index d4ba68f..94a82fe 100644
--- a/libbacktrace/UnwindCurrent.cpp
+++ b/libbacktrace/UnwindCurrent.cpp
@@ -40,17 +40,11 @@
     struct sigcontext uc_mcontext;
     uint32_t uc_sigmask;
   } ucontext_t;
-#elif defined(__mips__)
-  typedef struct ucontext {
-    uint32_t sp;
-    uint32_t ra;
-    uint32_t pc;
-  } ucontext_t;
 #elif defined(__i386__)
   #include <asm/sigcontext.h>
   #include <asm/ucontext.h>
   typedef struct ucontext ucontext_t;
-#else
+#elif !defined(__mips__)
   #error Unsupported architecture.
 #endif
 
@@ -93,6 +87,7 @@
   int ret = unw_init_local(cursor, &context_);
   if (ret < 0) {
     BACK_LOGW("unw_init_local failed %d", ret);
+    delete cursor;
     return false;
   }
 
@@ -172,14 +167,8 @@
   context->regs[13] = uc->uc_mcontext.arm_sp;
   context->regs[14] = uc->uc_mcontext.arm_lr;
   context->regs[15] = uc->uc_mcontext.arm_pc;
-#elif defined(__mips__)
-  context->uc_mcontext.sp = uc->sp;
-  context->uc_mcontext.pc = uc->pc;
-  context->uc_mcontext.ra = uc->ra;
-#elif defined(__i386__)
-  context->uc_mcontext.gregs[REG_EBP] = uc->uc_mcontext.gregs[REG_EBP];
-  context->uc_mcontext.gregs[REG_ESP] = uc->uc_mcontext.gregs[REG_ESP];
-  context->uc_mcontext.gregs[REG_EIP] = uc->uc_mcontext.gregs[REG_EIP];
+#elif defined(__mips__) || defined(__i386__)
+  context->uc_mcontext = uc->uc_mcontext;
 #endif
 }
 
@@ -205,11 +194,11 @@
 //-------------------------------------------------------------------------
 // C++ object creation function.
 //-------------------------------------------------------------------------
-Backtrace* CreateCurrentObj() {
-  return new BacktraceCurrent(new UnwindCurrent());
+Backtrace* CreateCurrentObj(backtrace_map_info_t* map_info) {
+  return new BacktraceCurrent(new UnwindCurrent(), map_info);
 }
 
-Backtrace* CreateThreadObj(pid_t tid) {
+Backtrace* CreateThreadObj(pid_t tid, backtrace_map_info_t* map_info) {
   UnwindThread* thread_obj = new UnwindThread();
-  return new BacktraceThread(thread_obj, thread_obj, tid);
+  return new BacktraceThread(thread_obj, thread_obj, tid, map_info);
 }
diff --git a/libbacktrace/UnwindPtrace.cpp b/libbacktrace/UnwindPtrace.cpp
index a734a24..7a9bbdd 100644
--- a/libbacktrace/UnwindPtrace.cpp
+++ b/libbacktrace/UnwindPtrace.cpp
@@ -131,6 +131,6 @@
 //-------------------------------------------------------------------------
 // C++ object creation function.
 //-------------------------------------------------------------------------
-Backtrace* CreatePtraceObj(pid_t pid, pid_t tid) {
-  return new BacktracePtrace(new UnwindPtrace(), pid, tid);
+Backtrace* CreatePtraceObj(pid_t pid, pid_t tid, backtrace_map_info_t* map_info) {
+  return new BacktracePtrace(new UnwindPtrace(), pid, tid, map_info);
 }
diff --git a/libcorkscrew/arch-mips/backtrace-mips.c b/libcorkscrew/arch-mips/backtrace-mips.c
index 1abdd83..57cb324 100644
--- a/libcorkscrew/arch-mips/backtrace-mips.c
+++ b/libcorkscrew/arch-mips/backtrace-mips.c
@@ -23,20 +23,34 @@
 
 #include "../backtrace-arch.h"
 #include "../backtrace-helper.h"
+#include "../ptrace-arch.h"
 #include <corkscrew/ptrace.h>
+#include "dwarf.h"
 
 #include <stdlib.h>
 #include <signal.h>
 #include <stdbool.h>
 #include <limits.h>
 #include <errno.h>
+#include <string.h>
 #include <sys/ptrace.h>
-#include <sys/exec_elf.h>
 #include <cutils/log.h>
 
+#if defined(__BIONIC__)
+
+#if defined(__BIONIC_HAVE_UCONTEXT_T)
+
+// Bionic offers the Linux kernel headers.
+#include <asm/sigcontext.h>
+#include <asm/ucontext.h>
+typedef struct ucontext ucontext_t;
+
+#else /* __BIONIC_HAVE_UCONTEXT_T */
+
+/* Old versions of the Android <signal.h> didn't define ucontext_t. */
+
 /* For PTRACE_GETREGS */
 typedef struct {
-    /* FIXME: check this definition */
     uint64_t regs[32];
     uint64_t lo;
     uint64_t hi;
@@ -46,22 +60,48 @@
     uint64_t cause;
 } user_regs_struct;
 
+enum {
+    REG_ZERO = 0, REG_AT, REG_V0, REG_V1,
+    REG_A0, REG_A1, REG_A2, REG_A3,
+    REG_T0, REG_T1, REG_T2, REG_T3,
+    REG_T4, REG_T5, REG_T6, REG_T7,
+    REG_S0, REG_S1, REG_S2, REG_S3,
+    REG_S4, REG_S5, REG_S6, REG_S7,
+    REG_T8, REG_T9, REG_K0, REG_K1,
+    REG_GP, REG_SP, REG_S8, REG_RA,
+};
+
 /* Machine context at the time a signal was raised. */
 typedef struct ucontext {
-    /* FIXME: use correct definition */
-    uint32_t sp;
-    uint32_t ra;
-    uint32_t pc;
+    unsigned int sc_regmask;
+    unsigned int sc_status;
+    unsigned long long sc_pc;
+    unsigned long long sc_regs[32];
+    unsigned long long sc_fpregs[32];
+    unsigned int sc_acx;
+    unsigned int sc_fpc_csr;
+    unsigned int sc_fpc_eir;
+    unsigned int sc_used_math;
+    unsigned int sc_dsp;
+    unsigned long long sc_mdhi;
+    unsigned long long sc_mdlo;
+    unsigned long sc_hi1;
+    unsigned long sc_lo1;
+    unsigned long sc_hi2;
+    unsigned long sc_lo2;
+    unsigned long sc_hi3;
+    unsigned long sc_lo3;
 } ucontext_t;
 
+#endif /* __BIONIC_HAVE_UCONTEXT_T */
+#endif
+
 /* Unwind state. */
 typedef struct {
-    uint32_t sp;
-    uint32_t ra;
-    uint32_t pc;
+    uint32_t reg[DWARF_REGISTERS];
 } unwind_state_t;
 
-uintptr_t rewind_pc_arch(const memory_t* memory, uintptr_t pc) {
+uintptr_t rewind_pc_arch(const memory_t* memory __attribute__((unused)), uintptr_t pc) {
     if (pc == 0)
         return pc;
     if ((pc & 1) == 0)
@@ -69,108 +109,806 @@
     return pc;
 }
 
+/* Read byte through 4 byte cache. Usually we read byte by byte and updating cursor. */
+static bool try_get_byte(const memory_t* memory, uintptr_t ptr, uint8_t* out_value, uint32_t* cursor) {
+    static uintptr_t lastptr;
+    static uint32_t buf;
+
+    ptr += *cursor;
+
+    if (ptr < lastptr || lastptr + 3 < ptr) {
+        lastptr = (ptr >> 2) << 2;
+        if (!try_get_word(memory, lastptr, &buf)) {
+            return false;
+        }
+    }
+    *out_value = (uint8_t)((buf >> ((ptr & 3) * 8)) & 0xff);
+    ++*cursor;
+    return true;
+}
+
+/* Getting X bytes. 4 is maximum for now. */
+static bool try_get_xbytes(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint8_t bytes, uint32_t* cursor) {
+    uint32_t data = 0;
+    if (bytes > 4) {
+        ALOGE("can't read more than 4 bytes, trying to read %d", bytes);
+        return false;
+    }
+    for (int i = 0; i < bytes; i++) {
+        uint8_t buf;
+        if (!try_get_byte(memory, ptr, &buf, cursor)) {
+            return false;
+        }
+        data |= (uint32_t)buf << (i * 8);
+    }
+    *out_value = data;
+    return true;
+}
+
+/* Reads signed/unsigned LEB128 encoded data. From 1 to 4 bytes. */
+static bool try_get_leb128(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint32_t* cursor, bool sign_extend) {
+    uint8_t buf = 0;
+    uint32_t val = 0;
+    uint8_t c = 0;
+    do {
+        if (!try_get_byte(memory, ptr, &buf, cursor)) {
+            return false;
+        }
+        val |= ((uint32_t)buf & 0x7f) << (c * 7);
+        c++;
+    } while (buf & 0x80 && (c * 7) <= 32);
+    if (c * 7 > 32) {
+        ALOGE("%s: data exceeds expected 4 bytes maximum", __FUNCTION__);
+        return false;
+    }
+    if (sign_extend) {
+        if (buf & 0x40) {
+            val |= ((uint32_t)-1 << (c * 7));
+        }
+    }
+    *out_value = val;
+    return true;
+}
+
+/* Reads signed LEB128 encoded data. From 1 to 4 bytes. */
+static bool try_get_sleb128(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint32_t* cursor) {
+    return try_get_leb128(memory, ptr, out_value, cursor, true);
+}
+
+/* Reads unsigned LEB128 encoded data. From 1 to 4 bytes. */
+static bool try_get_uleb128(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint32_t* cursor) {
+    return try_get_leb128(memory, ptr, out_value, cursor, false);
+}
+
+/* Getting data encoded by dwarf encodings. */
+static bool read_dwarf(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint8_t encoding, uint32_t* cursor) {
+    uint32_t data = 0;
+    bool issigned = true;
+    uintptr_t addr = ptr + *cursor;
+    /* Lower 4 bits is data type/size */
+    /* TODO: add more encodings if it becomes necessary */
+    switch (encoding & 0xf) {
+    case DW_EH_PE_absptr:
+        if (!try_get_xbytes(memory, ptr, &data, 4, cursor)) {
+            return false;
+        }
+        *out_value = data;
+        return true;
+    case DW_EH_PE_udata4:
+        issigned = false;
+    case DW_EH_PE_sdata4:
+        if (!try_get_xbytes(memory, ptr, &data, 4, cursor)) {
+            return false;
+        }
+        break;
+    default:
+        ALOGE("unrecognized dwarf lower part encoding: 0x%x", encoding);
+        return false;
+    }
+    /* Higher 4 bits is modifier */
+    /* TODO: add more encodings if it becomes necessary */
+    switch (encoding & 0xf0) {
+    case 0:
+        *out_value = data;
+        break;
+    case DW_EH_PE_pcrel:
+        if (issigned) {
+            *out_value = addr + (int32_t)data;
+        } else {
+            *out_value = addr + data;
+        }
+        break;
+        /* Assuming ptr is correct base to calculate datarel */
+    case DW_EH_PE_datarel:
+        if (issigned) {
+            *out_value = ptr + (int32_t)data;
+        } else {
+            *out_value = ptr + data;
+        }
+        break;
+    default:
+        ALOGE("unrecognized dwarf higher part encoding: 0x%x", encoding);
+        return false;
+    }
+    return true;
+}
+
+/* Having PC find corresponding FDE by reading .eh_frame_hdr section data. */
+static uintptr_t find_fde(const memory_t* memory,
+                          const map_info_t* map_info_list, uintptr_t pc) {
+    if (!pc) {
+        ALOGV("find_fde: pc is zero, no eh_frame");
+        return 0;
+    }
+    const map_info_t* mi = find_map_info(map_info_list, pc);
+    if (!mi) {
+        ALOGV("find_fde: no map info for pc:0x%x", pc);
+        return 0;
+    }
+    const map_info_data_t* midata = mi->data;
+    if (!midata) {
+        ALOGV("find_fde: no eh_frame_hdr for map: start=0x%x, end=0x%x", mi->start, mi->end);
+        return 0;
+    }
+
+    eh_frame_hdr_info_t eh_hdr_info;
+    memset(&eh_hdr_info, 0, sizeof(eh_frame_hdr_info_t));
+
+    /* Getting the first word of eh_frame_hdr:
+       1st byte is version;
+       2nd byte is encoding of pointer to eh_frames;
+       3rd byte is encoding of count of FDEs in lookup table;
+       4th byte is encoding of lookup table entries.
+    */
+    uintptr_t eh_frame_hdr = midata->eh_frame_hdr;
+    uint32_t c = 0;
+    if (!try_get_byte(memory, eh_frame_hdr, &eh_hdr_info.version, &c)) return 0;
+    if (!try_get_byte(memory, eh_frame_hdr, &eh_hdr_info.eh_frame_ptr_enc, &c)) return 0;
+    if (!try_get_byte(memory, eh_frame_hdr, &eh_hdr_info.fde_count_enc, &c)) return 0;
+    if (!try_get_byte(memory, eh_frame_hdr, &eh_hdr_info.fde_table_enc, &c)) return 0;
+
+    /* TODO: 3rd byte can be DW_EH_PE_omit, that means no lookup table available and we should
+       try to parse eh_frame instead. Not sure how often it may occur, skipping now.
+    */
+    if (eh_hdr_info.version != 1) {
+        ALOGV("find_fde: eh_frame_hdr version %d is not supported", eh_hdr_info.version);
+        return 0;
+    }
+    /* Getting the data:
+       2nd word is eh_frame pointer (normally not used, because lookup table has all we need);
+       3rd word is count of FDEs in the lookup table;
+       starting from 4 word there is FDE lookup table (pairs of PC and FDE pointer) sorted by PC;
+    */
+    if (!read_dwarf(memory, eh_frame_hdr, &eh_hdr_info.eh_frame_ptr, eh_hdr_info.eh_frame_ptr_enc, &c)) return 0;
+    if (!read_dwarf(memory, eh_frame_hdr, &eh_hdr_info.fde_count, eh_hdr_info.fde_count_enc, &c)) return 0;
+    ALOGV("find_fde: found %d FDEs", eh_hdr_info.fde_count);
+
+    int32_t low = 0;
+    int32_t high = eh_hdr_info.fde_count;
+    uintptr_t start = 0;
+    uintptr_t fde = 0;
+    /* eh_frame_hdr + c points to lookup table at this point. */
+    while (low <= high) {
+        uint32_t mid = (high + low)/2;
+        uint32_t entry = c + mid * 8;
+        if (!read_dwarf(memory, eh_frame_hdr, &start, eh_hdr_info.fde_table_enc, &entry)) return 0;
+        if (pc <= start) {
+            high = mid - 1;
+        } else {
+            low = mid + 1;
+        }
+    }
+    /* Value found is at high. */
+    if (high < 0) {
+        ALOGV("find_fde: pc %x is out of FDE bounds: %x", pc, start);
+        return 0;
+    }
+    c += high * 8;
+    if (!read_dwarf(memory, eh_frame_hdr, &start, eh_hdr_info.fde_table_enc, &c)) return 0;
+    if (!read_dwarf(memory, eh_frame_hdr, &fde, eh_hdr_info.fde_table_enc, &c)) return 0;
+    ALOGV("pc 0x%x, ENTRY %d: start=0x%x, fde=0x%x", pc, high, start, fde);
+    return fde;
+}
+
+/* Execute single dwarf instruction and update dwarf state accordingly. */
+static bool execute_dwarf(const memory_t* memory, uintptr_t ptr, cie_info_t* cie_info,
+                          dwarf_state_t* dstate, uint32_t* cursor,
+                          dwarf_state_t* stack, uint8_t* stack_ptr) {
+    uint8_t inst;
+    uint8_t op = 0;
+
+    if (!try_get_byte(memory, ptr, &inst, cursor)) {
+        return false;
+    }
+    ALOGV("DW_CFA inst: 0x%x", inst);
+
+    /* For some instructions upper 2 bits is opcode and lower 6 bits is operand. See dwarf-2.0 7.23. */
+    if (inst & 0xc0) {
+        op = inst & 0x3f;
+        inst &= 0xc0;
+    }
+
+    switch ((dwarf_CFA)inst) {
+        uint32_t reg = 0;
+        uint32_t offset = 0;
+    case DW_CFA_advance_loc:
+        dstate->loc += op * cie_info->code_align;
+        ALOGV("DW_CFA_advance_loc: %d to 0x%x", op, dstate->loc);
+        break;
+    case DW_CFA_offset:
+        if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false;
+        dstate->regs[op].rule = 'o';
+        dstate->regs[op].value = offset * cie_info->data_align;
+        ALOGV("DW_CFA_offset: r%d = o(%d)", op, dstate->regs[op].value);
+        break;
+    case DW_CFA_restore:
+        dstate->regs[op].rule = stack->regs[op].rule;
+        dstate->regs[op].value = stack->regs[op].value;
+        ALOGV("DW_CFA_restore: r%d = %c(%d)", op, dstate->regs[op].rule, dstate->regs[op].value);
+        break;
+    case DW_CFA_nop:
+        break;
+    case DW_CFA_set_loc: // probably we don't have it on mips.
+        if (!try_get_xbytes(memory, ptr, &offset, 4, cursor)) return false;
+        if (offset < dstate->loc) {
+            ALOGE("DW_CFA_set_loc: attempt to move location backward");
+            return false;
+        }
+        dstate->loc = offset * cie_info->code_align;
+        ALOGV("DW_CFA_set_loc: %d to 0x%x", offset * cie_info->code_align, dstate->loc);
+        break;
+    case DW_CFA_advance_loc1:
+        if (!try_get_byte(memory, ptr, (uint8_t*)&offset, cursor)) return false;
+        dstate->loc += (uint8_t)offset * cie_info->code_align;
+        ALOGV("DW_CFA_advance_loc1: %d to 0x%x", (uint8_t)offset * cie_info->code_align, dstate->loc);
+        break;
+    case DW_CFA_advance_loc2:
+        if (!try_get_xbytes(memory, ptr, &offset, 2, cursor)) return false;
+        dstate->loc += (uint16_t)offset * cie_info->code_align;
+        ALOGV("DW_CFA_advance_loc2: %d to 0x%x", (uint16_t)offset * cie_info->code_align, dstate->loc);
+        break;
+    case DW_CFA_advance_loc4:
+        if (!try_get_xbytes(memory, ptr, &offset, 4, cursor)) return false;
+        dstate->loc += offset * cie_info->code_align;
+        ALOGV("DW_CFA_advance_loc4: %d to 0x%x", offset * cie_info->code_align, dstate->loc);
+        break;
+    case DW_CFA_offset_extended: // probably we don't have it on mips.
+        if (!try_get_uleb128(memory, ptr, &reg, cursor)) return false;
+        if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false;
+        if (reg >= DWARF_REGISTERS) {
+            ALOGE("DW_CFA_offset_extended: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS);
+            return false;
+        }
+        dstate->regs[reg].rule = 'o';
+        dstate->regs[reg].value = offset * cie_info->data_align;
+        ALOGV("DW_CFA_offset_extended: r%d = o(%d)", reg, dstate->regs[reg].value);
+        break;
+    case DW_CFA_restore_extended: // probably we don't have it on mips.
+        if (!try_get_uleb128(memory, ptr, &reg, cursor)) return false;
+        if (reg >= DWARF_REGISTERS) {
+            ALOGE("DW_CFA_restore_extended: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS);
+            return false;
+        }
+        dstate->regs[reg].rule = stack->regs[reg].rule;
+        dstate->regs[reg].value = stack->regs[reg].value;
+        ALOGV("DW_CFA_restore: r%d = %c(%d)", reg, dstate->regs[reg].rule, dstate->regs[reg].value);
+        break;
+    case DW_CFA_undefined: // probably we don't have it on mips.
+        if (!try_get_uleb128(memory, ptr, &reg, cursor)) return false;
+        if (reg >= DWARF_REGISTERS) {
+            ALOGE("DW_CFA_undefined: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS);
+            return false;
+        }
+        dstate->regs[reg].rule = 'u';
+        dstate->regs[reg].value = 0;
+        ALOGV("DW_CFA_undefined: r%d", reg);
+        break;
+    case DW_CFA_same_value: // probably we don't have it on mips.
+        if (!try_get_uleb128(memory, ptr, &reg, cursor)) return false;
+        if (reg >= DWARF_REGISTERS) {
+            ALOGE("DW_CFA_undefined: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS);
+            return false;
+        }
+        dstate->regs[reg].rule = 's';
+        dstate->regs[reg].value = 0;
+        ALOGV("DW_CFA_same_value: r%d", reg);
+        break;
+    case DW_CFA_register: // probably we don't have it on mips.
+        if (!try_get_uleb128(memory, ptr, &reg, cursor)) return false;
+        /* that's new register actually, not offset */
+        if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false;
+        if (reg >= DWARF_REGISTERS || offset >= DWARF_REGISTERS) {
+            ALOGE("DW_CFA_register: r%d or r%d exceeds supported number of registers (%d)", reg, offset, DWARF_REGISTERS);
+            return false;
+        }
+        dstate->regs[reg].rule = 'r';
+        dstate->regs[reg].value = offset;
+        ALOGV("DW_CFA_register: r%d = r(%d)", reg, dstate->regs[reg].value);
+        break;
+    case DW_CFA_remember_state:
+        if (*stack_ptr == DWARF_STATES_STACK) {
+            ALOGE("DW_CFA_remember_state: states stack overflow %d", *stack_ptr);
+            return false;
+        }
+        stack[(*stack_ptr)++] = *dstate;
+        ALOGV("DW_CFA_remember_state: stacktop moves to %d", *stack_ptr);
+        break;
+    case DW_CFA_restore_state:
+        /* We have CIE state saved at 0 position. It's not supposed to be taken
+           by DW_CFA_restore_state. */
+        if (*stack_ptr == 1) {
+            ALOGE("DW_CFA_restore_state: states stack is empty");
+            return false;
+        }
+        /* Don't touch location on restore. */
+        uintptr_t saveloc = dstate->loc;
+        *dstate = stack[--*stack_ptr];
+        dstate->loc = saveloc;
+        ALOGV("DW_CFA_restore_state: stacktop moves to %d", *stack_ptr);
+        break;
+    case DW_CFA_def_cfa:
+        if (!try_get_uleb128(memory, ptr, &reg, cursor)) return false;
+        if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false;
+        dstate->cfa_reg = reg;
+        dstate->cfa_off = offset;
+        ALOGV("DW_CFA_def_cfa: %x(r%d)", offset, reg);
+        break;
+    case DW_CFA_def_cfa_register:
+        if (!try_get_uleb128(memory, ptr, &reg, cursor)) {
+            return false;
+        }
+        dstate->cfa_reg = reg;
+        ALOGV("DW_CFA_def_cfa_register: r%d", reg);
+        break;
+    case DW_CFA_def_cfa_offset:
+        if (!try_get_uleb128(memory, ptr, &offset, cursor)) {
+            return false;
+        }
+        dstate->cfa_off = offset;
+        ALOGV("DW_CFA_def_cfa_offset: %x", offset);
+        break;
+    default:
+        ALOGE("unrecognized DW_CFA_* instruction: 0x%x", inst);
+        return false;
+    }
+    return true;
+}
+
+/* Restoring particular register value based on dwarf state. */
+static bool get_old_register_value(const memory_t* memory, uint32_t cfa,
+                                   dwarf_state_t* dstate, uint8_t reg,
+                                   unwind_state_t* state, unwind_state_t* newstate) {
+    uint32_t addr;
+    switch (dstate->regs[reg].rule) {
+    case 0:
+        /* We don't have dstate updated for this register, so assuming value kept the same.
+           Normally we should look into state and return current value as the old one
+           but we don't have all registers in state to handle this properly */
+        ALOGV("get_old_register_value: value of r%d is the same", reg);
+        // for SP if it's not updated by dwarf rule we assume it's equal to CFA
+        // for PC if it's not updated by dwarf rule we assume it's equal to RA
+        if (reg == DWARF_SP) {
+            ALOGV("get_old_register_value: adjusting sp to CFA: 0x%x", cfa);
+            newstate->reg[reg] = cfa;
+        } else if (reg == DWARF_PC) {
+            ALOGV("get_old_register_value: adjusting PC to RA: 0x%x", newstate->reg[DWARF_RA]);
+            newstate->reg[reg] = newstate->reg[DWARF_RA];
+        } else {
+            newstate->reg[reg] = state->reg[reg];
+        }
+        break;
+    case 'o':
+        addr = cfa + (int32_t)dstate->regs[reg].value;
+        if (!try_get_word(memory, addr, &newstate->reg[reg])) {
+            ALOGE("get_old_register_value: can't read from 0x%x", addr);
+            return false;
+        }
+        ALOGV("get_old_register_value: r%d at 0x%x is 0x%x", reg, addr, newstate->reg[reg]);
+        break;
+    case 'r':
+        /* We don't have all registers in state so don't even try to look at 'r' */
+        ALOGE("get_old_register_value: register lookup not implemented yet");
+        break;
+    default:
+        ALOGE("get_old_register_value: unexpected rule:%c value:%d for register %d",
+              dstate->regs[reg].rule, (int32_t)dstate->regs[reg].value, reg);
+        return false;
+    }
+    return true;
+}
+
+/* Updaing state based on dwarf state. */
+static bool update_state(const memory_t* memory, unwind_state_t* state,
+                         dwarf_state_t* dstate) {
+    unwind_state_t newstate;
+    /* We can restore more registers here if we need them. Meanwile doing minimal work here. */
+    /* Getting CFA. */
+    uintptr_t cfa = 0;
+    if (dstate->cfa_reg == DWARF_SP) {
+        cfa = state->reg[DWARF_SP] + dstate->cfa_off;
+    } else if (dstate->cfa_reg == DWARF_FP) {
+        cfa = state->reg[DWARF_FP] + dstate->cfa_off;
+    } else {
+        ALOGE("update_state: unexpected CFA register: %d", dstate->cfa_reg);
+        return false;
+    }
+    ALOGV("update_state: new CFA: 0x%x", cfa);
+
+    /* Update registers. Order is important to allow RA to propagate to PC */
+    /* Getting FP. */
+    if (!get_old_register_value(memory, cfa, dstate, DWARF_FP, state, &newstate)) return false;
+    /* Getting SP. */
+    if (!get_old_register_value(memory, cfa, dstate, DWARF_SP, state, &newstate)) return false;
+    /* Getting RA. */
+    if (!get_old_register_value(memory, cfa, dstate, DWARF_RA, state, &newstate)) return false;
+    /* Getting PC. */
+    if (!get_old_register_value(memory, cfa, dstate, DWARF_PC, state, &newstate)) return false;
+
+    ALOGV("update_state: PC: 0x%x; restore PC: 0x%x", state->reg[DWARF_PC], newstate.reg[DWARF_PC]);
+    ALOGV("update_state: RA: 0x%x; restore RA: 0x%x", state->reg[DWARF_RA], newstate.reg[DWARF_RA]);
+    ALOGV("update_state: FP: 0x%x; restore FP: 0x%x", state->reg[DWARF_FP], newstate.reg[DWARF_FP]);
+    ALOGV("update_state: SP: 0x%x; restore SP: 0x%x", state->reg[DWARF_SP], newstate.reg[DWARF_SP]);
+
+    if (newstate.reg[DWARF_PC] == 0)
+        return false;
+
+    /* End backtrace if registers do not change */
+    if ((state->reg[DWARF_PC] == newstate.reg[DWARF_PC]) &&
+        (state->reg[DWARF_RA] == newstate.reg[DWARF_RA]) &&
+        (state->reg[DWARF_FP] == newstate.reg[DWARF_FP]) &&
+        (state->reg[DWARF_SP] == newstate.reg[DWARF_SP]))
+        return false;
+
+    *state = newstate;
+    return true;
+}
+
+/* Execute CIE and FDE instructions for FDE found with find_fde. */
+static bool execute_fde(const memory_t* memory,
+                        uintptr_t fde,
+                        unwind_state_t* state) {
+    uint32_t fde_length = 0;
+    uint32_t cie_length = 0;
+    uintptr_t cie = 0;
+    uintptr_t cie_offset = 0;
+    cie_info_t cie_i;
+    cie_info_t* cie_info = &cie_i;
+    fde_info_t fde_i;
+    fde_info_t* fde_info = &fde_i;
+    dwarf_state_t dwarf_state;
+    dwarf_state_t* dstate = &dwarf_state;
+    dwarf_state_t stack[DWARF_STATES_STACK];
+    uint8_t stack_ptr = 0;
+
+    memset(dstate, 0, sizeof(dwarf_state_t));
+    memset(cie_info, 0, sizeof(cie_info_t));
+    memset(fde_info, 0, sizeof(fde_info_t));
+
+    /* Read common CIE or FDE area:
+       1st word is length;
+       2nd word is ID: 0 for CIE, CIE pointer for FDE.
+    */
+    if (!try_get_word(memory, fde, &fde_length)) {
+        return false;
+    }
+    if ((int32_t)fde_length == -1) {
+        ALOGV("execute_fde: 64-bit dwarf detected, not implemented yet");
+        return false;
+    }
+    if (!try_get_word(memory, fde + 4, &cie_offset)) {
+        return false;
+    }
+    if (cie_offset == 0) {
+        /* This is CIE. We shouldn't be here normally. */
+        cie = fde;
+        cie_length = fde_length;
+    } else {
+        /* Find CIE. */
+        /* Positive cie_offset goes backward from current field. */
+        cie = fde + 4 - cie_offset;
+        if (!try_get_word(memory, cie, &cie_length)) {
+            return false;
+        }
+        if ((int32_t)cie_length == -1) {
+            ALOGV("execute_fde: 64-bit dwarf detected, not implemented yet");
+            return false;
+        }
+        if (!try_get_word(memory, cie + 4, &cie_offset)) {
+            return false;
+        }
+        if (cie_offset != 0) {
+            ALOGV("execute_fde: can't find CIE");
+            return false;
+        }
+    }
+    ALOGV("execute_fde: FDE length: %d", fde_length);
+    ALOGV("execute_fde: CIE pointer: %x", cie);
+    ALOGV("execute_fde: CIE length: %d", cie_length);
+
+    /* Read CIE:
+       Augmentation independent:
+       1st byte is version;
+       next x bytes is /0 terminated augmentation string;
+       next x bytes is unsigned LEB128 encoded code alignment factor;
+       next x bytes is signed LEB128 encoded data alignment factor;
+       next 1 (CIE version 1) or x (CIE version 3 unsigned LEB128) bytes is return register column;
+       Augmentation dependent:
+       if 'z' next x bytes is unsigned LEB128 encoded augmentation data size;
+       if 'L' next 1 byte is LSDA encoding;
+       if 'R' next 1 byte is FDE encoding;
+       if 'S' CIE represents signal handler stack frame;
+       if 'P' next 1 byte is personality encoding folowed by personality function pointer;
+       Next x bytes is CIE program.
+    */
+
+    uint32_t c = 8;
+    if (!try_get_byte(memory, cie, &cie_info->version, &c)) {
+        return false;
+    }
+    ALOGV("execute_fde: CIE version: %d", cie_info->version);
+    uint8_t ch;
+    do {
+        if (!try_get_byte(memory, cie, &ch, &c)) {
+            return false;
+        }
+        switch (ch) {
+        case '\0': break;
+        case 'z': cie_info->aug_z = 1; break;
+        case 'L': cie_info->aug_L = 1; break;
+        case 'R': cie_info->aug_R = 1; break;
+        case 'S': cie_info->aug_S = 1; break;
+        case 'P': cie_info->aug_P = 1; break;
+        default:
+            ALOGV("execute_fde: Unrecognized CIE augmentation char: '%c'", ch);
+            return false;
+            break;
+        }
+    } while (ch);
+    if (!try_get_uleb128(memory, cie, &cie_info->code_align, &c)) {
+        return false;
+    }
+    if (!try_get_sleb128(memory, cie, &cie_info->data_align, &c)) {
+        return false;
+    }
+    if (cie_info->version >= 3) {
+        if (!try_get_uleb128(memory, cie, &cie_info->reg, &c)) {
+            return false;
+        }
+    } else {
+        if (!try_get_byte(memory, cie, (uint8_t*)&cie_info->reg, &c)) {
+            return false;
+        }
+    }
+    ALOGV("execute_fde: CIE code alignment factor: %d", cie_info->code_align);
+    ALOGV("execute_fde: CIE data alignment factor: %d", cie_info->data_align);
+    if (cie_info->aug_z) {
+        if (!try_get_uleb128(memory, cie, &cie_info->aug_z, &c)) {
+            return false;
+        }
+    }
+    if (cie_info->aug_L) {
+        if (!try_get_byte(memory, cie, &cie_info->aug_L, &c)) {
+            return false;
+        }
+    } else {
+        /* Default encoding. */
+        cie_info->aug_L = DW_EH_PE_absptr;
+    }
+    if (cie_info->aug_R) {
+        if (!try_get_byte(memory, cie, &cie_info->aug_R, &c)) {
+            return false;
+        }
+    } else {
+        /* Default encoding. */
+        cie_info->aug_R = DW_EH_PE_absptr;
+    }
+    if (cie_info->aug_P) {
+        /* Get encoding of personality routine pointer. We don't use it now. */
+        if (!try_get_byte(memory, cie, (uint8_t*)&cie_info->aug_P, &c)) {
+            return false;
+        }
+        /* Get routine pointer. */
+        if (!read_dwarf(memory, cie, &cie_info->aug_P, (uint8_t)cie_info->aug_P, &c)) {
+            return false;
+        }
+    }
+    /* CIE program. */
+    /* Length field itself (4 bytes) is not included into length. */
+    stack[0] = *dstate;
+    stack_ptr = 1;
+    while (c < cie_length + 4) {
+        if (!execute_dwarf(memory, cie, cie_info, dstate, &c, stack, &stack_ptr)) {
+            return false;
+        }
+    }
+
+    /* We went directly to CIE. Normally it shouldn't occur. */
+    if (cie == fde) return true;
+
+    /* Go back to FDE. */
+    c = 8;
+    /* Read FDE:
+       Augmentation independent:
+       next x bytes (encoded as specified in CIE) is FDE starting address;
+       next x bytes (encoded as specified in CIE) is FDE number of instructions covered;
+       Augmentation dependent:
+       if 'z' next x bytes is unsigned LEB128 encoded augmentation data size;
+       if 'L' next x bytes is LSDA pointer (encoded as specified in CIE);
+       Next x bytes is FDE program.
+    */
+    if (!read_dwarf(memory, fde, &fde_info->start, (uint8_t)cie_info->aug_R, &c)) {
+        return false;
+    }
+    dstate->loc = fde_info->start;
+    ALOGV("execute_fde: FDE start: %x", dstate->loc);
+    if (!read_dwarf(memory, fde, &fde_info->length, 0, &c)) {
+        return false;
+    }
+    ALOGV("execute_fde: FDE length: %x", fde_info->length);
+    if (cie_info->aug_z) {
+        if (!try_get_uleb128(memory, fde, &fde_info->aug_z, &c)) {
+            return false;
+        }
+    }
+    if (cie_info->aug_L && cie_info->aug_L != DW_EH_PE_omit) {
+        if (!read_dwarf(memory, fde, &fde_info->aug_L, cie_info->aug_L, &c)) {
+            return false;
+        }
+    }
+    /* FDE program. */
+    /* Length field itself (4 bytes) is not included into length. */
+    /* Save CIE state as 0 element of stack. Used by DW_CFA_restore. */
+    stack[0] = *dstate;
+    stack_ptr = 1;
+    while (c < fde_length + 4 && state->reg[DWARF_PC] >= dstate->loc) {
+        if (!execute_dwarf(memory, fde, cie_info, dstate, &c, stack, &stack_ptr)) {
+            return false;
+        }
+        ALOGV("PC: %x, LOC: %x", state->reg[DWARF_PC], dstate->loc);
+    }
+
+    return update_state(memory, state, dstate);
+}
+
+static bool heuristic_state_update(const memory_t* memory, unwind_state_t* state)
+{
+    bool found_start = false;
+    int maxcheck = 1024;
+    int32_t stack_size = 0;
+    int32_t ra_offset = 0;
+    dwarf_state_t dwarf_state;
+    dwarf_state_t* dstate = &dwarf_state;
+
+    static struct {
+        uint32_t insn;
+        uint32_t mask;
+    } frame0sig[] = {
+        {0x3c1c0000, 0xffff0000}, /* lui     gp,xxxx */
+        {0x279c0000, 0xffff0000}, /* addiu   gp,gp,xxxx */
+        {0x039fe021, 0xffffffff}, /* addu    gp,gp,ra */
+    };
+    const int nframe0sig = sizeof(frame0sig)/sizeof(frame0sig[0]);
+    int f0 = nframe0sig;
+    memset(dstate, 0, sizeof(dwarf_state_t));
+
+    /* Search code backwards looking for function prologue */
+    for (uint32_t pc = state->reg[DWARF_PC]-4; maxcheck-- > 0 && !found_start; pc -= 4) {
+        uint32_t op;
+        int32_t immediate;
+
+        if (!try_get_word(memory, pc, &op))
+            return false;
+
+        // ALOGV("@0x%08x: 0x%08x\n", pc, op);
+
+        // Check for frame 0 signature
+        if ((op & frame0sig[f0].mask) == frame0sig[f0].insn) {
+            if (f0 == 0)
+                return false;
+            f0--;
+        }
+        else {
+            f0 = nframe0sig;
+        }
+
+        switch (op & 0xffff0000) {
+        case 0x27bd0000: // addiu sp, imm
+            // looking for stack being decremented
+            immediate = (((int32_t)op) << 16) >> 16;
+            if (immediate < 0) {
+                stack_size = -immediate;
+                ALOGV("@0x%08x: found stack adjustment=%d\n", pc, stack_size);
+            }
+            break;
+        case 0x039f0000: // e021
+
+        case 0xafbf0000: // sw ra, imm(sp)
+            ra_offset = (((int32_t)op) << 16) >> 16;
+            ALOGV("@0x%08x: found ra offset=%d\n", pc, ra_offset);
+            break;
+        case 0x3c1c0000: // lui gp
+            ALOGV("@0x%08x: found function boundary", pc);
+            found_start = true;
+            break;
+        default:
+            break;
+        }
+    }
+
+    dstate->cfa_reg = DWARF_SP;
+    dstate->cfa_off = stack_size;
+
+    if (ra_offset) {
+        dstate->regs[DWARF_RA].rule = 'o';
+        dstate->regs[DWARF_RA].value = -stack_size + ra_offset;
+    }
+
+    return update_state(memory, state, dstate);
+}
+
 static ssize_t unwind_backtrace_common(const memory_t* memory,
         const map_info_t* map_info_list,
         unwind_state_t* state, backtrace_frame_t* backtrace,
         size_t ignore_depth, size_t max_depth) {
+
     size_t ignored_frames = 0;
     size_t returned_frames = 0;
 
+    ALOGV("Unwinding tid: %d", memory->tid);
+    ALOGV("PC: %x", state->reg[DWARF_PC]);
+    ALOGV("RA: %x", state->reg[DWARF_RA]);
+    ALOGV("FP: %x", state->reg[DWARF_FP]);
+    ALOGV("SP: %x", state->reg[DWARF_SP]);
+
     for (size_t index = 0; returned_frames < max_depth; index++) {
-        uintptr_t pc = index ? rewind_pc_arch(memory, state->pc) : state->pc;
-        backtrace_frame_t* frame;
-        uintptr_t addr;
-        int maxcheck = 1024;
-        int stack_size = 0, ra_offset = 0;
-        bool found_start = false;
+        uintptr_t fde = find_fde(memory, map_info_list, state->reg[DWARF_PC]);
+        backtrace_frame_t* frame = add_backtrace_entry(
+            index ? rewind_pc_arch(memory, state->reg[DWARF_PC]) : state->reg[DWARF_PC],
+            backtrace, ignore_depth, max_depth,
+            &ignored_frames, &returned_frames);
+        uint32_t stack_top = state->reg[DWARF_SP];
 
-        frame = add_backtrace_entry(pc, backtrace, ignore_depth,
-                                    max_depth, &ignored_frames, &returned_frames);
+        if (fde) {
+            /* Use FDE to update state */
+            if (!execute_fde(memory, fde, state))
+                break;
+        }
+        else {
+            /* FDE is not found, update state heuristically */
+            if (!heuristic_state_update(memory, state))
+                break;
+        }
 
-        if (frame)
-            frame->stack_top = state->sp;
-
-        ALOGV("#%d: frame=%p pc=%08x sp=%08x\n",
-              index, frame, frame->absolute_pc, frame->stack_top);
-
-        for (addr = state->pc; maxcheck-- > 0 && !found_start; addr -= 4) {
-            uint32_t op;
-            if (!try_get_word(memory, addr, &op))
-                break;
-
-            // ALOGV("@0x%08x: 0x%08x\n", addr, op);
-            switch (op & 0xffff0000) {
-            case 0x27bd0000: // addiu sp, imm
-                {
-                    // looking for stack being decremented
-                    int32_t immediate = ((((int)op) << 16) >> 16);
-                    if (immediate < 0) {
-                        stack_size = -immediate;
-                        found_start = true;
-                        ALOGV("@0x%08x: found stack adjustment=%d\n", addr, stack_size);
-                    }
-                }
-                break;
-            case 0xafbf0000: // sw ra, imm(sp)
-                ra_offset = ((((int)op) << 16) >> 16);
-                ALOGV("@0x%08x: found ra offset=%d\n", addr, ra_offset);
-                break;
-            case 0x3c1c0000: // lui gp
-                ALOGV("@0x%08x: found function boundary\n", addr);
-                found_start = true;
-                break;
-            default:
-                break;
+        if (frame) {
+            frame->stack_top = stack_top;
+            if (stack_top < state->reg[DWARF_SP]) {
+                frame->stack_size = state->reg[DWARF_SP] - stack_top;
             }
         }
-
-        if (ra_offset) {
-            uint32_t next_ra;
-            if (!try_get_word(memory, state->sp + ra_offset, &next_ra))
-                break;
-            state->ra = next_ra;
-            ALOGV("New ra: 0x%08x\n", state->ra);
-        }
-
-        if (stack_size) {
-            if (frame)
-                frame->stack_size = stack_size;
-            state->sp += stack_size;
-            ALOGV("New sp: 0x%08x\n", state->sp);
-        }
-
-        if (state->pc == state->ra && stack_size == 0)
-            break;
-
-        if (state->ra == 0)
-            break;
-
-        state->pc = state->ra;
+        ALOGV("Stack: 0x%x ... 0x%x - %d bytes", frame->stack_top, state->reg[DWARF_SP], frame->stack_size);
     }
-
-    ALOGV("returning %d frames\n", returned_frames);
-
     return returned_frames;
 }
 
-ssize_t unwind_backtrace_signal_arch(siginfo_t* siginfo, void* sigcontext,
+ssize_t unwind_backtrace_signal_arch(siginfo_t* siginfo __attribute__((unused)), void* sigcontext,
         const map_info_t* map_info_list,
         backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) {
     const ucontext_t* uc = (const ucontext_t*)sigcontext;
 
     unwind_state_t state;
-    state.sp = uc->sp;
-    state.pc = uc->pc;
-    state.ra = uc->ra;
+    state.reg[DWARF_PC] = uc->sc_pc;
+    state.reg[DWARF_RA] = uc->sc_regs[REG_RA];
+    state.reg[DWARF_FP] = uc->sc_regs[REG_S8];
+    state.reg[DWARF_SP] = uc->sc_regs[REG_SP];
 
     ALOGV("unwind_backtrace_signal_arch: "
           "ignore_depth=%d max_depth=%d pc=0x%08x sp=0x%08x ra=0x%08x\n",
-          ignore_depth, max_depth, state.pc, state.sp, state.ra);
+          ignore_depth, max_depth, state.reg[DWARF_PC], state.reg[DWARF_SP], state.reg[DWARF_RA]);
 
     memory_t memory;
     init_memory(&memory, map_info_list);
     return unwind_backtrace_common(&memory, map_info_list,
-            &state, backtrace, ignore_depth, max_depth);
+                                   &state, backtrace, ignore_depth, max_depth);
 }
 
 ssize_t unwind_backtrace_ptrace_arch(pid_t tid, const ptrace_context_t* context,
@@ -182,16 +920,17 @@
     }
 
     unwind_state_t state;
-    state.sp = regs.regs[29];
-    state.ra = regs.regs[31];
-    state.pc = regs.epc;
+    state.reg[DWARF_PC] = regs.epc;
+    state.reg[DWARF_RA] = regs.regs[REG_RA];
+    state.reg[DWARF_FP] = regs.regs[REG_S8];
+    state.reg[DWARF_SP] = regs.regs[REG_SP];
 
     ALOGV("unwind_backtrace_ptrace_arch: "
           "ignore_depth=%d max_depth=%d pc=0x%08x sp=0x%08x ra=0x%08x\n",
-          ignore_depth, max_depth, state.pc, state.sp, state.ra);
+          ignore_depth, max_depth, state.reg[DWARF_PC], state.reg[DWARF_SP], state.reg[DWARF_RA]);
 
     memory_t memory;
     init_memory_ptrace(&memory, tid);
     return unwind_backtrace_common(&memory, context->map_info_list,
-            &state, backtrace, ignore_depth, max_depth);
+                                   &state, backtrace, ignore_depth, max_depth);
 }
diff --git a/libcorkscrew/arch-mips/dwarf.h b/libcorkscrew/arch-mips/dwarf.h
new file mode 100644
index 0000000..8504ea0
--- /dev/null
+++ b/libcorkscrew/arch-mips/dwarf.h
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ */
+
+/*
+ * Dwarf2 data encoding flags.
+ */
+
+#define DW_EH_PE_absptr         0x00
+#define DW_EH_PE_omit           0xff
+#define DW_EH_PE_uleb128        0x01
+#define DW_EH_PE_udata2         0x02
+#define DW_EH_PE_udata4         0x03
+#define DW_EH_PE_udata8         0x04
+#define DW_EH_PE_sleb128        0x09
+#define DW_EH_PE_sdata2         0x0A
+#define DW_EH_PE_sdata4         0x0B
+#define DW_EH_PE_sdata8         0x0C
+#define DW_EH_PE_signed         0x08
+#define DW_EH_PE_pcrel          0x10
+#define DW_EH_PE_textrel        0x20
+#define DW_EH_PE_datarel        0x30
+#define DW_EH_PE_funcrel        0x40
+#define DW_EH_PE_aligned        0x50
+#define DW_EH_PE_indirect       0x80
+
+/*
+ * Dwarf2 call frame instructions.
+ */
+
+typedef enum {
+    DW_CFA_advance_loc = 0x40,
+    DW_CFA_offset = 0x80,
+    DW_CFA_restore = 0xc0,
+    DW_CFA_nop = 0x00,
+    DW_CFA_set_loc = 0x01,
+    DW_CFA_advance_loc1 = 0x02,
+    DW_CFA_advance_loc2 = 0x03,
+    DW_CFA_advance_loc4 = 0x04,
+    DW_CFA_offset_extended = 0x05,
+    DW_CFA_restore_extended = 0x06,
+    DW_CFA_undefined = 0x07,
+    DW_CFA_same_value = 0x08,
+    DW_CFA_register = 0x09,
+    DW_CFA_remember_state = 0x0a,
+    DW_CFA_restore_state = 0x0b,
+    DW_CFA_def_cfa = 0x0c,
+    DW_CFA_def_cfa_register = 0x0d,
+    DW_CFA_def_cfa_offset = 0x0e
+} dwarf_CFA;
+
+/*
+ * eh_frame_hdr information.
+*/
+
+typedef struct {
+      uint8_t version;
+      uint8_t eh_frame_ptr_enc;
+      uint8_t fde_count_enc;
+      uint8_t fde_table_enc;
+      uintptr_t eh_frame_ptr;
+      uint32_t fde_count;
+} eh_frame_hdr_info_t;
+
+/*
+ * CIE information.
+*/
+
+typedef struct {
+      uint8_t version;
+      uint32_t code_align;
+      uint32_t data_align;
+      uint32_t reg;
+      uint32_t aug_z;
+      uint8_t aug_L;
+      uint8_t aug_R;
+      uint8_t aug_S;
+      uint32_t aug_P;
+} cie_info_t;
+
+/*
+ * FDE information.
+*/
+
+typedef struct {
+      uint32_t start;
+      uint32_t length; // number of instructions covered by FDE
+      uint32_t aug_z;
+      uint32_t aug_L;
+} fde_info_t;
+
+/*
+ * Dwarf state.
+*/
+
+/* Stack of states: required for DW_CFA_remember_state/DW_CFA_restore_state
+   30 should be enough */
+#define DWARF_STATES_STACK 30
+
+typedef struct {
+    char rule;         // rule: o - offset(value); r - register(value)
+    uint32_t value;    // value
+} reg_rule_t;
+
+/* Dwarf preserved number of registers for mips */
+typedef enum
+  {
+    UNW_MIPS_R0,
+    UNW_MIPS_R1,
+    UNW_MIPS_R2,
+    UNW_MIPS_R3,
+    UNW_MIPS_R4,
+    UNW_MIPS_R5,
+    UNW_MIPS_R6,
+    UNW_MIPS_R7,
+    UNW_MIPS_R8,
+    UNW_MIPS_R9,
+    UNW_MIPS_R10,
+    UNW_MIPS_R11,
+    UNW_MIPS_R12,
+    UNW_MIPS_R13,
+    UNW_MIPS_R14,
+    UNW_MIPS_R15,
+    UNW_MIPS_R16,
+    UNW_MIPS_R17,
+    UNW_MIPS_R18,
+    UNW_MIPS_R19,
+    UNW_MIPS_R20,
+    UNW_MIPS_R21,
+    UNW_MIPS_R22,
+    UNW_MIPS_R23,
+    UNW_MIPS_R24,
+    UNW_MIPS_R25,
+    UNW_MIPS_R26,
+    UNW_MIPS_R27,
+    UNW_MIPS_R28,
+    UNW_MIPS_R29,
+    UNW_MIPS_R30,
+    UNW_MIPS_R31,
+
+    UNW_MIPS_PC = 34,
+
+    /* FIXME: Other registers!  */
+
+    /* For MIPS, the CFA is the value of SP (r29) at the call site in the
+       previous frame.  */
+    UNW_MIPS_CFA,
+
+    UNW_TDEP_LASTREG,
+
+    UNW_TDEP_LAST_REG = UNW_MIPS_R31,
+
+    UNW_TDEP_IP = UNW_MIPS_R31,
+    UNW_TDEP_SP = UNW_MIPS_R29,
+    UNW_TDEP_EH = UNW_MIPS_R0   /* FIXME.  */
+
+  }
+mips_regnum_t;
+
+#define DWARF_REGISTERS UNW_TDEP_LASTREG
+
+typedef struct {
+    uintptr_t loc;     // location (ip)
+    uint8_t cfa_reg;   // index of register where CFA location stored
+    intptr_t cfa_off;  // offset
+    reg_rule_t regs[DWARF_REGISTERS]; // dwarf preserved registers for mips
+} dwarf_state_t;
+
+/* DWARF registers we are caring about. */
+
+
+#define DWARF_SP      UNW_MIPS_R29
+#define DWARF_RA      UNW_MIPS_R31
+#define DWARF_PC      UNW_MIPS_PC
+#define DWARF_FP      UNW_MIPS_CFA /* FIXME is this correct? */
diff --git a/libcorkscrew/arch-mips/ptrace-mips.c b/libcorkscrew/arch-mips/ptrace-mips.c
index f0ea110..ba3b60a 100644
--- a/libcorkscrew/arch-mips/ptrace-mips.c
+++ b/libcorkscrew/arch-mips/ptrace-mips.c
@@ -19,10 +19,59 @@
 
 #include "../ptrace-arch.h"
 
+#include <stddef.h>
+#include <elf.h>
 #include <cutils/log.h>
 
-void load_ptrace_map_info_data_arch(pid_t pid, map_info_t* mi, map_info_data_t* data) {
+static void load_eh_frame_hdr(pid_t pid, map_info_t* mi, uintptr_t *eh_frame_hdr) {
+    uint32_t elf_phoff;
+    uint32_t elf_phentsize_ehsize;
+    uint32_t elf_shentsize_phnum;
+
+
+    try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_phoff), &elf_phoff);
+    ALOGV("reading 0x%08x elf_phoff:%x",  mi->start + offsetof(Elf32_Ehdr, e_phoff), elf_phoff);
+    try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_ehsize), &elf_phentsize_ehsize);
+    ALOGV("reading 0x%08x elf_phentsize_ehsize:%x", mi->start + offsetof(Elf32_Ehdr, e_ehsize), elf_phentsize_ehsize);
+    try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_phnum), &elf_shentsize_phnum);
+    ALOGV("reading 0x%08x elf_shentsize_phnum:%x", mi->start + offsetof(Elf32_Ehdr, e_phnum), elf_shentsize_phnum);
+
+
+
+    if (try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_phoff), &elf_phoff)
+            && try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_ehsize),
+                    &elf_phentsize_ehsize)
+            && try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_phnum),
+                    &elf_shentsize_phnum)) {
+        uint32_t elf_phentsize = elf_phentsize_ehsize >> 16;
+        uint32_t elf_phnum = elf_shentsize_phnum & 0xffff;
+        for (uint32_t i = 0; i < elf_phnum; i++) {
+            uintptr_t elf_phdr = mi->start + elf_phoff + i * elf_phentsize;
+            uint32_t elf_phdr_type;
+            if (!try_get_word_ptrace(pid, elf_phdr + offsetof(Elf32_Phdr, p_type), &elf_phdr_type)) {
+                break;
+            }
+            if (elf_phdr_type == PT_GNU_EH_FRAME) {
+                uint32_t elf_phdr_offset;
+                if (!try_get_word_ptrace(pid, elf_phdr + offsetof(Elf32_Phdr, p_offset),
+                        &elf_phdr_offset)) {
+                    break;
+                }
+                *eh_frame_hdr = mi->start + elf_phdr_offset;
+                ALOGV("Parsed .eh_frame_hdr info for %s: start=0x%08x", mi->name, *eh_frame_hdr);
+                return;
+            }
+        }
+    }
+    *eh_frame_hdr = 0;
 }
 
-void free_ptrace_map_info_data_arch(map_info_t* mi, map_info_data_t* data) {
+void load_ptrace_map_info_data_arch(pid_t pid, map_info_t* mi, map_info_data_t* data) {
+    ALOGV("load_ptrace_map_info_data_arch");
+    load_eh_frame_hdr(pid, mi, &data->eh_frame_hdr);
+}
+
+void free_ptrace_map_info_data_arch(map_info_t* mi __attribute__((unused)),
+                                    map_info_data_t* data __attribute__((unused))) {
+    ALOGV("free_ptrace_map_info_data_arch");
 }
diff --git a/libcorkscrew/ptrace-arch.h b/libcorkscrew/ptrace-arch.h
index 4451c29..0bcff63 100755
--- a/libcorkscrew/ptrace-arch.h
+++ b/libcorkscrew/ptrace-arch.h
@@ -33,6 +33,8 @@
 #ifdef __arm__
     uintptr_t exidx_start;
     size_t exidx_size;
+#elif __mips__
+    uintptr_t eh_frame_hdr;
 #elif __i386__
     uintptr_t eh_frame_hdr;
 #endif
diff --git a/libdiskconfig/config_mbr.c b/libdiskconfig/config_mbr.c
index b89d382..7641b29 100644
--- a/libdiskconfig/config_mbr.c
+++ b/libdiskconfig/config_mbr.c
@@ -88,7 +88,7 @@
         /* DO NOT DEREFERENCE */
         struct pc_boot_record *mbr = (void *)PC_MBR_DISK_OFFSET; 
         /* grab the offset in mbr where to write this partition entry. */
-        item->offset = (loff_t)((uint32_t)((uint8_t *)(&mbr->ptable[pnum])));
+        item->offset = (loff_t)((uintptr_t)((uint8_t *)(&mbr->ptable[pnum])));
     }
 
     pentry = (struct pc_partition *) &item->data;
diff --git a/libion/Android.mk b/libion/Android.mk
index 5121fee..e5d495b 100644
--- a/libion/Android.mk
+++ b/libion/Android.mk
@@ -5,11 +5,16 @@
 LOCAL_MODULE := libion
 LOCAL_MODULE_TAGS := optional
 LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/kernel-headers
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include $(LOCAL_PATH)/kernel-headers
 include $(BUILD_SHARED_LIBRARY)
 
 include $(CLEAR_VARS)
 LOCAL_SRC_FILES := ion.c ion_test.c
 LOCAL_MODULE := iontest
 LOCAL_MODULE_TAGS := optional tests
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/kernel-headers
 LOCAL_SHARED_LIBRARIES := liblog
 include $(BUILD_EXECUTABLE)
+
+include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/include/ion/ion.h b/libion/include/ion/ion.h
similarity index 74%
rename from include/ion/ion.h
rename to libion/include/ion/ion.h
index fbd34e2..f47793d 100644
--- a/include/ion/ion.h
+++ b/libion/include/ion/ion.h
@@ -21,6 +21,7 @@
 #ifndef __SYS_CORE_ION_H
 #define __SYS_CORE_ION_H
 
+#include <sys/types.h>
 #include <linux/ion.h>
 
 __BEGIN_DECLS
@@ -30,15 +31,15 @@
 int ion_open();
 int ion_close(int fd);
 int ion_alloc(int fd, size_t len, size_t align, unsigned int heap_mask,
-	      unsigned int flags, struct ion_handle **handle);
+              unsigned int flags, ion_user_handle_t *handle);
 int ion_alloc_fd(int fd, size_t len, size_t align, unsigned int heap_mask,
-		 unsigned int flags, int *handle_fd);
+              unsigned int flags, int *handle_fd);
 int ion_sync_fd(int fd, int handle_fd);
-int ion_free(int fd, struct ion_handle *handle);
-int ion_map(int fd, struct ion_handle *handle, size_t length, int prot,
+int ion_free(int fd, ion_user_handle_t handle);
+int ion_map(int fd, ion_user_handle_t handle, size_t length, int prot,
             int flags, off_t offset, unsigned char **ptr, int *map_fd);
-int ion_share(int fd, struct ion_handle *handle, int *share_fd);
-int ion_import(int fd, int share_fd, struct ion_handle **handle);
+int ion_share(int fd, ion_user_handle_t handle, int *share_fd);
+int ion_import(int fd, int share_fd, ion_user_handle_t *handle);
 
 __END_DECLS
 
diff --git a/libion/ion.c b/libion/ion.c
index 020c35b..80bdc2a 100644
--- a/libion/ion.c
+++ b/libion/ion.c
@@ -32,119 +32,139 @@
 
 int ion_open()
 {
-        int fd = open("/dev/ion", O_RDWR);
-        if (fd < 0)
-                ALOGE("open /dev/ion failed!\n");
-        return fd;
+    int fd = open("/dev/ion", O_RDWR);
+    if (fd < 0)
+        ALOGE("open /dev/ion failed!\n");
+    return fd;
 }
 
 int ion_close(int fd)
 {
-        return close(fd);
+    int ret = close(fd);
+    if (ret < 0)
+        return -errno;
+    return ret;
 }
 
 static int ion_ioctl(int fd, int req, void *arg)
 {
-        int ret = ioctl(fd, req, arg);
-        if (ret < 0) {
-                ALOGE("ioctl %x failed with code %d: %s\n", req,
-                       ret, strerror(errno));
-                return -errno;
-        }
-        return ret;
+    int ret = ioctl(fd, req, arg);
+    if (ret < 0) {
+        ALOGE("ioctl %x failed with code %d: %s\n", req,
+              ret, strerror(errno));
+        return -errno;
+    }
+    return ret;
 }
 
 int ion_alloc(int fd, size_t len, size_t align, unsigned int heap_mask,
-	      unsigned int flags, struct ion_handle **handle)
+              unsigned int flags, ion_user_handle_t *handle)
 {
-        int ret;
-        struct ion_allocation_data data = {
-                .len = len,
-                .align = align,
-		.heap_mask = heap_mask,
-                .flags = flags,
-        };
+    int ret;
+    struct ion_allocation_data data = {
+        .len = len,
+        .align = align,
+        .heap_id_mask = heap_mask,
+        .flags = flags,
+    };
 
-        ret = ion_ioctl(fd, ION_IOC_ALLOC, &data);
-        if (ret < 0)
-                return ret;
-        *handle = data.handle;
+    if (handle == NULL)
+        return -EINVAL;
+
+    ret = ion_ioctl(fd, ION_IOC_ALLOC, &data);
+    if (ret < 0)
         return ret;
+    *handle = data.handle;
+    return ret;
 }
 
-int ion_free(int fd, struct ion_handle *handle)
+int ion_free(int fd, ion_user_handle_t handle)
 {
-        struct ion_handle_data data = {
-                .handle = handle,
-        };
-        return ion_ioctl(fd, ION_IOC_FREE, &data);
+    struct ion_handle_data data = {
+        .handle = handle,
+    };
+    return ion_ioctl(fd, ION_IOC_FREE, &data);
 }
 
-int ion_map(int fd, struct ion_handle *handle, size_t length, int prot,
+int ion_map(int fd, ion_user_handle_t handle, size_t length, int prot,
             int flags, off_t offset, unsigned char **ptr, int *map_fd)
 {
-        struct ion_fd_data data = {
-                .handle = handle,
-        };
+    int ret;
+    struct ion_fd_data data = {
+        .handle = handle,
+    };
 
-        int ret = ion_ioctl(fd, ION_IOC_MAP, &data);
-        if (ret < 0)
-                return ret;
-        *map_fd = data.fd;
-        if (*map_fd < 0) {
-                ALOGE("map ioctl returned negative fd\n");
-                return -EINVAL;
-        }
-        *ptr = mmap(NULL, length, prot, flags, *map_fd, offset);
-        if (*ptr == MAP_FAILED) {
-                ALOGE("mmap failed: %s\n", strerror(errno));
-                return -errno;
-        }
+    if (map_fd == NULL)
+        return -EINVAL;
+    if (ptr == NULL)
+        return -EINVAL;
+
+    ret = ion_ioctl(fd, ION_IOC_MAP, &data);
+    if (ret < 0)
         return ret;
+    *map_fd = data.fd;
+    if (*map_fd < 0) {
+        ALOGE("map ioctl returned negative fd\n");
+        return -EINVAL;
+    }
+    *ptr = mmap(NULL, length, prot, flags, *map_fd, offset);
+    if (*ptr == MAP_FAILED) {
+        ALOGE("mmap failed: %s\n", strerror(errno));
+        return -errno;
+    }
+    return ret;
 }
 
-int ion_share(int fd, struct ion_handle *handle, int *share_fd)
+int ion_share(int fd, ion_user_handle_t handle, int *share_fd)
 {
-        int map_fd;
-        struct ion_fd_data data = {
-                .handle = handle,
-        };
+    int map_fd;
+    int ret;
+    struct ion_fd_data data = {
+        .handle = handle,
+    };
 
-        int ret = ion_ioctl(fd, ION_IOC_SHARE, &data);
-        if (ret < 0)
-                return ret;
-        *share_fd = data.fd;
-        if (*share_fd < 0) {
-                ALOGE("share ioctl returned negative fd\n");
-                return -EINVAL;
-        }
+    if (share_fd == NULL)
+        return -EINVAL;
+
+    ret = ion_ioctl(fd, ION_IOC_SHARE, &data);
+    if (ret < 0)
         return ret;
+    *share_fd = data.fd;
+    if (*share_fd < 0) {
+        ALOGE("share ioctl returned negative fd\n");
+        return -EINVAL;
+    }
+    return ret;
 }
 
 int ion_alloc_fd(int fd, size_t len, size_t align, unsigned int heap_mask,
-		 unsigned int flags, int *handle_fd) {
-	struct ion_handle *handle;
-	int ret;
+                 unsigned int flags, int *handle_fd) {
+    ion_user_handle_t handle;
+    int ret;
 
-	ret = ion_alloc(fd, len, align, heap_mask, flags, &handle);
-	if (ret < 0)
-		return ret;
-	ret = ion_share(fd, handle, handle_fd);
-	ion_free(fd, handle);
-	return ret;
+    ret = ion_alloc(fd, len, align, heap_mask, flags, &handle);
+    if (ret < 0)
+        return ret;
+    ret = ion_share(fd, handle, handle_fd);
+    ion_free(fd, handle);
+    return ret;
 }
 
-int ion_import(int fd, int share_fd, struct ion_handle **handle)
+int ion_import(int fd, int share_fd, ion_user_handle_t *handle)
 {
-        struct ion_fd_data data = {
-                .fd = share_fd,
-        };
+    int ret;
+    struct ion_fd_data data = {
+        .fd = share_fd,
+    };
 
-        int ret = ion_ioctl(fd, ION_IOC_IMPORT, &data);
-        if (ret < 0)
-                return ret;
-        *handle = data.handle;
+    if (handle == NULL)
+        return -EINVAL;
+
+    ret = ion_ioctl(fd, ION_IOC_IMPORT, &data);
+    if (ret < 0)
         return ret;
+    *handle = data.handle;
+    return ret;
 }
 
 int ion_sync_fd(int fd, int handle_fd)
diff --git a/libion/ion_test.c b/libion/ion_test.c
index d2c37ca..12163e9 100644
--- a/libion/ion_test.c
+++ b/libion/ion_test.c
@@ -1,3 +1,19 @@
+/*
+ *   Copyright 2013 Google, Inc
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  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.
+ */
+
 #include <errno.h>
 #include <fcntl.h>
 #include <getopt.h>
@@ -22,256 +38,250 @@
 int test = -1;
 size_t stride;
 
-int _ion_alloc_test(int *fd, struct ion_handle **handle)
+int _ion_alloc_test(int *fd, ion_user_handle_t *handle)
 {
-	int ret;
+    int ret;
 
-	*fd = ion_open();
-	if (*fd < 0)
-		return *fd;
+    *fd = ion_open();
+    if (*fd < 0)
+        return *fd;
 
-	ret = ion_alloc(*fd, len, align, heap_mask, alloc_flags, handle);
+    ret = ion_alloc(*fd, len, align, heap_mask, alloc_flags, handle);
 
-	if (ret)
-		printf("%s failed: %s\n", __func__, strerror(ret));
-	return ret;
+    if (ret)
+        printf("%s failed: %s\n", __func__, strerror(ret));
+    return ret;
 }
 
 void ion_alloc_test()
 {
-	int fd, ret;
-	struct ion_handle *handle;
+    int fd, ret;
+    ion_user_handle_t handle;
 
-	if(_ion_alloc_test(&fd, &handle))
-			return;
+    if(_ion_alloc_test(&fd, &handle))
+        return;
 
-	ret = ion_free(fd, handle);
-	if (ret) {
-		printf("%s failed: %s %p\n", __func__, strerror(ret), handle);
-		return;
-	}
-	ion_close(fd);
-	printf("ion alloc test: passed\n");
+    ret = ion_free(fd, handle);
+    if (ret) {
+        printf("%s failed: %s %p\n", __func__, strerror(ret), handle);
+        return;
+    }
+    ion_close(fd);
+    printf("ion alloc test: passed\n");
 }
 
 void ion_map_test()
 {
-	int fd, map_fd, ret;
-	size_t i;
-	struct ion_handle *handle;
-	unsigned char *ptr;
+    int fd, map_fd, ret;
+    size_t i;
+    ion_user_handle_t handle;
+    unsigned char *ptr;
 
-	if(_ion_alloc_test(&fd, &handle))
-		return;
+    if(_ion_alloc_test(&fd, &handle))
+        return;
 
-	ret = ion_map(fd, handle, len, prot, map_flags, 0, &ptr, &map_fd);
-	if (ret)
-		return;
+    ret = ion_map(fd, handle, len, prot, map_flags, 0, &ptr, &map_fd);
+    if (ret)
+        return;
 
-	for (i = 0; i < len; i++) {
-		ptr[i] = (unsigned char)i;
-	}
-	for (i = 0; i < len; i++)
-		if (ptr[i] != (unsigned char)i)
-			printf("%s failed wrote %d read %d from mapped "
-			       "memory\n", __func__, i, ptr[i]);
-	/* clean up properly */
-	ret = ion_free(fd, handle);
-	ion_close(fd);
-	munmap(ptr, len);
-	close(map_fd);
+    for (i = 0; i < len; i++) {
+        ptr[i] = (unsigned char)i;
+    }
+    for (i = 0; i < len; i++)
+        if (ptr[i] != (unsigned char)i)
+            printf("%s failed wrote %zu read %d from mapped "
+                   "memory\n", __func__, i, ptr[i]);
+    /* clean up properly */
+    ret = ion_free(fd, handle);
+    ion_close(fd);
+    munmap(ptr, len);
+    close(map_fd);
 
-	_ion_alloc_test(&fd, &handle);
-	close(fd);
+    _ion_alloc_test(&fd, &handle);
+    close(fd);
 
 #if 0
-	munmap(ptr, len);
-	close(map_fd);
-	ion_close(fd);
+    munmap(ptr, len);
+    close(map_fd);
+    ion_close(fd);
 
-	_ion_alloc_test(len, align, flags, &fd, &handle);
-	close(map_fd);
-	ret = ion_map(fd, handle, len, prot, flags, 0, &ptr, &map_fd);
-	/* don't clean up */
+    _ion_alloc_test(len, align, flags, &fd, &handle);
+    close(map_fd);
+    ret = ion_map(fd, handle, len, prot, flags, 0, &ptr, &map_fd);
+    /* don't clean up */
 #endif
 }
 
 void ion_share_test()
 
 {
-	struct ion_handle *handle;
-	int sd[2];
-	int num_fd = 1;
-	struct iovec count_vec = {
-		.iov_base = &num_fd,
-		.iov_len = sizeof num_fd,
-	};
-	char buf[CMSG_SPACE(sizeof(int))];
-	socketpair(AF_UNIX, SOCK_STREAM, 0, sd);
-	if (fork()) {
-		struct msghdr msg = {
-			.msg_control = buf,
-			.msg_controllen = sizeof buf,
-			.msg_iov = &count_vec,
-			.msg_iovlen = 1,
-		};
+    ion_user_handle_t handle;
+    int sd[2];
+    int num_fd = 1;
+    struct iovec count_vec = {
+        .iov_base = &num_fd,
+        .iov_len = sizeof num_fd,
+    };
+    char buf[CMSG_SPACE(sizeof(int))];
+    socketpair(AF_UNIX, SOCK_STREAM, 0, sd);
+    if (fork()) {
+        struct msghdr msg = {
+            .msg_control = buf,
+            .msg_controllen = sizeof buf,
+            .msg_iov = &count_vec,
+            .msg_iovlen = 1,
+        };
 
-		struct cmsghdr *cmsg;
-		int fd, share_fd, ret;
-		char *ptr;
-		/* parent */
-		if(_ion_alloc_test(&fd, &handle))
-			return;
-		ret = ion_share(fd, handle, &share_fd);
-		if (ret)
-			printf("share failed %s\n", strerror(errno));
-		ptr = mmap(NULL, len, prot, map_flags, share_fd, 0);
-		if (ptr == MAP_FAILED) {
-			return;
-		}
-		strcpy(ptr, "master");
-		cmsg = CMSG_FIRSTHDR(&msg);
-		cmsg->cmsg_level = SOL_SOCKET;
-		cmsg->cmsg_type = SCM_RIGHTS;
-		cmsg->cmsg_len = CMSG_LEN(sizeof(int));
-		*(int *)CMSG_DATA(cmsg) = share_fd;
-		/* send the fd */
-		printf("master? [%10s] should be [master]\n", ptr);
-		printf("master sending msg 1\n");
-		sendmsg(sd[0], &msg, 0);
-		if (recvmsg(sd[0], &msg, 0) < 0)
-			perror("master recv msg 2");
-		printf("master? [%10s] should be [child]\n", ptr);
+        struct cmsghdr *cmsg;
+        int fd, share_fd, ret;
+        char *ptr;
+        /* parent */
+        if(_ion_alloc_test(&fd, &handle))
+            return;
+        ret = ion_share(fd, handle, &share_fd);
+        if (ret)
+            printf("share failed %s\n", strerror(errno));
+        ptr = mmap(NULL, len, prot, map_flags, share_fd, 0);
+        if (ptr == MAP_FAILED) {
+            return;
+        }
+        strcpy(ptr, "master");
+        cmsg = CMSG_FIRSTHDR(&msg);
+        cmsg->cmsg_level = SOL_SOCKET;
+        cmsg->cmsg_type = SCM_RIGHTS;
+        cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+        *(int *)CMSG_DATA(cmsg) = share_fd;
+        /* send the fd */
+        printf("master? [%10s] should be [master]\n", ptr);
+        printf("master sending msg 1\n");
+        sendmsg(sd[0], &msg, 0);
+        if (recvmsg(sd[0], &msg, 0) < 0)
+            perror("master recv msg 2");
+        printf("master? [%10s] should be [child]\n", ptr);
 
-		/* send ping */
-		sendmsg(sd[0], &msg, 0);
-		printf("master->master? [%10s]\n", ptr);
-		if (recvmsg(sd[0], &msg, 0) < 0)
-			perror("master recv 1");
-	} else {
-		struct msghdr msg;
-		struct cmsghdr *cmsg;
-		char* ptr;
-		int fd, recv_fd;
-		char* child_buf[100];
-		/* child */
-		struct iovec count_vec = {
-			.iov_base = child_buf,
-			.iov_len = sizeof child_buf,
-		};
+        /* send ping */
+        sendmsg(sd[0], &msg, 0);
+        printf("master->master? [%10s]\n", ptr);
+        if (recvmsg(sd[0], &msg, 0) < 0)
+            perror("master recv 1");
+    } else {
+        struct msghdr msg;
+        struct cmsghdr *cmsg;
+        char* ptr;
+        int fd, recv_fd;
+        char* child_buf[100];
+        /* child */
+        struct iovec count_vec = {
+            .iov_base = child_buf,
+            .iov_len = sizeof child_buf,
+        };
 
-		struct msghdr child_msg = {
-			.msg_control = buf,
-			.msg_controllen = sizeof buf,
-			.msg_iov = &count_vec,
-			.msg_iovlen = 1,
-		};
+        struct msghdr child_msg = {
+            .msg_control = buf,
+            .msg_controllen = sizeof buf,
+            .msg_iov = &count_vec,
+            .msg_iovlen = 1,
+        };
 
-		if (recvmsg(sd[1], &child_msg, 0) < 0)
-			perror("child recv msg 1");
-		cmsg = CMSG_FIRSTHDR(&child_msg);
-		if (cmsg == NULL) {
-			printf("no cmsg rcvd in child");
-			return;
-		}
-		recv_fd = *(int*)CMSG_DATA(cmsg);
-		if (recv_fd < 0) {
-			printf("could not get recv_fd from socket");
-			return;
-		}
-		printf("child %d\n", recv_fd);
-		fd = ion_open();
-		ptr = mmap(NULL, len, prot, map_flags, recv_fd, 0);
-		if (ptr == MAP_FAILED) {
-			return;
-		}
-		printf("child? [%10s] should be [master]\n", ptr);
-		strcpy(ptr, "child");
-		printf("child sending msg 2\n");
-		sendmsg(sd[1], &child_msg, 0);
-	}
+        if (recvmsg(sd[1], &child_msg, 0) < 0)
+            perror("child recv msg 1");
+        cmsg = CMSG_FIRSTHDR(&child_msg);
+        if (cmsg == NULL) {
+            printf("no cmsg rcvd in child");
+            return;
+        }
+        recv_fd = *(int*)CMSG_DATA(cmsg);
+        if (recv_fd < 0) {
+            printf("could not get recv_fd from socket");
+            return;
+        }
+        printf("child %d\n", recv_fd);
+        fd = ion_open();
+        ptr = mmap(NULL, len, prot, map_flags, recv_fd, 0);
+        if (ptr == MAP_FAILED) {
+            return;
+        }
+        printf("child? [%10s] should be [master]\n", ptr);
+        strcpy(ptr, "child");
+        printf("child sending msg 2\n");
+        sendmsg(sd[1], &child_msg, 0);
+    }
 }
 
 int main(int argc, char* argv[]) {
-	int c;
-	enum tests {
-		ALLOC_TEST = 0, MAP_TEST, SHARE_TEST,
-	};
+    int c;
+    enum tests {
+        ALLOC_TEST = 0, MAP_TEST, SHARE_TEST,
+    };
 
-	while (1) {
-		static struct option opts[] = {
-			{"alloc", no_argument, 0, 'a'},
-			{"alloc_flags", required_argument, 0, 'f'},
-			{"heap_mask", required_argument, 0, 'h'},
-			{"map", no_argument, 0, 'm'},
-			{"share", no_argument, 0, 's'},
-			{"len", required_argument, 0, 'l'},
-			{"align", required_argument, 0, 'g'},
-			{"map_flags", required_argument, 0, 'z'},
-			{"prot", required_argument, 0, 'p'},
-		};
-		int i = 0;
-		c = getopt_long(argc, argv, "af:h:l:mr:st", opts, &i);
-		if (c == -1)
-			break;
+    while (1) {
+        static struct option opts[] = {
+            {"alloc", no_argument, 0, 'a'},
+            {"alloc_flags", required_argument, 0, 'f'},
+            {"heap_mask", required_argument, 0, 'h'},
+            {"map", no_argument, 0, 'm'},
+            {"share", no_argument, 0, 's'},
+            {"len", required_argument, 0, 'l'},
+            {"align", required_argument, 0, 'g'},
+            {"map_flags", required_argument, 0, 'z'},
+            {"prot", required_argument, 0, 'p'},
+        };
+        int i = 0;
+        c = getopt_long(argc, argv, "af:h:l:mr:st", opts, &i);
+        if (c == -1)
+            break;
 
-		switch (c) {
-		case 'l':
-			len = atol(optarg);
-			break;
-		case 'g':
-			align = atol(optarg);
-			break;
-		case 'z':
-			map_flags = 0;
-			map_flags |= strstr(optarg, "PROT_EXEC") ?
-				PROT_EXEC : 0;
-			map_flags |= strstr(optarg, "PROT_READ") ?
-				PROT_READ: 0;
-			map_flags |= strstr(optarg, "PROT_WRITE") ?
-				PROT_WRITE: 0;
-			map_flags |= strstr(optarg, "PROT_NONE") ?
-				PROT_NONE: 0;
-			break;
-		case 'p':
-			prot = 0;
-			prot |= strstr(optarg, "MAP_PRIVATE") ?
-				MAP_PRIVATE	 : 0;
-			prot |= strstr(optarg, "MAP_SHARED") ?
-				MAP_PRIVATE	 : 0;
-			break;
-		case 'f':
-			alloc_flags = atol(optarg);
-			break;
-		case 'h':
-			heap_mask = atol(optarg);
-			break;
-		case 'a':
-			test = ALLOC_TEST;
-			break;
-		case 'm':
-			test = MAP_TEST;
-			break;
-		case 's':
-			test = SHARE_TEST;
-			break;
-		}
-	}
-	printf("test %d, len %u, align %u, map_flags %d, prot %d, heap_mask %d,"
-	       " alloc_flags %d\n", test, len, align, map_flags, prot,
-	       heap_mask, alloc_flags);
-	switch (test) {
-		case ALLOC_TEST:
-			ion_alloc_test();
-			break;
-		case MAP_TEST:
-			ion_map_test();
-			break;
-		case SHARE_TEST:
-			ion_share_test();
-			break;
-		default:
-			printf("must specify a test (alloc, map, share)\n");
-	}
-	return 0;
+        switch (c) {
+        case 'l':
+            len = atol(optarg);
+            break;
+        case 'g':
+            align = atol(optarg);
+            break;
+        case 'z':
+            map_flags = 0;
+            map_flags |= strstr(optarg, "PROT_EXEC") ? PROT_EXEC : 0;
+            map_flags |= strstr(optarg, "PROT_READ") ? PROT_READ: 0;
+            map_flags |= strstr(optarg, "PROT_WRITE") ? PROT_WRITE: 0;
+            map_flags |= strstr(optarg, "PROT_NONE") ? PROT_NONE: 0;
+            break;
+        case 'p':
+            prot = 0;
+            prot |= strstr(optarg, "MAP_PRIVATE") ? MAP_PRIVATE : 0;
+            prot |= strstr(optarg, "MAP_SHARED") ? MAP_PRIVATE : 0;
+            break;
+        case 'f':
+            alloc_flags = atol(optarg);
+            break;
+        case 'h':
+            heap_mask = atol(optarg);
+            break;
+        case 'a':
+            test = ALLOC_TEST;
+            break;
+        case 'm':
+            test = MAP_TEST;
+            break;
+        case 's':
+            test = SHARE_TEST;
+            break;
+        }
+    }
+    printf("test %d, len %zu, align %zu, map_flags %d, prot %d, heap_mask %d,"
+           " alloc_flags %d\n", test, len, align, map_flags, prot,
+           heap_mask, alloc_flags);
+    switch (test) {
+        case ALLOC_TEST:
+            ion_alloc_test();
+            break;
+        case MAP_TEST:
+            ion_map_test();
+            break;
+        case SHARE_TEST:
+            ion_share_test();
+            break;
+        default:
+            printf("must specify a test (alloc, map, share)\n");
+    }
+    return 0;
 }
diff --git a/libion/kernel-headers/linux/ion.h b/libion/kernel-headers/linux/ion.h
new file mode 100644
index 0000000..5af39d0
--- /dev/null
+++ b/libion/kernel-headers/linux/ion.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ ***   This header was automatically generated from a Linux kernel header
+ ***   of the same name, to make information necessary for userspace to
+ ***   call into the kernel available to libc.  It contains only constants,
+ ***   structures, and macros generated from the original header, and thus,
+ ***   contains no copyrightable information.
+ ***
+ ***   To edit the content of this header, modify the corresponding
+ ***   source file (e.g. under external/kernel-headers/original/) then
+ ***   run bionic/libc/kernel/tools/update_all.py
+ ***
+ ***   Any manual change here will be lost the next time this script will
+ ***   be run. You've been warned!
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#ifndef _UAPI_LINUX_ION_H
+#define _UAPI_LINUX_ION_H
+#include <linux/ioctl.h>
+#include <linux/types.h>
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+typedef int ion_user_handle_t;
+enum ion_heap_type {
+ ION_HEAP_TYPE_SYSTEM,
+ ION_HEAP_TYPE_SYSTEM_CONTIG,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ ION_HEAP_TYPE_CARVEOUT,
+ ION_HEAP_TYPE_CHUNK,
+ ION_HEAP_TYPE_DMA,
+ ION_HEAP_TYPE_CUSTOM,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ ION_NUM_HEAPS = 16,
+};
+#define ION_HEAP_SYSTEM_MASK (1 << ION_HEAP_TYPE_SYSTEM)
+#define ION_HEAP_SYSTEM_CONTIG_MASK (1 << ION_HEAP_TYPE_SYSTEM_CONTIG)
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ION_HEAP_CARVEOUT_MASK (1 << ION_HEAP_TYPE_CARVEOUT)
+#define ION_HEAP_TYPE_DMA_MASK (1 << ION_HEAP_TYPE_DMA)
+#define ION_NUM_HEAP_IDS sizeof(unsigned int) * 8
+#define ION_FLAG_CACHED 1
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ION_FLAG_CACHED_NEEDS_SYNC 2
+struct ion_allocation_data {
+ size_t len;
+ size_t align;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ unsigned int heap_id_mask;
+ unsigned int flags;
+ ion_user_handle_t handle;
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct ion_fd_data {
+ ion_user_handle_t handle;
+ int fd;
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct ion_handle_data {
+ ion_user_handle_t handle;
+};
+struct ion_custom_data {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ unsigned int cmd;
+ unsigned long arg;
+};
+#define ION_IOC_MAGIC 'I'
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ION_IOC_ALLOC _IOWR(ION_IOC_MAGIC, 0,   struct ion_allocation_data)
+#define ION_IOC_FREE _IOWR(ION_IOC_MAGIC, 1, struct ion_handle_data)
+#define ION_IOC_MAP _IOWR(ION_IOC_MAGIC, 2, struct ion_fd_data)
+#define ION_IOC_SHARE _IOWR(ION_IOC_MAGIC, 4, struct ion_fd_data)
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ION_IOC_IMPORT _IOWR(ION_IOC_MAGIC, 5, struct ion_fd_data)
+#define ION_IOC_SYNC _IOWR(ION_IOC_MAGIC, 7, struct ion_fd_data)
+#define ION_IOC_CUSTOM _IOWR(ION_IOC_MAGIC, 6, struct ion_custom_data)
+#endif
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
diff --git a/libion/kernel-headers/linux/ion_test.h b/libion/kernel-headers/linux/ion_test.h
new file mode 100644
index 0000000..6f3e2a7
--- /dev/null
+++ b/libion/kernel-headers/linux/ion_test.h
@@ -0,0 +1,38 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ ***   This header was automatically generated from a Linux kernel header
+ ***   of the same name, to make information necessary for userspace to
+ ***   call into the kernel available to libc.  It contains only constants,
+ ***   structures, and macros generated from the original header, and thus,
+ ***   contains no copyrightable information.
+ ***
+ ***   To edit the content of this header, modify the corresponding
+ ***   source file (e.g. under external/kernel-headers/original/) then
+ ***   run bionic/libc/kernel/tools/update_all.py
+ ***
+ ***   Any manual change here will be lost the next time this script will
+ ***   be run. You've been warned!
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#ifndef _UAPI_LINUX_ION_TEST_H
+#define _UAPI_LINUX_ION_TEST_H
+#include <linux/ioctl.h>
+#include <linux/types.h>
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct ion_test_rw_data {
+ __u64 ptr;
+ __u64 offset;
+ __u64 size;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ int write;
+ int __padding;
+};
+#define ION_IOC_MAGIC 'I'
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ION_IOC_TEST_SET_FD   _IO(ION_IOC_MAGIC, 0xf0)
+#define ION_IOC_TEST_DMA_MAPPING   _IOW(ION_IOC_MAGIC, 0xf1, struct ion_test_rw_data)
+#define ION_IOC_TEST_KERNEL_MAPPING   _IOW(ION_IOC_MAGIC, 0xf2, struct ion_test_rw_data)
+#endif
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
diff --git a/libion/original-kernel-headers/linux/ion.h b/libion/original-kernel-headers/linux/ion.h
new file mode 100644
index 0000000..f09e7c1
--- /dev/null
+++ b/libion/original-kernel-headers/linux/ion.h
@@ -0,0 +1,196 @@
+/*
+ * drivers/staging/android/uapi/ion.h
+ *
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _UAPI_LINUX_ION_H
+#define _UAPI_LINUX_ION_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+typedef int ion_user_handle_t;
+
+/**
+ * enum ion_heap_types - list of all possible types of heaps
+ * @ION_HEAP_TYPE_SYSTEM:	 memory allocated via vmalloc
+ * @ION_HEAP_TYPE_SYSTEM_CONTIG: memory allocated via kmalloc
+ * @ION_HEAP_TYPE_CARVEOUT:	 memory allocated from a prereserved
+ * 				 carveout heap, allocations are physically
+ * 				 contiguous
+ * @ION_HEAP_TYPE_DMA:		 memory allocated via DMA API
+ * @ION_NUM_HEAPS:		 helper for iterating over heaps, a bit mask
+ * 				 is used to identify the heaps, so only 32
+ * 				 total heap types are supported
+ */
+enum ion_heap_type {
+	ION_HEAP_TYPE_SYSTEM,
+	ION_HEAP_TYPE_SYSTEM_CONTIG,
+	ION_HEAP_TYPE_CARVEOUT,
+	ION_HEAP_TYPE_CHUNK,
+	ION_HEAP_TYPE_DMA,
+	ION_HEAP_TYPE_CUSTOM, /* must be last so device specific heaps always
+				 are at the end of this enum */
+	ION_NUM_HEAPS = 16,
+};
+
+#define ION_HEAP_SYSTEM_MASK		(1 << ION_HEAP_TYPE_SYSTEM)
+#define ION_HEAP_SYSTEM_CONTIG_MASK	(1 << ION_HEAP_TYPE_SYSTEM_CONTIG)
+#define ION_HEAP_CARVEOUT_MASK		(1 << ION_HEAP_TYPE_CARVEOUT)
+#define ION_HEAP_TYPE_DMA_MASK		(1 << ION_HEAP_TYPE_DMA)
+
+#define ION_NUM_HEAP_IDS		sizeof(unsigned int) * 8
+
+/**
+ * allocation flags - the lower 16 bits are used by core ion, the upper 16
+ * bits are reserved for use by the heaps themselves.
+ */
+#define ION_FLAG_CACHED 1		/* mappings of this buffer should be
+					   cached, ion will do cache
+					   maintenance when the buffer is
+					   mapped for dma */
+#define ION_FLAG_CACHED_NEEDS_SYNC 2	/* mappings of this buffer will created
+					   at mmap time, if this is set
+					   caches must be managed manually */
+
+/**
+ * DOC: Ion Userspace API
+ *
+ * create a client by opening /dev/ion
+ * most operations handled via following ioctls
+ *
+ */
+
+/**
+ * struct ion_allocation_data - metadata passed from userspace for allocations
+ * @len:		size of the allocation
+ * @align:		required alignment of the allocation
+ * @heap_id_mask:	mask of heap ids to allocate from
+ * @flags:		flags passed to heap
+ * @handle:		pointer that will be populated with a cookie to use to 
+ *			refer to this allocation
+ *
+ * Provided by userspace as an argument to the ioctl
+ */
+struct ion_allocation_data {
+	size_t len;
+	size_t align;
+	unsigned int heap_id_mask;
+	unsigned int flags;
+	ion_user_handle_t handle;
+};
+
+/**
+ * struct ion_fd_data - metadata passed to/from userspace for a handle/fd pair
+ * @handle:	a handle
+ * @fd:		a file descriptor representing that handle
+ *
+ * For ION_IOC_SHARE or ION_IOC_MAP userspace populates the handle field with
+ * the handle returned from ion alloc, and the kernel returns the file
+ * descriptor to share or map in the fd field.  For ION_IOC_IMPORT, userspace
+ * provides the file descriptor and the kernel returns the handle.
+ */
+struct ion_fd_data {
+	ion_user_handle_t handle;
+	int fd;
+};
+
+/**
+ * struct ion_handle_data - a handle passed to/from the kernel
+ * @handle:	a handle
+ */
+struct ion_handle_data {
+	ion_user_handle_t handle;
+};
+
+/**
+ * struct ion_custom_data - metadata passed to/from userspace for a custom ioctl
+ * @cmd:	the custom ioctl function to call
+ * @arg:	additional data to pass to the custom ioctl, typically a user
+ *		pointer to a predefined structure
+ *
+ * This works just like the regular cmd and arg fields of an ioctl.
+ */
+struct ion_custom_data {
+	unsigned int cmd;
+	unsigned long arg;
+};
+
+#define ION_IOC_MAGIC		'I'
+
+/**
+ * DOC: ION_IOC_ALLOC - allocate memory
+ *
+ * Takes an ion_allocation_data struct and returns it with the handle field
+ * populated with the opaque handle for the allocation.
+ */
+#define ION_IOC_ALLOC		_IOWR(ION_IOC_MAGIC, 0, \
+				      struct ion_allocation_data)
+
+/**
+ * DOC: ION_IOC_FREE - free memory
+ *
+ * Takes an ion_handle_data struct and frees the handle.
+ */
+#define ION_IOC_FREE		_IOWR(ION_IOC_MAGIC, 1, struct ion_handle_data)
+
+/**
+ * DOC: ION_IOC_MAP - get a file descriptor to mmap
+ *
+ * Takes an ion_fd_data struct with the handle field populated with a valid
+ * opaque handle.  Returns the struct with the fd field set to a file
+ * descriptor open in the current address space.  This file descriptor
+ * can then be used as an argument to mmap.
+ */
+#define ION_IOC_MAP		_IOWR(ION_IOC_MAGIC, 2, struct ion_fd_data)
+
+/**
+ * DOC: ION_IOC_SHARE - creates a file descriptor to use to share an allocation
+ *
+ * Takes an ion_fd_data struct with the handle field populated with a valid
+ * opaque handle.  Returns the struct with the fd field set to a file
+ * descriptor open in the current address space.  This file descriptor
+ * can then be passed to another process.  The corresponding opaque handle can
+ * be retrieved via ION_IOC_IMPORT.
+ */
+#define ION_IOC_SHARE		_IOWR(ION_IOC_MAGIC, 4, struct ion_fd_data)
+
+/**
+ * DOC: ION_IOC_IMPORT - imports a shared file descriptor
+ *
+ * Takes an ion_fd_data struct with the fd field populated with a valid file
+ * descriptor obtained from ION_IOC_SHARE and returns the struct with the handle
+ * filed set to the corresponding opaque handle.
+ */
+#define ION_IOC_IMPORT		_IOWR(ION_IOC_MAGIC, 5, struct ion_fd_data)
+
+/**
+ * DOC: ION_IOC_SYNC - syncs a shared file descriptors to memory
+ *
+ * Deprecated in favor of using the dma_buf api's correctly (syncing
+ * will happend automatically when the buffer is mapped to a device).
+ * If necessary should be used after touching a cached buffer from the cpu,
+ * this will make the buffer in memory coherent.
+ */
+#define ION_IOC_SYNC		_IOWR(ION_IOC_MAGIC, 7, struct ion_fd_data)
+
+/**
+ * DOC: ION_IOC_CUSTOM - call architecture specific ion ioctl
+ *
+ * Takes the argument of the architecture specific ioctl to call and
+ * passes appropriate userdata for that ioctl
+ */
+#define ION_IOC_CUSTOM		_IOWR(ION_IOC_MAGIC, 6, struct ion_custom_data)
+
+#endif /* _UAPI_LINUX_ION_H */
diff --git a/libion/original-kernel-headers/linux/ion_test.h b/libion/original-kernel-headers/linux/ion_test.h
new file mode 100644
index 0000000..ffef06f
--- /dev/null
+++ b/libion/original-kernel-headers/linux/ion_test.h
@@ -0,0 +1,70 @@
+/*
+ * drivers/staging/android/uapi/ion.h
+ *
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _UAPI_LINUX_ION_TEST_H
+#define _UAPI_LINUX_ION_TEST_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+/**
+ * struct ion_test_rw_data - metadata passed to the kernel to read handle
+ * @ptr:	a pointer to an area at least as large as size
+ * @offset:	offset into the ion buffer to start reading
+ * @size:	size to read or write
+ * @write:	1 to write, 0 to read
+ */
+struct ion_test_rw_data {
+	__u64 ptr;
+	__u64 offset;
+	__u64 size;
+	int write;
+	int __padding;
+};
+
+#define ION_IOC_MAGIC		'I'
+
+/**
+ * DOC: ION_IOC_TEST_SET_DMA_BUF - attach a dma buf to the test driver
+ *
+ * Attaches a dma buf fd to the test driver.  Passing a second fd or -1 will
+ * release the first fd.
+ */
+#define ION_IOC_TEST_SET_FD \
+			_IO(ION_IOC_MAGIC, 0xf0)
+
+/**
+ * DOC: ION_IOC_TEST_DMA_MAPPING - read or write memory from a handle as DMA
+ *
+ * Reads or writes the memory from a handle using an uncached mapping.  Can be
+ * used by unit tests to emulate a DMA engine as close as possible.  Only
+ * expected to be used for debugging and testing, may not always be available.
+ */
+#define ION_IOC_TEST_DMA_MAPPING \
+			_IOW(ION_IOC_MAGIC, 0xf1, struct ion_test_rw_data)
+
+/**
+ * DOC: ION_IOC_TEST_KERNEL_MAPPING - read or write memory from a handle
+ *
+ * Reads or writes the memory from a handle using a kernel mapping.  Can be
+ * used by unit tests to test heap map_kernel functions.  Only expected to be
+ * used for debugging and testing, may not always be available.
+ */
+#define ION_IOC_TEST_KERNEL_MAPPING \
+			_IOW(ION_IOC_MAGIC, 0xf2, struct ion_test_rw_data)
+
+
+#endif /* _UAPI_LINUX_ION_H */
diff --git a/libion/tests/Android.mk b/libion/tests/Android.mk
new file mode 100644
index 0000000..8dc7f9d
--- /dev/null
+++ b/libion/tests/Android.mk
@@ -0,0 +1,34 @@
+#
+# Copyright (C) 2013 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# 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.
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := ion-unit-tests
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+LOCAL_CFLAGS += -g -Wall -Werror -std=gnu++11 -Wno-missing-field-initializers
+LOCAL_SHARED_LIBRARIES += libion
+LOCAL_STATIC_LIBRARIES += libgtest_main
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/../kernel-headers
+LOCAL_SRC_FILES := \
+	ion_test_fixture.cpp \
+	allocate_test.cpp \
+	formerly_valid_handle_test.cpp \
+	invalid_values_test.cpp \
+	map_test.cpp \
+	device_test.cpp \
+	exit_test.cpp
+include $(BUILD_NATIVE_TEST)
diff --git a/libion/tests/allocate_test.cpp b/libion/tests/allocate_test.cpp
new file mode 100644
index 0000000..e26b302
--- /dev/null
+++ b/libion/tests/allocate_test.cpp
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ */
+
+#include <sys/mman.h>
+
+#include <gtest/gtest.h>
+
+#include <ion/ion.h>
+#include "ion_test_fixture.h"
+
+class Allocate : public IonAllHeapsTest {
+};
+
+TEST_F(Allocate, Allocate)
+{
+    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
+        for (size_t size : allocationSizes) {
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message() << "size " << size);
+            ion_user_handle_t handle = 0;
+            ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, 0, &handle));
+            ASSERT_TRUE(handle != 0);
+            ASSERT_EQ(0, ion_free(m_ionFd, handle));
+        }
+    }
+}
+
+TEST_F(Allocate, AllocateCached)
+{
+    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
+        for (size_t size : allocationSizes) {
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message() << "size " << size);
+            ion_user_handle_t handle = 0;
+            ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED, &handle));
+            ASSERT_TRUE(handle != 0);
+            ASSERT_EQ(0, ion_free(m_ionFd, handle));
+        }
+    }
+}
+
+TEST_F(Allocate, AllocateCachedNeedsSync)
+{
+    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
+        for (size_t size : allocationSizes) {
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message() << "size " << size);
+            ion_user_handle_t handle = 0;
+            ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED_NEEDS_SYNC, &handle));
+            ASSERT_TRUE(handle != 0);
+            ASSERT_EQ(0, ion_free(m_ionFd, handle));
+        }
+    }
+}
+
+TEST_F(Allocate, RepeatedAllocate)
+{
+    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
+        for (size_t size : allocationSizes) {
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message() << "size " << size);
+            ion_user_handle_t handle = 0;
+
+            for (unsigned int i = 0; i < 1024; i++) {
+                SCOPED_TRACE(::testing::Message() << "iteration " << i);
+                ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, 0, &handle));
+                ASSERT_TRUE(handle != 0);
+                ASSERT_EQ(0, ion_free(m_ionFd, handle));
+            }
+        }
+    }
+}
+
+TEST_F(Allocate, Zeroed)
+{
+    void *zeroes = calloc(4096, 1);
+
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        int fds[16];
+        for (unsigned int i = 0; i < 16; i++) {
+            int map_fd = -1;
+
+            ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, 0, &map_fd));
+            ASSERT_GE(map_fd, 0);
+
+            void *ptr = NULL;
+            ptr = mmap(NULL, 4096, PROT_WRITE, MAP_SHARED, map_fd, 0);
+            ASSERT_TRUE(ptr != NULL);
+
+            memset(ptr, 0xaa, 4096);
+
+            ASSERT_EQ(0, munmap(ptr, 4096));
+            fds[i] = map_fd;
+        }
+
+        for (unsigned int i = 0; i < 16; i++) {
+            ASSERT_EQ(0, close(fds[i]));
+        }
+
+        int newIonFd = ion_open();
+        int map_fd = -1;
+
+        ASSERT_EQ(0, ion_alloc_fd(newIonFd, 4096, 0, heapMask, 0, &map_fd));
+        ASSERT_GE(map_fd, 0);
+
+        void *ptr = NULL;
+        ptr = mmap(NULL, 4096, PROT_READ, MAP_SHARED, map_fd, 0);
+        ASSERT_TRUE(ptr != NULL);
+
+        ASSERT_EQ(0, memcmp(ptr, zeroes, 4096));
+
+        ASSERT_EQ(0, munmap(ptr, 4096));
+        ASSERT_EQ(0, close(map_fd));
+    }
+
+    free(zeroes);
+
+}
+
+TEST_F(Allocate, Large)
+{
+    for (unsigned int heapMask : m_allHeaps) {
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        ion_user_handle_t handle = 0;
+        ASSERT_EQ(-ENOMEM, ion_alloc(m_ionFd, 3UL*1024*1024*1024, 0, heapMask, 0, &handle));
+    }
+}
diff --git a/libion/tests/device_test.cpp b/libion/tests/device_test.cpp
new file mode 100644
index 0000000..6f6e1bd
--- /dev/null
+++ b/libion/tests/device_test.cpp
@@ -0,0 +1,568 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ */
+
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <linux/ion_test.h>
+
+#include <gtest/gtest.h>
+
+#include <ion/ion.h>
+
+#include "ion_test_fixture.h"
+
+#define ALIGN(x,y) (((x) + ((y) - 1)) & ~((y) - 1))
+
+class Device : public IonAllHeapsTest {
+ public:
+    virtual void SetUp();
+    virtual void TearDown();
+    int m_deviceFd;
+    void readDMA(int fd, void *buf, size_t size);
+    void writeDMA(int fd, void *buf, size_t size);
+    void readKernel(int fd, void *buf, size_t size);
+    void writeKernel(int fd, void *buf, size_t size);
+    void blowCache();
+    void dirtyCache(void *ptr, size_t size);
+};
+
+void Device::SetUp()
+{
+    IonAllHeapsTest::SetUp();
+    m_deviceFd = open("/dev/ion-test", O_RDWR);
+    ASSERT_GE(m_deviceFd, 0);
+}
+
+void Device::TearDown()
+{
+    ASSERT_EQ(0, close(m_deviceFd));
+    IonAllHeapsTest::TearDown();
+}
+
+void Device::readDMA(int fd, void *buf, size_t size)
+{
+    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, fd));
+    struct ion_test_rw_data ion_test_rw_data = {
+            .ptr = (uint64_t)buf,
+            .offset = 0,
+            .size = size,
+            .write = 0,
+    };
+
+    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_DMA_MAPPING, &ion_test_rw_data));
+    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, -1));
+}
+
+void Device::writeDMA(int fd, void *buf, size_t size)
+{
+    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, fd));
+    struct ion_test_rw_data ion_test_rw_data = {
+            .ptr = (uint64_t)buf,
+            .offset = 0,
+            .size = size,
+            .write = 1,
+    };
+
+    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_DMA_MAPPING, &ion_test_rw_data));
+    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, -1));
+}
+
+void Device::readKernel(int fd, void *buf, size_t size)
+{
+    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, fd));
+    struct ion_test_rw_data ion_test_rw_data = {
+            .ptr = (uint64_t)buf,
+            .offset = 0,
+            .size = size,
+            .write = 0,
+    };
+
+    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_KERNEL_MAPPING, &ion_test_rw_data));
+    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, -1));
+}
+
+void Device::writeKernel(int fd, void *buf, size_t size)
+{
+    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, fd));
+    struct ion_test_rw_data ion_test_rw_data = {
+            .ptr = (uint64_t)buf,
+            .offset = 0,
+            .size = size,
+            .write = 1,
+    };
+
+    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_KERNEL_MAPPING, &ion_test_rw_data));
+    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, -1));
+}
+
+void Device::blowCache()
+{
+    const size_t bigger_than_cache = 8*1024*1024;
+    void *buf1 = malloc(bigger_than_cache);
+    void *buf2 = malloc(bigger_than_cache);
+    memset(buf1, 0xaa, bigger_than_cache);
+    memcpy(buf2, buf1, bigger_than_cache);
+    free(buf1);
+    free(buf2);
+}
+
+void Device::dirtyCache(void *ptr, size_t size)
+{
+    /* try to dirty cache lines */
+    for (size_t i = size-1; i > 0; i--) {
+        ((volatile char *)ptr)[i];
+        ((char *)ptr)[i] = i;
+    }
+}
+
+TEST_F(Device, KernelReadCached)
+{
+    void *alloc = malloc(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        int map_fd = -1;
+        unsigned int flags = ION_FLAG_CACHED;
+
+        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+        ASSERT_GE(map_fd, 0);
+
+        void *ptr;
+        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+        ASSERT_TRUE(ptr != NULL);
+
+        for (int i = 0; i < 4096; i++)
+            ((char *)ptr)[i] = i;
+
+        ((char*)buf)[4096] = 0x12;
+        readKernel(map_fd, buf, 4096);
+        ASSERT_EQ(((char*)buf)[4096], 0x12);
+
+        for (int i = 0; i < 4096; i++)
+            ASSERT_EQ((char)i, ((char *)buf)[i]);
+
+        ASSERT_EQ(0, munmap(ptr, 4096));
+        ASSERT_EQ(0, close(map_fd));
+    }
+
+    free(alloc);
+}
+
+TEST_F(Device, KernelWriteCached)
+{
+    void *alloc = malloc(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+
+    for (int i = 0; i < 4096; i++)
+        ((char *)buf)[i] = i;
+
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        int map_fd = -1;
+        unsigned int flags = ION_FLAG_CACHED;
+
+        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+        ASSERT_GE(map_fd, 0);
+
+        void *ptr;
+        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+        ASSERT_TRUE(ptr != NULL);
+
+        dirtyCache(ptr, 4096);
+
+        writeKernel(map_fd, buf, 4096);
+
+        for (int i = 0; i < 4096; i++)
+            ASSERT_EQ((char)i, ((char *)ptr)[i]) << i;
+
+        ASSERT_EQ(0, munmap(ptr, 4096));
+        ASSERT_EQ(0, close(map_fd));
+    }
+
+    free(alloc);
+}
+
+TEST_F(Device, DMAReadCached)
+{
+    void *alloc = malloc(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        int map_fd = -1;
+        unsigned int flags = ION_FLAG_CACHED;
+
+        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+        ASSERT_GE(map_fd, 0);
+
+        void *ptr;
+        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+        ASSERT_TRUE(ptr != NULL);
+
+        for (int i = 0; i < 4096; i++)
+            ((char *)ptr)[i] = i;
+
+        readDMA(map_fd, buf, 4096);
+
+        for (int i = 0; i < 4096; i++)
+            ASSERT_EQ((char)i, ((char *)buf)[i]);
+
+        ASSERT_EQ(0, munmap(ptr, 4096));
+        ASSERT_EQ(0, close(map_fd));
+    }
+
+    free(alloc);
+}
+
+TEST_F(Device, DMAWriteCached)
+{
+    void *alloc = malloc(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+
+    for (int i = 0; i < 4096; i++)
+        ((char *)buf)[i] = i;
+
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        int map_fd = -1;
+        unsigned int flags = ION_FLAG_CACHED;
+
+        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+        ASSERT_GE(map_fd, 0);
+
+        void *ptr;
+        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+        ASSERT_TRUE(ptr != NULL);
+
+        dirtyCache(ptr, 4096);
+
+        writeDMA(map_fd, buf, 4096);
+
+        for (int i = 0; i < 4096; i++)
+            ASSERT_EQ((char)i, ((char *)ptr)[i]) << i;
+
+        ASSERT_EQ(0, munmap(ptr, 4096));
+        ASSERT_EQ(0, close(map_fd));
+    }
+
+    free(alloc);
+}
+
+TEST_F(Device, KernelReadCachedNeedsSync)
+{
+    void *alloc = malloc(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        int map_fd = -1;
+        unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
+
+        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+        ASSERT_GE(map_fd, 0);
+
+        void *ptr;
+        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+        ASSERT_TRUE(ptr != NULL);
+
+        for (int i = 0; i < 4096; i++)
+            ((char *)ptr)[i] = i;
+
+        ((char*)buf)[4096] = 0x12;
+        readKernel(map_fd, buf, 4096);
+        ASSERT_EQ(((char*)buf)[4096], 0x12);
+
+        for (int i = 0; i < 4096; i++)
+            ASSERT_EQ((char)i, ((char *)buf)[i]);
+
+        ASSERT_EQ(0, munmap(ptr, 4096));
+        ASSERT_EQ(0, close(map_fd));
+    }
+
+    free(alloc);
+}
+
+TEST_F(Device, KernelWriteCachedNeedsSync)
+{
+    void *alloc = malloc(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+
+    for (int i = 0; i < 4096; i++)
+        ((char *)buf)[i] = i;
+
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        int map_fd = -1;
+        unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
+
+        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+        ASSERT_GE(map_fd, 0);
+
+        void *ptr;
+        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+        ASSERT_TRUE(ptr != NULL);
+
+        dirtyCache(ptr, 4096);
+
+        writeKernel(map_fd, buf, 4096);
+
+        for (int i = 0; i < 4096; i++)
+            ASSERT_EQ((char)i, ((char *)ptr)[i]) << i;
+
+        ASSERT_EQ(0, munmap(ptr, 4096));
+        ASSERT_EQ(0, close(map_fd));
+    }
+
+    free(alloc);
+}
+
+TEST_F(Device, DMAReadCachedNeedsSync)
+{
+    void *alloc = malloc(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        int map_fd = -1;
+        unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
+
+        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+        ASSERT_GE(map_fd, 0);
+
+        void *ptr;
+        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+        ASSERT_TRUE(ptr != NULL);
+
+        for (int i = 0; i < 4096; i++)
+            ((char *)ptr)[i] = i;
+
+        ion_sync_fd(m_ionFd, map_fd);
+
+        readDMA(map_fd, buf, 4096);
+
+        for (int i = 0; i < 4096; i++)
+            ASSERT_EQ((char)i, ((char *)buf)[i]);
+
+        ASSERT_EQ(0, munmap(ptr, 4096));
+        ASSERT_EQ(0, close(map_fd));
+    }
+
+    free(alloc);
+}
+
+TEST_F(Device, DMAWriteCachedNeedsSync)
+{
+    void *alloc = malloc(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+
+    for (int i = 0; i < 4096; i++)
+        ((char *)buf)[i] = i;
+
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        int map_fd = -1;
+        unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
+
+        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+        ASSERT_GE(map_fd, 0);
+
+        void *ptr;
+        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+        ASSERT_TRUE(ptr != NULL);
+
+        dirtyCache(ptr, 4096);
+
+        writeDMA(map_fd, buf, 4096);
+
+        ion_sync_fd(m_ionFd, map_fd);
+
+        for (int i = 0; i < 4096; i++)
+            ASSERT_EQ((char)i, ((char *)ptr)[i]) << i;
+
+        ASSERT_EQ(0, munmap(ptr, 4096));
+        ASSERT_EQ(0, close(map_fd));
+    }
+
+    free(alloc);
+}
+TEST_F(Device, KernelRead)
+{
+    void *alloc = malloc(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        int map_fd = -1;
+        unsigned int flags = 0;
+
+        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+        ASSERT_GE(map_fd, 0);
+
+        void *ptr;
+        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+        ASSERT_TRUE(ptr != NULL);
+
+        for (int i = 0; i < 4096; i++)
+            ((char *)ptr)[i] = i;
+
+        ((char*)buf)[4096] = 0x12;
+        readKernel(map_fd, buf, 4096);
+        ASSERT_EQ(((char*)buf)[4096], 0x12);
+
+        for (int i = 0; i < 4096; i++)
+            ASSERT_EQ((char)i, ((char *)buf)[i]);
+
+        ASSERT_EQ(0, munmap(ptr, 4096));
+        ASSERT_EQ(0, close(map_fd));
+    }
+
+    free(alloc);
+}
+
+TEST_F(Device, KernelWrite)
+{
+    void *alloc = malloc(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+
+    for (int i = 0; i < 4096; i++)
+        ((char *)buf)[i] = i;
+
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        int map_fd = -1;
+        unsigned int flags = 0;
+
+        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+        ASSERT_GE(map_fd, 0);
+
+        void *ptr;
+        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+        ASSERT_TRUE(ptr != NULL);
+
+        dirtyCache(ptr, 4096);
+
+        writeKernel(map_fd, buf, 4096);
+
+        for (int i = 0; i < 4096; i++)
+            ASSERT_EQ((char)i, ((char *)ptr)[i]) << i;
+
+        ASSERT_EQ(0, munmap(ptr, 4096));
+        ASSERT_EQ(0, close(map_fd));
+    }
+
+    free(alloc);
+}
+
+TEST_F(Device, DMARead)
+{
+    void *alloc = malloc(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        int map_fd = -1;
+        unsigned int flags = 0;
+
+        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+        ASSERT_GE(map_fd, 0);
+
+        void *ptr;
+        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+        ASSERT_TRUE(ptr != NULL);
+
+        for (int i = 0; i < 4096; i++)
+            ((char *)ptr)[i] = i;
+
+        readDMA(map_fd, buf, 4096);
+
+        for (int i = 0; i < 4096; i++)
+            ASSERT_EQ((char)i, ((char *)buf)[i]);
+
+        ASSERT_EQ(0, munmap(ptr, 4096));
+        ASSERT_EQ(0, close(map_fd));
+    }
+
+    free(alloc);
+}
+
+TEST_F(Device, DMAWrite)
+{
+    void *alloc = malloc(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+
+    for (int i = 0; i < 4096; i++)
+        ((char *)buf)[i] = i;
+
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        int map_fd = -1;
+        unsigned int flags = 0;
+
+        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+        ASSERT_GE(map_fd, 0);
+
+        void *ptr;
+        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+        ASSERT_TRUE(ptr != NULL);
+
+        dirtyCache(ptr, 4096);
+
+        writeDMA(map_fd, buf, 4096);
+
+        for (int i = 0; i < 4096; i++)
+            ASSERT_EQ((char)i, ((char *)ptr)[i]) << i;
+
+        ASSERT_EQ(0, munmap(ptr, 4096));
+        ASSERT_EQ(0, close(map_fd));
+    }
+
+    free(alloc);
+}
+
+TEST_F(Device, IsCached)
+{
+    void *buf = malloc(4096);
+
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        int map_fd = -1;
+        unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
+
+        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+        ASSERT_GE(map_fd, 0);
+
+        void *ptr;
+        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+        ASSERT_TRUE(ptr != NULL);
+
+        dirtyCache(ptr, 4096);
+
+        readDMA(map_fd, buf, 4096);
+
+        bool same = true;
+        for (int i = 4096-16; i >= 0; i -= 16)
+            if (((char *)buf)[i] != i)
+                same = false;
+        ASSERT_FALSE(same);
+
+        ASSERT_EQ(0, munmap(ptr, 4096));
+        ASSERT_EQ(0, close(map_fd));
+    }
+}
diff --git a/libion/tests/exit_test.cpp b/libion/tests/exit_test.cpp
new file mode 100644
index 0000000..cdd3e27
--- /dev/null
+++ b/libion/tests/exit_test.cpp
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ */
+
+#include <sys/mman.h>
+
+#include <gtest/gtest.h>
+
+#include <ion/ion.h>
+
+#include "ion_test_fixture.h"
+
+class Exit : public IonAllHeapsTest {
+};
+
+TEST_F(Exit, WithAlloc)
+{
+    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
+        for (size_t size : allocationSizes) {
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message() << "size " << size);
+            EXPECT_EXIT({
+                ion_user_handle_t handle = 0;
+
+                ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, 0, &handle));
+                ASSERT_TRUE(handle != 0);
+                exit(0);
+            }, ::testing::ExitedWithCode(0), "");
+        }
+    }
+}
+
+TEST_F(Exit, WithAllocFd)
+{
+    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
+        for (size_t size : allocationSizes) {
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message() << "size " << size);
+            EXPECT_EXIT({
+                int handle_fd = -1;
+
+                ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, 0, &handle_fd));
+                ASSERT_NE(-1, handle_fd);
+                exit(0);
+            }, ::testing::ExitedWithCode(0), "");
+        }
+    }
+}
+
+TEST_F(Exit, WithRepeatedAllocFd)
+{
+    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
+        for (size_t size : allocationSizes) {
+            for (unsigned int i = 0; i < 1024; i++) {
+                SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+                SCOPED_TRACE(::testing::Message() << "size " << size);
+                ASSERT_EXIT({
+                    int handle_fd = -1;
+
+                    ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, 0, &handle_fd));
+                    ASSERT_NE(-1, handle_fd);
+                    exit(0);
+                }, ::testing::ExitedWithCode(0), "")
+                        << "failed on heap " << heapMask
+                        << " and size " << size
+                        << " on iteration " << i;
+            }
+        }
+    }
+}
+
+
+TEST_F(Exit, WithMapping)
+{
+    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
+        for (size_t size : allocationSizes) {
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message() << "size " << size);
+            EXPECT_EXIT({
+                int map_fd = -1;
+
+                ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, 0, &map_fd));
+                ASSERT_GE(map_fd, 0);
+
+                void *ptr;
+                ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+                ASSERT_TRUE(ptr != NULL);
+                exit(0);
+            }, ::testing::ExitedWithCode(0), "");
+        }
+    }
+
+}
+
+TEST_F(Exit, WithPartialMapping)
+{
+    static const size_t allocationSizes[] = {64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
+        for (size_t size : allocationSizes) {
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message() << "size " << size);
+            EXPECT_EXIT({
+                int map_fd = -1;
+
+                ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, 0, &map_fd));
+                ASSERT_GE(map_fd, 0);
+
+                void *ptr;
+                ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+                ASSERT_TRUE(ptr != NULL);
+
+                ASSERT_EQ(0, munmap(ptr, size / 2));
+                exit(0);
+            }, ::testing::ExitedWithCode(0), "");
+        }
+    }
+}
+
+TEST_F(Exit, WithMappingCached)
+{
+    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
+        for (size_t size : allocationSizes) {
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message() << "size " << size);
+            EXPECT_EXIT({
+                int map_fd = -1;
+
+                ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED, &map_fd));
+                ASSERT_GE(map_fd, 0);
+
+                void *ptr;
+                ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+                ASSERT_TRUE(ptr != NULL);
+                exit(0);
+            }, ::testing::ExitedWithCode(0), "");
+        }
+    }
+
+}
+
+TEST_F(Exit, WithPartialMappingCached)
+{
+    static const size_t allocationSizes[] = {64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
+        for (size_t size : allocationSizes) {
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message() << "size " << size);
+            EXPECT_EXIT({
+                int map_fd = -1;
+
+                ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED, &map_fd));
+                ASSERT_GE(map_fd, 0);
+
+                void *ptr;
+                ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+                ASSERT_TRUE(ptr != NULL);
+
+                ASSERT_EQ(0, munmap(ptr, size / 2));
+                exit(0);
+            }, ::testing::ExitedWithCode(0), "");
+        }
+    }
+}
+
+TEST_F(Exit, WithMappingNeedsSync)
+{
+    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
+        for (size_t size : allocationSizes) {
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message() << "size " << size);
+            EXPECT_EXIT({
+                int map_fd = -1;
+
+                ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC, &map_fd));
+                ASSERT_GE(map_fd, 0);
+
+                void *ptr;
+                ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+                ASSERT_TRUE(ptr != NULL);
+                exit(0);
+            }, ::testing::ExitedWithCode(0), "");
+        }
+    }
+
+}
+
+TEST_F(Exit, WithPartialMappingNeedsSync)
+{
+    static const size_t allocationSizes[] = {64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
+        for (size_t size : allocationSizes) {
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message() << "size " << size);
+            EXPECT_EXIT({
+                int map_fd = -1;
+
+                ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC, &map_fd));
+                ASSERT_GE(map_fd, 0);
+
+                void *ptr;
+                ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+                ASSERT_TRUE(ptr != NULL);
+
+                ASSERT_EQ(0, munmap(ptr, size / 2));
+                exit(0);
+            }, ::testing::ExitedWithCode(0), "");
+        }
+    }
+}
diff --git a/libion/tests/formerly_valid_handle_test.cpp b/libion/tests/formerly_valid_handle_test.cpp
new file mode 100644
index 0000000..01ab8f3
--- /dev/null
+++ b/libion/tests/formerly_valid_handle_test.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ */
+
+#include <sys/mman.h>
+
+#include <gtest/gtest.h>
+
+#include <ion/ion.h>
+
+#include "ion_test_fixture.h"
+
+class FormerlyValidHandle : public IonTest {
+ public:
+    virtual void SetUp();
+    virtual void TearDown();
+    ion_user_handle_t m_handle;
+};
+
+void FormerlyValidHandle::SetUp()
+{
+    IonTest::SetUp();
+    ASSERT_EQ(0, ion_alloc(m_ionFd, 4096, 0, 1/* ion_env->m_firstHeap */, 0, &m_handle));
+    ASSERT_TRUE(m_handle != 0);
+    ASSERT_EQ(0, ion_free(m_ionFd, m_handle));
+}
+
+void FormerlyValidHandle::TearDown()
+{
+    m_handle = 0;
+}
+
+TEST_F(FormerlyValidHandle, free)
+{
+	ASSERT_EQ(-EINVAL, ion_free(m_ionFd, m_handle));
+}
+
+TEST_F(FormerlyValidHandle, map)
+{
+    int map_fd;
+    unsigned char *ptr;
+
+    ASSERT_EQ(-EINVAL, ion_map(m_ionFd, m_handle, 4096, PROT_READ, 0, 0, &ptr, &map_fd));
+}
+
+TEST_F(FormerlyValidHandle, share)
+{
+    int share_fd;
+
+    ASSERT_EQ(-EINVAL, ion_share(m_ionFd, m_handle, &share_fd));
+}
diff --git a/libion/tests/invalid_values_test.cpp b/libion/tests/invalid_values_test.cpp
new file mode 100644
index 0000000..77fea17
--- /dev/null
+++ b/libion/tests/invalid_values_test.cpp
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ */
+
+#include <sys/mman.h>
+
+#include <gtest/gtest.h>
+
+#include <ion/ion.h>
+
+#include "ion_test_fixture.h"
+
+class InvalidValues : public IonAllHeapsTest {
+ public:
+    virtual void SetUp();
+    virtual void TearDown();
+    ion_user_handle_t m_validHandle;
+    int m_validShareFd;
+    ion_user_handle_t const m_badHandle = -1;
+};
+
+void InvalidValues::SetUp()
+{
+    IonAllHeapsTest::SetUp();
+    ASSERT_EQ(0, ion_alloc(m_ionFd, 4096, 0, m_firstHeap, 0, &m_validHandle))
+      << m_ionFd << " " << m_firstHeap;
+    ASSERT_TRUE(m_validHandle != 0);
+    ASSERT_EQ(0, ion_share(m_ionFd, m_validHandle, &m_validShareFd));
+}
+
+void InvalidValues::TearDown()
+{
+    ASSERT_EQ(0, ion_free(m_ionFd, m_validHandle));
+    ASSERT_EQ(0, close(m_validShareFd));
+    m_validHandle = 0;
+    IonAllHeapsTest::TearDown();
+}
+
+TEST_F(InvalidValues, ion_close)
+{
+    EXPECT_EQ(-EBADF, ion_close(-1));
+}
+
+TEST_F(InvalidValues, ion_alloc)
+{
+    ion_user_handle_t handle;
+    /* invalid ion_fd */
+    int ret = ion_alloc(0, 4096, 0, m_firstHeap, 0, &handle);
+    EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
+    /* invalid ion_fd */
+    EXPECT_EQ(-EBADF, ion_alloc(-1, 4096, 0, m_firstHeap, 0, &handle));
+    /* no heaps */
+    EXPECT_EQ(-ENODEV, ion_alloc(m_ionFd, 4096, 0, 0, 0, &handle));
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        /* zero size */
+        EXPECT_EQ(-EINVAL, ion_alloc(m_ionFd, 0, 0, heapMask, 0, &handle));
+        /* too large size */
+        EXPECT_EQ(-EINVAL, ion_alloc(m_ionFd, -1, 0, heapMask, 0, &handle));
+        /* bad alignment */
+        EXPECT_EQ(-EINVAL, ion_alloc(m_ionFd, 4096, -1, heapMask, 0, &handle));
+        /* NULL handle */
+        EXPECT_EQ(-EINVAL, ion_alloc(m_ionFd, 4096, 0, heapMask, 0, NULL));
+    }
+}
+
+TEST_F(InvalidValues, ion_alloc_fd)
+{
+    int fd;
+    /* invalid ion_fd */
+    int ret = ion_alloc_fd(0, 4096, 0, m_firstHeap, 0, &fd);
+    EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
+    /* invalid ion_fd */
+    EXPECT_EQ(-EBADF, ion_alloc_fd(-1, 4096, 0, m_firstHeap, 0, &fd));
+    /* no heaps */
+    EXPECT_EQ(-ENODEV, ion_alloc_fd(m_ionFd, 4096, 0, 0, 0, &fd));
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        /* zero size */
+        EXPECT_EQ(-EINVAL, ion_alloc_fd(m_ionFd, 0, 0, heapMask, 0, &fd));
+        /* too large size */
+        EXPECT_EQ(-EINVAL, ion_alloc_fd(m_ionFd, -1, 0, heapMask, 0, &fd));
+        /* bad alignment */
+        EXPECT_EQ(-EINVAL, ion_alloc_fd(m_ionFd, 4096, -1, heapMask, 0, &fd));
+        /* NULL handle */
+        EXPECT_EQ(-EINVAL, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, 0, NULL));
+    }
+}
+
+TEST_F(InvalidValues, ion_free)
+{
+    /* invalid ion fd */
+    int ret = ion_free(0, m_validHandle);
+    EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
+    /* invalid ion fd */
+    EXPECT_EQ(-EBADF, ion_free(-1, m_validHandle));
+    /* zero handle */
+    EXPECT_EQ(-EINVAL, ion_free(m_ionFd, 0));
+    /* bad handle */
+    EXPECT_EQ(-EINVAL, ion_free(m_ionFd, m_badHandle));
+}
+
+TEST_F(InvalidValues, ion_map)
+{
+    int map_fd;
+    unsigned char *ptr;
+
+    /* invalid ion fd */
+    int ret = ion_map(0, m_validHandle, 4096, PROT_READ, 0, 0, &ptr, &map_fd);
+    EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
+    /* invalid ion fd */
+    EXPECT_EQ(-EBADF, ion_map(-1, m_validHandle, 4096, PROT_READ, 0, 0, &ptr, &map_fd));
+    /* zero handle */
+    EXPECT_EQ(-EINVAL, ion_map(m_ionFd, 0, 4096, PROT_READ, 0, 0, &ptr, &map_fd));
+    /* bad handle */
+    EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_badHandle, 4096, PROT_READ, 0, 0, &ptr, &map_fd));
+    /* zero length */
+    EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_validHandle, 0, PROT_READ, 0, 0, &ptr, &map_fd));
+    /* bad prot */
+    EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_validHandle, 4096, -1, 0, 0, &ptr, &map_fd));
+    /* bad offset */
+    EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_validHandle, 4096, PROT_READ, 0, -1, &ptr, &map_fd));
+    /* NULL ptr */
+    EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_validHandle, 4096, PROT_READ, 0, 0, NULL, &map_fd));
+    /* NULL map_fd */
+    EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_validHandle, 4096, PROT_READ, 0, 0, &ptr, NULL));
+}
+
+TEST_F(InvalidValues, ion_share)
+{
+    int share_fd;
+
+    /* invalid ion fd */
+    int ret = ion_share(0, m_validHandle, &share_fd);
+    EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
+    /* invalid ion fd */
+    EXPECT_EQ(-EBADF, ion_share(-1, m_validHandle, &share_fd));
+    /* zero handle */
+    EXPECT_EQ(-EINVAL, ion_share(m_ionFd, 0, &share_fd));
+    /* bad handle */
+    EXPECT_EQ(-EINVAL, ion_share(m_ionFd, m_badHandle, &share_fd));
+    /* NULL share_fd */
+    EXPECT_EQ(-EINVAL, ion_share(m_ionFd, m_validHandle, NULL));
+}
+
+TEST_F(InvalidValues, ion_import)
+{
+    ion_user_handle_t handle;
+
+    /* invalid ion fd */
+    int ret = ion_import(0, m_validShareFd, &handle);
+    EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
+    /* invalid ion fd */
+    EXPECT_EQ(-EBADF, ion_import(-1, m_validShareFd, &handle));
+    /* bad share_fd */
+    EXPECT_EQ(-EINVAL, ion_import(m_ionFd, 0, &handle));
+    /* invalid share_fd */
+    EXPECT_EQ(-EBADF, ion_import(m_ionFd, -1, &handle));
+    /* NULL handle */
+    EXPECT_EQ(-EINVAL, ion_import(m_ionFd, m_validShareFd, NULL));
+}
+
+TEST_F(InvalidValues, ion_sync_fd)
+{
+    /* invalid ion fd */
+    int ret = ion_sync_fd(0, m_validShareFd);
+    EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
+    /* invalid ion fd */
+    EXPECT_EQ(-EBADF, ion_sync_fd(-1, m_validShareFd));
+    /* bad share_fd */
+    EXPECT_EQ(-EINVAL, ion_sync_fd(m_ionFd, 0));
+    /* invalid share_fd */
+    EXPECT_EQ(-EBADF, ion_sync_fd(m_ionFd, -1));
+}
diff --git a/libion/tests/ion_test_fixture.cpp b/libion/tests/ion_test_fixture.cpp
new file mode 100644
index 0000000..e20c730
--- /dev/null
+++ b/libion/tests/ion_test_fixture.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include <ion/ion.h>
+
+#include "ion_test_fixture.h"
+
+IonTest::IonTest() : m_ionFd(-1)
+{
+}
+
+void IonTest::SetUp() {
+    m_ionFd = ion_open();
+    ASSERT_GE(m_ionFd, 0);
+}
+
+void IonTest::TearDown() {
+    ion_close(m_ionFd);
+}
+
+IonAllHeapsTest::IonAllHeapsTest() :
+        m_firstHeap(0),
+        m_lastHeap(0),
+        m_allHeaps()
+{
+}
+
+void IonAllHeapsTest::SetUp() {
+    int fd = ion_open();
+    ASSERT_GE(fd, 0);
+
+    for (int i = 1; i != 0; i <<= 1) {
+        ion_user_handle_t handle = 0;
+        int ret;
+        ret = ion_alloc(fd, 4096, 0, i, 0, &handle);
+        if (ret == 0 && handle != 0) {
+            ion_free(fd, handle);
+            if (!m_firstHeap) {
+                m_firstHeap = i;
+            }
+            m_lastHeap = i;
+            m_allHeaps.push_back(i);
+        } else {
+            ASSERT_EQ(-ENODEV, ret);
+        }
+    }
+    ion_close(fd);
+
+    EXPECT_NE(0U, m_firstHeap);
+    EXPECT_NE(0U, m_lastHeap);
+
+    RecordProperty("Heaps", m_allHeaps.size());
+    IonTest::SetUp();
+}
+
+void IonAllHeapsTest::TearDown() {
+    IonTest::TearDown();
+}
diff --git a/libion/tests/ion_test_fixture.h b/libion/tests/ion_test_fixture.h
new file mode 100644
index 0000000..4098214
--- /dev/null
+++ b/libion/tests/ion_test_fixture.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ */
+
+#ifndef ION_TEST_FIXTURE_H_
+#define ION_TEST_FIXTURE_H_
+
+#include <gtest/gtest.h>
+
+using ::testing::Test;
+
+class IonTest : public virtual Test {
+ public:
+    IonTest();
+	virtual ~IonTest() {};
+	virtual void SetUp();
+	virtual void TearDown();
+	int m_ionFd;
+};
+
+class IonAllHeapsTest : public IonTest {
+ public:
+    IonAllHeapsTest();
+    virtual ~IonAllHeapsTest() {};
+    virtual void SetUp();
+    virtual void TearDown();
+
+    unsigned int m_firstHeap;
+    unsigned int m_lastHeap;
+
+    std::vector<unsigned int> m_allHeaps;
+};
+
+#endif /* ION_TEST_FIXTURE_H_ */
diff --git a/libion/tests/map_test.cpp b/libion/tests/map_test.cpp
new file mode 100644
index 0000000..c006dc8
--- /dev/null
+++ b/libion/tests/map_test.cpp
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ */
+
+#include <sys/mman.h>
+
+#include <gtest/gtest.h>
+
+#include <ion/ion.h>
+
+#include "ion_test_fixture.h"
+
+class Map : public IonAllHeapsTest {
+};
+
+TEST_F(Map, MapHandle)
+{
+    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
+        for (size_t size : allocationSizes) {
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message() << "size " << size);
+            ion_user_handle_t handle = 0;
+
+            ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, 0, &handle));
+            ASSERT_TRUE(handle != 0);
+
+            int map_fd = -1;
+            unsigned char *ptr = NULL;
+            ASSERT_EQ(0, ion_map(m_ionFd, handle, size, PROT_READ | PROT_WRITE, MAP_SHARED, 0, &ptr, &map_fd));
+            ASSERT_TRUE(ptr != NULL);
+            ASSERT_GE(map_fd, 0);
+
+            ASSERT_EQ(0, close(map_fd));
+
+            ASSERT_EQ(0, ion_free(m_ionFd, handle));
+
+            memset(ptr, 0xaa, size);
+
+            ASSERT_EQ(0, munmap(ptr, size));
+        }
+    }
+}
+
+TEST_F(Map, MapFd)
+{
+    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
+        for (size_t size : allocationSizes) {
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message() << "size " << size);
+            int map_fd = -1;
+
+            ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, 0, &map_fd));
+            ASSERT_GE(map_fd, 0);
+
+            void *ptr;
+            ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+            ASSERT_TRUE(ptr != NULL);
+
+            ASSERT_EQ(0, close(map_fd));
+
+            memset(ptr, 0xaa, size);
+
+            ASSERT_EQ(0, munmap(ptr, size));
+        }
+    }
+}
+
+TEST_F(Map, MapOffset)
+{
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        int map_fd = -1;
+
+        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, PAGE_SIZE * 2, 0, heapMask, 0, &map_fd));
+        ASSERT_GE(map_fd, 0);
+
+        unsigned char *ptr;
+        ptr = (unsigned char *)mmap(NULL, PAGE_SIZE * 2, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+        ASSERT_TRUE(ptr != NULL);
+
+        memset(ptr, 0, PAGE_SIZE);
+        memset(ptr + PAGE_SIZE, 0xaa, PAGE_SIZE);
+
+        ASSERT_EQ(0, munmap(ptr, PAGE_SIZE * 2));
+
+        ptr = (unsigned char *)mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, PAGE_SIZE);
+        ASSERT_TRUE(ptr != NULL);
+
+        ASSERT_EQ(ptr[0], 0xaa);
+        ASSERT_EQ(ptr[PAGE_SIZE - 1], 0xaa);
+
+        ASSERT_EQ(0, munmap(ptr, PAGE_SIZE));
+
+        ASSERT_EQ(0, close(map_fd));
+    }
+}
+
+TEST_F(Map, MapCached)
+{
+    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
+        for (size_t size : allocationSizes) {
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message() << "size " << size);
+            int map_fd = -1;
+            unsigned int flags = ION_FLAG_CACHED;
+
+            ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, flags, &map_fd));
+            ASSERT_GE(map_fd, 0);
+
+            void *ptr;
+            ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+            ASSERT_TRUE(ptr != NULL);
+
+            ASSERT_EQ(0, close(map_fd));
+
+            memset(ptr, 0xaa, size);
+
+            ASSERT_EQ(0, munmap(ptr, size));
+        }
+    }
+}
+
+TEST_F(Map, MapCachedNeedsSync)
+{
+    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
+        for (size_t size : allocationSizes) {
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message() << "size " << size);
+            int map_fd = -1;
+            unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
+
+            ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, flags, &map_fd));
+            ASSERT_GE(map_fd, 0);
+
+            void *ptr;
+            ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+            ASSERT_TRUE(ptr != NULL);
+
+            ASSERT_EQ(0, close(map_fd));
+
+            memset(ptr, 0xaa, size);
+
+            ASSERT_EQ(0, munmap(ptr, size));
+        }
+    }
+}
diff --git a/libpixelflinger/Android.mk b/libpixelflinger/Android.mk
index 7f20e5b..0f502c0 100644
--- a/libpixelflinger/Android.mk
+++ b/libpixelflinger/Android.mk
@@ -9,13 +9,11 @@
 PIXELFLINGER_SRC_FILES:= \
     codeflinger/ARMAssemblerInterface.cpp \
     codeflinger/ARMAssemblerProxy.cpp \
-    codeflinger/ARMAssembler.cpp \
     codeflinger/CodeCache.cpp \
     codeflinger/GGLAssembler.cpp \
     codeflinger/load_store.cpp \
     codeflinger/blending.cpp \
     codeflinger/texturing.cpp \
-    codeflinger/disassem.c \
 	codeflinger/tinyutils/SharedBuffer.cpp \
 	codeflinger/tinyutils/VectorImpl.cpp \
 	fixed.cpp.arm \
@@ -39,6 +37,8 @@
 endif
 
 ifeq ($(TARGET_ARCH),arm)
+PIXELFLINGER_SRC_FILES += codeflinger/ARMAssembler.cpp
+PIXELFLINGER_SRC_FILES += codeflinger/disassem.c
 # special optimization flags for pixelflinger
 PIXELFLINGER_CFLAGS += -fstrict-aliasing -fomit-frame-pointer
 endif
@@ -52,6 +52,14 @@
 
 LOCAL_SHARED_LIBRARIES := libcutils liblog
 
+ifeq ($(TARGET_ARCH),aarch64)
+PIXELFLINGER_SRC_FILES += arch-aarch64/t32cb16blend.S
+PIXELFLINGER_SRC_FILES += arch-aarch64/col32cb16blend.S
+PIXELFLINGER_SRC_FILES += codeflinger/Aarch64Assembler.cpp
+PIXELFLINGER_SRC_FILES += codeflinger/Aarch64Disassembler.cpp
+PIXELFLINGER_CFLAGS += -fstrict-aliasing -fomit-frame-pointer
+endif
+
 #
 # Shared library
 #
diff --git a/libpixelflinger/arch-aarch64/col32cb16blend.S b/libpixelflinger/arch-aarch64/col32cb16blend.S
new file mode 100644
index 0000000..aa969a4
--- /dev/null
+++ b/libpixelflinger/arch-aarch64/col32cb16blend.S
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+    .text
+    .align
+
+    .global scanline_col32cb16blend_aarch64
+
+//
+// This function alpha blends a fixed color into a destination scanline, using
+// the formula:
+//
+//     d = s + (((a + (a >> 7)) * d) >> 8)
+//
+// where d is the destination pixel,
+//       s is the source color,
+//       a is the alpha channel of the source color.
+//
+
+// x0 = destination buffer pointer
+// w1 = color value
+// w2 = count
+
+
+scanline_col32cb16blend_aarch64:
+
+    lsr         w5, w1, #24                     // shift down alpha
+    mov         w9, #0xff                       // create mask
+    add         w5, w5, w5, lsr #7              // add in top bit
+    mov         w4, #256                        // create #0x100
+    sub         w5, w4, w5                      // invert alpha
+    and         w10, w1, #0xff                  // extract red
+    and         w12, w9, w1, lsr #8             // extract green
+    and         w4,  w9, w1, lsr #16            // extract blue
+    lsl         w10, w10, #5                    // prescale red
+    lsl         w12, w12, #6                    // prescale green
+    lsl         w4,  w4,  #5                    // prescale blue
+    lsr         w9,  w9,  #2                    // create dest green mask
+
+1:
+    ldrh        w8, [x0]                        // load dest pixel
+    subs        w2, w2, #1                      // decrement loop counter
+    lsr         w6, w8, #11                     // extract dest red
+    and         w7, w9, w8, lsr #5              // extract dest green
+    and         w8, w8, #0x1f                   // extract dest blue
+
+    madd        w6, w6, w5, w10                 // dest red * alpha + src red
+    madd        w7, w7, w5, w12                 // dest green * alpha + src green
+    madd        w8, w8, w5, w4                  // dest blue * alpha + src blue
+
+    lsr         w6, w6, #8                      // shift down red
+    lsr         w7, w7, #8                      // shift down green
+    lsl         w6, w6, #11                     // shift red into 565
+    orr         w6, w6, w7, lsl #5              // shift green into 565
+    orr         w6, w6, w8, lsr #8              // shift blue into 565
+
+    strh        w6, [x0], #2                    // store pixel to dest, update ptr
+    b.ne        1b                              // if count != 0, loop
+
+    ret
+
+
+
diff --git a/libpixelflinger/arch-aarch64/t32cb16blend.S b/libpixelflinger/arch-aarch64/t32cb16blend.S
new file mode 100644
index 0000000..b62ed36
--- /dev/null
+++ b/libpixelflinger/arch-aarch64/t32cb16blend.S
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+    .text
+    .align
+
+    .global scanline_t32cb16blend_aarch64
+
+/*
+ * .macro pixel
+ *
+ *  This macro alpha blends RGB565 original pixel located in either
+ *  top or bottom 16 bits of DREG register with SRC 32 bit pixel value
+ *  and writes the result to FB register
+ *
+ * \DREG is a 32-bit register containing *two* original destination RGB565
+ *       pixels, with the even one in the low-16 bits, and the odd one in the
+ *       high 16 bits.
+ *
+ * \SRC is a 32-bit 0xAABBGGRR pixel value, with pre-multiplied colors.
+ *
+ * \FB is a target register that will contain the blended pixel values.
+ *
+ * \ODD is either 0 or 1 and indicates if we're blending the lower or
+ *      upper 16-bit pixels in DREG into FB
+ *
+ *
+ * clobbered: w6, w7, w16, w17, w18
+ *
+ */
+
+.macro pixel,   DREG, SRC, FB, ODD
+
+    // SRC = 0xAABBGGRR
+    lsr     w7, \SRC, #24               // sA
+    add     w7, w7, w7, lsr #7          // sA + (sA >> 7)
+    mov     w6, #0x100
+    sub     w7, w6, w7                  // sA = 0x100 - (sA+(sA>>7))
+
+1:
+
+.if \ODD //Blending odd pixel present in top 16 bits of DREG register
+
+    // red
+    lsr     w16, \DREG, #(16 + 11)
+    mul     w16, w7, w16
+    lsr     w6, \SRC, #3
+    and     w6, w6, #0x1F
+    add     w16, w6, w16, lsr #8
+    cmp     w16, #0x1F
+    orr     w17, \FB, #(0x1F<<(16 + 11))
+    orr     w18, \FB, w16, lsl #(16 + 11)
+    csel    \FB, w17, w18, hi
+        // green
+        and     w6, \DREG, #(0x3F<<(16 + 5))
+        lsr     w17,w6,#(16+5)
+        mul     w6, w7, w17
+        lsr     w16, \SRC, #(8+2)
+        and     w16, w16, #0x3F
+        add     w6, w16, w6, lsr #8
+        cmp     w6, #0x3F
+        orr     w17, \FB, #(0x3F<<(16 + 5))
+        orr     w18, \FB, w6, lsl #(16 + 5)
+        csel    \FB, w17, w18, hi
+            // blue
+            and     w16, \DREG, #(0x1F << 16)
+            lsr     w17,w16,#16
+            mul     w16, w7, w17
+            lsr     w6, \SRC, #(8+8+3)
+            and     w6, w6, #0x1F
+            add     w16, w6, w16, lsr #8
+            cmp     w16, #0x1F
+            orr     w17, \FB, #(0x1F << 16)
+            orr     w18, \FB, w16, lsl #16
+            csel    \FB, w17, w18, hi
+
+.else //Blending even pixel present in bottom 16 bits of DREG register
+
+    // red
+    lsr     w16, \DREG, #11
+    and     w16, w16, #0x1F
+    mul     w16, w7, w16
+    lsr     w6, \SRC, #3
+    and     w6, w6, #0x1F
+    add     w16, w6, w16, lsr #8
+    cmp     w16, #0x1F
+    mov     w17, #(0x1F<<11)
+    lsl     w18, w16, #11
+    csel    \FB, w17, w18, hi
+
+
+        // green
+        and     w6, \DREG, #(0x3F<<5)
+        mul     w6, w7, w6
+        lsr     w16, \SRC, #(8+2)
+        and     w16, w16, #0x3F
+        add     w6, w16, w6, lsr #(5+8)
+        cmp     w6, #0x3F
+        orr     w17, \FB, #(0x3F<<5)
+        orr     w18, \FB, w6, lsl #5
+        csel    \FB, w17, w18, hi
+
+            // blue
+            and     w16, \DREG, #0x1F
+            mul     w16, w7, w16
+            lsr     w6, \SRC, #(8+8+3)
+            and     w6, w6, #0x1F
+            add     w16, w6, w16, lsr #8
+            cmp     w16, #0x1F
+            orr     w17, \FB, #0x1F
+            orr     w18, \FB, w16
+            csel    \FB, w17, w18, hi
+
+.endif // End of blending even pixel
+
+.endm // End of pixel macro
+
+
+// x0:  dst ptr
+// x1:  src ptr
+// w2:  count
+// w3:  d
+// w4:  s0
+// w5:  s1
+// w6:  pixel
+// w7:  pixel
+// w8:  free
+// w9:  free
+// w10: free
+// w11: free
+// w12: scratch
+// w14: pixel
+
+scanline_t32cb16blend_aarch64:
+
+    // align DST to 32 bits
+    tst     x0, #0x3
+    b.eq    aligned
+    subs    w2, w2, #1
+    b.lo    return
+
+last:
+    ldr     w4, [x1], #4
+    ldrh    w3, [x0]
+    pixel   w3, w4, w12, 0
+    strh    w12, [x0], #2
+
+aligned:
+    subs    w2, w2, #2
+    b.lo    9f
+
+    // The main loop is unrolled twice and processes 4 pixels
+8:
+    ldp   w4,w5, [x1], #8
+    add     x0, x0, #4
+    // it's all zero, skip this pixel
+    orr     w3, w4, w5
+    cbz     w3, 7f
+
+    // load the destination
+    ldr     w3, [x0, #-4]
+    // stream the destination
+    pixel   w3, w4, w12, 0
+    pixel   w3, w5, w12, 1
+    str     w12, [x0, #-4]
+
+    // 2nd iteration of the loop, don't stream anything
+    subs    w2, w2, #2
+    csel    w4, w5, w4, lt
+    blt     9f
+    ldp     w4,w5, [x1], #8
+    add     x0, x0, #4
+    orr     w3, w4, w5
+    cbz     w3, 7f
+    ldr     w3, [x0, #-4]
+    pixel   w3, w4, w12, 0
+    pixel   w3, w5, w12, 1
+    str     w12, [x0, #-4]
+
+7:  subs    w2, w2, #2
+    bhs     8b
+    mov     w4, w5
+
+9:  adds    w2, w2, #1
+    b.lo    return
+    b       last
+
+return:
+    ret
diff --git a/libpixelflinger/buffer.cpp b/libpixelflinger/buffer.cpp
index af7356b..cbdab5a 100644
--- a/libpixelflinger/buffer.cpp
+++ b/libpixelflinger/buffer.cpp
@@ -93,7 +93,7 @@
         gen.width   = s.width;
         gen.height  = s.height;
         gen.stride  = s.stride;
-        gen.data    = int32_t(s.data);
+        gen.data    = uintptr_t(s.data);
     }
 }
 
diff --git a/libpixelflinger/codeflinger/ARMAssembler.cpp b/libpixelflinger/codeflinger/ARMAssembler.cpp
index 607ed3c..92243da 100644
--- a/libpixelflinger/codeflinger/ARMAssembler.cpp
+++ b/libpixelflinger/codeflinger/ARMAssembler.cpp
@@ -99,8 +99,8 @@
         if (comment >= 0) {
             printf("; %s\n", mComments.valueAt(comment));
         }
-        printf("%08x:    %08x    ", int(i), int(i[0]));
-        ::disassemble((u_int)i);
+        printf("%08x:    %08x    ", uintptr_t(i), int(i[0]));
+        ::disassemble((uintptr_t)i);
         i++;
     }
 }
@@ -186,7 +186,7 @@
 
 #if defined(WITH_LIB_HARDWARE)
     if (__builtin_expect(mQemuTracing, 0)) {
-        int err = qemu_add_mapping(int(base()), name);
+        int err = qemu_add_mapping(uintptr_t(base()), name);
         mQemuTracing = (err >= 0);
     }
 #endif
diff --git a/libpixelflinger/codeflinger/ARMAssemblerInterface.cpp b/libpixelflinger/codeflinger/ARMAssemblerInterface.cpp
index 073633c..5041999 100644
--- a/libpixelflinger/codeflinger/ARMAssemblerInterface.cpp
+++ b/libpixelflinger/codeflinger/ARMAssemblerInterface.cpp
@@ -61,6 +61,29 @@
             ((W&1)<<21) | (((offset&0xF0)<<4)|(offset&0xF));
 }
 
+// The following four functions are required for address manipulation
+// These are virtual functions, which can be overridden by architectures
+// that need special handling of address values (e.g. 64-bit arch)
 
+void ARMAssemblerInterface::ADDR_LDR(int cc, int Rd,
+     int Rn, uint32_t offset)
+{
+    LDR(cc, Rd, Rn, offset);
+}
+void ARMAssemblerInterface::ADDR_STR(int cc, int Rd,
+     int Rn, uint32_t offset)
+{
+    STR(cc, Rd, Rn, offset);
+}
+void ARMAssemblerInterface::ADDR_ADD(int cc, int s,
+     int Rd, int Rn, uint32_t Op2)
+{
+    dataProcessing(opADD, cc, s, Rd, Rn, Op2);
+}
+void ARMAssemblerInterface::ADDR_SUB(int cc, int s,
+     int Rd, int Rn, uint32_t Op2)
+{
+    dataProcessing(opSUB, cc, s, Rd, Rn, Op2);
+}
 }; // namespace android
 
diff --git a/libpixelflinger/codeflinger/ARMAssemblerInterface.h b/libpixelflinger/codeflinger/ARMAssemblerInterface.h
index 9991980..6e0d7c6 100644
--- a/libpixelflinger/codeflinger/ARMAssemblerInterface.h
+++ b/libpixelflinger/codeflinger/ARMAssemblerInterface.h
@@ -63,7 +63,7 @@
     };
 
     enum {
-        CODEGEN_ARCH_ARM = 1, CODEGEN_ARCH_MIPS
+        CODEGEN_ARCH_ARM = 1, CODEGEN_ARCH_MIPS, CODEGEN_ARCH_AARCH64
     };
 
     // -----------------------------------------------------------------------
@@ -331,6 +331,16 @@
     inline void
     SMLAWT(int cc, int Rd, int Rm, int Rs, int Rn) {
         SMLAW(cc, yT, Rd, Rm, Rs, Rn);    }
+
+    // Address loading/storing/manipulation
+    virtual void ADDR_LDR(int cc, int Rd,
+                int Rn, uint32_t offset = __immed12_pre(0));
+    virtual void ADDR_STR (int cc, int Rd,
+                int Rn, uint32_t offset = __immed12_pre(0));
+    virtual void ADDR_ADD(int cc, int s, int Rd,
+                int Rn, uint32_t Op2);
+    virtual void ADDR_SUB(int cc, int s, int Rd,
+                int Rn, uint32_t Op2);
 };
 
 }; // namespace android
diff --git a/libpixelflinger/codeflinger/ARMAssemblerProxy.cpp b/libpixelflinger/codeflinger/ARMAssemblerProxy.cpp
index 1c7bc76..816de48 100644
--- a/libpixelflinger/codeflinger/ARMAssemblerProxy.cpp
+++ b/libpixelflinger/codeflinger/ARMAssemblerProxy.cpp
@@ -294,5 +294,18 @@
     mTarget->UBFX(cc, Rd, Rn, lsb, width);
 }
 
+void ARMAssemblerProxy::ADDR_LDR(int cc, int Rd, int Rn, uint32_t offset) {
+     mTarget->ADDR_LDR(cc, Rd, Rn, offset);
+}
+void ARMAssemblerProxy::ADDR_STR(int cc, int Rd, int Rn, uint32_t offset) {
+     mTarget->ADDR_STR(cc, Rd, Rn, offset);
+}
+void ARMAssemblerProxy::ADDR_ADD(int cc, int s, int Rd, int Rn, uint32_t Op2){
+     mTarget->ADDR_ADD(cc, s, Rd, Rn, Op2);
+}
+void ARMAssemblerProxy::ADDR_SUB(int cc, int s, int Rd, int Rn, uint32_t Op2){
+     mTarget->ADDR_SUB(cc, s, Rd, Rn, Op2);
+}
+
 }; // namespace android
 
diff --git a/libpixelflinger/codeflinger/ARMAssemblerProxy.h b/libpixelflinger/codeflinger/ARMAssemblerProxy.h
index 70cb464..b852794 100644
--- a/libpixelflinger/codeflinger/ARMAssemblerProxy.h
+++ b/libpixelflinger/codeflinger/ARMAssemblerProxy.h
@@ -146,6 +146,15 @@
     virtual void UXTB16(int cc, int Rd, int Rm, int rotate);
     virtual void UBFX(int cc, int Rd, int Rn, int lsb, int width);
 
+    virtual void ADDR_LDR(int cc, int Rd,
+                int Rn, uint32_t offset = __immed12_pre(0));
+    virtual void ADDR_STR (int cc, int Rd,
+                int Rn, uint32_t offset = __immed12_pre(0));
+    virtual void ADDR_ADD(int cc, int s, int Rd,
+                int Rn, uint32_t Op2);
+    virtual void ADDR_SUB(int cc, int s, int Rd,
+                int Rn, uint32_t Op2);
+
 private:
     ARMAssemblerInterface*  mTarget;
 };
diff --git a/libpixelflinger/codeflinger/Aarch64Assembler.cpp b/libpixelflinger/codeflinger/Aarch64Assembler.cpp
new file mode 100644
index 0000000..0e4f7df
--- /dev/null
+++ b/libpixelflinger/codeflinger/Aarch64Assembler.cpp
@@ -0,0 +1,1242 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define LOG_TAG "ArmToAarch64Assembler"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#include <private/pixelflinger/ggl_context.h>
+
+#include "codeflinger/Aarch64Assembler.h"
+#include "codeflinger/CodeCache.h"
+#include "codeflinger/Aarch64Disassembler.h"
+
+
+/*
+** --------------------------------------------
+** Support for Aarch64 in GGLAssembler JIT
+** --------------------------------------------
+**
+** Approach
+** - GGLAssembler and associated files are largely un-changed.
+** - A translator class maps ArmAssemblerInterface calls to
+**   generate AArch64 instructions.
+**
+** ----------------------
+** ArmToAarch64Assembler
+** ----------------------
+**
+** - Subclassed from ArmAssemblerInterface
+**
+** - Translates each ArmAssemblerInterface call to generate
+**   one or more Aarch64 instructions  as necessary.
+**
+** - Does not implement ArmAssemblerInterface portions unused by GGLAssembler
+**   It calls NOT_IMPLEMENTED() for such cases, which in turn logs
+**    a fatal message.
+**
+** - Uses A64_.. series of functions to generate instruction machine code
+**   for Aarch64 instructions. These functions also log the instruction
+**   to LOG, if AARCH64_ASM_DEBUG define is set to 1
+**
+** - Dumps machine code and eqvt assembly if "debug.pf.disasm" option is set
+**   It uses aarch64_disassemble to perform disassembly
+**
+** - Uses register 13 (SP in ARM), 15 (PC in ARM), 16, 17 for storing
+**   intermediate results. GGLAssembler does not use SP and PC as these
+**   registers are marked as reserved. The temporary registers are not
+**   saved/restored on stack as these are caller-saved registers in Aarch64
+**
+** - Uses CSEL instruction to support conditional execution. The result is
+**   stored in a temporary register and then copied to the target register
+**   if the condition is true.
+**
+** - In the case of conditional data transfer instructions, conditional
+**   branch is used to skip over instruction, if the condition is false
+**
+** - Wherever possible, immediate values are transferred to temporary
+**   register prior to processing. This simplifies overall implementation
+**   as instructions requiring immediate values are converted to
+**   move immediate instructions followed by register-register instruction.
+**
+** --------------------------------------------
+** ArmToAarch64Assembler unit test bench
+** --------------------------------------------
+**
+** - Tests ArmToAarch64Assembler interface for all the possible
+**   ways in which GGLAssembler uses ArmAssemblerInterface interface.
+**
+** - Uses test jacket (written in assembly) to set the registers,
+**   condition flags prior to calling generated instruction. It also
+**   copies registers and flags at the end of execution. Caller then
+**   checks if generated code performed correct operation based on
+**   output registers and flags.
+**
+** - Broadly contains three type of tests, (i) data operation tests
+**   (ii) data transfer tests and (iii) LDM/STM tests.
+**
+** ----------------------
+** Aarch64 disassembler
+** ----------------------
+** - This disassembler disassembles only those machine codes which can be
+**   generated by ArmToAarch64Assembler. It has a unit testbench which
+**   tests all the instructions supported by the disassembler.
+**
+** ------------------------------------------------------------------
+** ARMAssembler/ARMAssemblerInterface/ARMAssemblerProxy changes
+** ------------------------------------------------------------------
+**
+** - In existing code, addresses were being handled as 32 bit values at
+**   certain places.
+**
+** - Added a new set of functions for address load/store/manipulation.
+**   These are ADDR_LDR, ADDR_STR, ADDR_ADD, ADDR_SUB and they map to
+**   default 32 bit implementations in ARMAssemblerInterface.
+**
+** - ArmToAarch64Assembler maps these functions to appropriate 64 bit
+**   functions.
+**
+** ----------------------
+** GGLAssembler changes
+** ----------------------
+** - Since ArmToAarch64Assembler can generate 4 Aarch64 instructions for
+**   each call in worst case, the memory required is set to 4 times
+**   ARM memory
+**
+** - Address load/store/manipulation were changed to use new functions
+**   added in the ARMAssemblerInterface.
+**
+*/
+
+
+#define NOT_IMPLEMENTED()  LOG_FATAL("Arm instruction %s not yet implemented\n", __func__)
+
+#define AARCH64_ASM_DEBUG 0
+
+#if AARCH64_ASM_DEBUG
+    #define LOG_INSTR(...) ALOGD("\t" __VA_ARGS__)
+    #define LOG_LABEL(...) ALOGD(__VA_ARGS__)
+#else
+    #define LOG_INSTR(...) ((void)0)
+    #define LOG_LABEL(...) ((void)0)
+#endif
+
+namespace android {
+
+static const char* shift_codes[] =
+{
+    "LSL", "LSR", "ASR", "ROR"
+};
+static const char *cc_codes[] =
+{
+    "EQ", "NE", "CS", "CC", "MI",
+    "PL", "VS", "VC", "HI", "LS",
+    "GE", "LT", "GT", "LE", "AL", "NV"
+};
+
+ArmToAarch64Assembler::ArmToAarch64Assembler(const sp<Assembly>& assembly)
+    :   ARMAssemblerInterface(),
+        mAssembly(assembly)
+{
+    mBase = mPC = (uint32_t *)assembly->base();
+    mDuration = ggl_system_time();
+    mZeroReg = 13;
+    mTmpReg1 = 15;
+    mTmpReg2 = 16;
+    mTmpReg3 = 17;
+}
+
+ArmToAarch64Assembler::ArmToAarch64Assembler(void *base)
+    :   ARMAssemblerInterface(), mAssembly(NULL)
+{
+    mBase = mPC = (uint32_t *)base;
+    mDuration = ggl_system_time();
+    // Regs 13, 15, 16, 17 are used as temporary registers
+    mZeroReg = 13;
+    mTmpReg1 = 15;
+    mTmpReg2 = 16;
+    mTmpReg3 = 17;
+}
+
+ArmToAarch64Assembler::~ArmToAarch64Assembler()
+{
+}
+
+uint32_t* ArmToAarch64Assembler::pc() const
+{
+    return mPC;
+}
+
+uint32_t* ArmToAarch64Assembler::base() const
+{
+    return mBase;
+}
+
+void ArmToAarch64Assembler::reset()
+{
+    if(mAssembly == NULL)
+        mPC = mBase;
+    else
+        mBase = mPC = (uint32_t *)mAssembly->base();
+    mBranchTargets.clear();
+    mLabels.clear();
+    mLabelsInverseMapping.clear();
+    mComments.clear();
+#if AARCH64_ASM_DEBUG
+    ALOGI("RESET\n");
+#endif
+}
+
+int ArmToAarch64Assembler::getCodegenArch()
+{
+    return CODEGEN_ARCH_AARCH64;
+}
+
+// ----------------------------------------------------------------------------
+
+void ArmToAarch64Assembler::disassemble(const char* name)
+{
+    if(name)
+    {
+        printf("%s:\n", name);
+    }
+    size_t count = pc()-base();
+    uint32_t* i = base();
+    while (count--)
+    {
+        ssize_t label = mLabelsInverseMapping.indexOfKey(i);
+        if (label >= 0)
+        {
+            printf("%s:\n", mLabelsInverseMapping.valueAt(label));
+        }
+        ssize_t comment = mComments.indexOfKey(i);
+        if (comment >= 0)
+        {
+            printf("; %s\n", mComments.valueAt(comment));
+        }
+        printf("%p:    %08x    ", i, uint32_t(i[0]));
+        {
+            char instr[256];
+            ::aarch64_disassemble(*i, instr);
+            printf("%s\n", instr);
+        }
+        i++;
+    }
+}
+
+void ArmToAarch64Assembler::comment(const char* string)
+{
+    mComments.add(mPC, string);
+    LOG_INSTR("//%s\n", string);
+}
+
+void ArmToAarch64Assembler::label(const char* theLabel)
+{
+    mLabels.add(theLabel, mPC);
+    mLabelsInverseMapping.add(mPC, theLabel);
+    LOG_LABEL("%s:\n", theLabel);
+}
+
+void ArmToAarch64Assembler::B(int cc, const char* label)
+{
+    mBranchTargets.add(branch_target_t(label, mPC));
+    LOG_INSTR("B%s %s\n", cc_codes[cc], label );
+    *mPC++ = (0x54 << 24) | cc;
+}
+
+void ArmToAarch64Assembler::BL(int cc, const char* label)
+{
+    NOT_IMPLEMENTED(); //Not Required
+}
+
+// ----------------------------------------------------------------------------
+//Prolog/Epilog & Generate...
+// ----------------------------------------------------------------------------
+
+void ArmToAarch64Assembler::prolog()
+{
+    // write prolog code
+    mPrologPC = mPC;
+    *mPC++ = A64_MOVZ_X(mZeroReg,0,0);
+}
+
+void ArmToAarch64Assembler::epilog(uint32_t touched)
+{
+    // write epilog code
+    static const int XLR = 30;
+    *mPC++ = A64_RET(XLR);
+}
+
+int ArmToAarch64Assembler::generate(const char* name)
+{
+    // fixup all the branches
+    size_t count = mBranchTargets.size();
+    while (count--)
+    {
+        const branch_target_t& bt = mBranchTargets[count];
+        uint32_t* target_pc = mLabels.valueFor(bt.label);
+        LOG_ALWAYS_FATAL_IF(!target_pc,
+                "error resolving branch targets, target_pc is null");
+        int32_t offset = int32_t(target_pc - bt.pc);
+        *bt.pc |= (offset & 0x7FFFF) << 5;
+    }
+
+    if(mAssembly != NULL)
+        mAssembly->resize( int(pc()-base())*4 );
+
+    // the instruction cache is flushed by CodeCache
+    const int64_t duration = ggl_system_time() - mDuration;
+    const char * const format = "generated %s (%d ins) at [%p:%p] in %ld ns\n";
+    ALOGI(format, name, int(pc()-base()), base(), pc(), duration);
+
+
+    char value[PROPERTY_VALUE_MAX];
+    property_get("debug.pf.disasm", value, "0");
+    if (atoi(value) != 0)
+    {
+        printf(format, name, int(pc()-base()), base(), pc(), duration);
+        disassemble(name);
+    }
+    return NO_ERROR;
+}
+
+uint32_t* ArmToAarch64Assembler::pcForLabel(const char* label)
+{
+    return mLabels.valueFor(label);
+}
+
+// ----------------------------------------------------------------------------
+// Data Processing...
+// ----------------------------------------------------------------------------
+void ArmToAarch64Assembler::dataProcessingCommon(int opcode,
+        int s, int Rd, int Rn, uint32_t Op2)
+{
+    if(opcode != opSUB && s == 1)
+    {
+        NOT_IMPLEMENTED(); //Not required
+        return;
+    }
+
+    if(opcode != opSUB && opcode != opADD && opcode != opAND &&
+       opcode != opORR && opcode != opMVN)
+    {
+        NOT_IMPLEMENTED(); //Not required
+        return;
+    }
+
+    if(Op2 == OPERAND_REG_IMM && mAddrMode.reg_imm_shift > 31)
+        {
+        NOT_IMPLEMENTED();
+        return;
+    }
+
+    //Store immediate in temporary register and convert
+    //immediate operation into register operation
+    if(Op2 == OPERAND_IMM)
+    {
+        int imm = mAddrMode.immediate;
+        *mPC++ = A64_MOVZ_W(mTmpReg2, imm & 0x0000FFFF, 0);
+        *mPC++ = A64_MOVK_W(mTmpReg2, (imm >> 16) & 0x0000FFFF, 16);
+        Op2 = mTmpReg2;
+    }
+
+
+    {
+        uint32_t shift;
+        uint32_t amount;
+        uint32_t Rm;
+
+        if(Op2 == OPERAND_REG_IMM)
+        {
+            shift   = mAddrMode.reg_imm_type;
+            amount  = mAddrMode.reg_imm_shift;
+            Rm      = mAddrMode.reg_imm_Rm;
+        }
+        else if(Op2 < OPERAND_REG)
+        {
+            shift   = 0;
+            amount  = 0;
+            Rm      = Op2;
+        }
+        else
+        {
+            NOT_IMPLEMENTED(); //Not required
+            return;
+        }
+
+        switch(opcode)
+        {
+            case opADD: *mPC++ = A64_ADD_W(Rd, Rn, Rm, shift, amount); break;
+            case opAND: *mPC++ = A64_AND_W(Rd, Rn, Rm, shift, amount); break;
+            case opORR: *mPC++ = A64_ORR_W(Rd, Rn, Rm, shift, amount); break;
+            case opMVN: *mPC++ = A64_ORN_W(Rd, Rn, Rm, shift, amount); break;
+            case opSUB: *mPC++ = A64_SUB_W(Rd, Rn, Rm, shift, amount, s);break;
+        };
+
+    }
+}
+
+void ArmToAarch64Assembler::dataProcessing(int opcode, int cc,
+        int s, int Rd, int Rn, uint32_t Op2)
+{
+    uint32_t Wd;
+
+    if(cc != AL)
+        Wd = mTmpReg1;
+    else
+        Wd = Rd;
+
+    if(opcode == opADD || opcode == opAND || opcode == opORR ||opcode == opSUB)
+    {
+        dataProcessingCommon(opcode, s, Wd, Rn, Op2);
+    }
+    else if(opcode == opCMP)
+    {
+        dataProcessingCommon(opSUB, 1, mTmpReg3, Rn, Op2);
+    }
+    else if(opcode == opRSB)
+    {
+        dataProcessingCommon(opSUB, s, Wd, Rn, Op2);
+        dataProcessingCommon(opSUB, s, Wd, mZeroReg, Wd);
+    }
+    else if(opcode == opMOV)
+    {
+        dataProcessingCommon(opORR, 0, Wd, mZeroReg, Op2);
+        if(s == 1)
+        {
+            dataProcessingCommon(opSUB, 1, mTmpReg3, Wd, mZeroReg);
+        }
+    }
+    else if(opcode == opMVN)
+    {
+        dataProcessingCommon(opMVN, s, Wd, mZeroReg, Op2);
+    }
+    else if(opcode == opBIC)
+    {
+        dataProcessingCommon(opMVN, s, mTmpReg3, mZeroReg, Op2);
+        dataProcessingCommon(opAND, s, Wd, Rn, mTmpReg3);
+    }
+    else
+    {
+        NOT_IMPLEMENTED();
+        return;
+    }
+
+    if(cc != AL)
+    {
+        *mPC++ = A64_CSEL_W(Rd, mTmpReg1, Rd, cc);
+    }
+}
+// ----------------------------------------------------------------------------
+// Address Processing...
+// ----------------------------------------------------------------------------
+
+void ArmToAarch64Assembler::ADDR_ADD(int cc,
+        int s, int Rd, int Rn, uint32_t Op2)
+{
+    if(cc != AL){ NOT_IMPLEMENTED(); return;} //Not required
+    if(s  != 0) { NOT_IMPLEMENTED(); return;} //Not required
+
+
+    if(Op2 == OPERAND_REG_IMM && mAddrMode.reg_imm_type == LSL)
+    {
+        int Rm = mAddrMode.reg_imm_Rm;
+        int amount = mAddrMode.reg_imm_shift;
+        *mPC++ = A64_ADD_X_Wm_SXTW(Rd, Rn, Rm, amount);
+    }
+    else if(Op2 < OPERAND_REG)
+    {
+        int Rm = Op2;
+        int amount = 0;
+        *mPC++ = A64_ADD_X_Wm_SXTW(Rd, Rn, Rm, amount);
+    }
+    else if(Op2 == OPERAND_IMM)
+    {
+        int imm = mAddrMode.immediate;
+        *mPC++ = A64_MOVZ_W(mTmpReg1, imm & 0x0000FFFF, 0);
+        *mPC++ = A64_MOVK_W(mTmpReg1, (imm >> 16) & 0x0000FFFF, 16);
+
+        int Rm = mTmpReg1;
+        int amount = 0;
+        *mPC++ = A64_ADD_X_Wm_SXTW(Rd, Rn, Rm, amount);
+    }
+    else
+    {
+        NOT_IMPLEMENTED(); //Not required
+    }
+}
+
+void ArmToAarch64Assembler::ADDR_SUB(int cc,
+        int s, int Rd, int Rn, uint32_t Op2)
+{
+    if(cc != AL){ NOT_IMPLEMENTED(); return;} //Not required
+    if(s  != 0) { NOT_IMPLEMENTED(); return;} //Not required
+
+    if(Op2 == OPERAND_REG_IMM && mAddrMode.reg_imm_type == LSR)
+    {
+        *mPC++ = A64_ADD_W(mTmpReg1, mZeroReg, mAddrMode.reg_imm_Rm,
+                           LSR, mAddrMode.reg_imm_shift);
+        *mPC++ = A64_SUB_X_Wm_SXTW(Rd, Rn, mTmpReg1, 0);
+    }
+    else
+    {
+        NOT_IMPLEMENTED(); //Not required
+    }
+}
+
+// ----------------------------------------------------------------------------
+// multiply...
+// ----------------------------------------------------------------------------
+void ArmToAarch64Assembler::MLA(int cc, int s,int Rd, int Rm, int Rs, int Rn)
+{
+    if(cc != AL){ NOT_IMPLEMENTED(); return;} //Not required
+
+    *mPC++ = A64_MADD_W(Rd, Rm, Rs, Rn);
+    if(s == 1)
+        dataProcessingCommon(opSUB, 1, mTmpReg1, Rd, mZeroReg);
+}
+void ArmToAarch64Assembler::MUL(int cc, int s, int Rd, int Rm, int Rs)
+{
+    if(cc != AL){ NOT_IMPLEMENTED(); return;} //Not required
+    if(s  != 0) { NOT_IMPLEMENTED(); return;} //Not required
+    *mPC++ = A64_MADD_W(Rd, Rm, Rs, mZeroReg);
+}
+void ArmToAarch64Assembler::UMULL(int cc, int s,
+        int RdLo, int RdHi, int Rm, int Rs)
+{
+    NOT_IMPLEMENTED(); //Not required
+}
+void ArmToAarch64Assembler::UMUAL(int cc, int s,
+        int RdLo, int RdHi, int Rm, int Rs)
+{
+    NOT_IMPLEMENTED(); //Not required
+}
+void ArmToAarch64Assembler::SMULL(int cc, int s,
+        int RdLo, int RdHi, int Rm, int Rs)
+{
+    NOT_IMPLEMENTED(); //Not required
+}
+void ArmToAarch64Assembler::SMUAL(int cc, int s,
+        int RdLo, int RdHi, int Rm, int Rs)
+{
+    NOT_IMPLEMENTED(); //Not required
+}
+
+// ----------------------------------------------------------------------------
+// branches relative to PC...
+// ----------------------------------------------------------------------------
+void ArmToAarch64Assembler::B(int cc, uint32_t* pc){
+    NOT_IMPLEMENTED(); //Not required
+}
+
+void ArmToAarch64Assembler::BL(int cc, uint32_t* pc){
+    NOT_IMPLEMENTED(); //Not required
+}
+
+void ArmToAarch64Assembler::BX(int cc, int Rn){
+    NOT_IMPLEMENTED(); //Not required
+}
+
+// ----------------------------------------------------------------------------
+// data transfer...
+// ----------------------------------------------------------------------------
+enum dataTransferOp
+{
+    opLDR,opLDRB,opLDRH,opSTR,opSTRB,opSTRH
+};
+
+void ArmToAarch64Assembler::dataTransfer(int op, int cc,
+                            int Rd, int Rn, uint32_t op_type, uint32_t size)
+{
+    const int XSP = 31;
+    if(Rn == SP)
+        Rn = XSP;
+
+    if(op_type == OPERAND_IMM)
+    {
+        int addrReg;
+        int imm = mAddrMode.immediate;
+        if(imm >= 0 && imm < (1<<12))
+            *mPC++ = A64_ADD_IMM_X(mTmpReg1, mZeroReg, imm, 0);
+        else if(imm < 0 && -imm < (1<<12))
+            *mPC++ = A64_SUB_IMM_X(mTmpReg1, mZeroReg, -imm, 0);
+        else
+        {
+            NOT_IMPLEMENTED();
+            return;
+        }
+
+        addrReg = Rn;
+        if(mAddrMode.preindex == true || mAddrMode.postindex == true)
+        {
+            *mPC++ = A64_ADD_X(mTmpReg2, addrReg, mTmpReg1);
+            if(mAddrMode.preindex == true)
+                addrReg = mTmpReg2;
+        }
+
+        if(cc != AL)
+            *mPC++ = A64_B_COND(cc^1, 8);
+
+        *mPC++ = A64_LDRSTR_Wm_SXTW_0(op, size, Rd, addrReg, mZeroReg);
+
+        if(mAddrMode.writeback == true)
+            *mPC++ = A64_CSEL_X(Rn, mTmpReg2, Rn, cc);
+    }
+    else if(op_type == OPERAND_REG_OFFSET)
+    {
+        if(cc != AL)
+            *mPC++ = A64_B_COND(cc^1, 8);
+        *mPC++ = A64_LDRSTR_Wm_SXTW_0(op, size, Rd, Rn, mAddrMode.reg_offset);
+
+    }
+    else if(op_type > OPERAND_UNSUPPORTED)
+    {
+        if(cc != AL)
+            *mPC++ = A64_B_COND(cc^1, 8);
+        *mPC++ = A64_LDRSTR_Wm_SXTW_0(op, size, Rd, Rn, mZeroReg);
+    }
+    else
+    {
+        NOT_IMPLEMENTED(); // Not required
+    }
+    return;
+
+}
+void ArmToAarch64Assembler::ADDR_LDR(int cc, int Rd, int Rn, uint32_t op_type)
+{
+    return dataTransfer(opLDR, cc, Rd, Rn, op_type, 64);
+}
+void ArmToAarch64Assembler::ADDR_STR(int cc, int Rd, int Rn, uint32_t op_type)
+{
+    return dataTransfer(opSTR, cc, Rd, Rn, op_type, 64);
+}
+void ArmToAarch64Assembler::LDR(int cc, int Rd, int Rn, uint32_t op_type)
+{
+    return dataTransfer(opLDR, cc, Rd, Rn, op_type);
+}
+void ArmToAarch64Assembler::LDRB(int cc, int Rd, int Rn, uint32_t op_type)
+{
+    return dataTransfer(opLDRB, cc, Rd, Rn, op_type);
+}
+void ArmToAarch64Assembler::STR(int cc, int Rd, int Rn, uint32_t op_type)
+{
+    return dataTransfer(opSTR, cc, Rd, Rn, op_type);
+}
+
+void ArmToAarch64Assembler::STRB(int cc, int Rd, int Rn, uint32_t op_type)
+{
+    return dataTransfer(opSTRB, cc, Rd, Rn, op_type);
+}
+
+void ArmToAarch64Assembler::LDRH(int cc, int Rd, int Rn, uint32_t op_type)
+{
+    return dataTransfer(opLDRH, cc, Rd, Rn, op_type);
+}
+void ArmToAarch64Assembler::LDRSB(int cc, int Rd, int Rn, uint32_t offset)
+{
+    NOT_IMPLEMENTED(); //Not required
+}
+void ArmToAarch64Assembler::LDRSH(int cc, int Rd, int Rn, uint32_t offset)
+{
+    NOT_IMPLEMENTED(); //Not required
+}
+
+void ArmToAarch64Assembler::STRH(int cc, int Rd, int Rn, uint32_t op_type)
+{
+    return dataTransfer(opSTRH, cc, Rd, Rn, op_type);
+}
+
+// ----------------------------------------------------------------------------
+// block data transfer...
+// ----------------------------------------------------------------------------
+void ArmToAarch64Assembler::LDM(int cc, int dir,
+        int Rn, int W, uint32_t reg_list)
+{
+    const int XSP = 31;
+    if(cc != AL || dir != IA || W == 0 || Rn != SP)
+    {
+        NOT_IMPLEMENTED();
+        return;
+    }
+
+    for(int i = 0; i < 32; ++i)
+    {
+        if((reg_list & (1 << i)))
+        {
+            int reg = i;
+            int size = 16;
+            *mPC++ = A64_LDR_IMM_PostIndex(reg, XSP, size);
+        }
+    }
+}
+
+void ArmToAarch64Assembler::STM(int cc, int dir,
+        int Rn, int W, uint32_t reg_list)
+{
+    const int XSP = 31;
+    if(cc != AL || dir != DB || W == 0 || Rn != SP)
+    {
+        NOT_IMPLEMENTED();
+        return;
+    }
+
+    for(int i = 31; i >= 0; --i)
+    {
+        if((reg_list & (1 << i)))
+        {
+            int size = -16;
+            int reg  = i;
+            *mPC++ = A64_STR_IMM_PreIndex(reg, XSP, size);
+        }
+    }
+}
+
+// ----------------------------------------------------------------------------
+// special...
+// ----------------------------------------------------------------------------
+void ArmToAarch64Assembler::SWP(int cc, int Rn, int Rd, int Rm)
+{
+    NOT_IMPLEMENTED(); //Not required
+}
+void ArmToAarch64Assembler::SWPB(int cc, int Rn, int Rd, int Rm)
+{
+    NOT_IMPLEMENTED(); //Not required
+}
+void ArmToAarch64Assembler::SWI(int cc, uint32_t comment)
+{
+    NOT_IMPLEMENTED(); //Not required
+}
+
+// ----------------------------------------------------------------------------
+// DSP instructions...
+// ----------------------------------------------------------------------------
+void ArmToAarch64Assembler::PLD(int Rn, uint32_t offset) {
+    NOT_IMPLEMENTED(); //Not required
+}
+
+void ArmToAarch64Assembler::CLZ(int cc, int Rd, int Rm)
+{
+    NOT_IMPLEMENTED(); //Not required
+}
+
+void ArmToAarch64Assembler::QADD(int cc,  int Rd, int Rm, int Rn)
+{
+    NOT_IMPLEMENTED(); //Not required
+}
+
+void ArmToAarch64Assembler::QDADD(int cc,  int Rd, int Rm, int Rn)
+{
+    NOT_IMPLEMENTED(); //Not required
+}
+
+void ArmToAarch64Assembler::QSUB(int cc,  int Rd, int Rm, int Rn)
+{
+    NOT_IMPLEMENTED(); //Not required
+}
+
+void ArmToAarch64Assembler::QDSUB(int cc,  int Rd, int Rm, int Rn)
+{
+    NOT_IMPLEMENTED(); //Not required
+}
+
+// ----------------------------------------------------------------------------
+// 16 x 16 multiplication
+// ----------------------------------------------------------------------------
+void ArmToAarch64Assembler::SMUL(int cc, int xy,
+                int Rd, int Rm, int Rs)
+{
+    if(cc != AL){ NOT_IMPLEMENTED(); return;} //Not required
+
+    if (xy & xyTB)
+        *mPC++ = A64_SBFM_W(mTmpReg1, Rm, 16, 31);
+    else
+        *mPC++ = A64_SBFM_W(mTmpReg1, Rm, 0, 15);
+
+    if (xy & xyBT)
+        *mPC++ = A64_SBFM_W(mTmpReg2, Rs, 16, 31);
+    else
+        *mPC++ = A64_SBFM_W(mTmpReg2, Rs, 0, 15);
+
+    *mPC++ = A64_MADD_W(Rd,mTmpReg1,mTmpReg2, mZeroReg);
+}
+// ----------------------------------------------------------------------------
+// 32 x 16 multiplication
+// ----------------------------------------------------------------------------
+void ArmToAarch64Assembler::SMULW(int cc, int y, int Rd, int Rm, int Rs)
+{
+    if(cc != AL){ NOT_IMPLEMENTED(); return;} //Not required
+
+    if (y & yT)
+        *mPC++ = A64_SBFM_W(mTmpReg1, Rs, 16, 31);
+    else
+        *mPC++ = A64_SBFM_W(mTmpReg1, Rs, 0, 15);
+
+    *mPC++ = A64_SBFM_W(mTmpReg2, Rm, 0, 31);
+    *mPC++ = A64_SMADDL(mTmpReg3,mTmpReg1,mTmpReg2, mZeroReg);
+    *mPC++ = A64_UBFM_X(Rd,mTmpReg3, 16, 47);
+}
+// ----------------------------------------------------------------------------
+// 16 x 16 multiplication and accumulate
+// ----------------------------------------------------------------------------
+void ArmToAarch64Assembler::SMLA(int cc, int xy, int Rd, int Rm, int Rs, int Rn)
+{
+    if(cc != AL){ NOT_IMPLEMENTED(); return;} //Not required
+    if(xy != xyBB) { NOT_IMPLEMENTED(); return;} //Not required
+
+    *mPC++ = A64_SBFM_W(mTmpReg1, Rm, 0, 15);
+    *mPC++ = A64_SBFM_W(mTmpReg2, Rs, 0, 15);
+    *mPC++ = A64_MADD_W(Rd, mTmpReg1, mTmpReg2, Rn);
+}
+
+void ArmToAarch64Assembler::SMLAL(int cc, int xy,
+                int RdHi, int RdLo, int Rs, int Rm)
+{
+    NOT_IMPLEMENTED(); //Not required
+    return;
+}
+
+void ArmToAarch64Assembler::SMLAW(int cc, int y,
+                int Rd, int Rm, int Rs, int Rn)
+{
+    NOT_IMPLEMENTED(); //Not required
+    return;
+}
+
+// ----------------------------------------------------------------------------
+// Byte/half word extract and extend
+// ----------------------------------------------------------------------------
+void ArmToAarch64Assembler::UXTB16(int cc, int Rd, int Rm, int rotate)
+{
+    if(cc != AL){ NOT_IMPLEMENTED(); return;} //Not required
+
+    *mPC++ = A64_EXTR_W(mTmpReg1, Rm, Rm, rotate * 8);
+
+    uint32_t imm = 0x00FF00FF;
+    *mPC++ = A64_MOVZ_W(mTmpReg2, imm & 0xFFFF, 0);
+    *mPC++ = A64_MOVK_W(mTmpReg2, (imm >> 16) & 0x0000FFFF, 16);
+    *mPC++ = A64_AND_W(Rd,mTmpReg1, mTmpReg2);
+}
+
+// ----------------------------------------------------------------------------
+// Bit manipulation
+// ----------------------------------------------------------------------------
+void ArmToAarch64Assembler::UBFX(int cc, int Rd, int Rn, int lsb, int width)
+{
+    if(cc != AL){ NOT_IMPLEMENTED(); return;} //Not required
+    *mPC++ = A64_UBFM_W(Rd, Rn, lsb, lsb + width - 1);
+}
+// ----------------------------------------------------------------------------
+// Shifters...
+// ----------------------------------------------------------------------------
+int ArmToAarch64Assembler::buildImmediate(
+        uint32_t immediate, uint32_t& rot, uint32_t& imm)
+{
+    rot = 0;
+    imm = immediate;
+    return 0; // Always true
+}
+
+
+bool ArmToAarch64Assembler::isValidImmediate(uint32_t immediate)
+{
+    uint32_t rot, imm;
+    return buildImmediate(immediate, rot, imm) == 0;
+}
+
+uint32_t ArmToAarch64Assembler::imm(uint32_t immediate)
+{
+    mAddrMode.immediate = immediate;
+    mAddrMode.writeback = false;
+    mAddrMode.preindex  = false;
+    mAddrMode.postindex = false;
+    return OPERAND_IMM;
+
+}
+
+uint32_t ArmToAarch64Assembler::reg_imm(int Rm, int type, uint32_t shift)
+{
+    mAddrMode.reg_imm_Rm = Rm;
+    mAddrMode.reg_imm_type = type;
+    mAddrMode.reg_imm_shift = shift;
+    return OPERAND_REG_IMM;
+}
+
+uint32_t ArmToAarch64Assembler::reg_rrx(int Rm)
+{
+    NOT_IMPLEMENTED();
+    return OPERAND_UNSUPPORTED;
+}
+
+uint32_t ArmToAarch64Assembler::reg_reg(int Rm, int type, int Rs)
+{
+    NOT_IMPLEMENTED(); //Not required
+    return OPERAND_UNSUPPORTED;
+}
+// ----------------------------------------------------------------------------
+// Addressing modes...
+// ----------------------------------------------------------------------------
+uint32_t ArmToAarch64Assembler::immed12_pre(int32_t immed12, int W)
+{
+    mAddrMode.immediate = immed12;
+    mAddrMode.writeback = W;
+    mAddrMode.preindex  = true;
+    mAddrMode.postindex = false;
+    return OPERAND_IMM;
+}
+
+uint32_t ArmToAarch64Assembler::immed12_post(int32_t immed12)
+{
+    mAddrMode.immediate = immed12;
+    mAddrMode.writeback = true;
+    mAddrMode.preindex  = false;
+    mAddrMode.postindex = true;
+    return OPERAND_IMM;
+}
+
+uint32_t ArmToAarch64Assembler::reg_scale_pre(int Rm, int type,
+        uint32_t shift, int W)
+{
+    if(type != 0 || shift != 0 || W != 0)
+    {
+        NOT_IMPLEMENTED(); //Not required
+        return OPERAND_UNSUPPORTED;
+    }
+    else
+    {
+        mAddrMode.reg_offset = Rm;
+        return OPERAND_REG_OFFSET;
+    }
+}
+
+uint32_t ArmToAarch64Assembler::reg_scale_post(int Rm, int type, uint32_t shift)
+{
+    NOT_IMPLEMENTED(); //Not required
+    return OPERAND_UNSUPPORTED;
+}
+
+uint32_t ArmToAarch64Assembler::immed8_pre(int32_t immed8, int W)
+{
+    mAddrMode.immediate = immed8;
+    mAddrMode.writeback = W;
+    mAddrMode.preindex  = true;
+    mAddrMode.postindex = false;
+    return OPERAND_IMM;
+}
+
+uint32_t ArmToAarch64Assembler::immed8_post(int32_t immed8)
+{
+    mAddrMode.immediate = immed8;
+    mAddrMode.writeback = true;
+    mAddrMode.preindex  = false;
+    mAddrMode.postindex = true;
+    return OPERAND_IMM;
+}
+
+uint32_t ArmToAarch64Assembler::reg_pre(int Rm, int W)
+{
+    if(W != 0)
+    {
+        NOT_IMPLEMENTED(); //Not required
+        return OPERAND_UNSUPPORTED;
+    }
+    else
+    {
+        mAddrMode.reg_offset = Rm;
+        return OPERAND_REG_OFFSET;
+    }
+}
+
+uint32_t ArmToAarch64Assembler::reg_post(int Rm)
+{
+    NOT_IMPLEMENTED(); //Not required
+    return OPERAND_UNSUPPORTED;
+}
+
+// ----------------------------------------------------------------------------
+// A64 instructions
+// ----------------------------------------------------------------------------
+
+static const char * dataTransferOpName[] =
+{
+    "LDR","LDRB","LDRH","STR","STRB","STRH"
+};
+
+static const uint32_t dataTransferOpCode [] =
+{
+    ((0xB8u << 24) | (0x3 << 21) | (0x6 << 13) | (0x0 << 12) |(0x1 << 11)),
+    ((0x38u << 24) | (0x3 << 21) | (0x6 << 13) | (0x1 << 12) |(0x1 << 11)),
+    ((0x78u << 24) | (0x3 << 21) | (0x6 << 13) | (0x0 << 12) |(0x1 << 11)),
+    ((0xB8u << 24) | (0x1 << 21) | (0x6 << 13) | (0x0 << 12) |(0x1 << 11)),
+    ((0x38u << 24) | (0x1 << 21) | (0x6 << 13) | (0x1 << 12) |(0x1 << 11)),
+    ((0x78u << 24) | (0x1 << 21) | (0x6 << 13) | (0x0 << 12) |(0x1 << 11))
+};
+uint32_t ArmToAarch64Assembler::A64_LDRSTR_Wm_SXTW_0(uint32_t op,
+                            uint32_t size, uint32_t Rt,
+                            uint32_t Rn, uint32_t Rm)
+{
+    if(size == 32)
+    {
+        LOG_INSTR("%s W%d, [X%d, W%d, SXTW #0]\n",
+                   dataTransferOpName[op], Rt, Rn, Rm);
+        return(dataTransferOpCode[op] | (Rm << 16) | (Rn << 5) | Rt);
+    }
+    else
+    {
+        LOG_INSTR("%s X%d, [X%d, W%d, SXTW #0]\n",
+                  dataTransferOpName[op], Rt, Rn, Rm);
+        return(dataTransferOpCode[op] | (0x1<<30) | (Rm<<16) | (Rn<<5)|Rt);
+    }
+}
+
+uint32_t ArmToAarch64Assembler::A64_STR_IMM_PreIndex(uint32_t Rt,
+                            uint32_t Rn, int32_t simm)
+{
+    if(Rn == 31)
+        LOG_INSTR("STR W%d, [SP, #%d]!\n", Rt, simm);
+    else
+        LOG_INSTR("STR W%d, [X%d, #%d]!\n", Rt, Rn, simm);
+
+    uint32_t imm9 = (unsigned)(simm) & 0x01FF;
+    return (0xB8 << 24) | (imm9 << 12) | (0x3 << 10) | (Rn << 5) | Rt;
+}
+
+uint32_t ArmToAarch64Assembler::A64_LDR_IMM_PostIndex(uint32_t Rt,
+                            uint32_t Rn, int32_t simm)
+{
+    if(Rn == 31)
+        LOG_INSTR("LDR W%d, [SP], #%d\n",Rt,simm);
+    else
+        LOG_INSTR("LDR W%d, [X%d], #%d\n",Rt, Rn, simm);
+
+    uint32_t imm9 = (unsigned)(simm) & 0x01FF;
+    return (0xB8 << 24) | (0x1 << 22) |
+             (imm9 << 12) | (0x1 << 10) | (Rn << 5) | Rt;
+
+}
+uint32_t ArmToAarch64Assembler::A64_ADD_X_Wm_SXTW(uint32_t Rd,
+                               uint32_t Rn,
+                               uint32_t Rm,
+                               uint32_t amount)
+{
+    LOG_INSTR("ADD X%d, X%d, W%d, SXTW #%d\n", Rd, Rn, Rm, amount);
+    return ((0x8B << 24) | (0x1 << 21) |(Rm << 16) |
+              (0x6 << 13) | (amount << 10) | (Rn << 5) | Rd);
+
+}
+
+uint32_t ArmToAarch64Assembler::A64_SUB_X_Wm_SXTW(uint32_t Rd,
+                               uint32_t Rn,
+                               uint32_t Rm,
+                               uint32_t amount)
+{
+    LOG_INSTR("SUB X%d, X%d, W%d, SXTW #%d\n", Rd, Rn, Rm, amount);
+    return ((0xCB << 24) | (0x1 << 21) |(Rm << 16) |
+            (0x6 << 13) | (amount << 10) | (Rn << 5) | Rd);
+
+}
+
+uint32_t ArmToAarch64Assembler::A64_B_COND(uint32_t cc, uint32_t offset)
+{
+    LOG_INSTR("B.%s #.+%d\n", cc_codes[cc], offset);
+    return (0x54 << 24) | ((offset/4) << 5) | (cc);
+
+}
+uint32_t ArmToAarch64Assembler::A64_ADD_X(uint32_t Rd, uint32_t Rn,
+                                          uint32_t Rm, uint32_t shift,
+                                          uint32_t amount)
+{
+    LOG_INSTR("ADD X%d, X%d, X%d, %s #%d\n",
+               Rd, Rn, Rm, shift_codes[shift], amount);
+    return ((0x8B << 24) | (shift << 22) | ( Rm << 16) |
+            (amount << 10) |(Rn << 5) | Rd);
+}
+uint32_t ArmToAarch64Assembler::A64_ADD_IMM_X(uint32_t Rd, uint32_t Rn,
+                                          uint32_t imm, uint32_t shift)
+{
+    LOG_INSTR("ADD X%d, X%d, #%d, LSL #%d\n", Rd, Rn, imm, shift);
+    return (0x91 << 24) | ((shift/12) << 22) | (imm << 10) | (Rn << 5) | Rd;
+}
+
+uint32_t ArmToAarch64Assembler::A64_SUB_IMM_X(uint32_t Rd, uint32_t Rn,
+                                          uint32_t imm, uint32_t shift)
+{
+    LOG_INSTR("SUB X%d, X%d, #%d, LSL #%d\n", Rd, Rn, imm, shift);
+    return (0xD1 << 24) | ((shift/12) << 22) | (imm << 10) | (Rn << 5) | Rd;
+}
+
+uint32_t ArmToAarch64Assembler::A64_ADD_W(uint32_t Rd, uint32_t Rn,
+                                          uint32_t Rm, uint32_t shift,
+                                          uint32_t amount)
+{
+    LOG_INSTR("ADD W%d, W%d, W%d, %s #%d\n",
+               Rd, Rn, Rm, shift_codes[shift], amount);
+    return ((0x0B << 24) | (shift << 22) | ( Rm << 16) |
+            (amount << 10) |(Rn << 5) | Rd);
+}
+
+uint32_t ArmToAarch64Assembler::A64_SUB_W(uint32_t Rd, uint32_t Rn,
+                                          uint32_t Rm, uint32_t shift,
+                                          uint32_t amount,
+                                          uint32_t setflag)
+{
+    if(setflag == 0)
+    {
+        LOG_INSTR("SUB W%d, W%d, W%d, %s #%d\n",
+               Rd, Rn, Rm, shift_codes[shift], amount);
+        return ((0x4B << 24) | (shift << 22) | ( Rm << 16) |
+                (amount << 10) |(Rn << 5) | Rd);
+    }
+    else
+    {
+        LOG_INSTR("SUBS W%d, W%d, W%d, %s #%d\n",
+                   Rd, Rn, Rm, shift_codes[shift], amount);
+        return ((0x6B << 24) | (shift << 22) | ( Rm << 16) |
+                (amount << 10) |(Rn << 5) | Rd);
+    }
+}
+
+uint32_t ArmToAarch64Assembler::A64_AND_W(uint32_t Rd, uint32_t Rn,
+                                          uint32_t Rm, uint32_t shift,
+                                          uint32_t amount)
+{
+    LOG_INSTR("AND W%d, W%d, W%d, %s #%d\n",
+               Rd, Rn, Rm, shift_codes[shift], amount);
+    return ((0x0A << 24) | (shift << 22) | ( Rm << 16) |
+            (amount << 10) |(Rn << 5) | Rd);
+}
+
+uint32_t ArmToAarch64Assembler::A64_ORR_W(uint32_t Rd, uint32_t Rn,
+                                          uint32_t Rm, uint32_t shift,
+                                          uint32_t amount)
+{
+    LOG_INSTR("ORR W%d, W%d, W%d, %s #%d\n",
+               Rd, Rn, Rm, shift_codes[shift], amount);
+    return ((0x2A << 24) | (shift << 22) | ( Rm << 16) |
+            (amount << 10) |(Rn << 5) | Rd);
+}
+
+uint32_t ArmToAarch64Assembler::A64_ORN_W(uint32_t Rd, uint32_t Rn,
+                                          uint32_t Rm, uint32_t shift,
+                                          uint32_t amount)
+{
+    LOG_INSTR("ORN W%d, W%d, W%d, %s #%d\n",
+               Rd, Rn, Rm, shift_codes[shift], amount);
+    return ((0x2A << 24) | (shift << 22) | (0x1 << 21) | ( Rm << 16) |
+            (amount << 10) |(Rn << 5) | Rd);
+}
+
+uint32_t ArmToAarch64Assembler::A64_CSEL_X(uint32_t Rd, uint32_t Rn,
+                                           uint32_t Rm, uint32_t cond)
+{
+    LOG_INSTR("CSEL X%d, X%d, X%d, %s\n", Rd, Rn, Rm, cc_codes[cond]);
+    return ((0x9A << 24)|(0x1 << 23)|(Rm << 16) |(cond << 12)| (Rn << 5) | Rd);
+}
+
+uint32_t ArmToAarch64Assembler::A64_CSEL_W(uint32_t Rd, uint32_t Rn,
+                                           uint32_t Rm, uint32_t cond)
+{
+    LOG_INSTR("CSEL W%d, W%d, W%d, %s\n", Rd, Rn, Rm, cc_codes[cond]);
+    return ((0x1A << 24)|(0x1 << 23)|(Rm << 16) |(cond << 12)| (Rn << 5) | Rd);
+}
+
+uint32_t ArmToAarch64Assembler::A64_RET(uint32_t Rn)
+{
+    LOG_INSTR("RET X%d\n", Rn);
+    return ((0xD6 << 24) | (0x1 << 22) | (0x1F << 16) | (Rn << 5));
+}
+
+uint32_t ArmToAarch64Assembler::A64_MOVZ_X(uint32_t Rd, uint32_t imm,
+                                         uint32_t shift)
+{
+    LOG_INSTR("MOVZ X%d, #0x%x, LSL #%d\n", Rd, imm, shift);
+    return(0xD2 << 24) | (0x1 << 23) | ((shift/16) << 21) |  (imm << 5) | Rd;
+}
+
+uint32_t ArmToAarch64Assembler::A64_MOVK_W(uint32_t Rd, uint32_t imm,
+                                         uint32_t shift)
+{
+    LOG_INSTR("MOVK W%d, #0x%x, LSL #%d\n", Rd, imm, shift);
+    return (0x72 << 24) | (0x1 << 23) | ((shift/16) << 21) | (imm << 5) | Rd;
+}
+
+uint32_t ArmToAarch64Assembler::A64_MOVZ_W(uint32_t Rd, uint32_t imm,
+                                         uint32_t shift)
+{
+    LOG_INSTR("MOVZ W%d, #0x%x, LSL #%d\n", Rd, imm, shift);
+    return(0x52 << 24) | (0x1 << 23) | ((shift/16) << 21) |  (imm << 5) | Rd;
+}
+
+uint32_t ArmToAarch64Assembler::A64_SMADDL(uint32_t Rd, uint32_t Rn,
+                                           uint32_t Rm, uint32_t Ra)
+{
+    LOG_INSTR("SMADDL X%d, W%d, W%d, X%d\n",Rd, Rn, Rm, Ra);
+    return ((0x9B << 24) | (0x1 << 21) | (Rm << 16)|(Ra << 10)|(Rn << 5) | Rd);
+}
+
+uint32_t ArmToAarch64Assembler::A64_MADD_W(uint32_t Rd, uint32_t Rn,
+                                           uint32_t Rm, uint32_t Ra)
+{
+    LOG_INSTR("MADD W%d, W%d, W%d, W%d\n",Rd, Rn, Rm, Ra);
+    return ((0x1B << 24) | (Rm << 16) | (Ra << 10) |(Rn << 5) | Rd);
+}
+
+uint32_t ArmToAarch64Assembler::A64_SBFM_W(uint32_t Rd, uint32_t Rn,
+                                           uint32_t immr, uint32_t imms)
+{
+    LOG_INSTR("SBFM W%d, W%d, #%d, #%d\n", Rd, Rn, immr, imms);
+    return ((0x13 << 24) | (immr << 16) | (imms << 10) | (Rn << 5) | Rd);
+
+}
+uint32_t ArmToAarch64Assembler::A64_UBFM_W(uint32_t Rd, uint32_t Rn,
+                                           uint32_t immr, uint32_t imms)
+{
+    LOG_INSTR("UBFM W%d, W%d, #%d, #%d\n", Rd, Rn, immr, imms);
+    return ((0x53 << 24) | (immr << 16) | (imms << 10) | (Rn << 5) | Rd);
+
+}
+uint32_t ArmToAarch64Assembler::A64_UBFM_X(uint32_t Rd, uint32_t Rn,
+                                           uint32_t immr, uint32_t imms)
+{
+    LOG_INSTR("UBFM X%d, X%d, #%d, #%d\n", Rd, Rn, immr, imms);
+    return ((0xD3 << 24) | (0x1 << 22) |
+            (immr << 16) | (imms << 10) | (Rn << 5) | Rd);
+
+}
+uint32_t ArmToAarch64Assembler::A64_EXTR_W(uint32_t Rd, uint32_t Rn,
+                                           uint32_t Rm, uint32_t lsb)
+{
+    LOG_INSTR("EXTR W%d, W%d, W%d, #%d\n", Rd, Rn, Rm, lsb);
+    return (0x13 << 24)|(0x1 << 23) | (Rm << 16) | (lsb << 10)|(Rn << 5) | Rd;
+}
+
+}; // namespace android
+
diff --git a/libpixelflinger/codeflinger/Aarch64Assembler.h b/libpixelflinger/codeflinger/Aarch64Assembler.h
new file mode 100644
index 0000000..79c912b
--- /dev/null
+++ b/libpixelflinger/codeflinger/Aarch64Assembler.h
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef ANDROID_ARMTOAARCH64ASSEMBLER_H
+#define ANDROID_ARMTOAARCH64ASSEMBLER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "tinyutils/Vector.h"
+#include "tinyutils/KeyedVector.h"
+#include "tinyutils/smartpointer.h"
+
+#include "tinyutils/smartpointer.h"
+#include "codeflinger/ARMAssemblerInterface.h"
+#include "codeflinger/CodeCache.h"
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+class ArmToAarch64Assembler : public ARMAssemblerInterface
+{
+public:
+                ArmToAarch64Assembler(const sp<Assembly>& assembly);
+                ArmToAarch64Assembler(void *base);
+    virtual     ~ArmToAarch64Assembler();
+
+    uint32_t*   base() const;
+    uint32_t*   pc() const;
+
+
+    void        disassemble(const char* name);
+
+    // ------------------------------------------------------------------------
+    // ARMAssemblerInterface...
+    // ------------------------------------------------------------------------
+
+    virtual void    reset();
+
+    virtual int     generate(const char* name);
+    virtual int     getCodegenArch();
+
+    virtual void    prolog();
+    virtual void    epilog(uint32_t touched);
+    virtual void    comment(const char* string);
+
+
+    // -----------------------------------------------------------------------
+    // shifters and addressing modes
+    // -----------------------------------------------------------------------
+
+    // shifters...
+    virtual bool        isValidImmediate(uint32_t immed);
+    virtual int         buildImmediate(uint32_t i, uint32_t& rot, uint32_t& imm);
+
+    virtual uint32_t    imm(uint32_t immediate);
+    virtual uint32_t    reg_imm(int Rm, int type, uint32_t shift);
+    virtual uint32_t    reg_rrx(int Rm);
+    virtual uint32_t    reg_reg(int Rm, int type, int Rs);
+
+    // addressing modes...
+    virtual uint32_t    immed12_pre(int32_t immed12, int W=0);
+    virtual uint32_t    immed12_post(int32_t immed12);
+    virtual uint32_t    reg_scale_pre(int Rm, int type=0, uint32_t shift=0, int W=0);
+    virtual uint32_t    reg_scale_post(int Rm, int type=0, uint32_t shift=0);
+    virtual uint32_t    immed8_pre(int32_t immed8, int W=0);
+    virtual uint32_t    immed8_post(int32_t immed8);
+    virtual uint32_t    reg_pre(int Rm, int W=0);
+    virtual uint32_t    reg_post(int Rm);
+
+
+    virtual void    dataProcessing(int opcode, int cc, int s,
+                                int Rd, int Rn,
+                                uint32_t Op2);
+    virtual void MLA(int cc, int s,
+                int Rd, int Rm, int Rs, int Rn);
+    virtual void MUL(int cc, int s,
+                int Rd, int Rm, int Rs);
+    virtual void UMULL(int cc, int s,
+                int RdLo, int RdHi, int Rm, int Rs);
+    virtual void UMUAL(int cc, int s,
+                int RdLo, int RdHi, int Rm, int Rs);
+    virtual void SMULL(int cc, int s,
+                int RdLo, int RdHi, int Rm, int Rs);
+    virtual void SMUAL(int cc, int s,
+                int RdLo, int RdHi, int Rm, int Rs);
+
+    virtual void B(int cc, uint32_t* pc);
+    virtual void BL(int cc, uint32_t* pc);
+    virtual void BX(int cc, int Rn);
+    virtual void label(const char* theLabel);
+    virtual void B(int cc, const char* label);
+    virtual void BL(int cc, const char* label);
+
+    virtual uint32_t* pcForLabel(const char* label);
+
+    virtual void ADDR_LDR(int cc, int Rd,
+                int Rn, uint32_t offset = 0);
+    virtual void ADDR_ADD(int cc, int s, int Rd,
+                int Rn, uint32_t Op2);
+    virtual void ADDR_SUB(int cc, int s, int Rd,
+                int Rn, uint32_t Op2);
+    virtual void ADDR_STR (int cc, int Rd,
+                int Rn, uint32_t offset = 0);
+
+    virtual void LDR (int cc, int Rd,
+                int Rn, uint32_t offset = 0);
+    virtual void LDRB(int cc, int Rd,
+                int Rn, uint32_t offset = 0);
+    virtual void STR (int cc, int Rd,
+                int Rn, uint32_t offset = 0);
+    virtual void STRB(int cc, int Rd,
+                int Rn, uint32_t offset = 0);
+    virtual void LDRH (int cc, int Rd,
+                int Rn, uint32_t offset = 0);
+    virtual void LDRSB(int cc, int Rd,
+                int Rn, uint32_t offset = 0);
+    virtual void LDRSH(int cc, int Rd,
+                int Rn, uint32_t offset = 0);
+    virtual void STRH (int cc, int Rd,
+                int Rn, uint32_t offset = 0);
+
+
+    virtual void LDM(int cc, int dir,
+                int Rn, int W, uint32_t reg_list);
+    virtual void STM(int cc, int dir,
+                int Rn, int W, uint32_t reg_list);
+
+    virtual void SWP(int cc, int Rn, int Rd, int Rm);
+    virtual void SWPB(int cc, int Rn, int Rd, int Rm);
+    virtual void SWI(int cc, uint32_t comment);
+
+    virtual void PLD(int Rn, uint32_t offset);
+    virtual void CLZ(int cc, int Rd, int Rm);
+    virtual void QADD(int cc, int Rd, int Rm, int Rn);
+    virtual void QDADD(int cc, int Rd, int Rm, int Rn);
+    virtual void QSUB(int cc, int Rd, int Rm, int Rn);
+    virtual void QDSUB(int cc, int Rd, int Rm, int Rn);
+    virtual void SMUL(int cc, int xy,
+                int Rd, int Rm, int Rs);
+    virtual void SMULW(int cc, int y,
+                int Rd, int Rm, int Rs);
+    virtual void SMLA(int cc, int xy,
+                int Rd, int Rm, int Rs, int Rn);
+    virtual void SMLAL(int cc, int xy,
+                int RdHi, int RdLo, int Rs, int Rm);
+    virtual void SMLAW(int cc, int y,
+                int Rd, int Rm, int Rs, int Rn);
+    virtual void UXTB16(int cc, int Rd, int Rm, int rotate);
+    virtual void UBFX(int cc, int Rd, int Rn, int lsb, int width);
+
+private:
+    ArmToAarch64Assembler(const ArmToAarch64Assembler& rhs);
+    ArmToAarch64Assembler& operator = (const ArmToAarch64Assembler& rhs);
+
+    // -----------------------------------------------------------------------
+    // helper functions
+    // -----------------------------------------------------------------------
+
+    void dataTransfer(int operation, int cc, int Rd, int Rn,
+                      uint32_t operand_type, uint32_t size = 32);
+    void dataProcessingCommon(int opcode, int s,
+                      int Rd, int Rn, uint32_t Op2);
+
+    // -----------------------------------------------------------------------
+    // Aarch64 instructions
+    // -----------------------------------------------------------------------
+    uint32_t A64_B_COND(uint32_t cc, uint32_t offset);
+    uint32_t A64_RET(uint32_t Rn);
+
+    uint32_t A64_LDRSTR_Wm_SXTW_0(uint32_t operation,
+                                uint32_t size, uint32_t Rt,
+                                uint32_t Rn, uint32_t Rm);
+
+    uint32_t A64_STR_IMM_PreIndex(uint32_t Rt, uint32_t Rn, int32_t simm);
+    uint32_t A64_LDR_IMM_PostIndex(uint32_t Rt,uint32_t Rn, int32_t simm);
+
+    uint32_t A64_ADD_X_Wm_SXTW(uint32_t Rd, uint32_t Rn, uint32_t Rm,
+                               uint32_t amount);
+    uint32_t A64_SUB_X_Wm_SXTW(uint32_t Rd, uint32_t Rn, uint32_t Rm,
+                               uint32_t amount);
+
+    uint32_t A64_ADD_IMM_X(uint32_t Rd, uint32_t Rn,
+                           uint32_t imm, uint32_t shift = 0);
+    uint32_t A64_SUB_IMM_X(uint32_t Rd, uint32_t Rn,
+                           uint32_t imm, uint32_t shift = 0);
+
+    uint32_t A64_ADD_X(uint32_t Rd, uint32_t Rn,
+                       uint32_t Rm, uint32_t shift = 0, uint32_t amount = 0);
+    uint32_t A64_ADD_W(uint32_t Rd, uint32_t Rn, uint32_t Rm,
+                       uint32_t shift = 0, uint32_t amount = 0);
+    uint32_t A64_SUB_W(uint32_t Rd, uint32_t Rn, uint32_t Rm,
+                       uint32_t shift = 0, uint32_t amount = 0,
+                       uint32_t setflag = 0);
+    uint32_t A64_AND_W(uint32_t Rd, uint32_t Rn,
+                       uint32_t Rm, uint32_t shift = 0, uint32_t amount = 0);
+    uint32_t A64_ORR_W(uint32_t Rd, uint32_t Rn,
+                       uint32_t Rm, uint32_t shift = 0, uint32_t amount = 0);
+    uint32_t A64_ORN_W(uint32_t Rd, uint32_t Rn,
+                       uint32_t Rm, uint32_t shift = 0, uint32_t amount = 0);
+
+    uint32_t A64_MOVZ_W(uint32_t Rd, uint32_t imm, uint32_t shift);
+    uint32_t A64_MOVZ_X(uint32_t Rd, uint32_t imm, uint32_t shift);
+    uint32_t A64_MOVK_W(uint32_t Rd, uint32_t imm, uint32_t shift);
+
+    uint32_t A64_SMADDL(uint32_t Rd, uint32_t Rn, uint32_t Rm, uint32_t Ra);
+    uint32_t A64_MADD_W(uint32_t Rd, uint32_t Rn, uint32_t Rm, uint32_t Ra);
+
+    uint32_t A64_SBFM_W(uint32_t Rd, uint32_t Rn,
+                        uint32_t immr, uint32_t imms);
+    uint32_t A64_UBFM_W(uint32_t Rd, uint32_t Rn,
+                        uint32_t immr, uint32_t imms);
+    uint32_t A64_UBFM_X(uint32_t Rd, uint32_t Rn,
+                        uint32_t immr, uint32_t imms);
+
+    uint32_t A64_EXTR_W(uint32_t Rd, uint32_t Rn, uint32_t Rm, uint32_t lsb);
+    uint32_t A64_CSEL_X(uint32_t Rd, uint32_t Rn, uint32_t Rm, uint32_t cond);
+    uint32_t A64_CSEL_W(uint32_t Rd, uint32_t Rn, uint32_t Rm, uint32_t cond);
+
+    uint32_t*       mBase;
+    uint32_t*       mPC;
+    uint32_t*       mPrologPC;
+    int64_t         mDuration;
+    uint32_t        mTmpReg1, mTmpReg2, mTmpReg3, mZeroReg;
+
+    struct branch_target_t {
+        inline branch_target_t() : label(0), pc(0) { }
+        inline branch_target_t(const char* l, uint32_t* p)
+            : label(l), pc(p) { }
+        const char* label;
+        uint32_t*   pc;
+    };
+
+    sp<Assembly>    mAssembly;
+    Vector<branch_target_t>                 mBranchTargets;
+    KeyedVector< const char*, uint32_t* >   mLabels;
+    KeyedVector< uint32_t*, const char* >   mLabelsInverseMapping;
+    KeyedVector< uint32_t*, const char* >   mComments;
+
+    enum operand_type_t
+    {
+        OPERAND_REG = 0x20,
+        OPERAND_IMM,
+        OPERAND_REG_IMM,
+        OPERAND_REG_OFFSET,
+        OPERAND_UNSUPPORTED
+    };
+
+    struct addr_mode_t {
+        int32_t         immediate;
+        bool            writeback;
+        bool            preindex;
+        bool            postindex;
+        int32_t         reg_imm_Rm;
+        int32_t         reg_imm_type;
+        uint32_t        reg_imm_shift;
+        int32_t         reg_offset;
+    } mAddrMode;
+
+};
+
+}; // namespace android
+
+#endif //ANDROID_AARCH64ASSEMBLER_H
diff --git a/libpixelflinger/codeflinger/Aarch64Disassembler.cpp b/libpixelflinger/codeflinger/Aarch64Disassembler.cpp
new file mode 100644
index 0000000..4bb97b4
--- /dev/null
+++ b/libpixelflinger/codeflinger/Aarch64Disassembler.cpp
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <inttypes.h>
+#include <string.h>
+
+struct disasm_table_entry_t
+{
+    uint32_t       mask;
+    uint32_t       value;
+    const char*    instr_template;
+};
+
+
+static disasm_table_entry_t disasm_table[] =
+{
+    {0xff000000, 0x91000000, "add <xd|sp>, <xn|sp>, #<imm1>, <shift1>"},
+    {0xff000000, 0xd1000000, "sub <xd|sp>, <xn|sp>, #<imm1>, <shift1>"},
+    {0xff200000, 0x8b000000, "add <xd>, <xn>, <xm>, <shift2> #<amt1>"},
+    {0xff200000, 0x0b000000, "add <wd>, <wn>, <wm>, <shift2> #<amt1>"},
+    {0xff200000, 0x4b000000, "sub <wd>, <wn>, <wm>, <shift2> #<amt1>"},
+    {0xff200000, 0x6b000000, "subs <wd>, <wn>, <wm>, <shift2> #<amt1>"},
+    {0xff200000, 0x0a000000, "and <wd>, <wn>, <wm>, <shift2> #<amt1>"},
+    {0xff200000, 0x2a000000, "orr <wd>, <wn>, <wm>, <shift2> #<amt1>"},
+    {0xff200000, 0x2a200000, "orn <wd>, <wn>, <wm>, <shift2> #<amt1>"},
+    {0xff800000, 0x72800000, "movk <wd>, #<imm2>, lsl #<shift3>"},
+    {0xff800000, 0x52800000, "movz <wd>, #<imm2>, lsl #<shift3>"},
+    {0xff800000, 0xd2800000, "movz <xd>, #<imm2>, lsl #<shift3>"},
+    {0xffe00c00, 0x1a800000, "csel <wd>, <wn>, <wm>, <cond1>"},
+    {0xffe00c00, 0x9a800000, "csel <xd>, <xn>, <xm>, <cond1>"},
+    {0xffe00c00, 0x5a800000, "csinv <wd>, <wn>, <wm>, <cond1>"},
+    {0xffe08000, 0x1b000000, "madd <wd>, <wn>, <wm>, <wa>"},
+    {0xffe08000, 0x9b200000, "smaddl <xd>, <wn>, <wm>, <xa>"},
+    {0xffe04c00, 0xb8604800, "ldr <wt>, [<xn|sp>, <r1><m1>, <ext1> #<amt2>]"},
+    {0xffe04c00, 0xb8204800, "str <wt>, [<xn|sp>, <r1><m1>, <ext1> #<amt2>]"},
+    {0xffe04c00, 0xf8604800, "ldr <xt>, [<xn|sp>, <r1><m1>, <ext1> #<amt3>]"},
+    {0xffe04c00, 0xf8204800, "str <xt>, [<xn|sp>, <r1><m1>, <ext1> #<amt3>]"},
+    {0xffe04c00, 0x38604800, "ldrb <wt>, [<xn|sp>, <r1><m1>, <ext1> <amt5>]"},
+    {0xffe04c00, 0x38204800, "strb <wt>, [<xn|sp>, <r1><m1>, <ext1> <amt5>]"},
+    {0xffe04c00, 0x78604800, "ldrh <wt>, [<xn|sp>, <r1><m1>, <ext1> #<amt6>]"},
+    {0xffe04c00, 0x78204800, "strh <wt>, [<xn|sp>, <r1><m1>, <ext1> #<amt6>]"},
+    {0xffe00c00, 0xb8400400, "ldr <wt>, [<xn|sp>], #<simm1>"},
+    {0xffe00c00, 0xb8000c00, "str <wt>, [<xn|sp>, #<simm1>]!"},
+    {0xffc00000, 0x13000000, "sbfm <wd>, <wn>, #<immr1>, #<imms1>"},
+    {0xffc00000, 0x53000000, "ubfm <wd>, <wn>, #<immr1>, #<imms1>"},
+    {0xffc00000, 0xd3400000, "ubfm <xd>, <xn>, #<immr1>, #<imms1>"},
+    {0xffe00000, 0x13800000, "extr <wd>, <wn>, <wm>, #<lsb1>"},
+    {0xff000000, 0x54000000, "b.<cond2> <label1>"},
+    {0xfffffc1f, 0xd65f0000, "ret <xn>"},
+    {0xffe00000, 0x8b200000, "add <xd|sp>, <xn|sp>, <r2><m1>, <ext2> #<amt4>"},
+    {0xffe00000, 0xcb200000, "sub <xd|sp>, <xn|sp>, <r2><m1>, <ext2> #<amt4>"}
+};
+
+static int32_t bits_signed(uint32_t instr, uint32_t msb, uint32_t lsb)
+{
+    int32_t value;
+    value   = ((int32_t)instr) << (31 - msb);
+    value >>= (31 - msb);
+    value >>= lsb;
+    return value;
+}
+static uint32_t bits_unsigned(uint32_t instr, uint32_t msb, uint32_t lsb)
+{
+    uint32_t width = msb - lsb + 1;
+    uint32_t mask  = (1 << width) - 1;
+    return ((instr >> lsb) & mask);
+}
+
+static void get_token(const char *instr, uint32_t index, char *token)
+{
+    uint32_t i, j;
+    for(i = index, j = 0; i < strlen(instr); ++i)
+    {
+        if(instr[index] == '<' && instr[i] == '>')
+        {
+            token[j++] = instr[i];
+            break;
+        }
+        else if(instr[index] != '<' && instr[i] == '<')
+        {
+            break;
+        }
+        else
+        {
+            token[j++] = instr[i];
+        }
+    }
+    token[j] = '\0';
+    return;
+}
+
+
+static const char * token_cc_table[] =
+{
+    "eq", "ne", "cs", "cc", "mi",
+    "pl", "vs", "vc", "hi", "ls",
+    "ge", "lt", "gt", "le", "al", "nv"
+};
+
+static void decode_rx_zr_token(uint32_t reg, const char *prefix, char *instr_part)
+{
+    if(reg == 31)
+        sprintf(instr_part, "%s%s", prefix, "zr");
+    else
+        sprintf(instr_part, "%s%d", prefix, reg);
+}
+
+static void decode_token(uint32_t code, char *token, char *instr_part)
+{
+    if(strcmp(token, "<imm1>") == 0)
+        sprintf(instr_part, "0x%x", bits_unsigned(code, 21,10));
+    else if(strcmp(token, "<imm2>") == 0)
+        sprintf(instr_part, "0x%x", bits_unsigned(code, 20,5));
+    else if(strcmp(token, "<shift1>") == 0)
+        sprintf(instr_part, "lsl #%d", bits_unsigned(code, 23,22) * 12);
+    else if(strcmp(token, "<shift2>") == 0)
+    {
+        static const char * shift2_table[] = { "lsl", "lsr", "asr", "ror"};
+        sprintf(instr_part, "%s", shift2_table[bits_unsigned(code, 23,22)]);
+    }
+    else if(strcmp(token, "<shift3>") == 0)
+        sprintf(instr_part, "%d", bits_unsigned(code, 22,21) * 16);
+    else if(strcmp(token, "<amt1>") == 0)
+        sprintf(instr_part, "%d", bits_unsigned(code, 15,10));
+    else if(strcmp(token, "<amt2>") == 0)
+        sprintf(instr_part, "%d", bits_unsigned(code, 12,12) * 2);
+    else if(strcmp(token, "<amt3>") == 0)
+        sprintf(instr_part, "%d", bits_unsigned(code, 12,12) * 3);
+    else if(strcmp(token, "<amt4>") == 0)
+        sprintf(instr_part, "%d", bits_unsigned(code, 12,10));
+    else if(strcmp(token, "<amt5>") == 0)
+    {
+        static const char * amt5_table[] = {"", "#0"};
+        sprintf(instr_part, "%s", amt5_table[bits_unsigned(code, 12,12)]);
+    }
+    else if(strcmp(token, "<amt6>") == 0)
+        sprintf(instr_part, "%d", bits_unsigned(code, 12,12));
+    else if(strcmp(token, "<simm1>") == 0)
+        sprintf(instr_part, "%d", bits_signed(code, 20,12));
+    else if(strcmp(token, "<immr1>") == 0)
+        sprintf(instr_part, "%d", bits_unsigned(code, 21,16));
+    else if(strcmp(token, "<imms1>") == 0)
+        sprintf(instr_part, "%d", bits_unsigned(code, 15,10));
+    else if(strcmp(token, "<lsb1>") == 0)
+        sprintf(instr_part, "%d", bits_unsigned(code, 15,10));
+    else if(strcmp(token, "<cond1>") == 0)
+        sprintf(instr_part, "%s", token_cc_table[bits_unsigned(code, 15,12)]);
+    else if(strcmp(token, "<cond2>") == 0)
+        sprintf(instr_part, "%s", token_cc_table[bits_unsigned(code, 4,0)]);
+    else if(strcmp(token, "<r1>") == 0)
+    {
+        const char * token_r1_table[] =
+        {
+            "reserved", "reserved", "w", "x",
+            "reserved", "reserved", "w", "x"
+        };
+        sprintf(instr_part, "%s", token_r1_table[bits_unsigned(code, 15,13)]);
+    }
+    else if(strcmp(token, "<r2>") == 0)
+    {
+        static const char * token_r2_table[] =
+        {
+                "w","w","w", "x", "w", "w", "w", "x"
+        };
+        sprintf(instr_part, "%s", token_r2_table[bits_unsigned(code, 15,13)]);
+    }
+    else if(strcmp(token, "<m1>") == 0)
+    {
+        uint32_t reg = bits_unsigned(code, 20,16);
+        if(reg == 31)
+            sprintf(instr_part, "%s", "zr");
+        else
+            sprintf(instr_part, "%d", reg);
+    }
+    else if(strcmp(token, "<ext1>") == 0)
+    {
+        static const char * token_ext1_table[] =
+        {
+             "reserved","reserved","uxtw", "lsl",
+             "reserved","reserved", "sxtw", "sxtx"
+        };
+        sprintf(instr_part, "%s", token_ext1_table[bits_unsigned(code, 15,13)]);
+    }
+    else if(strcmp(token, "<ext2>") == 0)
+    {
+        static const char * token_ext2_table[] =
+        {
+                "uxtb","uxth","uxtw","uxtx",
+                "sxtb","sxth","sxtw","sxtx"
+        };
+        sprintf(instr_part, "%s", token_ext2_table[bits_unsigned(code, 15,13)]);
+    }
+    else if (strcmp(token, "<label1>") == 0)
+    {
+        int32_t offset = bits_signed(code, 23,5) * 4;
+        if(offset > 0)
+            sprintf(instr_part, "#.+%d", offset);
+        else
+            sprintf(instr_part, "#.-%d", -offset);
+    }
+    else if (strcmp(token, "<xn|sp>") == 0)
+    {
+        uint32_t reg = bits_unsigned(code, 9, 5);
+        if(reg == 31)
+            sprintf(instr_part, "%s", "sp");
+        else
+            sprintf(instr_part, "x%d", reg);
+    }
+    else if (strcmp(token, "<xd|sp>") == 0)
+    {
+        uint32_t reg = bits_unsigned(code, 4, 0);
+        if(reg == 31)
+            sprintf(instr_part, "%s", "sp");
+        else
+            sprintf(instr_part, "x%d", reg);
+    }
+    else if (strcmp(token, "<xn>") == 0)
+        decode_rx_zr_token(bits_unsigned(code, 9, 5), "x", instr_part);
+    else if (strcmp(token, "<xd>") == 0)
+        decode_rx_zr_token(bits_unsigned(code, 4, 0), "x", instr_part);
+    else if (strcmp(token, "<xm>") == 0)
+        decode_rx_zr_token(bits_unsigned(code, 20, 16), "x", instr_part);
+    else if (strcmp(token, "<xa>") == 0)
+        decode_rx_zr_token(bits_unsigned(code, 14, 10), "x", instr_part);
+    else if (strcmp(token, "<xt>") == 0)
+        decode_rx_zr_token(bits_unsigned(code, 4, 0), "x", instr_part);
+    else if (strcmp(token, "<wn>") == 0)
+        decode_rx_zr_token(bits_unsigned(code, 9, 5), "w", instr_part);
+    else if (strcmp(token, "<wd>") == 0)
+        decode_rx_zr_token(bits_unsigned(code, 4, 0), "w", instr_part);
+    else if (strcmp(token, "<wm>") == 0)
+        decode_rx_zr_token(bits_unsigned(code, 20, 16), "w", instr_part);
+    else if (strcmp(token, "<wa>") == 0)
+        decode_rx_zr_token(bits_unsigned(code, 14, 10), "w", instr_part);
+    else if (strcmp(token, "<wt>") == 0)
+        decode_rx_zr_token(bits_unsigned(code, 4, 0), "w", instr_part);
+    else
+    {
+        sprintf(instr_part, "error");
+    }
+    return;
+}
+
+int aarch64_disassemble(uint32_t code, char* instr)
+{
+    uint32_t i;
+    char token[256];
+    char instr_part[256];
+
+    if(instr == NULL)
+        return -1;
+
+    bool matched = false;
+    disasm_table_entry_t *entry = NULL;
+    for(i = 0; i < sizeof(disasm_table)/sizeof(disasm_table_entry_t); ++i)
+    {
+        entry = &disasm_table[i];
+        if((code & entry->mask) == entry->value)
+        {
+            matched = true;
+            break;
+        }
+    }
+    if(matched == false)
+    {
+        strcpy(instr, "Unknown Instruction");
+        return -1;
+    }
+    else
+    {
+        uint32_t index = 0;
+        uint32_t length = strlen(entry->instr_template);
+        instr[0] = '\0';
+        do
+        {
+            get_token(entry->instr_template, index, token);
+            if(token[0] == '<')
+            {
+                decode_token(code, token, instr_part);
+                strcat(instr, instr_part);
+            }
+            else
+            {
+                strcat(instr, token);
+            }
+            index += strlen(token);
+        }while(index < length);
+        return 0;
+    }
+}
diff --git a/libpixelflinger/codeflinger/Aarch64Disassembler.h b/libpixelflinger/codeflinger/Aarch64Disassembler.h
new file mode 100644
index 0000000..177d692
--- /dev/null
+++ b/libpixelflinger/codeflinger/Aarch64Disassembler.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef ANDROID_AARCH64DISASSEMBLER_H
+#define ANDROID_AARCH64DISASSEMBLER_H
+
+#include <inttypes.h>
+int aarch64_disassemble(uint32_t code, char* instr);
+
+#endif //ANDROID_AARCH64ASSEMBLER_H
diff --git a/libpixelflinger/codeflinger/CodeCache.cpp b/libpixelflinger/codeflinger/CodeCache.cpp
index 58fde7e..4fe30d9 100644
--- a/libpixelflinger/codeflinger/CodeCache.cpp
+++ b/libpixelflinger/codeflinger/CodeCache.cpp
@@ -34,7 +34,7 @@
 
 // ----------------------------------------------------------------------------
 
-#if defined(__arm__)
+#if defined(__arm__) || defined(__aarch64__)
 #include <unistd.h>
 #include <errno.h>
 #endif
@@ -201,7 +201,7 @@
         mCacheInUse += assemblySize;
         mWhen++;
         // synchronize caches...
-#if defined(__arm__) || defined(__mips__)
+#if defined(__arm__) || defined(__mips__) || defined(__aarch64__)
         const long base = long(assembly->base());
         const long curr = base + long(assembly->size());
         err = cacheflush(base, curr, 0);
diff --git a/libpixelflinger/codeflinger/GGLAssembler.cpp b/libpixelflinger/codeflinger/GGLAssembler.cpp
index 0cb042e..7f088db 100644
--- a/libpixelflinger/codeflinger/GGLAssembler.cpp
+++ b/libpixelflinger/codeflinger/GGLAssembler.cpp
@@ -263,7 +263,7 @@
                 const int mask = GGL_DITHER_SIZE-1;
                 parts.dither = reg_t(regs.obtain());
                 AND(AL, 0, parts.dither.reg, parts.count.reg, imm(mask));
-                ADD(AL, 0, parts.dither.reg, parts.dither.reg, ctxtReg);
+                ADDR_ADD(AL, 0, parts.dither.reg, ctxtReg, parts.dither.reg);
                 LDRB(AL, parts.dither.reg, parts.dither.reg,
                         immed12_pre(GGL_OFFSETOF(ditherMatrix)));
             }
@@ -336,7 +336,7 @@
         build_iterate_z(parts);
         build_iterate_f(parts);
         if (!mAllMasked) {
-            ADD(AL, 0, parts.cbPtr.reg, parts.cbPtr.reg, imm(parts.cbPtr.size>>3));
+            ADDR_ADD(AL, 0, parts.cbPtr.reg, parts.cbPtr.reg, imm(parts.cbPtr.size>>3));
         }
         SUB(AL, S, parts.count.reg, parts.count.reg, imm(1<<16));
         B(PL, "fragment_loop");
@@ -392,7 +392,7 @@
         int Rs = scratches.obtain();
         parts.cbPtr.setTo(obtainReg(), cb_bits);
         CONTEXT_LOAD(Rs, state.buffers.color.stride);
-        CONTEXT_LOAD(parts.cbPtr.reg, state.buffers.color.data);
+        CONTEXT_ADDR_LOAD(parts.cbPtr.reg, state.buffers.color.data);
         SMLABB(AL, Rs, Ry, Rs, Rx);  // Rs = Rx + Ry*Rs
         base_offset(parts.cbPtr, parts.cbPtr, Rs);
         scratches.recycle(Rs);
@@ -428,11 +428,11 @@
         int Rs = dzdx;
         int zbase = scratches.obtain();
         CONTEXT_LOAD(Rs, state.buffers.depth.stride);
-        CONTEXT_LOAD(zbase, state.buffers.depth.data);
+        CONTEXT_ADDR_LOAD(zbase, state.buffers.depth.data);
         SMLABB(AL, Rs, Ry, Rs, Rx);
         ADD(AL, 0, Rs, Rs, reg_imm(parts.count.reg, LSR, 16));
-        ADD(AL, 0, zbase, zbase, reg_imm(Rs, LSL, 1));
-        CONTEXT_STORE(zbase, generated_vars.zbase);
+        ADDR_ADD(AL, 0, zbase, zbase, reg_imm(Rs, LSL, 1));
+        CONTEXT_ADDR_STORE(zbase, generated_vars.zbase);
     }
 
     // init texture coordinates
@@ -445,8 +445,8 @@
     // init coverage factor application (anti-aliasing)
     if (mAA) {
         parts.covPtr.setTo(obtainReg(), 16);
-        CONTEXT_LOAD(parts.covPtr.reg, state.buffers.coverage);
-        ADD(AL, 0, parts.covPtr.reg, parts.covPtr.reg, reg_imm(Rx, LSL, 1));
+        CONTEXT_ADDR_LOAD(parts.covPtr.reg, state.buffers.coverage);
+        ADDR_ADD(AL, 0, parts.covPtr.reg, parts.covPtr.reg, reg_imm(Rx, LSL, 1));
     }
 }
 
@@ -765,8 +765,8 @@
         int depth = scratches.obtain();
         int z = parts.z.reg;
         
-        CONTEXT_LOAD(zbase, generated_vars.zbase);  // stall
-        SUB(AL, 0, zbase, zbase, reg_imm(parts.count.reg, LSR, 15));
+        CONTEXT_ADDR_LOAD(zbase, generated_vars.zbase);  // stall
+        ADDR_SUB(AL, 0, zbase, zbase, reg_imm(parts.count.reg, LSR, 15));
             // above does zbase = zbase + ((count >> 16) << 1)
 
         if (mask & Z_TEST) {
@@ -901,6 +901,10 @@
         AND( AL, 0, d, s, imm(mask) );
         return;
     }
+    else if (getCodegenArch() == CODEGEN_ARCH_AARCH64) {
+        AND( AL, 0, d, s, imm(mask) );
+        return;
+    }
 
     int negative_logic = !isValidImmediate(mask);
     if (negative_logic) {
@@ -990,22 +994,22 @@
 {
     switch (b.size) {
     case 32:
-        ADD(AL, 0, d.reg, b.reg, reg_imm(o.reg, LSL, 2));
+        ADDR_ADD(AL, 0, d.reg, b.reg, reg_imm(o.reg, LSL, 2));
         break;
     case 24:
         if (d.reg == b.reg) {
-            ADD(AL, 0, d.reg, b.reg, reg_imm(o.reg, LSL, 1));
-            ADD(AL, 0, d.reg, d.reg, o.reg);
+            ADDR_ADD(AL, 0, d.reg, b.reg, reg_imm(o.reg, LSL, 1));
+            ADDR_ADD(AL, 0, d.reg, d.reg, o.reg);
         } else {
-            ADD(AL, 0, d.reg, o.reg, reg_imm(o.reg, LSL, 1));
-            ADD(AL, 0, d.reg, d.reg, b.reg);
+            ADDR_ADD(AL, 0, d.reg, o.reg, reg_imm(o.reg, LSL, 1));
+            ADDR_ADD(AL, 0, d.reg, d.reg, b.reg);
         }
         break;
     case 16:
-        ADD(AL, 0, d.reg, b.reg, reg_imm(o.reg, LSL, 1));
+        ADDR_ADD(AL, 0, d.reg, b.reg, reg_imm(o.reg, LSL, 1));
         break;
     case 8:
-        ADD(AL, 0, d.reg, b.reg, o.reg);
+        ADDR_ADD(AL, 0, d.reg, b.reg, o.reg);
         break;
     }
 }
diff --git a/libpixelflinger/codeflinger/GGLAssembler.h b/libpixelflinger/codeflinger/GGLAssembler.h
index d993684..9db20df 100644
--- a/libpixelflinger/codeflinger/GGLAssembler.h
+++ b/libpixelflinger/codeflinger/GGLAssembler.h
@@ -31,6 +31,12 @@
 
 // ----------------------------------------------------------------------------
 
+#define CONTEXT_ADDR_LOAD(REG, FIELD) \
+    ADDR_LDR(AL, REG, mBuilderContext.Rctx, immed12_pre(GGL_OFFSETOF(FIELD)))
+
+#define CONTEXT_ADDR_STORE(REG, FIELD) \
+    ADDR_STR(AL, REG, mBuilderContext.Rctx, immed12_pre(GGL_OFFSETOF(FIELD)))
+
 #define CONTEXT_LOAD(REG, FIELD) \
     LDR(AL, REG, mBuilderContext.Rctx, immed12_pre(GGL_OFFSETOF(FIELD)))
 
diff --git a/libpixelflinger/codeflinger/texturing.cpp b/libpixelflinger/codeflinger/texturing.cpp
index 9e3d217..b2cfbb3 100644
--- a/libpixelflinger/codeflinger/texturing.cpp
+++ b/libpixelflinger/codeflinger/texturing.cpp
@@ -356,7 +356,7 @@
             // merge base & offset
             CONTEXT_LOAD(txPtr.reg, generated_vars.texture[i].stride);
             SMLABB(AL, Rx, Ry, txPtr.reg, Rx);               // x+y*stride
-            CONTEXT_LOAD(txPtr.reg, generated_vars.texture[i].data);
+            CONTEXT_ADDR_LOAD(txPtr.reg, generated_vars.texture[i].data);
             base_offset(txPtr, txPtr, Rx);
         } else {
             Scratch scratches(registerFile());
@@ -629,7 +629,7 @@
                 return;
 
             CONTEXT_LOAD(stride,    generated_vars.texture[i].stride);
-            CONTEXT_LOAD(txPtr.reg, generated_vars.texture[i].data);
+            CONTEXT_ADDR_LOAD(txPtr.reg, generated_vars.texture[i].data);
             SMLABB(AL, u, v, stride, u);    // u+v*stride 
             base_offset(txPtr, txPtr, u);
 
diff --git a/libpixelflinger/scanline.cpp b/libpixelflinger/scanline.cpp
index 9663a2b..bc774f3 100644
--- a/libpixelflinger/scanline.cpp
+++ b/libpixelflinger/scanline.cpp
@@ -31,8 +31,11 @@
 
 #include "codeflinger/CodeCache.h"
 #include "codeflinger/GGLAssembler.h"
+#if defined(__arm__)
 #include "codeflinger/ARMAssembler.h"
-#if defined(__mips__)
+#elif defined(__aarch64__)
+#include "codeflinger/Aarch64Assembler.h"
+#elif defined(__mips__)
 #include "codeflinger/MIPSAssembler.h"
 #endif
 //#include "codeflinger/ARMAssemblerOptimizer.h"
@@ -52,7 +55,7 @@
 #   define ANDROID_CODEGEN      ANDROID_CODEGEN_GENERATED
 #endif
 
-#if defined(__arm__) || defined(__mips__)
+#if defined(__arm__) || defined(__mips__) || defined(__aarch64__)
 #   define ANDROID_ARM_CODEGEN  1
 #else
 #   define ANDROID_ARM_CODEGEN  0
@@ -68,6 +71,8 @@
 
 #ifdef __mips__
 #define ASSEMBLY_SCRATCH_SIZE   4096
+#elif defined(__aarch64__)
+#define ASSEMBLY_SCRATCH_SIZE   8192
 #else
 #define ASSEMBLY_SCRATCH_SIZE   2048
 #endif
@@ -122,6 +127,9 @@
 extern "C" void scanline_t32cb16_arm(uint16_t *dst, uint32_t *src, size_t ct);
 extern "C" void scanline_col32cb16blend_neon(uint16_t *dst, uint32_t *col, size_t ct);
 extern "C" void scanline_col32cb16blend_arm(uint16_t *dst, uint32_t col, size_t ct);
+#elif defined(__aarch64__)
+extern "C" void scanline_t32cb16blend_aarch64(uint16_t*, uint32_t*, size_t);
+extern "C" void scanline_col32cb16blend_aarch64(uint16_t *dst, uint32_t col, size_t ct);
 #elif defined(__mips__)
 extern "C" void scanline_t32cb16blend_mips(uint16_t*, uint32_t*, size_t);
 #endif
@@ -276,6 +284,8 @@
 
 #if defined(__mips__)
 static CodeCache gCodeCache(32 * 1024);
+#elif defined(__aarch64__)
+static CodeCache gCodeCache(48 * 1024);
 #else
 static CodeCache gCodeCache(12 * 1024);
 #endif
@@ -394,6 +404,8 @@
 #endif
 #if defined(__mips__)
         GGLAssembler assembler( new ArmToMipsAssembler(a) );
+#elif defined(__aarch64__)
+        GGLAssembler assembler( new ArmToAarch64Assembler(a) );
 #endif
         // generate the scanline code for the given needs
         int err = assembler.scanline(c->state.needs, c);
@@ -1717,7 +1729,7 @@
             gen.width   = t.surface.width;
             gen.height  = t.surface.height;
             gen.stride  = t.surface.stride;
-            gen.data    = int32_t(t.surface.data);
+            gen.data    = uintptr_t(t.surface.data);
             gen.dsdx = ti.dsdx;
             gen.dtdx = ti.dtdx;
         }
@@ -2085,6 +2097,8 @@
 #else  // defined(__ARM_HAVE_NEON) && BYTE_ORDER == LITTLE_ENDIAN
     scanline_col32cb16blend_arm(dst, GGL_RGBA_TO_HOST(c->packed8888), ct);
 #endif // defined(__ARM_HAVE_NEON) && BYTE_ORDER == LITTLE_ENDIAN
+#elif ((ANDROID_CODEGEN >= ANDROID_CODEGEN_ASM) && defined(__aarch64__))
+    scanline_col32cb16blend_aarch64(dst, GGL_RGBA_TO_HOST(c->packed8888), ct);
 #else
     uint32_t s = GGL_RGBA_TO_HOST(c->packed8888);
     int sA = (s>>24);
@@ -2125,7 +2139,7 @@
     int sR, sG, sB;
     uint32_t s, d;
 
-    if (ct==1 || uint32_t(dst)&2) {
+    if (ct==1 || uintptr_t(dst)&2) {
 last_one:
         s = GGL_RGBA_TO_HOST( *src++ );
         *dst++ = convertAbgr8888ToRgb565(s);
@@ -2157,7 +2171,7 @@
 
 void scanline_t32cb16blend(context_t* c)
 {
-#if ((ANDROID_CODEGEN >= ANDROID_CODEGEN_ASM) && (defined(__arm__) || defined(__mips)))
+#if ((ANDROID_CODEGEN >= ANDROID_CODEGEN_ASM) && (defined(__arm__) || defined(__mips__) || defined(__aarch64__)))
     int32_t x = c->iterators.xl;
     size_t ct = c->iterators.xr - x;
     int32_t y = c->iterators.y;
@@ -2171,7 +2185,9 @@
 
 #ifdef __arm__
     scanline_t32cb16blend_arm(dst, src, ct);
-#else
+#elif defined(__aarch64__)
+    scanline_t32cb16blend_aarch64(dst, src, ct);
+#elif defined(__mips__)
     scanline_t32cb16blend_mips(dst, src, ct);
 #endif
 #else
diff --git a/libpixelflinger/tests/arch-aarch64/Android.mk b/libpixelflinger/tests/arch-aarch64/Android.mk
new file mode 100644
index 0000000..f096491
--- /dev/null
+++ b/libpixelflinger/tests/arch-aarch64/Android.mk
@@ -0,0 +1,3 @@
+ifeq ($(TARGET_ARCH),aarch64)
+include $(all-subdir-makefiles)
+endif
diff --git a/libpixelflinger/tests/arch-aarch64/assembler/Android.mk b/libpixelflinger/tests/arch-aarch64/assembler/Android.mk
new file mode 100644
index 0000000..10e06c4
--- /dev/null
+++ b/libpixelflinger/tests/arch-aarch64/assembler/Android.mk
@@ -0,0 +1,19 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+    aarch64_assembler_test.cpp\
+    asm_test_jacket.S
+
+LOCAL_SHARED_LIBRARIES := \
+    libcutils \
+    libpixelflinger
+
+LOCAL_C_INCLUDES := \
+    system/core/libpixelflinger
+
+LOCAL_MODULE:= test-pixelflinger-aarch64-assembler-test
+
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_EXECUTABLE)
diff --git a/libpixelflinger/tests/arch-aarch64/assembler/aarch64_assembler_test.cpp b/libpixelflinger/tests/arch-aarch64/assembler/aarch64_assembler_test.cpp
new file mode 100644
index 0000000..d3e57b3
--- /dev/null
+++ b/libpixelflinger/tests/arch-aarch64/assembler/aarch64_assembler_test.cpp
@@ -0,0 +1,782 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <sys/mman.h>
+#include <cutils/ashmem.h>
+#include <cutils/atomic.h>
+
+#define __STDC_FORMAT_MACROS
+#include <inttypes.h>
+
+#include "codeflinger/ARMAssemblerInterface.h"
+#include "codeflinger/Aarch64Assembler.h"
+using namespace android;
+
+#define TESTS_DATAOP_ENABLE             1
+#define TESTS_DATATRANSFER_ENABLE       1
+#define TESTS_LDMSTM_ENABLE             1
+#define TESTS_REG_CORRUPTION_ENABLE     0
+
+void *instrMem;
+uint32_t  instrMemSize = 128 * 1024;
+char     dataMem[8192];
+
+typedef void (*asm_function_t)();
+extern "C" void asm_test_jacket(asm_function_t function,
+                                int64_t regs[], int32_t flags[]);
+
+#define MAX_32BIT (uint32_t)(((uint64_t)1 << 32) - 1)
+const uint32_t NA = 0;
+const uint32_t NUM_REGS = 32;
+const uint32_t NUM_FLAGS = 16;
+
+enum instr_t
+{
+    INSTR_ADD,
+    INSTR_SUB,
+    INSTR_AND,
+    INSTR_ORR,
+    INSTR_RSB,
+    INSTR_BIC,
+    INSTR_CMP,
+    INSTR_MOV,
+    INSTR_MVN,
+    INSTR_MUL,
+    INSTR_MLA,
+    INSTR_SMULBB,
+    INSTR_SMULBT,
+    INSTR_SMULTB,
+    INSTR_SMULTT,
+    INSTR_SMULWB,
+    INSTR_SMULWT,
+    INSTR_SMLABB,
+    INSTR_UXTB16,
+    INSTR_UBFX,
+    INSTR_ADDR_ADD,
+    INSTR_ADDR_SUB,
+    INSTR_LDR,
+    INSTR_LDRB,
+    INSTR_LDRH,
+    INSTR_ADDR_LDR,
+    INSTR_LDM,
+    INSTR_STR,
+    INSTR_STRB,
+    INSTR_STRH,
+    INSTR_ADDR_STR,
+    INSTR_STM
+};
+
+enum shift_t
+{
+    SHIFT_LSL,
+    SHIFT_LSR,
+    SHIFT_ASR,
+    SHIFT_ROR,
+    SHIFT_NONE
+};
+
+enum offset_t
+{
+    REG_SCALE_OFFSET,
+    REG_OFFSET,
+    IMM8_OFFSET,
+    IMM12_OFFSET,
+    NO_OFFSET
+};
+
+enum cond_t
+{
+    EQ, NE, CS, CC, MI, PL, VS, VC, HI, LS, GE, LT, GT, LE, AL, NV,
+    HS = CS,
+    LO = CC
+};
+
+const char * cc_code[] =
+{
+    "EQ", "NE", "CS", "CC", "MI", "PL", "VS", "VC",
+    "HI", "LS","GE","LT", "GT", "LE", "AL", "NV"
+};
+
+
+struct dataOpTest_t
+{
+    uint32_t id;
+    instr_t  op;
+    uint32_t preFlag;
+    cond_t   cond;
+    bool     setFlags;
+    uint64_t RnValue;
+    uint64_t RsValue;
+    bool     immediate;
+    uint32_t immValue;
+    uint64_t RmValue;
+    uint32_t shiftMode;
+    uint32_t shiftAmount;
+    uint64_t RdValue;
+    bool     checkRd;
+    uint64_t postRdValue;
+    bool     checkFlag;
+    uint32_t postFlag;
+};
+
+struct dataTransferTest_t
+{
+    uint32_t id;
+    instr_t op;
+    uint32_t preFlag;
+    cond_t   cond;
+    bool     setMem;
+    uint64_t memOffset;
+    uint64_t memValue;
+    uint64_t RnValue;
+    offset_t offsetType;
+    uint64_t RmValue;
+    uint32_t immValue;
+    bool     writeBack;
+    bool     preIndex;
+    bool     postIndex;
+    uint64_t RdValue;
+    uint64_t postRdValue;
+    uint64_t postRnValue;
+    bool     checkMem;
+    uint64_t postMemOffset;
+    uint32_t postMemLength;
+    uint64_t postMemValue;
+};
+
+
+dataOpTest_t dataOpTests [] =
+{
+     {0xA000,INSTR_ADD,AL,AL,0,1,NA,1,MAX_32BIT ,NA,NA,NA,NA,1,0,0,0},
+     {0xA001,INSTR_ADD,AL,AL,0,1,NA,1,MAX_32BIT -1,NA,NA,NA,NA,1,MAX_32BIT,0,0},
+     {0xA002,INSTR_ADD,AL,AL,0,1,NA,0,NA,MAX_32BIT ,NA,NA,NA,1,0,0,0},
+     {0xA003,INSTR_ADD,AL,AL,0,1,NA,0,NA,MAX_32BIT -1,NA,NA,NA,1,MAX_32BIT,0,0},
+     {0xA004,INSTR_ADD,AL,AL,0,1,NA,0,0,MAX_32BIT ,SHIFT_LSL,0,NA,1,0,0,0},
+     {0xA005,INSTR_ADD,AL,AL,0,1,NA,0,0,MAX_32BIT ,SHIFT_LSL,31,NA,1,0x80000001,0,0},
+     {0xA006,INSTR_ADD,AL,AL,0,1,NA,0,0,3,SHIFT_LSR,1,NA,1,2,0,0},
+     {0xA007,INSTR_ADD,AL,AL,0,1,NA,0,0,MAX_32BIT ,SHIFT_LSR,31,NA,1,2,0,0},
+     {0xA008,INSTR_ADD,AL,AL,0,0,NA,0,0,3,SHIFT_ASR,1,NA,1,1,0,0},
+     {0xA009,INSTR_ADD,AL,AL,0,1,NA,0,0,MAX_32BIT ,SHIFT_ASR,31,NA,1,0,0,0},
+     {0xA010,INSTR_AND,AL,AL,0,1,NA,1,MAX_32BIT ,0,0,0,NA,1,1,0,0},
+     {0xA011,INSTR_AND,AL,AL,0,1,NA,1,MAX_32BIT -1,0,0,0,NA,1,0,0,0},
+     {0xA012,INSTR_AND,AL,AL,0,1,NA,0,0,MAX_32BIT ,0,0,NA,1,1,0,0},
+     {0xA013,INSTR_AND,AL,AL,0,1,NA,0,0,MAX_32BIT -1,0,0,NA,1,0,0,0},
+     {0xA014,INSTR_AND,AL,AL,0,1,NA,0,0,MAX_32BIT ,SHIFT_LSL,0,NA,1,1,0,0},
+     {0xA015,INSTR_AND,AL,AL,0,1,NA,0,0,MAX_32BIT ,SHIFT_LSL,31,NA,1,0,0,0},
+     {0xA016,INSTR_AND,AL,AL,0,1,NA,0,0,3,SHIFT_LSR,1,NA,1,1,0,0},
+     {0xA017,INSTR_AND,AL,AL,0,1,NA,0,0,MAX_32BIT ,SHIFT_LSR,31,NA,1,1,0,0},
+     {0xA018,INSTR_AND,AL,AL,0,0,NA,0,0,3,SHIFT_ASR,1,NA,1,0,0,0},
+     {0xA019,INSTR_AND,AL,AL,0,1,NA,0,0,MAX_32BIT ,SHIFT_ASR,31,NA,1,1,0,0},
+     {0xA020,INSTR_ORR,AL,AL,0,3,NA,1,MAX_32BIT ,0,0,0,NA,1,MAX_32BIT,0,0},
+     {0xA021,INSTR_ORR,AL,AL,0,2,NA,1,MAX_32BIT -1,0,0,0,NA,1,MAX_32BIT-1,0,0},
+     {0xA022,INSTR_ORR,AL,AL,0,3,NA,0,0,MAX_32BIT ,0,0,NA,1,MAX_32BIT,0,0},
+     {0xA023,INSTR_ORR,AL,AL,0,2,NA,0,0,MAX_32BIT -1,0,0,NA,1,MAX_32BIT-1,0,0},
+     {0xA024,INSTR_ORR,AL,AL,0,1,NA,0,0,MAX_32BIT ,SHIFT_LSL,0,NA,1,MAX_32BIT,0,0},
+     {0xA025,INSTR_ORR,AL,AL,0,1,NA,0,0,MAX_32BIT ,SHIFT_LSL,31,NA,1,0x80000001,0,0},
+     {0xA026,INSTR_ORR,AL,AL,0,1,NA,0,0,3,SHIFT_LSR,1,NA,1,1,0,0},
+     {0xA027,INSTR_ORR,AL,AL,0,0,NA,0,0,MAX_32BIT ,SHIFT_LSR,31,NA,1,1,0,0},
+     {0xA028,INSTR_ORR,AL,AL,0,0,NA,0,0,3,SHIFT_ASR,1,NA,1,1,0,0},
+     {0xA029,INSTR_ORR,AL,AL,0,1,NA,0,0,MAX_32BIT ,SHIFT_ASR,31,NA,1,MAX_32BIT ,0,0},
+     {0xA030,INSTR_CMP,AL,AL,1,0x10000,NA,1,0x10000,0,0,0,NA,0,0,1,HS},
+     {0xA031,INSTR_CMP,AL,AL,1,0x00000,NA,1,0x10000,0,0,0,NA,0,0,1,CC},
+     {0xA032,INSTR_CMP,AL,AL,1,0x00000,NA,0,0,0x10000,0,0,NA,0,0,1,LT},
+     {0xA033,INSTR_CMP,AL,AL,1,0x10000,NA,0,0,0x10000,0,0,NA,0,0,1,EQ},
+     {0xA034,INSTR_CMP,AL,AL,1,0x00000,NA,0,0,0x10000,0,0,NA,0,0,1,LS},
+     {0xA035,INSTR_CMP,AL,AL,1,0x10000,NA,0,0,0x10000,0,0,NA,0,0,1,LS},
+     {0xA036,INSTR_CMP,AL,AL,1,0x10000,NA,0,0,0x00000,0,0,NA,0,0,1,HI},
+     {0xA037,INSTR_CMP,AL,AL,1,0x10000,NA,0,0,0x10000,0,0,NA,0,0,1,HS},
+     {0xA038,INSTR_CMP,AL,AL,1,0x10000,NA,0,0,0x00000,0,0,NA,0,0,1,HS},
+     {0xA039,INSTR_CMP,AL,AL,1,0x10000,NA,0,0,0x00000,0,0,NA,0,0,1,NE},
+     {0xA040,INSTR_CMP,AL,AL,1,0,NA,0,0,MAX_32BIT ,SHIFT_LSR,1,NA,0,0,1,LT},
+     {0xA041,INSTR_CMP,AL,AL,1,1,NA,0,0,MAX_32BIT ,SHIFT_LSR,31,NA,0,0,1,EQ},
+     {0xA042,INSTR_CMP,AL,AL,1,0,NA,0,0,0x10000,SHIFT_LSR,31,NA,0,0,1,LS},
+     {0xA043,INSTR_CMP,AL,AL,1,0x10000,NA,0,0,0x30000,SHIFT_LSR,1,NA,0,0,1,LS},
+     {0xA044,INSTR_CMP,AL,AL,1,0x10000,NA,0,0,0x00000,SHIFT_LSR,31,NA,0,0,1,HI},
+     {0xA045,INSTR_CMP,AL,AL,1,1,NA,0,0,MAX_32BIT ,SHIFT_LSR,31,NA,0,0,1,HS},
+     {0xA046,INSTR_CMP,AL,AL,1,0x10000,NA,0,0,0x2000,SHIFT_LSR,1,NA,0,0,1,HS},
+     {0xA047,INSTR_CMP,AL,AL,1,0,NA,0,0,MAX_32BIT ,SHIFT_LSR,1,NA,0,0,1,NE},
+     {0xA048,INSTR_CMP,AL,AL,1,0,NA,0,0,0x10000,SHIFT_ASR,2,NA,0,0,1,LT},
+     {0xA049,INSTR_CMP,AL,AL,1,MAX_32BIT ,NA,0,0,MAX_32BIT ,SHIFT_ASR,1,NA,0,0,1,EQ},
+     {0xA050,INSTR_CMP,AL,AL,1,MAX_32BIT ,NA,0,0,MAX_32BIT ,SHIFT_ASR,31,NA,0,0,1,LS},
+     {0xA051,INSTR_CMP,AL,AL,1,0,NA,0,0,0x10000,SHIFT_ASR,1,NA,0,0,1,LS},
+     {0xA052,INSTR_CMP,AL,AL,1,0x10000,NA,0,0,0x10000,SHIFT_ASR,1,NA,0,0,1,HI},
+     {0xA053,INSTR_CMP,AL,AL,1,1,NA,0,0,0x10000,SHIFT_ASR,31,NA,0,0,1,HS},
+     {0xA054,INSTR_CMP,AL,AL,1,1,NA,0,0,0x10000,SHIFT_ASR,16,NA,0,0,1,HS},
+     {0xA055,INSTR_CMP,AL,AL,1,1,NA,0,0,MAX_32BIT ,SHIFT_ASR,1,NA,0,0,1,NE},
+     {0xA056,INSTR_MUL,AL,AL,0,0,0x10000,0,0,0x10000,0,0,NA,1,0,0,0},
+     {0xA057,INSTR_MUL,AL,AL,0,0,0x1000,0,0,0x10000,0,0,NA,1,0x10000000,0,0},
+     {0xA058,INSTR_MUL,AL,AL,0,0,MAX_32BIT ,0,0,1,0,0,NA,1,MAX_32BIT ,0,0},
+     {0xA059,INSTR_MLA,AL,AL,0,0x10000,0x10000,0,0,0x10000,0,0,NA,1,0x10000,0,0},
+     {0xA060,INSTR_MLA,AL,AL,0,0x10000,0x1000,0,0,0x10000,0,0,NA,1,0x10010000,0,0},
+     {0xA061,INSTR_MLA,AL,AL,1,1,MAX_32BIT ,0,0,1,0,0,NA,1,0,1,PL},
+     {0xA062,INSTR_MLA,AL,AL,1,0,MAX_32BIT ,0,0,1,0,0,NA,1,MAX_32BIT ,1,MI},
+     {0xA063,INSTR_SUB,AL,AL,1,1 << 16,NA,1,1 << 16,NA,NA,NA,NA,1,0,1,PL},
+     {0xA064,INSTR_SUB,AL,AL,1,(1 << 16) + 1,NA,1,1 << 16,NA,NA,NA,NA,1,1,1,PL},
+     {0xA065,INSTR_SUB,AL,AL,1,0,NA,1,1 << 16,NA,NA,NA,NA,1,(uint32_t)(0 - (1<<16)),1,MI},
+     {0xA066,INSTR_SUB,MI,MI,0,2,NA,0,NA,1,NA,NA,2,1,1,0,NA},
+     {0xA067,INSTR_SUB,EQ,MI,0,2,NA,0,NA,1,NA,NA,2,1,2,0,NA},
+     {0xA068,INSTR_SUB,GT,GE,0,2,NA,1,1,NA,NA,NA,2,1,1,0,NA},
+     {0xA069,INSTR_SUB,LT,GE,0,2,NA,1,1,NA,NA,NA,2,1,2,0,NA},
+     {0xA070,INSTR_SUB,CS,HS,0,2,NA,1,1,NA,NA,NA,2,1,1,0,NA},
+     {0xA071,INSTR_SUB,CC,HS,0,2,NA,1,1,NA,NA,NA,2,1,2,0,NA},
+     {0xA072,INSTR_SUB,AL,AL,0,1,NA,1,1 << 16,0,0,0,NA,1,(uint32_t)(1 - (1 << 16)),0,NA},
+     {0xA073,INSTR_SUB,AL,AL,0,MAX_32BIT,NA,1,1,0,0,0,NA,1,MAX_32BIT  - 1,0,NA},
+     {0xA074,INSTR_SUB,AL,AL,0,1,NA,1,1,0,0,0,NA,1,0,0,NA},
+     {0xA075,INSTR_SUB,AL,AL,0,1,NA,0,NA,1 << 16,0,0,NA,1,(uint32_t)(1 - (1 << 16)),0,NA},
+     {0xA076,INSTR_SUB,AL,AL,0,MAX_32BIT,NA,0,NA,1,0,0,NA,1,MAX_32BIT  - 1,0,NA},
+     {0xA077,INSTR_SUB,AL,AL,0,1,NA,0,NA,1,0,0,NA,1,0,0,NA},
+     {0xA078,INSTR_SUB,AL,AL,0,1,NA,0,NA,1,SHIFT_LSL,16,NA,1,(uint32_t)(1 - (1 << 16)),0,NA},
+     {0xA079,INSTR_SUB,AL,AL,0,0x80000001,NA,0,NA,MAX_32BIT ,SHIFT_LSL,31,NA,1,1,0,NA},
+     {0xA080,INSTR_SUB,AL,AL,0,1,NA,0,NA,3,SHIFT_LSR,1,NA,1,0,0,NA},
+     {0xA081,INSTR_SUB,AL,AL,0,1,NA,0,NA,MAX_32BIT ,SHIFT_LSR,31,NA,1,0,0,NA},
+     {0xA082,INSTR_RSB,GT,GE,0,2,NA,1,0,NA,NA,NA,2,1,(uint32_t)-2,0,NA},
+     {0xA083,INSTR_RSB,LT,GE,0,2,NA,1,0,NA,NA,NA,2,1,2,0,NA},
+     {0xA084,INSTR_RSB,AL,AL,0,1,NA,1,1 << 16,NA,NA,NA,NA,1,(1 << 16) - 1,0,NA},
+     {0xA085,INSTR_RSB,AL,AL,0,MAX_32BIT,NA,1,1,NA,NA,NA,NA,1,(uint32_t) (1 - MAX_32BIT),0,NA},
+     {0xA086,INSTR_RSB,AL,AL,0,1,NA,1,1,NA,NA,NA,NA,1,0,0,NA},
+     {0xA087,INSTR_RSB,AL,AL,0,1,NA,0,NA,1 << 16,0,0,NA,1,(1 << 16) - 1,0,NA},
+     {0xA088,INSTR_RSB,AL,AL,0,MAX_32BIT,NA,0,NA,1,0,0,NA,1,(uint32_t) (1 - MAX_32BIT),0,NA},
+     {0xA089,INSTR_RSB,AL,AL,0,1,NA,0,NA,1,0,0,NA,1,0,0,NA},
+     {0xA090,INSTR_RSB,AL,AL,0,1,NA,0,NA,1,SHIFT_LSL,16,NA,1,(1 << 16) - 1,0,NA},
+     {0xA091,INSTR_RSB,AL,AL,0,0x80000001,NA,0,NA,MAX_32BIT ,SHIFT_LSL,31,NA,1,(uint32_t)-1,0,NA},
+     {0xA092,INSTR_RSB,AL,AL,0,1,NA,0,NA,3,SHIFT_LSR,1,NA,1,0,0,NA},
+     {0xA093,INSTR_RSB,AL,AL,0,1,NA,0,NA,MAX_32BIT ,SHIFT_LSR,31,NA,1,0,0,NA},
+     {0xA094,INSTR_MOV,AL,AL,0,NA,NA,1,0x80000001,NA,NA,NA,NA,1,0x80000001,0,0},
+     {0xA095,INSTR_MOV,AL,AL,0,NA,NA,0,0,0x80000001,0,0,NA,1,0x80000001,0,0},
+     {0xA096,INSTR_MOV,AL,AL,0,NA,NA,0,0,MAX_32BIT ,SHIFT_LSL,1,NA,1,MAX_32BIT -1,0,0},
+     {0xA097,INSTR_MOV,AL,AL,0,NA,NA,0,0,MAX_32BIT ,SHIFT_LSL,31,NA,1,0x80000000,0,0},
+     {0xA098,INSTR_MOV,AL,AL,0,NA,NA,0,0,3,SHIFT_LSR,1,NA,1,1,0,0},
+     {0xA099,INSTR_MOV,AL,AL,0,NA,NA,0,0,MAX_32BIT ,SHIFT_LSR,31,NA,1,1,0,0},
+     {0xA100,INSTR_MOV,AL,AL,0,NA,NA,0,0,3,SHIFT_ASR,1,NA,1,1,0,0},
+     {0xA101,INSTR_MOV,AL,AL,0,NA,NA,0,0,MAX_32BIT ,SHIFT_ASR,31,NA,1,MAX_32BIT ,0,0},
+     {0xA102,INSTR_MOV,AL,AL,0,NA,NA,0,0,3,SHIFT_ROR,1,NA,1,0x80000001,0,0},
+     {0xA103,INSTR_MOV,AL,AL,0,NA,NA,0,0,0x80000001,SHIFT_ROR,31,NA,1,3,0,0},
+     {0xA104,INSTR_MOV,AL,AL,1,NA,NA,0,0,MAX_32BIT -1,SHIFT_ASR,1,NA,1,MAX_32BIT,1,MI},
+     {0xA105,INSTR_MOV,AL,AL,1,NA,NA,0,0,3,SHIFT_ASR,1,NA,1,1,1,PL},
+     {0xA106,INSTR_MOV,PL,MI,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,2,0,0},
+     {0xA107,INSTR_MOV,MI,MI,0,NA,NA,0,0,0x80000001,0,0,2,1,0x80000001,0,0},
+     {0xA108,INSTR_MOV,EQ,LT,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,2,0,0},
+     {0xA109,INSTR_MOV,LT,LT,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,0x80000001,0,0},
+     {0xA110,INSTR_MOV,GT,GE,0,NA,NA,0,0,MAX_32BIT ,SHIFT_LSL,1,2,1,MAX_32BIT -1,0,0},
+     {0xA111,INSTR_MOV,EQ,GE,0,NA,NA,0,0,MAX_32BIT ,SHIFT_LSL,31,2,1,0x80000000,0,0},
+     {0xA112,INSTR_MOV,LT,GE,0,NA,NA,0,0,MAX_32BIT ,SHIFT_LSL,31,2,1,2,0,0},
+     {0xA113,INSTR_MOV,GT,LE,0,NA,NA,0,0,MAX_32BIT ,SHIFT_LSL,1,2,1,2,0,0},
+     {0xA114,INSTR_MOV,EQ,LE,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,0x80000001,0,0},
+     {0xA115,INSTR_MOV,LT,LE,0,NA,NA,0,0,MAX_32BIT ,SHIFT_LSL,31,2,1,0x80000000,0,0},
+     {0xA116,INSTR_MOV,EQ,GT,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,2,0,0},
+     {0xA117,INSTR_MOV,GT,GT,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,0x80000001,0,0},
+     {0xA118,INSTR_MOV,LE,GT,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,2,0,0},
+     {0xA119,INSTR_MOV,EQ,GT,0,NA,NA,0,0,0x80000001,0,0,2,1,2,0,0},
+     {0xA120,INSTR_MOV,GT,GT,0,NA,NA,0,0,0x80000001,0,0,2,1,0x80000001,0,0},
+     {0xA121,INSTR_MOV,LE,GT,0,NA,NA,0,0,0x80000001,0,0,2,1,2,0,0},
+     {0xA122,INSTR_MOV,EQ,GT,0,NA,NA,0,0,MAX_32BIT ,SHIFT_LSL,1,2,1,2,0,0},
+     {0xA123,INSTR_MOV,GT,GT,0,NA,NA,0,0,MAX_32BIT ,SHIFT_LSL,1,2,1,MAX_32BIT -1,0,0},
+     {0xA124,INSTR_MOV,LE,GT,0,NA,NA,0,0,MAX_32BIT ,SHIFT_LSL,1,2,1,2,0,0},
+     {0xA125,INSTR_MOV,LO,HS,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,2,0,0},
+     {0xA126,INSTR_MOV,HS,HS,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,0x80000001,0,0},
+     {0xA127,INSTR_MVN,LO,HS,0,NA,NA,1,MAX_32BIT -1,NA,NA,NA,2,1,2,0,0},
+     {0xA128,INSTR_MVN,HS,HS,0,NA,NA,1,MAX_32BIT -1,NA,NA,NA,2,1,1,0,0},
+     {0xA129,INSTR_MVN,AL,AL,0,NA,NA,1,0,NA,NA,NA,2,1,MAX_32BIT,0,NA},
+     {0xA130,INSTR_MVN,AL,AL,0,NA,NA,0,NA,MAX_32BIT -1,NA,0,2,1,1,0,NA},
+     {0xA131,INSTR_MVN,AL,AL,0,NA,NA,0,NA,0x80000001,NA,0,2,1,0x7FFFFFFE,0,NA},
+     {0xA132,INSTR_BIC,AL,AL,0,1,NA,1,MAX_32BIT ,NA,NA,NA,NA,1,0,0,0},
+     {0xA133,INSTR_BIC,AL,AL,0,1,NA,1,MAX_32BIT -1,NA,NA,NA,NA,1,1,0,0},
+     {0xA134,INSTR_BIC,AL,AL,0,1,NA,0,0,MAX_32BIT ,0,0,NA,1,0,0,0},
+     {0xA135,INSTR_BIC,AL,AL,0,1,NA,0,0,MAX_32BIT -1,0,0,NA,1,1,0,0},
+     {0xA136,INSTR_BIC,AL,AL,0,0xF0,NA,0,0,3,SHIFT_ASR,1,NA,1,0xF0,0,0},
+     {0xA137,INSTR_BIC,AL,AL,0,0xF0,NA,0,0,MAX_32BIT ,SHIFT_ASR,31,NA,1,0,0,0},
+     {0xA138,INSTR_SMULBB,AL,AL,0,NA,0xABCDFFFF,0,NA,0xABCD0001,NA,NA,NA,1,0xFFFFFFFF,0,0},
+     {0xA139,INSTR_SMULBB,AL,AL,0,NA,0xABCD0001,0,NA,0xABCD0FFF,NA,NA,NA,1,0x00000FFF,0,0},
+     {0xA140,INSTR_SMULBB,AL,AL,0,NA,0xABCD0001,0,NA,0xABCDFFFF,NA,NA,NA,1,0xFFFFFFFF,0,0},
+     {0xA141,INSTR_SMULBB,AL,AL,0,NA,0xABCDFFFF,0,NA,0xABCDFFFF,NA,NA,NA,1,1,0,0},
+     {0xA142,INSTR_SMULBT,AL,AL,0,NA,0xFFFFABCD,0,NA,0xABCD0001,NA,NA,NA,1,0xFFFFFFFF,0,0},
+     {0xA143,INSTR_SMULBT,AL,AL,0,NA,0x0001ABCD,0,NA,0xABCD0FFF,NA,NA,NA,1,0x00000FFF,0,0},
+     {0xA144,INSTR_SMULBT,AL,AL,0,NA,0x0001ABCD,0,NA,0xABCDFFFF,NA,NA,NA,1,0xFFFFFFFF,0,0},
+     {0xA145,INSTR_SMULBT,AL,AL,0,NA,0xFFFFABCD,0,NA,0xABCDFFFF,NA,NA,NA,1,1,0,0},
+     {0xA146,INSTR_SMULTB,AL,AL,0,NA,0xABCDFFFF,0,NA,0x0001ABCD,NA,NA,NA,1,0xFFFFFFFF,0,0},
+     {0xA147,INSTR_SMULTB,AL,AL,0,NA,0xABCD0001,0,NA,0x0FFFABCD,NA,NA,NA,1,0x00000FFF,0,0},
+     {0xA148,INSTR_SMULTB,AL,AL,0,NA,0xABCD0001,0,NA,0xFFFFABCD,NA,NA,NA,1,0xFFFFFFFF,0,0},
+     {0xA149,INSTR_SMULTB,AL,AL,0,NA,0xABCDFFFF,0,NA,0xFFFFABCD,NA,NA,NA,1,1,0,0},
+     {0xA150,INSTR_SMULTT,AL,AL,0,NA,0xFFFFABCD,0,NA,0x0001ABCD,NA,NA,NA,1,0xFFFFFFFF,0,0},
+     {0xA151,INSTR_SMULTT,AL,AL,0,NA,0x0001ABCD,0,NA,0x0FFFABCD,NA,NA,NA,1,0x00000FFF,0,0},
+     {0xA152,INSTR_SMULTT,AL,AL,0,NA,0x0001ABCD,0,NA,0xFFFFABCD,NA,NA,NA,1,0xFFFFFFFF,0,0},
+     {0xA153,INSTR_SMULTT,AL,AL,0,NA,0xFFFFABCD,0,NA,0xFFFFABCD,NA,NA,NA,1,1,0,0},
+     {0xA154,INSTR_SMULWB,AL,AL,0,NA,0xABCDFFFF,0,NA,0x0001ABCD,NA,NA,NA,1,0xFFFFFFFE,0,0},
+     {0xA155,INSTR_SMULWB,AL,AL,0,NA,0xABCD0001,0,NA,0x0FFFABCD,NA,NA,NA,1,0x00000FFF,0,0},
+     {0xA156,INSTR_SMULWB,AL,AL,0,NA,0xABCD0001,0,NA,0xFFFFABCD,NA,NA,NA,1,0xFFFFFFFF,0,0},
+     {0xA157,INSTR_SMULWB,AL,AL,0,NA,0xABCDFFFF,0,NA,0xFFFFABCD,NA,NA,NA,1,0,0,0},
+     {0xA158,INSTR_SMULWT,AL,AL,0,NA,0xFFFFABCD,0,NA,0x0001ABCD,NA,NA,NA,1,0xFFFFFFFE,0,0},
+     {0xA159,INSTR_SMULWT,AL,AL,0,NA,0x0001ABCD,0,NA,0x0FFFABCD,NA,NA,NA,1,0x00000FFF,0,0},
+     {0xA160,INSTR_SMULWT,AL,AL,0,NA,0x0001ABCD,0,NA,0xFFFFABCD,NA,NA,NA,1,0xFFFFFFFF,0,0},
+     {0xA161,INSTR_SMULWT,AL,AL,0,NA,0xFFFFABCD,0,NA,0xFFFFABCD,NA,NA,NA,1,0,0,0},
+     {0xA162,INSTR_SMLABB,AL,AL,0,1,0xABCDFFFF,0,NA,0xABCD0001,NA,NA,NA,1,0,0,0},
+     {0xA163,INSTR_SMLABB,AL,AL,0,1,0xABCD0001,0,NA,0xABCD0FFF,NA,NA,NA,1,0x00001000,0,0},
+     {0xA164,INSTR_SMLABB,AL,AL,0,0xFFFFFFFF,0xABCD0001,0,NA,0xABCDFFFF,NA,NA,NA,1,0xFFFFFFFE,0,0},
+     {0xA165,INSTR_SMLABB,AL,AL,0,0xFFFFFFFF,0xABCDFFFF,0,NA,0xABCDFFFF,NA,NA,NA,1,0,0,0},
+     {0xA166,INSTR_UXTB16,AL,AL,0,NA,NA,0,NA,0xABCDEF01,SHIFT_ROR,0,NA,1,0x00CD0001,0,0},
+     {0xA167,INSTR_UXTB16,AL,AL,0,NA,NA,0,NA,0xABCDEF01,SHIFT_ROR,1,NA,1,0x00AB00EF,0,0},
+     {0xA168,INSTR_UXTB16,AL,AL,0,NA,NA,0,NA,0xABCDEF01,SHIFT_ROR,2,NA,1,0x000100CD,0,0},
+     {0xA169,INSTR_UXTB16,AL,AL,0,NA,NA,0,NA,0xABCDEF01,SHIFT_ROR,3,NA,1,0x00EF00AB,0,0},
+     {0xA170,INSTR_UBFX,AL,AL,0,0xABCDEF01,4,0,NA,24,NA,NA,NA,1,0x00BCDEF0,0,0},
+     {0xA171,INSTR_UBFX,AL,AL,0,0xABCDEF01,1,0,NA,2,NA,NA,NA,1,0,0,0},
+     {0xA172,INSTR_UBFX,AL,AL,0,0xABCDEF01,16,0,NA,8,NA,NA,NA,1,0xCD,0,0},
+     {0xA173,INSTR_UBFX,AL,AL,0,0xABCDEF01,31,0,NA,1,NA,NA,NA,1,1,0,0},
+     {0xA174,INSTR_ADDR_ADD,AL,AL,0,0xCFFFFFFFF,NA,0,NA,0x1,SHIFT_LSL,1,NA,1,0xD00000001,0,0},
+     {0xA175,INSTR_ADDR_ADD,AL,AL,0,0x01,NA,0,NA,0x1,SHIFT_LSL,2,NA,1,0x5,0,0},
+     {0xA176,INSTR_ADDR_ADD,AL,AL,0,0xCFFFFFFFF,NA,0,NA,0x1,NA,0,NA,1,0xD00000000,0,0},
+     {0xA177,INSTR_ADDR_SUB,AL,AL,0,0xD00000001,NA,0,NA,0x010000,SHIFT_LSR,15,NA,1,0xCFFFFFFFF,0,0},
+     {0xA178,INSTR_ADDR_SUB,AL,AL,0,0xCFFFFFFFF,NA,0,NA,0x020000,SHIFT_LSR,15,NA,1,0xCFFFFFFFB,0,0},
+     {0xA179,INSTR_ADDR_SUB,AL,AL,0,3,NA,0,NA,0x010000,SHIFT_LSR,15,NA,1,1,0,0},
+};
+
+dataTransferTest_t dataTransferTests [] =
+{
+    {0xB000,INSTR_LDR,AL,AL,1,24,0xABCDEF0123456789,0,REG_SCALE_OFFSET,24,NA,NA,NA,NA,NA,0x23456789,0,0,NA,NA,NA},
+    {0xB001,INSTR_LDR,AL,AL,1,4064,0xABCDEF0123456789,0,IMM12_OFFSET,NA,4068,0,1,0,NA,0xABCDEF01,0,0,NA,NA,NA},
+    {0xB002,INSTR_LDR,AL,AL,1,0,0xABCDEF0123456789,0,IMM12_OFFSET,NA,4,1,0,1,NA,0x23456789,4,0,NA,NA,NA},
+    {0xB003,INSTR_LDR,AL,AL,1,0,0xABCDEF0123456789,0,NO_OFFSET,NA,NA,0,0,0,NA,0x23456789,0,0,NA,NA,NA},
+    {0xB004,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,0,REG_SCALE_OFFSET,4064,NA,NA,NA,NA,NA,0x89,0,0,NA,NA,NA},
+    {0xB005,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,0,IMM12_OFFSET,NA,4065,0,1,0,NA,0x67,0,0,NA,NA,NA},
+    {0xB006,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,4065,IMM12_OFFSET,NA,0,0,1,0,NA,0x67,4065,0,NA,NA,NA},
+    {0xB007,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,4065,IMM12_OFFSET,NA,1,0,1,0,NA,0x45,4065,0,NA,NA,NA},
+    {0xB008,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,4065,IMM12_OFFSET,NA,2,0,1,0,NA,0x23,4065,0,NA,NA,NA},
+    {0xB009,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,4065,IMM12_OFFSET,NA,1,1,0,1,NA,0x67,4066,0,NA,NA,NA},
+    {0xB010,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,0,NO_OFFSET,NA,NA,0,0,0,NA,0x89,0,0,NA,NA,NA},
+    {0xB011,INSTR_LDRH,AL,AL,1,0,0xABCDEF0123456789,0,IMM8_OFFSET,NA,2,1,0,1,NA,0x6789,2,0,NA,NA,NA},
+    {0xB012,INSTR_LDRH,AL,AL,1,4064,0xABCDEF0123456789,0,REG_OFFSET,4064,0,0,1,0,NA,0x6789,0,0,NA,NA,NA},
+    {0xB013,INSTR_LDRH,AL,AL,1,4064,0xABCDEF0123456789,0,REG_OFFSET,4066,0,0,1,0,NA,0x2345,0,0,NA,NA,NA},
+    {0xB014,INSTR_LDRH,AL,AL,1,0,0xABCDEF0123456789,0,NO_OFFSET,NA,0,0,0,0,NA,0x6789,0,0,NA,NA,NA},
+    {0xB015,INSTR_LDRH,AL,AL,1,0,0xABCDEF0123456789,2,NO_OFFSET,NA,0,0,0,0,NA,0x2345,2,0,NA,NA,NA},
+    {0xB016,INSTR_ADDR_LDR,AL,AL,1,4064,0xABCDEF0123456789,0,IMM12_OFFSET,NA,4064,0,1,0,NA,0xABCDEF0123456789,0,0,NA,NA,NA},
+    {0xB017,INSTR_STR,AL,AL,1,2,0xDEADBEEFDEADBEEF,4,IMM12_OFFSET,NA,4,1,0,1,0xABCDEF0123456789,0xABCDEF0123456789,8,1,2,8,0xDEAD23456789BEEF},
+    {0xB018,INSTR_STR,AL,AL,1,2,0xDEADBEEFDEADBEEF,4,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4,1,2,8,0xDEAD23456789BEEF},
+    {0xB019,INSTR_STR,AL,AL,1,4066,0xDEADBEEFDEADBEEF,4,IMM12_OFFSET,NA,4064,0,1,0,0xABCDEF0123456789,0xABCDEF0123456789,4,1,4066,8,0xDEAD23456789BEEF},
+    {0xB020,INSTR_STRB,AL,AL,1,0,0xDEADBEEFDEADBEEF,1,IMM12_OFFSET,NA,0,0,1,0,0xABCDEF0123456789,0xABCDEF0123456789,1,1,0,8,0xDEADBEEFDEAD89EF},
+    {0xB021,INSTR_STRB,AL,AL,1,0,0xDEADBEEFDEADBEEF,1,IMM12_OFFSET,NA,1,0,1,0,0xABCDEF0123456789,0xABCDEF0123456789,1,1,0,8,0xDEADBEEFDE89BEEF},
+    {0xB022,INSTR_STRB,AL,AL,1,0,0xDEADBEEFDEADBEEF,1,IMM12_OFFSET,NA,2,0,1,0,0xABCDEF0123456789,0xABCDEF0123456789,1,1,0,8,0xDEADBEEF89ADBEEF},
+    {0xB023,INSTR_STRB,AL,AL,1,0,0xDEADBEEFDEADBEEF,1,IMM12_OFFSET,NA,4,1,0,1,0xABCDEF0123456789,0xABCDEF0123456789,5,1,0,8,0xDEADBEEFDEAD89EF},
+    {0xB024,INSTR_STRB,AL,AL,1,0,0xDEADBEEFDEADBEEF,1,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,1,1,0,8,0xDEADBEEFDEAD89EF},
+    {0xB025,INSTR_STRH,AL,AL,1,4066,0xDEADBEEFDEADBEEF,4070,IMM12_OFFSET,NA,2,1,0,1,0xABCDEF0123456789,0xABCDEF0123456789,4072,1,4066,8,0xDEAD6789DEADBEEF},
+    {0xB026,INSTR_STRH,AL,AL,1,4066,0xDEADBEEFDEADBEEF,4070,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4070,1,4066,8,0xDEAD6789DEADBEEF},
+    {0xB027,INSTR_STRH,EQ,NE,1,4066,0xDEADBEEFDEADBEEF,4070,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4070,1,4066,8,0xDEADBEEFDEADBEEF},
+    {0xB028,INSTR_STRH,NE,NE,1,4066,0xDEADBEEFDEADBEEF,4070,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4070,1,4066,8,0xDEAD6789DEADBEEF},
+    {0xB029,INSTR_STRH,NE,EQ,1,4066,0xDEADBEEFDEADBEEF,4070,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4070,1,4066,8,0xDEADBEEFDEADBEEF},
+    {0xB030,INSTR_STRH,EQ,EQ,1,4066,0xDEADBEEFDEADBEEF,4070,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4070,1,4066,8,0xDEAD6789DEADBEEF},
+    {0xB031,INSTR_STRH,HI,LS,1,4066,0xDEADBEEFDEADBEEF,4070,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4070,1,4066,8,0xDEADBEEFDEADBEEF},
+    {0xB032,INSTR_STRH,LS,LS,1,4066,0xDEADBEEFDEADBEEF,4070,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4070,1,4066,8,0xDEAD6789DEADBEEF},
+    {0xB033,INSTR_STRH,LS,HI,1,4066,0xDEADBEEFDEADBEEF,4070,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4070,1,4066,8,0xDEADBEEFDEADBEEF},
+    {0xB034,INSTR_STRH,HI,HI,1,4066,0xDEADBEEFDEADBEEF,4070,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4070,1,4066,8,0xDEAD6789DEADBEEF},
+    {0xB035,INSTR_STRH,CC,HS,1,4066,0xDEADBEEFDEADBEEF,4070,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4070,1,4066,8,0xDEADBEEFDEADBEEF},
+    {0xB036,INSTR_STRH,CS,HS,1,4066,0xDEADBEEFDEADBEEF,4070,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4070,1,4066,8,0xDEAD6789DEADBEEF},
+    {0xB037,INSTR_STRH,GE,LT,1,4066,0xDEADBEEFDEADBEEF,4070,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4070,1,4066,8,0xDEADBEEFDEADBEEF},
+    {0xB038,INSTR_STRH,LT,LT,1,4066,0xDEADBEEFDEADBEEF,4070,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4070,1,4066,8,0xDEAD6789DEADBEEF},
+    {0xB039,INSTR_ADDR_STR,AL,AL,1,4064,0xDEADBEEFDEADBEEF,4,IMM12_OFFSET,NA,4060,0,1,0,0xABCDEF0123456789,0xABCDEF0123456789,4,1,4064,8,0xABCDEF0123456789},
+};
+
+
+int flushcache()
+{
+    const long base = long(instrMem);
+    const long curr = base + long(instrMemSize);
+    return cacheflush(base, curr, 0);
+}
+void dataOpTest(dataOpTest_t test, ARMAssemblerInterface *a64asm, uint32_t Rd = 0,
+                uint32_t Rn = 1, uint32_t Rm = 2, uint32_t Rs = 3)
+{
+    int64_t  regs[NUM_REGS] = {0};
+    int32_t  flags[NUM_FLAGS] = {0};
+    int64_t  savedRegs[NUM_REGS] = {0};
+    uint32_t i;
+    uint32_t op2;
+
+    for(i = 0; i < NUM_REGS; ++i)
+    {
+        regs[i] = i;
+    }
+
+    regs[Rd] = test.RdValue;
+    regs[Rn] = test.RnValue;
+    regs[Rs] = test.RsValue;
+    flags[test.preFlag] = 1;
+    a64asm->reset();
+    a64asm->prolog();
+    if(test.immediate == true)
+    {
+        op2 = a64asm->imm(test.immValue);
+    }
+    else if(test.immediate == false && test.shiftAmount == 0)
+    {
+        op2 = Rm;
+        regs[Rm] = test.RmValue;
+    }
+    else
+    {
+        op2 = a64asm->reg_imm(Rm, test.shiftMode, test.shiftAmount);
+        regs[Rm] = test.RmValue;
+    }
+    switch(test.op)
+    {
+    case INSTR_ADD: a64asm->ADD(test.cond, test.setFlags, Rd,Rn,op2); break;
+    case INSTR_SUB: a64asm->SUB(test.cond, test.setFlags, Rd,Rn,op2); break;
+    case INSTR_RSB: a64asm->RSB(test.cond, test.setFlags, Rd,Rn,op2); break;
+    case INSTR_AND: a64asm->AND(test.cond, test.setFlags, Rd,Rn,op2); break;
+    case INSTR_ORR: a64asm->ORR(test.cond, test.setFlags, Rd,Rn,op2); break;
+    case INSTR_BIC: a64asm->BIC(test.cond, test.setFlags, Rd,Rn,op2); break;
+    case INSTR_MUL: a64asm->MUL(test.cond, test.setFlags, Rd,Rm,Rs); break;
+    case INSTR_MLA: a64asm->MLA(test.cond, test.setFlags, Rd,Rm,Rs,Rn); break;
+    case INSTR_CMP: a64asm->CMP(test.cond, Rn,op2); break;
+    case INSTR_MOV: a64asm->MOV(test.cond, test.setFlags,Rd,op2); break;
+    case INSTR_MVN: a64asm->MVN(test.cond, test.setFlags,Rd,op2); break;
+    case INSTR_SMULBB:a64asm->SMULBB(test.cond, Rd,Rm,Rs); break;
+    case INSTR_SMULBT:a64asm->SMULBT(test.cond, Rd,Rm,Rs); break;
+    case INSTR_SMULTB:a64asm->SMULTB(test.cond, Rd,Rm,Rs); break;
+    case INSTR_SMULTT:a64asm->SMULTT(test.cond, Rd,Rm,Rs); break;
+    case INSTR_SMULWB:a64asm->SMULWB(test.cond, Rd,Rm,Rs); break;
+    case INSTR_SMULWT:a64asm->SMULWT(test.cond, Rd,Rm,Rs); break;
+    case INSTR_SMLABB:a64asm->SMLABB(test.cond, Rd,Rm,Rs,Rn); break;
+    case INSTR_UXTB16:a64asm->UXTB16(test.cond, Rd,Rm,test.shiftAmount); break;
+    case INSTR_UBFX:
+    {
+        int32_t lsb   = test.RsValue;
+        int32_t width = test.RmValue;
+        a64asm->UBFX(test.cond, Rd,Rn,lsb, width);
+        break;
+    }
+    case INSTR_ADDR_ADD: a64asm->ADDR_ADD(test.cond, test.setFlags, Rd,Rn,op2); break;
+    case INSTR_ADDR_SUB: a64asm->ADDR_SUB(test.cond, test.setFlags, Rd,Rn,op2); break;
+    default: printf("Error"); return;
+    }
+    a64asm->epilog(0);
+    flushcache();
+
+    asm_function_t asm_function = (asm_function_t)(instrMem);
+
+    for(i = 0; i < NUM_REGS; ++i)
+        savedRegs[i] = regs[i];
+
+    asm_test_jacket(asm_function, regs, flags);
+
+    /* Check if all regs except Rd is same */
+    for(i = 0; i < NUM_REGS; ++i)
+    {
+        if(i == Rd) continue;
+        if(regs[i] != savedRegs[i])
+        {
+            printf("Test %x failed Reg(%d) tampered Expected(0x%"PRIx64"),"
+                   "Actual(0x%"PRIx64") t\n", test.id, i, savedRegs[i], regs[i]);
+            return;
+        }
+    }
+
+    if(test.checkRd == 1 && (uint64_t)regs[Rd] != test.postRdValue)
+    {
+        printf("Test %x failed, Expected(%"PRIx64"), Actual(%"PRIx64")\n",
+                test.id, test.postRdValue, regs[Rd]);
+    }
+    else if(test.checkFlag == 1 && flags[test.postFlag] == 0)
+    {
+        printf("Test %x failed Flag(%s) NOT set\n",
+                test.id,cc_code[test.postFlag]);
+    }
+    else
+    {
+        printf("Test %x passed\n", test.id);
+    }
+}
+
+
+void dataTransferTest(dataTransferTest_t test, ARMAssemblerInterface *a64asm,
+                      uint32_t Rd = 0, uint32_t Rn = 1,uint32_t Rm = 2)
+{
+    int64_t regs[NUM_REGS] = {0};
+    int64_t savedRegs[NUM_REGS] = {0};
+    int32_t flags[NUM_FLAGS] = {0};
+    uint32_t i;
+    for(i = 0; i < NUM_REGS; ++i)
+    {
+        regs[i] = i;
+    }
+
+    uint32_t op2;
+
+    regs[Rd] = test.RdValue;
+    regs[Rn] = (uint64_t)(&dataMem[test.RnValue]);
+    regs[Rm] = test.RmValue;
+    flags[test.preFlag] = 1;
+
+    if(test.setMem == true)
+    {
+        unsigned char *mem = (unsigned char *)&dataMem[test.memOffset];
+        uint64_t value = test.memValue;
+        for(int j = 0; j < 8; ++j)
+        {
+            mem[j] = value & 0x00FF;
+            value >>= 8;
+        }
+    }
+    a64asm->reset();
+    a64asm->prolog();
+    if(test.offsetType == REG_SCALE_OFFSET)
+    {
+        op2 = a64asm->reg_scale_pre(Rm);
+    }
+    else if(test.offsetType == REG_OFFSET)
+    {
+        op2 = a64asm->reg_pre(Rm);
+    }
+    else if(test.offsetType == IMM12_OFFSET && test.preIndex == true)
+    {
+        op2 = a64asm->immed12_pre(test.immValue, test.writeBack);
+    }
+    else if(test.offsetType == IMM12_OFFSET && test.postIndex == true)
+    {
+        op2 = a64asm->immed12_post(test.immValue);
+    }
+    else if(test.offsetType == IMM8_OFFSET && test.preIndex == true)
+    {
+        op2 = a64asm->immed8_pre(test.immValue, test.writeBack);
+    }
+    else if(test.offsetType == IMM8_OFFSET && test.postIndex == true)
+    {
+        op2 = a64asm->immed8_post(test.immValue);
+    }
+    else if(test.offsetType == NO_OFFSET)
+    {
+        op2 = a64asm->__immed12_pre(0);
+    }
+    else
+    {
+        printf("Error - Unknown offset\n"); return;
+    }
+
+    switch(test.op)
+    {
+    case INSTR_LDR:  a64asm->LDR(test.cond, Rd,Rn,op2); break;
+    case INSTR_LDRB: a64asm->LDRB(test.cond, Rd,Rn,op2); break;
+    case INSTR_LDRH: a64asm->LDRH(test.cond, Rd,Rn,op2); break;
+    case INSTR_ADDR_LDR: a64asm->ADDR_LDR(test.cond, Rd,Rn,op2); break;
+    case INSTR_STR:  a64asm->STR(test.cond, Rd,Rn,op2); break;
+    case INSTR_STRB: a64asm->STRB(test.cond, Rd,Rn,op2); break;
+    case INSTR_STRH: a64asm->STRH(test.cond, Rd,Rn,op2); break;
+    case INSTR_ADDR_STR: a64asm->ADDR_STR(test.cond, Rd,Rn,op2); break;
+    default: printf("Error"); return;
+    }
+    a64asm->epilog(0);
+    flushcache();
+
+    asm_function_t asm_function = (asm_function_t)(instrMem);
+
+    for(i = 0; i < NUM_REGS; ++i)
+        savedRegs[i] = regs[i];
+
+
+    asm_test_jacket(asm_function, regs, flags);
+
+    /* Check if all regs except Rd/Rn are same */
+    for(i = 0; i < NUM_REGS; ++i)
+    {
+        if(i == Rd || i == Rn) continue;
+        if(regs[i] != savedRegs[i])
+        {
+            printf("Test %x failed Reg(%d) tampered"
+                   " Expected(0x%"PRIx64"), Actual(0x%"PRIx64") t\n",
+                   test.id, i, savedRegs[i], regs[i]);
+            return;
+        }
+    }
+
+    if((uint64_t)regs[Rd] != test.postRdValue)
+    {
+        printf("Test %x failed, "
+               "Expected in Rd(0x%"PRIx64"), Actual(0x%"PRIx64")\n",
+               test.id, test.postRdValue, regs[Rd]);
+    }
+    else if((uint64_t)regs[Rn] != (uint64_t)(&dataMem[test.postRnValue]))
+    {
+        printf("Test %x failed, "
+               "Expected in Rn(0x%"PRIx64"), Actual(0x%"PRIx64")\n",
+               test.id, test.postRnValue, regs[Rn] - (uint64_t)dataMem);
+    }
+    else if(test.checkMem == true)
+    {
+        unsigned char *addr = (unsigned char *)&dataMem[test.postMemOffset];
+        uint64_t value;
+        value = 0;
+        for(uint32_t j = 0; j < test.postMemLength; ++j)
+            value = (value << 8) | addr[test.postMemLength-j-1];
+        if(value != test.postMemValue)
+        {
+            printf("Test %x failed, "
+                   "Expected in Mem(0x%"PRIx64"), Actual(0x%"PRIx64")\n",
+                   test.id, test.postMemValue, value);
+        }
+        else
+        {
+            printf("Test %x passed\n", test.id);
+        }
+    }
+    else
+    {
+        printf("Test %x passed\n", test.id);
+    }
+}
+
+void dataTransferLDMSTM(ARMAssemblerInterface *a64asm)
+{
+    int64_t regs[NUM_REGS] = {0};
+    int32_t flags[NUM_FLAGS] = {0};
+    const uint32_t numArmv7Regs = 16;
+
+    uint32_t Rn = ARMAssemblerInterface::SP;
+
+    uint32_t patterns[] =
+    {
+        0x5A03,
+        0x4CF0,
+        0x1EA6,
+        0x0DBF,
+    };
+
+    uint32_t i, j;
+    for(i = 0; i < sizeof(patterns)/sizeof(uint32_t); ++i)
+    {
+        for(j = 0; j < NUM_REGS; ++j)
+        {
+            regs[j] = j;
+        }
+        a64asm->reset();
+        a64asm->prolog();
+        a64asm->STM(AL,ARMAssemblerInterface::DB,Rn,1,patterns[i]);
+        for(j = 0; j < numArmv7Regs; ++j)
+        {
+            uint32_t op2 = a64asm->imm(0x31);
+            a64asm->MOV(AL, 0,j,op2);
+        }
+        a64asm->LDM(AL,ARMAssemblerInterface::IA,Rn,1,patterns[i]);
+        a64asm->epilog(0);
+        flushcache();
+
+        asm_function_t asm_function = (asm_function_t)(instrMem);
+        asm_test_jacket(asm_function, regs, flags);
+
+        for(j = 0; j < numArmv7Regs; ++j)
+        {
+            if((1 << j) & patterns[i])
+            {
+                if(regs[j] != j)
+                {
+                    printf("LDM/STM Test %x failed "
+                           "Reg%d expected(0x%x) Actual(0x%"PRIx64") \n",
+                            patterns[i],j,j,regs[j]);
+                    break;
+                }
+            }
+        }
+        if(j == numArmv7Regs)
+            printf("LDM/STM Test %x passed\n", patterns[i]);
+    }
+}
+
+int main(void)
+{
+    uint32_t i;
+
+    /* Allocate memory to store instructions generated by ArmToAarch64Assembler */
+    {
+        int fd = ashmem_create_region("code cache", instrMemSize);
+        if(fd < 0)
+            printf("Creating code cache, ashmem_create_region "
+                                "failed with error '%s'", strerror(errno));
+        instrMem = mmap(NULL, instrMemSize,
+                                    PROT_READ | PROT_WRITE | PROT_EXEC,
+                                MAP_PRIVATE, fd, 0);
+    }
+
+    ArmToAarch64Assembler a64asm(instrMem);
+
+    if(TESTS_DATAOP_ENABLE)
+    {
+        printf("Running data processing tests\n");
+        for(i = 0; i < sizeof(dataOpTests)/sizeof(dataOpTest_t); ++i)
+            dataOpTest(dataOpTests[i], &a64asm);
+    }
+
+    if(TESTS_DATATRANSFER_ENABLE)
+    {
+        printf("Running data transfer tests\n");
+        for(i = 0; i < sizeof(dataTransferTests)/sizeof(dataTransferTest_t); ++i)
+            dataTransferTest(dataTransferTests[i], &a64asm);
+    }
+
+    if(TESTS_LDMSTM_ENABLE)
+    {
+        printf("Running LDM/STM tests\n");
+        dataTransferLDMSTM(&a64asm);
+    }
+
+
+    if(TESTS_REG_CORRUPTION_ENABLE)
+    {
+        uint32_t reg_list[] = {0,1,12,14};
+        uint32_t Rd, Rm, Rs, Rn;
+        uint32_t i;
+        uint32_t numRegs = sizeof(reg_list)/sizeof(uint32_t);
+
+        printf("Running Register corruption tests\n");
+        for(i = 0; i < sizeof(dataOpTests)/sizeof(dataOpTest_t); ++i)
+        {
+            for(Rd = 0; Rd < numRegs; ++Rd)
+            {
+                for(Rn = 0; Rn < numRegs; ++Rn)
+                {
+                    for(Rm = 0; Rm < numRegs; ++Rm)
+                    {
+                        for(Rs = 0; Rs < numRegs;++Rs)
+                        {
+                            if(Rd == Rn || Rd == Rm || Rd == Rs) continue;
+                            if(Rn == Rm || Rn == Rs) continue;
+                            if(Rm == Rs) continue;
+                            printf("Testing combination Rd(%d), Rn(%d),"
+                                   " Rm(%d), Rs(%d): ",
+                                   reg_list[Rd], reg_list[Rn], reg_list[Rm], reg_list[Rs]);
+                            dataOpTest(dataOpTests[i], &a64asm, reg_list[Rd],
+                                       reg_list[Rn], reg_list[Rm], reg_list[Rs]);
+                        }
+                    }
+                }
+            }
+        }
+    }
+    return 0;
+}
diff --git a/libpixelflinger/tests/arch-aarch64/assembler/asm_test_jacket.S b/libpixelflinger/tests/arch-aarch64/assembler/asm_test_jacket.S
new file mode 100644
index 0000000..a1392c2
--- /dev/null
+++ b/libpixelflinger/tests/arch-aarch64/assembler/asm_test_jacket.S
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+    .text
+    .align
+
+    .global asm_test_jacket
+
+    // Set the register and flag values
+    // Calls the asm function
+    // Reads the register/flag values to output register
+
+    // Parameters
+    // X0 - Function to jump
+    // X1 - register values array
+    // X2 - flag values array
+asm_test_jacket:
+    // Save registers to stack
+    stp    x29, x30, [sp,#-16]!
+    stp    x27, x28, [sp,#-16]!
+
+    mov x30, x0
+    mov x28, x1
+    mov x27, x2
+
+    //Set the flags based on flag array
+    //EQ
+    ldr w0, [x27,#0]
+    cmp w0, #1
+    b.ne bt_aeq
+    cmp w0,#1
+    b bt_end
+bt_aeq:
+
+    //NE
+    ldr w0, [x27,#4]
+    cmp w0, #1
+    b.ne bt_ane
+    cmp w0,#2
+    b bt_end
+bt_ane:
+
+    //CS
+    ldr w0, [x27,#8]
+    cmp w0, #1
+    b.ne bt_acs
+    cmp w0,#0
+    b bt_end
+bt_acs:
+
+    //CC
+    ldr w0, [x27,#12]
+    cmp w0, #1
+    b.ne bt_acc
+    cmp w0,#2
+    b bt_end
+bt_acc:
+
+    //MI
+    ldr w0, [x27,#16]
+    cmp w0, #1
+    b.ne bt_ami
+    subs w0,w0,#2
+    b bt_end
+bt_ami:
+
+    //PL
+    ldr w0, [x27,#20]
+    cmp w0, #1
+    b.ne bt_apl
+    subs w0,w0,#0
+    b bt_end
+bt_apl:
+    //HI - (C==1) && (Z==0)
+    ldr w0, [x27,#32]
+    cmp w0, #1
+    b.ne bt_ahi
+    cmp w0,#0
+    b bt_end
+bt_ahi:
+
+    //LS - (C==0) || (Z==1)
+    ldr w0, [x27,#36]
+    cmp w0, #1
+    b.ne bt_als
+    cmp w0,#1
+    b bt_end
+bt_als:
+
+    //GE
+    ldr w0, [x27,#40]
+    cmp w0, #1
+    b.ne bt_age
+    cmp w0,#0
+    b bt_end
+bt_age:
+
+    //LT
+    ldr w0, [x27,#44]
+    cmp w0, #1
+    b.ne bt_alt
+    cmp w0,#2
+    b bt_end
+bt_alt:
+
+    //GT
+    ldr w0, [x27,#48]
+    cmp w0, #1
+    b.ne bt_agt
+    cmp w0,#0
+    b bt_end
+bt_agt:
+
+    //LE
+    ldr w0, [x27,#52]
+    cmp w0, #1
+    b.ne bt_ale
+    cmp w0,#2
+    b bt_end
+bt_ale:
+
+
+bt_end:
+
+    // Load the registers from reg array
+    ldr x0, [x28,#0]
+    ldr x1, [x28,#8]
+    ldr x2, [x28,#16]
+    ldr x3, [x28,#24]
+    ldr x4, [x28,#32]
+    ldr x5, [x28,#40]
+    ldr x6, [x28,#48]
+    ldr x7, [x28,#56]
+    ldr x8, [x28,#64]
+    ldr x9, [x28,#72]
+    ldr x10, [x28,#80]
+    ldr x11, [x28,#88]
+    ldr x12, [x28,#96]
+    ldr x14, [x28,#112]
+
+    // Call the function
+    blr X30
+
+    // Save the registers to reg array
+    str x0, [x28,#0]
+    str x1, [x28,#8]
+    str x2, [x28,#16]
+    str x3, [x28,#24]
+    str x4, [x28,#32]
+    str x5, [x28,#40]
+    str x6, [x28,#48]
+    str x7, [x28,#56]
+    str x8, [x28,#64]
+    str x9, [x28,#72]
+    str x10, [x28,#80]
+    str x11, [x28,#88]
+    str x12, [x28,#96]
+    str x14, [x28,#112]
+
+    //Set the flags array based on result flags
+    movz w0, #0
+    movz w1, #1
+    csel w2, w1, w0, EQ
+    str w2, [x27,#0]
+    csel w2, w1, w0, NE
+    str w2, [x27,#4]
+    csel w2, w1, w0, CS
+    str w2, [x27,#8]
+    csel w2, w1, w0, CC
+    str w2, [x27,#12]
+    csel w2, w1, w0, MI
+    str w2, [x27,#16]
+    csel w2, w1, w0, PL
+    str w2, [x27,#20]
+    csel w2, w1, w0, VS
+    str w2, [x27,#24]
+    csel w2, w1, w0, VC
+    str w2, [x27,#28]
+    csel w2, w1, w0, HI
+    str w2, [x27,#32]
+    csel w2, w1, w0, LS
+    str w2, [x27,#36]
+    csel w2, w1, w0, GE
+    str w2, [x27,#40]
+    csel w2, w1, w0, LT
+    str w2, [x27,#44]
+    csel w2, w1, w0, GT
+    str w2, [x27,#48]
+    csel w2, w1, w0, LE
+    str w2, [x27,#52]
+
+    // Restore registers from stack
+    ldp    x27, x28, [sp],#16
+    ldp    x29, x30, [sp],#16
+    ret
+
diff --git a/libpixelflinger/tests/arch-aarch64/col32cb16blend/Android.mk b/libpixelflinger/tests/arch-aarch64/col32cb16blend/Android.mk
new file mode 100644
index 0000000..7445fc8
--- /dev/null
+++ b/libpixelflinger/tests/arch-aarch64/col32cb16blend/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+    col32cb16blend_test.c \
+    ../../../arch-aarch64/col32cb16blend.S
+
+LOCAL_SHARED_LIBRARIES :=
+
+LOCAL_C_INCLUDES :=
+
+LOCAL_MODULE:= test-pixelflinger-aarch64-col32cb16blend
+
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_EXECUTABLE)
diff --git a/libpixelflinger/tests/arch-aarch64/col32cb16blend/col32cb16blend_test.c b/libpixelflinger/tests/arch-aarch64/col32cb16blend/col32cb16blend_test.c
new file mode 100644
index 0000000..f057884
--- /dev/null
+++ b/libpixelflinger/tests/arch-aarch64/col32cb16blend/col32cb16blend_test.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+
+#define ARGB_8888_MAX   0xFFFFFFFF
+#define ARGB_8888_MIN   0x00000000
+#define RGB_565_MAX 0xFFFF
+#define RGB_565_MIN 0x0000
+
+struct test_t
+{
+    char name[256];
+    uint32_t dst_color;
+    uint32_t src_color;
+    size_t count;
+};
+
+struct test_t tests[] =
+{
+    {"Count 1, Src=Max, Dst=Min", ARGB_8888_MAX, RGB_565_MIN, 1},
+    {"Count 2, Src=Min, Dst=Max", ARGB_8888_MIN, RGB_565_MAX, 2},
+    {"Count 3, Src=Max, Dst=Max", ARGB_8888_MAX, RGB_565_MAX, 3},
+    {"Count 4, Src=Min, Dst=Min", ARGB_8888_MAX, RGB_565_MAX, 4},
+    {"Count 1, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 1},
+    {"Count 2, Src=Rand, Dst=Rand", 0xABCDEF12, 0x2345, 2},
+    {"Count 3, Src=Rand, Dst=Rand", 0x11111111, 0xEDFE, 3},
+    {"Count 4, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 4},
+    {"Count 5, Src=Rand, Dst=Rand", 0xEFEFFEFE, 0xFACC, 5},
+    {"Count 10, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 10}
+};
+
+void scanline_col32cb16blend_aarch64(uint16_t *dst, int32_t src, size_t count);
+void scanline_col32cb16blend_c(uint16_t * dst, int32_t src, size_t count)
+{
+    int srcAlpha = (src>>24);
+    int f = 0x100 - (srcAlpha + (srcAlpha>>7));
+    while (count--)
+    {
+        uint16_t d = *dst;
+        int dstR = (d>>11)&0x1f;
+        int dstG = (d>>5)&0x3f;
+        int dstB = (d)&0x1f;
+        int srcR = (src >> (   3))&0x1F;
+        int srcG = (src >> ( 8+2))&0x3F;
+        int srcB = (src >> (16+3))&0x1F;
+        srcR += (f*dstR)>>8;
+        srcG += (f*dstG)>>8;
+        srcB += (f*dstB)>>8;
+        *dst++ = (uint16_t)((srcR<<11)|(srcG<<5)|srcB);
+    }
+}
+
+void scanline_col32cb16blend_test()
+{
+    uint16_t dst_c[16], dst_asm[16];
+    uint32_t i, j;
+
+    for(i = 0; i < sizeof(tests)/sizeof(struct test_t); ++i)
+    {
+        struct test_t test = tests[i];
+
+        printf("Testing - %s:",test.name);
+
+        memset(dst_c, 0, sizeof(dst_c));
+        memset(dst_asm, 0, sizeof(dst_asm));
+
+        for(j = 0; j < test.count; ++j)
+        {
+            dst_c[j]   = test.dst_color;
+            dst_asm[j] = test.dst_color;
+        }
+
+
+        scanline_col32cb16blend_c(dst_c, test.src_color, test.count);
+        scanline_col32cb16blend_aarch64(dst_asm, test.src_color, test.count);
+
+
+        if(memcmp(dst_c, dst_asm, sizeof(dst_c)) == 0)
+            printf("Passed\n");
+        else
+            printf("Failed\n");
+
+        for(j = 0; j < test.count; ++j)
+        {
+            printf("dst_c[%d] = %x, dst_asm[%d] = %x \n", j, dst_c[j], j, dst_asm[j]);
+        }
+    }
+}
+
+int main()
+{
+    scanline_col32cb16blend_test();
+    return 0;
+}
diff --git a/libpixelflinger/tests/arch-aarch64/disassembler/Android.mk b/libpixelflinger/tests/arch-aarch64/disassembler/Android.mk
new file mode 100644
index 0000000..376c3b7
--- /dev/null
+++ b/libpixelflinger/tests/arch-aarch64/disassembler/Android.mk
@@ -0,0 +1,17 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+    aarch64_diassembler_test.cpp \
+    ../../../codeflinger/Aarch64Disassembler.cpp
+
+LOCAL_SHARED_LIBRARIES :=
+
+LOCAL_C_INCLUDES := \
+    system/core/libpixelflinger/codeflinger
+
+LOCAL_MODULE:= test-pixelflinger-aarch64-disassembler-test
+
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_EXECUTABLE)
diff --git a/libpixelflinger/tests/arch-aarch64/disassembler/aarch64_diassembler_test.cpp b/libpixelflinger/tests/arch-aarch64/disassembler/aarch64_diassembler_test.cpp
new file mode 100644
index 0000000..17caee1
--- /dev/null
+++ b/libpixelflinger/tests/arch-aarch64/disassembler/aarch64_diassembler_test.cpp
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <stdio.h>
+#include <inttypes.h>
+#include <string.h>
+
+int aarch64_disassemble(uint32_t code, char* instr);
+
+struct test_table_entry_t
+{
+     uint32_t code;
+     const char *instr;
+};
+static test_table_entry_t test_table [] =
+{
+    { 0x91000240, "add x0, x18, #0x0, lsl #0"         },
+    { 0x9140041f, "add sp, x0, #0x1, lsl #12"         },
+    { 0x917ffff2, "add x18, sp, #0xfff, lsl #12"      },
+
+    { 0xd13ffe40, "sub x0, x18, #0xfff, lsl #0"       },
+    { 0xd140001f, "sub sp, x0, #0x0, lsl #12"         },
+    { 0xd14007f2, "sub x18, sp, #0x1, lsl #12"        },
+
+    { 0x8b1e0200, "add x0, x16, x30, lsl #0"          },
+    { 0x8b507fdf, "add xzr, x30, x16, lsr #31"        },
+    { 0x8b8043f0, "add x16, xzr, x0, asr #16"         },
+    { 0x8b5f401e, "add x30, x0, xzr, lsr #16"         },
+
+
+    { 0x4b1e0200, "sub w0, w16, w30, lsl #0"          },
+    { 0x4b507fdf, "sub wzr, w30, w16, lsr #31"        },
+    { 0x4b8043f0, "sub w16, wzr, w0, asr #16"         },
+    { 0x4b5f401e, "sub w30, w0, wzr, lsr #16"         },
+
+    { 0x6b1e0200, "subs w0, w16, w30, lsl #0"         },
+    { 0x6b507fdf, "subs wzr, w30, w16, lsr #31"       },
+    { 0x6b8043f0, "subs w16, wzr, w0, asr #16"        },
+    { 0x6b5f401e, "subs w30, w0, wzr, lsr #16"        },
+
+    { 0x0a1e0200, "and w0, w16, w30, lsl #0"          },
+    { 0x0a507fdf, "and wzr, w30, w16, lsr #31"        },
+    { 0x0a8043f0, "and w16, wzr, w0, asr #16"         },
+    { 0x0adf401e, "and w30, w0, wzr, ror #16"         },
+
+    { 0x2a1e0200, "orr w0, w16, w30, lsl #0"          },
+    { 0x2a507fdf, "orr wzr, w30, w16, lsr #31"        },
+    { 0x2a8043f0, "orr w16, wzr, w0, asr #16"         },
+    { 0x2adf401e, "orr w30, w0, wzr, ror #16"         },
+
+    { 0x2a3e0200, "orn w0, w16, w30, lsl #0"          },
+    { 0x2a707fdf, "orn wzr, w30, w16, lsr #31"        },
+    { 0x2aa043f0, "orn w16, wzr, w0, asr #16"         },
+    { 0x2aff401e, "orn w30, w0, wzr, ror #16"         },
+
+    { 0x729fffe0, "movk w0, #0xffff, lsl #0"          },
+    { 0x72a0000f, "movk w15, #0x0, lsl #16"           },
+    { 0x7281fffe, "movk w30, #0xfff, lsl #0"          },
+    { 0x72a0003f, "movk wzr, #0x1, lsl #16"           },
+
+    { 0x529fffe0, "movz w0, #0xffff, lsl #0"          },
+    { 0x52a0000f, "movz w15, #0x0, lsl #16"           },
+    { 0x5281fffe, "movz w30, #0xfff, lsl #0"          },
+    { 0x52a0003f, "movz wzr, #0x1, lsl #16"           },
+
+    { 0xd29fffe0, "movz x0, #0xffff, lsl #0"          },
+    { 0xd2a0000f, "movz x15, #0x0, lsl #16"           },
+    { 0xd2c1fffe, "movz x30, #0xfff, lsl #32"         },
+    { 0xd2e0003f, "movz xzr, #0x1, lsl #48"           },
+
+    { 0x1a8003e0, "csel w0, wzr, w0, eq"              },
+    { 0x1a831001, "csel w1, w0, w3, ne"               },
+    { 0x1a9e2022, "csel w2, w1, w30, cs"              },
+    { 0x1a8a3083, "csel w3, w4, w10, cc"              },
+    { 0x1a8b40e4, "csel w4, w7, w11, mi"              },
+    { 0x1a9b5105, "csel w5, w8, w27, pl"              },
+    { 0x1a846167, "csel w7, w11, w4, vs"              },
+    { 0x1a8671c8, "csel w8, w14, w6, vc"              },
+    { 0x1a878289, "csel w9, w20, w7, hi"              },
+    { 0x1a8c92aa, "csel w10, w21, w12, ls"            },
+    { 0x1a8ea2ce, "csel w14, w22, w14, ge"            },
+    { 0x1a9fb3b2, "csel w18, w29, wzr, lt"            },
+    { 0x1a9fc3d8, "csel w24, w30, wzr, gt"            },
+    { 0x1a82d17e, "csel w30, w11, w2, le"             },
+    { 0x1a81e19f, "csel wzr, w12, w1, al"             },
+
+    { 0x9a8003e0, "csel x0, xzr, x0, eq"              },
+    { 0x9a831001, "csel x1, x0, x3, ne"               },
+    { 0x9a9e2022, "csel x2, x1, x30, cs"              },
+    { 0x9a8a3083, "csel x3, x4, x10, cc"              },
+    { 0x9a8b40e4, "csel x4, x7, x11, mi"              },
+    { 0x9a9b5105, "csel x5, x8, x27, pl"              },
+    { 0x9a846167, "csel x7, x11, x4, vs"              },
+    { 0x9a8671c8, "csel x8, x14, x6, vc"              },
+    { 0x9a878289, "csel x9, x20, x7, hi"              },
+    { 0x9a8c92aa, "csel x10, x21, x12, ls"            },
+    { 0x9a8ea2ce, "csel x14, x22, x14, ge"            },
+    { 0x9a9fb3b2, "csel x18, x29, xzr, lt"            },
+    { 0x9a9fc3d8, "csel x24, x30, xzr, gt"            },
+    { 0x9a82d17e, "csel x30, x11, x2, le"             },
+    { 0x9a81e19f, "csel xzr, x12, x1, al"             },
+
+    { 0x5a8003e0, "csinv w0, wzr, w0, eq"             },
+    { 0x5a831001, "csinv w1, w0, w3, ne"              },
+    { 0x5a9e2022, "csinv w2, w1, w30, cs"             },
+    { 0x5a8a3083, "csinv w3, w4, w10, cc"             },
+    { 0x5a8b40e4, "csinv w4, w7, w11, mi"             },
+    { 0x5a9b5105, "csinv w5, w8, w27, pl"             },
+    { 0x5a846167, "csinv w7, w11, w4, vs"             },
+    { 0x5a8671c8, "csinv w8, w14, w6, vc"             },
+    { 0x5a878289, "csinv w9, w20, w7, hi"             },
+    { 0x5a8c92aa, "csinv w10, w21, w12, ls"           },
+    { 0x5a8ea2ce, "csinv w14, w22, w14, ge"           },
+    { 0x5a9fb3b2, "csinv w18, w29, wzr, lt"           },
+    { 0x5a9fc3d8, "csinv w24, w30, wzr, gt"           },
+    { 0x5a82d17e, "csinv w30, w11, w2, le"            },
+    { 0x5a81e19f, "csinv wzr, w12, w1, al"            },
+
+    { 0x1b1f3fc0, "madd w0, w30, wzr, w15"            },
+    { 0x1b0079ef, "madd w15, w15, w0, w30"            },
+    { 0x1b0f7ffe, "madd w30, wzr, w15, wzr"           },
+    { 0x1b1e001f, "madd wzr, w0, w30, w0"             },
+
+    { 0x9b3f3fc0, "smaddl x0, w30, wzr, x15"          },
+    { 0x9b2079ef, "smaddl x15, w15, w0, x30"          },
+    { 0x9b2f7ffe, "smaddl x30, wzr, w15, xzr"         },
+    { 0x9b3e001f, "smaddl xzr, w0, w30, x0"           },
+
+    { 0xd65f0000, "ret x0"                            },
+    { 0xd65f01e0, "ret x15"                           },
+    { 0xd65f03c0, "ret x30"                           },
+    { 0xd65f03e0, "ret xzr"                           },
+
+    { 0xb87f4be0, "ldr w0, [sp, wzr, uxtw #0]"        },
+    { 0xb87ed80f, "ldr w15, [x0, w30, sxtw #2]"       },
+    { 0xb86fc9fe, "ldr w30, [x15, w15, sxtw #0]"      },
+    { 0xb8605bdf, "ldr wzr, [x30, w0, uxtw #2]"       },
+    { 0xb87febe0, "ldr w0, [sp, xzr, sxtx #0]"        },
+    { 0xb87e780f, "ldr w15, [x0, x30, lsl #2]"        },
+    { 0xb86f69fe, "ldr w30, [x15, x15, lsl #0]"       },
+    { 0xb860fbdf, "ldr wzr, [x30, x0, sxtx #2]"       },
+
+    { 0xb83f4be0, "str w0, [sp, wzr, uxtw #0]"        },
+    { 0xb83ed80f, "str w15, [x0, w30, sxtw #2]"       },
+    { 0xb82fc9fe, "str w30, [x15, w15, sxtw #0]"      },
+    { 0xb8205bdf, "str wzr, [x30, w0, uxtw #2]"       },
+    { 0xb83febe0, "str w0, [sp, xzr, sxtx #0]"        },
+    { 0xb83e780f, "str w15, [x0, x30, lsl #2]"        },
+    { 0xb82f69fe, "str w30, [x15, x15, lsl #0]"       },
+    { 0xb820fbdf, "str wzr, [x30, x0, sxtx #2]"       },
+
+    { 0x787f4be0, "ldrh w0, [sp, wzr, uxtw #0]"       },
+    { 0x787ed80f, "ldrh w15, [x0, w30, sxtw #1]"      },
+    { 0x786fc9fe, "ldrh w30, [x15, w15, sxtw #0]"     },
+    { 0x78605bdf, "ldrh wzr, [x30, w0, uxtw #1]"      },
+    { 0x787febe0, "ldrh w0, [sp, xzr, sxtx #0]"       },
+    { 0x787e780f, "ldrh w15, [x0, x30, lsl #1]"       },
+    { 0x786f69fe, "ldrh w30, [x15, x15, lsl #0]"      },
+    { 0x7860fbdf, "ldrh wzr, [x30, x0, sxtx #1]"      },
+
+    { 0x783f4be0, "strh w0, [sp, wzr, uxtw #0]"       },
+    { 0x783ed80f, "strh w15, [x0, w30, sxtw #1]"      },
+    { 0x782fc9fe, "strh w30, [x15, w15, sxtw #0]"     },
+    { 0x78205bdf, "strh wzr, [x30, w0, uxtw #1]"      },
+    { 0x783febe0, "strh w0, [sp, xzr, sxtx #0]"       },
+    { 0x783e780f, "strh w15, [x0, x30, lsl #1]"       },
+    { 0x782f69fe, "strh w30, [x15, x15, lsl #0]"      },
+    { 0x7820fbdf, "strh wzr, [x30, x0, sxtx #1]"      },
+
+    { 0x387f5be0, "ldrb w0, [sp, wzr, uxtw #0]"       },
+    { 0x387ec80f, "ldrb w15, [x0, w30, sxtw ]"        },
+    { 0x386fd9fe, "ldrb w30, [x15, w15, sxtw #0]"     },
+    { 0x38604bdf, "ldrb wzr, [x30, w0, uxtw ]"        },
+    { 0x387ffbe0, "ldrb w0, [sp, xzr, sxtx #0]"       },
+    { 0x387e780f, "ldrb w15, [x0, x30, lsl #0]"       },
+    { 0x386f79fe, "ldrb w30, [x15, x15, lsl #0]"      },
+    { 0x3860ebdf, "ldrb wzr, [x30, x0, sxtx ]"        },
+
+    { 0x383f5be0, "strb w0, [sp, wzr, uxtw #0]"       },
+    { 0x383ec80f, "strb w15, [x0, w30, sxtw ]"        },
+    { 0x382fd9fe, "strb w30, [x15, w15, sxtw #0]"     },
+    { 0x38204bdf, "strb wzr, [x30, w0, uxtw ]"        },
+    { 0x383ffbe0, "strb w0, [sp, xzr, sxtx #0]"       },
+    { 0x383e780f, "strb w15, [x0, x30, lsl #0]"       },
+    { 0x382f79fe, "strb w30, [x15, x15, lsl #0]"      },
+    { 0x3820ebdf, "strb wzr, [x30, x0, sxtx ]"        },
+
+    { 0xf87f4be0, "ldr x0, [sp, wzr, uxtw #0]"        },
+    { 0xf87ed80f, "ldr x15, [x0, w30, sxtw #3]"       },
+    { 0xf86fc9fe, "ldr x30, [x15, w15, sxtw #0]"      },
+    { 0xf8605bdf, "ldr xzr, [x30, w0, uxtw #3]"       },
+    { 0xf87febe0, "ldr x0, [sp, xzr, sxtx #0]"        },
+    { 0xf87e780f, "ldr x15, [x0, x30, lsl #3]"        },
+    { 0xf86f69fe, "ldr x30, [x15, x15, lsl #0]"       },
+    { 0xf860fbdf, "ldr xzr, [x30, x0, sxtx #3]"       },
+
+    { 0xf83f4be0, "str x0, [sp, wzr, uxtw #0]"        },
+    { 0xf83ed80f, "str x15, [x0, w30, sxtw #3]"       },
+    { 0xf82fc9fe, "str x30, [x15, w15, sxtw #0]"      },
+    { 0xf8205bdf, "str xzr, [x30, w0, uxtw #3]"       },
+    { 0xf83febe0, "str x0, [sp, xzr, sxtx #0]"        },
+    { 0xf83e780f, "str x15, [x0, x30, lsl #3]"        },
+    { 0xf82f69fe, "str x30, [x15, x15, lsl #0]"       },
+    { 0xf820fbdf, "str xzr, [x30, x0, sxtx #3]"       },
+
+    { 0xb85007e0, "ldr w0, [sp], #-256"               },
+    { 0xb840040f, "ldr w15, [x0], #0"                 },
+    { 0xb84015fe, "ldr w30, [x15], #1"                },
+    { 0xb84ff7df, "ldr wzr, [x30], #255"              },
+    { 0xb8100fe0, "str w0, [sp, #-256]!"              },
+    { 0xb8000c0f, "str w15, [x0, #0]!"                },
+    { 0xb8001dfe, "str w30, [x15, #1]!"               },
+    { 0xb80fffdf, "str wzr, [x30, #255]!"             },
+
+    { 0x13017be0, "sbfm w0, wzr, #1, #30"             },
+    { 0x131e7fcf, "sbfm w15, w30, #30, #31"           },
+    { 0x131f01fe, "sbfm w30, w15, #31, #0"            },
+    { 0x1300041f, "sbfm wzr, w0, #0, #1"              },
+
+    { 0x53017be0, "ubfm w0, wzr, #1, #30"             },
+    { 0x531e7fcf, "ubfm w15, w30, #30, #31"           },
+    { 0x531f01fe, "ubfm w30, w15, #31, #0"            },
+    { 0x5300041f, "ubfm wzr, w0, #0, #1"              },
+    { 0xd3417fe0, "ubfm x0, xzr, #1, #31"             },
+    { 0xd35fffcf, "ubfm x15, x30, #31, #63"           },
+    { 0xd35f01fe, "ubfm x30, x15, #31, #0"            },
+    { 0xd340041f, "ubfm xzr, x0, #0, #1"              },
+
+    { 0x139e7be0, "extr w0, wzr, w30, #30"            },
+    { 0x138f7fcf, "extr w15, w30, w15, #31"           },
+    { 0x138001fe, "extr w30, w15, w0, #0"             },
+    { 0x139f041f, "extr wzr, w0, wzr, #1"             },
+
+    { 0x54000020, "b.eq #.+4"                         },
+    { 0x54000201, "b.ne #.+64"                        },
+    { 0x54000802, "b.cs #.+256"                       },
+    { 0x54002003, "b.cc #.+1024"                      },
+    { 0x54008004, "b.mi #.+4096"                      },
+    { 0x54ffffe5, "b.pl #.-4"                         },
+    { 0x54ffff06, "b.vs #.-32"                        },
+    { 0x54fffc07, "b.vc #.-128"                       },
+    { 0x54fff008, "b.hi #.-512"                       },
+    { 0x54000049, "b.ls #.+8"                         },
+    { 0x5400006a, "b.ge #.+12"                        },
+    { 0x5400008b, "b.lt #.+16"                        },
+    { 0x54ffffcc, "b.gt #.-8"                         },
+    { 0x54ffffad, "b.le #.-12"                        },
+    { 0x54ffff8e, "b.al #.-16"                        },
+
+    { 0x8b2001e0, "add x0, x15, w0, uxtb #0"          },
+    { 0x8b2f27cf, "add x15, x30, w15, uxth #1"        },
+    { 0x8b3e4bfe, "add x30, sp, w30, uxtw #2"         },
+    { 0x8b3f6c1f, "add sp, x0, xzr, uxtx #3"          },
+    { 0x8b2091e0, "add x0, x15, w0, sxtb #4"          },
+    { 0x8b2fa3cf, "add x15, x30, w15, sxth #0"        },
+    { 0x8b3ec7fe, "add x30, sp, w30, sxtw #1"         },
+    { 0x8b3fe81f, "add sp, x0, xzr, sxtx #2"          },
+
+    { 0xcb2001e0, "sub x0, x15, w0, uxtb #0"          },
+    { 0xcb2f27cf, "sub x15, x30, w15, uxth #1"        },
+    { 0xcb3e4bfe, "sub x30, sp, w30, uxtw #2"         },
+    { 0xcb3f6c1f, "sub sp, x0, xzr, uxtx #3"          },
+    { 0xcb2091e0, "sub x0, x15, w0, sxtb #4"          },
+    { 0xcb2fa3cf, "sub x15, x30, w15, sxth #0"        },
+    { 0xcb3ec7fe, "sub x30, sp, w30, sxtw #1"         },
+    { 0xcb3fe81f, "sub sp, x0, xzr, sxtx #2"          }
+};
+
+int main()
+{
+    char instr[256];
+    uint32_t failed = 0;
+    for(uint32_t i = 0; i < sizeof(test_table)/sizeof(test_table_entry_t); ++i)
+    {
+        test_table_entry_t *test;
+        test = &test_table[i];
+        aarch64_disassemble(test->code, instr);
+        if(strcmp(instr, test->instr) != 0)
+        {
+            printf("Test Failed \n"
+                   "Code     : 0x%0x\n"
+                   "Expected : %s\n"
+                   "Actual   : %s\n", test->code, test->instr, instr);
+            failed++;
+        }
+    }
+    if(failed == 0)
+    {
+        printf("All tests PASSED\n");
+        return 0;
+    }
+    else
+    {
+        printf("%d tests FAILED\n", failed);
+        return -1;
+    }
+}
diff --git a/libpixelflinger/tests/arch-aarch64/t32cb16blend/Android.mk b/libpixelflinger/tests/arch-aarch64/t32cb16blend/Android.mk
new file mode 100644
index 0000000..a67f0e3
--- /dev/null
+++ b/libpixelflinger/tests/arch-aarch64/t32cb16blend/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+    t32cb16blend_test.c \
+    ../../../arch-aarch64/t32cb16blend.S
+
+LOCAL_SHARED_LIBRARIES :=
+
+LOCAL_C_INCLUDES :=
+
+LOCAL_MODULE:= test-pixelflinger-aarch64-t32cb16blend
+
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_EXECUTABLE)
diff --git a/libpixelflinger/tests/arch-aarch64/t32cb16blend/t32cb16blend_test.c b/libpixelflinger/tests/arch-aarch64/t32cb16blend/t32cb16blend_test.c
new file mode 100644
index 0000000..bcde3e6
--- /dev/null
+++ b/libpixelflinger/tests/arch-aarch64/t32cb16blend/t32cb16blend_test.c
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#define ARGB_8888_MAX   0xFFFFFFFF
+#define ARGB_8888_MIN   0x00000000
+#define RGB_565_MAX     0xFFFF
+#define RGB_565_MIN     0x0000
+
+struct test_t
+{
+    char name[256];
+    uint32_t src_color;
+    uint16_t dst_color;
+    size_t count;
+};
+
+struct test_t tests[] =
+{
+    {"Count 0", 0, 0, 0},
+    {"Count 1, Src=Max, Dst=Min", ARGB_8888_MAX, RGB_565_MIN, 1},
+    {"Count 2, Src=Min, Dst=Max", ARGB_8888_MIN, RGB_565_MAX, 2},
+    {"Count 3, Src=Max, Dst=Max", ARGB_8888_MAX, RGB_565_MAX, 3},
+    {"Count 4, Src=Min, Dst=Min", ARGB_8888_MAX, RGB_565_MAX, 4},
+    {"Count 1, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 1},
+    {"Count 2, Src=Rand, Dst=Rand", 0xABCDEF12, 0x2345, 2},
+    {"Count 3, Src=Rand, Dst=Rand", 0x11111111, 0xEDFE, 3},
+    {"Count 4, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 4},
+    {"Count 5, Src=Rand, Dst=Rand", 0xEFEFFEFE, 0xFACC, 5},
+    {"Count 10, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 10}
+
+};
+
+void scanline_t32cb16blend_aarch64(uint16_t*, uint32_t*, size_t);
+void scanline_t32cb16blend_c(uint16_t * dst, uint32_t* src, size_t count)
+{
+    while (count--)
+    {
+        uint16_t d = *dst;
+        uint32_t s = *src++;
+        int dstR = (d>>11)&0x1f;
+        int dstG = (d>>5)&0x3f;
+        int dstB = (d)&0x1f;
+        int srcR = (s >> (   3))&0x1F;
+        int srcG = (s >> ( 8+2))&0x3F;
+        int srcB = (s >> (16+3))&0x1F;
+        int srcAlpha = (s>>24) & 0xFF;
+
+
+        int f = 0x100 - (srcAlpha + ((srcAlpha>>7) & 0x1));
+        srcR += (f*dstR)>>8;
+        srcG += (f*dstG)>>8;
+        srcB += (f*dstB)>>8;
+        srcR = srcR > 0x1F? 0x1F: srcR;
+        srcG = srcG > 0x3F? 0x3F: srcG;
+        srcB = srcB > 0x1F? 0x1F: srcB;
+        *dst++ = (uint16_t)((srcR<<11)|(srcG<<5)|srcB);
+    }
+}
+
+void scanline_t32cb16blend_test()
+{
+    uint16_t dst_c[16], dst_asm[16];
+    uint32_t src[16];
+    uint32_t i;
+    uint32_t  j;
+
+    for(i = 0; i < sizeof(tests)/sizeof(struct test_t); ++i)
+    {
+        struct test_t test = tests[i];
+
+        printf("Testing - %s:",test.name);
+
+        memset(dst_c, 0, sizeof(dst_c));
+        memset(dst_asm, 0, sizeof(dst_asm));
+
+        for(j = 0; j < test.count; ++j)
+        {
+            dst_c[j]   = test.dst_color;
+            dst_asm[j] = test.dst_color;
+            src[j] = test.src_color;
+        }
+
+        scanline_t32cb16blend_c(dst_c,src,test.count);
+        scanline_t32cb16blend_aarch64(dst_asm,src,test.count);
+
+
+        if(memcmp(dst_c, dst_asm, sizeof(dst_c)) == 0)
+            printf("Passed\n");
+        else
+            printf("Failed\n");
+
+        for(j = 0; j < test.count; ++j)
+        {
+            printf("dst_c[%d] = %x, dst_asm[%d] = %x \n", j, dst_c[j], j, dst_asm[j]);
+        }
+    }
+}
+
+int main()
+{
+    scanline_t32cb16blend_test();
+    return 0;
+}
diff --git a/libpixelflinger/tests/codegen/codegen.cpp b/libpixelflinger/tests/codegen/codegen.cpp
index 3d5a040..e8a4f5e 100644
--- a/libpixelflinger/tests/codegen/codegen.cpp
+++ b/libpixelflinger/tests/codegen/codegen.cpp
@@ -10,8 +10,9 @@
 #include "codeflinger/GGLAssembler.h"
 #include "codeflinger/ARMAssembler.h"
 #include "codeflinger/MIPSAssembler.h"
+#include "codeflinger/Aarch64Assembler.h"
 
-#if defined(__arm__) || defined(__mips__)
+#if defined(__arm__) || defined(__mips__) || defined(__aarch64__)
 #   define ANDROID_ARM_CODEGEN  1
 #else
 #   define ANDROID_ARM_CODEGEN  0
@@ -19,6 +20,8 @@
 
 #if defined (__mips__)
 #define ASSEMBLY_SCRATCH_SIZE   4096
+#elif defined(__aarch64__)
+#define ASSEMBLY_SCRATCH_SIZE   8192
 #else
 #define ASSEMBLY_SCRATCH_SIZE   2048
 #endif
@@ -53,13 +56,17 @@
     GGLAssembler assembler( new ArmToMipsAssembler(a) );
 #endif
 
+#if defined(__aarch64__)
+    GGLAssembler assembler( new ArmToAarch64Assembler(a) );
+#endif
+
     int err = assembler.scanline(needs, (context_t*)c);
     if (err != 0) {
         printf("error %08x (%s)\n", err, strerror(-err));
     }
     gglUninit(c);
 #else
-    printf("This test runs only on ARM or MIPS\n");
+    printf("This test runs only on ARM, Aarch64 or MIPS\n");
 #endif
 }
 
diff --git a/libpixelflinger/tests/gglmul/Android.mk b/libpixelflinger/tests/gglmul/Android.mk
new file mode 100644
index 0000000..64f88b7
--- /dev/null
+++ b/libpixelflinger/tests/gglmul/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	gglmul_test.cpp
+
+LOCAL_SHARED_LIBRARIES :=
+
+LOCAL_C_INCLUDES := \
+	system/core/libpixelflinger
+
+LOCAL_MODULE:= test-pixelflinger-gglmul
+
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_EXECUTABLE)
diff --git a/libpixelflinger/tests/gglmul/gglmul_test.cpp b/libpixelflinger/tests/gglmul/gglmul_test.cpp
new file mode 100644
index 0000000..103e4e9
--- /dev/null
+++ b/libpixelflinger/tests/gglmul/gglmul_test.cpp
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+
+#include "private/pixelflinger/ggl_fixed.h"
+
+// gglClampx() tests
+struct gglClampx_test_t
+{
+    GGLfixed input;
+    GGLfixed output;
+};
+
+gglClampx_test_t gglClampx_tests[] =
+{
+    {FIXED_ONE + 1, FIXED_ONE},
+    {FIXED_ONE, FIXED_ONE},
+    {FIXED_ONE - 1, FIXED_ONE - 1},
+    {1, 1},
+    {0, 0},
+    {FIXED_MIN,0}
+};
+
+void gglClampx_test()
+{
+    uint32_t i;
+
+    printf("Testing gglClampx\n");
+    for(i = 0; i < sizeof(gglClampx_tests)/sizeof(gglClampx_test_t); ++i)
+    {
+        gglClampx_test_t *test = &gglClampx_tests[i];
+        printf("Test input=0x%08x output=0x%08x :",
+                test->input, test->output);
+        if(gglClampx(test->input) == test->output)
+            printf("Passed\n");
+        else
+            printf("Failed\n");
+    }
+}
+
+// gglClz() tests
+struct gglClz_test_t
+{
+    GGLfixed input;
+    GGLfixed output;
+};
+
+gglClz_test_t gglClz_tests[] =
+{
+    {0, 32},
+    {1, 31},
+    {-1,0}
+};
+
+void gglClz_test()
+{
+    uint32_t i;
+
+    printf("Testing gglClz\n");
+    for(i = 0; i < sizeof(gglClz_tests)/sizeof(gglClz_test_t); ++i)
+    {
+        gglClz_test_t *test = &gglClz_tests[i];
+        printf("Test input=0x%08x output=%2d :", test->input, test->output);
+        if(gglClz(test->input) == test->output)
+            printf("Passed\n");
+        else
+            printf("Failed\n");
+    }
+}
+
+// gglMulx() tests
+struct gglMulx_test_t
+{
+    GGLfixed x;
+    GGLfixed y;
+    int      shift;
+};
+
+gglMulx_test_t gglMulx_tests[] =
+{
+    {1,1,1},
+    {0,1,1},
+    {FIXED_ONE,FIXED_ONE,16},
+    {FIXED_MIN,FIXED_MAX,16},
+    {FIXED_MAX,FIXED_MAX,16},
+    {FIXED_MIN,FIXED_MIN,16},
+    {FIXED_HALF,FIXED_ONE,16},
+    {FIXED_MAX,FIXED_MAX,31},
+    {FIXED_ONE,FIXED_MAX,31}
+};
+
+void gglMulx_test()
+{
+    uint32_t i;
+    GGLfixed actual, expected;
+
+    printf("Testing gglMulx\n");
+    for(i = 0; i < sizeof(gglMulx_tests)/sizeof(gglMulx_test_t); ++i)
+    {
+        gglMulx_test_t *test = &gglMulx_tests[i];
+        printf("Test x=0x%08x y=0x%08x shift=%2d :",
+                test->x, test->y, test->shift);
+        actual = gglMulx(test->x, test->y, test->shift);
+        expected =
+          ((int64_t)test->x * test->y + (1 << (test->shift-1))) >> test->shift;
+    if(actual == expected)
+        printf(" Passed\n");
+    else
+        printf(" Failed Actual(0x%08x) Expected(0x%08x)\n",
+               actual, expected);
+    }
+}
+// gglMulAddx() tests
+struct gglMulAddx_test_t
+{
+    GGLfixed x;
+    GGLfixed y;
+    int      shift;
+    GGLfixed a;
+};
+
+gglMulAddx_test_t gglMulAddx_tests[] =
+{
+    {1,2,1,1},
+    {0,1,1,1},
+    {FIXED_ONE,FIXED_ONE,16, 0},
+    {FIXED_MIN,FIXED_MAX,16, FIXED_HALF},
+    {FIXED_MAX,FIXED_MAX,16, FIXED_MIN},
+    {FIXED_MIN,FIXED_MIN,16, FIXED_MAX},
+    {FIXED_HALF,FIXED_ONE,16,FIXED_ONE},
+    {FIXED_MAX,FIXED_MAX,31, FIXED_HALF},
+    {FIXED_ONE,FIXED_MAX,31, FIXED_HALF}
+};
+
+void gglMulAddx_test()
+{
+    uint32_t i;
+    GGLfixed actual, expected;
+
+    printf("Testing gglMulAddx\n");
+    for(i = 0; i < sizeof(gglMulAddx_tests)/sizeof(gglMulAddx_test_t); ++i)
+    {
+        gglMulAddx_test_t *test = &gglMulAddx_tests[i];
+        printf("Test x=0x%08x y=0x%08x shift=%2d a=0x%08x :",
+                test->x, test->y, test->shift, test->a);
+        actual = gglMulAddx(test->x, test->y,test->a, test->shift);
+        expected = (((int64_t)test->x * test->y) >> test->shift) + test->a;
+
+        if(actual == expected)
+            printf(" Passed\n");
+        else
+            printf(" Failed Actual(0x%08x) Expected(0x%08x)\n",
+                    actual, expected);
+    }
+}
+// gglMulSubx() tests
+struct gglMulSubx_test_t
+{
+    GGLfixed x;
+    GGLfixed y;
+    int      shift;
+    GGLfixed a;
+};
+
+gglMulSubx_test_t gglMulSubx_tests[] =
+{
+    {1,2,1,1},
+    {0,1,1,1},
+    {FIXED_ONE,FIXED_ONE,16, 0},
+    {FIXED_MIN,FIXED_MAX,16, FIXED_HALF},
+    {FIXED_MAX,FIXED_MAX,16, FIXED_MIN},
+    {FIXED_MIN,FIXED_MIN,16, FIXED_MAX},
+    {FIXED_HALF,FIXED_ONE,16,FIXED_ONE},
+    {FIXED_MAX,FIXED_MAX,31, FIXED_HALF},
+    {FIXED_ONE,FIXED_MAX,31, FIXED_HALF}
+};
+
+void gglMulSubx_test()
+{
+    uint32_t i;
+    GGLfixed actual, expected;
+
+    printf("Testing gglMulSubx\n");
+    for(i = 0; i < sizeof(gglMulSubx_tests)/sizeof(gglMulSubx_test_t); ++i)
+    {
+        gglMulSubx_test_t *test = &gglMulSubx_tests[i];
+        printf("Test x=0x%08x y=0x%08x shift=%2d a=0x%08x :",
+                test->x, test->y, test->shift, test->a);
+        actual = gglMulSubx(test->x, test->y, test->a, test->shift);
+        expected = (((int64_t)test->x * test->y) >> test->shift) - test->a;
+
+        if(actual == expected)
+            printf(" Passed\n");
+        else
+            printf(" Failed Actual(0x%08x) Expected(0x%08x)\n",
+                actual, expected);
+    }
+}
+
+// gglMulii() tests
+const int32_t INT32_MAX = 0x7FFFFFFF;
+const int32_t INT32_MIN = 0x80000000;
+
+struct gglMulii_test_t
+{
+    int32_t x;
+    int32_t y;
+};
+
+gglMulii_test_t gglMulii_tests[] =
+{
+    {1,INT32_MIN},
+    {1,INT32_MAX},
+    {0,INT32_MIN},
+    {0,INT32_MAX},
+    {INT32_MIN, INT32_MAX},
+    {INT32_MAX, INT32_MIN},
+    {INT32_MIN, INT32_MIN},
+    {INT32_MAX, INT32_MAX}
+};
+
+void gglMulii_test()
+{
+    uint32_t i;
+    int64_t actual, expected;
+
+    printf("Testing gglMulii\n");
+    for(i = 0; i < sizeof(gglMulii_tests)/sizeof(gglMulii_test_t); ++i)
+    {
+        gglMulii_test_t *test = &gglMulii_tests[i];
+        printf("Test x=0x%08x y=0x%08x :", test->x, test->y);
+        actual = gglMulii(test->x, test->y);
+        expected = ((int64_t)test->x * test->y);
+
+        if(actual == expected)
+            printf(" Passed\n");
+        else
+            printf(" Failed Actual(%ld) Expected(%ld)\n",
+                    actual, expected);
+    }
+}
+
+int main(int argc, char** argv)
+{
+    gglClampx_test();
+    gglClz_test();
+    gglMulx_test();
+    gglMulAddx_test();
+    gglMulSubx_test();
+    gglMulii_test();
+    return 0;
+}
diff --git a/libsync/Android.mk b/libsync/Android.mk
index 73de069..626b762 100644
--- a/libsync/Android.mk
+++ b/libsync/Android.mk
@@ -5,6 +5,8 @@
 LOCAL_MODULE := libsync
 LOCAL_MODULE_TAGS := optional
 LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
 include $(BUILD_SHARED_LIBRARY)
 
 include $(CLEAR_VARS)
@@ -12,4 +14,5 @@
 LOCAL_MODULE := sync_test
 LOCAL_MODULE_TAGS := optional tests
 LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
 include $(BUILD_EXECUTABLE)
diff --git a/include/sync/sync.h b/libsync/include/sync/sync.h
similarity index 100%
rename from include/sync/sync.h
rename to libsync/include/sync/sync.h
diff --git a/include/sync/sw_sync.h b/libsync/sw_sync.h
similarity index 97%
rename from include/sync/sw_sync.h
rename to libsync/sw_sync.h
index 3bf4110..fda1c4c 100644
--- a/include/sync/sw_sync.h
+++ b/libsync/sw_sync.h
@@ -19,8 +19,6 @@
 #ifndef __SYS_CORE_SW_SYNC_H
 #define __SYS_CORE_SW_SYNC_H
 
-#include "sync.h"
-
 __BEGIN_DECLS
 
 /*
diff --git a/libsync/sync_test.c b/libsync/sync_test.c
index 386747a..ee9ea3c 100644
--- a/libsync/sync_test.c
+++ b/libsync/sync_test.c
@@ -23,6 +23,7 @@
 #include <unistd.h>
 
 #include <sync/sync.h>
+#include "sw_sync.h"
 
 pthread_mutex_t printf_mutex = PTHREAD_MUTEX_INITIALIZER;
 
diff --git a/libutils/Android.mk b/libutils/Android.mk
index 7e6b1be..720443e 100644
--- a/libutils/Android.mk
+++ b/libutils/Android.mk
@@ -26,6 +26,8 @@
 	LinearAllocator.cpp \
 	LinearTransform.cpp \
 	Log.cpp \
+	Printer.cpp \
+	ProcessCallStack.cpp \
 	PropertyMap.cpp \
 	RefBase.cpp \
 	SharedBuffer.cpp \
diff --git a/libutils/CallStack.cpp b/libutils/CallStack.cpp
index e60f5d8..4ceaa7c 100644
--- a/libutils/CallStack.cpp
+++ b/libutils/CallStack.cpp
@@ -16,14 +16,12 @@
 
 #define LOG_TAG "CallStack"
 
-#include <string.h>
-
-#include <utils/Log.h>
-#include <utils/Errors.h>
 #include <utils/CallStack.h>
+#include <utils/Printer.h>
+#include <utils/Errors.h>
+#include <utils/Log.h>
 #include <corkscrew/backtrace.h>
 
-/*****************************************************************************/
 namespace android {
 
 CallStack::CallStack() :
@@ -31,8 +29,8 @@
 }
 
 CallStack::CallStack(const char* logtag, int32_t ignoreDepth, int32_t maxDepth) {
-    this->update(ignoreDepth+1, maxDepth);
-    this->dump(logtag);
+    this->update(ignoreDepth+1, maxDepth, CURRENT_THREAD);
+    this->log(logtag);
 }
 
 CallStack::CallStack(const CallStack& rhs) :
@@ -93,31 +91,44 @@
     mCount = 0;
 }
 
-void CallStack::update(int32_t ignoreDepth, int32_t maxDepth) {
+void CallStack::update(int32_t ignoreDepth, int32_t maxDepth, pid_t tid) {
     if (maxDepth > MAX_DEPTH) {
         maxDepth = MAX_DEPTH;
     }
-    ssize_t count = unwind_backtrace(mStack, ignoreDepth + 1, maxDepth);
+    ssize_t count;
+
+    if (tid >= 0) {
+        count = unwind_backtrace_thread(tid, mStack, ignoreDepth + 1, maxDepth);
+    } else if (tid == CURRENT_THREAD) {
+        count = unwind_backtrace(mStack, ignoreDepth + 1, maxDepth);
+    } else {
+        ALOGE("%s: Invalid tid specified (%d)", __FUNCTION__, tid);
+        count = 0;
+    }
+
     mCount = count > 0 ? count : 0;
 }
 
-void CallStack::dump(const char* logtag, const char* prefix) const {
-    backtrace_symbol_t symbols[mCount];
+void CallStack::log(const char* logtag, android_LogPriority priority, const char* prefix) const {
+    LogPrinter printer(logtag, priority, prefix, /*ignoreBlankLines*/false);
+    print(printer);
+}
 
-    get_backtrace_symbols(mStack, mCount, symbols);
-    for (size_t i = 0; i < mCount; i++) {
-        char line[MAX_BACKTRACE_LINE_LENGTH];
-        format_backtrace_line(i, &mStack[i], &symbols[i],
-                line, MAX_BACKTRACE_LINE_LENGTH);
-        ALOG(LOG_DEBUG, logtag, "%s%s",
-                prefix ? prefix : "",
-                line);
-    }
-    free_backtrace_symbols(symbols, mCount);
+void CallStack::dump(int fd, int indent, const char* prefix) const {
+    FdPrinter printer(fd, indent, prefix);
+    print(printer);
 }
 
 String8 CallStack::toString(const char* prefix) const {
     String8 str;
+
+    String8Printer printer(&str, prefix);
+    print(printer);
+
+    return str;
+}
+
+void CallStack::print(Printer& printer) const {
     backtrace_symbol_t symbols[mCount];
 
     get_backtrace_symbols(mStack, mCount, symbols);
@@ -125,14 +136,9 @@
         char line[MAX_BACKTRACE_LINE_LENGTH];
         format_backtrace_line(i, &mStack[i], &symbols[i],
                 line, MAX_BACKTRACE_LINE_LENGTH);
-        if (prefix) {
-            str.append(prefix);
-        }
-        str.append(line);
-        str.append("\n");
+        printer.printLine(line);
     }
     free_backtrace_symbols(symbols, mCount);
-    return str;
 }
 
 }; // namespace android
diff --git a/libutils/Looper.cpp b/libutils/Looper.cpp
index c51df2d..9a2dd6c 100644
--- a/libutils/Looper.cpp
+++ b/libutils/Looper.cpp
@@ -43,7 +43,7 @@
 
 // --- SimpleLooperCallback ---
 
-SimpleLooperCallback::SimpleLooperCallback(ALooper_callbackFunc callback) :
+SimpleLooperCallback::SimpleLooperCallback(Looper_callbackFunc callback) :
         mCallback(callback) {
 }
 
@@ -139,7 +139,7 @@
 }
 
 sp<Looper> Looper::prepare(int opts) {
-    bool allowNonCallbacks = opts & ALOOPER_PREPARE_ALLOW_NON_CALLBACKS;
+    bool allowNonCallbacks = opts & PREPARE_ALLOW_NON_CALLBACKS;
     sp<Looper> looper = Looper::getForThread();
     if (looper == NULL) {
         looper = new Looper(allowNonCallbacks);
@@ -147,7 +147,7 @@
     }
     if (looper->getAllowNonCallbacks() != allowNonCallbacks) {
         ALOGW("Looper already prepared for this thread with a different value for the "
-                "ALOOPER_PREPARE_ALLOW_NON_CALLBACKS option.");
+                "LOOPER_PREPARE_ALLOW_NON_CALLBACKS option.");
     }
     return looper;
 }
@@ -212,7 +212,7 @@
     }
 
     // Poll.
-    int result = ALOOPER_POLL_WAKE;
+    int result = POLL_WAKE;
     mResponses.clear();
     mResponseIndex = 0;
 
@@ -234,7 +234,7 @@
             goto Done;
         }
         ALOGW("Poll failed with an unexpected error, errno=%d", errno);
-        result = ALOOPER_POLL_ERROR;
+        result = POLL_ERROR;
         goto Done;
     }
 
@@ -243,7 +243,7 @@
 #if DEBUG_POLL_AND_WAKE
         ALOGD("%p ~ pollOnce - timeout", this);
 #endif
-        result = ALOOPER_POLL_TIMEOUT;
+        result = POLL_TIMEOUT;
         goto Done;
     }
 
@@ -265,10 +265,10 @@
             ssize_t requestIndex = mRequests.indexOfKey(fd);
             if (requestIndex >= 0) {
                 int events = 0;
-                if (epollEvents & EPOLLIN) events |= ALOOPER_EVENT_INPUT;
-                if (epollEvents & EPOLLOUT) events |= ALOOPER_EVENT_OUTPUT;
-                if (epollEvents & EPOLLERR) events |= ALOOPER_EVENT_ERROR;
-                if (epollEvents & EPOLLHUP) events |= ALOOPER_EVENT_HANGUP;
+                if (epollEvents & EPOLLIN) events |= EVENT_INPUT;
+                if (epollEvents & EPOLLOUT) events |= EVENT_OUTPUT;
+                if (epollEvents & EPOLLERR) events |= EVENT_ERROR;
+                if (epollEvents & EPOLLHUP) events |= EVENT_HANGUP;
                 pushResponse(events, mRequests.valueAt(requestIndex));
             } else {
                 ALOGW("Ignoring unexpected epoll events 0x%x on fd %d that is "
@@ -304,7 +304,7 @@
 
             mLock.lock();
             mSendingMessage = false;
-            result = ALOOPER_POLL_CALLBACK;
+            result = POLL_CALLBACK;
         } else {
             // The last message left at the head of the queue determines the next wakeup time.
             mNextMessageUptime = messageEnvelope.uptime;
@@ -318,7 +318,7 @@
     // Invoke all response callbacks.
     for (size_t i = 0; i < mResponses.size(); i++) {
         Response& response = mResponses.editItemAt(i);
-        if (response.request.ident == ALOOPER_POLL_CALLBACK) {
+        if (response.request.ident == POLL_CALLBACK) {
             int fd = response.request.fd;
             int events = response.events;
             void* data = response.request.data;
@@ -333,7 +333,7 @@
             // Clear the callback reference in the response structure promptly because we
             // will not clear the response vector itself until the next poll.
             response.request.callback.clear();
-            result = ALOOPER_POLL_CALLBACK;
+            result = POLL_CALLBACK;
         }
     }
     return result;
@@ -344,7 +344,7 @@
         int result;
         do {
             result = pollOnce(timeoutMillis, outFd, outEvents, outData);
-        } while (result == ALOOPER_POLL_CALLBACK);
+        } while (result == POLL_CALLBACK);
         return result;
     } else {
         nsecs_t endTime = systemTime(SYSTEM_TIME_MONOTONIC)
@@ -352,14 +352,14 @@
 
         for (;;) {
             int result = pollOnce(timeoutMillis, outFd, outEvents, outData);
-            if (result != ALOOPER_POLL_CALLBACK) {
+            if (result != POLL_CALLBACK) {
                 return result;
             }
 
             nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
             timeoutMillis = toMillisecondTimeoutDelay(now, endTime);
             if (timeoutMillis == 0) {
-                return ALOOPER_POLL_TIMEOUT;
+                return POLL_TIMEOUT;
             }
         }
     }
@@ -401,7 +401,7 @@
     mResponses.push(response);
 }
 
-int Looper::addFd(int fd, int ident, int events, ALooper_callbackFunc callback, void* data) {
+int Looper::addFd(int fd, int ident, int events, Looper_callbackFunc callback, void* data) {
     return addFd(fd, ident, events, callback ? new SimpleLooperCallback(callback) : NULL, data);
 }
 
@@ -422,12 +422,12 @@
             return -1;
         }
     } else {
-        ident = ALOOPER_POLL_CALLBACK;
+        ident = POLL_CALLBACK;
     }
 
     int epollEvents = 0;
-    if (events & ALOOPER_EVENT_INPUT) epollEvents |= EPOLLIN;
-    if (events & ALOOPER_EVENT_OUTPUT) epollEvents |= EPOLLOUT;
+    if (events & EVENT_INPUT) epollEvents |= EPOLLIN;
+    if (events & EVENT_OUTPUT) epollEvents |= EPOLLOUT;
 
     { // acquire lock
         AutoMutex _l(mLock);
diff --git a/libutils/Printer.cpp b/libutils/Printer.cpp
new file mode 100644
index 0000000..ac729e0
--- /dev/null
+++ b/libutils/Printer.cpp
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ */
+
+#define LOG_TAG "Printer"
+// #define LOG_NDEBUG 0
+
+#include <utils/Printer.h>
+#include <utils/String8.h>
+#include <utils/Log.h>
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifndef __BIONIC__
+#define fdprintf dprintf
+#endif
+
+namespace android {
+
+/*
+ * Implementation of Printer
+ */
+Printer::Printer() {
+    // Intentionally left empty
+}
+
+Printer::~Printer() {
+    // Intentionally left empty
+}
+
+void Printer::printFormatLine(const char* format, ...) {
+    va_list arglist;
+    va_start(arglist, format);
+
+    char* formattedString;
+
+#ifndef USE_MINGW
+    if (vasprintf(&formattedString, format, arglist) < 0) { // returns -1 on error
+        ALOGE("%s: Failed to format string", __FUNCTION__);
+        return;
+    }
+#else
+    return;
+#endif
+
+    va_end(arglist);
+
+    printLine(formattedString);
+    free(formattedString);
+}
+
+/*
+ * Implementation of LogPrinter
+ */
+LogPrinter::LogPrinter(const char* logtag,
+                       android_LogPriority priority,
+                       const char* prefix,
+                       bool ignoreBlankLines) :
+        mLogTag(logtag),
+        mPriority(priority),
+        mPrefix(prefix ?: ""),
+        mIgnoreBlankLines(ignoreBlankLines) {
+}
+
+void LogPrinter::printLine(const char* string) {
+    if (string == NULL) {
+        ALOGW("%s: NULL string passed in", __FUNCTION__);
+        return;
+    }
+
+    if (mIgnoreBlankLines || (*string)) {
+        // Simple case: Line is not blank, or we don't care about printing blank lines
+        printRaw(string);
+    } else {
+        // Force logcat to print empty lines by adding prefixing with a space
+        printRaw(" ");
+    }
+}
+
+void LogPrinter::printRaw(const char* string) {
+    __android_log_print(mPriority, mLogTag, "%s%s", mPrefix, string);
+}
+
+
+/*
+ * Implementation of FdPrinter
+ */
+FdPrinter::FdPrinter(int fd, unsigned int indent, const char* prefix) :
+        mFd(fd), mIndent(indent), mPrefix(prefix ?: "") {
+
+    if (fd < 0) {
+        ALOGW("%s: File descriptor out of range (%d)", __FUNCTION__, fd);
+    }
+
+    // <indent><prefix><line> -- e.g. '%-4s%s\n' for indent=4
+    snprintf(mFormatString, sizeof(mFormatString), "%%-%us%%s\n", mIndent);
+}
+
+void FdPrinter::printLine(const char* string) {
+    if (string == NULL) {
+        ALOGW("%s: NULL string passed in", __FUNCTION__);
+        return;
+    } else if (mFd < 0) {
+        ALOGW("%s: File descriptor out of range (%d)", __FUNCTION__, mFd);
+        return;
+    }
+
+#ifndef USE_MINGW
+    fdprintf(mFd, mFormatString, mPrefix, string);
+#endif
+}
+
+/*
+ * Implementation of String8Printer
+ */
+String8Printer::String8Printer(String8* target, const char* prefix) :
+        mTarget(target),
+        mPrefix(prefix ?: "") {
+
+    if (target == NULL) {
+        ALOGW("%s: Target string was NULL", __FUNCTION__);
+    }
+}
+
+void String8Printer::printLine(const char* string) {
+    if (string == NULL) {
+        ALOGW("%s: NULL string passed in", __FUNCTION__);
+        return;
+    } else if (mTarget == NULL) {
+        ALOGW("%s: Target string was NULL", __FUNCTION__);
+        return;
+    }
+
+    mTarget->append(string);
+    mTarget->append("\n");
+}
+
+/*
+ * Implementation of PrefixPrinter
+ */
+PrefixPrinter::PrefixPrinter(Printer& printer, const char* prefix) :
+        mPrinter(printer), mPrefix(prefix ?: "") {
+}
+
+void PrefixPrinter::printLine(const char* string) {
+    mPrinter.printFormatLine("%s%s", mPrefix, string);
+}
+
+}; //namespace android
diff --git a/libutils/ProcessCallStack.cpp b/libutils/ProcessCallStack.cpp
new file mode 100644
index 0000000..f9340c5
--- /dev/null
+++ b/libutils/ProcessCallStack.cpp
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ */
+
+#define LOG_TAG "ProcessCallStack"
+// #define LOG_NDEBUG 0
+
+#include <string.h>
+#include <stdio.h>
+#include <dirent.h>
+
+#include <utils/Log.h>
+#include <utils/Errors.h>
+#include <utils/ProcessCallStack.h>
+#include <utils/Printer.h>
+
+#include <limits.h>
+
+namespace android {
+
+enum {
+    // Max sizes for various dynamically generated strings
+    MAX_TIME_STRING = 64,
+    MAX_PROC_PATH = 1024,
+
+    // Dump related prettiness constants
+    IGNORE_DEPTH_CURRENT_THREAD = 2,
+};
+
+static const char* CALL_STACK_PREFIX = "  ";
+static const char* PATH_THREAD_NAME = "/proc/self/task/%d/comm";
+static const char* PATH_SELF_TASK = "/proc/self/task";
+
+static void dumpProcessHeader(Printer& printer, pid_t pid, const char* timeStr) {
+    if (timeStr == NULL) {
+        ALOGW("%s: timeStr was NULL", __FUNCTION__);
+        return;
+    }
+
+    char path[PATH_MAX];
+    char procNameBuf[MAX_PROC_PATH];
+    char* procName = NULL;
+    FILE* fp;
+
+    snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
+    if ((fp = fopen(path, "r"))) {
+        procName = fgets(procNameBuf, sizeof(procNameBuf), fp);
+        fclose(fp);
+    }
+
+    if (!procName) {
+        procName = const_cast<char*>("<unknown>");
+    }
+
+    printer.printLine();
+    printer.printLine();
+    printer.printFormatLine("----- pid %d at %s -----", pid, timeStr);
+    printer.printFormatLine("Cmd line: %s", procName);
+}
+
+static void dumpProcessFooter(Printer& printer, pid_t pid) {
+    printer.printLine();
+    printer.printFormatLine("----- end %d -----", pid);
+    printer.printLine();
+}
+
+static String8 getThreadName(pid_t tid) {
+    char path[PATH_MAX];
+    char* procName = NULL;
+    char procNameBuf[MAX_PROC_PATH];
+    FILE* fp;
+
+    snprintf(path, sizeof(path), PATH_THREAD_NAME, tid);
+    if ((fp = fopen(path, "r"))) {
+        procName = fgets(procNameBuf, sizeof(procNameBuf), fp);
+        fclose(fp);
+    } else {
+        ALOGE("%s: Failed to open %s", __FUNCTION__, path);
+    }
+
+    // Strip ending newline
+    strtok(procName, "\n");
+
+    return String8(procName);
+}
+
+static String8 getTimeString(struct tm tm) {
+    char timestr[MAX_TIME_STRING];
+    // i.e. '2013-10-22 14:42:05'
+    strftime(timestr, sizeof(timestr), "%F %T", &tm);
+
+    return String8(timestr);
+}
+
+/*
+ * Implementation of ProcessCallStack
+ */
+ProcessCallStack::ProcessCallStack() {
+}
+
+ProcessCallStack::ProcessCallStack(const ProcessCallStack& rhs) :
+        mThreadMap(rhs.mThreadMap),
+        mTimeUpdated(rhs.mTimeUpdated) {
+}
+
+ProcessCallStack::~ProcessCallStack() {
+}
+
+void ProcessCallStack::clear() {
+    mThreadMap.clear();
+    mTimeUpdated = tm();
+}
+
+void ProcessCallStack::update(int32_t maxDepth) {
+    DIR *dp;
+    struct dirent *ep;
+    struct dirent entry;
+
+    dp = opendir(PATH_SELF_TASK);
+    if (dp == NULL) {
+        ALOGE("%s: Failed to update the process's call stacks (errno = %d, '%s')",
+              __FUNCTION__, errno, strerror(errno));
+        return;
+    }
+
+    pid_t selfPid = getpid();
+
+    clear();
+
+    // Get current time.
+#ifndef USE_MINGW
+    {
+        time_t t = time(NULL);
+        struct tm tm;
+        localtime_r(&t, &tm);
+
+        mTimeUpdated = tm;
+    }
+
+    /*
+     * Each tid is a directory inside of /proc/self/task
+     * - Read every file in directory => get every tid
+     */
+    int code;
+    while ((code = readdir_r(dp, &entry, &ep)) == 0 && ep != NULL) {
+        pid_t tid = -1;
+        sscanf(ep->d_name, "%d", &tid);
+
+        if (tid < 0) {
+            // Ignore '.' and '..'
+            ALOGV("%s: Failed to read tid from %s/%s",
+                  __FUNCTION__, PATH_SELF_TASK, ep->d_name);
+            continue;
+        }
+
+        ssize_t idx = mThreadMap.add(tid, ThreadInfo());
+        if (idx < 0) { // returns negative error value on error
+            ALOGE("%s: Failed to add new ThreadInfo (errno = %zd, '%s')",
+                  __FUNCTION__, idx, strerror(-idx));
+            continue;
+        }
+
+        ThreadInfo& threadInfo = mThreadMap.editValueAt(static_cast<size_t>(idx));
+
+        /*
+         * Ignore CallStack::update and ProcessCallStack::update for current thread
+         * - Every other thread doesn't need this since we call update off-thread
+         */
+        int ignoreDepth = (selfPid == tid) ? IGNORE_DEPTH_CURRENT_THREAD : 0;
+
+        // Update thread's call stacks
+        CallStack& cs = threadInfo.callStack;
+        cs.update(ignoreDepth, maxDepth, tid);
+
+        // Read/save thread name
+        threadInfo.threadName = getThreadName(tid);
+
+        ALOGV("%s: Got call stack for tid %d (size %zu)",
+              __FUNCTION__, tid, cs.size());
+    }
+    if (code != 0) { // returns positive error value on error
+        ALOGE("%s: Failed to readdir from %s (errno = %d, '%s')",
+              __FUNCTION__, PATH_SELF_TASK, -code, strerror(code));
+    }
+#endif
+
+    closedir(dp);
+}
+
+void ProcessCallStack::log(const char* logtag, android_LogPriority priority,
+                           const char* prefix) const {
+    LogPrinter printer(logtag, priority, prefix, /*ignoreBlankLines*/false);
+    print(printer);
+}
+
+void ProcessCallStack::print(Printer& printer) const {
+    /*
+     * Print the header/footer with the regular printer.
+     * Print the callstack with an additional two spaces as the prefix for legibility.
+     */
+    PrefixPrinter csPrinter(printer, CALL_STACK_PREFIX);
+    printInternal(printer, csPrinter);
+}
+
+void ProcessCallStack::printInternal(Printer& printer, Printer& csPrinter) const {
+    dumpProcessHeader(printer, getpid(),
+                      getTimeString(mTimeUpdated).string());
+
+    for (size_t i = 0; i < mThreadMap.size(); ++i) {
+        pid_t tid = mThreadMap.keyAt(i);
+        const ThreadInfo& threadInfo = mThreadMap.valueAt(i);
+        const CallStack& cs = threadInfo.callStack;
+        const String8& threadName = threadInfo.threadName;
+
+        printer.printLine("");
+        printer.printFormatLine("\"%s\" sysTid=%d", threadName.string(), tid);
+
+        cs.print(csPrinter);
+    }
+
+    dumpProcessFooter(printer, getpid());
+}
+
+void ProcessCallStack::dump(int fd, int indent, const char* prefix) const {
+
+    if (indent < 0) {
+        ALOGW("%s: Bad indent (%d)", __FUNCTION__, indent);
+        return;
+    }
+
+    FdPrinter printer(fd, static_cast<unsigned int>(indent), prefix);
+    print(printer);
+}
+
+String8 ProcessCallStack::toString(const char* prefix) const {
+
+    String8 dest;
+    String8Printer printer(&dest, prefix);
+    print(printer);
+
+    return dest;
+}
+
+size_t ProcessCallStack::size() const {
+    return mThreadMap.size();
+}
+
+}; //namespace android
diff --git a/libutils/SystemClock.cpp b/libutils/SystemClock.cpp
index ac8da88..413250f 100644
--- a/libutils/SystemClock.cpp
+++ b/libutils/SystemClock.cpp
@@ -119,22 +119,6 @@
     static volatile int prevMethod;
 #endif
 
-#if 0
-    /*
-     * b/7100774
-     * clock_gettime appears to have clock skews and can sometimes return
-     * backwards values. Disable its use until we find out what's wrong.
-     */
-    result = clock_gettime(CLOCK_BOOTTIME, &ts);
-    if (result == 0) {
-        timestamp = seconds_to_nanoseconds(ts.tv_sec) + ts.tv_nsec;
-        checkTimeStamps(timestamp, &prevTimestamp, &prevMethod,
-                        METHOD_CLOCK_GETTIME);
-        return timestamp;
-    }
-#endif
-
-    // CLOCK_BOOTTIME doesn't exist, fallback to /dev/alarm
     static int s_fd = -1;
 
     if (s_fd == -1) {
@@ -153,6 +137,15 @@
         return timestamp;
     }
 
+    // /dev/alarm doesn't exist, fallback to CLOCK_BOOTTIME
+    result = clock_gettime(CLOCK_BOOTTIME, &ts);
+    if (result == 0) {
+        timestamp = seconds_to_nanoseconds(ts.tv_sec) + ts.tv_nsec;
+        checkTimeStamps(timestamp, &prevTimestamp, &prevMethod,
+                        METHOD_CLOCK_GETTIME);
+        return timestamp;
+    }
+
     // XXX: there was an error, probably because the driver didn't
     // exist ... this should return
     // a real error, like an exception!
diff --git a/libutils/VectorImpl.cpp b/libutils/VectorImpl.cpp
index 5a79647..30ca663 100644
--- a/libutils/VectorImpl.cpp
+++ b/libutils/VectorImpl.cpp
@@ -384,7 +384,11 @@
         {
             const SharedBuffer* cur_sb = SharedBuffer::bufferFromData(mStorage);
             SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize);
-            mStorage = sb->data();
+            if (sb) {
+                mStorage = sb->data();
+            } else {
+                return NULL;
+            }
         } else {
             SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize);
             if (sb) {
@@ -399,6 +403,8 @@
                 }
                 release_storage();
                 mStorage = const_cast<void*>(array);
+            } else {
+                return NULL;
             }
         }
     } else {
@@ -436,7 +442,11 @@
         {
             const SharedBuffer* cur_sb = SharedBuffer::bufferFromData(mStorage);
             SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize);
-            mStorage = sb->data();
+            if (sb) {
+                mStorage = sb->data();
+            } else {
+                return;
+            }
         } else {
             SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize);
             if (sb) {
@@ -451,6 +461,8 @@
                 }
                 release_storage();
                 mStorage = const_cast<void*>(array);
+            } else{
+                return;
             }
         }
     } else {
diff --git a/libutils/tests/Looper_test.cpp b/libutils/tests/Looper_test.cpp
index 8bf2ba2..00077e6 100644
--- a/libutils/tests/Looper_test.cpp
+++ b/libutils/tests/Looper_test.cpp
@@ -119,8 +119,8 @@
 
     EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
             << "elapsed time should approx. equal timeout";
-    EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result)
-            << "pollOnce result should be ALOOPER_POLL_TIMEOUT";
+    EXPECT_EQ(Looper::POLL_TIMEOUT, result)
+            << "pollOnce result should be LOOPER_POLL_TIMEOUT";
 }
 
 TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndAwokenBeforeWaiting_ImmediatelyReturns) {
@@ -132,8 +132,8 @@
 
     EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
             << "elapsed time should approx. zero because wake() was called before waiting";
-    EXPECT_EQ(ALOOPER_POLL_WAKE, result)
-            << "pollOnce result should be ALOOPER_POLL_CALLBACK because loop was awoken";
+    EXPECT_EQ(Looper::POLL_WAKE, result)
+            << "pollOnce result should be Looper::POLL_CALLBACK because loop was awoken";
 }
 
 TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndAwokenWhileWaiting_PromptlyReturns) {
@@ -146,8 +146,8 @@
 
     EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
             << "elapsed time should approx. equal wake delay";
-    EXPECT_EQ(ALOOPER_POLL_WAKE, result)
-            << "pollOnce result should be ALOOPER_POLL_CALLBACK because loop was awoken";
+    EXPECT_EQ(Looper::POLL_WAKE, result)
+            << "pollOnce result should be Looper::POLL_CALLBACK because loop was awoken";
 }
 
 TEST_F(LooperTest, PollOnce_WhenZeroTimeoutAndNoRegisteredFDs_ImmediatelyReturns) {
@@ -157,15 +157,15 @@
 
     EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
             << "elapsed time should be approx. zero";
-    EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result)
-            << "pollOnce result should be ALOOPER_POLL_TIMEOUT";
+    EXPECT_EQ(Looper::POLL_TIMEOUT, result)
+            << "pollOnce result should be Looper::POLL_TIMEOUT";
 }
 
 TEST_F(LooperTest, PollOnce_WhenZeroTimeoutAndNoSignalledFDs_ImmediatelyReturns) {
     Pipe pipe;
     StubCallbackHandler handler(true);
 
-    handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT);
+    handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);
 
     StopWatch stopWatch("pollOnce");
     int result = mLooper->pollOnce(0);
@@ -173,8 +173,8 @@
 
     EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
             << "elapsed time should be approx. zero";
-    EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result)
-            << "pollOnce result should be ALOOPER_POLL_TIMEOUT";
+    EXPECT_EQ(Looper::POLL_TIMEOUT, result)
+            << "pollOnce result should be Looper::POLL_TIMEOUT";
     EXPECT_EQ(0, handler.callbackCount)
             << "callback should not have been invoked because FD was not signalled";
 }
@@ -184,7 +184,7 @@
     StubCallbackHandler handler(true);
 
     ASSERT_EQ(OK, pipe.writeSignal());
-    handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT);
+    handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);
 
     StopWatch stopWatch("pollOnce");
     int result = mLooper->pollOnce(0);
@@ -192,21 +192,21 @@
 
     EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
             << "elapsed time should be approx. zero";
-    EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
-            << "pollOnce result should be ALOOPER_POLL_CALLBACK because FD was signalled";
+    EXPECT_EQ(Looper::POLL_CALLBACK, result)
+            << "pollOnce result should be Looper::POLL_CALLBACK because FD was signalled";
     EXPECT_EQ(1, handler.callbackCount)
             << "callback should be invoked exactly once";
     EXPECT_EQ(pipe.receiveFd, handler.fd)
             << "callback should have received pipe fd as parameter";
-    EXPECT_EQ(ALOOPER_EVENT_INPUT, handler.events)
-            << "callback should have received ALOOPER_EVENT_INPUT as events";
+    EXPECT_EQ(Looper::EVENT_INPUT, handler.events)
+            << "callback should have received Looper::EVENT_INPUT as events";
 }
 
 TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndNoSignalledFDs_WaitsForTimeoutAndReturns) {
     Pipe pipe;
     StubCallbackHandler handler(true);
 
-    handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT);
+    handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);
 
     StopWatch stopWatch("pollOnce");
     int result = mLooper->pollOnce(100);
@@ -214,8 +214,8 @@
 
     EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
             << "elapsed time should approx. equal timeout";
-    EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result)
-            << "pollOnce result should be ALOOPER_POLL_TIMEOUT";
+    EXPECT_EQ(Looper::POLL_TIMEOUT, result)
+            << "pollOnce result should be Looper::POLL_TIMEOUT";
     EXPECT_EQ(0, handler.callbackCount)
             << "callback should not have been invoked because FD was not signalled";
 }
@@ -225,7 +225,7 @@
     StubCallbackHandler handler(true);
 
     pipe.writeSignal();
-    handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT);
+    handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);
 
     StopWatch stopWatch("pollOnce");
     int result = mLooper->pollOnce(100);
@@ -235,14 +235,14 @@
             << "signal should actually have been written";
     EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
             << "elapsed time should be approx. zero";
-    EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
-            << "pollOnce result should be ALOOPER_POLL_CALLBACK because FD was signalled";
+    EXPECT_EQ(Looper::POLL_CALLBACK, result)
+            << "pollOnce result should be Looper::POLL_CALLBACK because FD was signalled";
     EXPECT_EQ(1, handler.callbackCount)
             << "callback should be invoked exactly once";
     EXPECT_EQ(pipe.receiveFd, handler.fd)
             << "callback should have received pipe fd as parameter";
-    EXPECT_EQ(ALOOPER_EVENT_INPUT, handler.events)
-            << "callback should have received ALOOPER_EVENT_INPUT as events";
+    EXPECT_EQ(Looper::EVENT_INPUT, handler.events)
+            << "callback should have received Looper::EVENT_INPUT as events";
 }
 
 TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDWhileWaiting_PromptlyInvokesCallbackAndReturns) {
@@ -250,7 +250,7 @@
     StubCallbackHandler handler(true);
     sp<DelayedWriteSignal> delayedWriteSignal = new DelayedWriteSignal(100, & pipe);
 
-    handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT);
+    handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);
     delayedWriteSignal->run();
 
     StopWatch stopWatch("pollOnce");
@@ -261,21 +261,21 @@
             << "signal should actually have been written";
     EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
             << "elapsed time should approx. equal signal delay";
-    EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
-            << "pollOnce result should be ALOOPER_POLL_CALLBACK because FD was signalled";
+    EXPECT_EQ(Looper::POLL_CALLBACK, result)
+            << "pollOnce result should be Looper::POLL_CALLBACK because FD was signalled";
     EXPECT_EQ(1, handler.callbackCount)
             << "callback should be invoked exactly once";
     EXPECT_EQ(pipe.receiveFd, handler.fd)
             << "callback should have received pipe fd as parameter";
-    EXPECT_EQ(ALOOPER_EVENT_INPUT, handler.events)
-            << "callback should have received ALOOPER_EVENT_INPUT as events";
+    EXPECT_EQ(Looper::EVENT_INPUT, handler.events)
+            << "callback should have received Looper::EVENT_INPUT as events";
 }
 
 TEST_F(LooperTest, PollOnce_WhenCallbackAddedThenRemoved_CallbackShouldNotBeInvoked) {
     Pipe pipe;
     StubCallbackHandler handler(true);
 
-    handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT);
+    handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);
     pipe.writeSignal(); // would cause FD to be considered signalled
     mLooper->removeFd(pipe.receiveFd);
 
@@ -287,8 +287,8 @@
             << "signal should actually have been written";
     EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
             << "elapsed time should approx. equal timeout because FD was no longer registered";
-    EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result)
-            << "pollOnce result should be ALOOPER_POLL_TIMEOUT";
+    EXPECT_EQ(Looper::POLL_TIMEOUT, result)
+            << "pollOnce result should be Looper::POLL_TIMEOUT";
     EXPECT_EQ(0, handler.callbackCount)
             << "callback should not be invoked";
 }
@@ -297,7 +297,7 @@
     Pipe pipe;
     StubCallbackHandler handler(false);
 
-    handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT);
+    handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);
 
     // First loop: Callback is registered and FD is signalled.
     pipe.writeSignal();
@@ -310,8 +310,8 @@
             << "signal should actually have been written";
     EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
             << "elapsed time should approx. equal zero because FD was already signalled";
-    EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
-            << "pollOnce result should be ALOOPER_POLL_CALLBACK because FD was signalled";
+    EXPECT_EQ(Looper::POLL_CALLBACK, result)
+            << "pollOnce result should be Looper::POLL_CALLBACK because FD was signalled";
     EXPECT_EQ(1, handler.callbackCount)
             << "callback should be invoked";
 
@@ -326,8 +326,8 @@
             << "signal should actually have been written";
     EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
             << "elapsed time should approx. equal zero because timeout was zero";
-    EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result)
-            << "pollOnce result should be ALOOPER_POLL_TIMEOUT";
+    EXPECT_EQ(Looper::POLL_TIMEOUT, result)
+            << "pollOnce result should be Looper::POLL_TIMEOUT";
     EXPECT_EQ(1, handler.callbackCount)
             << "callback should not be invoked this time";
 }
@@ -339,7 +339,7 @@
     Pipe pipe;
 
     pipe.writeSignal();
-    mLooper->addFd(pipe.receiveFd, expectedIdent, ALOOPER_EVENT_INPUT, NULL, expectedData);
+    mLooper->addFd(pipe.receiveFd, expectedIdent, Looper::EVENT_INPUT, NULL, expectedData);
 
     StopWatch stopWatch("pollOnce");
     int fd;
@@ -356,15 +356,15 @@
             << "pollOnce result should be the ident of the FD that was signalled";
     EXPECT_EQ(pipe.receiveFd, fd)
             << "pollOnce should have returned the received pipe fd";
-    EXPECT_EQ(ALOOPER_EVENT_INPUT, events)
-            << "pollOnce should have returned ALOOPER_EVENT_INPUT as events";
+    EXPECT_EQ(Looper::EVENT_INPUT, events)
+            << "pollOnce should have returned Looper::EVENT_INPUT as events";
     EXPECT_EQ(expectedData, data)
             << "pollOnce should have returned the data";
 }
 
 TEST_F(LooperTest, AddFd_WhenCallbackAdded_ReturnsOne) {
     Pipe pipe;
-    int result = mLooper->addFd(pipe.receiveFd, 0, ALOOPER_EVENT_INPUT, NULL, NULL);
+    int result = mLooper->addFd(pipe.receiveFd, 0, Looper::EVENT_INPUT, NULL, NULL);
 
     EXPECT_EQ(1, result)
             << "addFd should return 1 because FD was added";
@@ -372,7 +372,7 @@
 
 TEST_F(LooperTest, AddFd_WhenIdentIsNegativeAndCallbackIsNull_ReturnsError) {
     Pipe pipe;
-    int result = mLooper->addFd(pipe.receiveFd, -1, ALOOPER_EVENT_INPUT, NULL, NULL);
+    int result = mLooper->addFd(pipe.receiveFd, -1, Looper::EVENT_INPUT, NULL, NULL);
 
     EXPECT_EQ(-1, result)
             << "addFd should return -1 because arguments were invalid";
@@ -397,7 +397,7 @@
 TEST_F(LooperTest, RemoveFd_WhenCallbackAddedThenRemovedTwice_ReturnsOnceFirstTimeAndReturnsZeroSecondTime) {
     Pipe pipe;
     StubCallbackHandler handler(false);
-    handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT);
+    handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);
 
     // First time.
     int result = mLooper->removeFd(pipe.receiveFd);
@@ -417,8 +417,8 @@
     StubCallbackHandler handler1(true);
     StubCallbackHandler handler2(true);
 
-    handler1.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT);
-    handler2.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); // replace it
+    handler1.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);
+    handler2.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT); // replace it
     pipe.writeSignal(); // would cause FD to be considered signalled
 
     StopWatch stopWatch("pollOnce");
@@ -429,8 +429,8 @@
             << "signal should actually have been written";
     EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
             << "elapsed time should approx. zero because FD was already signalled";
-    EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
-            << "pollOnce result should be ALOOPER_POLL_CALLBACK because FD was signalled";
+    EXPECT_EQ(Looper::POLL_CALLBACK, result)
+            << "pollOnce result should be Looper::POLL_CALLBACK because FD was signalled";
     EXPECT_EQ(0, handler1.callbackCount)
             << "original handler callback should not be invoked because it was replaced";
     EXPECT_EQ(1, handler2.callbackCount)
@@ -447,8 +447,8 @@
 
     EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
             << "elapsed time should approx. zero because message was already sent";
-    EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
-            << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent";
+    EXPECT_EQ(Looper::POLL_CALLBACK, result)
+            << "pollOnce result should be Looper::POLL_CALLBACK because message was sent";
     EXPECT_EQ(size_t(1), handler->messages.size())
             << "handled message";
     EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
@@ -469,8 +469,8 @@
 
     EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
             << "elapsed time should approx. zero because message was already sent";
-    EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
-            << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent";
+    EXPECT_EQ(Looper::POLL_CALLBACK, result)
+            << "pollOnce result should be Looper::POLL_CALLBACK because message was sent";
     EXPECT_EQ(size_t(3), handler1->messages.size())
             << "handled message";
     EXPECT_EQ(MSG_TEST1, handler1->messages[0].what)
@@ -495,8 +495,8 @@
 
     EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
             << "first poll should end quickly because next message timeout was computed";
-    EXPECT_EQ(ALOOPER_POLL_WAKE, result)
-            << "pollOnce result should be ALOOPER_POLL_WAKE due to wakeup";
+    EXPECT_EQ(Looper::POLL_WAKE, result)
+            << "pollOnce result should be Looper::POLL_WAKE due to wakeup";
     EXPECT_EQ(size_t(0), handler->messages.size())
             << "no message handled yet";
 
@@ -509,16 +509,16 @@
             << "handled message";
     EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
             << "second poll should end around the time of the delayed message dispatch";
-    EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
-            << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent";
+    EXPECT_EQ(Looper::POLL_CALLBACK, result)
+            << "pollOnce result should be Looper::POLL_CALLBACK because message was sent";
 
     result = mLooper->pollOnce(100);
     elapsedMillis = ns2ms(stopWatch.elapsedTime());
 
     EXPECT_NEAR(100 + 100, elapsedMillis, TIMING_TOLERANCE_MS)
             << "third poll should timeout";
-    EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result)
-            << "pollOnce result should be ALOOPER_POLL_TIMEOUT because there were no messages left";
+    EXPECT_EQ(Looper::POLL_TIMEOUT, result)
+            << "pollOnce result should be Looper::POLL_TIMEOUT because there were no messages left";
 }
 
 TEST_F(LooperTest, SendMessageDelayed_WhenSentToThePast_ShouldInvokeHandlerDuringNextPoll) {
@@ -531,8 +531,8 @@
 
     EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
             << "elapsed time should approx. zero because message was already sent";
-    EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
-            << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent";
+    EXPECT_EQ(Looper::POLL_CALLBACK, result)
+            << "pollOnce result should be Looper::POLL_CALLBACK because message was sent";
     EXPECT_EQ(size_t(1), handler->messages.size())
             << "handled message";
     EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
@@ -549,8 +549,8 @@
 
     EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
             << "elapsed time should approx. zero because message was already sent";
-    EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
-            << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent";
+    EXPECT_EQ(Looper::POLL_CALLBACK, result)
+            << "pollOnce result should be Looper::POLL_CALLBACK because message was sent";
     EXPECT_EQ(size_t(1), handler->messages.size())
             << "handled message";
     EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
@@ -568,8 +568,8 @@
 
     EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
             << "first poll should end quickly because next message timeout was computed";
-    EXPECT_EQ(ALOOPER_POLL_WAKE, result)
-            << "pollOnce result should be ALOOPER_POLL_WAKE due to wakeup";
+    EXPECT_EQ(Looper::POLL_WAKE, result)
+            << "pollOnce result should be Looper::POLL_WAKE due to wakeup";
     EXPECT_EQ(size_t(0), handler->messages.size())
             << "no message handled yet";
 
@@ -582,16 +582,16 @@
             << "handled message";
     EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
             << "second poll should end around the time of the delayed message dispatch";
-    EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
-            << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent";
+    EXPECT_EQ(Looper::POLL_CALLBACK, result)
+            << "pollOnce result should be Looper::POLL_CALLBACK because message was sent";
 
     result = mLooper->pollOnce(100);
     elapsedMillis = ns2ms(stopWatch.elapsedTime());
 
     EXPECT_NEAR(100 + 100, elapsedMillis, TIMING_TOLERANCE_MS)
             << "third poll should timeout";
-    EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result)
-            << "pollOnce result should be ALOOPER_POLL_TIMEOUT because there were no messages left";
+    EXPECT_EQ(Looper::POLL_TIMEOUT, result)
+            << "pollOnce result should be Looper::POLL_TIMEOUT because there were no messages left";
 }
 
 TEST_F(LooperTest, SendMessageAtTime_WhenSentToThePast_ShouldInvokeHandlerDuringNextPoll) {
@@ -605,8 +605,8 @@
 
     EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
             << "elapsed time should approx. zero because message was already sent";
-    EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
-            << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent";
+    EXPECT_EQ(Looper::POLL_CALLBACK, result)
+            << "pollOnce result should be Looper::POLL_CALLBACK because message was sent";
     EXPECT_EQ(size_t(1), handler->messages.size())
             << "handled message";
     EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
@@ -624,8 +624,8 @@
 
     EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
             << "elapsed time should approx. zero because message was already sent";
-    EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
-            << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent";
+    EXPECT_EQ(Looper::POLL_CALLBACK, result)
+            << "pollOnce result should be Looper::POLL_CALLBACK because message was sent";
     EXPECT_EQ(size_t(1), handler->messages.size())
             << "handled message";
     EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
@@ -645,15 +645,15 @@
 
     EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
             << "elapsed time should approx. zero because message was sent so looper was awoken";
-    EXPECT_EQ(ALOOPER_POLL_WAKE, result)
-            << "pollOnce result should be ALOOPER_POLL_WAKE because looper was awoken";
+    EXPECT_EQ(Looper::POLL_WAKE, result)
+            << "pollOnce result should be Looper::POLL_WAKE because looper was awoken";
     EXPECT_EQ(size_t(0), handler->messages.size())
             << "no messages to handle";
 
     result = mLooper->pollOnce(0);
 
-    EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result)
-            << "pollOnce result should be ALOOPER_POLL_TIMEOUT because there was nothing to do";
+    EXPECT_EQ(Looper::POLL_TIMEOUT, result)
+            << "pollOnce result should be Looper::POLL_TIMEOUT because there was nothing to do";
     EXPECT_EQ(size_t(0), handler->messages.size())
             << "no messages to handle";
 }
@@ -673,8 +673,8 @@
 
     EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
             << "elapsed time should approx. zero because message was sent so looper was awoken";
-    EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
-            << "pollOnce result should be ALOOPER_POLL_CALLBACK because two messages were sent";
+    EXPECT_EQ(Looper::POLL_CALLBACK, result)
+            << "pollOnce result should be Looper::POLL_CALLBACK because two messages were sent";
     EXPECT_EQ(size_t(2), handler->messages.size())
             << "no messages to handle";
     EXPECT_EQ(MSG_TEST2, handler->messages[0].what)
@@ -684,8 +684,8 @@
 
     result = mLooper->pollOnce(0);
 
-    EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result)
-            << "pollOnce result should be ALOOPER_POLL_TIMEOUT because there was nothing to do";
+    EXPECT_EQ(Looper::POLL_TIMEOUT, result)
+            << "pollOnce result should be Looper::POLL_TIMEOUT because there was nothing to do";
     EXPECT_EQ(size_t(2), handler->messages.size())
             << "no more messages to handle";
 }
diff --git a/libziparchive/Android.mk b/libziparchive/Android.mk
new file mode 100644
index 0000000..e754c3b
--- /dev/null
+++ b/libziparchive/Android.mk
@@ -0,0 +1,69 @@
+#
+# Copyright (C) 2013 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+source_files := \
+	zip_archive.h \
+	zip_archive.cc
+
+includes := external/zlib
+
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_SRC_FILES := ${source_files}
+
+LOCAL_STATIC_LIBRARIES := libz
+LOCAL_SHARED_LIBRARIES := libutils
+LOCAL_MODULE:= libziparchive
+
+LOCAL_C_INCLUDES += ${includes}
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libziparchive
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_SRC_FILES := ${source_files}
+LOCAL_C_INCLUDES += ${includes}
+
+LOCAL_STATIC_LIBRARIES := libz libutils
+LOCAL_MODULE:= libziparchive-host
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := ziparchive-tests
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_CFLAGS += \
+    -DGTEST_OS_LINUX_ANDROID \
+    -DGTEST_HAS_STD_STRING
+LOCAL_SRC_FILES := zip_archive_test.cc
+LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_STATIC_LIBRARIES := libziparchive libz libgtest libgtest_main libutils
+include $(BUILD_NATIVE_TEST)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := ziparchive-tests-host
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_CFLAGS += \
+    -DGTEST_OS_LINUX \
+    -DGTEST_HAS_STD_STRING
+LOCAL_SRC_FILES := zip_archive_test.cc
+LOCAL_STATIC_LIBRARIES := libziparchive-host \
+	libz \
+	libgtest_host \
+	libgtest_main_host \
+	liblog \
+	libutils
+include $(BUILD_HOST_NATIVE_TEST)
diff --git a/libziparchive/testdata/valid.zip b/libziparchive/testdata/valid.zip
new file mode 100644
index 0000000..9e7cb78
--- /dev/null
+++ b/libziparchive/testdata/valid.zip
Binary files differ
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
new file mode 100644
index 0000000..8436d49
--- /dev/null
+++ b/libziparchive/zip_archive.cc
@@ -0,0 +1,1048 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ */
+
+/*
+ * Read-only access to Zip archives, with minimal heap allocation.
+ */
+#include "ziparchive/zip_archive.h"
+
+#include <zlib.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <limits.h>
+#include <log/log.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <utils/FileMap.h>
+
+#include <JNIHelp.h>  // TEMP_FAILURE_RETRY may or may not be in unistd
+
+// This is for windows. If we don't open a file in binary mode, weirds
+// things will happen.
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+/*
+ * Zip file constants.
+ */
+static const uint32_t kEOCDSignature    = 0x06054b50;
+static const uint32_t kEOCDLen          = 2;
+static const uint32_t kEOCDNumEntries   = 8;              // offset to #of entries in file
+static const uint32_t kEOCDSize         = 12;             // size of the central directory
+static const uint32_t kEOCDFileOffset   = 16;             // offset to central directory
+
+static const uint32_t kMaxCommentLen    = 65535;          // longest possible in ushort
+static const uint32_t kMaxEOCDSearch    = (kMaxCommentLen + kEOCDLen);
+
+static const uint32_t kLFHSignature     = 0x04034b50;
+static const uint32_t kLFHLen           = 30;             // excluding variable-len fields
+static const uint32_t kLFHGPBFlags      = 6;              // general purpose bit flags
+static const uint32_t kLFHCRC           = 14;             // offset to CRC
+static const uint32_t kLFHCompLen       = 18;             // offset to compressed length
+static const uint32_t kLFHUncompLen     = 22;             // offset to uncompressed length
+static const uint32_t kLFHNameLen       = 26;             // offset to filename length
+static const uint32_t kLFHExtraLen      = 28;             // offset to extra length
+
+static const uint32_t kCDESignature     = 0x02014b50;
+static const uint32_t kCDELen           = 46;             // excluding variable-len fields
+static const uint32_t kCDEMethod        = 10;             // offset to compression method
+static const uint32_t kCDEModWhen       = 12;             // offset to modification timestamp
+static const uint32_t kCDECRC           = 16;             // offset to entry CRC
+static const uint32_t kCDECompLen       = 20;             // offset to compressed length
+static const uint32_t kCDEUncompLen     = 24;             // offset to uncompressed length
+static const uint32_t kCDENameLen       = 28;             // offset to filename length
+static const uint32_t kCDEExtraLen      = 30;             // offset to extra length
+static const uint32_t kCDECommentLen    = 32;             // offset to comment length
+static const uint32_t kCDELocalOffset   = 42;             // offset to local hdr
+
+static const uint32_t kDDOptSignature   = 0x08074b50;     // *OPTIONAL* data descriptor signature
+static const uint32_t kDDSignatureLen   = 4;
+static const uint32_t kDDLen            = 12;
+static const uint32_t kDDMaxLen         = 16;             // max of 16 bytes with a signature, 12 bytes without
+static const uint32_t kDDCrc32          = 0;              // offset to crc32
+static const uint32_t kDDCompLen        = 4;              // offset to compressed length
+static const uint32_t kDDUncompLen      = 8;              // offset to uncompressed length
+
+static const uint32_t kGPBDDFlagMask    = 0x0008;         // mask value that signifies that the entry has a DD
+
+static const uint32_t kMaxErrorLen = 1024;
+
+static const char* kErrorMessages[] = {
+  "Unknown return code.",
+  "Iteration ended",
+  "Zlib error",
+  "Invalid file",
+  "Invalid handle",
+  "Duplicate entries in archive",
+  "Empty archive",
+  "Entry not found",
+  "Invalid offset",
+  "Inconsistent information",
+  "Invalid entry name",
+  "I/O Error",
+  "File mapping failed"
+};
+
+static const int32_t kErrorMessageUpperBound = 0;
+
+static const int32_t kIterationEnd = -1;
+
+// We encountered a Zlib error when inflating a stream from this file.
+// Usually indicates file corruption.
+static const int32_t kZlibError = -2;
+
+// The input file cannot be processed as a zip archive. Usually because
+// it's too small, too large or does not have a valid signature.
+static const int32_t kInvalidFile = -3;
+
+// An invalid iteration / ziparchive handle was passed in as an input
+// argument.
+static const int32_t kInvalidHandle = -4;
+
+// The zip archive contained two (or possibly more) entries with the same
+// name.
+static const int32_t kDuplicateEntry = -5;
+
+// The zip archive contains no entries.
+static const int32_t kEmptyArchive = -6;
+
+// The specified entry was not found in the archive.
+static const int32_t kEntryNotFound = -7;
+
+// The zip archive contained an invalid local file header pointer.
+static const int32_t kInvalidOffset = -8;
+
+// The zip archive contained inconsistent entry information. This could
+// be because the central directory & local file header did not agree, or
+// if the actual uncompressed length or crc32 do not match their declared
+// values.
+static const int32_t kInconsistentInformation = -9;
+
+// An invalid entry name was encountered.
+static const int32_t kInvalidEntryName = -10;
+
+// An I/O related system call (read, lseek, ftruncate, map) failed.
+static const int32_t kIoError = -11;
+
+// We were not able to mmap the central directory or entry contents.
+static const int32_t kMmapFailed = -12;
+
+static const int32_t kErrorMessageLowerBound = -13;
+
+static const char kTempMappingFileName[] = "zip: ExtractFileToFile";
+
+/*
+ * A Read-only Zip archive.
+ *
+ * We want "open" and "find entry by name" to be fast operations, and
+ * we want to use as little memory as possible.  We memory-map the zip
+ * central directory, and load a hash table with pointers to the filenames
+ * (which aren't null-terminated).  The other fields are at a fixed offset
+ * from the filename, so we don't need to extract those (but we do need
+ * to byte-read and endian-swap them every time we want them).
+ *
+ * It's possible that somebody has handed us a massive (~1GB) zip archive,
+ * so we can't expect to mmap the entire file.
+ *
+ * To speed comparisons when doing a lookup by name, we could make the mapping
+ * "private" (copy-on-write) and null-terminate the filenames after verifying
+ * the record structure.  However, this requires a private mapping of
+ * every page that the Central Directory touches.  Easier to tuck a copy
+ * of the string length into the hash table entry.
+ */
+struct ZipArchive {
+  /* open Zip archive */
+  int fd;
+
+  /* mapped central directory area */
+  off64_t directory_offset;
+  android::FileMap* directory_map;
+
+  /* number of entries in the Zip archive */
+  uint16_t num_entries;
+
+  /*
+   * We know how many entries are in the Zip archive, so we can have a
+   * fixed-size hash table. We define a load factor of 0.75 and overallocat
+   * so the maximum number entries can never be higher than
+   * ((4 * UINT16_MAX) / 3 + 1) which can safely fit into a uint32_t.
+   */
+  uint32_t hash_table_size;
+  ZipEntryName* hash_table;
+};
+
+// Returns 0 on success and negative values on failure.
+static android::FileMap* MapFileSegment(const int fd, const off64_t start,
+                                        const size_t length, const bool read_only,
+                                        const char* debug_file_name) {
+  android::FileMap* file_map = new android::FileMap;
+  const bool success = file_map->create(debug_file_name, fd, start, length, read_only);
+  if (!success) {
+    file_map->release();
+    return NULL;
+  }
+
+  return file_map;
+}
+
+static int32_t CopyFileToFile(int fd, uint8_t* begin, const uint32_t length, uint64_t *crc_out) {
+  static const uint32_t kBufSize = 32768;
+  uint8_t buf[kBufSize];
+
+  uint32_t count = 0;
+  uint64_t crc = 0;
+  while (count < length) {
+    uint32_t remaining = length - count;
+
+    // Safe conversion because kBufSize is narrow enough for a 32 bit signed
+    // value.
+    ssize_t get_size = (remaining > kBufSize) ? kBufSize : remaining;
+    ssize_t actual = TEMP_FAILURE_RETRY(read(fd, buf, get_size));
+
+    if (actual != get_size) {
+      ALOGW("CopyFileToFile: copy read failed (%d vs %zd)",
+          (int) actual, get_size);
+      return kIoError;
+    }
+
+    memcpy(begin + count, buf, get_size);
+    crc = crc32(crc, buf, get_size);
+    count += get_size;
+  }
+
+  *crc_out = crc;
+
+  return 0;
+}
+
+/*
+ * Round up to the next highest power of 2.
+ *
+ * Found on http://graphics.stanford.edu/~seander/bithacks.html.
+ */
+static uint32_t RoundUpPower2(uint32_t val) {
+  val--;
+  val |= val >> 1;
+  val |= val >> 2;
+  val |= val >> 4;
+  val |= val >> 8;
+  val |= val >> 16;
+  val++;
+
+  return val;
+}
+
+static uint32_t ComputeHash(const char* str, uint16_t len) {
+  uint32_t hash = 0;
+
+  while (len--) {
+    hash = hash * 31 + *str++;
+  }
+
+  return hash;
+}
+
+/*
+ * Convert a ZipEntry to a hash table index, verifying that it's in a
+ * valid range.
+ */
+static int64_t EntryToIndex(const ZipEntryName* hash_table,
+                            const uint32_t hash_table_size,
+                            const char* name, uint16_t length) {
+  const uint32_t hash = ComputeHash(name, length);
+
+  // NOTE: (hash_table_size - 1) is guaranteed to be non-negative.
+  uint32_t ent = hash & (hash_table_size - 1);
+  while (hash_table[ent].name != NULL) {
+    if (hash_table[ent].name_length == length &&
+        memcmp(hash_table[ent].name, name, length) == 0) {
+      return ent;
+    }
+
+    ent = (ent + 1) & (hash_table_size - 1);
+  }
+
+  ALOGV("Zip: Unable to find entry %.*s", name_length, name);
+  return kEntryNotFound;
+}
+
+/*
+ * Add a new entry to the hash table.
+ */
+static int32_t AddToHash(ZipEntryName *hash_table, const uint64_t hash_table_size,
+                         const char* name, uint16_t length) {
+  const uint64_t hash = ComputeHash(name, length);
+  uint32_t ent = hash & (hash_table_size - 1);
+
+  /*
+   * We over-allocated the table, so we're guaranteed to find an empty slot.
+   * Further, we guarantee that the hashtable size is not 0.
+   */
+  while (hash_table[ent].name != NULL) {
+    if (hash_table[ent].name_length == length &&
+        memcmp(hash_table[ent].name, name, length) == 0) {
+      // We've found a duplicate entry. We don't accept it
+      ALOGW("Zip: Found duplicate entry %.*s", length, name);
+      return kDuplicateEntry;
+    }
+    ent = (ent + 1) & (hash_table_size - 1);
+  }
+
+  hash_table[ent].name = name;
+  hash_table[ent].name_length = length;
+  return 0;
+}
+
+/*
+ * Get 2 little-endian bytes.
+ */
+static uint16_t get2LE(const uint8_t* src) {
+  return src[0] | (src[1] << 8);
+}
+
+/*
+ * Get 4 little-endian bytes.
+ */
+static uint32_t get4LE(const uint8_t* src) {
+  uint32_t result;
+
+  result = src[0];
+  result |= src[1] << 8;
+  result |= src[2] << 16;
+  result |= src[3] << 24;
+
+  return result;
+}
+
+static int32_t MapCentralDirectory0(int fd, const char* debug_file_name,
+                                    ZipArchive* archive, off64_t file_length,
+                                    uint32_t read_amount, uint8_t* scan_buffer) {
+  const off64_t search_start = file_length - read_amount;
+
+  if (lseek64(fd, search_start, SEEK_SET) != search_start) {
+    ALOGW("Zip: seek %lld failed: %s", search_start, strerror(errno));
+    return kIoError;
+  }
+  ssize_t actual = TEMP_FAILURE_RETRY(read(fd, scan_buffer, read_amount));
+  if (actual != (ssize_t) read_amount) {
+    ALOGW("Zip: read %zd failed: %s", read_amount, strerror(errno));
+    return kIoError;
+  }
+
+  /*
+   * Scan backward for the EOCD magic.  In an archive without a trailing
+   * comment, we'll find it on the first try.  (We may want to consider
+   * doing an initial minimal read; if we don't find it, retry with a
+   * second read as above.)
+   */
+  int i;
+  for (i = read_amount - kEOCDLen; i >= 0; i--) {
+    if (scan_buffer[i] == 0x50 && get4LE(&scan_buffer[i]) == kEOCDSignature) {
+      ALOGV("+++ Found EOCD at buf+%d", i);
+      break;
+    }
+  }
+  if (i < 0) {
+    ALOGD("Zip: EOCD not found, %s is not zip", debug_file_name);
+    return kInvalidFile;
+  }
+
+  const off64_t eocd_offset = search_start + i;
+  const uint8_t* eocd_ptr = scan_buffer + i;
+
+  assert(eocd_offset < file_length);
+
+  /*
+   * Grab the CD offset and size, and the number of entries in the
+   * archive.  Verify that they look reasonable. Widen dir_size and
+   * dir_offset to the file offset type.
+   */
+  const uint16_t num_entries = get2LE(eocd_ptr + kEOCDNumEntries);
+  const off64_t dir_size = get4LE(eocd_ptr + kEOCDSize);
+  const off64_t dir_offset = get4LE(eocd_ptr + kEOCDFileOffset);
+
+  if (dir_offset + dir_size > eocd_offset) {
+    ALOGW("Zip: bad offsets (dir %lld, size %lld, eocd %lld)",
+        dir_offset, dir_size, eocd_offset);
+    return kInvalidOffset;
+  }
+  if (num_entries == 0) {
+    ALOGW("Zip: empty archive?");
+    return kEmptyArchive;
+  }
+
+  ALOGV("+++ num_entries=%d dir_size=%d dir_offset=%d", num_entries, dir_size,
+      dir_offset);
+
+  /*
+   * It all looks good.  Create a mapping for the CD, and set the fields
+   * in archive.
+   */
+  android::FileMap* map = MapFileSegment(fd, dir_offset, dir_size,
+                                         true /* read only */, debug_file_name);
+  if (map == NULL) {
+    archive->directory_map = NULL;
+    return kMmapFailed;
+  }
+
+  archive->directory_map = map;
+  archive->num_entries = num_entries;
+  archive->directory_offset = dir_offset;
+
+  return 0;
+}
+
+/*
+ * Find the zip Central Directory and memory-map it.
+ *
+ * On success, returns 0 after populating fields from the EOCD area:
+ *   directory_offset
+ *   directory_map
+ *   num_entries
+ */
+static int32_t MapCentralDirectory(int fd, const char* debug_file_name,
+                                   ZipArchive* archive) {
+
+  // Test file length. We use lseek64 to make sure the file
+  // is small enough to be a zip file (Its size must be less than
+  // 0xffffffff bytes).
+  off64_t file_length = lseek64(fd, 0, SEEK_END);
+  if (file_length == -1) {
+    ALOGV("Zip: lseek on fd %d failed", fd);
+    return kInvalidFile;
+  }
+
+  if (file_length > (off64_t) 0xffffffff) {
+    ALOGV("Zip: zip file too long %d", file_length);
+    return kInvalidFile;
+  }
+
+  if (file_length < (int64_t) kEOCDLen) {
+    ALOGV("Zip: length %ld is too small to be zip", file_length);
+    return kInvalidFile;
+  }
+
+  /*
+   * Perform the traditional EOCD snipe hunt.
+   *
+   * We're searching for the End of Central Directory magic number,
+   * which appears at the start of the EOCD block.  It's followed by
+   * 18 bytes of EOCD stuff and up to 64KB of archive comment.  We
+   * need to read the last part of the file into a buffer, dig through
+   * it to find the magic number, parse some values out, and use those
+   * to determine the extent of the CD.
+   *
+   * We start by pulling in the last part of the file.
+   */
+  uint32_t read_amount = kMaxEOCDSearch;
+  if (file_length < (off64_t) read_amount) {
+    read_amount = file_length;
+  }
+
+  uint8_t* scan_buffer = (uint8_t*) malloc(read_amount);
+  int32_t result = MapCentralDirectory0(fd, debug_file_name, archive,
+                                        file_length, read_amount, scan_buffer);
+
+  free(scan_buffer);
+  return result;
+}
+
+/*
+ * Parses the Zip archive's Central Directory.  Allocates and populates the
+ * hash table.
+ *
+ * Returns 0 on success.
+ */
+static int32_t ParseZipArchive(ZipArchive* archive) {
+  int32_t result = -1;
+  const uint8_t* cd_ptr = (const uint8_t*) archive->directory_map->getDataPtr();
+  size_t cd_length = archive->directory_map->getDataLength();
+  uint16_t num_entries = archive->num_entries;
+
+  /*
+   * Create hash table.  We have a minimum 75% load factor, possibly as
+   * low as 50% after we round off to a power of 2.  There must be at
+   * least one unused entry to avoid an infinite loop during creation.
+   */
+  archive->hash_table_size = RoundUpPower2(1 + (num_entries * 4) / 3);
+  archive->hash_table = (ZipEntryName*) calloc(archive->hash_table_size,
+      sizeof(ZipEntryName));
+
+  /*
+   * Walk through the central directory, adding entries to the hash
+   * table and verifying values.
+   */
+  const uint8_t* ptr = cd_ptr;
+  for (uint16_t i = 0; i < num_entries; i++) {
+    if (get4LE(ptr) != kCDESignature) {
+      ALOGW("Zip: missed a central dir sig (at %d)", i);
+      goto bail;
+    }
+
+    if (ptr + kCDELen > cd_ptr + cd_length) {
+      ALOGW("Zip: ran off the end (at %d)", i);
+      goto bail;
+    }
+
+    const off64_t local_header_offset = get4LE(ptr + kCDELocalOffset);
+    if (local_header_offset >= archive->directory_offset) {
+      ALOGW("Zip: bad LFH offset %lld at entry %d", local_header_offset, i);
+      goto bail;
+    }
+
+    const uint16_t file_name_length = get2LE(ptr + kCDENameLen);
+    const uint16_t extra_length = get2LE(ptr + kCDEExtraLen);
+    const uint16_t comment_length = get2LE(ptr + kCDECommentLen);
+
+    /* add the CDE filename to the hash table */
+    const int add_result = AddToHash(archive->hash_table,
+        archive->hash_table_size, (const char*) ptr + kCDELen, file_name_length);
+    if (add_result) {
+      ALOGW("Zip: Error adding entry to hash table %d", add_result);
+      result = add_result;
+      goto bail;
+    }
+
+    ptr += kCDELen + file_name_length + extra_length + comment_length;
+    if ((size_t)(ptr - cd_ptr) > cd_length) {
+      ALOGW("Zip: bad CD advance (%d vs %zd) at entry %d",
+        (int) (ptr - cd_ptr), cd_length, i);
+      goto bail;
+    }
+  }
+  ALOGV("+++ zip good scan %d entries", num_entries);
+
+  result = 0;
+
+bail:
+  return result;
+}
+
+static int32_t OpenArchiveInternal(ZipArchive* archive,
+                                   const char* debug_file_name) {
+  int32_t result = -1;
+  if ((result = MapCentralDirectory(archive->fd, debug_file_name, archive))) {
+    return result;
+  }
+
+  if ((result = ParseZipArchive(archive))) {
+    return result;
+  }
+
+  return 0;
+}
+
+int32_t OpenArchiveFd(int fd, const char* debug_file_name,
+                      ZipArchiveHandle* handle) {
+  ZipArchive* archive = (ZipArchive*) malloc(sizeof(ZipArchive));
+  memset(archive, 0, sizeof(*archive));
+  *handle = archive;
+
+  archive->fd = fd;
+
+  return OpenArchiveInternal(archive, debug_file_name);
+}
+
+int32_t OpenArchive(const char* fileName, ZipArchiveHandle* handle) {
+  ZipArchive* archive = (ZipArchive*) malloc(sizeof(ZipArchive));
+  memset(archive, 0, sizeof(*archive));
+  *handle = archive;
+
+  const int fd = open(fileName, O_RDONLY | O_BINARY, 0);
+  if (fd < 0) {
+    ALOGW("Unable to open '%s': %s", fileName, strerror(errno));
+    return kIoError;
+  } else {
+    archive->fd = fd;
+  }
+
+  return OpenArchiveInternal(archive, fileName);
+}
+
+/*
+ * Close a ZipArchive, closing the file and freeing the contents.
+ */
+void CloseArchive(ZipArchiveHandle handle) {
+  ZipArchive* archive = (ZipArchive*) handle;
+  ALOGV("Closing archive %p", archive);
+
+  if (archive->fd >= 0) {
+    close(archive->fd);
+  }
+
+  if (archive->directory_map != NULL) {
+    archive->directory_map->release();
+  }
+  free(archive->hash_table);
+
+  /* ensure nobody tries to use the ZipArchive after it's closed */
+  archive->directory_offset = -1;
+  archive->fd = -1;
+  archive->num_entries = -1;
+  archive->hash_table_size = -1;
+  archive->hash_table = NULL;
+}
+
+static int32_t UpdateEntryFromDataDescriptor(int fd,
+                                             ZipEntry *entry) {
+  uint8_t ddBuf[kDDMaxLen];
+  ssize_t actual = TEMP_FAILURE_RETRY(read(fd, ddBuf, sizeof(ddBuf)));
+  if (actual != sizeof(ddBuf)) {
+    return kIoError;
+  }
+
+  const uint32_t ddSignature = get4LE(ddBuf);
+  uint16_t ddOffset = 0;
+  if (ddSignature == kDDOptSignature) {
+    ddOffset = 4;
+  }
+
+  entry->crc32 = get4LE(ddBuf + ddOffset + kDDCrc32);
+  entry->compressed_length = get4LE(ddBuf + ddOffset + kDDCompLen);
+  entry->uncompressed_length = get4LE(ddBuf + ddOffset + kDDUncompLen);
+
+  return 0;
+}
+
+// Attempts to read |len| bytes into |buf| at offset |off|.
+//
+// This method uses pread64 on platforms that support it and
+// lseek64 + read on platforms that don't. This implies that
+// callers should not rely on the |fd| offset being incremented
+// as a side effect of this call.
+static inline ssize_t ReadAtOffset(int fd, uint8_t* buf, size_t len,
+                                   off64_t off) {
+#ifdef HAVE_PREAD
+  return TEMP_FAILURE_RETRY(pread64(fd, buf, len, off));
+#else
+  // The only supported platform that doesn't support pread at the moment
+  // is Windows. Only recent versions of windows support unix like forks,
+  // and even there the semantics are quite different.
+  if (lseek64(fd, off, SEEK_SET) != off) {
+    ALOGW("Zip: failed seek to offset %lld", off);
+    return kIoError;
+  }
+
+  return TEMP_FAILURE_RETRY(read(fd, buf, len));
+#endif  // HAVE_PREAD
+}
+
+static int32_t FindEntry(const ZipArchive* archive, const int ent,
+                         ZipEntry* data) {
+  const uint16_t nameLen = archive->hash_table[ent].name_length;
+  const char* name = archive->hash_table[ent].name;
+
+  // Recover the start of the central directory entry from the filename
+  // pointer.  The filename is the first entry past the fixed-size data,
+  // so we can just subtract back from that.
+  const unsigned char* ptr = (const unsigned char*) name;
+  ptr -= kCDELen;
+
+  // This is the base of our mmapped region, we have to sanity check that
+  // the name that's in the hash table is a pointer to a location within
+  // this mapped region.
+  const unsigned char* base_ptr = (const unsigned char*)
+    archive->directory_map->getDataPtr();
+  if (ptr < base_ptr || ptr > base_ptr + archive->directory_map->getDataLength()) {
+    ALOGW("Zip: Invalid entry pointer");
+    return kInvalidOffset;
+  }
+
+  // The offset of the start of the central directory in the zipfile.
+  // We keep this lying around so that we can sanity check all our lengths
+  // and our per-file structures.
+  const off64_t cd_offset = archive->directory_offset;
+
+  // Fill out the compression method, modification time, crc32
+  // and other interesting attributes from the central directory. These
+  // will later be compared against values from the local file header.
+  data->method = get2LE(ptr + kCDEMethod);
+  data->mod_time = get4LE(ptr + kCDEModWhen);
+  data->crc32 = get4LE(ptr + kCDECRC);
+  data->compressed_length = get4LE(ptr + kCDECompLen);
+  data->uncompressed_length = get4LE(ptr + kCDEUncompLen);
+
+  // Figure out the local header offset from the central directory. The
+  // actual file data will begin after the local header and the name /
+  // extra comments.
+  const off64_t local_header_offset = get4LE(ptr + kCDELocalOffset);
+  if (local_header_offset + (off64_t) kLFHLen >= cd_offset) {
+    ALOGW("Zip: bad local hdr offset in zip");
+    return kInvalidOffset;
+  }
+
+  uint8_t lfh_buf[kLFHLen];
+  ssize_t actual = ReadAtOffset(archive->fd, lfh_buf, sizeof(lfh_buf),
+                                 local_header_offset);
+  if (actual != sizeof(lfh_buf)) {
+    ALOGW("Zip: failed reading lfh name from offset %lld", local_header_offset);
+    return kIoError;
+  }
+
+  if (get4LE(lfh_buf) != kLFHSignature) {
+    ALOGW("Zip: didn't find signature at start of lfh, offset=%lld",
+        local_header_offset);
+    return kInvalidOffset;
+  }
+
+  // Paranoia: Match the values specified in the local file header
+  // to those specified in the central directory.
+  const uint16_t lfhGpbFlags = get2LE(lfh_buf + kLFHGPBFlags);
+  const uint16_t lfhNameLen = get2LE(lfh_buf + kLFHNameLen);
+  const uint16_t lfhExtraLen = get2LE(lfh_buf + kLFHExtraLen);
+
+  if ((lfhGpbFlags & kGPBDDFlagMask) == 0) {
+    const uint32_t lfhCrc = get4LE(lfh_buf + kLFHCRC);
+    const uint32_t lfhCompLen = get4LE(lfh_buf + kLFHCompLen);
+    const uint32_t lfhUncompLen = get4LE(lfh_buf + kLFHUncompLen);
+
+    data->has_data_descriptor = 0;
+    if (data->compressed_length != lfhCompLen || data->uncompressed_length != lfhUncompLen
+        || data->crc32 != lfhCrc) {
+      ALOGW("Zip: size/crc32 mismatch. expected {%d, %d, %x}, was {%d, %d, %x}",
+        data->compressed_length, data->uncompressed_length, data->crc32,
+        lfhCompLen, lfhUncompLen, lfhCrc);
+      return kInconsistentInformation;
+    }
+  } else {
+    data->has_data_descriptor = 1;
+  }
+
+  // Check that the local file header name matches the declared
+  // name in the central directory.
+  if (lfhNameLen == nameLen) {
+    const off64_t name_offset = local_header_offset + kLFHLen;
+    if (name_offset + lfhNameLen >= cd_offset) {
+      ALOGW("Zip: Invalid declared length");
+      return kInvalidOffset;
+    }
+
+    uint8_t* name_buf = (uint8_t*) malloc(nameLen);
+    ssize_t actual = ReadAtOffset(archive->fd, name_buf, nameLen,
+                                  name_offset);
+
+    if (actual != nameLen) {
+      ALOGW("Zip: failed reading lfh name from offset %lld", name_offset);
+      free(name_buf);
+      return kIoError;
+    }
+
+    if (memcmp(name, name_buf, nameLen)) {
+      free(name_buf);
+      return kInconsistentInformation;
+    }
+
+    free(name_buf);
+  } else {
+    ALOGW("Zip: lfh name did not match central directory.");
+    return kInconsistentInformation;
+  }
+
+  const off64_t data_offset = local_header_offset + kLFHLen + lfhNameLen + lfhExtraLen;
+  if (data_offset >= cd_offset) {
+    ALOGW("Zip: bad data offset %lld in zip", (off64_t) data_offset);
+    return kInvalidOffset;
+  }
+
+  if ((off64_t)(data_offset + data->compressed_length) > cd_offset) {
+    ALOGW("Zip: bad compressed length in zip (%lld + %zd > %lld)",
+      data_offset, data->compressed_length, cd_offset);
+    return kInvalidOffset;
+  }
+
+  if (data->method == kCompressStored &&
+    (off64_t)(data_offset + data->uncompressed_length) > cd_offset) {
+     ALOGW("Zip: bad uncompressed length in zip (%lld + %zd > %lld)",
+       data_offset, data->uncompressed_length, cd_offset);
+     return kInvalidOffset;
+  }
+
+  data->offset = data_offset;
+  return 0;
+}
+
+struct IterationHandle {
+  uint32_t position;
+  const char* prefix;
+  uint16_t prefix_len;
+  ZipArchive* archive;
+};
+
+int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr, const char* prefix) {
+  ZipArchive* archive = (ZipArchive *) handle;
+
+  if (archive == NULL || archive->hash_table == NULL) {
+    ALOGW("Zip: Invalid ZipArchiveHandle");
+    return kInvalidHandle;
+  }
+
+  IterationHandle* cookie = (IterationHandle*) malloc(sizeof(IterationHandle));
+  cookie->position = 0;
+  cookie->prefix = prefix;
+  cookie->archive = archive;
+  if (prefix != NULL) {
+    cookie->prefix_len = strlen(prefix);
+  }
+
+  *cookie_ptr = cookie ;
+  return 0;
+}
+
+int32_t FindEntry(const ZipArchiveHandle handle, const char* entryName,
+                  ZipEntry* data) {
+  const ZipArchive* archive = (ZipArchive*) handle;
+  const int nameLen = strlen(entryName);
+  if (nameLen == 0 || nameLen > 65535) {
+    ALOGW("Zip: Invalid filename %s", entryName);
+    return kInvalidEntryName;
+  }
+
+  const int64_t ent = EntryToIndex(archive->hash_table,
+    archive->hash_table_size, entryName, nameLen);
+
+  if (ent < 0) {
+    ALOGV("Zip: Could not find entry %.*s", nameLen, entryName);
+    return ent;
+  }
+
+  return FindEntry(archive, ent, data);
+}
+
+int32_t Next(void* cookie, ZipEntry* data, ZipEntryName* name) {
+  IterationHandle* handle = (IterationHandle *) cookie;
+  if (handle == NULL) {
+    return kInvalidHandle;
+  }
+
+  ZipArchive* archive = handle->archive;
+  if (archive == NULL || archive->hash_table == NULL) {
+    ALOGW("Zip: Invalid ZipArchiveHandle");
+    return kInvalidHandle;
+  }
+
+  const uint32_t currentOffset = handle->position;
+  const uint32_t hash_table_length = archive->hash_table_size;
+  const ZipEntryName *hash_table = archive->hash_table;
+
+  for (uint32_t i = currentOffset; i < hash_table_length; ++i) {
+    if (hash_table[i].name != NULL &&
+        (handle->prefix == NULL ||
+         (memcmp(handle->prefix, hash_table[i].name, handle->prefix_len) == 0))) {
+      handle->position = (i + 1);
+      const int error = FindEntry(archive, i, data);
+      if (!error) {
+        name->name = hash_table[i].name;
+        name->name_length = hash_table[i].name_length;
+      }
+
+      return error;
+    }
+  }
+
+  handle->position = 0;
+  return kIterationEnd;
+}
+
+static int32_t InflateToFile(int fd, const ZipEntry* entry,
+                             uint8_t* begin, uint32_t length,
+                             uint64_t* crc_out) {
+  int32_t result = -1;
+  const uint32_t kBufSize = 32768;
+  uint8_t read_buf[kBufSize];
+  uint8_t write_buf[kBufSize];
+  z_stream zstream;
+  int zerr;
+
+  /*
+   * Initialize the zlib stream struct.
+   */
+  memset(&zstream, 0, sizeof(zstream));
+  zstream.zalloc = Z_NULL;
+  zstream.zfree = Z_NULL;
+  zstream.opaque = Z_NULL;
+  zstream.next_in = NULL;
+  zstream.avail_in = 0;
+  zstream.next_out = (Bytef*) write_buf;
+  zstream.avail_out = kBufSize;
+  zstream.data_type = Z_UNKNOWN;
+
+  /*
+   * Use the undocumented "negative window bits" feature to tell zlib
+   * that there's no zlib header waiting for it.
+   */
+  zerr = inflateInit2(&zstream, -MAX_WBITS);
+  if (zerr != Z_OK) {
+    if (zerr == Z_VERSION_ERROR) {
+      ALOGE("Installed zlib is not compatible with linked version (%s)",
+        ZLIB_VERSION);
+    } else {
+      ALOGW("Call to inflateInit2 failed (zerr=%d)", zerr);
+    }
+
+    return kZlibError;
+  }
+
+  const uint32_t uncompressed_length = entry->uncompressed_length;
+
+  uint32_t compressed_length = entry->compressed_length;
+  uint32_t write_count = 0;
+  do {
+    /* read as much as we can */
+    if (zstream.avail_in == 0) {
+      const ssize_t getSize = (compressed_length > kBufSize) ? kBufSize : compressed_length;
+      const ssize_t actual = TEMP_FAILURE_RETRY(read(fd, read_buf, getSize));
+      if (actual != getSize) {
+        ALOGW("Zip: inflate read failed (%d vs %zd)", actual, getSize);
+        result = kIoError;
+        goto z_bail;
+      }
+
+      compressed_length -= getSize;
+
+      zstream.next_in = read_buf;
+      zstream.avail_in = getSize;
+    }
+
+    /* uncompress the data */
+    zerr = inflate(&zstream, Z_NO_FLUSH);
+    if (zerr != Z_OK && zerr != Z_STREAM_END) {
+      ALOGW("Zip: inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)",
+          zerr, zstream.next_in, zstream.avail_in,
+          zstream.next_out, zstream.avail_out);
+      result = kZlibError;
+      goto z_bail;
+    }
+
+    /* write when we're full or when we're done */
+    if (zstream.avail_out == 0 ||
+      (zerr == Z_STREAM_END && zstream.avail_out != kBufSize)) {
+      const size_t write_size = zstream.next_out - write_buf;
+      // The file might have declared a bogus length.
+      if (write_size + write_count > length) {
+        goto z_bail;
+      }
+      memcpy(begin + write_count, write_buf, write_size);
+      write_count += write_size;
+
+      zstream.next_out = write_buf;
+      zstream.avail_out = kBufSize;
+    }
+  } while (zerr == Z_OK);
+
+  assert(zerr == Z_STREAM_END);     /* other errors should've been caught */
+
+  // stream.adler holds the crc32 value for such streams.
+  *crc_out = zstream.adler;
+
+  if (zstream.total_out != uncompressed_length || compressed_length != 0) {
+    ALOGW("Zip: size mismatch on inflated file (%ld vs %zd)",
+        zstream.total_out, uncompressed_length);
+    result = kInconsistentInformation;
+    goto z_bail;
+  }
+
+  result = 0;
+
+z_bail:
+  inflateEnd(&zstream);    /* free up any allocated structures */
+
+  return result;
+}
+
+int32_t ExtractToMemory(ZipArchiveHandle handle,
+                        ZipEntry* entry, uint8_t* begin, uint32_t size) {
+  ZipArchive* archive = (ZipArchive*) handle;
+  const uint16_t method = entry->method;
+  off64_t data_offset = entry->offset;
+
+  if (lseek64(archive->fd, data_offset, SEEK_SET) != data_offset) {
+    ALOGW("Zip: lseek to data at %lld failed", (off64_t) data_offset);
+    return kIoError;
+  }
+
+  // this should default to kUnknownCompressionMethod.
+  int32_t return_value = -1;
+  uint64_t crc = 0;
+  if (method == kCompressStored) {
+    return_value = CopyFileToFile(archive->fd, begin, size, &crc);
+  } else if (method == kCompressDeflated) {
+    return_value = InflateToFile(archive->fd, entry, begin, size, &crc);
+  }
+
+  if (!return_value && entry->has_data_descriptor) {
+    return_value = UpdateEntryFromDataDescriptor(archive->fd, entry);
+    if (return_value) {
+      return return_value;
+    }
+  }
+
+  // TODO: Fix this check by passing the right flags to inflate2 so that
+  // it calculates the CRC for us.
+  if (entry->crc32 != crc && false) {
+    ALOGW("Zip: crc mismatch: expected %u, was %llu", entry->crc32, crc);
+    return kInconsistentInformation;
+  }
+
+  return return_value;
+}
+
+int32_t ExtractEntryToFile(ZipArchiveHandle handle,
+                           ZipEntry* entry, int fd) {
+  const int32_t declared_length = entry->uncompressed_length;
+
+  const off64_t current_offset = lseek64(fd, 0, SEEK_CUR);
+  if (current_offset == -1) {
+    ALOGW("Zip: unable to seek to current location on fd %d: %s", fd,
+          strerror(errno));
+    return kIoError;
+  }
+
+  int result = TEMP_FAILURE_RETRY(ftruncate(fd, declared_length + current_offset));
+  if (result == -1) {
+    ALOGW("Zip: unable to truncate file to %lld: %s", declared_length + current_offset,
+          strerror(errno));
+    return kIoError;
+  }
+
+  android::FileMap* map  = MapFileSegment(fd, current_offset, declared_length,
+                                          false, kTempMappingFileName);
+  if (map == NULL) {
+    return kMmapFailed;
+  }
+
+  const int32_t error = ExtractToMemory(handle, entry,
+                                        reinterpret_cast<uint8_t*>(map->getDataPtr()),
+                                        map->getDataLength());
+  map->release();
+  return error;
+}
+
+const char* ErrorCodeString(int32_t error_code) {
+  if (error_code > kErrorMessageLowerBound && error_code < kErrorMessageUpperBound) {
+    return kErrorMessages[error_code * -1];
+  }
+
+  return kErrorMessages[0];
+}
+
+int GetFileDescriptor(const ZipArchiveHandle handle) {
+  return ((ZipArchive*) handle)->fd;
+}
+
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
new file mode 100644
index 0000000..022be5b
--- /dev/null
+++ b/libziparchive/zip_archive_test.cc
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ */
+
+#include "ziparchive/zip_archive.h"
+
+#include <errno.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+static std::string test_data_dir;
+
+static const std::string kValidZip = "valid.zip";
+
+static const uint8_t kATxtContents[] = {
+  'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
+  'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
+  '\n'
+};
+
+static const uint8_t kBTxtContents[] = {
+  'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
+  '\n'
+};
+
+static int32_t OpenArchiveWrapper(const std::string& name,
+                                  ZipArchiveHandle* handle) {
+  const std::string abs_path = test_data_dir + "/" + name;
+  return OpenArchive(abs_path.c_str(), handle);
+}
+
+static void AssertNameEquals(const std::string& name_str,
+                             const ZipEntryName& name) {
+  ASSERT_EQ(name_str.size(), name.name_length);
+  ASSERT_EQ(0, memcmp(name_str.c_str(), name.name, name.name_length));
+}
+
+TEST(ziparchive, Open) {
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+
+  CloseArchive(handle);
+}
+
+TEST(ziparchive, Iteration) {
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+
+  void* iteration_cookie;
+  ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, NULL));
+
+  ZipEntry data;
+  ZipEntryName name;
+
+  // b/c.txt
+  ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
+  AssertNameEquals("b/c.txt", name);
+
+  // b/d.txt
+  ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
+  AssertNameEquals("b/d.txt", name);
+
+  // a.txt
+  ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
+  AssertNameEquals("a.txt", name);
+
+  // b.txt
+  ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
+  AssertNameEquals("b.txt", name);
+
+  // b/
+  ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
+  AssertNameEquals("b/", name);
+
+  // End of iteration.
+  ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
+
+  CloseArchive(handle);
+}
+
+TEST(ziparchive, FindEntry) {
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+
+  ZipEntry data;
+  ASSERT_EQ(0, FindEntry(handle, "a.txt", &data));
+
+  // Known facts about a.txt, from zipinfo -v.
+  ASSERT_EQ(63, data.offset);
+  ASSERT_EQ(kCompressDeflated, data.method);
+  ASSERT_EQ(static_cast<uint32_t>(17), data.uncompressed_length);
+  ASSERT_EQ(static_cast<uint32_t>(13), data.compressed_length);
+  ASSERT_EQ(0x950821c5, data.crc32);
+
+  // An entry that doesn't exist. Should be a negative return code.
+  ASSERT_LT(FindEntry(handle, "nonexistent.txt", &data), 0);
+
+  CloseArchive(handle);
+}
+
+TEST(ziparchive, ExtractToMemory) {
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+
+  // An entry that's deflated.
+  ZipEntry data;
+  ASSERT_EQ(0, FindEntry(handle, "a.txt", &data));
+  const uint32_t a_size = data.uncompressed_length;
+  ASSERT_EQ(a_size, sizeof(kATxtContents));
+  uint8_t* buffer = new uint8_t[a_size];
+  ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, a_size));
+  ASSERT_EQ(0, memcmp(buffer, kATxtContents, a_size));
+  delete[] buffer;
+
+  // An entry that's stored.
+  ASSERT_EQ(0, FindEntry(handle, "b.txt", &data));
+  const uint32_t b_size = data.uncompressed_length;
+  ASSERT_EQ(b_size, sizeof(kBTxtContents));
+  buffer = new uint8_t[b_size];
+  ASSERT_EQ(0, ExtractToMemory(handle, &data, buffer, b_size));
+  ASSERT_EQ(0, memcmp(buffer, kBTxtContents, b_size));
+  delete[] buffer;
+
+  CloseArchive(handle);
+}
+
+TEST(ziparchive, ExtractToFile) {
+  char kTempFilePattern[] = "zip_archive_test_XXXXXX";
+  int fd = mkstemp(kTempFilePattern);
+  ASSERT_NE(-1, fd);
+  const uint8_t data[8] = { '1', '2', '3', '4', '5', '6', '7', '8' };
+  const ssize_t data_size = sizeof(data);
+
+  ASSERT_EQ(data_size, TEMP_FAILURE_RETRY(write(fd, data, data_size)));
+
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+
+  ZipEntry entry;
+  ASSERT_EQ(0, FindEntry(handle, "a.txt", &entry));
+  ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, fd));
+
+
+  // Assert that the first 8 bytes of the file haven't been clobbered.
+  uint8_t read_buffer[data_size];
+  ASSERT_EQ(0, lseek64(fd, 0, SEEK_SET));
+  ASSERT_EQ(data_size, TEMP_FAILURE_RETRY(read(fd, read_buffer, data_size)));
+  ASSERT_EQ(0, memcmp(read_buffer, data, data_size));
+
+  // Assert that the remainder of the file contains the incompressed data.
+  std::vector<uint8_t> uncompressed_data(entry.uncompressed_length);
+  ASSERT_EQ(static_cast<ssize_t>(entry.uncompressed_length),
+            TEMP_FAILURE_RETRY(
+                read(fd, &uncompressed_data[0], entry.uncompressed_length)));
+  ASSERT_EQ(0, memcmp(&uncompressed_data[0], kATxtContents,
+                      sizeof(kATxtContents)));
+
+  // Assert that the total length of the file is sane
+  ASSERT_EQ(data_size + sizeof(kATxtContents), lseek64(fd, 0, SEEK_END));
+
+  close(fd);
+}
+
+int main(int argc, char** argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+
+  static struct option options[] = {
+    { "test_data_dir", required_argument, NULL, 't' },
+    { NULL, 0, NULL, 0 }
+  };
+
+  while (true) {
+    int option_index;
+    const int c = getopt_long_only(argc, argv, "", options, &option_index);
+    if (c == -1) {
+      break;
+    }
+
+    if (c == 't') {
+      test_data_dir = optarg;
+    }
+  }
+
+  if (test_data_dir.size() == 0) {
+    printf("Test data flag (--test_data_dir) required\n\n");
+    return -1;
+  }
+
+  if (test_data_dir[0] != '/') {
+    printf("Test data must be an absolute path, was %s\n\n",
+           test_data_dir.c_str());
+    return -2;
+  }
+
+  return RUN_ALL_TESTS();
+}
+
diff --git a/reboot/reboot.c b/reboot/reboot.c
index 0e5170d..d9a4227 100644
--- a/reboot/reboot.c
+++ b/reboot/reboot.c
@@ -68,6 +68,11 @@
         perror("reboot");
         exit(EXIT_FAILURE);
     }
+
+    // Don't return early. Give the reboot command time to take effect
+    // to avoid messing up scripts which do "adb shell reboot && adb wait-for-device"
+    while(1) { pause(); }
+
     fprintf(stderr, "Done\n");
     return 0;
 }
diff --git a/rootdir/init.rc b/rootdir/init.rc
index cfc3d35..95ca6af 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -13,6 +13,9 @@
     # Set init and its forked children's oom_adj.
     write /proc/1/oom_adj -16
 
+    # Apply strict SELinux checking of PROT_EXEC on mmap/mprotect calls.
+    write /sys/fs/selinux/checkreqprot 0
+
     # Set the security context for the init process.
     # This should occur before anything else (e.g. ueventd) is started.
     setcon u:r:init:s0
@@ -69,8 +72,6 @@
 
     # Directory for putting things only root should see.
     mkdir /mnt/secure 0700 root root
-    # Create private mountpoint so we can MS_MOVE from staging
-    mount tmpfs tmpfs /mnt/secure mode=0700,uid=0,gid=0
 
     # Directory for staging bindmounts
     mkdir /mnt/secure/staging 0700 root root
@@ -146,7 +147,6 @@
     mount rootfs rootfs / ro remount
     # mount shared so changes propagate into child namespaces
     mount rootfs rootfs / shared rec
-    mount tmpfs tmpfs /mnt/secure private rec
 
     # We chown/chmod /cache again so because mount is run as root + defaults
     chown system cache /cache
@@ -214,6 +214,7 @@
     mkdir /data/misc/radio 0770 system radio
     mkdir /data/misc/sms 0770 system radio
     mkdir /data/misc/zoneinfo 0775 system system
+    restorecon_recursive /data/misc/zoneinfo
     mkdir /data/misc/vpn 0770 system vpn
     mkdir /data/misc/systemkeys 0700 system system
     # give system access to wpa_supplicant.conf for backup and restore
@@ -256,6 +257,7 @@
     # create directory for MediaDrm plug-ins - give drm the read/write access to
     # the following directory.
     mkdir /data/mediadrm 0770 mediadrm mediadrm
+    restorecon_recursive /data/mediadrm
 
     # symlink to bugreport storage location
     symlink /data/data/com.android.shell/files/bugreports /data/bugreports
@@ -359,9 +361,6 @@
     chown system system /sys/kernel/ipv4/tcp_rmem_max
     chown root radio /proc/cmdline
 
-# Set these so we can remotely update SELinux policy
-    chown system system /sys/fs/selinux/enforce
-
 # Define TCP buffer sizes for various networks
 #   ReadMin, ReadInitial, ReadMax, WriteMin, WriteInitial, WriteMax,
     setprop net.tcp.buffersize.default  4096,87380,110208,4096,16384,110208
@@ -436,6 +435,7 @@
     disabled
     user shell
     group log
+    seclabel u:r:shell:s0
 
 on property:ro.debuggable=1
     start console
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index ddf8753..9bf17e2 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -35,6 +35,7 @@
 /dev/uhid                 0660   system     net_bt_stack
 /dev/uinput               0660   system     net_bt_stack
 /dev/alarm                0664   system     radio
+/dev/rtc0                 0664   system     radio
 /dev/tty0                 0660   root       system
 /dev/graphics/*           0660   root       graphics
 /dev/msm_hw3dm            0660   system     graphics
diff --git a/toolbox/date.c b/toolbox/date.c
index 35ef846..ed307c0 100644
--- a/toolbox/date.c
+++ b/toolbox/date.c
@@ -6,15 +6,87 @@
 #include <errno.h>
 #include <time.h>
 #include <linux/android_alarm.h>
+#include <linux/rtc.h>
 #include <sys/ioctl.h>
 
+static int settime_alarm(struct timespec *ts) {
+    int fd, ret;
+
+    fd = open("/dev/alarm", O_RDWR);
+    if (fd < 0)
+        return fd;
+
+    ret = ioctl(fd, ANDROID_ALARM_SET_RTC, &ts);
+    close(fd);
+    return ret;
+}
+
+static int settime_alarm_tm(struct tm *tm) {
+    time_t t;
+    struct timespec ts;
+
+    t = mktime(tm);
+    ts.tv_sec = t;
+    ts.tv_nsec = 0;
+    return settime_alarm(&ts);
+}
+
+static int settime_alarm_timeval(struct timeval *tv) {
+    struct timespec ts;
+
+    ts.tv_sec = tv->tv_sec;
+    ts.tv_nsec = tv->tv_usec * 1000;
+    return settime_alarm(&ts);
+}
+
+static int settime_rtc_tm(struct tm *tm) {
+    int fd, ret;
+    struct timeval tv;
+    struct rtc_time rtc;
+
+    fd = open("/dev/rtc0", O_RDWR);
+    if (fd < 0)
+        return fd;
+
+    tv.tv_sec = mktime(tm);
+    tv.tv_usec = 0;
+
+    ret = settimeofday(&tv, NULL);
+    if (ret < 0)
+        goto done;
+
+    memset(&rtc, 0, sizeof(rtc));
+    rtc.tm_sec = tm->tm_sec;
+    rtc.tm_min = tm->tm_min;
+    rtc.tm_hour = tm->tm_hour;
+    rtc.tm_mday = tm->tm_mday;
+    rtc.tm_mon = tm->tm_mon;
+    rtc.tm_year = tm->tm_year;
+    rtc.tm_wday = tm->tm_wday;
+    rtc.tm_yday = tm->tm_yday;
+    rtc.tm_isdst = tm->tm_isdst;
+
+    ret = ioctl(fd, RTC_SET_TIME, rtc);
+done:
+    close(fd);
+    return ret;
+}
+
+static int settime_rtc_timeval(struct timeval *tv) {
+    struct tm tm, *err;
+    time_t t = tv->tv_sec;
+
+    err = gmtime_r(&t, &tm);
+    if (!err)
+        return -1;
+
+    return settime_rtc_tm(&tm);
+}
+
 static void settime(char *s) {
     struct tm tm;
     int day = atoi(s);
     int hour;
-    time_t t;
-    int fd;
-    struct timespec ts;
 
     while (*s && *s != '.')
         s++;
@@ -32,12 +104,8 @@
     tm.tm_sec = (hour % 100);
     tm.tm_isdst = -1;
 
-    t = mktime(&tm);
-    
-    fd = open("/dev/alarm", O_RDWR);
-    ts.tv_sec = t;
-    ts.tv_nsec = 0;
-    ioctl(fd, ANDROID_ALARM_SET_RTC, &ts);
+    if (settime_alarm_tm(&tm) < 0)
+        settime_rtc_tm(&tm);
 }
 
 int date_main(int argc, char *argv[])
@@ -114,12 +182,10 @@
         //tv.tv_sec = mktime(&tm);
         //tv.tv_usec = 0;
         strtotimeval(argv[optind], &tv);
-        printf("time %s -> %d.%d\n", argv[optind], tv.tv_sec, tv.tv_usec);
-        fd = open("/dev/alarm", O_RDWR);
-        ts.tv_sec = tv.tv_sec;
-        ts.tv_nsec = tv.tv_usec * 1000;
-        res = ioctl(fd, ANDROID_ALARM_SET_RTC, &ts);
-        //res = settimeofday(&tv, NULL);
+        printf("time %s -> %lu.%lu\n", argv[optind], tv.tv_sec, tv.tv_usec);
+        res = settime_alarm_timeval(&tv);
+        if (res < 0)
+            res = settime_rtc_timeval(&tv);
         if(res < 0) {
             fprintf(stderr,"settimeofday failed %s\n", strerror(errno));
             return 1;
diff --git a/toolbox/ls.c b/toolbox/ls.c
index c736958..c740f84 100644
--- a/toolbox/ls.c
+++ b/toolbox/ls.c
@@ -209,7 +209,7 @@
         break;
     case S_IFLNK: {
         char linkto[256];
-        int len;
+        ssize_t len;
 
         len = readlink(path, linkto, 256);
         if(len < 0) return -1;
@@ -235,7 +235,7 @@
     return 0;
 }
 
-static int listfile_maclabel(const char *path, struct stat *s, int flags)
+static int listfile_maclabel(const char *path, struct stat *s)
 {
     char mode[16];
     char user[32];
@@ -316,6 +316,7 @@
     }
 
     if(lstat(pathname, &s) < 0) {
+        fprintf(stderr, "lstat '%s' failed: %s\n", pathname, strerror(errno));
         return -1;
     }
 
@@ -324,7 +325,7 @@
     }
 
     if ((flags & LIST_MACLABEL) != 0) {
-        return listfile_maclabel(pathname, &s, flags);
+        return listfile_maclabel(pathname, &s);
     } else if ((flags & LIST_LONG) != 0) {
         return listfile_long(pathname, &s, flags);
     } else /*((flags & LIST_SIZE) != 0)*/ {
diff --git a/toolbox/swapon.c b/toolbox/swapon.c
index afa6868..a810b3d 100644
--- a/toolbox/swapon.c
+++ b/toolbox/swapon.c
@@ -5,12 +5,6 @@
 #include <asm/page.h>
 #include <sys/swap.h>
 
-/* XXX These need to be obtained from kernel headers. See b/9336527 */
-#define SWAP_FLAG_PREFER        0x8000
-#define SWAP_FLAG_PRIO_MASK     0x7fff
-#define SWAP_FLAG_PRIO_SHIFT    0
-#define SWAP_FLAG_DISCARD       0x10000
-
 void usage(char *name)
 {
     fprintf(stderr, "Usage: %s [-p prio] <filename>\n"
diff --git a/toolbox/uptime.c b/toolbox/uptime.c
index 1c312b0..3fb4606 100644
--- a/toolbox/uptime.c
+++ b/toolbox/uptime.c
@@ -54,24 +54,35 @@
         sprintf(buffer, "%02d:%02d:%02d", hours, minutes, seconds);
 }
 
-int64_t elapsedRealtime()
+static int elapsedRealtimeAlarm(struct timespec *ts)
 {
-    struct timespec ts;
     int fd, result;
 
     fd = open("/dev/alarm", O_RDONLY);
     if (fd < 0)
         return fd;
 
-   result = ioctl(fd, ANDROID_ALARM_GET_TIME(ANDROID_ALARM_ELAPSED_REALTIME), &ts);
-   close(fd);
+    result = ioctl(fd, ANDROID_ALARM_GET_TIME(ANDROID_ALARM_ELAPSED_REALTIME), ts);
+    close(fd);
+
+    return result;
+}
+
+int64_t elapsedRealtime()
+{
+    struct timespec ts;
+
+    int result = elapsedRealtimeAlarm(&ts);
+    if (result < 0)
+        result = clock_gettime(CLOCK_BOOTTIME, &ts);
 
     if (result == 0)
         return ts.tv_sec;
     return -1;
 }
 
-int uptime_main(int argc, char *argv[])
+int uptime_main(int argc __attribute__((unused)),
+        char *argv[] __attribute__((unused)))
 {
     float up_time, idle_time;
     char up_string[100], idle_string[100], sleep_string[100];