Merge \"simpleperf: add thread_id in report_sample\'s output.\"
am: 0611ad5a36

Change-Id: Ib3b22e80e1f59827aa3ed65413ef2344c7ede390
diff --git a/ANRdaemon/ANRdaemon.cpp b/ANRdaemon/ANRdaemon.cpp
index 5a4f8bf..7b77a86 100644
--- a/ANRdaemon/ANRdaemon.cpp
+++ b/ANRdaemon/ANRdaemon.cpp
@@ -54,28 +54,31 @@
 
 using namespace android;
 
-#define CHECK_PERIOD 1  // in sec
-#define TRACING_CHECK_PERIOD 500000 // in micro sec
-#define MIN_BUFFER_SIZE 4
-#define MIN_BUFFER_SIZE_STR "4"
-#define MAX_BUFFER_SIZE 128
-#define MAX_BUFFER_SIZE_STR "128"
-#define CPU_STAT_ENTRIES 7 // number of cpu stat entries
-
 #ifdef LOG_TAG
 #undef LOG_TAG
 #endif
 
 #define LOG_TAG "anrdaemon"
 
+static const int check_period = 1;              // in sec
+static const int tracing_check_period = 500000; // in micro sec
+static const int cpu_stat_entries = 7;          // number of cpu stat entries
+static const int min_buffer_size = 16;
+static const int max_buffer_size = 2048;
+static const char *min_buffer_size_str = "16";
+static const char *max_buffer_size_str = "2048";
+
 typedef struct cpu_stat {
     unsigned long utime, ntime, stime, itime;
     unsigned long iowtime, irqtime, sirqtime, steal;
     unsigned long total;
 } cpu_stat_t;
 
-/* Make the logging on/off threshold equal to 95% cpu usage. */
-static int idle_threshold = 5;
+/*
+ * Logging on/off threshold.
+ * Uint: 0.01%; default to 99.90% cpu.
+ */
+static int idle_threshold = 10;
 
 static bool quit = false;
 static bool suspend= false;
@@ -83,13 +86,12 @@
 static char err_msg[100];
 static bool tracing = false;
 
-static const char *buf_size_kb = "16";
+static const char *buf_size_kb = "2048";
+static const char *apps = "";
 static uint64_t tag = 0;
-static const char* apps = "";
 
 static cpu_stat_t new_cpu;
 static cpu_stat_t old_cpu;
-static Vector<String16> targets;
 
 /* Log certain kernel activity when enabled */
 static bool log_sched = false;
@@ -133,7 +135,7 @@
     } else {
         if (fscanf(fp, params, &cpu->utime, &cpu->ntime,
                 &cpu->stime, &cpu->itime, &cpu->iowtime, &cpu->irqtime,
-                &cpu->sirqtime) != CPU_STAT_ENTRIES) {
+                &cpu->sirqtime) != cpu_stat_entries) {
             /*
              * If failed in getting status, new_cpu won't be updated and
              * is_heavy_loaded() will return false.
@@ -151,26 +153,26 @@
 
 /*
  * Calculate cpu usage in the past interval.
- * If tracing is on, increase the idle threshold by 1% so that we do not
+ * If tracing is on, increase the idle threshold by 1.00% so that we do not
  * turn on and off tracing frequently whe the cpu load is right close to
  * threshold.
  */
 static bool is_heavy_load(void) {
     unsigned long diff_idle, diff_total;
-    int threshold = idle_threshold + (tracing?1:0);
+    int threshold = idle_threshold + (tracing?100:0);
     get_cpu_stat(&new_cpu);
     diff_idle = new_cpu.itime - old_cpu.itime;
     diff_total = new_cpu.total - old_cpu.total;
     old_cpu = new_cpu;
-    return (diff_idle * 100 < diff_total * threshold);
+    return (diff_idle * 10000 < diff_total * threshold);
 }
 
 /*
  * Force the userland processes to refresh their property for logging.
  */
-static void dfs_poke_binder(Vector<String16> services) {
+static void dfs_poke_binder(void) {
     sp<IServiceManager> sm = defaultServiceManager();
-    services = sm->listServices();
+    Vector<String16> services = sm->listServices();
     for (size_t i = 0; i < services.size(); i++) {
         sp<IBinder> obj = sm->checkService(services[i]);
         if (obj != NULL) {
@@ -194,8 +196,10 @@
     ssize_t len = strlen(control);
     int max_try = 10; // Fail if write was interrupted for 10 times
     while (write(fd, control, len) != len) {
-        if (errno == EINTR && max_try-- > 0)
+        if (errno == EINTR && max_try-- > 0) {
+            usleep(100);
             continue;
+        }
 
         err = true;
         sprintf(err_msg, "Error %d in writing to %s.", errno, path);
@@ -246,9 +250,6 @@
 static void start_tracing(void) {
     ALOGD("High cpu usage, start logging.");
 
-    dfs_set_property(tag, apps, true);
-    dfs_poke_binder(targets);
-
     if (dfs_enable(true, dfs_control_path) != 0) {
         ALOGE("Failed to start tracing.");
         return;
@@ -257,16 +258,13 @@
 
     /* Stop logging when cpu usage drops or the daemon is suspended.*/
     do {
-        usleep(TRACING_CHECK_PERIOD);
+        usleep(tracing_check_period);
     } while (!suspend && is_heavy_load());
 
     if (dfs_enable(false, dfs_control_path) != 0) {
         ALOGE("Failed to stop tracing.");
     }
 
-    dfs_set_property(0, "", false);
-    dfs_poke_binder(targets);
-
     ALOGD("Usage back to low, stop logging.");
     tracing = false;
 }
@@ -299,8 +297,12 @@
 static void start(void) {
     if ((set_tracing_buffer_size()) != 0)
         return;
+
+    dfs_set_property(tag, apps, true);
+    dfs_poke_binder();
+
     get_cpu_stat(&old_cpu);
-    sleep(CHECK_PERIOD);
+    sleep(check_period);
 
     while (!quit && !err) {
         if (!suspend && is_heavy_load()) {
@@ -312,7 +314,7 @@
             start_tracing();
             setpriority(PRIO_PROCESS, 0, 0);
         }
-        sleep(CHECK_PERIOD);
+        sleep(check_period);
     }
     return;
 }
@@ -326,7 +328,7 @@
     suspend = true;
     while (tracing) {
         ALOGI("Waiting logging to stop.");
-        usleep(TRACING_CHECK_PERIOD);
+        usleep(tracing_check_period);
         remain_attempts--;
         if (remain_attempts == 0) {
             ALOGE("Can't stop logging after 5 attempts. Dump aborted.");
@@ -335,7 +337,18 @@
     }
 
     /*
-     * Create a dump file "dump_of_anrdaemon.<current_time>" under /data/anr/
+     * Create /sdcard/ANRdaemon/ if it doesn't exist
+     */
+    struct stat st;
+    if (stat("/sdcard/ANRdaemon", &st) == -1) {
+        ALOGI("Creating /sdcard/ANRdaemon/");
+        int err = mkdir("/sdcard/ANRdaemon", 0700);
+        if (err != 0)
+            ALOGI("Creating /sdcard/ANRdaemon/ failed with %s", strerror(err));
+    }
+
+    /*
+     * Create a dump file "dump_of_anrdaemon.<current_time>" under /sdcard/ANRdaemon/
      */
     time_t now = time(0);
     struct tm  tstruct;
@@ -345,7 +358,7 @@
     ssize_t header_len = strlen(header);
     tstruct = *localtime(&now);
     strftime(time_buf, sizeof(time_buf), "%Y-%m-%d.%X", &tstruct);
-    sprintf(path_buf, "/data/anr/dump_of_anrdaemon.%s", time_buf);
+    sprintf(path_buf, "/sdcard/ANRdaemon/dump_of_anrdaemon.%s", time_buf);
     int output_fd = creat(path_buf, S_IRWXU);
     if (output_fd == -1) {
         ALOGE("Failed to create %s. Dump aborted.", path_buf);
@@ -494,16 +507,19 @@
                     "   -a appname  enable app-level tracing for a comma "
                        "separated list of cmdlines\n"
                     "   -t N        cpu threshold for logging to start "
-                        "(min = 50, max = 100, default = 95)\n"
+                        "(uint = 0.01%%, min = 5000, max = 9999, default = 9990)\n"
                     "   -s N        use a trace buffer size of N KB "
-                        "default to 16KB\n"
+                        "default to 2048KB\n"
                     "   -h          show helps\n");
     fprintf(stdout, "Categoris includes:\n"
                     "   am         - activity manager\n"
                     "   sm         - sync manager\n"
                     "   input      - input\n"
-                    "   app        - application\n"
                     "   dalvik     - dalvik VM\n"
+                    "   audio      - Audio\n"
+                    "   gfx        - Graphics\n"
+                    "   rs         - RenderScript\n"
+                    "   hal        - Hardware Modules\n"
                     "   irq        - kernel irq events\n"
                     "   sched      - kernel scheduler activity\n"
                     "   stack      - kernel stack\n"
@@ -526,20 +542,20 @@
                 apps = optarg;
                 break;
             case 's':
-                if (atoi(optarg) > MAX_BUFFER_SIZE)
-                    buf_size_kb = MAX_BUFFER_SIZE_STR;
-                else if (atoi(optarg) < MIN_BUFFER_SIZE)
-                    buf_size_kb = MIN_BUFFER_SIZE_STR;
+                if (atoi(optarg) > max_buffer_size)
+                    buf_size_kb = max_buffer_size_str;
+                else if (atoi(optarg) < min_buffer_size)
+                    buf_size_kb = min_buffer_size_str;
                 else
                     buf_size_kb = optarg;
                 break;
             case 't':
                 threshold = atoi(optarg);
-                if (threshold > 100 || threshold < 50) {
-                    fprintf(stderr, "logging threshold should be 50-100\n");
+                if (threshold > 9999 || threshold < 5000) {
+                    fprintf(stderr, "logging threshold should be 5000-9999\n");
                     return 1;
                 }
-                idle_threshold = 100 - threshold;
+                idle_threshold = 10000 - threshold;
                 break;
             case 'h':
                 show_help();
@@ -558,10 +574,16 @@
             tag |= ATRACE_TAG_INPUT;
         } else if (strcmp(argv[i], "sm") == 0) {
             tag |= ATRACE_TAG_SYNC_MANAGER;
-        } else if (strcmp(argv[i], "app") == 0) {
-            tag |= ATRACE_TAG_APP;
         } else if (strcmp(argv[i], "dalvik") == 0) {
             tag |= ATRACE_TAG_DALVIK;
+        } else if (strcmp(argv[i], "gfx") == 0) {
+            tag |= ATRACE_TAG_GRAPHICS;
+        } else if (strcmp(argv[i], "audio") == 0) {
+            tag |= ATRACE_TAG_AUDIO;
+        } else if (strcmp(argv[i], "hal") == 0) {
+            tag |= ATRACE_TAG_HAL;
+        } else if (strcmp(argv[i], "rs") == 0) {
+            tag |= ATRACE_TAG_RS;
         } else if (strcmp(argv[i], "sched") == 0) {
             log_sched = true;
         } else if (strcmp(argv[i], "stack") == 0) {
@@ -579,29 +601,10 @@
         }
     }
 
-    bool kernel_log = log_sched || log_stack || log_workq || log_irq || log_sync;
-    bool app_log = (tag == 0);
-
-    /*
-     * There are ~80 services. Too expensive to poke all of them. Just include
-     * service that may help high CPU ANR analysis.
-     */
-    if (app_log) {
-       targets.push_back(String16("activity"));
-       targets.push_back(String16("alarm"));
-       targets.push_back(String16("appops"));
-       targets.push_back(String16("cpuinfo"));
-       targets.push_back(String16("meminfo"));
-       targets.push_back(String16("procstats"));
-       targets.push_back(String16("input"));
-       targets.push_back(String16("lancherapps"));
-       targets.push_back(String16("bluetooth_manager"));
-       targets.push_back(String16("SurfaceFlinger"));
-       targets.push_back(String16("ClockworkProxyNativeService"));
-    }
-    if (!kernel_log && !app_log) {
-        tag |= ATRACE_TAG_ACTIVITY_MANAGER;
-        targets.push_back(String16("activity"));
+    /* If nothing is enabled, don't run */
+    if (!tag && !log_sched && !log_stack && !log_workq && !log_irq && !log_sync) {
+        ALOGE("Specify at least one category to trace.");
+        return 1;
     }
 
     return 0;
diff --git a/ANRdaemon/ANRdaemon_get_trace.sh b/ANRdaemon/ANRdaemon_get_trace.sh
new file mode 100755
index 0000000..be4062c
--- /dev/null
+++ b/ANRdaemon/ANRdaemon_get_trace.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+
+TRACE_DIR=/sdcard/ANRdaemon
+
+if [ $# -eq 1 ]; then
+    DEVICE=$(echo "-s $1")
+else
+    DEVICE=""
+fi
+
+PID=$(adb $DEVICE shell "ps | grep anrd")
+
+if [ $? -ne 0 ]; then
+    echo "FAILED. ADB failed or Daemon is not running."
+    exit 1
+fi
+
+PID=$(echo "$PID" | awk '{ print $2 }')
+adb $DEVICE shell "kill -s SIGUSR1 $PID"
+
+TRACE_FILE=$(adb $DEVICE shell "ls $TRACE_DIR | tail -n1" | tr -d '\r')
+
+# Wiat the trace file generation to complete
+adb $DEVICE shell "lsof $PID" | grep $TRACE_FILE > /dev/null
+while [ $? -eq 0 ];
+do
+    sleep 1
+    adb $DEVICE shell "lsof $PID" | grep "$TRACE_FILE" > /dev/null
+done
+
+if [ -z "$TRACE_FILE" ]; then
+    echo "FAILED. Trace file not created"
+fi
+
+adb $DEVICE pull "${TRACE_DIR}/${TRACE_FILE}" ${TRACE_FILE}
+
+CURRENT_DIR=$(pwd)
+echo SUCCEED!
+echo Trace stored at ${CURRENT_DIR}/${TRACE_FILE}
diff --git a/ANRdaemon/Android.mk b/ANRdaemon/Android.mk
index 535eb8c..24072e2 100644
--- a/ANRdaemon/Android.mk
+++ b/ANRdaemon/Android.mk
@@ -1,19 +1,19 @@
+ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
+
 LOCAL_PATH:= $(call my-dir)
+
 include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= ANRdaemon.cpp
-
+LOCAL_SRC_FILES := ANRdaemon.cpp
 LOCAL_C_INCLUDES += external/zlib
-
-LOCAL_MODULE:= anrdaemon
-
-LOCAL_MODULE_TAGS:= optional
-
+LOCAL_MODULE := anrd
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
 LOCAL_SHARED_LIBRARIES := \
     liblog \
     libbinder \
     libcutils \
     libutils \
-    libz \
-
+    libz
 include $(BUILD_EXECUTABLE)
+
+endif
diff --git a/ANRdaemon/README b/ANRdaemon/README
new file mode 100644
index 0000000..57ed594
--- /dev/null
+++ b/ANRdaemon/README
@@ -0,0 +1,30 @@
+ANRdaemon is a daemon to help analyze ANR due to CPU starvation by logging system
+activity when CPU usage is very high. The daemon uses debugfs underlying for
+logging. Trace are configured ahead by setting different modules in /d/tracing.
+Depending on the CPU usage level, the trace is turn on/off by writting to the
+global control /d/trace/trace_on. The raw trace file is stored at
+/d/tracing/trace.
+
+The daemon will be started at boot time and will be running with the following
+settings:
+$ ANRdaemon -t 9990 sched gfx am
+This means tracing will be enabled above 99.90% CPU utilization and will trace
+sched, gfx and am modules (See -h for more info).
+
+Use ANRdaemon_get_trace.sh [device serial] to dump and fetch the compressed trace file.
+
+The compressed trace file can be parsed using systrace:
+$ systrace.py --from-file=<path to compressed trace file>
+
+Known issue: in the systrace output, anrdaemon will show up when the trace is
+not running. This is because the daemon process turns off tracing when CPU usage
+drops, the last entry it leaves in the raw trace file is the scheduler switched
+from some other process to the daemon. Then sometime later (say 20 secs later),
+when the CPU usage becomes high and the daemon process turn on tracing again,
+the first entry in /d/tracing/trace logged by sched is switching away from the
+daemon process to some other process. Due to this artifact, when the raw trace
+file is parsed by systrace.py, the daemon process is shown as running for the
+whole 20secs (because from systrace's view, the two 20 sec apart sched trace
+entries regarding the daemon process indicates the daemon process ran continuously
+for all 20sec). However, this will not affect the actual captured trace during
+high CPU usage case.
diff --git a/alloc-stress/Android.mk b/alloc-stress/Android.mk
index 3185d55..06b5818 100644
--- a/alloc-stress/Android.mk
+++ b/alloc-stress/Android.mk
@@ -4,9 +4,12 @@
 LOCAL_CLANG := true
 LOCAL_MODULE := alloc-stress
 LOCAL_CFLAGS += -g -Wall -Werror -std=gnu++11 -Wno-missing-field-initializers -Wno-sign-compare
+ifneq ($(ENABLE_MEM_CGROUPS),)
+    LOCAL_CFLAGS += -DENABLE_MEM_CGROUPS
+endif
 LOCAL_C_INCLUDES += $(LOCAL_PATH)/../include
 LOCAL_C_INCLUDES += $(LOCAL_PATH)/..
-LOCAL_SHARED_LIBRARIES := libhardware
+LOCAL_SHARED_LIBRARIES := libhardware libcutils
 LOCAL_SRC_FILES := \
     alloc-stress.cpp
 include $(BUILD_EXECUTABLE)
diff --git a/alloc-stress/alloc-stress.cpp b/alloc-stress/alloc-stress.cpp
index 2fc3592..726ea96 100644
--- a/alloc-stress/alloc-stress.cpp
+++ b/alloc-stress/alloc-stress.cpp
@@ -1,5 +1,7 @@
+#include <arpa/inet.h>
 #include <iostream>
 #include <chrono>
+#include <cutils/sockets.h>
 #include <hardware/gralloc.h>
 #include <vector>
 #include <tuple>
@@ -9,6 +11,7 @@
 #include <fcntl.h>
 #include <string>
 #include <fstream>
+#include <sys/stat.h>
 #include <sys/wait.h>
 
 using namespace std;
@@ -108,7 +111,6 @@
         char writeFdStr[16];
         snprintf(readFdStr, sizeof(readFdStr), "%d", pipe.getReadFd());
         snprintf(writeFdStr, sizeof(writeFdStr), "%d", pipe.getWriteFd());
-
         execl(exName, exName, "--worker", arg, readFdStr, writeFdStr, 0);
         ASSERT_TRUE(0);
     }
@@ -123,22 +125,64 @@
 }
 
 
-void writeToFile(const char * path, const char *data)
-{
-    ofstream file(path);
-    file << data;
-    file.close();
+static void write_oomadj_to_lmkd(int oomadj) {
+    // Connect to lmkd and store our oom_adj
+    int lmk_procprio_cmd[4];
+    int sock;
+    int tries = 10;
+    while ((sock = socket_local_client("lmkd",
+                    ANDROID_SOCKET_NAMESPACE_RESERVED,
+                    SOCK_SEQPACKET)) < 0) {
+        usleep(100000);
+        if (tries-- < 0) break;
+    }
+    if (sock < 0) {
+        cout << "Failed to connect to lmkd, errno " << errno << endl;
+        exit(1);
+    }
+    lmk_procprio_cmd[0] = htonl(1);
+    lmk_procprio_cmd[1] = htonl(getpid());
+    lmk_procprio_cmd[2] = htonl(getuid());
+    lmk_procprio_cmd[3] = htonl(oomadj);
+
+    int written = write(sock, lmk_procprio_cmd, sizeof(lmk_procprio_cmd));
+    cout << "Wrote " << written << " bytes to lmkd control socket." << endl;
 }
 
+#ifdef ENABLE_MEM_CGROUPS
+static void create_memcg() {
+    char buf[256];
+    pid_t pid = getpid();
+    snprintf(buf, sizeof(buf), "/dev/memctl/apps/%u", pid);
+
+    int tasks = mkdir(buf, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
+    if (tasks < 0) {
+        cout << "Failed to create memory cgroup" << endl;
+        return;
+    }
+    snprintf(buf, sizeof(buf), "/dev/memctl/apps/%u/tasks", pid);
+    tasks = open(buf, O_WRONLY);
+    if (tasks < 0) {
+        cout << "Unable to add process to memory cgroup" << endl;
+        return;
+    }
+    snprintf(buf, sizeof(buf), "%u", pid);
+    write(tasks, buf, strlen(buf));
+    close(tasks);
+}
+#endif
+
 size_t s = 4 * (1 << 20);
 void *gptr;
 int main(int argc, char *argv[])
 {
     if ((argc > 1) && (std::string(argv[1]) == "--worker")) {
+#ifdef ENABLE_MEM_CGROUPS
+        create_memcg();
+#endif
+        write_oomadj_to_lmkd(atoi(argv[2]));
         Pipe p{atoi(argv[3]), atoi(argv[4])};
 
-        writeToFile("/proc/self/oom_adj", argv[2]);
-
         long long allocCount = 0;
         while (1) {
             p.wait();
@@ -155,10 +199,10 @@
             allocCount += s;
         }
     } else {
-        writeToFile("/proc/self/oom_adj", "-17");
         cout << "parent:" << argc << endl;
 
-        for (int i = 10; i >= 0; i--) {
+        write_oomadj_to_lmkd(-1000);
+        for (int i = 1000; i >= 0; i -= 100) {
             auto pipes = Pipe::createPipePair();
             char arg[16];
             snprintf(arg, sizeof(arg), "%d", i);
diff --git a/cpustats/cpustats.c b/cpustats/cpustats.c
index 32d75b2..0042caf 100644
--- a/cpustats/cpustats.c
+++ b/cpustats/cpustats.c
@@ -267,16 +267,22 @@
 
     sprintf(filename, "/sys/devices/system/cpu/cpu%d/cpufreq/stats/time_in_state", cpu);
     file = fopen(filename, "r");
-    if (!file) die("Could not open %s\n", filename);
     for (i = 0; i < new_cpus[cpu].freq_count; i++) {
-        fscanf(file, "%u %lu\n", &new_cpus[cpu].freqs[i].freq,
+        if (file) {
+            fscanf(file, "%u %lu\n", &new_cpus[cpu].freqs[i].freq,
                &new_cpus[cpu].freqs[i].time);
+        } else {
+            /* The CPU has been off lined for some reason */
+            new_cpus[cpu].freqs[i].freq = old_cpus[cpu].freqs[i].freq;
+            new_cpus[cpu].freqs[i].time = old_cpus[cpu].freqs[i].time;
+        }
         if (aggregate_freq_stats) {
             new_total_cpu.freqs[i].freq = new_cpus[cpu].freqs[i].freq;
             new_total_cpu.freqs[i].time += new_cpus[cpu].freqs[i].time;
         }
     }
-    fclose(file);
+    if (file)
+        fclose(file);
 }
 
 /*
diff --git a/crypto-perf/Android.mk b/crypto-perf/Android.mk
new file mode 100644
index 0000000..291aca1
--- /dev/null
+++ b/crypto-perf/Android.mk
@@ -0,0 +1,13 @@
+LOCAL_PATH := $(call my-dir)
+ifeq ($(TARGET_ARCH),arm64)
+include $(CLEAR_VARS)
+
+LOCAL_CFLAGS := -O0 -march=armv8-a+crypto
+LOCAL_SRC_FILES := crypto.cpp
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := crypto
+
+include $(BUILD_EXECUTABLE)
+endif
diff --git a/crypto-perf/NOTICE b/crypto-perf/NOTICE
new file mode 100644
index 0000000..c77f135
--- /dev/null
+++ b/crypto-perf/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2012, 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.
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/crypto-perf/crypto.cpp b/crypto-perf/crypto.cpp
new file mode 100644
index 0000000..2cd2709
--- /dev/null
+++ b/crypto-perf/crypto.cpp
@@ -0,0 +1,170 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sched.h>
+#include <sys/resource.h>
+#include <ctype.h>
+#define USEC_PER_SEC 1000000ULL
+#define MAX_COUNT 1000000000ULL
+#define NUM_INSTS_GARBAGE 18
+
+// Contains information about benchmark options.
+typedef struct {
+    int cpu_to_lock;
+    int locked_freq;
+} command_data_t;
+
+void usage() {
+    printf("--------------------------------------------------------------------------------\n");
+    printf("Usage:");
+    printf("	crypto [--cpu_to_lock CPU] [--locked_freq FREQ_IN_KHZ]\n\n");
+    printf("!!!!!!Lock the desired core to a desired frequency before invoking this benchmark.\n");
+    printf(
+          "Hint: Set scaling_max_freq=scaling_min_freq=FREQ_IN_KHZ. FREQ_IN_KHZ "
+          "can be obtained from scaling_available_freq\n");
+    printf("--------------------------------------------------------------------------------\n");
+}
+
+int processOptions(int argc, char **argv, command_data_t *cmd_data) {
+    // Initialize the command_flags.
+    cmd_data->cpu_to_lock = 0;
+    cmd_data->locked_freq = 1;
+    for (int i = 1; i < argc; i++) {
+        if (argv[i][0] == '-') {
+            int *save_value = NULL;
+            if (strcmp(argv[i], "--cpu_to_lock") == 0) {
+                save_value = &cmd_data->cpu_to_lock;
+	    } else if (strcmp(argv[i], "--locked_freq") == 0) {
+                save_value = &cmd_data->locked_freq;
+            } else {
+                printf("Unknown option %s\n", argv[i]);
+                return -1;
+            }
+            if (save_value) {
+                // Checking both characters without a strlen() call should be
+                // safe since as long as the argument exists, one character will
+                // be present (\0). And if the first character is '-', then
+                // there will always be a second character (\0 again).
+                if (i == argc - 1 ||
+                    (argv[i + 1][0] == '-' && !isdigit(argv[i + 1][1]))) {
+                    printf("The option %s requires one argument.\n", argv[i]);
+                    return -1;
+                }
+                *save_value = (int)strtol(argv[++i], NULL, 0);
+            }
+	}
+    }
+    return 0;
+}
+/* Performs encryption on garbage values. In Cortex-A57 r0p1 and later
+ * revisions, pairs of dependent AESE/AESMC and AESD/AESIMC instructions are
+ * higher performance when adjacent, and in the described order below. */
+void garbage_encrypt() {
+    __asm__ __volatile__(
+	"aese  v0.16b, v4.16b ;"
+        "aesmc	v0.16b, v0.16b ;"
+        "aese  v1.16b, v4.16b ;"
+        "aesmc	v1.16b, v1.16b ;"
+        "aese  v2.16b, v4.16b ;"
+        "aesmc	v2.16b, v2.16b ;"
+        "aese  v0.16b, v5.16b ;"
+        "aesmc	v0.16b, v0.16b ;"
+        "aese  v1.16b, v5.16b ;"
+        "aesmc	v1.16b, v1.16b ;"
+        "aese  v2.16b, v5.16b ;"
+        "aesmc	v2.16b, v2.16b ;"
+        "aese  v0.16b, v6.16b ;"
+        "aesmc	v0.16b, v0.16b ;"
+        "aese  v1.16b, v6.16b ;"
+        "aesmc	v1.16b, v1.16b ;"
+        "aese  v2.16b, v6.16b ;"
+        "aesmc	v2.16b, v2.16b ;");
+}
+
+void garbage_decrypt() {
+    __asm__ __volatile__(
+	"aesd  v0.16b, v4.16b ;"
+        "aesimc	v0.16b, v0.16b ;"
+        "aesd  v1.16b, v4.16b ;"
+        "aesimc	v1.16b, v1.16b ;"
+        "aesd  v2.16b, v4.16b ;"
+        "aesimc	v2.16b, v2.16b ;"
+        "aesd  v0.16b, v5.16b ;"
+        "aesimc	v0.16b, v0.16b ;"
+        "aesd  v1.16b, v5.16b ;"
+        "aesimc	v1.16b, v1.16b ;"
+        "aesd  v2.16b, v5.16b ;"
+        "aesimc	v2.16b, v2.16b ;"
+        "aesd  v0.16b, v6.16b ;"
+        "aesimc	v0.16b, v0.16b ;"
+        "aesd  v1.16b, v6.16b ;"
+        "aesimc	v1.16b, v1.16b ;"
+        "aesd  v2.16b, v6.16b ;"
+        "aesimc	v2.16b, v2.16b ;");
+}
+
+
+int main(int argc, char **argv) {
+    usage();
+    command_data_t cmd_data;
+
+    if(processOptions(argc, argv, &cmd_data) == -1) {
+        usage();
+        return -1;
+    }
+    unsigned long long count = 0;
+    struct timeval begin_time, end_time, elapsed_time;
+    cpu_set_t cpuset;
+    CPU_ZERO(&cpuset);
+    CPU_SET(cmd_data.cpu_to_lock, &cpuset);
+    if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0) {
+	perror("sched_setaffinity failed");
+	return false;
+    }
+    gettimeofday(&begin_time, NULL);
+    while (count < MAX_COUNT) {
+      garbage_encrypt();
+      count++;
+    }
+    gettimeofday(&end_time, NULL);
+    timersub(&end_time, &begin_time, &elapsed_time);
+    fprintf(stderr, "encrypt: %llu us\n",
+            elapsed_time.tv_sec * USEC_PER_SEC + elapsed_time.tv_usec);
+    fprintf(stderr, "encrypt instructions: %llu\n",
+            MAX_COUNT * NUM_INSTS_GARBAGE);
+    fprintf(stderr, "encrypt instructions per second: %f\n",
+            (float)(MAX_COUNT * NUM_INSTS_GARBAGE * USEC_PER_SEC) /
+                (elapsed_time.tv_sec * USEC_PER_SEC + elapsed_time.tv_usec));
+    if (cmd_data.locked_freq != 0) {
+	fprintf(stderr, "encrypt instructions per cycle: %f\n",
+		(float)(MAX_COUNT * NUM_INSTS_GARBAGE * USEC_PER_SEC) /
+		((elapsed_time.tv_sec * USEC_PER_SEC + elapsed_time.tv_usec) *
+		 1000 * cmd_data.locked_freq));
+    }
+    printf("--------------------------------------------------------------------------------\n");
+
+    count = 0;
+    gettimeofday(&begin_time, NULL);
+    while (count < MAX_COUNT) {
+      garbage_decrypt();
+      count++;
+    }
+    gettimeofday(&end_time, NULL);
+    timersub(&end_time, &begin_time, &elapsed_time);
+    fprintf(stderr, "decrypt: %llu us\n",
+            elapsed_time.tv_sec * USEC_PER_SEC + elapsed_time.tv_usec);
+    fprintf(stderr, "decrypt instructions: %llu\n",
+            MAX_COUNT * NUM_INSTS_GARBAGE);
+    fprintf(stderr, "decrypt instructions per second: %f\n",
+            (float)(MAX_COUNT * NUM_INSTS_GARBAGE * USEC_PER_SEC) /
+                (elapsed_time.tv_sec * USEC_PER_SEC + elapsed_time.tv_usec));
+    if (cmd_data.locked_freq != 0) {
+	fprintf(stderr, "decrypt instructions per cycle: %f\n",
+		(float)(MAX_COUNT * NUM_INSTS_GARBAGE * USEC_PER_SEC) /
+		((elapsed_time.tv_sec * USEC_PER_SEC + elapsed_time.tv_usec) *
+		 1000 * cmd_data.locked_freq));
+    }
+    return 0;
+}
diff --git a/ext4_utils/Android.mk b/ext4_utils/Android.mk
index ddad863..a8362b2 100644
--- a/ext4_utils/Android.mk
+++ b/ext4_utils/Android.mk
@@ -48,6 +48,13 @@
 LOCAL_CFLAGS_linux := -DHOST
 include $(BUILD_HOST_EXECUTABLE)
 
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := blk_alloc_to_base_fs.c
+LOCAL_MODULE := blk_alloc_to_base_fs
+LOCAL_SHARED_LIBRARIES += libcutils
+LOCAL_CFLAGS_darwin := -DHOST
+LOCAL_CFLAGS_linux := -DHOST
+include $(BUILD_HOST_EXECUTABLE)
 
 #
 # -- All host/targets excluding windows
@@ -55,8 +62,7 @@
 
 libext4_utils_src_files += \
     key_control.cpp \
-    ext4_crypt.cpp \
-    unencrypted_properties.cpp
+    ext4_crypt.cpp
 
 ifneq ($(HOST_OS),windows)
 
@@ -67,6 +73,7 @@
 # Various instances of dereferencing a type-punned pointer in extent.c
 LOCAL_CFLAGS += -fno-strict-aliasing
 LOCAL_SHARED_LIBRARIES := \
+    libbase \
     libcutils \
     libext2_uuid \
     libselinux \
@@ -83,8 +90,11 @@
 # Various instances of dereferencing a type-punned pointer in extent.c
 LOCAL_CFLAGS += -fno-strict-aliasing
 LOCAL_STATIC_LIBRARIES := \
+    libbase \
+    liblogwrap \
     libsparse_static \
-    libselinux
+    libselinux \
+    libbase
 include $(BUILD_STATIC_LIBRARY)
 
 
diff --git a/ext4_utils/allocate.c b/ext4_utils/allocate.c
index cca3dc1..497f580 100644
--- a/ext4_utils/allocate.c
+++ b/ext4_utils/allocate.c
@@ -5,7 +5,7 @@
  * 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
+ *	  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,
@@ -22,31 +22,6 @@
 #include <stdio.h>
 #include <stdlib.h>
 
-struct region {
-	u32 block;
-	u32 len;
-	int bg;
-	struct region *next;
-	struct region *prev;
-};
-
-struct block_group_info {
-	u32 first_block;
-	int header_blocks;
-	int data_blocks_used;
-	int has_superblock;
-	u8 *bitmaps;
-	u8 *block_bitmap;
-	u8 *inode_bitmap;
-	u8 *inode_table;
-	u32 free_blocks;
-	u32 first_free_block;
-	u32 free_inodes;
-	u32 first_free_inode;
-	u16 flags;
-	u16 used_dirs;
-};
-
 struct xattr_list_element {
 	struct ext4_inode *inode;
 	struct ext4_xattr_header *header;
@@ -106,7 +81,7 @@
 	reg->prev = NULL;
 }
 
-static void region_list_append(struct region_list *list, struct region *reg)
+void region_list_append(struct region_list *list, struct region *reg)
 {
 	if (list->first == NULL) {
 		list->first = reg;
@@ -122,6 +97,20 @@
 	reg->next = NULL;
 }
 
+void region_list_merge(struct region_list *list1, struct region_list *list2)
+{
+	if (list1->first == NULL) {
+		list1->first = list2->first;
+		list1->last = list2->last;
+		list1->iter = list2->first;
+		list1->partial_iter = 0;
+		list1->first->prev = NULL;
+	} else {
+		list1->last->next = list2->first;
+		list2->first->prev = list1->last;
+		list1->last = list2->last;
+	}
+}
 #if 0
 static void dump_starting_from(struct region *reg)
 {
@@ -141,15 +130,17 @@
 }
 #endif
 
-void print_blocks(FILE* f, struct block_allocation *alloc)
+void print_blocks(FILE* f, struct block_allocation *alloc, char separator)
 {
 	struct region *reg;
+	fputc(' ', f);
 	for (reg = alloc->list.first; reg; reg = reg->next) {
 		if (reg->len == 1) {
-			fprintf(f, " %d", reg->block);
+			fprintf(f, "%d", reg->block);
 		} else {
-			fprintf(f, " %d-%d", reg->block, reg->block + reg->len - 1);
+			fprintf(f, "%d-%d", reg->block, reg->block + reg->len - 1);
 		}
+		fputc(separator, f);
 	}
 	fputc('\n', f);
 }
@@ -205,50 +196,43 @@
 
 /* Marks a the first num_blocks blocks in a block group as used, and accounts
  for them in the block group free block info. */
-static int reserve_blocks(struct block_group_info *bg, u32 start, u32 num)
+static int reserve_blocks(struct block_group_info *bg, u32 bg_num, u32 start, u32 num)
 {
 	unsigned int i = 0;
 
 	u32 block = start;
-	if (num > bg->free_blocks)
-		return -1;
-
 	for (i = 0; i < num && block % 8 != 0; i++, block++) {
 		if (bitmap_set_bit(bg->block_bitmap, block)) {
-			error("attempted to reserve already reserved block");
+			error("attempted to reserve already reserved block %d in block group %d", block, bg_num);
 			return -1;
 		}
 	}
 
 	for (; i + 8 <= (num & ~7); i += 8, block += 8) {
 		if (bitmap_set_8_bits(bg->block_bitmap, block)) {
-			error("attempted to reserve already reserved block");
+			error("attempted to reserve already reserved block %d in block group %d", block, bg_num);
 			return -1;
 		}
 	}
 
 	for (; i < num; i++, block++) {
 		if (bitmap_set_bit(bg->block_bitmap, block)) {
-			error("attempted to reserve already reserved block");
+			error("attempted to reserve already reserved block %d in block group %d", block, bg_num);
 			return -1;
 		}
 	}
 
 	bg->free_blocks -= num;
-	if (start == bg->first_free_block)
-		bg->first_free_block = start + num;
 
 	return 0;
 }
 
-static void free_blocks(struct block_group_info *bg, u32 num_blocks)
+static void free_blocks(struct block_group_info *bg, u32 block, u32 num_blocks)
 {
 	unsigned int i;
-	u32 block = bg->first_free_block - 1;
 	for (i = 0; i < num_blocks; i++, block--)
 		bg->block_bitmap[block / 8] &= ~(1 << (block % 8));
 	bg->free_blocks += num_blocks;
-	bg->first_free_block -= num_blocks;
 }
 
 /* Reduces an existing allocation by len blocks by return the last blocks
@@ -258,14 +242,15 @@
 {
 	while (len) {
 		struct region *last_reg = alloc->list.last;
+		struct block_group_info *bg = &aux_info.bgs[last_reg->bg];
 
 		if (last_reg->len > len) {
-			free_blocks(&aux_info.bgs[last_reg->bg], len);
+			free_blocks(bg, last_reg->block + last_reg->len - bg->first_block - 1, len);
 			last_reg->len -= len;
 			len = 0;
 		} else {
 			struct region *reg = alloc->list.last->prev;
-			free_blocks(&aux_info.bgs[last_reg->bg], last_reg->len);
+			free_blocks(bg, last_reg->block + last_reg->len - bg->first_block - 1, last_reg->len);
 			len -= last_reg->len;
 			if (reg) {
 				reg->next = NULL;
@@ -304,18 +289,28 @@
 
 	bg->data_blocks_used = 0;
 	bg->free_blocks = info.blocks_per_group;
-	bg->first_free_block = 0;
 	bg->free_inodes = info.inodes_per_group;
 	bg->first_free_inode = 1;
 	bg->flags = EXT4_BG_INODE_UNINIT;
 
-	if (reserve_blocks(bg, bg->first_free_block, bg->header_blocks) < 0)
+	bg->chunk_count = 0;
+	bg->max_chunk_count = 1;
+	bg->chunks = (struct region*) calloc(bg->max_chunk_count, sizeof(struct region));
+
+	if (reserve_blocks(bg, i, 0, bg->header_blocks) < 0)
 		error("failed to reserve %u blocks in block group %u\n", bg->header_blocks, i);
+	// Add empty starting delimiter chunk
+	reserve_bg_chunk(i, bg->header_blocks, 0);
 
 	if (bg->first_block + info.blocks_per_group > aux_info.len_blocks) {
 		u32 overrun = bg->first_block + info.blocks_per_group - aux_info.len_blocks;
-		reserve_blocks(bg, info.blocks_per_group - overrun, overrun);
+		reserve_blocks(bg, i, info.blocks_per_group - overrun, overrun);
+		// Add empty ending delimiter chunk
+		reserve_bg_chunk(i, info.blocks_per_group - overrun, 0);
+	} else {
+		reserve_bg_chunk(i, info.blocks_per_group - 1, 0);
 	}
+
 }
 
 void block_allocator_init()
@@ -341,73 +336,80 @@
 	free(aux_info.bgs);
 }
 
-static u32 ext4_allocate_blocks_from_block_group(u32 len, int bg_num)
-{
-	if (get_free_blocks(bg_num) < len)
-		return EXT4_ALLOCATE_FAILED;
-
-	u32 block = aux_info.bgs[bg_num].first_free_block;
-	struct block_group_info *bg = &aux_info.bgs[bg_num];
-	if (reserve_blocks(bg, bg->first_free_block, len) < 0) {
-		error("failed to reserve %u blocks in block group %u\n", len, bg_num);
-		return EXT4_ALLOCATE_FAILED;
-	}
-
-	aux_info.bgs[bg_num].data_blocks_used += len;
-
-	return bg->first_block + block;
-}
-
 /* Allocate a single block and return its block number */
 u32 allocate_block()
 {
-	unsigned int i;
-	for (i = 0; i < aux_info.groups; i++) {
-		u32 block = ext4_allocate_blocks_from_block_group(1, i);
-
-		if (block != EXT4_ALLOCATE_FAILED)
-			return block;
+	u32 block;
+	struct block_allocation *blk_alloc = allocate_blocks(1);
+	if (!blk_alloc) {
+		return EXT4_ALLOCATE_FAILED;
 	}
-
-	return EXT4_ALLOCATE_FAILED;
+	block = blk_alloc->list.first->block;
+	free_alloc(blk_alloc);
+	return block;
 }
 
 static struct region *ext4_allocate_best_fit_partial(u32 len)
 {
-	unsigned int i;
-	unsigned int found_bg = 0;
-	u32 found_bg_len = 0;
+	unsigned int i, j;
+	unsigned int found_bg = 0, found_prev_chunk = 0, found_block = 0;
+	u32 found_allocate_len = 0;
+	bool minimize = false;
+	struct block_group_info *bgs = aux_info.bgs;
+	struct region *reg;
 
 	for (i = 0; i < aux_info.groups; i++) {
-		u32 bg_len = aux_info.bgs[i].free_blocks;
-
-		if ((len <= bg_len && (found_bg_len == 0 || bg_len < found_bg_len)) ||
-		    (len > found_bg_len && bg_len > found_bg_len)) {
-			found_bg = i;
-			found_bg_len = bg_len;
+		for (j = 1; j < bgs[i].chunk_count; j++) {
+			u32 hole_start, hole_size;
+			hole_start = bgs[i].chunks[j-1].block + bgs[i].chunks[j-1].len;
+			hole_size =  bgs[i].chunks[j].block - hole_start;
+			if (hole_size == len) {
+				// Perfect fit i.e. right between 2 chunks no need to keep searching
+				found_bg = i;
+				found_prev_chunk = j - 1;
+				found_block = hole_start;
+				found_allocate_len = hole_size;
+				goto done;
+			} else if (hole_size > len && (found_allocate_len == 0 || (found_allocate_len > hole_size))) {
+				found_bg = i;
+				found_prev_chunk = j - 1;
+				found_block = hole_start;
+				found_allocate_len = hole_size;
+				minimize = true;
+			} else if (!minimize) {
+				if (found_allocate_len < hole_size) {
+					found_bg = i;
+					found_prev_chunk = j - 1;
+					found_block = hole_start;
+					found_allocate_len = hole_size;
+				}
+			}
 		}
 	}
 
-	if (found_bg_len) {
-		u32 allocate_len = min(len, found_bg_len);
-		struct region *reg;
-		u32 block = ext4_allocate_blocks_from_block_group(allocate_len, found_bg);
-		if (block == EXT4_ALLOCATE_FAILED) {
-			error("failed to allocate %d blocks in block group %d", allocate_len, found_bg);
-			return NULL;
-		}
-		reg = malloc(sizeof(struct region));
-		reg->block = block;
-		reg->len = allocate_len;
-		reg->next = NULL;
-		reg->prev = NULL;
-		reg->bg = found_bg;
-		return reg;
-	} else {
+	if (found_allocate_len == 0) {
 		error("failed to allocate %u blocks, out of space?", len);
+		return NULL;
 	}
-
-	return NULL;
+	if (found_allocate_len > len) found_allocate_len = len;
+done:
+	// reclaim allocated space in chunk
+	bgs[found_bg].chunks[found_prev_chunk].len += found_allocate_len;
+	if (reserve_blocks(&bgs[found_bg],
+				found_bg,
+				found_block,
+				found_allocate_len) < 0) {
+		error("failed to reserve %u blocks in block group %u\n", found_allocate_len, found_bg);
+		return NULL;
+	}
+	bgs[found_bg].data_blocks_used += found_allocate_len;
+	reg = malloc(sizeof(struct region));
+	reg->block = found_block + bgs[found_bg].first_block;
+	reg->len = found_allocate_len;
+	reg->next = NULL;
+	reg->prev = NULL;
+	reg->bg = found_bg;
+	return reg;
 }
 
 static struct region *ext4_allocate_best_fit(u32 len)
@@ -439,9 +441,9 @@
 /* Allocate len blocks.  The blocks may be spread across multiple block groups,
    and are returned in a linked list of the blocks in each block group.  The
    allocation algorithm is:
-      1.  If the remaining allocation is larger than any available contiguous region,
-          allocate the largest contiguous region and loop
-      2.  Otherwise, allocate the smallest contiguous region that it fits in
+	  1.  If the remaining allocation is larger than any available contiguous region,
+		  allocate the largest contiguous region and loop
+	  2.  Otherwise, allocate the smallest contiguous region that it fits in
 */
 struct block_allocation *allocate_blocks(u32 len)
 {
@@ -452,6 +454,8 @@
 
 	struct block_allocation *alloc = create_allocation();
 	alloc->list.first = reg;
+	while (reg->next != NULL)
+		reg = reg->next;
 	alloc->list.last = reg;
 	alloc->list.iter = alloc->list.first;
 	alloc->list.partial_iter = 0;
@@ -779,3 +783,35 @@
 
 	free(alloc);
 }
+
+void reserve_bg_chunk(int bg, u32 start_block, u32 size) {
+	struct block_group_info *bgs = aux_info.bgs;
+	int chunk_count;
+	if (bgs[bg].chunk_count == bgs[bg].max_chunk_count) {
+		bgs[bg].max_chunk_count *= 2;
+		bgs[bg].chunks = realloc(bgs[bg].chunks, bgs[bg].max_chunk_count * sizeof(struct region));
+		if (!bgs[bg].chunks)
+			critical_error("realloc failed");
+	}
+	chunk_count = bgs[bg].chunk_count;
+	bgs[bg].chunks[chunk_count].block = start_block;
+	bgs[bg].chunks[chunk_count].len = size;
+	bgs[bg].chunks[chunk_count].bg = bg;
+	bgs[bg].chunk_count++;
+}
+
+int reserve_blocks_for_allocation(struct block_allocation *alloc) {
+	struct region *reg;
+	struct block_group_info *bgs = aux_info.bgs;
+
+	if (!alloc) return 0;
+	reg = alloc->list.first;
+	while (reg != NULL) {
+		if (reserve_blocks(&bgs[reg->bg], reg->bg, reg->block - bgs[reg->bg].first_block, reg->len) < 0) {
+			return -1;
+		}
+		reg = reg->next;
+	}
+	return 0;
+}
+
diff --git a/ext4_utils/allocate.h b/ext4_utils/allocate.h
index 5c26792..4a733d0 100644
--- a/ext4_utils/allocate.h
+++ b/ext4_utils/allocate.h
@@ -5,7 +5,7 @@
  * 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
+ *	  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,
@@ -21,7 +21,13 @@
 
 #include "ext4_utils.h"
 
-struct region;
+struct region {
+	u32 block;
+	u32 len;
+	int bg;
+	struct region *next;
+	struct region *prev;
+};
 
 struct region_list {
 	struct region *first;
@@ -37,6 +43,24 @@
 	struct block_allocation* next;
 };
 
+struct block_group_info {
+	u32 first_block;
+	int header_blocks;
+	int data_blocks_used;
+	int has_superblock;
+	u8 *bitmaps;
+	u8 *block_bitmap;
+	u8 *inode_bitmap;
+	u8 *inode_table;
+	u32 free_blocks;
+	u32 free_inodes;
+	u32 first_free_inode;
+	u16 flags;
+	u16 used_dirs;
+	int chunk_count;
+	int max_chunk_count;
+	struct region *chunks;
+};
 
 void block_allocator_init();
 void block_allocator_free();
@@ -69,6 +93,9 @@
 	u32 block, u32 len, int bg);
 struct block_allocation *create_allocation();
 int append_oob_allocation(struct block_allocation *alloc, u32 len);
-void print_blocks(FILE* f, struct block_allocation *alloc);
-
+void region_list_append(struct region_list *list, struct region *reg);
+void region_list_merge(struct region_list *list1, struct region_list *list2);
+void print_blocks(FILE* f, struct block_allocation *alloc, char separator);
+void reserve_bg_chunk(int bg, u32 start_block, u32 size);
+int reserve_blocks_for_allocation(struct block_allocation *alloc);
 #endif
diff --git a/ext4_utils/blk_alloc_to_base_fs.c b/ext4_utils/blk_alloc_to_base_fs.c
new file mode 100644
index 0000000..1761fda
--- /dev/null
+++ b/ext4_utils/blk_alloc_to_base_fs.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2016 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 <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define MAX_PATH 4096
+#define MAX_FILE_VERSION 100
+
+static void usage(char *filename)
+{
+    fprintf(stderr, "Usage: %s input_blk_alloc_file output_base_fs_file \n", filename);
+}
+
+int main(int argc, char **argv)
+{
+    FILE *blk_alloc_file = NULL, *base_fs_file = NULL;
+    char filename[MAX_PATH], file_version[MAX_FILE_VERSION], *spaced_allocs = NULL;
+    size_t spaced_allocs_len = 0;
+
+    if (argc != 3) {
+        usage(argv[0]);
+        exit(EXIT_FAILURE);
+    }
+    blk_alloc_file = fopen(argv[1], "r");
+    if (blk_alloc_file == NULL) {
+        fprintf(stderr, "failed to open %s: %s\n", argv[1], strerror(errno));
+        exit(EXIT_FAILURE);
+    }
+    base_fs_file = fopen(argv[2], "w");
+    if (base_fs_file == NULL) {
+        fprintf(stderr, "failed to open %s: %s\n", argv[2], strerror(errno));
+        exit(EXIT_FAILURE);
+    }
+    if (fscanf(blk_alloc_file, "Base EXT4 version %s", file_version) > 0) {
+        char c;
+        printf("%s is already in *.base_fs format, just copying into %s...\n", argv[1], argv[2]);
+        rewind(blk_alloc_file);
+        while ((c = fgetc(blk_alloc_file)) != EOF) {
+            fputc(c, base_fs_file);
+        }
+        return 0;
+    } else {
+        printf("Converting %s into *.base_fs format as %s...\n", argv[1], argv[2]);
+        rewind(blk_alloc_file);
+    }
+    fprintf(base_fs_file, "Base EXT4 version 1.0\n");
+    while(fscanf(blk_alloc_file, "%s ", filename) != EOF) {
+        int i;
+        fprintf(base_fs_file, "%s ", filename);
+        if (getline(&spaced_allocs, &spaced_allocs_len, blk_alloc_file) == -1) {
+            fprintf(stderr, "Bad blk_alloc format\n");
+            exit(EXIT_FAILURE);
+        }
+        for (i = 0; spaced_allocs[i]; i++) {
+            if (spaced_allocs[i] == ' ') {
+                if (!isspace(spaced_allocs[i + 1])) fputc(',', base_fs_file);
+            } else fputc(spaced_allocs[i], base_fs_file);
+        }
+    }
+    free(spaced_allocs);
+    fclose(blk_alloc_file);
+    fclose(base_fs_file);
+    return 0;
+}
diff --git a/ext4_utils/ext4_crypt.cpp b/ext4_utils/ext4_crypt.cpp
index 886d17a..be77b79 100644
--- a/ext4_utils/ext4_crypt.cpp
+++ b/ext4_utils/ext4_crypt.cpp
@@ -1,10 +1,20 @@
 /*
- * Copyright (c) 2015 Google, Inc.
+ * Copyright (C) 2015 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 TAG "ext4_utils"
-
-#include "ext4_crypt_init_extensions.h"
+#include "ext4_crypt.h"
 
 #include <dirent.h>
 #include <errno.h>
@@ -17,9 +27,8 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 
-#include <cutils/klog.h>
-
-#include "unencrypted_properties.h"
+#include <android-base/logging.h>
+#include <cutils/properties.h>
 
 #define XATTR_NAME_ENCRYPTION_POLICY "encryption.policy"
 #define EXT4_KEYREF_DELIMITER ((char)'.')
@@ -27,6 +36,8 @@
 // ext4enc:TODO Include structure from somewhere sensible
 // MUST be in sync with ext4_crypto.c in kernel
 #define EXT4_KEY_DESCRIPTOR_SIZE 8
+#define EXT4_KEY_DESCRIPTOR_SIZE_HEX 17
+
 struct ext4_encryption_policy {
     char version;
     char contents_encryption_mode;
@@ -39,68 +50,64 @@
 #define EXT4_ENCRYPTION_MODE_AES_256_CTS    4
 
 // ext4enc:TODO Get value from somewhere sensible
-#define EXT4_IOC_SET_ENCRYPTION_POLICY \
-    _IOR('f', 19, struct ext4_encryption_policy)
+#define EXT4_IOC_SET_ENCRYPTION_POLICY _IOR('f', 19, struct ext4_encryption_policy)
+#define EXT4_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct ext4_encryption_policy)
 
-/* Validate that all path items are available and accessible. */
-static int is_path_valid(const char *path)
-{
-    if (access(path, W_OK)) {
-        KLOG_ERROR(TAG, "Can't access %s: %s\n",strerror(errno), path);
-        return 0;
-    }
+#define HEX_LOOKUP "0123456789abcdef"
 
-    return 1;
+bool e4crypt_is_native() {
+    char value[PROPERTY_VALUE_MAX];
+    property_get("ro.crypto.type", value, "none");
+    return !strcmp(value, "file");
 }
 
-static int is_dir_empty(const char *dirname)
+static void policy_to_hex(const char* policy, char* hex) {
+    for (size_t i = 0, j = 0; i < EXT4_KEY_DESCRIPTOR_SIZE; i++) {
+        hex[j++] = HEX_LOOKUP[(policy[i] & 0xF0) >> 4];
+        hex[j++] = HEX_LOOKUP[policy[i] & 0x0F];
+    }
+    hex[EXT4_KEY_DESCRIPTOR_SIZE_HEX - 1] = '\0';
+}
+
+static bool is_dir_empty(const char *dirname, bool *is_empty)
 {
     int n = 0;
-    struct dirent *d;
-    DIR *dir;
-
-    dir = opendir(dirname);
-    while ((d = readdir(dir)) != NULL) {
-        if (strcmp(d->d_name, "lost+found") == 0) {
-            // Skip lost+found directory
-        } else if (++n > 2) {
+    auto dirp = std::unique_ptr<DIR, int (*)(DIR*)>(opendir(dirname), closedir);
+    if (!dirp) {
+        PLOG(ERROR) << "Unable to read directory: " << dirname;
+        return false;
+    }
+    for (;;) {
+        errno = 0;
+        auto entry = readdir(dirp.get());
+        if (!entry) {
+            if (errno) {
+                PLOG(ERROR) << "Unable to read directory: " << dirname;
+                return false;
+            }
             break;
         }
+        if (strcmp(entry->d_name, "lost+found") != 0) { // Skip lost+found
+            ++n;
+            if (n > 2) {
+                *is_empty = false;
+                return true;
+            }
+        }
     }
-    closedir(dir);
-    return n <= 2;
+    *is_empty = true;
+    return true;
 }
 
-int do_policy_set(const char *directory, const char *policy, int policy_length)
-{
-    struct stat st;
-    ssize_t ret;
-
+static bool e4crypt_policy_set(const char *directory, const char *policy, size_t policy_length) {
     if (policy_length != EXT4_KEY_DESCRIPTOR_SIZE) {
-        KLOG_ERROR("Policy wrong length\n");
-        return -EINVAL;
+        LOG(ERROR) << "Policy wrong length: " << policy_length;
+        return false;
     }
-
-    if (!is_path_valid(directory)) {
-        return -EINVAL;
-    }
-
-    stat(directory, &st);
-    if (!S_ISDIR(st.st_mode)) {
-        KLOG_ERROR(TAG, "Can only set policy on a directory (%s)\n", directory);
-        return -EINVAL;
-    }
-
-    if (!is_dir_empty(directory)) {
-        KLOG_ERROR(TAG, "Can only set policy on an empty directory (%s)\n",
-                   directory);
-        return -EINVAL;
-    }
-
-    int fd = open(directory, O_DIRECTORY);
+    int fd = open(directory, O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
     if (fd == -1) {
-        KLOG_ERROR(TAG, "Failed to open directory (%s)\n", directory);
-        return -EINVAL;
+        PLOG(ERROR) << "Failed to open directory " << directory;
+        return false;
     }
 
     ext4_encryption_policy eep;
@@ -109,23 +116,82 @@
     eep.filenames_encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_CTS;
     eep.flags = 0;
     memcpy(eep.master_key_descriptor, policy, EXT4_KEY_DESCRIPTOR_SIZE);
-    ret = ioctl(fd, EXT4_IOC_SET_ENCRYPTION_POLICY, &eep);
-    auto preserve_errno = errno;
+    if (ioctl(fd, EXT4_IOC_SET_ENCRYPTION_POLICY, &eep)) {
+        PLOG(ERROR) << "Failed to set encryption policy for " << directory;
+        close(fd);
+        return false;
+    }
     close(fd);
 
-    if (ret) {
-        KLOG_ERROR(TAG, "Failed to set encryption policy for %s: %s\n",
-                   directory, strerror(preserve_errno));
-        return -EINVAL;
-    }
-
-    KLOG_INFO(TAG, "Encryption policy for %s is set to %02x%02x%02x%02x\n",
-              directory, policy[0], policy[1], policy[2], policy[3]);
-    return 0;
+    char policy_hex[EXT4_KEY_DESCRIPTOR_SIZE_HEX];
+    policy_to_hex(policy, policy_hex);
+    LOG(INFO) << "Policy for " << directory << " set to " << policy_hex;
+    return true;
 }
 
-bool e4crypt_non_default_key(const char* dir)
-{
-    UnencryptedProperties props(dir);
-    return props.Get<int>(properties::is_default, 1) != 1;
+static bool e4crypt_policy_get(const char *directory, char *policy, size_t policy_length) {
+    if (policy_length != EXT4_KEY_DESCRIPTOR_SIZE) {
+        LOG(ERROR) << "Policy wrong length: " << policy_length;
+        return false;
+    }
+
+    int fd = open(directory, O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
+    if (fd == -1) {
+        PLOG(ERROR) << "Failed to open directory " << directory;
+        return false;
+    }
+
+    ext4_encryption_policy eep;
+    memset(&eep, 0, sizeof(ext4_encryption_policy));
+    if (ioctl(fd, EXT4_IOC_GET_ENCRYPTION_POLICY, &eep) != 0) {
+        PLOG(ERROR) << "Failed to get encryption policy for " << directory;
+        close(fd);
+        return -1;
+    }
+    close(fd);
+
+    if ((eep.version != 0)
+            || (eep.contents_encryption_mode != EXT4_ENCRYPTION_MODE_AES_256_XTS)
+            || (eep.filenames_encryption_mode != EXT4_ENCRYPTION_MODE_AES_256_CTS)
+            || (eep.flags != 0)) {
+        LOG(ERROR) << "Failed to find matching encryption policy for " << directory;
+        return false;
+    }
+    memcpy(policy, eep.master_key_descriptor, EXT4_KEY_DESCRIPTOR_SIZE);
+
+    return true;
+}
+
+static bool e4crypt_policy_check(const char *directory, const char *policy, size_t policy_length) {
+    if (policy_length != EXT4_KEY_DESCRIPTOR_SIZE) {
+        LOG(ERROR) << "Policy wrong length: " << policy_length;
+        return false;
+    }
+    char existing_policy[EXT4_KEY_DESCRIPTOR_SIZE];
+    if (!e4crypt_policy_get(directory, existing_policy, EXT4_KEY_DESCRIPTOR_SIZE)) return false;
+    char existing_policy_hex[EXT4_KEY_DESCRIPTOR_SIZE_HEX];
+
+    policy_to_hex(existing_policy, existing_policy_hex);
+
+    if (memcmp(policy, existing_policy, EXT4_KEY_DESCRIPTOR_SIZE) != 0) {
+        char policy_hex[EXT4_KEY_DESCRIPTOR_SIZE_HEX];
+        policy_to_hex(policy, policy_hex);
+        LOG(ERROR) << "Found policy " << existing_policy_hex << " at " << directory
+                   << " which doesn't match expected value " << policy_hex;
+        return false;
+    }
+    LOG(INFO) << "Found policy " << existing_policy_hex << " at " << directory
+              << " which matches expected value";
+    return true;
+}
+
+int e4crypt_policy_ensure(const char *directory, const char *policy, size_t policy_length) {
+    bool is_empty;
+    if (!is_dir_empty(directory, &is_empty)) return -1;
+    if (is_empty) {
+        if (!e4crypt_policy_set(directory, policy, policy_length)) return -1;
+    } else {
+        if (!e4crypt_policy_check(directory, policy, policy_length)) return -1;
+    }
+    return 0;
 }
diff --git a/ext4_utils/ext4_crypt.h b/ext4_utils/ext4_crypt.h
new file mode 100644
index 0000000..ddc09a7
--- /dev/null
+++ b/ext4_utils/ext4_crypt.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 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/cdefs.h>
+#include <stdbool.h>
+#include <cutils/multiuser.h>
+
+__BEGIN_DECLS
+
+bool e4crypt_is_native();
+
+int e4crypt_policy_ensure(const char *directory, const char* policy, size_t policy_length);
+
+static const char* e4crypt_unencrypted_folder = "/unencrypted";
+static const char* e4crypt_key_ref = "/unencrypted/ref";
+
+__END_DECLS
diff --git a/ext4_utils/ext4_crypt_init_extensions.cpp b/ext4_utils/ext4_crypt_init_extensions.cpp
index b8ba51d..c6baea7 100644
--- a/ext4_utils/ext4_crypt_init_extensions.cpp
+++ b/ext4_utils/ext4_crypt_init_extensions.cpp
@@ -1,8 +1,28 @@
+/*
+ * Copyright (C) 2016 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 TAG "ext4_utils"
 
 #include "ext4_crypt_init_extensions.h"
+#include "ext4_crypt.h"
+
+#include <android-base/logging.h>
 
 #include <string>
+#include <vector>
 
 #include <dirent.h>
 #include <errno.h>
@@ -10,116 +30,57 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
+#include <android-base/file.h>
+
 #include <cutils/klog.h>
 #include <cutils/properties.h>
 #include <cutils/sockets.h>
-#include <poll.h>
+#include <logwrap/logwrap.h>
 
 #include "key_control.h"
-#include "unencrypted_properties.h"
 
 static const std::string arbitrary_sequence_number = "42";
 static const int vold_command_timeout_ms = 60 * 1000;
 
-static std::string vold_command(std::string const& command)
-{
-    KLOG_INFO(TAG, "Running command %s\n", command.c_str());
-    int sock = -1;
-
-    while (true) {
-        sock = socket_local_client("cryptd",
-                                   ANDROID_SOCKET_NAMESPACE_RESERVED,
-                                   SOCK_STREAM);
-        if (sock >= 0) {
-            break;
-        }
-        usleep(10000);
+static void kernel_logger(android::base::LogId, android::base::LogSeverity severity, const char*,
+        const char*, unsigned int, const char* message) {
+    if (severity == android::base::ERROR || severity == android::base::FATAL) {
+        KLOG_ERROR(TAG, "%s\n", message);
+    } else if (severity == android::base::WARNING) {
+        KLOG_WARNING(TAG, "%s\n", message);
+    } else {
+        KLOG_INFO(TAG, "%s\n", message);
     }
+}
 
-    if (sock < 0) {
-        KLOG_INFO(TAG, "Cannot open vold, failing command (%s)\n", strerror(errno));
-        return "";
-    }
-
-    class CloseSocket
-    {
-        int sock_;
-    public:
-        explicit CloseSocket(int sock) : sock_(sock) {}
-        ~CloseSocket() { close(sock_); }
-    };
-
-    CloseSocket cs(sock);
-
-    // Use arbitrary sequence number. This should only be used when the
-    // framework is down, so this is (mostly) OK.
-    std::string actual_command = arbitrary_sequence_number + " " + command;
-    if (write(sock, actual_command.c_str(), actual_command.size() + 1) < 0) {
-        KLOG_ERROR(TAG, "Cannot write command (%s)\n", strerror(errno));
-        return "";
-    }
-
-    struct pollfd poll_sock = {sock, POLLIN, 0};
-
-    int rc = TEMP_FAILURE_RETRY(poll(&poll_sock, 1, vold_command_timeout_ms));
-    if (rc < 0) {
-        KLOG_ERROR(TAG, "Error in poll (%s)\n", strerror(errno));
-        return "";
-    }
-
-    if (!(poll_sock.revents & POLLIN)) {
-        KLOG_ERROR(TAG, "Timeout\n");
-        return "";
-    }
-    char buffer[4096];
-    memset(buffer, 0, sizeof(buffer));
-    rc = TEMP_FAILURE_RETRY(read(sock, buffer, sizeof(buffer)));
-    if (rc <= 0) {
-        if (rc == 0) {
-            KLOG_ERROR(TAG, "Lost connection to Vold - did it crash?\n");
-        } else {
-            KLOG_ERROR(TAG, "Error reading data (%s)\n", strerror(errno));
-        }
-        return "";
-    }
-
-    // We don't truly know that this is the correct result. However,
-    // since this will only be used when the framework is down,
-    // it should be OK unless someone is running vdc at the same time.
-    // Worst case we force a reboot in the very rare synchronization
-    // error
-    return std::string(buffer, rc);
+static void init_logging() {
+    android::base::SetLogger(kernel_logger);
 }
 
 int e4crypt_create_device_key(const char* dir,
                               int ensure_dir_exists(const char*))
 {
-    // Already encrypted with password? If so bail
-    std::string temp_folder = std::string() + dir + "/tmp_mnt";
-    DIR* temp_dir = opendir(temp_folder.c_str());
-    if (temp_dir) {
-        closedir(temp_dir);
-        return 0;
-    }
+    init_logging();
 
     // Make sure folder exists. Use make_dir to set selinux permissions.
-    if (ensure_dir_exists(UnencryptedProperties::GetPath(dir).c_str())) {
+    std::string unencrypted_dir = std::string(dir) + e4crypt_unencrypted_folder;
+    if (ensure_dir_exists(unencrypted_dir.c_str())) {
         KLOG_ERROR(TAG, "Failed to create %s (%s)\n",
-                   UnencryptedProperties::GetPath(dir).c_str(),
+                   unencrypted_dir.c_str(),
                    strerror(errno));
         return -1;
     }
 
-    auto result = vold_command("cryptfs enablefilecrypto");
-    // ext4enc:TODO proper error handling
-    KLOG_INFO(TAG, "enablefilecrypto returned with result %s\n",
-              result.c_str());
-
-    return 0;
+    const char* argv[] = { "/system/bin/vdc", "--wait", "cryptfs", "enablefilecrypto" };
+    int rc = android_fork_execvp(4, (char**) argv, NULL, false, true);
+    LOG(INFO) << "enablefilecrypto result: " << rc;
+    return rc;
 }
 
 int e4crypt_install_keyring()
 {
+    init_logging();
+
     key_serial_t device_keyring = add_key("keyring", "e4crypt", 0, 0,
                                           KEY_SPEC_SESSION_KEYRING);
 
@@ -128,14 +89,26 @@
         return -1;
     }
 
-    KLOG_INFO(TAG, "Keyring created wth id %d in process %d\n",
+    KLOG_INFO(TAG, "Keyring created with id %d in process %d\n",
               device_keyring, getpid());
 
     return 0;
 }
 
+int e4crypt_do_init_user0()
+{
+    init_logging();
+
+    const char* argv[] = { "/system/bin/vdc", "--wait", "cryptfs", "init_user0" };
+    int rc = android_fork_execvp(4, (char**) argv, NULL, false, true);
+    LOG(INFO) << "init_user0 result: " << rc;
+    return rc;
+}
+
 int e4crypt_set_directory_policy(const char* dir)
 {
+    init_logging();
+
     // Only set policy on first level /data directories
     // To make this less restrictive, consider using a policy file.
     // However this is overkill for as long as the policy is simply
@@ -144,25 +117,32 @@
         return 0;
     }
 
-    // Don't encrypt lost+found - ext4 doesn't like it
-    if (!strcmp(dir, "/data/lost+found")) {
-        return 0;
+    // Special case various directories that must not be encrypted,
+    // often because their subdirectories must be encrypted.
+    // This isn't a nice way to do this, see b/26641735
+    std::vector<std::string> directories_to_exclude = {
+        "lost+found",
+        "system_ce", "system_de",
+        "misc_ce", "misc_de",
+        "media",
+        "data", "user", "user_de",
+    };
+    std::string prefix = "/data/";
+    for (auto d: directories_to_exclude) {
+        if ((prefix + d) == dir) {
+            KLOG_INFO(TAG, "Not setting policy on %s\n", dir);
+            return 0;
+        }
     }
 
-    // ext4enc:TODO exclude /data/user with a horrible special case.
-    if (!strcmp(dir, "/data/user")) {
-        return 0;
+    std::string ref_filename = std::string("/data") + e4crypt_key_ref;
+    std::string policy;
+    if (!android::base::ReadFileToString(ref_filename, &policy)) {
+        KLOG_ERROR(TAG, "Unable to read system policy to set on %s\n", dir);
+        return -1;
     }
-
-    UnencryptedProperties props("/data");
-    std::string policy = props.Get<std::string>(properties::ref);
-    if (policy.empty()) {
-        // ext4enc:TODO why is this OK?
-        return 0;
-    }
-
     KLOG_INFO(TAG, "Setting policy on %s\n", dir);
-    int result = do_policy_set(dir, policy.c_str(), policy.size());
+    int result = e4crypt_policy_ensure(dir, policy.c_str(), policy.size());
     if (result) {
         KLOG_ERROR(TAG, "Setting %02x%02x%02x%02x policy on %s failed!\n",
                    policy[0], policy[1], policy[2], policy[3], dir);
@@ -171,13 +151,3 @@
 
     return 0;
 }
-
-int e4crypt_set_user_crypto_policies(const char* dir)
-{
-    auto command = std::string() + "cryptfs setusercryptopolicies " + dir;
-    auto result = vold_command(command);
-    // ext4enc:TODO proper error handling
-    KLOG_INFO(TAG, "setusercryptopolicies returned with result %s\n",
-              result.c_str());
-    return 0;
-}
diff --git a/ext4_utils/ext4_crypt_init_extensions.h b/ext4_utils/ext4_crypt_init_extensions.h
index d02d181..63f6d88 100644
--- a/ext4_utils/ext4_crypt_init_extensions.h
+++ b/ext4_utils/ext4_crypt_init_extensions.h
@@ -1,5 +1,22 @@
+/*
+ * Copyright (C) 2016 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/cdefs.h>
 #include <stdbool.h>
+#include <cutils/multiuser.h>
 
 __BEGIN_DECLS
 
@@ -9,8 +26,6 @@
 int e4crypt_create_device_key(const char* path,
                               int ensure_dir_exists(const char* dir));
 int e4crypt_set_directory_policy(const char* path);
-bool e4crypt_non_default_key(const char* path);
-int do_policy_set(const char *directory, const char *policy, int policy_length);
-int e4crypt_set_user_crypto_policies(const char* path);
+int e4crypt_do_init_user0();
 
 __END_DECLS
diff --git a/ext4_utils/ext4_utils.c b/ext4_utils/ext4_utils.c
index 28f650d..fba4f9f 100644
--- a/ext4_utils/ext4_utils.c
+++ b/ext4_utils/ext4_utils.c
@@ -49,6 +49,7 @@
 struct fs_info info;
 struct fs_aux_info aux_info;
 struct sparse_file *ext4_sparse_file;
+struct block_allocation *base_fs_allocations = NULL;
 
 jmp_buf setjmp_env;
 
diff --git a/ext4_utils/ext4_utils.h b/ext4_utils/ext4_utils.h
index 0159dbe..0fbbdd3 100644
--- a/ext4_utils/ext4_utils.h
+++ b/ext4_utils/ext4_utils.h
@@ -119,6 +119,7 @@
 extern struct fs_info info;
 extern struct fs_aux_info aux_info;
 extern struct sparse_file *ext4_sparse_file;
+extern struct block_allocation *base_fs_allocations;
 
 extern jmp_buf setjmp_env;
 
@@ -161,7 +162,7 @@
 						 const char *mountpoint, fs_config_func_t fs_config_func, int gzip,
 						 int sparse, int crc, int wipe, int real_uuid,
 						 struct selabel_handle *sehnd, int verbose, time_t fixed_time,
-						 FILE* block_list_file);
+						 FILE* block_list_file, FILE* base_alloc_file_in, FILE* base_alloc_file_out);
 
 int read_ext(int fd, int verbose);
 
diff --git a/ext4_utils/extent.c b/ext4_utils/extent.c
index 1900b10..7887488 100644
--- a/ext4_utils/extent.c
+++ b/ext4_utils/extent.c
@@ -5,7 +5,7 @@
  * 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
+ *	  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,
@@ -72,23 +72,43 @@
 }
 
 static struct block_allocation *do_inode_allocate_extents(
-	struct ext4_inode *inode, u64 len)
+	struct ext4_inode *inode, u64 len, struct block_allocation *prealloc)
 {
-	u32 block_len = DIV_ROUND_UP(len, info.block_size);
-	struct block_allocation *alloc = allocate_blocks(block_len + 1);
+	u32 block_len = DIV_ROUND_UP(len, info.block_size), prealloc_block_len;
+	struct block_allocation *alloc;
 	u32 extent_block = 0;
 	u32 file_block = 0;
 	struct ext4_extent *extent;
 	u64 blocks;
 
-	if (alloc == NULL) {
-		error("Failed to allocate %d blocks\n", block_len + 1);
-		return NULL;
+	if (!prealloc) {
+		alloc = allocate_blocks(block_len + 1);
+		if (alloc == NULL) {
+			error("Failed to allocate %d blocks\n", block_len + 1);
+			return NULL;
+		}
+	} else {
+		prealloc_block_len = block_allocation_len(prealloc);
+		if (block_len + 1 > prealloc_block_len) {
+			alloc = allocate_blocks(block_len + 1 - prealloc_block_len);
+			if (alloc == NULL) {
+				error("Failed to allocate %d blocks\n",
+						block_len + 1 - prealloc_block_len);
+				return NULL;
+			}
+			region_list_merge(&prealloc->list, &alloc->list);
+			free(alloc);
+		}
+		alloc = prealloc;
 	}
 
 	int allocation_len = block_allocation_num_regions(alloc);
 	if (allocation_len <= 3) {
 		reduce_allocation(alloc, 1);
+		// IMPORTANT: reduce_allocation may have changed allocation
+		// length, otherwise file corruption happens when fs thinks
+		// a block is missing from extent header.
+		allocation_len = block_allocation_num_regions(alloc);
 	} else {
 		reserve_oob_blocks(alloc, 1);
 		extent_block = get_oob_block(alloc, 0);
@@ -183,7 +203,7 @@
 	struct block_allocation *alloc;
 	u8 *data = NULL;
 
-	alloc = do_inode_allocate_extents(inode, len);
+	alloc = do_inode_allocate_extents(inode, len, NULL);
 	if (alloc == NULL) {
 		error("failed to allocate extents for %"PRIu64" bytes", len);
 		return NULL;
@@ -205,9 +225,26 @@
 struct block_allocation* inode_allocate_file_extents(struct ext4_inode *inode, u64 len,
 	const char *filename)
 {
-	struct block_allocation *alloc;
+	struct block_allocation *alloc, *prealloc = base_fs_allocations, *prev_prealloc = NULL;
+	// TODO(mkayyash): base_fs_allocations is sorted by filename, consider
+	// storing it in an array and then binary searching for a filename match instead
+	while (prealloc && prealloc->filename != NULL) {
+		if (!strcmp(filename, prealloc->filename)) {
+			break;
+		}
+		prev_prealloc = prealloc;
+		prealloc = prealloc->next;
+	}
+	if (prealloc) {
+		if (!prev_prealloc) {
+			base_fs_allocations = base_fs_allocations->next;
+		} else {
+			prev_prealloc->next = prealloc->next;
+		}
+		prealloc->next = NULL;
+	}
 
-	alloc = do_inode_allocate_extents(inode, len);
+	alloc = do_inode_allocate_extents(inode, len, prealloc);
 	if (alloc == NULL) {
 		error("failed to allocate extents for %"PRIu64" bytes", len);
 		return NULL;
@@ -222,7 +259,7 @@
 {
 	struct block_allocation *alloc;
 
-	alloc = do_inode_allocate_extents(inode, len);
+	alloc = do_inode_allocate_extents(inode, len, NULL);
 	if (alloc == NULL) {
 		error("failed to allocate extents for %"PRIu64" bytes", len);
 		return;
diff --git a/ext4_utils/make_ext4fs.c b/ext4_utils/make_ext4fs.c
index 1ba415b..f45a699 100644
--- a/ext4_utils/make_ext4fs.c
+++ b/ext4_utils/make_ext4fs.c
@@ -5,7 +5,7 @@
  * 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
+ *	  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,
@@ -79,6 +79,13 @@
 
 #endif
 
+#define MAX_PATH 4096
+#define MAX_BLK_MAPPING_STR 1000
+
+const int blk_file_major_ver = 1;
+const int blk_file_minor_ver = 0;
+const char *blk_file_header_fmt = "Base EXT4 version %d.%d";
+
 /* TODO: Not implemented:
    Allocating blocks in the same block group as the file inode
    Hash or binary tree directories
@@ -91,7 +98,7 @@
 }
 
 static u32 build_default_directory_structure(const char *dir_path,
-					     struct selabel_handle *sehnd)
+						 struct selabel_handle *sehnd)
 {
 	u32 inode;
 	u32 root_inode;
@@ -414,15 +421,31 @@
 int make_ext4fs_sparse_fd(int fd, long long len,
 				const char *mountpoint, struct selabel_handle *sehnd)
 {
+	return make_ext4fs_sparse_fd_directory(fd, len, mountpoint, sehnd, NULL);
+}
+
+int make_ext4fs_sparse_fd_directory(int fd, long long len,
+				const char *mountpoint, struct selabel_handle *sehnd,
+				const char *directory)
+{
 	reset_ext4fs_info();
 	info.len = len;
 
-	return make_ext4fs_internal(fd, NULL, NULL, mountpoint, NULL, 0, 1, 0, 0, 0, sehnd, 0, -1, NULL);
+	return make_ext4fs_internal(fd, directory, NULL, mountpoint, NULL,
+								0, 1, 0, 0, 0,
+								sehnd, 0, -1, NULL, NULL, NULL);
 }
 
 int make_ext4fs(const char *filename, long long len,
 				const char *mountpoint, struct selabel_handle *sehnd)
 {
+	return make_ext4fs_directory(filename, len, mountpoint, sehnd, NULL);
+}
+
+int make_ext4fs_directory(const char *filename, long long len,
+						  const char *mountpoint, struct selabel_handle *sehnd,
+						  const char *directory)
+{
 	int fd;
 	int status;
 
@@ -435,7 +458,9 @@
 		return EXIT_FAILURE;
 	}
 
-	status = make_ext4fs_internal(fd, NULL, NULL, mountpoint, NULL, 0, 0, 0, 1, 0, sehnd, 0, -1, NULL);
+	status = make_ext4fs_internal(fd, directory, NULL, mountpoint, NULL,
+								  0, 0, 0, 1, 0,
+								  sehnd, 0, -1, NULL, NULL, NULL);
 	close(fd);
 
 	return status;
@@ -501,17 +526,170 @@
 	return canonicalize_slashes(str, false);
 }
 
+static int compare_chunks(const void* chunk1, const void* chunk2) {
+	struct region* c1 = (struct region*) chunk1;
+	struct region* c2 = (struct region*) chunk2;
+	return c1->block - c2->block;
+}
+
+static int get_block_group(u32 block) {
+	int i, group = 0;
+	for(i = 0; i < aux_info.groups; i++) {
+		if (block >= aux_info.bgs[i].first_block)
+			group = i;
+		else
+			break;
+	}
+	return group;
+}
+
+static void extract_base_fs_allocations(const char *directory, const char *mountpoint,
+										FILE* base_alloc_file_in) {
+#define err_msg "base file badly formatted"
+#ifndef USE_MINGW
+	// FORMAT Version 1.0: filename blk_mapping
+	const char *base_alloc_file_in_format = "%s %s";
+	const int base_file_format_param_count = 2;
+
+	char stored_file_name[MAX_PATH], real_file_name[MAX_PATH], file_map[MAX_BLK_MAPPING_STR];
+	struct block_allocation *fs_alloc;
+	struct block_group_info *bgs = aux_info.bgs;
+	int i, major_version = 0, minor_version = 0;
+	char *base_file_line = NULL;
+	size_t base_file_line_len = 0;
+
+	printf("[v%d.%d] Generating an Incremental EXT4 image\n",
+			blk_file_major_ver, blk_file_minor_ver);
+	if (base_fs_allocations == NULL)
+		base_fs_allocations = create_allocation();
+	fs_alloc = base_fs_allocations;
+
+	fscanf(base_alloc_file_in, blk_file_header_fmt, &major_version, &minor_version);
+	if (major_version == 0) {
+		critical_error("Invalid base file");
+	}
+
+	if (major_version != blk_file_major_ver) {
+		critical_error("Incompatible base file: version required is %d.X",
+				blk_file_major_ver);
+	}
+
+	if (minor_version < blk_file_minor_ver) {
+		critical_error("Incompatible base file: version required is %d.%d or above",
+				blk_file_major_ver, blk_file_minor_ver);
+	}
+
+	while (getline(&base_file_line, &base_file_line_len, base_alloc_file_in) != -1) {
+		if (sscanf(base_file_line, base_alloc_file_in_format, &stored_file_name, &file_map)
+				!= base_file_format_param_count) {
+			continue;
+		}
+		if (strlen(stored_file_name) < strlen(mountpoint)) {
+			continue;
+		}
+		snprintf(real_file_name, MAX_PATH, "%s%s", directory, stored_file_name + strlen(mountpoint));
+		if (!access(real_file_name, R_OK)) {
+			char *block_range, *end_string;
+			int real_file_fd;
+			u32 start_block, end_block, block_file_size;
+			u32 real_file_block_size;
+
+			real_file_fd = open(real_file_name, O_RDONLY);
+			if (real_file_fd == -1) {
+				critical_error(err_msg);
+			}
+			real_file_block_size = get_file_size(real_file_fd);
+			close(real_file_fd);
+			real_file_block_size = DIV_ROUND_UP(real_file_block_size, info.block_size);
+			fs_alloc->filename = strdup(real_file_name);
+			block_range = strtok_r(file_map, ",", &end_string);
+			while (block_range && real_file_block_size) {
+				int block_group;
+				char *range, *end_token = NULL;
+				range = strtok_r(block_range, "-", &end_token);
+				if (!range) {
+					critical_error(err_msg);
+				}
+				start_block = parse_num(range);
+				range = strtok_r(NULL, "-", &end_token);
+				if (!range) {
+					end_block = start_block;
+				} else {
+					end_block = parse_num(range);
+				}
+				// Assummption is that allocations are within the same block group
+				block_group = get_block_group(start_block);
+				if (block_group != get_block_group(end_block)) {
+					critical_error("base file allocation's end block is in a different "
+								   "block group than start block. did you change fs params?");
+				}
+				block_range = strtok_r(NULL, ",", &end_string);
+				int bg_first_block = bgs[block_group].first_block;
+				int min_bg_bound = bgs[block_group].chunks[0].block + bgs[block_group].chunks[0].len;
+				int max_bg_bound = bgs[block_group].chunks[bgs[block_group].chunk_count - 1].block;
+
+				if (min_bg_bound >= start_block - bg_first_block ||
+					max_bg_bound <= end_block - bg_first_block) {
+					continue;
+				}
+				block_file_size = end_block - start_block + 1;
+				if (block_file_size > real_file_block_size) {
+					block_file_size = real_file_block_size;
+				}
+				append_region(fs_alloc, start_block, block_file_size, block_group);
+				reserve_bg_chunk(block_group, start_block - bgs[block_group].first_block, block_file_size);
+				real_file_block_size -= block_file_size;
+			}
+			if (reserve_blocks_for_allocation(fs_alloc) < 0)
+				critical_error("failed to reserve base fs allocation");
+			fs_alloc->next = create_allocation();
+			fs_alloc = fs_alloc->next;
+		}
+	}
+
+	for (i = 0; i < aux_info.groups; i++) {
+		qsort(bgs[i].chunks, bgs[i].chunk_count, sizeof(struct region), compare_chunks);
+	}
+
+	free(base_file_line);
+
+#else
+    return;
+#endif
+#undef err_msg
+}
+
+void generate_base_alloc_file_out(FILE* base_alloc_file_out, char* dir, char* mountpoint,
+								  struct block_allocation* p)
+{
+	size_t dirlen = dir ? strlen(dir) : 0;
+	fprintf(base_alloc_file_out, blk_file_header_fmt, blk_file_major_ver, blk_file_minor_ver);
+	fputc('\n', base_alloc_file_out);
+	while (p) {
+		if (dir && strncmp(p->filename, dir, dirlen) == 0) {
+			// substitute mountpoint for the leading directory in the filename, in the output file
+			fprintf(base_alloc_file_out, "%s%s", mountpoint, p->filename + dirlen);
+		} else {
+			fprintf(base_alloc_file_out, "%s", p->filename);
+		}
+		print_blocks(base_alloc_file_out, p, ',');
+		struct block_allocation* pn = p->next;
+		p = pn;
+	}
+}
+
 int make_ext4fs_internal(int fd, const char *_directory, const char *_target_out_directory,
 						 const char *_mountpoint, fs_config_func_t fs_config_func, int gzip,
 						 int sparse, int crc, int wipe, int real_uuid,
 						 struct selabel_handle *sehnd, int verbose, time_t fixed_time,
-						 FILE* block_list_file)
+						 FILE* block_list_file, FILE* base_alloc_file_in, FILE* base_alloc_file_out)
 {
 	u32 root_inode_num;
 	u16 root_mode;
 	char *mountpoint;
 	char *directory = NULL;
 	char *target_out_directory = NULL;
+	struct block_allocation* p;
 
 	if (setjmp(setjmp_env))
 		return EXIT_FAILURE; /* Handle a call to longjmp() */
@@ -610,6 +788,9 @@
 
 	ext4_fill_in_sb(real_uuid);
 
+	if (base_alloc_file_in) {
+		extract_base_fs_allocations(directory, mountpoint, base_alloc_file_in);
+	}
 	if (reserve_inodes(0, 10) == EXT4_ALLOCATE_FAILED)
 		error("failed to reserve first 10 inodes");
 
@@ -653,6 +834,8 @@
 
 	ext4_update_free();
 
+	// TODO: Consider migrating the OTA tools to the new base alloc file format
+	// used for generating incremental images (see go/incremental-ext4)
 	if (block_list_file) {
 		size_t dirlen = directory ? strlen(directory) : 0;
 		struct block_allocation* p = get_saved_allocation_chain();
@@ -663,13 +846,17 @@
 			} else {
 				fprintf(block_list_file, "%s", p->filename);
 			}
-			print_blocks(block_list_file, p);
+			print_blocks(block_list_file, p, ' ');
 			struct block_allocation* pn = p->next;
-			free_alloc(p);
 			p = pn;
 		}
 	}
 
+	if (base_alloc_file_out) {
+		struct block_allocation* p = get_saved_allocation_chain();
+		generate_base_alloc_file_out(base_alloc_file_out, directory, mountpoint, p);
+	}
+
 	printf("Created filesystem with %d/%d inodes and %d/%d blocks\n",
 			aux_info.sb->s_inodes_count - aux_info.sb->s_free_inodes_count,
 			aux_info.sb->s_inodes_count,
@@ -685,6 +872,13 @@
 	sparse_file_destroy(ext4_sparse_file);
 	ext4_sparse_file = NULL;
 
+	p = get_saved_allocation_chain();
+	while (p) {
+		struct block_allocation* pn = p->next;
+		free_alloc(p);
+		p = pn;
+	}
+
 	free(mountpoint);
 	free(directory);
 
diff --git a/ext4_utils/make_ext4fs.h b/ext4_utils/make_ext4fs.h
index 3784a9e..4498e62 100644
--- a/ext4_utils/make_ext4fs.h
+++ b/ext4_utils/make_ext4fs.h
@@ -25,8 +25,14 @@
 
 int make_ext4fs(const char *filename, long long len,
                 const char *mountpoint, struct selabel_handle *sehnd);
+int make_ext4fs_directory(const char *filename, long long len,
+                const char *mountpoint, struct selabel_handle *sehnd,
+                const char *directory);
 int make_ext4fs_sparse_fd(int fd, long long len,
                 const char *mountpoint, struct selabel_handle *sehnd);
+int make_ext4fs_sparse_fd_directory(int fd, long long len,
+                const char *mountpoint, struct selabel_handle *sehnd,
+                const char *directory);
 
 #ifdef __cplusplus
 }
diff --git a/ext4_utils/make_ext4fs_main.c b/ext4_utils/make_ext4fs_main.c
index 8261f0c..323a445 100644
--- a/ext4_utils/make_ext4fs_main.c
+++ b/ext4_utils/make_ext4fs_main.c
@@ -57,6 +57,7 @@
 	fprintf(stderr, "    [ -L <label> ] [ -f ] [ -a <android mountpoint> ] [ -u ]\n");
 	fprintf(stderr, "    [ -S file_contexts ] [ -C fs_config ] [ -T timestamp ]\n");
 	fprintf(stderr, "    [ -z | -s ] [ -w ] [ -c ] [ -J ] [ -v ] [ -B <block_list_file> ]\n");
+	fprintf(stderr, "    [ -d <base_alloc_file_in> ] [ -D <base_alloc_file_out> ]\n");
 	fprintf(stderr, "    <filename> [[<directory>] <target_out_directory>]\n");
 }
 
@@ -80,11 +81,13 @@
 	time_t fixed_time = -1;
 	struct selabel_handle *sehnd = NULL;
 	FILE* block_list_file = NULL;
+	FILE* base_alloc_file_in = NULL;
+	FILE* base_alloc_file_out = NULL;
 #ifndef USE_MINGW
 	struct selinux_opt seopts[] = { { SELABEL_OPT_PATH, "" } };
 #endif
 
-	while ((opt = getopt(argc, argv, "l:j:b:g:i:I:L:a:S:T:C:B:fwzJsctvu")) != -1) {
+	while ((opt = getopt(argc, argv, "l:j:b:g:i:I:L:a:S:T:C:B:d:D:fwzJsctvu")) != -1) {
 		switch (opt) {
 		case 'l':
 			info.len = parse_num(optarg);
@@ -166,6 +169,20 @@
 				exit(EXIT_FAILURE);
 			}
 			break;
+		case 'd':
+			base_alloc_file_in = fopen(optarg, "r");
+			if (base_alloc_file_in == NULL) {
+				fprintf(stderr, "failed to open base_alloc_file_in: %s\n", strerror(errno));
+				exit(EXIT_FAILURE);
+			}
+			break;
+		case 'D':
+			base_alloc_file_out = fopen(optarg, "w");
+			if (base_alloc_file_out == NULL) {
+				fprintf(stderr, "failed to open base_alloc_file_out: %s\n", strerror(errno));
+				exit(EXIT_FAILURE);
+			}
+			break;
 		default: /* '?' */
 			usage(argv[0]);
 			exit(EXIT_FAILURE);
@@ -237,10 +254,15 @@
 	}
 
 	exitcode = make_ext4fs_internal(fd, directory, target_out_directory, mountpoint, fs_config_func, gzip,
-		sparse, crc, wipe, real_uuid, sehnd, verbose, fixed_time, block_list_file);
+		sparse, crc, wipe, real_uuid, sehnd, verbose, fixed_time,
+		block_list_file, base_alloc_file_in, base_alloc_file_out);
 	close(fd);
 	if (block_list_file)
 		fclose(block_list_file);
+	if (base_alloc_file_out)
+		fclose(base_alloc_file_out);
+	if (base_alloc_file_in)
+		fclose(base_alloc_file_in);
 	if (exitcode && strcmp(filename, "-"))
 		unlink(filename);
 	return exitcode;
diff --git a/ext4_utils/mkuserimg.sh b/ext4_utils/mkuserimg.sh
index 8667013..b79baf9 100755
--- a/ext4_utils/mkuserimg.sh
+++ b/ext4_utils/mkuserimg.sh
@@ -6,7 +6,7 @@
 cat<<EOT
 Usage:
 mkuserimg.sh [-s] SRC_DIR OUTPUT_FILE EXT_VARIANT MOUNT_POINT SIZE [-j <journal_size>]
-             [-T TIMESTAMP] [-C FS_CONFIG] [-D PRODUCT_OUT] [-B BLOCK_LIST_FILE] [-L LABEL] [FILE_CONTEXTS]
+             [-T TIMESTAMP] [-C FS_CONFIG] [-D PRODUCT_OUT] [-B BLOCK_LIST_FILE] [-d BASE_ALLOC_FILE_IN ] [-A BASE_ALLOC_FILE_OUT ] [-L LABEL] [FILE_CONTEXTS]
 EOT
 }
 
@@ -67,6 +67,18 @@
   shift; shift
 fi
 
+BASE_ALLOC_FILE_IN=
+if [[ "$1" == "-d" ]]; then
+  BASE_ALLOC_FILE_IN=$2
+  shift; shift
+fi
+
+BASE_ALLOC_FILE_OUT=
+if [[ "$1" == "-A" ]]; then
+  BASE_ALLOC_FILE_OUT=$2
+  shift; shift
+fi
+
 LABEL=
 if [[ "$1" == "-L" ]]; then
   LABEL=$2
@@ -100,6 +112,12 @@
 if [ -n "$BLOCK_LIST" ]; then
   OPT="$OPT -B $BLOCK_LIST"
 fi
+if [ -n "$BASE_ALLOC_FILE_IN" ]; then
+  OPT="$OPT -d $BASE_ALLOC_FILE_IN"
+fi
+if [ -n "$BASE_ALLOC_FILE_OUT" ]; then
+  OPT="$OPT -D $BASE_ALLOC_FILE_OUT"
+fi
 if [ -n "$LABEL" ]; then
   OPT="$OPT -L $LABEL"
 fi
diff --git a/ext4_utils/unencrypted_properties.cpp b/ext4_utils/unencrypted_properties.cpp
deleted file mode 100644
index ed36e20..0000000
--- a/ext4_utils/unencrypted_properties.cpp
+++ /dev/null
@@ -1,99 +0,0 @@
-#include "unencrypted_properties.h"
-
-#include <sys/stat.h>
-#include <dirent.h>
-
-namespace properties {
-    const char* key = "key";
-    const char* ref = "ref";
-    const char* props = "props";
-    const char* is_default = "is_default";
-}
-
-namespace
-{
-    const char* unencrypted_folder = "unencrypted";
-}
-
-std::string UnencryptedProperties::GetPath(const char* device)
-{
-    return std::string() + device + "/" + unencrypted_folder;
-}
-
-UnencryptedProperties::UnencryptedProperties(const char* device)
-  : folder_(GetPath(device))
-{
-    DIR* dir = opendir(folder_.c_str());
-    if (dir) {
-        closedir(dir);
-    } else {
-        folder_.clear();
-    }
-}
-
-UnencryptedProperties::UnencryptedProperties()
-{
-}
-
-template<> std::string UnencryptedProperties::Get(const char* name,
-                                      std::string default_value) const
-{
-    if (!OK()) return default_value;
-    std::ifstream i(folder_ + "/" + name, std::ios::binary);
-    if (!i) {
-        return default_value;
-    }
-
-    i.seekg(0, std::ios::end);
-    int length = i.tellg();
-    i.seekg(0, std::ios::beg);
-    if (length == -1) {
-        return default_value;
-    }
-
-    std::string s(length, 0);
-    i.read(&s[0], length);
-    if (!i) {
-        return default_value;
-    }
-
-    return s;
-}
-
-template<> bool UnencryptedProperties::Set(const char* name, std::string const& value)
-{
-    if (!OK()) return false;
-    std::ofstream o(folder_ + "/" + name, std::ios::binary);
-    o << value;
-    return !o.fail();
-}
-
-UnencryptedProperties UnencryptedProperties::GetChild(const char* name) const
-{
-    UnencryptedProperties up;
-    if (!OK()) return up;
-
-    std::string directory(folder_ + "/" + name);
-    if (mkdir(directory.c_str(), 700) == -1 && errno != EEXIST) {
-        return up;
-    }
-
-    up.folder_ = directory;
-    return up;
-}
-
-bool UnencryptedProperties::Remove(const char* name)
-{
-    if (!OK()) return false;
-    if (remove((folder_ + "/" + name).c_str())
-        && errno != ENOENT) {
-        return false;
-    }
-
-    return true;
-}
-
-bool UnencryptedProperties::OK() const
-{
-    return !folder_.empty();
-}
diff --git a/ext4_utils/unencrypted_properties.h b/ext4_utils/unencrypted_properties.h
deleted file mode 100644
index b2d1295..0000000
--- a/ext4_utils/unencrypted_properties.h
+++ /dev/null
@@ -1,74 +0,0 @@
-#include <string>
-#include <fstream>
-
-// key names for properties we use
-namespace properties {
-    extern const char* key;
-    extern const char* ref;
-    extern const char* props;
-    extern const char* is_default;
-}
-
-/**
- * Class to store data on the unencrypted folder of a device.
- * Note that the folder must exist before this class is constructed.
- * All names must be valid single level (no '/') file or directory names
- * Data is organized hierarchically so we can get a child folder
- */
-class UnencryptedProperties
-{
-public:
-    // Get path of folder. Must create before using any properties
-    // This is to allow proper setting of SELinux policy
-    static std::string GetPath(const char* device);
-
-    // Opens properties folder on named device.
-    // If folder does not exist, OK will return false, all
-    // getters will return default properties and setters will fail.
-    UnencryptedProperties(const char* device);
-
-    // Get named object. Return default if object does not exist or error.
-    template<typename t> t Get(const char* name, t default_value = t()) const;
-
-    // Set named object. Return true if success, false otherwise
-    template<typename t> bool Set(const char* name, t const& value);
-
-    // Get child properties
-    UnencryptedProperties GetChild(const char* name) const;
-
-    // Remove named object
-    bool Remove(const char* name);
-
-    // Does folder exist?
-    bool OK() const;
-
-private:
-    UnencryptedProperties();
-    std::string folder_;
-};
-
-
-template<typename t> t UnencryptedProperties::Get(const char* name,
-                                                  t default_value) const
-{
-    if (!OK()) return default_value;
-    t value = default_value;
-    std::ifstream(folder_ + "/" + name) >> value;
-    return value;
-}
-
-template<typename t> bool UnencryptedProperties::Set(const char* name,
-                                                     t const& value)
-{
-    if (!OK()) return false;
-    std::ofstream o(folder_ + "/" + name);
-    o << value;
-    return !o.fail();
-}
-
-// Specialized getters/setters for strings
-template<> std::string UnencryptedProperties::Get(const char* name,
-                                      std::string default_value) const;
-
-template<> bool UnencryptedProperties::Set(const char* name,
-                                           std::string const& value);
diff --git a/mmap-perf/Android.mk b/mmap-perf/Android.mk
index 3c6f489..bf6e00e 100644
--- a/mmap-perf/Android.mk
+++ b/mmap-perf/Android.mk
@@ -15,15 +15,16 @@
 #
 
 LOCAL_PATH:= $(call my-dir)
-
 include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES :=  mmapPerf.cpp
 LOCAL_MODULE := mmapPerf
-LOCAL_SRC_FILES_64 := mmapPerf.cpp
-LOCAL_SRC_FILES_32 := unsupported.cpp
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
 LOCAL_CLANG := true
 LOCAL_CFLAGS += -g -Wall -Werror -std=c++11 -Wno-missing-field-initializers -Wno-sign-compare -O3
 LOCAL_FORCE_STATIC_EXECUTABLE := true
 LOCAL_CXX_STL := libc++_static
 LOCAL_STATIC_LIBRARIES := libc
-LOCAL_MODULE_TAGS := optional
-include $(BUILD_EXECUTABLE)
+include $(BUILD_NATIVE_BENCHMARK)
diff --git a/mmap-perf/mmapPerf.cpp b/mmap-perf/mmapPerf.cpp
index bbc83c7..d195850 100644
--- a/mmap-perf/mmapPerf.cpp
+++ b/mmap-perf/mmapPerf.cpp
@@ -1,3 +1,4 @@
+#include "benchmark/benchmark_api.h"
 #include <string>
 #include <cstring>
 #include <cstdlib>
@@ -12,7 +13,9 @@
 #include <sys/mman.h>
 
 using namespace std;
-static const size_t pageSize = 4096;
+static const size_t pageSize = PAGE_SIZE;
+static size_t fsize = 1024 * (1ull << 20);
+static size_t pagesTotal = fsize / pageSize;
 
 class Fd {
     int m_fd = -1;
@@ -54,17 +57,16 @@
     FileMap(const string &name, size_t size, Hint hint = FILE_MAP_HINT_NONE) : m_name{name}, m_size{size} {
         int fd = open(name.c_str(), O_CREAT | O_RDWR, S_IRWXU);
         if (fd < 0) {
-            cerr << "open failed: " << fd << endl;
-            return;
+            cout << "Error: open failed for " << name << ": " << strerror(errno) << endl;
+            exit(1);
         }
         m_fileFd.set(fd);
         fallocate(m_fileFd.get(), 0, 0, size);
         unlink(name.c_str());
         m_ptr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, m_fileFd.get(), 0);
         if ((int)(uintptr_t)m_ptr == -1) {
-            cerr << "mmap failed: " << (int)(uintptr_t)m_ptr << endl;
-            m_ptr = nullptr;
-            return;
+            cout << "Error: mmap failed: " << (int)(uintptr_t)m_ptr << ": " << strerror(errno) << endl;
+            exit(1);
         }
         switch (hint) {
         case FILE_MAP_HINT_NONE: break;
@@ -80,50 +82,21 @@
             fillPageJunk(targetPtr);
         }
     }
-    void benchRandom(bool write) {
-        size_t pagesTotal = m_size / pageSize;
-        size_t pagesToHit = pagesTotal / 128;
-        uint64_t nsTotal = 0;
-
-        chrono::time_point<chrono::high_resolution_clock> start, end;
-        start = chrono::high_resolution_clock::now();
-        for (int j = 0; j < pagesToHit; j++) {
-            int targetPage = rand() % pagesTotal;
-            uint8_t *targetPtr = (uint8_t*)m_ptr + 4096ull * targetPage;
-            if (write) {
-                *targetPtr = dummy;
-            }
-            else {
-                dummy += *targetPtr;
-            }
-        }
-        end = chrono::high_resolution_clock::now();
-        nsTotal += chrono::duration_cast<chrono::nanoseconds>(end - start).count();
-        //cout << "random: " << nsTotal / 1000.0 / (pagesToHit) << "us/page" << endl;
-        cout << "random " << (write ? "write" : "read") << ": " << ((4096.0 * pagesToHit) / (1 << 20)) / (nsTotal / 1.0E9) << "MB/s" << endl;
+    void benchRandomRead(unsigned int targetPage) {
+        uint8_t *targetPtr = (uint8_t*)m_ptr + pageSize * targetPage;
+        dummy += *targetPtr;
     }
-    void benchLinear(bool write) {
-        int pagesTotal = m_size / pageSize;
-        int iterations = 4;
-        uint64_t nsTotal = 0;
-
-        chrono::time_point<chrono::high_resolution_clock> start, end;
-        start = chrono::high_resolution_clock::now();
-        for (int i = 0; i < iterations; i++) {
-            for (int j = 0; j < pagesTotal; j++) {
-                uint8_t *targetPtr = (uint8_t*)m_ptr + 4096ull * j;
-                if (write) {
-                    *targetPtr = dummy;
-                }
-                else {
-                    dummy += *targetPtr;
-                }
-            }
-        }
-        end = chrono::high_resolution_clock::now();
-        nsTotal += chrono::duration_cast<chrono::nanoseconds>(end - start).count();
-        //cout << "linear: " << nsTotal / 1000.0 / (pagesTotal * iterations) << "us/page" << endl;
-        cout << "linear " << (write ? "write" : "read") << ": " << ((4096.0 * pagesTotal * iterations) / (1 << 20)) / (nsTotal / 1.0E9 ) << "MB/s" << endl;
+    void benchRandomWrite(unsigned int targetPage) {
+        uint8_t *targetPtr = (uint8_t*)m_ptr + pageSize * targetPage;
+        *targetPtr = dummy;
+    }
+    void benchLinearRead(unsigned int j) {
+        uint8_t *targetPtr = (uint8_t*)m_ptr + pageSize * j;
+        dummy += *targetPtr;
+    }
+    void benchLinearWrite(unsigned int j) {
+        uint8_t *targetPtr = (uint8_t*)m_ptr + pageSize * j;
+        *targetPtr = dummy;
     }
     void dropCache() {
         int ret1 = msync(m_ptr, m_size, MS_SYNC | MS_INVALIDATE);
@@ -137,27 +110,46 @@
 
 };
 
-int main(int argc, char *argv[])
-{
-    (void)argc;
-    (void)argv;
-    srand(0);
-
-    {
-        FileMap file{"/data/local/tmp/mmap_test", 16000 * (1ull << 20)};
-        file.benchRandom(false);
+static void benchRandomRead(benchmark::State& state) {
+    FileMap file{"/data/local/tmp/mmap_test", fsize};
+    while (state.KeepRunning()) {
+        unsigned int targetPage = rand() % pagesTotal;
+        file.benchRandomRead(targetPage);
     }
-    {
-        FileMap file{"/data/local/tmp/mmap_test", 16000 * (1ull << 20)};
-        file.benchLinear(false);
-    }
-    {
-        FileMap file{"/data/local/tmp/mmap_test", 16000 * (1ull << 20)};
-        file.benchRandom(true);
-    }
-    {
-        FileMap file{"/data/local/tmp/mmap_test", 16000 * (1ull << 20)};
-        file.benchLinear(true);
-    }
-    return 0;
+    state.SetBytesProcessed(state.iterations() * pageSize);
 }
+BENCHMARK(benchRandomRead);
+
+static void benchRandomWrite(benchmark::State& state) {
+    FileMap file{"/data/local/tmp/mmap_test", fsize};
+    while (state.KeepRunning()) {
+        unsigned int targetPage = rand() % pagesTotal;
+        file.benchRandomWrite(targetPage);
+    }
+    state.SetBytesProcessed(state.iterations() * pageSize);
+}
+BENCHMARK(benchRandomWrite);
+
+static void benchLinearRead(benchmark::State& state) {
+   FileMap file{"/data/local/tmp/mmap_test", fsize};
+   unsigned int j = 0;
+   while (state.KeepRunning()) {
+       file.benchLinearRead(j);
+       j = (j + 1) % pagesTotal;
+   }
+   state.SetBytesProcessed(state.iterations() * pageSize);
+}
+BENCHMARK(benchLinearRead);
+
+static void benchLinearWrite(benchmark::State& state) {
+   FileMap file{"/data/local/tmp/mmap_test", fsize};
+   unsigned int j = 0;
+   while (state.KeepRunning()) {
+       file.benchLinearWrite(j);
+       j = (j + 1) % pagesTotal;
+   }
+   state.SetBytesProcessed(state.iterations() * pageSize);
+}
+BENCHMARK(benchLinearWrite);
+
+BENCHMARK_MAIN()
diff --git a/multinetwork/Android.mk b/multinetwork/Android.mk
new file mode 100644
index 0000000..28e56d4
--- /dev/null
+++ b/multinetwork/Android.mk
@@ -0,0 +1,29 @@
+LOCAL_PATH := $(call my-dir)
+
+# The PDK build does not have access to frameworks/native elements.
+ifneq ($(TARGET_BUILD_PDK), true)
+
+# Sample util binaries.
+include $(CLEAR_VARS)
+LOCAL_MODULE := dnschk
+
+LOCAL_C_INCLUDES += frameworks/native/include external/libcxx/include
+LOCAL_CPPFLAGS += -std=c++11
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_SHARED_LIBRARIES := libandroid libbase libc++
+LOCAL_SRC_FILES := dnschk.cpp common.cpp
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := httpurl
+
+LOCAL_C_INCLUDES += frameworks/native/include external/libcxx/include
+LOCAL_CPPFLAGS += -std=c++11
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_SHARED_LIBRARIES := libandroid libbase libc++
+LOCAL_SRC_FILES := httpurl.cpp common.cpp
+include $(BUILD_EXECUTABLE)
+
+endif  # ifneq ($(TARGET_BUILD_PDK), true)
diff --git a/multinetwork/common.cpp b/multinetwork/common.cpp
new file mode 100644
index 0000000..7a5e7be
--- /dev/null
+++ b/multinetwork/common.cpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2016 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 "common.h"
+
+#include <android/api-level.h>
+#include <arpa/inet.h>
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <iostream>
+
+
+namespace {
+
+bool strEqual(const char *a, const char *b) {
+    return strcmp(a, b) == 0;
+}
+
+// Allow specifying network handles in decimal and hexadecimal.
+bool parseNetworkHandle(const char *arg, net_handle_t *nethandle) {
+    if (arg == nullptr || !isdigit(arg[0]) || nethandle == nullptr) {
+        return false;
+    }
+
+    net_handle_t nh;
+    char *end = nullptr;
+
+    nh = strtoull(arg, &end, 0);
+    if (end != nullptr && *end == '\0') {
+        *nethandle = nh;
+        return true;
+    }
+    return false;
+}
+
+}  // namespace
+
+
+void printUsage(const char *progname) {
+    std::cerr << "Usage: " << progname
+              << " [--nethandle <nethandle>]"
+              << " [--mode explicit|process]"
+              << " [--family unspec|ipv4|ipv6]"
+              << " <argument>"
+              << std::endl;
+    std::cerr << std::endl;
+    std::cerr << "Learn nethandle values from 'dumpsys connectivity --short' "
+              << "or 'dumpsys connectivity --diag'"
+              << std::endl;
+}
+
+Arguments::~Arguments() {}
+
+bool Arguments::parseArguments(int argc, const char* argv[]) {
+    if (argc < 1 || argv == nullptr) { return false; }
+
+    for (int i = 1; i < argc; i++) {
+        if (strEqual(argv[i], "--nethandle")) {
+            i++;
+            if (argc == i) break;
+            if (!parseNetworkHandle(argv[i], &nethandle)) {
+                std::cerr << "Failed to parse nethandle: '" << argv[i] << "'"
+                          << std::endl;
+                break;
+            }
+        } else if (strEqual(argv[i], "--family")) {
+            i++;
+            if (argc == i) break;
+            if (strEqual(argv[i], "unspec")) {
+                family = AF_UNSPEC;
+            } else if (strEqual(argv[i], "ipv4")) {
+                family = AF_INET;
+            } else if (strEqual(argv[i], "ipv6")) {
+                family = AF_INET6;
+            } else {
+                break;
+            }
+        } else if (strEqual(argv[i], "--mode")) {
+            i++;
+            if (argc == i) break;
+            if (strEqual(argv[i], "explicit")) {
+                api_mode = ApiMode::EXPLICIT;
+            } else if (strEqual(argv[i], "process")) {
+                api_mode = ApiMode::PROCESS;
+            } else {
+                break;
+            }
+        } else if (arg1 == nullptr) {
+            arg1 = argv[i];
+        } else {
+            arg1 = nullptr;
+            break;
+        }
+    }
+
+    if (arg1 != nullptr) {
+        return true;
+    }
+
+    printUsage(argv[0]);
+    return false;
+}
+
+
+std::string inetSockaddrToString(const sockaddr* sa) {
+    const bool is_ipv6 = (sa->sa_family == AF_INET6);
+    char host[INET6_ADDRSTRLEN];
+    char port[sizeof("65535")];
+    getnameinfo(sa, is_ipv6 ? sizeof(sockaddr_in6) : sizeof(sockaddr_in),
+                host, sizeof(host),
+                port, sizeof(port),
+                NI_NUMERICHOST | NI_NUMERICSERV);
+
+    if (port[0] == '0' || port[0] == '\0') {
+        return std::string(host);
+    }
+    return (is_ipv6 ? "[" : "") + std::string(host) + (is_ipv6 ? "]:" : ":") + std::string(port);
+}
diff --git a/multinetwork/common.h b/multinetwork/common.h
new file mode 100644
index 0000000..f431ea9
--- /dev/null
+++ b/multinetwork/common.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2016 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 SYSTEM_EXTRAS_MULTINETWORK_COMMON_H_
+#define SYSTEM_EXTRAS_MULTINETWORK_COMMON_H_
+
+#include <sys/cdefs.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <string>
+#include <android/multinetwork.h>
+
+enum class ApiMode {
+    EXPLICIT,
+    PROCESS,
+};
+
+
+struct Arguments {
+    Arguments() : nethandle(NETWORK_UNSPECIFIED),
+                  api_mode(ApiMode::EXPLICIT),
+                  family(AF_UNSPEC),
+                  arg1(nullptr) {}
+    ~Arguments();
+
+    bool parseArguments(int argc, const char* argv[]);
+
+    net_handle_t nethandle;
+    ApiMode api_mode;
+    sa_family_t family;
+    const char* arg1;
+};
+
+
+void printUsage(const char *progname);
+
+// If port is non-zero returns strings of the form "192.0.2.1:port" or
+// "[2001:db8::1]:port", else it returns the bare IP string literal.
+std::string inetSockaddrToString(const sockaddr* sa);
+
+
+struct FdAutoCloser {
+    FdAutoCloser() : fd(-1) {}
+    /* not explicit */ FdAutoCloser(int fd) : fd(fd) {}
+    ~FdAutoCloser() {
+        if (fd > -1) {
+            close(fd);
+        }
+        fd = -1;
+    }
+
+    int fd;
+};
+
+#endif  // SYSTEM_EXTRAS_MULTINETWORK_COMMON_H_
diff --git a/multinetwork/dnschk.cpp b/multinetwork/dnschk.cpp
new file mode 100644
index 0000000..a2c42d4
--- /dev/null
+++ b/multinetwork/dnschk.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2016 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 <arpa/inet.h>
+#include <errno.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include <iostream>
+#include <string>
+
+#include <android/multinetwork.h>
+#include "common.h"
+
+
+int main(int argc, const char* argv[]) {
+    int rval = -1;
+
+    struct Arguments args;
+    if (!args.parseArguments(argc, argv)) { return rval; }
+
+    const struct addrinfo hints = {
+            .ai_family = args.family,
+            .ai_socktype = SOCK_DGRAM,
+    };
+    struct addrinfo *result = nullptr;
+
+    std::cout << "# " << args.arg1
+              << " (via nethandle " << args.nethandle << "):"
+              << std::endl;
+
+    switch (args.api_mode) {
+        case ApiMode::EXPLICIT:
+            rval = android_getaddrinfofornetwork(args.nethandle,
+                    args.arg1, nullptr, &hints, &result);
+            break;
+        case ApiMode::PROCESS:
+            if (args.nethandle != NETWORK_UNSPECIFIED) {
+                rval = android_setprocnetwork(args.nethandle);
+                if (rval != 0) {
+                    std::cerr << "android_setprocnetwork returned " << rval
+                              << std::endl;
+                    return rval;
+                }
+            }
+            rval = getaddrinfo(args.arg1, nullptr, &hints, &result);
+            break;
+        default:
+            // Unreachable.
+            std::cerr << "Unknown api mode." << std::endl;
+            return -1;
+    }
+
+    if (rval != 0) {
+        std::cerr << "DNS resolution failure; gaierror=" << rval
+                  << " [" << gai_strerror(rval) << "]"
+                  << std::endl;
+        return rval;
+    }
+
+    for (struct addrinfo* rp = result; rp != nullptr; rp = rp->ai_next) {
+        std::cout << inetSockaddrToString(rp->ai_addr) << std::endl;
+    }
+
+    freeaddrinfo(result);
+    return 0;
+}
diff --git a/multinetwork/httpurl.cpp b/multinetwork/httpurl.cpp
new file mode 100644
index 0000000..e079c1d
--- /dev/null
+++ b/multinetwork/httpurl.cpp
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2016 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 <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <iostream>
+#include <string>
+
+#include <android/multinetwork.h>
+#include <android-base/stringprintf.h>
+#include "common.h"
+
+
+
+struct Parameters {
+    Parameters() : ss({}), port("80"), path("/") {}
+
+    struct sockaddr_storage ss;
+    std::string host;
+    std::string hostname;
+    std::string port;
+    std::string path;
+};
+
+
+bool parseUrl(const struct Arguments& args, struct Parameters* parameters) {
+    if (parameters == nullptr) { return false; }
+
+    static const char HTTP_PREFIX[] = "http://";
+    if (strncmp(args.arg1, HTTP_PREFIX, strlen(HTTP_PREFIX)) != 0) {
+        std::cerr << "Only " << HTTP_PREFIX << " URLs supported." << std::endl;
+        return false;
+    }
+
+    parameters->host = std::string(args.arg1).substr(strlen(HTTP_PREFIX));
+    const auto first_slash = parameters->host.find_first_of("/");
+    if (first_slash != std::string::npos) {
+        parameters->path = parameters->host.substr(first_slash);
+        parameters->host.erase(first_slash);
+    }
+
+    if (parameters->host.size() == 0) {
+        std::cerr << "Host portion cannot be empty." << std::endl;
+        return false;
+    }
+
+    if (parameters->host[0] == '[') {
+        const auto closing_bracket = parameters->host.find_first_of("]");
+        if (closing_bracket == std::string::npos) {
+            std::cerr << "Missing closing bracket." << std::endl;
+            return false;
+        }
+        parameters->hostname = parameters->host.substr(1, closing_bracket - 1);
+
+        const auto colon_port = closing_bracket + 1;
+        if (colon_port < parameters->host.size()) {
+            if (parameters->host[colon_port] != ':') {
+                std::cerr << "Malformed port portion." << std::endl;
+                return false;
+            }
+            parameters->port = parameters->host.substr(closing_bracket + 2);
+        }
+    } else {
+        const auto first_colon = parameters->host.find_first_of(":");
+        if (first_colon != std::string::npos) {
+            parameters->port = parameters->host.substr(first_colon + 1);
+            parameters->hostname = parameters->host.substr(0, first_colon);
+        } else {
+            parameters->hostname = parameters->host;
+        }
+    }
+
+    // TODO: find the request portion to send (before '#...').
+
+    std::cerr << "Resolving hostname=" << parameters->hostname
+              << ", port=" << parameters->port
+              << std::endl;
+
+    struct addrinfo hints = {
+            .ai_family = args.family,
+            .ai_socktype = SOCK_STREAM,
+    };
+    struct addrinfo *result = nullptr;
+
+    int rval = -1;
+    switch (args.api_mode) {
+        case ApiMode::EXPLICIT:
+            rval = android_getaddrinfofornetwork(args.nethandle,
+                                                 parameters->hostname.c_str(),
+                                                 parameters->port.c_str(),
+                                                 &hints, &result);
+            break;
+        case ApiMode::PROCESS:
+            rval = getaddrinfo(parameters->hostname.c_str(),
+                               parameters->port.c_str(),
+                               &hints, &result);
+            break;
+        default:
+            // Unreachable.
+            std::cerr << "Unknown api mode." << std::endl;
+            return false;
+    }
+
+    if (rval != 0) {
+        std::cerr << "DNS resolution failure; gaierror=" << rval
+                  << " [" << gai_strerror(rval) << "]"
+                  << std::endl;
+        return rval;
+    }
+
+    memcpy(&(parameters->ss), result[0].ai_addr, result[0].ai_addrlen);
+    std::cerr << "Connecting to: "
+              << inetSockaddrToString(result[0].ai_addr)
+              << std::endl;
+
+    freeaddrinfo(result);
+    return true;
+}
+
+
+int makeTcpSocket(sa_family_t address_family, net_handle_t nethandle) {
+    int fd = socket(address_family, SOCK_STREAM, IPPROTO_TCP);
+    if (fd < 0) {
+        std::cerr << "failed to create TCP socket" << std::endl;
+        return -1;
+    }
+
+    // Don't let reads or writes block indefinitely. We cannot control
+    // connect() timeouts without nonblocking sockets and select/poll/epoll.
+    const struct timeval timeo = { 5, 0 };  // 5 seconds
+    setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo));
+    setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeo, sizeof(timeo));
+
+    if (nethandle != NETWORK_UNSPECIFIED) {
+        if (android_setsocknetwork(nethandle, fd) != 0) {
+            int errnum = errno;
+            std::cerr << "android_setsocknetwork() failed;"
+                      << " errno: " << errnum << " [" << strerror(errnum) << "]"
+                      << std::endl;
+            close(fd);
+            return -1;
+        }
+    }
+    return fd;
+}
+
+
+int doHttpQuery(int fd, const struct Parameters& parameters) {
+    int rval = -1;
+    if (connect(fd,
+                reinterpret_cast<const struct sockaddr *>(&(parameters.ss)),
+                (parameters.ss.ss_family == AF_INET6)
+                        ? sizeof(struct sockaddr_in6)
+                        : sizeof(struct sockaddr_in)) != 0) {
+        int errnum = errno;
+        std::cerr << "Failed to connect; errno=" << errnum
+                  << " [" << strerror(errnum) << "]"
+                  << std::endl;
+        return -1;
+    }
+
+    const std::string request(android::base::StringPrintf(
+            "GET %s HTTP/1.1\r\n"
+            "Host: %s\r\n"
+            "Accept: */*\r\n"
+            "Connection: close\r\n"
+            "User-Agent: httpurl/0.0\r\n"
+            "\r\n",
+            parameters.path.c_str(), parameters.host.c_str()));
+    const ssize_t sent = write(fd, request.c_str(), request.size());
+    if (sent != static_cast<ssize_t>(request.size())) {
+        std::cerr << "Sent only " << sent << "/" << request.size() << " bytes"
+                  << std::endl;
+        return -1;
+    }
+
+    char buf[4*1024];
+    do {
+        rval = recv(fd, buf, sizeof(buf), 0);
+
+        if (rval < 0) {
+            const int saved_errno = errno;
+            std::cerr << "Failed to recv; errno=" << saved_errno
+                      << " [" << strerror(saved_errno) << "]"
+                      << std::endl;
+        } else if (rval > 0) {
+            std::cout.write(buf, rval);
+            std::cout.flush();
+        }
+    } while (rval > 0);
+    std::cout << std::endl;
+
+    return 0;
+}
+
+
+int main(int argc, const char* argv[]) {
+    int rval = -1;
+
+    struct Arguments args;
+    if (!args.parseArguments(argc, argv)) { return rval; }
+
+    if (args.api_mode == ApiMode::PROCESS) {
+        rval = android_setprocnetwork(args.nethandle);
+        if (rval != 0) {
+            int errnum = errno;
+            std::cerr << "android_setprocnetwork(" << args.nethandle << ") failed;"
+                      << " errno: " << errnum << " [" << strerror(errnum) << "]"
+                      << std::endl;
+            return rval;
+        }
+    }
+
+    struct Parameters parameters;
+    if (!parseUrl(args, &parameters)) { return -1; }
+
+    // TODO: Fall back from IPv6 to IPv4 if ss.ss_family is AF_UNSPEC.
+    // This will involve changes to parseUrl() as well.
+    struct FdAutoCloser closer = makeTcpSocket(
+            parameters.ss.ss_family,
+            (args.api_mode == ApiMode::EXPLICIT) ? args.nethandle
+                                                 : NETWORK_UNSPECIFIED);
+    if (closer.fd < 0) { return closer.fd; }
+
+    return doHttpQuery(closer.fd, parameters);
+}
diff --git a/multinetwork/quick_test.sh b/multinetwork/quick_test.sh
new file mode 100755
index 0000000..f586bae
--- /dev/null
+++ b/multinetwork/quick_test.sh
@@ -0,0 +1,48 @@
+#!/bin/bash
+
+nethandle=0
+
+readonly TEST_HOST="connectivitycheck.gstatic.com"
+readonly TEST_PATH="/generate_204"
+readonly PREFIX=">>>"
+
+function getUrls() {
+    if [ ! -z $(echo "$1" | sed -e 's/[^:]//g') ]; then
+        echo "http://[$1]$TEST_PATH"
+        echo "http://[$1]:80$TEST_PATH"
+    else
+        echo "http://$1$TEST_PATH"
+        echo "http://$1:80$TEST_PATH"
+    fi
+}
+
+function toHex() {
+    readonly local hexValue=$(bc -q 2>/dev/null << EOT
+obase=16
+$1
+EOT
+)
+    if [ ! -z "$hexValue" ]; then
+        echo "0x$hexValue"
+    fi
+}
+
+
+if [ ! -z "$1" ]; then
+    nethandle="$1"
+fi
+echo "$PREFIX Using nethandle $nethandle ($(toHex $nethandle))"
+echo ""
+
+readonly IPADDRESSES=$(
+    adb shell /system/xbin/dnschk --nethandle $nethandle $TEST_HOST |
+    sed -e 's/#.*//' -e '/^$/d')
+
+
+for host in $TEST_HOST $IPADDRESSES; do
+    urls=$(getUrls $host)
+    for url in $urls; do
+        echo "$PREFIX Checking $url" >&2
+        adb shell /system/xbin/httpurl --nethandle $nethandle "$url"
+    done
+done
diff --git a/pagecache/Android.mk b/pagecache/Android.mk
new file mode 100644
index 0000000..fe06410
--- /dev/null
+++ b/pagecache/Android.mk
@@ -0,0 +1,13 @@
+# Copyright 2015 The Android Open Source Project
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= dumpcache.c
+LOCAL_SHARED_LIBRARIES := libcutils
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE:= dumpcache
+
+include $(BUILD_EXECUTABLE)
+
diff --git a/pagecache/MODULE_LICENSE_APACHE2 b/pagecache/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/pagecache/MODULE_LICENSE_APACHE2
diff --git a/pagecache/NOTICE b/pagecache/NOTICE
new file mode 100644
index 0000000..34bdaf1
--- /dev/null
+++ b/pagecache/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-2015, 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.
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/pagecache/README b/pagecache/README
new file mode 100644
index 0000000..08f4b53
--- /dev/null
+++ b/pagecache/README
@@ -0,0 +1,4 @@
+Pagecache tools.
+
+dumpcache.c: dumps complete pagecache of device.
+pagecache.py: shows live info on files going in/out of pagecache.
diff --git a/pagecache/dumpcache.c b/pagecache/dumpcache.c
new file mode 100644
index 0000000..eb11bba
--- /dev/null
+++ b/pagecache/dumpcache.c
@@ -0,0 +1,153 @@
+#include <ftw.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <ctype.h>
+#include <stddef.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+// Initial size of the array holding struct file_info
+#define INITIAL_NUM_FILES 512
+
+// Max number of file descriptors to use for ntfw
+#define MAX_NUM_FD 1
+
+struct file_info {
+    char *name;
+    size_t file_size;
+    size_t num_cached_pages;
+};
+
+// Size of pages on this system
+static int g_page_size;
+
+// Total number of cached pages found so far
+static size_t g_total_cached = 0;
+
+// Total number of files scanned so far
+static size_t g_num_files = 0;
+
+// Scanned files and their associated cached page counts
+static struct file_info **g_files;
+
+// Current size of files array
+size_t g_files_size;
+
+static struct file_info *get_file_info(const char* fpath, size_t file_size) {
+    struct file_info *info;
+    if (g_num_files >= g_files_size) {
+        g_files = realloc(g_files, 2 * g_files_size * sizeof(struct file_info*));
+        if (!g_files) {
+            fprintf(stderr, "Couldn't allocate space for files array: %s\n", strerror(errno));
+            exit(EXIT_FAILURE);
+        }
+        g_files_size = 2 * g_files_size;
+    }
+
+    info = calloc(1, sizeof(*info));
+    if (!info) {
+        fprintf(stderr, "Couldn't allocate space for file struct: %s\n", strerror(errno));
+        exit(EXIT_FAILURE);
+    }
+
+    info->name = malloc(strlen(fpath) + 1);
+    if (!info->name) {
+        fprintf(stderr, "Couldn't allocate space for file struct: %s\n", strerror(errno));
+        exit(EXIT_FAILURE);
+    }
+    strcpy(info->name, fpath);
+
+    info->num_cached_pages = 0;
+    info->file_size = file_size;
+
+    g_files[g_num_files++] = info;
+
+    return info;
+}
+
+static int store_num_cached(const char* fpath, const struct stat *sb) {
+    int fd;
+    fd = open (fpath, O_RDONLY);
+
+    if (fd == -1) {
+        printf("Could not open file.");
+        return -1;
+    }
+
+    void* mapped_addr = mmap(NULL, sb->st_size, PROT_NONE, MAP_SHARED, fd, 0);
+
+    if (mapped_addr != MAP_FAILED) {
+        // Calculate bit-vector size
+        size_t num_file_pages = (sb->st_size + g_page_size - 1) / g_page_size;
+        unsigned char* mincore_data = calloc(1, num_file_pages);
+        int ret = mincore(mapped_addr, sb->st_size, mincore_data);
+        int num_cached = 0;
+        unsigned int page = 0;
+        for (page = 0; page < num_file_pages; page++) {
+           if (mincore_data[page]) num_cached++;
+        }
+        if (num_cached > 0) {
+            struct file_info *info = get_file_info(fpath, sb->st_size);
+            info->num_cached_pages += num_cached;
+            g_total_cached += num_cached;
+        }
+        munmap(mapped_addr, sb->st_size);
+    }
+
+    close(fd);
+    return 0;
+}
+
+static int scan_entry(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) {
+    if (typeflag == FTW_F) {
+        store_num_cached(fpath, sb);
+    }
+    return 0;
+}
+
+static int cmpsize(size_t a, size_t b) {
+    if (a < b) return -1;
+    if (a > b) return 1;
+    return 0;
+}
+
+static int cmpfiles(const void *a, const void *b) {
+    return cmpsize((*((struct file_info**)a))->num_cached_pages,
+            (*((struct file_info**)b))->num_cached_pages);
+}
+
+int main()
+{
+    size_t i;
+    g_page_size = getpagesize();
+
+    g_files = malloc(INITIAL_NUM_FILES * sizeof(struct file_info*));
+    g_files_size = INITIAL_NUM_FILES;
+
+    // Walk filesystem trees
+    nftw("/system/", &scan_entry, MAX_NUM_FD, 0);
+    nftw("/vendor/", &scan_entry, MAX_NUM_FD, 0);
+    nftw("/data/", &scan_entry, MAX_NUM_FD, 0);
+
+    // Sort entries
+    qsort(g_files, g_num_files, sizeof(g_files[0]), &cmpfiles);
+
+    // Dump entries
+    for (i = 0; i < g_num_files; i++) {
+        struct file_info *info = g_files[i];
+        fprintf(stdout, "%s: %zu cached pages (%.2f MB, %zu%% of total file size.)\n", info->name,
+                info->num_cached_pages,
+                (float) (info->num_cached_pages * g_page_size) / 1024 / 1024,
+                (100 * info->num_cached_pages * g_page_size) / info->file_size);
+    }
+
+    fprintf(stdout, "TOTAL CACHED: %zu pages (%f MB)\n", g_total_cached,
+            (float) (g_total_cached * 4096) / 1024 / 1024);
+    return 0;
+}
diff --git a/pagecache/pagecache.py b/pagecache/pagecache.py
new file mode 100755
index 0000000..c822ff0
--- /dev/null
+++ b/pagecache/pagecache.py
@@ -0,0 +1,404 @@
+#!/usr/bin/env python
+
+import curses
+import operator
+import optparse
+import os
+import re
+import subprocess
+import sys
+import threading
+import Queue
+
+STATS_UPDATE_INTERVAL = 0.2
+PAGE_SIZE = 4096
+
+class PagecacheStats():
+  """Holds pagecache stats by accounting for pages added and removed.
+
+  """
+  def __init__(self, inode_to_filename):
+    self._inode_to_filename = inode_to_filename
+    self._file_size = {}
+    self._file_pages = {}
+    self._total_pages_added = 0
+    self._total_pages_removed = 0
+
+  def add_page(self, device_number, inode, offset):
+    # See if we can find the page in our lookup table
+    if (device_number, inode) in self._inode_to_filename:
+      filename, filesize = self._inode_to_filename[(device_number, inode)]
+      if filename not in self._file_pages:
+        self._file_pages[filename] = [1, 0]
+      else:
+        self._file_pages[filename][0] += 1
+
+      self._total_pages_added += 1
+
+      if filename not in self._file_size:
+        self._file_size[filename] = filesize
+
+  def remove_page(self, device_number, inode, offset):
+    if (device_number, inode) in self._inode_to_filename:
+      filename, filesize = self._inode_to_filename[(device_number, inode)]
+      if filename not in self._file_pages:
+        self._file_pages[filename] = [0, 1]
+      else:
+        self._file_pages[filename][1] += 1
+
+      self._total_pages_removed += 1
+
+      if filename not in self._file_size:
+        self._file_size[filename] = filesize
+
+  def pages_to_mb(self, num_pages):
+    return "%.2f" % round(num_pages * PAGE_SIZE / 1024.0 / 1024.0, 2)
+
+  def bytes_to_mb(self, num_bytes):
+    return "%.2f" % round(int(num_bytes) / 1024.0 / 1024.0, 2)
+
+  def print_pages_and_mb(self, num_pages):
+    pages_string = str(num_pages) + ' (' + str(self.pages_to_mb(num_pages)) + ' MB)'
+    return pages_string
+
+  def reset_stats(self):
+    self._file_pages.clear()
+    self._total_pages_added = 0;
+    self._total_pages_removed = 0;
+
+  def print_stats(self):
+    # Create new merged dict
+    sorted_added = sorted(self._file_pages.items(), key=operator.itemgetter(1), reverse=True)
+    row_format = "{:<70}{:<12}{:<14}{:<9}"
+    print row_format.format('NAME', 'ADDED (MB)', 'REMOVED (MB)', 'SIZE (MB)')
+    for filename, added in sorted_added:
+      filesize = self._file_size[filename]
+      added = self._file_pages[filename][0]
+      removed = self._file_pages[filename][1]
+      if (filename > 64):
+        filename = filename[-64:]
+      print row_format.format(filename, self.pages_to_mb(added), self.pages_to_mb(removed), self.bytes_to_mb(filesize))
+
+    print row_format.format('TOTAL', self.pages_to_mb(self._total_pages_added), self.pages_to_mb(self._total_pages_removed), '')
+
+  def print_stats_curses(self, pad):
+    sorted_added = sorted(self._file_pages.items(), key=operator.itemgetter(1), reverse=True)
+    height, width = pad.getmaxyx()
+    pad.clear()
+    pad.addstr(0, 2, 'NAME'.ljust(68), curses.A_REVERSE)
+    pad.addstr(0, 70, 'ADDED (MB)'.ljust(12), curses.A_REVERSE)
+    pad.addstr(0, 82, 'REMOVED (MB)'.ljust(14), curses.A_REVERSE)
+    pad.addstr(0, 96, 'SIZE (MB)'.ljust(9), curses.A_REVERSE)
+    y = 1
+    for filename, added_removed in sorted_added:
+      filesize = self._file_size[filename]
+      added  = self._file_pages[filename][0]
+      removed = self._file_pages[filename][1]
+      if (filename > 64):
+        filename = filename[-64:]
+      pad.addstr(y, 2, filename)
+      pad.addstr(y, 70, self.pages_to_mb(added).rjust(10))
+      pad.addstr(y, 80, self.pages_to_mb(removed).rjust(14))
+      pad.addstr(y, 96, self.bytes_to_mb(filesize).rjust(9))
+      y += 1
+      if y == height - 2:
+        pad.addstr(y, 4, "<more...>")
+        break
+    y += 1
+    pad.addstr(y, 2, 'TOTAL'.ljust(74), curses.A_REVERSE)
+    pad.addstr(y, 70, str(self.pages_to_mb(self._total_pages_added)).rjust(10), curses.A_REVERSE)
+    pad.addstr(y, 80, str(self.pages_to_mb(self._total_pages_removed)).rjust(14), curses.A_REVERSE)
+    pad.refresh(0,0, 0,0, height,width)
+
+class FileReaderThread(threading.Thread):
+  """Reads data from a file/pipe on a worker thread.
+
+  Use the standard threading. Thread object API to start and interact with the
+  thread (start(), join(), etc.).
+  """
+
+  def __init__(self, file_object, output_queue, text_file, chunk_size=-1):
+    """Initializes a FileReaderThread.
+
+    Args:
+      file_object: The file or pipe to read from.
+      output_queue: A Queue.Queue object that will receive the data
+      text_file: If True, the file will be read one line at a time, and
+          chunk_size will be ignored.  If False, line breaks are ignored and
+          chunk_size must be set to a positive integer.
+      chunk_size: When processing a non-text file (text_file = False),
+          chunk_size is the amount of data to copy into the queue with each
+          read operation.  For text files, this parameter is ignored.
+    """
+    threading.Thread.__init__(self)
+    self._file_object = file_object
+    self._output_queue = output_queue
+    self._text_file = text_file
+    self._chunk_size = chunk_size
+    assert text_file or chunk_size > 0
+
+  def run(self):
+    """Overrides Thread's run() function.
+
+    Returns when an EOF is encountered.
+    """
+    if self._text_file:
+      # Read a text file one line at a time.
+      for line in self._file_object:
+        self._output_queue.put(line)
+    else:
+      # Read binary or text data until we get to EOF.
+      while True:
+        chunk = self._file_object.read(self._chunk_size)
+        if not chunk:
+          break
+        self._output_queue.put(chunk)
+
+  def set_chunk_size(self, chunk_size):
+    """Change the read chunk size.
+
+    This function can only be called if the FileReaderThread object was
+    created with an initial chunk_size > 0.
+    Args:
+      chunk_size: the new chunk size for this file.  Must be > 0.
+    """
+    # The chunk size can be changed asynchronously while a file is being read
+    # in a worker thread.  However, type of file can not be changed after the
+    # the FileReaderThread has been created.  These asserts verify that we are
+    # only changing the chunk size, and not the type of file.
+    assert not self._text_file
+    assert chunk_size > 0
+    self._chunk_size = chunk_size
+
+class AdbUtils():
+  @staticmethod
+  def add_adb_serial(adb_command, device_serial):
+    if device_serial is not None:
+      adb_command.insert(1, device_serial)
+      adb_command.insert(1, '-s')
+
+  @staticmethod
+  def construct_adb_shell_command(shell_args, device_serial):
+    adb_command = ['adb', 'shell', ' '.join(shell_args)]
+    AdbUtils.add_adb_serial(adb_command, device_serial)
+    return adb_command
+
+  @staticmethod
+  def run_adb_shell(shell_args, device_serial):
+    """Runs "adb shell" with the given arguments.
+
+    Args:
+      shell_args: array of arguments to pass to adb shell.
+      device_serial: if not empty, will add the appropriate command-line
+          parameters so that adb targets the given device.
+    Returns:
+      A tuple containing the adb output (stdout & stderr) and the return code
+      from adb.  Will exit if adb fails to start.
+    """
+    adb_command = AdbUtils.construct_adb_shell_command(shell_args, device_serial)
+
+    adb_output = []
+    adb_return_code = 0
+    try:
+      adb_output = subprocess.check_output(adb_command, stderr=subprocess.STDOUT,
+                                           shell=False, universal_newlines=True)
+    except OSError as error:
+      # This usually means that the adb executable was not found in the path.
+      print >> sys.stderr, ('\nThe command "%s" failed with the following error:'
+                            % ' '.join(adb_command))
+      print >> sys.stderr, '    %s' % str(error)
+      print >> sys.stderr, 'Is adb in your path?'
+      adb_return_code = error.errno
+      adb_output = error
+    except subprocess.CalledProcessError as error:
+      # The process exited with an error.
+      adb_return_code = error.returncode
+      adb_output = error.output
+
+    return (adb_output, adb_return_code)
+
+  @staticmethod
+  def do_preprocess_adb_cmd(command, serial):
+    args = [command]
+    dump, ret_code = AdbUtils.run_adb_shell(args, serial)
+    if ret_code != 0:
+      return None
+
+    dump = ''.join(dump)
+    return dump
+
+def parse_atrace_line(line, pagecache_stats, app_name):
+  # Find a mm_filemap_add_to_page_cache entry
+  m = re.match('.* (mm_filemap_add_to_page_cache|mm_filemap_delete_from_page_cache): dev (\d+):(\d+) ino ([0-9a-z]+) page=([0-9a-z]+) pfn=\d+ ofs=(\d+).*', line)
+  if m != None:
+    # Get filename
+    device_number = int(m.group(2)) << 8 | int(m.group(3))
+    if device_number == 0:
+      return
+    inode = int(m.group(4), 16)
+    if app_name != None and not (app_name in m.group(0)):
+      return
+    if m.group(1) == 'mm_filemap_add_to_page_cache':
+      pagecache_stats.add_page(device_number, inode, m.group(4))
+    elif m.group(1) == 'mm_filemap_delete_from_page_cache':
+      pagecache_stats.remove_page(device_number, inode, m.group(4))
+
+def build_inode_lookup_table(inode_dump):
+  inode2filename = {}
+  text = inode_dump.splitlines()
+  for line in text:
+    result = re.match('([0-9]+)d? ([0-9]+) ([0-9]+) (.*)', line)
+    if result:
+      inode2filename[(int(result.group(1)), int(result.group(2)))] = (result.group(4), result.group(3))
+
+  return inode2filename;
+
+def get_inode_data(datafile, dumpfile, adb_serial):
+  if datafile is not None and os.path.isfile(datafile):
+    print('Using cached inode data from ' + datafile)
+    f = open(datafile, 'r')
+    stat_dump = f.read();
+  else:
+    # Build inode maps if we were tracing page cache
+    print('Downloading inode data from device')
+    stat_dump = AdbUtils.do_preprocess_adb_cmd('find /system /data /vendor ' +
+                                    '-exec stat -c "%d %i %s %n" {} \;', adb_serial)
+    if stat_dump is None:
+      print 'Could not retrieve inode data from device.'
+      sys.exit(1)
+
+    if dumpfile is not None:
+      print 'Storing inode data in ' + dumpfile
+      f = open(dumpfile, 'w')
+      f.write(stat_dump)
+      f.close()
+
+    sys.stdout.write('Done.\n')
+
+  return stat_dump
+
+def read_and_parse_trace_file(trace_file, pagecache_stats, app_name):
+  for line in trace_file:
+    parse_atrace_line(line, pagecache_stats, app_name)
+  pagecache_stats.print_stats();
+
+def read_and_parse_trace_data_live(stdout, stderr, pagecache_stats, app_name):
+  # Start reading trace data
+  stdout_queue = Queue.Queue(maxsize=128)
+  stderr_queue = Queue.Queue()
+
+  stdout_thread = FileReaderThread(stdout, stdout_queue,
+                                   text_file=True, chunk_size=64)
+  stderr_thread = FileReaderThread(stderr, stderr_queue,
+                                   text_file=True)
+  stdout_thread.start()
+  stderr_thread.start()
+
+  stdscr = curses.initscr()
+
+  try:
+    height, width = stdscr.getmaxyx()
+    curses.noecho()
+    curses.cbreak()
+    stdscr.keypad(True)
+    stdscr.nodelay(True)
+    stdscr.refresh()
+    # We need at least a 30x100 window
+    used_width = max(width, 100)
+    used_height = max(height, 30)
+
+    # Create a pad for pagecache stats
+    pagecache_pad = curses.newpad(used_height - 2, used_width)
+
+    stdscr.addstr(used_height - 1, 0, 'KEY SHORTCUTS: (r)eset stats, CTRL-c to quit')
+    while (stdout_thread.isAlive() or stderr_thread.isAlive() or
+           not stdout_queue.empty() or not stderr_queue.empty()):
+      while not stderr_queue.empty():
+        # Pass along errors from adb.
+        line = stderr_queue.get()
+        sys.stderr.write(line)
+      while True:
+        try:
+          line = stdout_queue.get(True, STATS_UPDATE_INTERVAL)
+          parse_atrace_line(line, pagecache_stats, app_name)
+        except Queue.Empty:
+          break
+
+      key = ''
+      try:
+        key = stdscr.getkey()
+      except:
+        pass
+
+      if key == 'r':
+        pagecache_stats.reset_stats()
+
+      pagecache_stats.print_stats_curses(pagecache_pad)
+  except Exception, e:
+    curses.endwin()
+    print e
+  finally:
+    curses.endwin()
+    # The threads should already have stopped, so this is just for cleanup.
+    stdout_thread.join()
+    stderr_thread.join()
+
+    stdout.close()
+    stderr.close()
+
+def parse_options(argv):
+  usage = 'Usage: %prog [options]'
+  desc = 'Example: %prog'
+  parser = optparse.OptionParser(usage=usage, description=desc)
+  parser.add_option('-d', dest='inode_dump_file', metavar='FILE',
+                    help='Dump the inode data read from a device to a file.'
+                    ' This file can then be reused with the -i option to speed'
+                    ' up future invocations of this script.')
+  parser.add_option('-i', dest='inode_data_file', metavar='FILE',
+                    help='Read cached inode data from a file saved arlier with the'
+                    ' -d option.')
+  parser.add_option('-s', '--serial', dest='device_serial', type='string',
+                    help='adb device serial number')
+  parser.add_option('-f', dest='trace_file', metavar='FILE',
+                    help='Show stats from a trace file, instead of running live.')
+  parser.add_option('-a', dest='app_name', type='string',
+                    help='filter a particular app')
+
+  options, categories = parser.parse_args(argv[1:])
+  if options.inode_dump_file and options.inode_data_file:
+    parser.error('options -d and -i can\'t be used at the same time')
+  return (options, categories)
+
+def main():
+  options, categories = parse_options(sys.argv)
+
+  # Load inode data for this device
+  inode_data = get_inode_data(options.inode_data_file, options.inode_dump_file,
+      options.device_serial)
+  # Build (dev, inode) -> filename hash
+  inode_lookup_table = build_inode_lookup_table(inode_data)
+  # Init pagecache stats
+  pagecache_stats = PagecacheStats(inode_lookup_table)
+
+  if options.trace_file is not None:
+    if not os.path.isfile(options.trace_file):
+      print >> sys.stderr, ('Couldn\'t load trace file.')
+      sys.exit(1)
+    trace_file = open(options.trace_file, 'r')
+    read_and_parse_trace_file(trace_file, pagecache_stats, options.app_name)
+  else:
+    # Construct and execute trace command
+    trace_cmd = AdbUtils.construct_adb_shell_command(['atrace', '--stream', 'pagecache'],
+        options.device_serial)
+
+    try:
+      atrace = subprocess.Popen(trace_cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+          stderr=subprocess.PIPE)
+    except OSError as error:
+      print >> sys.stderr, ('The command failed')
+      sys.exit(1)
+
+    read_and_parse_trace_data_live(atrace.stdout, atrace.stderr, pagecache_stats, options.app_name)
+
+if __name__ == "__main__":
+  main()
diff --git a/procrank/procrank.cpp b/procrank/procrank.cpp
index cc8c2f1..36f4359 100644
--- a/procrank/procrank.cpp
+++ b/procrank/procrank.cpp
@@ -153,6 +153,35 @@
     }
 }
 
+static uint64_t get_zram_mem_used() {
+#define ZRAM_SYSFS "/sys/block/zram0/"
+    FILE *f = fopen(ZRAM_SYSFS "mm_stat", "r");
+    if (f) {
+        uint64_t mem_used_total = 0;
+
+        int matched = fscanf(f, "%*d %*d %" SCNu64 " %*d %*d %*d %*d", &mem_used_total);
+        if (matched != 1)
+            fprintf(stderr, "warning: failed to parse " ZRAM_SYSFS "mm_stat\n");
+
+        fclose(f);
+        return mem_used_total;
+    }
+
+    f = fopen(ZRAM_SYSFS "mem_used_total", "r");
+    if (f) {
+        uint64_t mem_used_total = 0;
+
+        int matched = fscanf(f, "%" SCNu64, &mem_used_total);
+        if (matched != 1)
+            fprintf(stderr, "warning: failed to parse " ZRAM_SYSFS "mem_used_total\n");
+
+        fclose(f);
+        return mem_used_total;
+    }
+
+    return 0;
+}
+
 int main(int argc, char *argv[]) {
     pm_kernel_t *ker;
     pm_process_t *proc;
@@ -180,8 +209,6 @@
 
     uint64_t mem[MEMINFO_COUNT] = { };
     pm_proportional_swap_t *p_swap;
-    int fd, len;
-    char buffer[1024];
     float zram_cr = 0.0;
 
     signal(SIGPIPE, SIG_IGN);
@@ -275,17 +302,12 @@
     qsort(procs.data(), procs.size(), sizeof(procs[0]), compfn);
 
     if (has_swap) {
-        fd = open("/sys/block/zram0/mem_used_total", O_RDONLY);
-        if (fd >= 0) {
-            len = read(fd, buffer, sizeof(buffer)-1);
-            close(fd);
-            if (len > 0) {
-                buffer[len] = 0;
-                mem[MEMINFO_ZRAM_TOTAL] = atoll(buffer)/1024;
-                zram_cr = (float) mem[MEMINFO_ZRAM_TOTAL] /
-                        (mem[MEMINFO_SWAP_TOTAL] - mem[MEMINFO_SWAP_FREE]);
-                has_zram = true;
-            }
+        uint64_t zram_mem_used = get_zram_mem_used();
+        if (zram_mem_used) {
+            mem[MEMINFO_ZRAM_TOTAL] = zram_mem_used/1024;
+            zram_cr = (float) mem[MEMINFO_ZRAM_TOTAL] /
+                    (mem[MEMINFO_SWAP_TOTAL] - mem[MEMINFO_SWAP_FREE]);
+            has_zram = true;
         }
     }
 
diff --git a/showmap/showmap.cpp b/showmap/showmap.cpp
index d7d41b6..6efe260 100644
--- a/showmap/showmap.cpp
+++ b/showmap/showmap.cpp
@@ -25,6 +25,11 @@
     char name[1];
 };
 
+static bool verbose = false;
+static bool terse = false;
+static bool addresses = false;
+static bool quiet = false;
+
 static int is_library(const char *name) {
     int len = strlen(name);
     return len >= 4 && name[0] == '/'
@@ -173,7 +178,7 @@
     snprintf(fn, sizeof(fn), "/proc/%d/smaps", pid);
     fp = fopen(fn, "r");
     if (fp == 0) {
-        fprintf(stderr, "cannot open /proc/%d/smaps: %s\n", pid, strerror(errno));
+        if (!quiet) fprintf(stderr, "cannot open /proc/%d/smaps: %s\n", pid, strerror(errno));
         return NULL;
     }
 
@@ -202,17 +207,13 @@
     fclose(fp);
 
     if (!head) {
-        fprintf(stderr, "could not read /proc/%d/smaps\n", pid);
+        if (!quiet) fprintf(stderr, "could not read /proc/%d/smaps\n", pid);
         return NULL;
     }
 
     return head;
 }
 
-static bool verbose = false;
-static bool terse = false;
-static bool addresses = false;
-
 static void print_header()
 {
     const char *addr1 = addresses ? "   start      end " : "";
@@ -264,7 +265,7 @@
 
     mapinfo *milist = load_maps(pid, addresses, !verbose && !addresses);
     if (milist == NULL) {
-        return 1;
+        return quiet ? 0 : 1;
     }
 
     print_header();
@@ -282,7 +283,7 @@
         total.pss += mi->pss;
         total.size += mi->size;
         total.count += mi->count;
-        
+
         if (terse && !mi->private_dirty) {
             goto out;
         }
@@ -328,6 +329,10 @@
             addresses = true;
             continue;
         }
+        if (!strcmp(arg,"-q")) {
+            quiet = true;
+            continue;
+        }
         if (argc != 1) {
             fprintf(stderr, "too many arguments\n");
             break;
@@ -346,10 +351,11 @@
 
     if (usage) {
         fprintf(stderr,
-                "showmap [-t] [-v] [-c] <pid>\n"
+                "showmap [-t] [-v] [-c] [-q] <pid>\n"
                 "        -t = terse (show only items with private pages)\n"
                 "        -v = verbose (don't coalesce maps with the same name)\n"
                 "        -a = addresses (show virtual memory map)\n"
+                "        -q = quiet (don't show error if map could not be read)\n"
                 );
         result = 1;
     }
diff --git a/simpleperf/cmd_record_test.cpp b/simpleperf/cmd_record_test.cpp
index afd5bdd..e0e1481 100644
--- a/simpleperf/cmd_record_test.cpp
+++ b/simpleperf/cmd_record_test.cpp
@@ -185,8 +185,7 @@
   CreateProcesses(2, &workloads);
   std::string pid_list = android::base::StringPrintf(
       "%d,%d", workloads[0]->GetPid(), workloads[1]->GetPid());
-  TemporaryFile tmpfile;
-  ASSERT_TRUE(RecordCmd()->Run({"-p", pid_list, "-o", tmpfile.path}));
+  ASSERT_TRUE(RunRecordCmd({"-p", pid_list}));
 }
 
 TEST(record_cmd, existing_threads) {
@@ -195,8 +194,7 @@
   // Process id can also be used as thread id in linux.
   std::string tid_list = android::base::StringPrintf(
       "%d,%d", workloads[0]->GetPid(), workloads[1]->GetPid());
-  TemporaryFile tmpfile;
-  ASSERT_TRUE(RecordCmd()->Run({"-t", tid_list, "-o", tmpfile.path}));
+  ASSERT_TRUE(RunRecordCmd({"-t", tid_list}));
 }
 
 TEST(record_cmd, no_monitored_threads) { ASSERT_FALSE(RecordCmd()->Run({""})); }
diff --git a/simpleperf/cmd_stat_test.cpp b/simpleperf/cmd_stat_test.cpp
index 4d63498..684e50f 100644
--- a/simpleperf/cmd_stat_test.cpp
+++ b/simpleperf/cmd_stat_test.cpp
@@ -56,7 +56,8 @@
                      std::vector<std::unique_ptr<Workload>>* workloads) {
   workloads->clear();
   for (size_t i = 0; i < count; ++i) {
-    auto workload = Workload::CreateWorkload({"sleep", "1"});
+    // Create a workload runs longer than profiling time.
+    auto workload = Workload::CreateWorkload({"sleep", "1000"});
     ASSERT_TRUE(workload != nullptr);
     ASSERT_TRUE(workload->Start());
     workloads->push_back(std::move(workload));
@@ -68,7 +69,7 @@
   CreateProcesses(2, &workloads);
   std::string pid_list = android::base::StringPrintf(
       "%d,%d", workloads[0]->GetPid(), workloads[1]->GetPid());
-  ASSERT_TRUE(StatCmd()->Run({"-p", pid_list}));
+  ASSERT_TRUE(StatCmd()->Run({"-p", pid_list, "sleep", "1"}));
 }
 
 TEST(stat_cmd, existing_threads) {
@@ -77,7 +78,7 @@
   // Process id can be used as thread id in linux.
   std::string tid_list = android::base::StringPrintf(
       "%d,%d", workloads[0]->GetPid(), workloads[1]->GetPid());
-  ASSERT_TRUE(StatCmd()->Run({"-t", tid_list}));
+  ASSERT_TRUE(StatCmd()->Run({"-t", tid_list, "sleep", "1"}));
 }
 
 TEST(stat_cmd, no_monitored_threads) { ASSERT_FALSE(StatCmd()->Run({""})); }
diff --git a/squashfs_utils/mksquashfsimage.sh b/squashfs_utils/mksquashfsimage.sh
index 5f8cfea..5f45a64 100755
--- a/squashfs_utils/mksquashfsimage.sh
+++ b/squashfs_utils/mksquashfsimage.sh
@@ -5,7 +5,7 @@
 function usage() {
 cat<<EOT
 Usage:
-${0##*/} SRC_DIR OUTPUT_FILE [-s] [-m MOUNT_POINT] [-d PRODUCT_OUT] [-C FS_CONFIG ] [-c FILE_CONTEXTS] [-b BLOCK_SIZE] [-z COMPRESSOR] [-zo COMPRESSOR_OPT]
+${0##*/} SRC_DIR OUTPUT_FILE [-s] [-m MOUNT_POINT] [-d PRODUCT_OUT] [-C FS_CONFIG ] [-c FILE_CONTEXTS] [-B BLOCK_MAP_FILE] [-b BLOCK_SIZE] [-z COMPRESSOR] [-zo COMPRESSOR_OPT] [-a ]
 EOT
 }
 
@@ -54,6 +54,12 @@
     shift; shift
 fi
 
+BLOCK_MAP_FILE=
+if [[ "$1" == "-B" ]]; then
+    BLOCK_MAP_FILE=$2
+    shift; shift
+fi
+
 BLOCK_SIZE=131072
 if [[ "$1" == "-b" ]]; then
     BLOCK_SIZE=$2
@@ -73,6 +79,12 @@
     shift; shift
 fi
 
+DISABLE_4K_ALIGN=false
+if [[ "$1" == "-a" ]]; then
+    DISABLE_4K_ALIGN=true
+    shift;
+fi
+
 OPT=""
 if [ -n "$MOUNT_POINT" ]; then
   OPT="$OPT -mount-point $MOUNT_POINT"
@@ -86,11 +98,17 @@
 if [ -n "$FILE_CONTEXTS" ]; then
   OPT="$OPT -context-file $FILE_CONTEXTS"
 fi
+if [ -n "$BLOCK_MAP_FILE" ]; then
+  OPT="$OPT -block-map $BLOCK_MAP_FILE"
+fi
 if [ -n "$BLOCK_SIZE" ]; then
   OPT="$OPT -b $BLOCK_SIZE"
 fi
+if [ "$DISABLE_4K_ALIGN" = true ]; then
+  OPT="$OPT -disable-4k-align"
+fi
 
-MAKE_SQUASHFS_CMD="mksquashfs $SRC_DIR/ $OUTPUT_FILE -no-progress -comp $COMPRESSOR $COMPRESSOR_OPT -no-exports -noappend -no-recovery -android-fs-config $OPT"
+MAKE_SQUASHFS_CMD="mksquashfs $SRC_DIR/ $OUTPUT_FILE -no-progress -comp $COMPRESSOR $COMPRESSOR_OPT -no-exports -noappend -no-recovery -no-fragments -no-duplicates -android-fs-config $OPT"
 echo $MAKE_SQUASHFS_CMD
 $MAKE_SQUASHFS_CMD
 
diff --git a/tests/binder/benchmarks/Android.mk b/tests/binder/benchmarks/Android.mk
index eb2ead2..fa7629c 100644
--- a/tests/binder/benchmarks/Android.mk
+++ b/tests/binder/benchmarks/Android.mk
@@ -20,7 +20,6 @@
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
 
 LOCAL_MODULE_TAGS := eng tests
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/nativebenchmark
 
 LOCAL_STATIC_LIBRARIES += \
     libtestUtil
@@ -37,4 +36,4 @@
 LOCAL_MODULE := binderAddInts
 LOCAL_SRC_FILES := binderAddInts.cpp
 
-include $(BUILD_EXECUTABLE)
+include $(BUILD_NATIVE_BENCHMARK)
diff --git a/tests/binder/benchmarks/binderAddInts.cpp b/tests/binder/benchmarks/binderAddInts.cpp
index f061f7c..afa464a 100644
--- a/tests/binder/benchmarks/binderAddInts.cpp
+++ b/tests/binder/benchmarks/binderAddInts.cpp
@@ -16,26 +16,14 @@
  */
 
 /*
- * Binder add integers benchmark
+ * Binder add integers benchmark (Using google-benchmark library)
  *
- * Measures the rate at which a short binder IPC operation can be
- * performed.  The operation consists of the client sending a parcel
- * that contains two integers.  For each parcel that the server
- * receives, it adds the two integers and sends the sum back to
- * the client.
- *
- * This benchmark supports the following command-line options:
- *
- *   -c cpu - bind client to specified cpu (default: unbound)
- *   -s cpu - bind server to specified cpu (default: unbound)
- *   -n num - perform IPC operation num times (default: 1000)
- *   -d time - delay specified amount of seconds after each
- *             IPC operation. (default 1e-3)
  */
 
 #include <cerrno>
 #include <grp.h>
 #include <iostream>
+#include <iomanip>
 #include <libgen.h>
 #include <time.h>
 #include <unistd.h>
@@ -48,6 +36,9 @@
 #include <binder/IPCThreadState.h>
 #include <binder/ProcessState.h>
 #include <binder/IServiceManager.h>
+
+#include <benchmark/benchmark.h>
+
 #include <utils/Log.h>
 #include <testUtil.h>
 
@@ -61,13 +52,11 @@
 struct options {
     int serverCPU;
     int clientCPU;
-    unsigned int iterations;
     float        iterDelay; // End of iteration delay in seconds
 } options = { // Set defaults
     unbound, // Server CPU
     unbound, // Client CPU
-    1000,    // Iterations
-    1e-3,    // End of iteration delay
+    0.0,    // End of iteration delay
 };
 
 class AddIntsService : public BBinder
@@ -89,137 +78,13 @@
 };
 
 // File scope function prototypes
-static void server(void);
-static void client(void);
+static bool server(void);
+static void BM_client(benchmark::State& state);
 static void bindCPU(unsigned int cpu);
 static ostream &operator<<(ostream &stream, const String16& str);
 static ostream &operator<<(ostream &stream, const cpu_set_t& set);
 
-int main(int argc, char *argv[])
-{
-    int rv;
-
-    // Determine CPUs available for use.
-    // This testcase limits its self to using CPUs that were
-    // available at the start of the benchmark.
-    cpu_set_t availCPUs;
-    if ((rv = sched_getaffinity(0, sizeof(availCPUs), &availCPUs)) != 0) {
-        cerr << "sched_getaffinity failure, rv: " << rv
-            << " errno: " << errno << endl;
-        exit(1);
-    }
-
-    // Parse command line arguments
-    int opt;
-    while ((opt = getopt(argc, argv, "s:c:n:d:?")) != -1) {
-        char *chptr; // character pointer for command-line parsing
-
-        switch (opt) {
-        case 'c': // client CPU
-        case 's': { // server CPU
-            // Parse the CPU number
-            int cpu = strtoul(optarg, &chptr, 10);
-            if (*chptr != '\0') {
-                cerr << "Invalid cpu specified for -" << (char) opt
-                    << " option of: " << optarg << endl;
-                exit(2);
-            }
-
-            // Is the CPU available?
-            if (!CPU_ISSET(cpu, &availCPUs)) {
-                cerr << "CPU " << optarg << " not currently available" << endl;
-                cerr << "  Available CPUs: " << availCPUs << endl;
-                exit(3);
-            }
-
-            // Record the choice
-            *((opt == 'c') ? &options.clientCPU : &options.serverCPU) = cpu;
-            break;
-        }
-
-        case 'n': // iterations
-            options.iterations = strtoul(optarg, &chptr, 10);
-            if (*chptr != '\0') {
-                cerr << "Invalid iterations specified of: " << optarg << endl;
-                exit(4);
-            }
-            if (options.iterations < 1) {
-                cerr << "Less than 1 iteration specified by: "
-                    << optarg << endl;
-                exit(5);
-            }
-            break;
-
-        case 'd': // Delay between each iteration
-            options.iterDelay = strtod(optarg, &chptr);
-            if ((*chptr != '\0') || (options.iterDelay < 0.0)) {
-                cerr << "Invalid delay specified of: " << optarg << endl;
-                exit(6);
-            }
-            break;
-
-        case '?':
-        default:
-            cerr << basename(argv[0]) << " [options]" << endl;
-            cerr << "  options:" << endl;
-            cerr << "    -s cpu - server CPU number" << endl;
-            cerr << "    -c cpu - client CPU number" << endl;
-            cerr << "    -n num - iterations" << endl;
-            cerr << "    -d time - delay after operation in seconds" << endl;
-            exit(((optopt == 0) || (optopt == '?')) ? 0 : 7);
-        }
-    }
-
-    // Display selected options
-    cout << "serverCPU: ";
-    if (options.serverCPU == unbound) {
-        cout << " unbound";
-    } else {
-        cout << options.serverCPU;
-    }
-    cout << endl;
-    cout << "clientCPU: ";
-    if (options.clientCPU == unbound) {
-        cout << " unbound";
-    } else {
-        cout << options.clientCPU;
-    }
-    cout << endl;
-    cout << "iterations: " << options.iterations << endl;
-    cout << "iterDelay: " << options.iterDelay << endl;
-
-    // Fork client, use this process as server
-    fflush(stdout);
-    switch (pid_t pid = fork()) {
-    case 0: // Child
-        client();
-        return 0;
-
-    default: // Parent
-        server();
-
-        // Wait for all children to end
-        do {
-            int stat;
-            rv = wait(&stat);
-            if ((rv == -1) && (errno == ECHILD)) { break; }
-            if (rv == -1) {
-                cerr << "wait failed, rv: " << rv << " errno: "
-                    << errno << endl;
-                perror(NULL);
-                exit(8);
-            }
-        } while (1);
-        return 0;
-
-    case -1: // Error
-        exit(9);
-    }
-
-    return 0;
-}
-
-static void server(void)
+static bool server(void)
 {
     int rv;
 
@@ -230,63 +95,62 @@
         new AddIntsService(options.serverCPU))) != 0) {
         cerr << "addService " << serviceName << " failed, rv: " << rv
             << " errno: " << errno << endl;
+        return false;
     }
 
     // Start threads to handle server work
     proc->startThreadPool();
+    return true;
 }
 
-static void client(void)
+static void BM_client(benchmark::State& state)
 {
+    server();
     int rv;
     sp<IServiceManager> sm = defaultServiceManager();
-    double min = FLT_MAX, max = 0.0, total = 0.0; // Time in seconds for all
-                                                  // the IPC calls.
 
     // If needed bind to client CPU
     if (options.clientCPU != unbound) { bindCPU(options.clientCPU); }
 
     // Attach to service
     sp<IBinder> binder;
-    do {
+    for (int i = 0; i < 3; i++) {
         binder = sm->getService(serviceName);
         if (binder != 0) break;
         cout << serviceName << " not published, waiting..." << endl;
         usleep(500000); // 0.5 s
-    } while(true);
+    }
 
-    // Perform the IPC operations
-    for (unsigned int iter = 0; iter < options.iterations; iter++) {
+    if (binder == 0) {
+        cout << serviceName << " failed to publish, aborting" << endl;
+        return;
+    }
+
+    unsigned int iter = 0;
+    // Perform the IPC operations in the benchmark
+    while (state.KeepRunning()) {
         Parcel send, reply;
 
         // Create parcel to be sent.  Will use the iteration cound
         // and the iteration count + 3 as the two integer values
         // to be sent.
+        state.PauseTiming();
         int val1 = iter;
         int val2 = iter + 3;
         int expected = val1 + val2;  // Expect to get the sum back
         send.writeInt32(val1);
         send.writeInt32(val2);
-
+        state.ResumeTiming();
         // Send the parcel, while timing how long it takes for
         // the answer to return.
-        struct timespec start;
-        clock_gettime(CLOCK_MONOTONIC, &start);
         if ((rv = binder->transact(AddIntsService::ADD_INTS,
             send, &reply)) != 0) {
             cerr << "binder->transact failed, rv: " << rv
                 << " errno: " << errno << endl;
             exit(10);
         }
-        struct timespec current;
-        clock_gettime(CLOCK_MONOTONIC, &current);
 
-        // Calculate how long this operation took and update the stats
-        struct timespec deltaTimespec = tsDelta(&start, &current);
-        double delta = ts2double(&deltaTimespec);
-        min = (delta < min) ? delta : min;
-        max = (delta > max) ? delta : max;
-        total += delta;
+        state.PauseTiming();
         int result = reply.readInt32();
         if (result != (int) (iter + iter + 3)) {
             cerr << "Unexpected result for iteration " << iter << endl;
@@ -295,14 +159,11 @@
         }
 
         if (options.iterDelay > 0.0) { testDelaySpin(options.iterDelay); }
+        state.ResumeTiming();
     }
-
-    // Display the results
-    cout << "Time per iteration min: " << min
-        << " avg: " << (total / options.iterations)
-        << " max: " << max
-        << endl;
 }
+BENCHMARK(BM_client);
+
 
 AddIntsService::AddIntsService(int cpu): cpu_(cpu) {
     if (cpu != unbound) { bindCPU(cpu); }
@@ -382,3 +243,68 @@
 
     return stream;
 }
+
+int main(int argc, char *argv[])
+{
+    int rv;
+    ::benchmark::Initialize(&argc, argv);
+    // Determine CPUs available for use.
+    // This testcase limits its self to using CPUs that were
+    // available at the start of the benchmark.
+    cpu_set_t availCPUs;
+    if ((rv = sched_getaffinity(0, sizeof(availCPUs), &availCPUs)) != 0) {
+        cerr << "sched_getaffinity failure, rv: " << rv
+            << " errno: " << errno << endl;
+        exit(1);
+    }
+
+    // Parse command line arguments
+    int opt;
+    while ((opt = getopt(argc, argv, "s:c:d:?")) != -1) {
+        char *chptr; // character pointer for command-line parsing
+
+        switch (opt) {
+        case 'c': // client CPU
+        case 's': { // server CPU
+            // Parse the CPU number
+            int cpu = strtoul(optarg, &chptr, 10);
+            if (*chptr != '\0') {
+                cerr << "Invalid cpu specified for -" << (char) opt
+                    << " option of: " << optarg << endl;
+                exit(2);
+            }
+
+            // Is the CPU available?
+            if (!CPU_ISSET(cpu, &availCPUs)) {
+                cerr << "CPU " << optarg << " not currently available" << endl;
+                cerr << "  Available CPUs: " << availCPUs << endl;
+                exit(3);
+            }
+
+            // Record the choice
+            *((opt == 'c') ? &options.clientCPU : &options.serverCPU) = cpu;
+            break;
+        }
+
+        case 'd': // delay between each iteration
+            options.iterDelay = strtod(optarg, &chptr);
+            if ((*chptr != '\0') || (options.iterDelay < 0.0)) {
+                cerr << "Invalid delay specified of: " << optarg << endl;
+                exit(6);
+            }
+            break;
+
+        case '?':
+        default:
+            cerr << basename(argv[0]) << " [options]" << endl;
+            cerr << "  options:" << endl;
+            cerr << "    -s cpu - server CPU number" << endl;
+            cerr << "    -c cpu - client CPU number" << endl;
+            cerr << "    -d time - delay after operation in seconds" << endl;
+            exit(((optopt == 0) || (optopt == '?')) ? 0 : 7);
+        }
+    }
+
+    ::benchmark::RunSpecifiedBenchmarks();
+}
+
diff --git a/tests/workloads/capture.sh b/tests/workloads/capture.sh
index 3b2f446..9c3eadc 100755
--- a/tests/workloads/capture.sh
+++ b/tests/workloads/capture.sh
Binary files differ
diff --git a/tests/workloads/chromefling.sh b/tests/workloads/chromefling.sh
new file mode 100755
index 0000000..db6aa2e
--- /dev/null
+++ b/tests/workloads/chromefling.sh
@@ -0,0 +1,153 @@
+#
+# Script to start 3 chrome tabs, fling each of them, repeat
+# For each iteration, Total frames and janky frames are reported.
+#
+# Options are described below.
+#
+iterations=10
+startapps=1
+capturesystrace=0
+waittime=4
+app=chrome
+
+function processLocalOption {
+	ret=0
+	case "$1" in
+	(-N) startapps=0;;
+	(-A) unset appList;;
+	(-L) appList=$2; shift; ret=1;;
+	(-T) capturesystrace=1;;
+	(-W) waittime=$2; shift; ret=1;;
+	(*)
+		echo "$0: unrecognized option: $1"
+		echo; echo "Usage: $0 [options]"
+		echo "-A : use all known applications"
+		echo "-L applist : list of applications"
+		echo "   default: $appList"
+		echo "-N : no app startups, just fling"
+		echo "-g : generate activity strings"
+		echo "-i iterations"
+		echo "-T : capture systrace on each iteration"
+		echo "-d device : device type (shamu, volantis, bullhead,...)"
+		exit 1;;
+	esac
+	return $ret
+}
+
+CMDDIR=$(dirname $0 2>/dev/null)
+CMDDIR=${CMDDIR:=.}
+. $CMDDIR/defs.sh
+
+case $DEVICE in
+(hammerhead)
+	flingtime=300
+	downCount=2
+	upCount=6
+	UP="70 400 70 100 $flingtime"
+	DOWN="70 100 70 400 $flingtime";;
+(shamu)
+	flingtime=100
+	downCount=2 
+	upCount=2
+	UP="700 1847 700 400 $flingtime"
+	DOWN="700 400 700 1847 $flingtime";;
+(angler)
+	flingtime=150
+	downCount=4
+	upCount=3
+	UP="500 1200 500 550 $flingtime"
+	DOWN="500 550 500 1200 $flingtime";;
+(bullhead|volantis)
+	flingtime=200
+	downCount=5
+	upCount=5
+	UP="500 1400 500 400 $flingtime"
+	DOWN="500 400 500 1400 $flingtime";;
+(ariel)
+	flingtime=200
+	downCount=5
+	upCount=5
+	UP="500 1560 500 530 $flingtime"
+	DOWN="500 530 500 1560 $flingtime";;
+(*)
+	echo "Error: No display information available for $DEVICE"
+	exit 1;;
+esac
+
+function swipe {
+	count=0
+	while [ $count -lt $2 ]
+	do
+		doSwipe $1
+		((count=count+1))
+	done
+	sleep 1
+}
+
+cur=1
+frameSum=0
+jankSum=0
+latency90Sum=0
+latency95Sum=0
+latency99Sum=0
+
+doKeyevent HOME
+sleep 0.5
+resetJankyFrames $(getPackageName $app)
+
+while [ $cur -le $iterations ]
+do
+	if [ $capturesystrace -gt 0 ]; then
+		${ADB}atrace --async_start -z -c -b 16000 freq gfx view idle sched
+	fi
+	t=$(startActivity $app)
+	sleep $waittime
+	swipe "$UP" $upCount
+
+	sleep $waittime
+	swipe "$DOWN" $downCount
+
+	doKeyevent BACK
+	sleep 0.5
+
+	if [ $capturesystrace -gt 0 ]; then
+		${ADB}atrace --async_dump -z -c -b 16000 freq gfx view idle sched > trace.${cur}.out
+	fi
+	doKeyevent HOME
+	sleep 0.5
+
+	set -- $(getJankyFrames $(getPackageName $app))
+	totalDiff=$1
+	jankyDiff=$2
+	latency90=$3
+	latency95=$4
+	latency99=$5
+	if [ ${totalDiff:=0} -eq 0 ]; then
+		echo Error: could not read frame info with \"dumpsys gfxinfo\"
+		exit 1
+	fi
+
+	((frameSum=frameSum+totalDiff))
+	((jankSum=jankSum+jankyDiff))
+	((latency90Sum=latency90Sum+latency90))
+	((latency95Sum=latency95Sum+latency95))
+	((latency99Sum=latency99Sum+latency99))
+	if [ "$totalDiff" -eq 0 ]; then
+		echo Error: no frames detected. Is the display off?
+		exit 1
+	fi
+	((jankPct=jankyDiff*100/totalDiff))
+	resetJankyFrames $(getPackageName $app)
+
+
+	echo Frames: $totalDiff latency: $latency90/$latency95/$latency99 Janks: $jankyDiff\(${jankPct}%\)
+	((cur=cur+1))
+done
+doKeyevent HOME
+((aveJankPct=jankSum*100/frameSum))
+((aveJanks=jankSum/iterations))
+((aveFrames=frameSum/iterations))
+((aveLatency90=latency90Sum/iterations))
+((aveLatency95=latency95Sum/iterations))
+((aveLatency99=latency99Sum/iterations))
+echo AVE: Frames: $aveFrames latency: $aveLatency90/$aveLatency95/$aveLatency99 Janks: $aveJanks\(${aveJankPct}%\)
diff --git a/tests/workloads/defs.sh b/tests/workloads/defs.sh
index a2b7138..400722b 100755
--- a/tests/workloads/defs.sh
+++ b/tests/workloads/defs.sh
Binary files differ
diff --git a/tests/workloads/powerave.py b/tests/workloads/powerave.py
new file mode 100755
index 0000000..4d786b9
--- /dev/null
+++ b/tests/workloads/powerave.py
@@ -0,0 +1,46 @@
+#!/usr/bin/python
+
+import sys
+import getopt
+
+def usage():
+    print "powersum.py [OPTIONS] HZ VOLTAGE [FILE]"
+    print "OPTIONS: "
+    print "-o OFFSET: subtract OFFSET from all data points"
+    print "\nHZ: samples per second in FILE or stdin"
+    sys.exit(0)
+
+offset = 0.0
+voltage = 4.3
+
+parsedargv,argvrem = getopt.getopt(sys.argv[1:], "vo:w:l:h", ["help"])
+for o,a in parsedargv:
+    if o == '-o': offset = float(a)
+    if o == '-h' or o == '--help': usage()
+
+hz = float(argvrem[0])
+voltage = float(argvrem[1])
+if len(argvrem) > 1:
+    f = open(argvrem[2], "r")
+else:
+    f = sys.stdin
+
+totalpower = 0.0
+samplectr = 0
+
+for line in f:
+    try:
+        val = float(line.split(" ")[1]) # xxx take 2nd arg in line
+        val -= offset
+    except:
+        print "Can't parse data line, did you remember the timestamp?"
+        print "data was: %s" % line
+        sys.exit(1)
+
+    samplectr+=1
+    totalpower += val/hz
+
+avecurrent = totalpower * hz *1000 / samplectr
+avepower = avecurrent * voltage
+
+print "%.3f %.3f" % (avecurrent, avepower)
diff --git a/tests/workloads/pwrsummary.sh b/tests/workloads/pwrsummary.sh
new file mode 100755
index 0000000..3d3aeb8
--- /dev/null
+++ b/tests/workloads/pwrsummary.sh
@@ -0,0 +1,235 @@
+# print summary of output generated by pwrtest.sh
+#
+# default results directories are <device>-<date>[-experiment]. By default
+# match any device and the year 201*.
+#
+# Examples:
+#
+# - show output for all bullhead tests in july 2015:
+#    ./pwrsummary.sh -r "bh-201507*"
+#
+# - generate CSV file for import into spreadsheet:
+#    ./pwrsummary.sh -o csv
+#
+
+CMDDIR=$(dirname $0 2>/dev/null)
+CMDDIR=${CMDDIR:=.}
+cd $CMDDIR
+CMDDIR=$(pwd)
+cd -
+POWERAVE="python $CMDDIR/powerave.py"
+
+defaultPattern="*-201*"
+defaultVoltage=4.3
+defaultFrequency=5
+
+function Usage {
+	echo "$0 [-o format] [-v voltage] [-h freq] [-f resultsDirectories]"
+}
+
+while [ $# -gt 0 ]
+do
+	case "$1" in
+	(-o) format=$2; shift;;
+	(-v) voltage=$2; shift;;
+	(-h) hz=$2; shift;;
+	(-r) testResults="$2"; shift;;
+	(--help) Usage; exit 0;;
+	(--) shift; break;;
+	(*)
+		echo Unknown option: $1
+		Usage
+		exit 1;;
+	esac
+	shift
+done
+
+testResults=${testResults:=$defaultPattern}
+voltage=${voltage:=$defaultVoltage}
+hz=${hz:=$defaultFrequency}
+
+function printHeader {
+	workload=$1
+	units="unknown"
+	case $workload in
+	(suntemple|shadowgrid2)
+		units="FPS";;
+	(recentfling|youtube|chrome)
+		units="FPS from app point of view: 1/(90th percentile render time)";;
+	(sysapps)
+		units="App start/switch per second";;
+	esac
+
+	echo "Performance unit for $workload is: $units"
+	if [ "$format" = csv ]; then
+		printf "%s,%s,%s,%s,%s,%s,%s,%s,%s\n" " " build min ave max net-mA@${voltage}v base-mW net-mW perf/W
+	else
+		printf "%-30s %-8s %12.12s %12.12s %12.12s %12.12s %12.12s %12.12s %12.12s\n" " " build min ave max net-mA@${voltage}v base-mW net-mW perf/W
+	fi
+}
+
+function average {
+	awk 'BEGIN { count=0; sum=0; max=-1000000000; min=1000000000; }
+	{
+		cur = $1;
+		sum = sum + cur; 
+		if (cur > max) max = cur;
+		if (cur < min) min = cur;
+		count++;
+	}
+
+	END {
+		if (count > 0) {
+			ave = sum / count;
+			printf "%.2f %.2f %.2f\n", min, ave, max; 
+		}
+	}'
+}
+
+function hwuiOutputParser {
+	# Stats since: 60659316905953ns
+	# Total frames rendered: 150
+	# Janky frames: 89 (59.33%)
+	# 90th percentile: 23ms
+	# 95th percentile: 27ms
+	# 99th percentile: 32ms
+	# Number Missed Vsync: 0
+	# Number High input latency: 0
+	# Number Slow UI thread: 0
+	# Number Slow bitmap uploads: 12
+	# Number Slow draw: 89
+	# use with "stdbuf -o0 " to disable pipe buffering
+	# stdbuf -o0 adb shell /data/hwuitest shadowgrid2 400 | stdbuf -o0 ./hwuitestfilter.sh  | tee t.csv
+	sed -e 's/ns//' -e 's/[\(\)%]/ /g' | awk '
+	BEGIN { startTime=0; lastTime=0; }
+	/^Stats since:/ {
+		curTime = $3;
+		if (startTime == 0) {
+			startTime = curTime;
+		}
+		if (lastTime) {
+			interval = curTime - lastTime;
+			fps = totalFrames*1000000000 / interval;
+			diffTime = curTime - startTime;
+			printf "%.2f, %.2f, ",diffTime/1000000, fps;
+		}
+	}
+	/^Total frames/ { totalFrames=$4; }
+	/^Janky frames:/ {
+		if (lastTime) {
+			printf "%.2f\n",$4; lastTime=curTime;
+		}
+		lastTime = curTime;
+	}'
+}
+
+function sysappOutputParser {
+	awk '
+	BEGIN { fmt=0; count=0; sum=0; }
+	/^App/ {
+		if (count != 0) {
+			if (fmt > 2) printf "Ave: %0.2fms\n", sum/count;
+			else printf " %0.2f\n", sum/count;
+			count = 0;
+			sum = 0;
+		}
+	}
+	/^[a-z]/ { val=$2; if (val != 0) { count++; sum+=val; } }
+	/^Iteration/ { if (fmt > 2) printf "%s : ", $0; else if (fmt) printf "%d ", $2; }
+	'
+}
+
+function calcPerfData {
+	testdir=$1
+	workload=$2
+	baselineCurrent=$3
+	baselinePower=$4
+
+	file=${workload}.out
+	powerfile=${workload}-power.out
+	build="$(cat build 2>/dev/null)"
+	build=${build:="Unknown"}
+
+	lines=$(wc -l $file 2>/dev/null | cut -f1 -d\ )
+
+	if [ ${lines:=0} -eq -0 ]; then
+		# No performance data captured
+		if [ "$format" = csv ]; then
+			printf "%s,%s,%s\n" $testdir "$build" "no data"
+		else
+			printf "%-30s %-8s %12.12s\n" $testdir "$build" "no data"
+		fi
+		return 1
+	fi
+
+	set -- $($POWERAVE $hz $voltage $powerfile)
+	current=$(echo $1 $baselineCurrent | awk '{ printf "%.2f", $1-$2; }')
+	power=$(echo $2 $baselinePower | awk '{ printf "%.2f", $1-$2; }')
+
+	case $workload in
+	(idle)
+		set -- 0 0 0
+		;;
+	(suntemple)
+		# units are fps
+		set -- $(grep "FPS average" $file  | sed 's/^.*seconds for a //' | awk '{ print $1; }' | average)
+		;;
+	(recentfling|youtube|chrome)
+		# units are ms, so need to convert to app/ms
+		set -- $(grep ^Frames:  $file | tr "/" " " | awk '{ print $4; }' | average | awk '{ printf "%.3f %.3f %.3f\n", 1000/$3, 1000/$2, 1000/$1;}'  )
+		;;
+	(sysapps)
+		# units are ms, so need to convert to app/ms
+		set -- $(cat $file | sysappOutputParser | average | awk '{ printf "%.3f %.3f %.3f\n", 1000/$3, 1000/$2, 1000/$1;}'  )
+		;;
+	(shadowgrid2)
+		# units are fps
+		set -- $(cat $file | hwuiOutputParser | tr ',' ' ' | awk '{print $2;}' | average)
+		;;
+	esac
+
+	minperf=$1
+	aveperf=$2
+	maxperf=$3
+	perfPerWatt=$(echo $aveperf $power | awk '{ if ($2) { val=$1*1000/$2; printf "%.3f\n", val; } else print "unknown"; }')
+	if [ "$format" = csv ]; then
+		printf "%s,%s,%f,%f,%f,%f,%f,%f," $testdir "$build" $minperf $aveperf $maxperf $current $baselinePower $power
+		printf "%s\n" $perfPerWatt
+	else
+		printf "%-30s %-8s %12.2f %12.2f %12.2f %12.2f %12.2f %12.2f " $testdir "$build" $minperf $aveperf $maxperf $current $baselinePower $power
+		printf "%12s\n" $perfPerWatt
+	fi
+}
+
+function calcBaselinePower {
+	workload=$1
+	defaultPowerFile="idle-display-power.out"
+	powerFile=$defaultPowerFile
+	case $workload in
+	(shadowgrid2|suntemple|recentfling)
+		powerFile="idle-airplane-display-power.out"
+		if [ ! -f  $powerFile ]; then
+			powerFile=$defaultPowerFile
+		fi;;
+	esac
+	if [ -f  $powerFile ]; then
+		$POWERAVE 5 4.3 $powerFile
+	fi
+}
+
+for t in $(cat tests)
+do
+	echo .======================= $t ================================
+	printHeader $t
+	for i in $testResults
+	do
+		cd $i
+		baseline="$(calcBaselinePower $t)"
+		if [ "$baseline" != "" ]; then
+	       		calcPerfData $i $t $baseline
+		else
+			echo "$i : no baseline current"
+		fi
+		cd - > /dev/null
+	done
+done
diff --git a/tests/workloads/pwrtest.sh b/tests/workloads/pwrtest.sh
new file mode 100755
index 0000000..39f7b11
--- /dev/null
+++ b/tests/workloads/pwrtest.sh
@@ -0,0 +1,365 @@
+# Script to gather perf and perf/watt data for several workloads
+#
+# Setup:
+#
+# - device connected to monsoon with USB passthrough enabled
+# - network enabled (baseline will be measured and subtracted
+#   from results) (network needed for chrome, youtube tests)
+# - the device is rebooted after each test (can be inhibited
+#   with "-r 0")
+#
+# Default behavior is to run each of the known workloads for
+# 30 minutes gathering both performance and power data.
+#
+# The default time can be overridden with the -t option. To
+# change individual test times, a config file can be specifed
+# via -f with times for individual tests. Example file contents:
+#
+#	idleTime=60
+#	recentflingTime=60
+#	chromeTime=60
+#	youtubeTime=0
+#	sysappsTime=60
+#	suntempleTime=5
+#
+# Output goes to the current directory.
+#
+# Examples:
+#
+# - Run all tests for 15 minutes (default is 30): ./pwrtest.sh -t 15 -R MDA20
+#
+# - Use a config file for test times: ./pwrtest.sh -f ./myconfig -R MDA20
+#
+# - Use a init file to setup device tuneables after each restart (this is
+#   a bash script which should include adb commands to set up device):
+#     ./pwrtest.sh -F devtunables
+#
+
+defaultTime=30
+garbageminutes=8
+
+function Usage {
+	echo "Usage: $0 [OPTIONS]"
+	echo "-d device : device type (shamu, bullhead, ...)"
+	echo "-f configFile : config file to override individual test times"
+	echo "-g garbageMinutes : time to skip power measurement at beginning of test"
+	echo "                    default=$garbagetime minutes"
+	echo "-r restart : 0=no reboot between tests, 1=reboot (default)"
+	echo "-t defaultTimeMin : default time to run each test"
+	echo "                    default=$defaultTime minutes"
+	echo "-D cmddir : directory to find defs.sh"
+	echo "-F restartHookFile : file of commands to set device tunables after restart (optional)"
+	echo "-R release : release running on device (MDA20, 2054728, etc)"
+}
+
+restart=1
+hz=5
+shadowgrid2TimeMax=25
+
+CMDDIR=$(dirname $0 2>/dev/null)
+CMDDIR=${CMDDIR:=.}
+
+MONSOON=monsoon.par
+
+while [ $# -gt 0 ]
+do
+	case "$1" in
+	(-D) CMDDIR=$2; shift;;
+	(-r) restart=$2; shift;;
+	(-t) defaultTime=$2; shift;;
+	(-F) restartfile=$2; shift;;
+	(-g) garbageminutes=$2; shift;;
+	(-f)
+		configFile=$2;
+		echo "Reading configs from $configFile..."
+		. ./$configFile
+		shift;;
+	(-R) echo $2 > ./build; shift;;
+	(--) ;;
+	(--help)
+		Usage
+		exit 0;;
+	(*)
+		echo "Unknown option: $1"
+		Usage
+		exit 1;;
+	esac
+	shift
+done
+
+. $CMDDIR/defs.sh --
+
+devdir="/data/local/tmp"
+suntempledir=${CMDDIR}/suntemple
+
+case $DEVICE in
+(shamu|hammerhead)
+	HWUITEST=hwuitest
+	onSwipe="700 1847 700 400 50"
+	;;
+(*)
+	HWUITEST=hwuitest64
+	onSwipe="500 1200 500 550 150"
+	;;
+esac
+
+scripts="defs.sh systemapps.sh recentfling.sh youtube.sh chromefling.sh $HWUITEST"
+
+if ! $MONSOON >/dev/null 2>&1; then
+	echo $MONSOON must be in your PATH >&2
+	exit 1
+fi
+
+function usbpassthru {
+	if [ "$1" = off ]; then
+		state=off
+	else
+		state=on
+	fi
+	echo Setting usb pass-thru to $state
+	monsoon.par --usbpassthrough=$state
+}
+
+function pwrcollect {
+	collectmin=$1
+	collectmin=${collectmin:=60}
+	# samples = hz * 60 * minutes
+	((samples=5*60*collectmin))
+	monsoon.par --timestamp --samples $samples --hz 5
+}
+
+function copy_files {
+	adb shell mkdir -p $devdir
+	for file in $scripts
+	do
+		adb push $CMDDIR/$file $devdir
+	done
+}
+
+function install_suntemple {
+	echo Checking for suntemple installation...
+	#stdest=/storage/sdcard0/obb/com.BrueComputing.SunTemple
+	stdest=/storage/emulated/0/obb/com.BrueComputing.SunTemple
+	dircontents=$(adb ls $stdest 2>/dev/null)
+	if [ "$dircontents" = "" ]; then
+		echo Installing suntemple...
+		adb install $suntempledir/*.apk
+		adb shell mkdir -p $stdest
+		adb push $suntempledir/main*obb $stdest
+	else
+		echo dircontents=$dircontents
+		echo Suntemple already installed.
+	fi
+}
+
+function run_test {
+	testName=$1
+	collectMinutes=$2
+	collectOutput=${testName}-power-raw.out
+	powerOutput=${testName}-power.out
+	echo -----------------------------------------------------
+	echo TEST: $testName
+	echo enabled Cores $(adb shell "cat /sys/devices/system/cpu/online")
+	date
+	echo -----------------------------------------------------
+	usbpassthru off
+	pwrcollect $collectMinutes > $collectOutput 2>/dev/null
+	# take off the first 2min of samples
+	totalSamples=$(cat $collectOutput | wc -l)
+	# we throw away the first "garbageminutes" of the data
+	# since it is volatile
+	((garbage=hz*60*garbageminutes))
+	((remaining=totalSamples-garbage))
+	if [ $remaining -gt 0 ]; then
+		tail -$remaining $collectOutput > $powerOutput
+	else
+		cp $collectOutput $powerOutput
+	fi
+	echo power data for $testName copied to $collectOutput
+	usbpassthru on
+	sleep 10
+	adb devices
+	sleep 10
+}
+
+function start_job {
+	cmdline="$1"
+	echo Running $cmdline
+	(adb shell "cd $devdir && nohup $cmdline > test.out") &
+	sleep 5
+	kill %1 2>/dev/null
+}
+
+function cleanup_job {
+	testName=$1
+	processName=$2
+	processName=${processName:=" sh "}
+	set -- $(adb shell ps | tr "\r" " " | grep "$processName")
+	echo killing PID=$2...
+	adb shell kill $2
+	sleep 1
+	echo copying test output to $testName...
+	adb pull $devdir/test.out
+	mv test.out ${testName}.out
+	if [ $restart -gt 0 ]; then
+		restart_device
+	else
+		doKeyevent HOME
+	fi
+}
+
+function airplane_mode {
+	if [ "$1" = "on" ]; then
+		mode=true
+		setting=1
+	else
+		mode=false
+		setting=0
+	fi
+	adb shell settings put global airplane_mode_on $setting
+	adb shell am broadcast -a android.intent.action.AIRPLANE_MODE --ez state $mode
+	echo Set airplane mode to $mode
+}
+
+function restart_device {
+	adb reboot
+	echo Wait 60s for device to restart...
+	sleep 60
+	while ! adb root
+	do
+		echo Waiting for device to come up...
+		sleep 10
+	done
+	echo Wait 30s to complete boot activities...
+	sleep 30
+	echo Restart complete.
+	doTap 897 1075
+	sleep 2
+	doSwipe $onSwipe
+	restartfile=${restartfile:="./restarthook"}
+	if [ -f $restartfile ]; then
+		# hook to change tunables after a restart
+		. $restartfile
+	fi
+}
+
+usbpassthru on
+adb devices 2>/dev/null
+
+airplane_mode off
+if [ $restart -gt 0 ]; then
+	restart_device
+fi
+
+echo Copying $scripts to device $devdir...
+copy_files
+tests=""
+
+# measure background power
+idleTime=${idleTime:=$defaultTime}
+if [ $idleTime -gt 0 ]; then
+	echo Test 1 : measure idle power for $idleTime minutes
+	run_test idle $idleTime
+	airplane_mode on
+	echo Restarting for power baseline in airplane mode...
+	restart_device
+	run_test idle-airplane $idleTime
+	airplane_mode off
+	# the screen blanks after 30 minutes. The first 2 minutes of the test
+	# have already been filtered off. For our power baseline, keep the first
+	# 20 minutes of the results
+	((twentyminutes=hz*20*60))
+	powerOutput="idle-power.out"
+	displayPowerOutput="idle-display-power.out"
+	airplanePowerOutput="idle-airplane-power.out"
+	airplaneDisplayPowerOutput="idle-airplane-display-power.out"
+	totalSamples=$(cat $powerOutput | wc -l)
+	if [ $twentyminutes -lt $totalSamples ]; then
+		head -$twentyminutes $powerOutput > $displayPowerOutput
+		head -$twentyminutes $airplanePowerOutput > $airplaneDisplayPowerOutput
+	else
+		cp $powerOutput $displayPowerOutput
+		cp $airplanePowerOutput $airplaneDisplayPowerOutput
+	fi
+	tests="$tests idle"
+fi
+
+recentflingTime=${recentflingTime:=$defaultTime}
+if [ $recentflingTime -gt 0 ]; then
+	echo $(date) Test 2 : recents fling for $recentflingTime minutes
+	airplane_mode on
+	adb shell "cd $devdir && ./systemapps.sh -A -T -i 1"
+	start_job "./recentfling.sh -N -i 1000 -d $DEVICE"
+	run_test recentfling $recentflingTime
+	cleanup_job recentfling
+	airplane_mode off
+	date
+	tests="$tests recentfling"
+fi
+
+suntempleTime=${suntempleTime:=$defaultTime}
+if [ $suntempleTime -gt 0 ]; then
+	echo $(date) Test 2 : run Sun Temple $suntempleTime minutes
+	airplane_mode on
+	install_suntemple
+	adb shell "am start $suntempleActivity"
+	run_test suntemple $suntempleTime
+	adb pull /sdcard/SunTemple/SunTemple/Saved/Logs/SunTemple.log
+	cleanup_job suntemple BrueComp
+	airplane_mode off
+	mv SunTemple.log suntemple.out
+	# grab the suntemple log
+	date
+	tests="$tests suntemple"
+fi
+
+chromeTime=${chromeTime:=$defaultTime}
+if [ $chromeTime -gt 0 ]; then
+	echo $(date) Test 3 : chrome fling for $chromeTime minutes
+	start_job "./chromefling.sh -i 1000 -d $DEVICE"
+	run_test chrome $chromeTime
+	cleanup_job chrome
+	date
+	tests="$tests chrome"
+fi
+
+shadowgrid2Time=${shadowgrid2Time:=$defaultTime}
+if [ $shadowgrid2Time -gt $shadowgrid2TimeMax ]; then
+	# we cap shadowgrid2 time since the display goes
+	# off after 30 minutes
+	$shadowgrid2Time=$shadowgrid2TimeMax
+fi
+if [ $shadowgrid2Time -gt 0 ]; then
+	airplane_mode on
+	echo $(date) Test 4 : shadowgrid2 for $shadowgrid2Time minutes
+	start_job "./$HWUITEST shadowgrid2 100000"
+	run_test shadowgrid2 $shadowgrid2Time
+	cleanup_job shadowgrid2 $HWUITEST
+	airplane_mode off
+	date
+	tests="$tests shadowgrid2"
+fi
+
+youtubeTime=${youtubeTime:=$defaultTime}
+if [ $youtubeTime -gt 0 ]; then
+	echo $(date) Test 5 : youtube for $youtubeTime minutes
+	start_job "./youtube.sh -i 1000 -d $DEVICE"
+	run_test youtube $youtubeTime
+	cleanup_job youtube
+	date
+	tests="$tests youtube"
+fi
+
+sysappsTime=${sysappsTime:=$defaultTime}
+if [ $sysappsTime -gt 0 ]; then
+	echo $(date) Test 6 : app switching for $sysappsTime minutes
+	start_job "./systemapps.sh -T -i 1000 -d $DEVICE"
+	run_test sysapps $sysappsTime
+	cleanup_job sysapps
+	date
+	tests="$tests sysapps"
+fi
+
+echo Ran tests: $tests
+echo $tests > tests
+
diff --git a/tests/workloads/recentfling.sh b/tests/workloads/recentfling.sh
index 092c8d9..79713f3 100755
--- a/tests/workloads/recentfling.sh
+++ b/tests/workloads/recentfling.sh
@@ -18,10 +18,12 @@
 	(-A) unset appList;;
 	(-L) appList=$2; shift; ret=1;;
 	(-T) capturesystrace=1;;
+	(-B) echo $$ > /dev/cpuset/background/tasks;;
 	(*)
 		echo "$0: unrecognized option: $1"
 		echo; echo "Usage: $0 [options]"
 		echo "-A : use all known applications"
+		echo "-B : run in background cpuset"
 		echo "-L applist : list of applications"
 		echo "   default: $appList"
 		echo "-N : no app startups, just fling"
@@ -44,6 +46,12 @@
 	upCount=6
 	UP="70 400 70 100 $flingtime"
 	DOWN="70 100 70 400 $flingtime";;
+(angler|ariel|mtp8996)
+	flingtime=150
+	downCount=4
+	upCount=3
+	UP="500 1200 500 550 $flingtime"
+	DOWN="500 550 500 1200 $flingtime";;
 (bullhead)
 	flingtime=200
 	downCount=5
@@ -122,7 +130,6 @@
 	latency99=$5
 	if [ ${totalDiff:=0} -eq 0 ]; then
 		echo Error: could not read frame info with \"dumpsys gfxinfo\"
-		exit 1
 	fi
 
 	((frameSum=frameSum+totalDiff))
@@ -132,7 +139,6 @@
 	((latency99Sum=latency99Sum+latency99))
 	if [ "$totalDiff" -eq 0 ]; then
 		echo Error: no frames detected. Is the display off?
-		exit 1
 	fi
 	((jankPct=jankyDiff*100/totalDiff))
 	resetJankyFrames
diff --git a/tests/workloads/systemapps.sh b/tests/workloads/systemapps.sh
index a263e7d..6c0db35 100755
--- a/tests/workloads/systemapps.sh
+++ b/tests/workloads/systemapps.sh
@@ -23,12 +23,13 @@
 # Other options are described below.
 #
 iterations=1
-tracecategories="gfx view am input memreclaim"
+tracecategories="gfx am memreclaim"
 totaltimetest=0
 forcecoldstart=0
 waitTime=3.0
+memstats=0
 
-appList="gmail hangouts chrome youtube play home"
+appList="gmail maps chrome youtube play home"
 
 function processLocalOption {
 	ret=0
@@ -38,6 +39,7 @@
 	(-L) appList=$2; shift; ret=1;;
 	(-T) totaltimetest=1;;
 	(-W) waitTime=$2; shift; ret=1;;
+	(-M) memstats=1;;
 	(*)
 		echo "$0: unrecognized option: $1"
 		echo; echo "Usage: $0 [options]"
@@ -141,6 +143,7 @@
 	if [ $iterations -gt 1 ]; then
 		echo =========================================
 		echo Iteration $cur of $iterations
+		date
 		echo =========================================
 	fi
 	if [ $iterations -gt 1 -o $cur -eq 1 ]; then
@@ -160,8 +163,11 @@
 		if [ $totaltimetest -eq 0 ]; then
 			tmpTraceOut="$tmpTraceOutBase-$app.out"
 			>$tmpTraceOut
-			startInstramentation
+			startInstramentation "$app-$cur"
 		else
+			if [ "$memstats" -gt 0 ]; then
+				startInstramentation "$app-$cur" 0
+			fi
 			if [ $appnum -eq 0 ]; then
 				printf "%-8s %5s(ms) %3s(ms) %s      %s\n" App Start Iter Jank Latency
 			fi
@@ -239,6 +245,8 @@
 	printf "%-10s %5.0f   %5.0f\n" TOTAL $totaltime $diffTime
 fi
 
+overallSum=0
+appCount=0
 if [ $iterations -gt 1 -a $totaltimetest -eq 0 ]; then
 	echo
 	echo =========================================
@@ -258,7 +266,14 @@
 		((ave90=l90/iterations))
 		((ave95=l95/iterations))
 		((ave99=l99/iterations))
-		((jankPct=100*janks/frames))
+		if [ $frames -gt 0 ]; then
+			((jankPct=100*janks/frames))
+		fi
 		printf "%-12s %5d      %5d      %5d      %5d      %5d     %5d(%d%%) %d/%d/%d\n" $app $1 $ave $2 $4 $5 $janks $jankPct $ave90 $ave95 $ave99
+		((overallSum=overallSum+ave))
+		((appCount=appCount+1))
 	done
+	if [ $appCount -gt 0 ]; then
+		printf "Average Start Time: %.2f\n", $(echo $overallSum $appCount | awk '{ printf "%.2f\n", $1/$2 }')
+	fi
 fi
diff --git a/tests/workloads/youtube.sh b/tests/workloads/youtube.sh
new file mode 100755
index 0000000..558e53c
--- /dev/null
+++ b/tests/workloads/youtube.sh
@@ -0,0 +1,149 @@
+#
+# Script to play a john oliver youtube video N times.
+# For each iteration, Total frames and janky frames are reported.
+#
+# Options are described below.
+#
+iterations=10
+app=youtube
+searchText="last week tonight with john oliver: online harassment"
+vidMinutes=15
+
+function processLocalOption {
+	ret=0
+	case "$1" in
+	(-S) searchText="$2"; shift;;
+	(-t) vidMinutes="$2"; shift;;
+	(*)
+		echo "$0: unrecognized option: $1"
+		echo; echo "Usage: $0 [options]"
+		echo "-i iterations"
+		echo "-S youtube search text"
+		echo "-d device"
+		echo "-t vidMinutes"
+		exit 1;;
+	esac
+	return $ret
+}
+
+CMDDIR=$(dirname $0 2>/dev/null)
+CMDDIR=${CMDDIR:=.}
+. $CMDDIR/defs.sh
+
+case $DEVICE in
+(angler)
+	searchButton="860 177"
+	selectFirstVideo="225 400"
+	enableControls="1000 610"
+	fullScreen="1011 632"
+	;;
+(shamu)
+	searchButton="1200 160"
+	selectFirstVideo="480 653"
+	enableControls="1377 812"
+	fullScreen="1377 812"
+	;;
+(bullhead|hammerhead)
+	searchButton="860 177"
+	selectFirstVideo="225 400"
+	enableControls="1000 610"
+	fullScreen="1011 632"
+	;;
+(volantis)
+	searchButton="1356 93"
+	selectFirstVideo="378 264"
+	enableControls="1464 812"
+	fullScreen="1480 835"
+	;;
+(ariel)
+	searchButton="1440 70"
+	selectFirstVideo="228 224"
+	enableControls="1528 880"
+	fullScreen="1528 880"
+	;;
+
+(*)
+	echo "Error: No display information available for $DEVICE"
+	exit 1;;
+esac
+
+function swipe {
+	count=0
+	while [ $count -lt $2 ]
+	do
+		echo doSwipe...
+		doSwipe $1
+		((count=count+1))
+	done
+	sleep 1
+}
+
+cur=1
+frameSum=0
+jankSum=0
+latency90Sum=0
+latency95Sum=0
+latency99Sum=0
+
+doKeyevent HOME
+sleep 0.5
+resetJankyFrames $(getPackageName $app)
+
+while [ $cur -le $iterations ]
+do
+	t=$(startActivity $app)
+	sleep 4.0
+	doTap $searchButton
+	sleep 1.0
+	doText "$searchText"
+	sleep 1.0
+	doKeyevent ENTER
+	sleep 5.0
+	doTap $selectFirstVideo
+	sleep 10.0
+	doTap $fullScreen
+	sleep 0.5
+	doTap $fullScreen
+	# 15 minutes
+	((vidTime=60*vidMinutes))
+	sleep $vidTime
+	doKeyevent BACK
+	sleep 0.5
+	doKeyevent BACK
+	sleep 0.5
+	doKeyevent BACK
+	sleep 0.5
+
+	set -- $(getJankyFrames $(getPackageName $app))
+	totalDiff=$1
+	jankyDiff=$2
+	latency90=$3
+	latency95=$4
+	latency99=$5
+	if [ ${totalDiff:=0} -eq 0 ]; then
+		echo Error: could not read frame info with \"dumpsys gfxinfo\"
+	fi
+
+	((frameSum=frameSum+totalDiff))
+	((jankSum=jankSum+jankyDiff))
+	((latency90Sum=latency90Sum+latency90))
+	((latency95Sum=latency95Sum+latency95))
+	((latency99Sum=latency99Sum+latency99))
+	if [ "$totalDiff" -eq 0 ]; then
+		echo Error: no frames detected. Is the display off?
+	fi
+	((jankPct=jankyDiff*100/totalDiff))
+	resetJankyFrames $(getPackageName $app)
+
+
+	echo Frames: $totalDiff latency: $latency90/$latency95/$latency99 Janks: $jankyDiff\(${jankPct}%\)
+	((cur=cur+1))
+done
+doKeyevent HOME
+((aveJankPct=jankSum*100/frameSum))
+((aveJanks=jankSum/iterations))
+((aveFrames=frameSum/iterations))
+((aveLatency90=latency90Sum/iterations))
+((aveLatency95=latency95Sum/iterations))
+((aveLatency99=latency99Sum/iterations))
+echo AVE: Frames: $aveFrames latency: $aveLatency90/$aveLatency95/$aveLatency99 Janks: $aveJanks\(${aveJankPct}%\)