Merge "adb: fix two device offline problems."
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 33d6bda..39e71e5 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -522,8 +522,8 @@
     if (!_try_make_handle_noninheritable(h)) {
         // Show the handle value to give us a clue in case we have problems
         // with pseudo-handle values.
-        fprintf(stderr, "Cannot make handle 0x%p non-inheritable: %s\n",
-                h, android::base::SystemErrorCodeToString(GetLastError()).c_str());
+        fprintf(stderr, "adb: cannot make handle 0x%p non-inheritable: %s\n", h,
+                android::base::SystemErrorCodeToString(GetLastError()).c_str());
         return false;
     }
 
@@ -538,7 +538,7 @@
     HANDLE pipe_read_raw = NULL;
     HANDLE pipe_write_raw = NULL;
     if (!CreatePipe(&pipe_read_raw, &pipe_write_raw, sa, 0)) {
-        fprintf(stderr, "Cannot create pipe: %s\n",
+        fprintf(stderr, "adb: CreatePipe failed: %s\n",
                 android::base::SystemErrorCodeToString(GetLastError()).c_str());
         return false;
     }
@@ -569,7 +569,8 @@
     std::unique_ptr<FILE, decltype(&fclose)> stream(nullptr, fclose);
 
     if (original_fd == -1) {
-        fprintf(stderr, "Failed to get file descriptor for %s: %s\n", output_name, strerror(errno));
+        fprintf(stderr, "adb: failed to get file descriptor for %s: %s\n", output_name,
+                strerror(errno));
         return EXIT_FAILURE;
     }
 
@@ -581,7 +582,7 @@
         // call this function if subprocesses may be started concurrently.
         const int fd = dup(original_fd);
         if (fd == -1) {
-            fprintf(stderr, "Failed to duplicate file descriptor for %s: %s\n", output_name,
+            fprintf(stderr, "adb: failed to duplicate file descriptor for %s: %s\n", output_name,
                     strerror(errno));
             return EXIT_FAILURE;
         }
@@ -589,7 +590,7 @@
         // Note that although we call fdopen() below with a binary flag, it may not adhere to that
         // flag, so we have to set the mode manually.
         if (_setmode(fd, _O_BINARY) == -1) {
-            fprintf(stderr, "Failed to set binary mode for duplicate of %s: %s\n", output_name,
+            fprintf(stderr, "adb: failed to set binary mode for duplicate of %s: %s\n", output_name,
                     strerror(errno));
             unix_close(fd);
             return EXIT_FAILURE;
@@ -597,7 +598,7 @@
 
         stream.reset(fdopen(fd, "wb"));
         if (stream.get() == nullptr) {
-            fprintf(stderr, "Failed to open duplicate stream for %s: %s\n", output_name,
+            fprintf(stderr, "adb: failed to open duplicate stream for %s: %s\n", output_name,
                     strerror(errno));
             unix_close(fd);
             return EXIT_FAILURE;
@@ -606,7 +607,7 @@
         // Unbuffer the stream because it will be buffered by default and we want subprocess output
         // to be shown immediately.
         if (setvbuf(stream.get(), NULL, _IONBF, 0) == -1) {
-            fprintf(stderr, "Failed to unbuffer %s: %s\n", output_name, strerror(errno));
+            fprintf(stderr, "adb: failed to unbuffer %s: %s\n", output_name, strerror(errno));
             return EXIT_FAILURE;
         }
 
@@ -623,7 +624,7 @@
             if (err == ERROR_BROKEN_PIPE) {
                 return EXIT_SUCCESS;
             } else {
-                fprintf(stderr, "Failed to read from %s: %s\n", output_name,
+                fprintf(stderr, "adb: failed to read from %s: %s\n", output_name,
                         android::base::SystemErrorCodeToString(err).c_str());
                 return EXIT_FAILURE;
             }
@@ -634,8 +635,8 @@
             // fwrite() actually calls adb_fwrite() which can write UTF-8 to the console.
             const size_t bytes_written = fwrite(buf, 1, bytes_read, stream.get());
             if (bytes_written != bytes_read) {
-                fprintf(stderr, "Only wrote %zu of %lu bytes to %s\n", bytes_written, bytes_read,
-                        output_name);
+                fprintf(stderr, "adb: error: only wrote %zu of %lu bytes to %s\n", bytes_written,
+                        bytes_read, output_name);
                 return EXIT_FAILURE;
             }
         }
@@ -678,7 +679,7 @@
             FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, OPEN_EXISTING,
             FILE_ATTRIBUTE_NORMAL, NULL));
     if (nul_read.get() == INVALID_HANDLE_VALUE) {
-        fprintf(stderr, "Cannot open 'nul': %s\n",
+        fprintf(stderr, "adb: CreateFileW 'nul' failed: %s\n",
                 android::base::SystemErrorCodeToString(GetLastError()).c_str());
         return -1;
     }
@@ -740,8 +741,7 @@
         // If this fires, either handle values are larger than 32-bits or else
         // there is a bug in our casting.
         // https://msdn.microsoft.com/en-us/library/windows/desktop/aa384203%28v=vs.85%29.aspx
-        fprintf(stderr, "Cannot fit pipe handle value into 32-bits: 0x%p\n",
-                ack_write.get());
+        fprintf(stderr, "adb: cannot fit pipe handle value into 32-bits: 0x%p\n", ack_write.get());
         return -1;
     }
 
@@ -751,7 +751,7 @@
                                                    arraysize(program_path));
     if ((module_result >= arraysize(program_path)) || (module_result == 0)) {
         // String truncation or some other error.
-        fprintf(stderr, "Cannot get executable path: %s\n",
+        fprintf(stderr, "adb: cannot get executable path: %s\n",
                 android::base::SystemErrorCodeToString(GetLastError()).c_str());
         return -1;
     }
@@ -776,7 +776,7 @@
             NULL,                    /* use parent's starting directory */
             &startup,                 /* startup info, i.e. std handles */
             &pinfo )) {
-        fprintf(stderr, "Cannot create process: %s\n",
+        fprintf(stderr, "adb: CreateProcessW failed: %s\n",
                 android::base::SystemErrorCodeToString(GetLastError()).c_str());
         return -1;
     }
@@ -806,7 +806,7 @@
             _beginthreadex(NULL, 0, _redirect_stdout_thread, stdout_read.get(),
                            0, NULL)));
     if (stdout_thread.get() == nullptr) {
-        fprintf(stderr, "Cannot create thread: %s\n", strerror(errno));
+        fprintf(stderr, "adb: cannot create thread: %s\n", strerror(errno));
         return -1;
     }
     stdout_read.release();  // Transfer ownership to new thread
@@ -815,7 +815,7 @@
             _beginthreadex(NULL, 0, _redirect_stderr_thread, stderr_read.get(),
                            0, NULL)));
     if (stderr_thread.get() == nullptr) {
-        fprintf(stderr, "Cannot create thread: %s\n", strerror(errno));
+        fprintf(stderr, "adb: cannot create thread: %s\n", strerror(errno));
         return -1;
     }
     stderr_read.release();  // Transfer ownership to new thread
@@ -860,22 +860,20 @@
     if (wait_result == WAIT_TIMEOUT) {
         // Threads did not finish after waiting a little while. Perhaps the
         // server didn't close pipes, or it is hung.
-        fprintf(stderr, "Timed-out waiting for threads to finish reading from "
-                "ADB Server\n");
+        fprintf(stderr, "adb: timed out waiting for threads to finish reading from ADB server\n");
         // Process handles are signaled when the process exits, so if we wait
         // on the handle for 0 seconds and it returns 'timeout', that means that
         // the process is still running.
         if (WaitForSingleObject(process_handle.get(), 0) == WAIT_TIMEOUT) {
             // We could TerminateProcess(), but that seems somewhat presumptive.
-            fprintf(stderr, "ADB Server is running: process id %lu\n",
-                    pinfo.dwProcessId);
+            fprintf(stderr, "adb: server is running with process id %lu\n", pinfo.dwProcessId);
         }
         return -1;
     }
 
     if (wait_result != WAIT_OBJECT_0) {
-        fprintf(stderr, "Unexpected result waiting for threads: %lu: %s\n",
-                wait_result, android::base::SystemErrorCodeToString(GetLastError()).c_str());
+        fprintf(stderr, "adb: unexpected result waiting for threads: %lu: %s\n", wait_result,
+                android::base::SystemErrorCodeToString(GetLastError()).c_str());
         return -1;
     }
 
@@ -909,7 +907,7 @@
         int result = execl(path.c_str(), "adb", "-L", socket_spec.c_str(), "fork-server", "server",
                            "--reply-fd", reply_fd, NULL);
         // this should not return
-        fprintf(stderr, "OOPS! execl returned %d, errno: %d\n", result, errno);
+        fprintf(stderr, "adb: execl returned %d: %s\n", result, strerror(errno));
     } else  {
         // parent side of the fork
 
diff --git a/adb/adb_client.cpp b/adb/adb_client.cpp
index ff9b32a..b656887 100644
--- a/adb/adb_client.cpp
+++ b/adb/adb_client.cpp
@@ -161,21 +161,21 @@
 
     D("adb_connect: service %s", service.c_str());
     if (fd == -2 && !is_local_socket_spec(__adb_server_socket_spec)) {
-        fprintf(stderr,"** Cannot start server on remote host\n");
+        fprintf(stderr, "* cannot start server on remote host\n");
         // error is the original network connection error
         return fd;
     } else if (fd == -2) {
-        fprintf(stdout, "* daemon not running. starting it now at %s *\n", __adb_server_socket_spec);
+        fprintf(stderr, "* daemon not running; starting now at %s\n", __adb_server_socket_spec);
     start_server:
         if (launch_server(__adb_server_socket_spec)) {
-            fprintf(stderr,"* failed to start daemon *\n");
+            fprintf(stderr, "* failed to start daemon\n");
             // launch_server() has already printed detailed error info, so just
             // return a generic error string about the overall adb_connect()
             // that the caller requested.
             *error = "cannot connect to daemon";
             return -1;
         } else {
-            fprintf(stdout,"* daemon started successfully *\n");
+            fprintf(stderr, "* daemon started successfully\n");
         }
         // Give the server some time to start properly and detect devices.
         std::this_thread::sleep_for(3s);
@@ -210,8 +210,8 @@
         }
 
         if (version != ADB_SERVER_VERSION) {
-            printf("adb server version (%d) doesn't match this client (%d); killing...\n",
-                   version, ADB_SERVER_VERSION);
+            fprintf(stderr, "adb server version (%d) doesn't match this client (%d); killing...\n",
+                    version, ADB_SERVER_VERSION);
             fd = _adb_connect("host:kill", error);
             if (fd >= 0) {
                 ReadOrderlyShutdown(fd);
@@ -237,7 +237,7 @@
     if (fd == -1) {
         D("_adb_connect error: %s", error->c_str());
     } else if(fd == -2) {
-        fprintf(stderr,"** daemon still not running\n");
+        fprintf(stderr, "* daemon still not running\n");
     }
     D("adb_connect: return fd %d", fd);
 
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index 31d3dc6..6f2403d 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -267,8 +267,8 @@
     adb_close(fd);
 }
 
-int usage(const char* fmt, ...) {
-    fprintf(stderr, "adb: ");
+int syntax_error(const char* fmt, ...) {
+    fprintf(stderr, "adb: usage: ");
 
     va_list ap;
     va_start(ap, fmt);
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index e0ad103..c1d5549 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -21,7 +21,7 @@
 
 #include <android-base/macros.h>
 
-int usage(const char*, ...);
+int syntax_error(const char*, ...);
 
 void close_stdin();
 
diff --git a/adb/bugreport.cpp b/adb/bugreport.cpp
index 9dc9811..da2cfa2 100644
--- a/adb/bugreport.cpp
+++ b/adb/bugreport.cpp
@@ -137,7 +137,7 @@
             SetSrcFile(&line[strlen(BUGZ_OK_PREFIX)]);
         } else if (android::base::StartsWith(line, BUGZ_FAIL_PREFIX)) {
             const char* error_message = &line[strlen(BUGZ_FAIL_PREFIX)];
-            fprintf(stderr, "Device failed to take a zipped bugreport: %s\n", error_message);
+            fprintf(stderr, "adb: device failed to take a zipped bugreport: %s\n", error_message);
             status_ = -1;
         } else if (show_progress_ && android::base::StartsWith(line, BUGZ_PROGRESS_PREFIX)) {
             // progress_line should have the following format:
@@ -195,7 +195,7 @@
 };
 
 int Bugreport::DoIt(TransportType transport_type, const char* serial, int argc, const char** argv) {
-    if (argc > 2) return usage("usage: adb bugreport [PATH]");
+    if (argc > 2) return syntax_error("adb bugreport [PATH]");
 
     // Gets bugreportz version.
     std::string bugz_stdout, bugz_stderr;
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index 33456bf..5f55ab9 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -691,8 +691,7 @@
         switch (opt) {
             case 'e':
                 if (!(strlen(optarg) == 1 || strcmp(optarg, "none") == 0)) {
-                    fprintf(stderr, "error: -e requires a single-character argument or 'none'\n");
-                    return 1;
+                    return syntax_error("-e requires a single-character argument or 'none'");
                 }
                 escape_char = (strcmp(optarg, "none") == 0) ? 0 : optarg[0];
                 break;
@@ -844,7 +843,10 @@
  *   we hang up.
  */
 static int adb_sideload_host(const char* filename) {
-    fprintf(stderr, "opening '%s'...\n", filename);
+    // TODO: use a LinePrinter instead...
+    fprintf(stdout, "opening '%s'...\n", filename);
+    fflush(stdout);
+
     struct stat sb;
     if (stat(filename, &sb) == -1) {
         fprintf(stderr, "failed to stat file %s: %s\n", filename, strerror(errno));
@@ -856,7 +858,8 @@
         return -1;
     }
 
-    fprintf(stderr, "connecting...\n");
+    fprintf(stdout, "connecting...\n");
+    fflush(stdout);
     std::string service = android::base::StringPrintf(
         "sideload-host:%d:%d", static_cast<int>(sb.st_size), SIDELOAD_HOST_BLOCK_SIZE);
     std::string error;
@@ -945,19 +948,13 @@
     fprintf(stderr, "error: adb %s not implemented on Win32\n", argv[0]);
     return -1;
 #else
-    if (argc < 2) {
-        fprintf(stderr, "usage: adb %s <adb service name> [ppp opts]\n",
-                argv[0]);
-
-        return 1;
-    }
+    if (argc < 2) return syntax_error("adb %s <adb service name> [ppp opts]", argv[0]);
 
     const char* adb_service_name = argv[1];
     std::string error;
     int fd = adb_connect(adb_service_name, &error);
     if (fd < 0) {
-        fprintf(stderr,"Error: Could not open adb service: %s. Error: %s\n",
-                adb_service_name, error.c_str());
+        fprintf(stderr, "adb: could not open adb service %s: %s\n", adb_service_name, error.c_str());
         return 1;
     }
 
@@ -1176,10 +1173,7 @@
     /* find, extract, and use any -f argument */
     for (int i = 1; i < argc; i++) {
         if (!strcmp("-f", argv[i])) {
-            if (i == argc-1) {
-                fprintf(stderr, "adb: backup -f passed with no filename.\n");
-                return EXIT_FAILURE;
-            }
+            if (i == argc - 1) return syntax_error("backup -f passed with no filename");
             filename = argv[i+1];
             for (int j = i+2; j <= argc; ) {
                 argv[i++] = argv[j++];
@@ -1191,10 +1185,7 @@
 
     // Bare "adb backup" or "adb backup -f filename" are not valid invocations ---
     // a list of packages is required.
-    if (argc < 2) {
-        fprintf(stderr, "adb: backup either needs a list of packages or -all/-shared.\n");
-        return EXIT_FAILURE;
-    }
+    if (argc < 2) return syntax_error("backup either needs a list of packages or -all/-shared");
 
     adb_unlink(filename);
     int outFd = adb_creat(filename, 0640);
@@ -1219,7 +1210,7 @@
         return EXIT_FAILURE;
     }
 
-    printf("Now unlock your device and confirm the backup operation...\n");
+    fprintf(stdout, "Now unlock your device and confirm the backup operation...\n");
     fflush(stdout);
 
     copy_to_file(fd, outFd);
@@ -1230,7 +1221,7 @@
 }
 
 static int restore(int argc, const char** argv) {
-    if (argc != 2) return usage("restore requires an argument");
+    if (argc != 2) return syntax_error("restore requires an argument");
 
     const char* filename = argv[1];
     int tarFd = adb_open(filename, O_RDONLY);
@@ -1247,7 +1238,9 @@
         return -1;
     }
 
-    printf("Now unlock your device and confirm the restore operation.\n");
+    fprintf(stdout, "Now unlock your device and confirm the restore operation.\n");
+    fflush(stdout);
+
     copy_to_file(tarFd, fd);
 
     // Provide an in-band EOD marker in case the archive file is malformed
@@ -1339,7 +1332,7 @@
             } else if (!strcmp(*arg, "--")) {
                 ignore_flags = true;
             } else {
-                fprintf(stderr, "adb: unrecognized option '%s'\n", *arg);
+                syntax_error("unrecognized option '%s'", *arg);
                 exit(1);
             }
         }
@@ -1438,7 +1431,7 @@
             /* this is a special flag used only when the ADB client launches the ADB Server */
             is_daemon = 1;
         } else if (!strcmp(argv[0], "--reply-fd")) {
-            if (argc < 2) return usage("--reply-fd requires an argument");
+            if (argc < 2) return syntax_error("--reply-fd requires an argument");
             const char* reply_fd_str = argv[1];
             argc--;
             argv++;
@@ -1450,7 +1443,7 @@
         } else if (!strncmp(argv[0], "-p", 2)) {
             const char* product = nullptr;
             if (argv[0][2] == '\0') {
-                if (argc < 2) return usage("-p requires an argument");
+                if (argc < 2) return syntax_error("-p requires an argument");
                 product = argv[1];
                 argc--;
                 argv++;
@@ -1466,7 +1459,7 @@
             if (isdigit(argv[0][2])) {
                 serial = argv[0] + 2;
             } else {
-                if (argc < 2 || argv[0][2] != '\0') return usage("-s requires an argument");
+                if (argc < 2 || argv[0][2] != '\0') return syntax_error("-s requires an argument");
                 serial = argv[1];
                 argc--;
                 argv++;
@@ -1479,7 +1472,7 @@
             gListenAll = 1;
         } else if (!strncmp(argv[0], "-H", 2)) {
             if (argv[0][2] == '\0') {
-                if (argc < 2) return usage("-H requires an argument");
+                if (argc < 2) return syntax_error("-H requires an argument");
                 server_host_str = argv[1];
                 argc--;
                 argv++;
@@ -1488,7 +1481,7 @@
             }
         } else if (!strncmp(argv[0], "-P", 2)) {
             if (argv[0][2] == '\0') {
-                if (argc < 2) return usage("-P requires an argument");
+                if (argc < 2) return syntax_error("-P requires an argument");
                 server_port_str = argv[1];
                 argc--;
                 argv++;
@@ -1496,7 +1489,7 @@
                 server_port_str = argv[0] + 2;
             }
         } else if (!strcmp(argv[0], "-L")) {
-            if (argc < 2) return usage("-L requires an argument");
+            if (argc < 2) return syntax_error("-L requires an argument");
             server_socket_str = argv[1];
             argc--;
             argv++;
@@ -1509,8 +1502,7 @@
     }
 
     if ((server_host_str || server_port_str) && server_socket_str) {
-        fprintf(stderr, "adb: -L is incompatible with -H or -P\n");
-        exit(1);
+        return syntax_error("-L is incompatible with -H or -P");
     }
 
     // If -L, -H, or -P are specified, ignore environment variables.
@@ -1605,8 +1597,7 @@
         } else if (argc == 2 && !strcmp(argv[1], "-l")) {
             listopt = argv[1];
         } else {
-            fprintf(stderr, "Usage: adb devices [-l]\n");
-            return 1;
+            return syntax_error("adb devices [-l]");
         }
 
         std::string query = android::base::StringPrintf("host:%s%s", argv[0], listopt);
@@ -1614,19 +1605,13 @@
         return adb_query_command(query);
     }
     else if (!strcmp(argv[0], "connect")) {
-        if (argc != 2) {
-            fprintf(stderr, "Usage: adb connect <host>[:<port>]\n");
-            return 1;
-        }
+        if (argc != 2) return syntax_error("adb connect <host>[:<port>]");
 
         std::string query = android::base::StringPrintf("host:connect:%s", argv[1]);
         return adb_query_command(query);
     }
     else if (!strcmp(argv[0], "disconnect")) {
-        if (argc > 2) {
-            fprintf(stderr, "Usage: adb disconnect [<host>[:<port>]]\n");
-            return 1;
-        }
+        if (argc > 2) return syntax_error("adb disconnect [<host>[:<port>]]");
 
         std::string query = android::base::StringPrintf("host:disconnect:%s",
                                                         (argc == 2) ? argv[1] : "");
@@ -1641,10 +1626,7 @@
     else if (!strcmp(argv[0], "exec-in") || !strcmp(argv[0], "exec-out")) {
         int exec_in = !strcmp(argv[0], "exec-in");
 
-        if (argc < 2) {
-            fprintf(stderr, "Usage: adb %s command\n", argv[0]);
-            return 1;
-        }
+        if (argc < 2) return syntax_error("adb %s command", argv[0]);
 
         std::string cmd = "exec:";
         cmd += argv[1];
@@ -1692,7 +1674,7 @@
         }
     }
     else if (!strcmp(argv[0], "sideload")) {
-        if (argc != 2) return usage("sideload requires an argument");
+        if (argc != 2) return syntax_error("sideload requires an argument");
         if (adb_sideload_host(argv[1])) {
             return 1;
         } else {
@@ -1726,7 +1708,7 @@
         bool reverse = !strcmp(argv[0], "reverse");
         ++argv;
         --argc;
-        if (argc < 1) return usage("%s requires an argument", argv[0]);
+        if (argc < 1) return syntax_error("%s requires an argument", argv[0]);
 
         // Determine the <host-prefix> for this command.
         std::string host_prefix;
@@ -1746,24 +1728,24 @@
 
         std::string cmd, error;
         if (strcmp(argv[0], "--list") == 0) {
-            if (argc != 1) return usage("--list doesn't take any arguments");
+            if (argc != 1) return syntax_error("--list doesn't take any arguments");
             return adb_query_command(host_prefix + ":list-forward");
         } else if (strcmp(argv[0], "--remove-all") == 0) {
-            if (argc != 1) return usage("--remove-all doesn't take any arguments");
+            if (argc != 1) return syntax_error("--remove-all doesn't take any arguments");
             cmd = host_prefix + ":killforward-all";
         } else if (strcmp(argv[0], "--remove") == 0) {
             // forward --remove <local>
-            if (argc != 2) return usage("--remove requires an argument");
+            if (argc != 2) return syntax_error("--remove requires an argument");
             cmd = host_prefix + ":killforward:" + argv[1];
         } else if (strcmp(argv[0], "--no-rebind") == 0) {
             // forward --no-rebind <local> <remote>
-            if (argc != 3) return usage("--no-rebind takes two arguments");
+            if (argc != 3) return syntax_error("--no-rebind takes two arguments");
             if (forward_targets_are_valid(argv[1], argv[2], &error)) {
                 cmd = host_prefix + ":forward:norebind:" + argv[1] + ";" + argv[2];
             }
         } else {
             // forward <local> <remote>
-            if (argc != 2) return usage("forward takes two arguments");
+            if (argc != 2) return syntax_error("forward takes two arguments");
             if (forward_targets_are_valid(argv[0], argv[1], &error)) {
                 cmd = host_prefix + ":forward:" + argv[0] + ";" + argv[1];
             }
@@ -1792,7 +1774,7 @@
     }
     /* do_sync_*() commands */
     else if (!strcmp(argv[0], "ls")) {
-        if (argc != 2) return usage("ls requires an argument");
+        if (argc != 2) return syntax_error("ls requires an argument");
         return do_sync_ls(argv[1]) ? 0 : 1;
     }
     else if (!strcmp(argv[0], "push")) {
@@ -1801,7 +1783,7 @@
         const char* dst = nullptr;
 
         parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, &copy_attrs);
-        if (srcs.empty() || !dst) return usage("push requires an argument");
+        if (srcs.empty() || !dst) return syntax_error("push requires an argument");
         return do_sync_push(srcs, dst) ? 0 : 1;
     }
     else if (!strcmp(argv[0], "pull")) {
@@ -1810,22 +1792,22 @@
         const char* dst = ".";
 
         parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, &copy_attrs);
-        if (srcs.empty()) return usage("pull requires an argument");
+        if (srcs.empty()) return syntax_error("pull requires an argument");
         return do_sync_pull(srcs, dst, copy_attrs) ? 0 : 1;
     }
     else if (!strcmp(argv[0], "install")) {
-        if (argc < 2) return usage("install requires an argument");
+        if (argc < 2) return syntax_error("install requires an argument");
         if (_use_legacy_install()) {
             return install_app_legacy(transport_type, serial, argc, argv);
         }
         return install_app(transport_type, serial, argc, argv);
     }
     else if (!strcmp(argv[0], "install-multiple")) {
-        if (argc < 2) return usage("install-multiple requires an argument");
+        if (argc < 2) return syntax_error("install-multiple requires an argument");
         return install_multiple_app(transport_type, serial, argc, argv);
     }
     else if (!strcmp(argv[0], "uninstall")) {
-        if (argc < 2) return usage("uninstall requires an argument");
+        if (argc < 2) return syntax_error("uninstall requires an argument");
         if (_use_legacy_install()) {
             return uninstall_app_legacy(transport_type, serial, argc, argv);
         }
@@ -1848,12 +1830,12 @@
             // A local path or "android"/"data" arg was specified.
             src = argv[1];
         } else {
-            return usage("usage: adb sync [-l] [PARTITION]");
+            return syntax_error("adb sync [-l] [PARTITION]");
         }
 
         if (src != "" &&
             src != "system" && src != "data" && src != "vendor" && src != "oem") {
-            return usage("don't know how to sync %s partition", src.c_str());
+            return syntax_error("don't know how to sync %s partition", src.c_str());
         }
 
         std::string system_src_path = product_file("system");
@@ -1905,7 +1887,7 @@
         return restore(argc, argv);
     }
     else if (!strcmp(argv[0], "keygen")) {
-        if (argc != 2) return usage("keygen requires an argument");
+        if (argc != 2) return syntax_error("keygen requires an argument");
         // Always print key generation information for keygen command.
         adb_trace_enable(AUTH);
         return adb_auth_keygen(argv[1]);
@@ -1958,12 +1940,12 @@
                 std::string err;
                 return adb_query_command("host:reconnect-offline");
             } else {
-                return usage("usage: adb reconnect [device|offline]");
+                return syntax_error("adb reconnect [device|offline]");
             }
         }
     }
 
-    usage("unknown command %s", argv[0]);
+    syntax_error("unknown command %s", argv[0]);
     return 1;
 }
 
@@ -1990,19 +1972,18 @@
     // The last argument must be the APK file
     const char* file = argv[argc - 1];
     if (!android::base::EndsWithIgnoreCase(file, ".apk")) {
-        fprintf(stderr, "Filename doesn't end .apk: %s\n", file);
-        return EXIT_FAILURE;
+        return syntax_error("filename doesn't end .apk: %s", file);
     }
 
     struct stat sb;
     if (stat(file, &sb) == -1) {
-        fprintf(stderr, "Failed to stat %s: %s\n", file, strerror(errno));
+        fprintf(stderr, "adb: failed to stat %s: %s\n", file, strerror(errno));
         return 1;
     }
 
     int localFd = adb_open(file, O_RDONLY);
     if (localFd < 0) {
-        fprintf(stderr, "Failed to open %s: %s\n", file, strerror(errno));
+        fprintf(stderr, "adb: failed to open %s: %s\n", file, strerror(errno));
         return 1;
     }
 
@@ -2020,7 +2001,7 @@
 
     int remoteFd = adb_connect(cmd, &error);
     if (remoteFd < 0) {
-        fprintf(stderr, "Connect error for write: %s\n", error.c_str());
+        fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
         adb_close(localFd);
         return 1;
     }
@@ -2032,12 +2013,12 @@
     adb_close(localFd);
     adb_close(remoteFd);
 
-    if (strncmp("Success", buf, 7)) {
-        fprintf(stderr, "Failed to install %s: %s", file, buf);
-        return 1;
+    if (!strncmp("Success", buf, 7)) {
+        fputs(buf, stdout);
+        return 0;
     }
-    fputs(buf, stderr);
-    return 0;
+    fprintf(stderr, "adb: failed to install %s: %s", file, buf);
+    return 1;
 }
 
 static int install_multiple_app(TransportType transport, const char* serial, int argc,
@@ -2059,10 +2040,7 @@
         }
     }
 
-    if (first_apk == -1) {
-        fprintf(stderr, "No APK file on command line\n");
-        return 1;
-    }
+    if (first_apk == -1) return syntax_error("need APK file on command line");
 
     std::string install_cmd;
     if (_use_legacy_install()) {
@@ -2080,7 +2058,7 @@
     std::string error;
     int fd = adb_connect(cmd, &error);
     if (fd < 0) {
-        fprintf(stderr, "Connect error for create: %s\n", error.c_str());
+        fprintf(stderr, "adb: connect error for create: %s\n", error.c_str());
         return EXIT_FAILURE;
     }
     char buf[BUFSIZ];
@@ -2097,7 +2075,7 @@
         }
     }
     if (session_id < 0) {
-        fprintf(stderr, "Failed to create session\n");
+        fprintf(stderr, "adb: failed to create session\n");
         fputs(buf, stderr);
         return EXIT_FAILURE;
     }
@@ -2108,7 +2086,7 @@
         const char* file = argv[i];
         struct stat sb;
         if (stat(file, &sb) == -1) {
-            fprintf(stderr, "Failed to stat %s: %s\n", file, strerror(errno));
+            fprintf(stderr, "adb: failed to stat %s: %s\n", file, strerror(errno));
             success = 0;
             goto finalize_session;
         }
@@ -2120,7 +2098,7 @@
 
         int localFd = adb_open(file, O_RDONLY);
         if (localFd < 0) {
-            fprintf(stderr, "Failed to open %s: %s\n", file, strerror(errno));
+            fprintf(stderr, "adb: failed to open %s: %s\n", file, strerror(errno));
             success = 0;
             goto finalize_session;
         }
@@ -2128,7 +2106,7 @@
         std::string error;
         int remoteFd = adb_connect(cmd, &error);
         if (remoteFd < 0) {
-            fprintf(stderr, "Connect error for write: %s\n", error.c_str());
+            fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
             adb_close(localFd);
             success = 0;
             goto finalize_session;
@@ -2141,7 +2119,7 @@
         adb_close(remoteFd);
 
         if (strncmp("Success", buf, 7)) {
-            fprintf(stderr, "Failed to write %s\n", file);
+            fprintf(stderr, "adb: failed to write %s\n", file);
             fputs(buf, stderr);
             success = 0;
             goto finalize_session;
@@ -2155,20 +2133,19 @@
                                         install_cmd.c_str(), success ? "commit" : "abandon", session_id);
     fd = adb_connect(service, &error);
     if (fd < 0) {
-        fprintf(stderr, "Connect error for finalize: %s\n", error.c_str());
+        fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str());
         return EXIT_FAILURE;
     }
     read_status_line(fd, buf, sizeof(buf));
     adb_close(fd);
 
     if (!strncmp("Success", buf, 7)) {
-        fputs(buf, stderr);
+        fputs(buf, stdout);
         return 0;
-    } else {
-        fprintf(stderr, "Failed to finalize session\n");
-        fputs(buf, stderr);
-        return EXIT_FAILURE;
     }
+    fprintf(stderr, "adb: failed to finalize session\n");
+    fputs(buf, stderr);
+    return EXIT_FAILURE;
 }
 
 static int pm_command(TransportType transport, const char* serial, int argc, const char** argv) {
@@ -2226,10 +2203,7 @@
         }
     }
 
-    if (last_apk == -1) {
-        fprintf(stderr, "No APK file on command line\n");
-        return EXIT_FAILURE;
-    }
+    if (last_apk == -1) return syntax_error("need APK file on command line");
 
     int result = -1;
     std::vector<const char*> apk_file = {argv[last_apk]};
diff --git a/adb/commandline.h b/adb/commandline.h
index 0cf655c..9ba69a3 100644
--- a/adb/commandline.h
+++ b/adb/commandline.h
@@ -87,7 +87,6 @@
 extern DefaultStandardStreamsCallback DEFAULT_STANDARD_STREAMS_CALLBACK;
 
 int adb_commandline(int argc, const char** argv);
-int usage();
 
 // Connects to the device "shell" service with |command| and prints the
 // resulting output.
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index 7da94ce..7a87df4 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -32,10 +32,10 @@
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <libminijail.h>
+#include <log/log_properties.h>
 #include <scoped_minijail.h>
 
 #include <private/android_filesystem_config.h>
-#include <private/android_logger.h>
 #include "debuggerd/handler.h"
 #include "selinux/android.h"
 
diff --git a/adb/services.cpp b/adb/services.cpp
index dbf163d..9605e6e 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -43,7 +43,7 @@
 #include <android-base/properties.h>
 #include <bootloader_message/bootloader_message.h>
 #include <cutils/android_reboot.h>
-#include <private/android_logger.h>
+#include <log/log_properties.h>
 #endif
 
 #include "adb.h"
diff --git a/adb/set_verity_enable_state_service.cpp b/adb/set_verity_enable_state_service.cpp
index 76b156d..d4dd256 100644
--- a/adb/set_verity_enable_state_service.cpp
+++ b/adb/set_verity_enable_state_service.cpp
@@ -26,7 +26,7 @@
 
 #include "android-base/properties.h"
 #include "android-base/stringprintf.h"
-#include <private/android_logger.h>
+#include <log/log_properties.h>
 
 #include "adb.h"
 #include "adb_io.h"
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index 04290a6..14ad1ff 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -32,7 +32,7 @@
 
 #if !ADB_HOST
 #include <android-base/properties.h>
-#include <private/android_logger.h>
+#include <log/log_properties.h>
 #endif
 
 #include "adb.h"
diff --git a/base/Android.bp b/base/Android.bp
index 3af7686..afb7dd3 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -20,8 +20,28 @@
     "-Werror",
 ]
 
+cc_library_headers {
+    name: "libbase_headers",
+    vendor_available: true,
+    host_supported: true,
+    export_include_dirs: ["include"],
+
+    header_libs: ["libutils_headers"],
+    export_header_lib_headers: ["libutils_headers"],
+
+    target: {
+        linux_bionic: {
+            enabled: true,
+        },
+        windows: {
+            enabled: true,
+        },
+    },
+}
+
 cc_library {
     name: "libbase",
+    vendor_available: true,
     clang: true,
     host_supported: true,
     srcs: [
@@ -33,9 +53,11 @@
         "strings.cpp",
         "test_utils.cpp",
     ],
-    local_include_dirs: ["include"],
+
+    header_libs: ["libbase_headers"],
+    export_header_lib_headers: ["libbase_headers"],
+
     cppflags: libbase_cppflags,
-    export_include_dirs: ["include"],
     shared_libs: ["liblog"],
     target: {
         android: {
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index 5610cc0..2c578a9 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -51,7 +51,7 @@
 
 LOCAL_SRC_FILES_windows := usb_windows.cpp
 LOCAL_STATIC_LIBRARIES_windows := AdbWinApi
-LOCAL_REQUIRED_MODULES_windows := AdbWinApi
+LOCAL_REQUIRED_MODULES_windows := AdbWinApi AdbWinUsbApi
 LOCAL_LDLIBS_windows := -lws2_32
 LOCAL_C_INCLUDES_windows := development/host/windows/usb/api
 
diff --git a/fastboot/engine.cpp b/fastboot/engine.cpp
index 728dcb8..bf887c9 100644
--- a/fastboot/engine.cpp
+++ b/fastboot/engine.cpp
@@ -44,6 +44,7 @@
 #define OP_NOTICE     4
 #define OP_DOWNLOAD_SPARSE 5
 #define OP_WAIT_FOR_DISCONNECT 6
+#define OP_DOWNLOAD_FD 7
 
 typedef struct Action Action;
 
@@ -56,6 +57,7 @@
     char cmd[CMD_SIZE];
     const char* prod;
     void* data;
+    int fd;
 
     // The protocol only supports 32-bit sizes, so you'll have to break
     // anything larger into chunks.
@@ -142,7 +144,20 @@
     a->msg = mkmsg("erasing '%s'", ptn);
 }
 
-void fb_queue_flash(const char *ptn, void *data, unsigned sz)
+void fb_queue_flash_fd(const char *ptn, int fd, uint32_t sz)
+{
+    Action *a;
+
+    a = queue_action(OP_DOWNLOAD_FD, "");
+    a->fd = fd;
+    a->size = sz;
+    a->msg = mkmsg("sending '%s' (%d KB)", ptn, sz / 1024);
+
+    a = queue_action(OP_COMMAND, "flash:%s", ptn);
+    a->msg = mkmsg("writing '%s'", ptn);
+}
+
+void fb_queue_flash(const char *ptn, void *data, uint32_t sz)
 {
     Action *a;
 
@@ -155,7 +170,7 @@
     a->msg = mkmsg("writing '%s'", ptn);
 }
 
-void fb_queue_flash_sparse(const char* ptn, struct sparse_file* s, unsigned sz, size_t current,
+void fb_queue_flash_sparse(const char* ptn, struct sparse_file* s, uint32_t sz, size_t current,
                            size_t total) {
     Action *a;
 
@@ -282,7 +297,7 @@
     return 0;
 }
 
-void fb_queue_query_save(const char *var, char *dest, unsigned dest_size)
+void fb_queue_query_save(const char *var, char *dest, uint32_t dest_size)
 {
     Action *a;
     a = queue_action(OP_QUERY, "getvar:%s", var);
@@ -309,7 +324,7 @@
     a->msg = msg;
 }
 
-void fb_queue_download(const char *name, void *data, unsigned size)
+void fb_queue_download(const char *name, void *data, uint32_t size)
 {
     Action *a = queue_action(OP_DOWNLOAD, "");
     a->data = data;
@@ -328,11 +343,11 @@
     queue_action(OP_WAIT_FOR_DISCONNECT, "");
 }
 
-int fb_execute_queue(Transport* transport)
+int64_t fb_execute_queue(Transport* transport)
 {
     Action *a;
     char resp[FB_RESPONSE_SZ+1];
-    int status = 0;
+    int64_t status = 0;
 
     a = action_list;
     if (!a)
@@ -351,6 +366,10 @@
             status = fb_download_data(transport, a->data, a->size);
             status = a->func(a, status, status ? fb_get_error().c_str() : "");
             if (status) break;
+        } else if (a->op == OP_DOWNLOAD_FD) {
+            status = fb_download_data_fd(transport, a->fd, a->size);
+            status = a->func(a, status, status ? fb_get_error().c_str() : "");
+            if (status) break;
         } else if (a->op == OP_COMMAND) {
             status = fb_command(transport, a->cmd);
             status = a->func(a, status, status ? fb_get_error().c_str() : "");
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 704dc43..3b524ac 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -55,6 +55,7 @@
 #include <android-base/parsenetaddress.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <android-base/unique_fd.h>
 #include <sparse/sparse.h>
 #include <ziparchive/zip_archive.h>
 
@@ -67,6 +68,8 @@
 #include "udp.h"
 #include "usb.h"
 
+using android::base::unique_fd;
+
 #ifndef O_BINARY
 #define O_BINARY 0
 #endif
@@ -78,6 +81,10 @@
 static const char* cmdline = nullptr;
 static unsigned short vendor_id = 0;
 static int long_listing = 0;
+// Don't resparse files in too-big chunks.
+// libsparse will support INT_MAX, but this results in large allocations, so
+// let's keep it at 1GB to avoid memory pressure on the host.
+static constexpr int64_t RESPARSE_LIMIT = 1 * 1024 * 1024 * 1024;
 static int64_t sparse_limit = -1;
 static int64_t target_sparse_limit = -1;
 
@@ -91,7 +98,7 @@
 static const std::string convert_fbe_marker_filename("convert_fbe");
 
 enum fb_buffer_type {
-    FB_BUFFER,
+    FB_BUFFER_FD,
     FB_BUFFER_SPARSE,
 };
 
@@ -99,6 +106,7 @@
     enum fb_buffer_type type;
     void* data;
     int64_t sz;
+    int fd;
 };
 
 static struct {
@@ -789,7 +797,7 @@
     }
 
     if (size > limit) {
-        return limit;
+        return std::min(limit, RESPARSE_LIMIT);
     }
 
     return 0;
@@ -822,10 +830,9 @@
         buf->type = FB_BUFFER_SPARSE;
         buf->data = s;
     } else {
-        void* data = load_fd(fd, &sz);
-        if (data == nullptr) return -1;
-        buf->type = FB_BUFFER;
-        buf->data = data;
+        buf->type = FB_BUFFER_FD;
+        buf->data = nullptr;
+        buf->fd = fd;
         buf->sz = sz;
     }
 
@@ -833,11 +840,22 @@
 }
 
 static bool load_buf(Transport* transport, const char* fname, struct fastboot_buffer* buf) {
-    int fd = open(fname, O_RDONLY | O_BINARY);
+    unique_fd fd(TEMP_FAILURE_RETRY(open(fname, O_RDONLY | O_BINARY)));
+
     if (fd == -1) {
         return false;
     }
-    return load_buf_fd(transport, fd, buf);
+
+    struct stat s;
+    if (fstat(fd, &s)) {
+        return false;
+    }
+    if (!S_ISREG(s.st_mode)) {
+        errno = S_ISDIR(s.st_mode) ? EISDIR : EINVAL;
+        return false;
+    }
+
+    return load_buf_fd(transport, fd.release(), buf);
 }
 
 static void flash_buf(const char *pname, struct fastboot_buffer *buf)
@@ -860,9 +878,8 @@
             }
             break;
         }
-
-        case FB_BUFFER:
-            fb_queue_flash(pname, buf->data, buf->sz);
+        case FB_BUFFER_FD:
+            fb_queue_flash_fd(pname, buf->fd, buf->sz);
             break;
         default:
             die("unknown buffer type: %d", buf->type);
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
index 6699b6a..3f95270 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/fastboot.h
@@ -41,7 +41,8 @@
 /* protocol.c - fastboot protocol */
 int fb_command(Transport* transport, const char* cmd);
 int fb_command_response(Transport* transport, const char* cmd, char* response);
-int fb_download_data(Transport* transport, const void* data, uint32_t size);
+int64_t fb_download_data(Transport* transport, const void* data, uint32_t size);
+int64_t fb_download_data_fd(Transport* transport, int fd, uint32_t size);
 int fb_download_data_sparse(Transport* transport, struct sparse_file* s);
 const std::string fb_get_error();
 
@@ -51,6 +52,7 @@
 /* engine.c - high level command queue engine */
 bool fb_getvar(Transport* transport, const std::string& key, std::string* value);
 void fb_queue_flash(const char *ptn, void *data, uint32_t sz);
+void fb_queue_flash_fd(const char *ptn, int fd, uint32_t sz);
 void fb_queue_flash_sparse(const char* ptn, struct sparse_file* s, uint32_t sz, size_t current,
                            size_t total);
 void fb_queue_erase(const char *ptn);
@@ -64,7 +66,7 @@
 void fb_queue_download(const char *name, void *data, uint32_t size);
 void fb_queue_notice(const char *notice);
 void fb_queue_wait_for_disconnect(void);
-int fb_execute_queue(Transport* transport);
+int64_t fb_execute_queue(Transport* transport);
 void fb_set_active(const char *slot);
 
 /* util stuff */
diff --git a/fastboot/protocol.cpp b/fastboot/protocol.cpp
index bfa83b0..334f81f 100644
--- a/fastboot/protocol.cpp
+++ b/fastboot/protocol.cpp
@@ -38,6 +38,7 @@
 
 #include <android-base/stringprintf.h>
 #include <sparse/sparse.h>
+#include <utils/FileMap.h>
 
 #include "fastboot.h"
 #include "transport.h"
@@ -48,7 +49,7 @@
     return g_error;
 }
 
-static int check_response(Transport* transport, uint32_t size, char* response) {
+static int64_t check_response(Transport* transport, uint32_t size, char* response) {
     char status[65];
 
     while (true) {
@@ -105,7 +106,7 @@
     return -1;
 }
 
-static int _command_start(Transport* transport, const char* cmd, uint32_t size, char* response) {
+static int64_t _command_start(Transport* transport, const char* cmd, uint32_t size, char* response) {
     size_t cmdsize = strlen(cmd);
     if (cmdsize > 64) {
         g_error = android::base::StringPrintf("command too large (%zu)", cmdsize);
@@ -125,14 +126,14 @@
     return check_response(transport, size, response);
 }
 
-static int _command_data(Transport* transport, const void* data, uint32_t size) {
-    int r = transport->Write(data, size);
+static int64_t _command_data(Transport* transport, const void* data, uint32_t size) {
+    int64_t r = transport->Write(data, size);
     if (r < 0) {
         g_error = android::base::StringPrintf("data transfer failure (%s)", strerror(errno));
         transport->Close();
         return -1;
     }
-    if (r != ((int) size)) {
+    if (r != static_cast<int64_t>(size)) {
         g_error = "data transfer failure (short transfer)";
         transport->Close();
         return -1;
@@ -140,17 +141,17 @@
     return r;
 }
 
-static int _command_end(Transport* transport) {
+static int64_t _command_end(Transport* transport) {
     return check_response(transport, 0, 0) < 0 ? -1 : 0;
 }
 
-static int _command_send(Transport* transport, const char* cmd, const void* data, uint32_t size,
+static int64_t _command_send(Transport* transport, const char* cmd, const void* data, uint32_t size,
                          char* response) {
     if (size == 0) {
         return -1;
     }
 
-    int r = _command_start(transport, cmd, size, response);
+    int64_t r = _command_start(transport, cmd, size, response);
     if (r < 0) {
         return -1;
     }
@@ -168,6 +169,39 @@
     return size;
 }
 
+static int64_t _command_send_fd(Transport* transport, const char* cmd, int fd, uint32_t size,
+                                char* response) {
+    static constexpr uint32_t MAX_MAP_SIZE = 512 * 1024 * 1024;
+    off64_t offset = 0;
+    uint32_t remaining = size;
+
+    if (_command_start(transport, cmd, size, response) < 0) {
+        return -1;
+    }
+
+    while (remaining) {
+        android::FileMap filemap;
+        size_t len = std::min(remaining, MAX_MAP_SIZE);
+
+        if (!filemap.create(NULL, fd, offset, len, true)) {
+            return -1;
+        }
+
+        if (_command_data(transport, filemap.getDataPtr(), len) < 0) {
+            return -1;
+        }
+
+        remaining -= len;
+        offset += len;
+    }
+
+    if (_command_end(transport) < 0) {
+        return -1;
+    }
+
+    return size;
+}
+
 static int _command_send_no_data(Transport* transport, const char* cmd, char* response) {
     return _command_start(transport, cmd, 0, response);
 }
@@ -180,10 +214,14 @@
     return _command_send_no_data(transport, cmd, response);
 }
 
-int fb_download_data(Transport* transport, const void* data, uint32_t size) {
-    char cmd[64];
-    snprintf(cmd, sizeof(cmd), "download:%08x", size);
-    return _command_send(transport, cmd, data, size, 0) < 0 ? -1 : 0;
+int64_t fb_download_data(Transport* transport, const void* data, uint32_t size) {
+    std::string cmd(android::base::StringPrintf("download:%08x", size));
+    return _command_send(transport, cmd.c_str(), data, size, 0) < 0 ? -1 : 0;
+}
+
+int64_t fb_download_data_fd(Transport* transport, int fd, uint32_t size) {
+    std::string cmd(android::base::StringPrintf("download:%08x", size));
+    return _command_send_fd(transport, cmd.c_str(), fd, size, 0) < 0 ? -1 : 0;
 }
 
 #define TRANSPORT_BUF_SIZE 1024
@@ -242,7 +280,8 @@
 
 static int fb_download_data_sparse_flush(Transport* transport) {
     if (transport_buf_len > 0) {
-        if (_command_data(transport, transport_buf, transport_buf_len) != transport_buf_len) {
+        int64_t r = _command_data(transport, transport_buf, transport_buf_len);
+        if (r != static_cast<int64_t>(transport_buf_len)) {
             return -1;
         }
         transport_buf_len = 0;
@@ -256,9 +295,8 @@
         return -1;
     }
 
-    char cmd[64];
-    snprintf(cmd, sizeof(cmd), "download:%08x", size);
-    int r = _command_start(transport, cmd, size, 0);
+    std::string cmd(android::base::StringPrintf("download:%08x", size));
+    int r = _command_start(transport, cmd.c_str(), size, 0);
     if (r < 0) {
         return -1;
     }
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index e635e53..e3d4f87 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -48,8 +48,8 @@
 #include <linux/fs.h>
 #include <linux/loop.h>
 #include <linux/magic.h>
+#include <log/log_properties.h>
 #include <logwrap/logwrap.h>
-#include <private/android_logger.h>  // for __android_log_is_debuggable()
 
 #include "fs_mgr.h"
 #include "fs_mgr_avb.h"
@@ -128,18 +128,20 @@
     }
 }
 
+static bool should_force_check(int fs_stat) {
+    return fs_stat & (FS_STAT_E2FSCK_F_ALWAYS | FS_STAT_UNCLEAN_SHUTDOWN | FS_STAT_QUOTA_ENABLED |
+                      FS_STAT_TUNE2FS_FAILED | FS_STAT_RO_MOUNT_FAILED | FS_STAT_RO_UNMOUNT_FAILED |
+                      FS_STAT_FULL_MOUNT_FAILED | FS_STAT_E2FSCK_FAILED);
+}
+
 static void check_fs(const char *blk_device, char *fs_type, char *target, int *fs_stat)
 {
     int status;
     int ret;
     long tmpmnt_flags = MS_NOATIME | MS_NOEXEC | MS_NOSUID;
     char tmpmnt_opts[64] = "errors=remount-ro";
-    const char *e2fsck_argv[] = {
-        E2FSCK_BIN,
-        "-f",
-        "-y",
-        blk_device
-    };
+    const char* e2fsck_argv[] = {E2FSCK_BIN, "-y", blk_device};
+    const char* e2fsck_forced_argv[] = {E2FSCK_BIN, "-f", "-y", blk_device};
 
     /* Check for the types of filesystems we know how to check */
     if (!strcmp(fs_type, "ext2") || !strcmp(fs_type, "ext3") || !strcmp(fs_type, "ext4")) {
@@ -159,32 +161,35 @@
          * filesytsem due to an error, e2fsck is still run to do a full check
          * fix the filesystem.
          */
-        errno = 0;
-        if (!strcmp(fs_type, "ext4")) {
-            // This option is only valid with ext4
-            strlcat(tmpmnt_opts, ",nomblk_io_submit", sizeof(tmpmnt_opts));
-        }
-        ret = mount(blk_device, target, fs_type, tmpmnt_flags, tmpmnt_opts);
-        PINFO << __FUNCTION__ << "(): mount(" << blk_device <<  "," << target
-              << "," << fs_type << ")=" << ret;
-        if (!ret) {
-            int i;
-            for (i = 0; i < 5; i++) {
-                // Try to umount 5 times before continuing on.
-                // Should we try rebooting if all attempts fail?
-                int result = umount(target);
-                if (result == 0) {
-                    LINFO << __FUNCTION__ << "(): unmount(" << target
-                          << ") succeeded";
-                    break;
-                }
-                *fs_stat |= FS_STAT_RO_UNMOUNT_FAILED;
-                PERROR << __FUNCTION__ << "(): umount(" << target << ")="
-                       << result;
-                sleep(1);
+        if (!(*fs_stat & FS_STAT_FULL_MOUNT_FAILED)) {  // already tried if full mount failed
+            errno = 0;
+            if (!strcmp(fs_type, "ext4")) {
+                // This option is only valid with ext4
+                strlcat(tmpmnt_opts, ",nomblk_io_submit", sizeof(tmpmnt_opts));
             }
-        } else {
-            *fs_stat |= FS_STAT_RO_MOUNT_FAILED;
+            ret = mount(blk_device, target, fs_type, tmpmnt_flags, tmpmnt_opts);
+            PINFO << __FUNCTION__ << "(): mount(" << blk_device << "," << target << "," << fs_type
+                  << ")=" << ret;
+            if (!ret) {
+                bool umounted = false;
+                int retry_count = 5;
+                while (retry_count-- > 0) {
+                    umounted = umount(target) == 0;
+                    if (umounted) {
+                        LINFO << __FUNCTION__ << "(): unmount(" << target << ") succeeded";
+                        break;
+                    }
+                    PERROR << __FUNCTION__ << "(): umount(" << target << ") failed";
+                    if (retry_count) sleep(1);
+                }
+                if (!umounted) {
+                    // boot may fail but continue and leave it to later stage for now.
+                    PERROR << __FUNCTION__ << "(): umount(" << target << ") timed out";
+                    *fs_stat |= FS_STAT_RO_UNMOUNT_FAILED;
+                }
+            } else {
+                *fs_stat |= FS_STAT_RO_MOUNT_FAILED;
+            }
         }
 
         /*
@@ -196,14 +201,15 @@
                   << " (executable not in system image)";
         } else {
             LINFO << "Running " << E2FSCK_BIN << " on " << blk_device;
-
-            *fs_stat |= FS_STAT_E2FSCK_F_ALWAYS;
-            ret = android_fork_execvp_ext(ARRAY_SIZE(e2fsck_argv),
-                                          const_cast<char **>(e2fsck_argv),
-                                          &status, true, LOG_KLOG | LOG_FILE,
-                                          true,
-                                          const_cast<char *>(FSCK_LOG_FILE),
-                                          NULL, 0);
+            if (should_force_check(*fs_stat)) {
+                ret = android_fork_execvp_ext(
+                    ARRAY_SIZE(e2fsck_forced_argv), const_cast<char**>(e2fsck_forced_argv), &status,
+                    true, LOG_KLOG | LOG_FILE, true, const_cast<char*>(FSCK_LOG_FILE), NULL, 0);
+            } else {
+                ret = android_fork_execvp_ext(
+                    ARRAY_SIZE(e2fsck_argv), const_cast<char**>(e2fsck_argv), &status, true,
+                    LOG_KLOG | LOG_FILE, true, const_cast<char*>(FSCK_LOG_FILE), NULL, 0);
+            }
 
             if (ret < 0) {
                 /* No need to check for error in fork, we can't really handle it now */
@@ -574,21 +580,31 @@
                                  &fstab->recs[i], &fs_stat);
             }
 
-            if (!__mount(fstab->recs[i].blk_device, fstab->recs[i].mount_point, &fstab->recs[i])) {
-                *attempted_idx = i;
-                mounted = 1;
-                if (i != start_idx) {
-                    LERROR << __FUNCTION__ << "(): Mounted "
-                           << fstab->recs[i].blk_device << " on "
-                           << fstab->recs[i].mount_point << " with fs_type="
-                           << fstab->recs[i].fs_type << " instead of "
-                           << fstab->recs[start_idx].fs_type;
-                }
-            } else {
-                fs_stat |= FS_STAT_FULL_MOUNT_FAILED;
-                /* back up the first errno for crypto decisions */
-                if (mount_errno == 0) {
-                    mount_errno = errno;
+            int retry_count = 2;
+            while (retry_count-- > 0) {
+                if (!__mount(fstab->recs[i].blk_device, fstab->recs[i].mount_point,
+                             &fstab->recs[i])) {
+                    *attempted_idx = i;
+                    mounted = 1;
+                    if (i != start_idx) {
+                        LERROR << __FUNCTION__ << "(): Mounted " << fstab->recs[i].blk_device
+                               << " on " << fstab->recs[i].mount_point
+                               << " with fs_type=" << fstab->recs[i].fs_type << " instead of "
+                               << fstab->recs[start_idx].fs_type;
+                    }
+                    fs_stat &= ~FS_STAT_FULL_MOUNT_FAILED;
+                    mount_errno = 0;
+                    break;
+                } else {
+                    if (retry_count <= 0) break;  // run check_fs only once
+                    fs_stat |= FS_STAT_FULL_MOUNT_FAILED;
+                    /* back up the first errno for crypto decisions */
+                    if (mount_errno == 0) {
+                        mount_errno = errno;
+                    }
+                    // retry after fsck
+                    check_fs(fstab->recs[i].blk_device, fstab->recs[i].fs_type,
+                             fstab->recs[i].mount_point, &fs_stat);
                 }
             }
             log_fs_stat(fstab->recs[i].blk_device, fs_stat);
@@ -1074,17 +1090,22 @@
         } else {
             m = fstab->recs[i].mount_point;
         }
-        if (__mount(n_blk_device, m, &fstab->recs[i])) {
-            if (!first_mount_errno) first_mount_errno = errno;
-            mount_errors++;
-            fs_stat |= FS_STAT_FULL_MOUNT_FAILED;
-            log_fs_stat(fstab->recs[i].blk_device, fs_stat);
-            continue;
-        } else {
-            ret = 0;
-            log_fs_stat(fstab->recs[i].blk_device, fs_stat);
-            goto out;
+        int retry_count = 2;
+        while (retry_count-- > 0) {
+            if (!__mount(n_blk_device, m, &fstab->recs[i])) {
+                ret = 0;
+                fs_stat &= ~FS_STAT_FULL_MOUNT_FAILED;
+                goto out;
+            } else {
+                if (retry_count <= 0) break;  // run check_fs only once
+                if (!first_mount_errno) first_mount_errno = errno;
+                mount_errors++;
+                fs_stat |= FS_STAT_FULL_MOUNT_FAILED;
+                // try again after fsck
+                check_fs(n_blk_device, fstab->recs[i].fs_type, fstab->recs[i].mount_point, &fs_stat);
+            }
         }
+        log_fs_stat(fstab->recs[i].blk_device, fs_stat);
     }
     if (mount_errors) {
         PERROR << "Cannot mount filesystem on " << n_blk_device
diff --git a/fs_mgr/fs_mgr_avb.cpp b/fs_mgr/fs_mgr_avb.cpp
index 7c82bb1..83bf8a7 100644
--- a/fs_mgr/fs_mgr_avb.cpp
+++ b/fs_mgr/fs_mgr_avb.cpp
@@ -493,6 +493,10 @@
         return nullptr;
     }
 
+    // Sets the MAJOR.MINOR for init to set it into "ro.boot.avb_version".
+    avb_handle->avb_version_ =
+        android::base::StringPrintf("%d.%d", AVB_VERSION_MAJOR, AVB_VERSION_MINOR);
+
     // Verifies vbmeta images against the digest passed from bootloader.
     if (!avb_verifier->VerifyVbmetaImages(*avb_handle->avb_slot_data_)) {
         LERROR << "VerifyVbmetaImages failed";
diff --git a/fs_mgr/include/fs_mgr_avb.h b/fs_mgr/include/fs_mgr_avb.h
index 526a5ce..a66ff42 100644
--- a/fs_mgr/include/fs_mgr_avb.h
+++ b/fs_mgr/include/fs_mgr_avb.h
@@ -72,7 +72,8 @@
     // Otherwise, returns false.
     bool SetUpAvb(fstab_rec* fstab_entry, bool wait_for_verity_dev);
 
-    bool AvbHashtreeDisabled() { return status_ == kFsManagerAvbHandleHashtreeDisabled; }
+    bool hashtree_disabled() const { return status_ == kFsManagerAvbHandleHashtreeDisabled; }
+    const std::string& avb_version() const { return avb_version_; }
 
     FsManagerAvbHandle(const FsManagerAvbHandle&) = delete;             // no copy
     FsManagerAvbHandle& operator=(const FsManagerAvbHandle&) = delete;  // no assignment
@@ -92,6 +93,7 @@
   private:
     AvbSlotVerifyData* avb_slot_data_;
     FsManagerAvbHandleStatus status_;
+    std::string avb_version_;
 };
 
 #endif /* __CORE_FS_MGR_AVB_H */
diff --git a/init/README.md b/init/README.md
index fc50730..1eb42e0 100644
--- a/init/README.md
+++ b/init/README.md
@@ -16,9 +16,7 @@
 or options belong to the section most recently declared.  Commands
 or options before the first section are ignored.
 
-Actions and Services have unique names.  If a second Action is defined
-with the same name as an existing one, its commands are appended to
-the commands of the existing action.  If a second Service is defined
+Services have unique names.  If a second Service is defined
 with the same name as an existing one, it is ignored and an error
 message is logged.
 
@@ -31,13 +29,21 @@
 
 /init.rc is the primary .rc file and is loaded by the init executable
 at the beginning of its execution.  It is responsible for the initial
-set up of the system.  It imports /init.${ro.hardware}.rc which is the
-primary vendor supplied .rc file.
+set up of the system.
 
-During the mount\_all command, the init executable loads all of the
-files contained within the /{system,vendor,odm}/etc/init/ directories.
-These directories are intended for all Actions and Services used after
-file system mounting.
+Devices that mount /system, /vendor through the early mount mechanism
+load all of the files contained within the
+/{system,vendor,odm}/etc/init/ directories immediately after loading
+the primary /init.rc.  This is explained in more details in the
+Imports section of this file.
+
+Legacy devices without the early mount mechanism do the following:
+1. /init.rc imports /init.${ro.hardware}.rc which is the primary
+   vendor supplied .rc file.
+2. During the mount\_all command, the init executable loads all of the
+   files contained within the /{system,vendor,odm}/etc/init/ directories.
+   These directories are intended for all Actions and Services used after
+   file system mounting.
 
 One may specify paths in the mount\_all command line to have it import
 .rc files at the specified paths instead of the default ones listed above.
@@ -89,7 +95,7 @@
 Actions
 -------
 Actions are named sequences of commands.  Actions have a trigger which
-is used to determine when the action should occur.  When an event
+is used to determine when the action is executed.  When an event
 occurs which matches an action's trigger, that action is added to
 the tail of a to-be-executed queue (unless it is already on the
 queue).
@@ -106,6 +112,34 @@
        <command>
        <command>
 
+Actions are added to the queue and executed based on the order that
+the file that contains them was parsed (see the Imports section), then
+sequentially within an individual file.
+
+For example if a file contains:
+
+    on boot
+       setprop a 1
+       setprop b 2
+
+    on boot && property:true=true
+       setprop c 1
+       setprop d 2
+
+    on boot
+       setprop e 1
+       setprop f 2
+
+Then when the `boot` trigger occurs and assuming the property `true`
+equals `true`, then the order of the commands executed will be:
+
+    setprop a 1
+    setprop b 2
+    setprop c 1
+    setprop d 2
+    setprop e 1
+    setprop f 2
+
 
 Services
 --------
@@ -276,7 +310,8 @@
 
 `class_start <serviceclass>`
 > Start all services of the specified class if they are
-  not already running.
+  not already running.  See the start entry for more information on
+  starting services.
 
 `class_stop <serviceclass>`
 > Stop and disable all services of the specified class if they are
@@ -370,10 +405,6 @@
   _options_ include "barrier=1", "noauto\_da\_alloc", "discard", ... as
   a comma separated string, eg: barrier=1,noauto\_da\_alloc
 
-`powerctl`
-> Internal implementation detail used to respond to changes to the
-  "sys.powerctl" system property, used to implement rebooting.
-
 `restart <service>`
 > Stops and restarts a running service, does nothing if the service is currently
   restarting, otherwise, it just starts the service.
@@ -405,6 +436,16 @@
 
 `start <service>`
 > Start a service running if it is not already running.
+  Note that this is _not_ synchronous, and even if it were, there is
+  no guarantee that the operating system's scheduler will execute the
+  service sufficiently to guarantee anything about the service's status.
+
+> This creates an important consequence that if the service offers
+  functionality to other services, such as providing a
+  communication channel, simply starting this service before those
+  services is _not_ sufficient to guarantee that the channel has
+  been set up before those services ask for it.  There must be a
+  separate mechanism to make any such guarantees.
 
 `stop <service>`
 > Stop a service from running if it is currently running.
@@ -451,21 +492,54 @@
 
 Imports
 -------
-The import keyword is not a command, but rather its own section and is
-handled immediately after the .rc file that contains it has finished
-being parsed.  It takes the below form:
-
 `import <path>`
 > Parse an init config file, extending the current configuration.
   If _path_ is a directory, each file in the directory is parsed as
   a config file. It is not recursive, nested directories will
   not be parsed.
 
-There are only two times where the init executable imports .rc files:
+The import keyword is not a command, but rather its own section,
+meaning that it does not happen as part of an Action, but rather,
+imports are handled as a file is being parsed and follow the below logic.
 
-   1. When it imports /init.rc during initial boot
-   2. When it imports /{system,vendor,odm}/etc/init/ or .rc files at specified
-      paths during mount_all
+There are only three times where the init executable imports .rc files:
+
+   1. When it imports /init.rc or the script indicated by the property
+      `ro.boot.init_rc` during initial boot.
+   2. When it imports /{system,vendor,odm}/etc/init/ for early mount
+      devices immediately after importing /init.rc.
+   3. When it imports /{system,vendor,odm}/etc/init/ or .rc files at specified
+      paths during mount_all.
+
+The order that files are imported is a bit complex for legacy reasons
+and to keep backwards compatibility.  It is not strictly guaranteed.
+
+The only correct way to guarantee that a command has been run before a
+different command is to either 1) place it in an Action with an
+earlier executed trigger, or 2) place it in an Action with the same
+trigger within the same file at an earlier line.
+
+Nonetheless, the defacto order for early mount devices is:
+1. /init.rc is parsed then recursively each of its imports are
+   parsed.
+2. The contents of /system/etc/init/ are alphabetized and parsed
+   sequentially, with imports happening recursively after each file is
+   parsed.
+3. Step 2 is repeated for /vendor/etc/init then /odm/etc/init
+
+The below pseudocode may explain this more clearly:
+
+    fn Import(file)
+      Parse(file)
+      for (import : file.imports)
+        Import(import)
+
+    Import(/init.rc)
+    Directories = [/system/etc/init, /vendor/etc/init, /odm/etc/init]
+    for (directory : Directories)
+      files = <Alphabetical order of directory's contents>
+      for (file : files)
+        Import(file)
 
 
 Properties
diff --git a/init/action.cpp b/init/action.cpp
index 347edb0..c128968 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -26,10 +26,8 @@
 using android::base::Join;
 using android::base::StringPrintf;
 
-Command::Command(BuiltinFunction f, const std::vector<std::string>& args,
-                 const std::string& filename, int line)
-    : func_(f), args_(args), filename_(filename), line_(line) {
-}
+Command::Command(BuiltinFunction f, const std::vector<std::string>& args, int line)
+    : func_(f), args_(args), line_(line) {}
 
 int Command::InvokeFunc() const {
     std::vector<std::string> expanded_args;
@@ -49,21 +47,12 @@
     return Join(args_, ' ');
 }
 
-std::string Command::BuildSourceString() const {
-    if (!filename_.empty()) {
-        return StringPrintf(" (%s:%d)", filename_.c_str(), line_);
-    } else {
-        return std::string();
-    }
-}
-
-Action::Action(bool oneshot) : oneshot_(oneshot) {
-}
+Action::Action(bool oneshot, const std::string& filename, int line)
+    : oneshot_(oneshot), filename_(filename), line_(line) {}
 
 const KeywordMap<BuiltinFunction>* Action::function_map_ = nullptr;
 
-bool Action::AddCommand(const std::vector<std::string>& args,
-                        const std::string& filename, int line, std::string* err) {
+bool Action::AddCommand(const std::vector<std::string>& args, int line, std::string* err) {
     if (!function_map_) {
         *err = "no function map available";
         return false;
@@ -79,20 +68,12 @@
         return false;
     }
 
-    AddCommand(function, args, filename, line);
+    AddCommand(function, args, line);
     return true;
 }
 
-void Action::AddCommand(BuiltinFunction f,
-                        const std::vector<std::string>& args,
-                        const std::string& filename, int line) {
-    commands_.emplace_back(f, args, filename, line);
-}
-
-void Action::CombineAction(const Action& action) {
-    for (const auto& c : action.commands_) {
-        commands_.emplace_back(c);
-    }
+void Action::AddCommand(BuiltinFunction f, const std::vector<std::string>& args, int line) {
+    commands_.emplace_back(f, args, line);
 }
 
 std::size_t Action::NumCommands() const {
@@ -122,7 +103,7 @@
         android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
         std::string trigger_name = BuildTriggersString();
         std::string cmd_str = command.BuildCommandString();
-        std::string source = command.BuildSourceString();
+        std::string source = StringPrintf(" (%s:%d)", filename_.c_str(), command.line());
 
         LOG(INFO) << "Command '" << cmd_str << "' action=" << trigger_name << source
                   << " returned " << result << " took " << duration_ms << "ms.";
@@ -234,11 +215,6 @@
     return event_trigger_.empty() && CheckPropertyTriggers(name, value);
 }
 
-bool Action::TriggersEqual(const Action& other) const {
-    return property_triggers_ == other.property_triggers_ &&
-        event_trigger_ == other.event_trigger_;
-}
-
 std::string Action::BuildTriggersString() const {
     std::vector<std::string> triggers;
 
@@ -306,17 +282,7 @@
 }
 
 void ActionManager::AddAction(std::unique_ptr<Action> action) {
-    auto old_action_it =
-        std::find_if(actions_.begin(), actions_.end(),
-                     [&action] (std::unique_ptr<Action>& a) {
-                         return action->TriggersEqual(*a);
-                     });
-
-    if (old_action_it != actions_.end()) {
-        (*old_action_it)->CombineAction(*action);
-    } else {
-        actions_.emplace_back(std::move(action));
-    }
+    actions_.emplace_back(std::move(action));
 }
 
 void ActionManager::QueueEventTrigger(const std::string& trigger) {
@@ -332,16 +298,15 @@
     QueuePropertyTrigger("", "");
 }
 
-void ActionManager::QueueBuiltinAction(BuiltinFunction func,
-                                       const std::string& name) {
-    auto action = std::make_unique<Action>(true);
+void ActionManager::QueueBuiltinAction(BuiltinFunction func, const std::string& name) {
+    auto action = std::make_unique<Action>(true, "<Builtin Action>", 0);
     std::vector<std::string> name_vector{name};
 
     if (!action->InitSingleTrigger(name)) {
         return;
     }
 
-    action->AddCommand(func, name_vector);
+    action->AddCommand(func, name_vector, 0);
 
     trigger_queue_.push(std::make_unique<BuiltinTrigger>(action.get()));
     actions_.emplace_back(std::move(action));
@@ -366,7 +331,8 @@
 
     if (current_command_ == 0) {
         std::string trigger_name = action->BuildTriggersString();
-        LOG(INFO) << "processing action (" << trigger_name << ")";
+        LOG(INFO) << "processing action (" << trigger_name << ") from (" << action->filename()
+                  << ":" << action->line() << ")";
     }
 
     action->ExecuteOneCommand(current_command_);
@@ -397,15 +363,15 @@
     }
 }
 
-bool ActionParser::ParseSection(const std::vector<std::string>& args,
-                                std::string* err) {
+bool ActionParser::ParseSection(const std::vector<std::string>& args, const std::string& filename,
+                                int line, std::string* err) {
     std::vector<std::string> triggers(args.begin() + 1, args.end());
     if (triggers.size() < 1) {
         *err = "actions must have a trigger";
         return false;
     }
 
-    auto action = std::make_unique<Action>(false);
+    auto action = std::make_unique<Action>(false, filename, line);
     if (!action->InitTriggers(triggers, err)) {
         return false;
     }
@@ -414,10 +380,9 @@
     return true;
 }
 
-bool ActionParser::ParseLineSection(const std::vector<std::string>& args,
-                                    const std::string& filename, int line,
-                                    std::string* err) const {
-    return action_ ? action_->AddCommand(args, filename, line, err) : false;
+bool ActionParser::ParseLineSection(const std::vector<std::string>& args, int line,
+                                    std::string* err) {
+    return action_ ? action_->AddCommand(args, line, err) : false;
 }
 
 void ActionParser::EndSection() {
diff --git a/init/action.h b/init/action.h
index 0bae9f0..25e5a3e 100644
--- a/init/action.h
+++ b/init/action.h
@@ -27,31 +27,26 @@
 #include "keyword_map.h"
 
 class Command {
-public:
-    Command(BuiltinFunction f, const std::vector<std::string>& args,
-            const std::string& filename, int line);
+  public:
+    Command(BuiltinFunction f, const std::vector<std::string>& args, int line);
 
     int InvokeFunc() const;
     std::string BuildCommandString() const;
-    std::string BuildSourceString() const;
 
-private:
+    int line() const { return line_; }
+
+  private:
     BuiltinFunction func_;
     std::vector<std::string> args_;
-    std::string filename_;
     int line_;
 };
 
 class Action {
-public:
-    explicit Action(bool oneshot = false);
+  public:
+    explicit Action(bool oneshot, const std::string& filename, int line);
 
-    bool AddCommand(const std::vector<std::string>& args,
-                    const std::string& filename, int line, std::string* err);
-    void AddCommand(BuiltinFunction f,
-                    const std::vector<std::string>& args,
-                    const std::string& filename = "", int line = 0);
-    void CombineAction(const Action& action);
+    bool AddCommand(const std::vector<std::string>& args, int line, std::string* err);
+    void AddCommand(BuiltinFunction f, const std::vector<std::string>& args, int line);
     bool InitTriggers(const std::vector<std::string>& args, std::string* err);
     bool InitSingleTrigger(const std::string& trigger);
     std::size_t NumCommands() const;
@@ -60,11 +55,12 @@
     bool CheckEventTrigger(const std::string& trigger) const;
     bool CheckPropertyTrigger(const std::string& name,
                               const std::string& value) const;
-    bool TriggersEqual(const Action& other) const;
     std::string BuildTriggersString() const;
     void DumpState() const;
 
     bool oneshot() const { return oneshot_; }
+    const std::string& filename() const { return filename_; }
+    int line() const { return line_; }
     static void set_function_map(const KeywordMap<BuiltinFunction>* function_map) {
         function_map_ = function_map;
     }
@@ -80,6 +76,8 @@
     std::string event_trigger_;
     std::vector<Command> commands_;
     bool oneshot_;
+    std::string filename_;
+    int line_;
     static const KeywordMap<BuiltinFunction>* function_map_;
 };
 
@@ -115,18 +113,17 @@
 };
 
 class ActionParser : public SectionParser {
-public:
+  public:
     ActionParser() : action_(nullptr) {
     }
-    bool ParseSection(const std::vector<std::string>& args,
+    bool ParseSection(const std::vector<std::string>& args, const std::string& filename, int line,
                       std::string* err) override;
-    bool ParseLineSection(const std::vector<std::string>& args,
-                          const std::string& filename, int line,
-                          std::string* err) const override;
+    bool ParseLineSection(const std::vector<std::string>& args, int line, std::string* err) override;
     void EndSection() override;
     void EndFile(const std::string&) override {
     }
-private:
+
+  private:
     std::unique_ptr<Action> action_;
 };
 
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 2327cdf..43eb420 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -598,58 +598,6 @@
     return 0;
 }
 
-static int do_powerctl(const std::vector<std::string>& args) {
-    const std::string& command = args[1];
-    unsigned int cmd = 0;
-    std::vector<std::string> cmd_params = android::base::Split(command, ",");
-    std::string reason_string = cmd_params[0];
-    std::string reboot_target = "";
-    bool runFsck = false;
-    bool commandInvalid = false;
-
-    if (cmd_params.size() > 3) {
-        commandInvalid = true;
-    } else if (cmd_params[0] == "shutdown") {
-        cmd = ANDROID_RB_POWEROFF;
-        if (cmd_params.size() == 2 && cmd_params[1] == "userrequested") {
-            // The shutdown reason is PowerManager.SHUTDOWN_USER_REQUESTED.
-            // Run fsck once the file system is remounted in read-only mode.
-            runFsck = true;
-            reason_string = cmd_params[1];
-        }
-    } else if (cmd_params[0] == "reboot") {
-        cmd = ANDROID_RB_RESTART2;
-        if (cmd_params.size() >= 2) {
-            reboot_target = cmd_params[1];
-            // When rebooting to the bootloader notify the bootloader writing
-            // also the BCB.
-            if (reboot_target == "bootloader") {
-                std::string err;
-                if (!write_reboot_bootloader(&err)) {
-                    LOG(ERROR) << "reboot-bootloader: Error writing "
-                                  "bootloader_message: "
-                               << err;
-                }
-            }
-            // If there is an additional bootloader parameter, pass it along
-            if (cmd_params.size() == 3) {
-                reboot_target += "," + cmd_params[2];
-            }
-        }
-    } else if (command == "thermal-shutdown") {  // no additional parameter allowed
-        cmd = ANDROID_RB_THERMOFF;
-    } else {
-        commandInvalid = true;
-    }
-    if (commandInvalid) {
-        LOG(ERROR) << "powerctl: unrecognized command '" << command << "'";
-        return -EINVAL;
-    }
-
-    DoReboot(cmd, reason_string, reboot_target, runFsck);
-    return 0;
-}
-
 static int do_trigger(const std::vector<std::string>& args) {
     ActionManager::GetInstance().QueueEventTrigger(args[1]);
     return 0;
@@ -919,7 +867,6 @@
         {"mount_all",               {1,     kMax, do_mount_all}},
         {"mount",                   {3,     kMax, do_mount}},
         {"umount",                  {1,     1,    do_umount}},
-        {"powerctl",                {1,     1,    do_powerctl}},
         {"restart",                 {1,     1,    do_restart}},
         {"restorecon",              {1,     kMax, do_restorecon}},
         {"restorecon_recursive",    {1,     kMax, do_restorecon_recursive}},
diff --git a/init/devices.cpp b/init/devices.cpp
index ad313a0..6e13863 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "devices.h"
+
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -27,23 +29,19 @@
 #include <sys/sendfile.h>
 #include <sys/socket.h>
 #include <sys/time.h>
-#include <sys/types.h>
 #include <sys/un.h>
 #include <sys/wait.h>
 #include <unistd.h>
 
 #include <algorithm>
 #include <memory>
-#include <string>
 #include <thread>
-#include <vector>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
-#include <cutils/list.h>
 #include <cutils/uevent.h>
 #include <private/android_filesystem_config.h>
 #include <selinux/android.h>
@@ -51,119 +49,67 @@
 #include <selinux/label.h>
 #include <selinux/selinux.h>
 
-#include "devices.h"
 #include "ueventd_parser.h"
 #include "util.h"
 
-#define SYSFS_PREFIX    "/sys"
-
 extern struct selabel_handle *sehandle;
 
 static android::base::unique_fd device_fd;
 
-struct perms_ {
-    char *name;
-    char *attr;
-    mode_t perm;
-    unsigned int uid;
-    unsigned int gid;
-    unsigned short prefix;
-    unsigned short wildcard;
-};
-
-struct perm_node {
-    struct perms_ dp;
-    struct listnode plist;
-};
-
-static list_declare(sys_perms);
-static list_declare(dev_perms);
-
-int add_dev_perms(const char *name, const char *attr,
-                  mode_t perm, unsigned int uid, unsigned int gid,
-                  unsigned short prefix,
-                  unsigned short wildcard) {
-    struct perm_node *node = (perm_node*) calloc(1, sizeof(*node));
-    if (!node)
-        return -ENOMEM;
-
-    node->dp.name = strdup(name);
-    if (!node->dp.name) {
-        free(node);
-        return -ENOMEM;
+Permissions::Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid)
+    : name_(name), perm_(perm), uid_(uid), gid_(gid), prefix_(false), wildcard_(false) {
+    // If the first * is the last character, then we'll treat name_ as a prefix
+    // Otherwise, if a * is present, then we do a full fnmatch().
+    auto wildcard_position = name_.find('*');
+    if (wildcard_position == name_.length() - 1) {
+        prefix_ = true;
+        name_.pop_back();
+    } else if (wildcard_position != std::string::npos) {
+        wildcard_ = true;
     }
-
-    if (attr) {
-        node->dp.attr = strdup(attr);
-        if (!node->dp.attr) {
-            free(node->dp.name);
-            free(node);
-            return -ENOMEM;
-        }
-    }
-
-    node->dp.perm = perm;
-    node->dp.uid = uid;
-    node->dp.gid = gid;
-    node->dp.prefix = prefix;
-    node->dp.wildcard = wildcard;
-
-    if (attr)
-        list_add_tail(&sys_perms, &node->plist);
-    else
-        list_add_tail(&dev_perms, &node->plist);
-
-    return 0;
 }
 
-static bool perm_path_matches(const char *path, struct perms_ *dp)
-{
-    if (dp->prefix) {
-        if (strncmp(path, dp->name, strlen(dp->name)) == 0)
-            return true;
-    } else if (dp->wildcard) {
-        if (fnmatch(dp->name, path, FNM_PATHNAME) == 0)
-            return true;
+bool Permissions::Match(const std::string& path) const {
+    if (prefix_) {
+        return android::base::StartsWith(path, name_.c_str());
+    } else if (wildcard_) {
+        return fnmatch(name_.c_str(), path.c_str(), FNM_PATHNAME) == 0;
     } else {
-        if (strcmp(path, dp->name) == 0)
-            return true;
+        return path == name_;
     }
 
     return false;
 }
 
-static bool match_subsystem(perms_* dp, const char* pattern,
-                            const char* path, const char* subsystem) {
-    if (!pattern || !subsystem || strstr(dp->name, subsystem) == NULL) {
-        return false;
+bool SysfsPermissions::MatchWithSubsystem(const std::string& path,
+                                          const std::string& subsystem) const {
+    std::string path_basename = android::base::Basename(path);
+    if (name().find(subsystem) != std::string::npos) {
+        if (Match("/sys/class/" + subsystem + "/" + path_basename)) return true;
+        if (Match("/sys/bus/" + subsystem + "/devices/" + path_basename)) return true;
     }
-
-    std::string subsys_path = android::base::StringPrintf(pattern, subsystem, basename(path));
-    return perm_path_matches(subsys_path.c_str(), dp);
+    return Match(path);
 }
 
-static void fixup_sys_perms(const std::string& upath, const std::string& subsystem) {
+void SysfsPermissions::SetPermissions(const std::string& path) const {
+    std::string attribute_file = path + "/" + attribute_;
+    LOG(INFO) << "fixup " << attribute_file << " " << uid() << " " << gid() << " " << std::oct
+              << perm();
+    chown(attribute_file.c_str(), uid(), gid());
+    chmod(attribute_file.c_str(), perm());
+}
+
+// TODO: Move these to be member variables of a future devices class.
+std::vector<Permissions> dev_permissions;
+std::vector<SysfsPermissions> sysfs_permissions;
+
+static void fixup_sys_permissions(const std::string& upath, const std::string& subsystem) {
     // upaths omit the "/sys" that paths in this list
     // contain, so we prepend it...
-    std::string path = SYSFS_PREFIX + upath;
+    std::string path = "/sys" + upath;
 
-    listnode* node;
-    list_for_each(node, &sys_perms) {
-        perms_* dp = &(node_to_item(node, perm_node, plist))->dp;
-        if (match_subsystem(dp, SYSFS_PREFIX "/class/%s/%s", path.c_str(), subsystem.c_str())) {
-            ; // matched
-        } else if (match_subsystem(dp, SYSFS_PREFIX "/bus/%s/devices/%s", path.c_str(),
-                                   subsystem.c_str())) {
-            ; // matched
-        } else if (!perm_path_matches(path.c_str(), dp)) {
-            continue;
-        }
-
-        std::string attr_file = path + "/" + dp->attr;
-        LOG(INFO) << "fixup " << attr_file
-                  << " " << dp->uid << " " << dp->gid << " " << std::oct << dp->perm;
-        chown(attr_file.c_str(), dp->uid, dp->gid);
-        chmod(attr_file.c_str(), dp->perm);
+    for (const auto& s : sysfs_permissions) {
+        if (s.MatchWithSubsystem(path, subsystem)) s.SetPermissions(path);
     }
 
     if (access(path.c_str(), F_OK) == 0) {
@@ -172,42 +118,26 @@
     }
 }
 
-static mode_t get_device_perm(const char* path, const std::vector<std::string>& links,
-                              unsigned* uid, unsigned* gid) {
-    struct listnode *node;
-    struct perm_node *perm_node;
-    struct perms_ *dp;
-
-    /* search the perms list in reverse so that ueventd.$hardware can
-     * override ueventd.rc
-     */
-    list_for_each_reverse(node, &dev_perms) {
-        perm_node = node_to_item(node, struct perm_node, plist);
-        dp = &perm_node->dp;
-
-        if (perm_path_matches(path, dp) ||
-            std::any_of(links.begin(), links.end(),
-                        [dp](const auto& link) { return perm_path_matches(link.c_str(), dp); })) {
-            *uid = dp->uid;
-            *gid = dp->gid;
-            return dp->perm;
+static std::tuple<mode_t, uid_t, gid_t> get_device_permissions(
+    const std::string& path, const std::vector<std::string>& links) {
+    // Search the perms list in reverse so that ueventd.$hardware can override ueventd.rc.
+    for (auto it = dev_permissions.rbegin(); it != dev_permissions.rend(); ++it) {
+        if (it->Match(path) || std::any_of(links.begin(), links.end(),
+                                           [it](const auto& link) { return it->Match(link); })) {
+            return {it->perm(), it->uid(), it->gid()};
         }
     }
     /* Default if nothing found. */
-    *uid = 0;
-    *gid = 0;
-    return 0600;
+    return {0600, 0, 0};
 }
 
 static void make_device(const std::string& path, int block, int major, int minor,
                         const std::vector<std::string>& links) {
-    unsigned uid;
-    unsigned gid;
-    mode_t mode;
     dev_t dev;
     char *secontext = NULL;
 
-    mode = get_device_perm(path.c_str(), links, &uid, &gid) | (block ? S_IFBLK : S_IFCHR);
+    auto [mode, uid, gid] = get_device_permissions(path, links);
+    mode |= (block ? S_IFBLK : S_IFCHR);
 
     if (sehandle) {
         std::vector<const char*> c_links;
@@ -607,7 +537,7 @@
 static void handle_device_event(struct uevent *uevent)
 {
     if (uevent->action == "add" || uevent->action == "change" || uevent->action == "online") {
-        fixup_sys_perms(uevent->path, uevent->subsystem);
+        fixup_sys_permissions(uevent->path, uevent->subsystem);
     }
 
     if (uevent->subsystem == "block") {
diff --git a/init/devices.h b/init/devices.h
index b8b039f..2cbae66 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -18,6 +18,7 @@
 #define _INIT_DEVICES_H
 
 #include <sys/stat.h>
+#include <sys/types.h>
 
 #include <functional>
 #include <string>
@@ -47,15 +48,48 @@
     int minor;
 };
 
+class Permissions {
+  public:
+    Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid);
+
+    bool Match(const std::string& path) const;
+
+    mode_t perm() const { return perm_; }
+    uid_t uid() const { return uid_; }
+    gid_t gid() const { return gid_; }
+
+  protected:
+    const std::string& name() const { return name_; }
+
+  private:
+    std::string name_;
+    mode_t perm_;
+    uid_t uid_;
+    gid_t gid_;
+    bool prefix_;
+    bool wildcard_;
+};
+
+class SysfsPermissions : public Permissions {
+  public:
+    SysfsPermissions(const std::string& name, const std::string& attribute, mode_t perm, uid_t uid,
+                     gid_t gid)
+        : Permissions(name, perm, uid, gid), attribute_(attribute) {}
+
+    bool MatchWithSubsystem(const std::string& path, const std::string& subsystem) const;
+    void SetPermissions(const std::string& path) const;
+
+  private:
+    const std::string attribute_;
+};
+
+extern std::vector<Permissions> dev_permissions;
+extern std::vector<SysfsPermissions> sysfs_permissions;
+
 typedef std::function<coldboot_action_t(struct uevent* uevent)> coldboot_callback;
 extern coldboot_action_t handle_device_fd(coldboot_callback fn = nullptr);
 extern void device_init(const char* path = nullptr, coldboot_callback fn = nullptr);
 extern void device_close();
-
-extern int add_dev_perms(const char *name, const char *attr,
-                         mode_t perm, unsigned int uid,
-                         unsigned int gid, unsigned short prefix,
-                         unsigned short wildcard);
 int get_device_fd();
 
 // Exposed for testing
diff --git a/init/devices_test.cpp b/init/devices_test.cpp
index 693fb54..66521db 100644
--- a/init/devices_test.cpp
+++ b/init/devices_test.cpp
@@ -316,3 +316,94 @@
     sanitize_partition_name(&string);
     EXPECT_EQ("_", string);
 }
+
+TEST(devices, DevPermissionsMatchNormal) {
+    // Basic from ueventd.rc
+    // /dev/null                 0666   root       root
+    Permissions permissions("/dev/null", 0666, 0, 0);
+    EXPECT_TRUE(permissions.Match("/dev/null"));
+    EXPECT_FALSE(permissions.Match("/dev/nullsuffix"));
+    EXPECT_FALSE(permissions.Match("/dev/nul"));
+    EXPECT_EQ(0666U, permissions.perm());
+    EXPECT_EQ(0U, permissions.uid());
+    EXPECT_EQ(0U, permissions.gid());
+}
+
+TEST(devices, DevPermissionsMatchPrefix) {
+    // Prefix from ueventd.rc
+    // /dev/dri/*                0666   root       graphics
+    Permissions permissions("/dev/dri/*", 0666, 0, 1000);
+    EXPECT_TRUE(permissions.Match("/dev/dri/some_dri_device"));
+    EXPECT_TRUE(permissions.Match("/dev/dri/some_other_dri_device"));
+    EXPECT_TRUE(permissions.Match("/dev/dri/"));
+    EXPECT_FALSE(permissions.Match("/dev/dr/non_match"));
+    EXPECT_EQ(0666U, permissions.perm());
+    EXPECT_EQ(0U, permissions.uid());
+    EXPECT_EQ(1000U, permissions.gid());
+}
+
+TEST(devices, DevPermissionsMatchWildcard) {
+    // Wildcard example
+    // /dev/device*name                0666   root       graphics
+    Permissions permissions("/dev/device*name", 0666, 0, 1000);
+    EXPECT_TRUE(permissions.Match("/dev/devicename"));
+    EXPECT_TRUE(permissions.Match("/dev/device123name"));
+    EXPECT_TRUE(permissions.Match("/dev/deviceabcname"));
+    EXPECT_FALSE(permissions.Match("/dev/device123name/subdevice"));
+    EXPECT_FALSE(permissions.Match("/dev/deviceame"));
+    EXPECT_EQ(0666U, permissions.perm());
+    EXPECT_EQ(0U, permissions.uid());
+    EXPECT_EQ(1000U, permissions.gid());
+}
+
+TEST(devices, DevPermissionsMatchWildcardPrefix) {
+    // Wildcard+Prefix example
+    // /dev/device*name*                0666   root       graphics
+    Permissions permissions("/dev/device*name*", 0666, 0, 1000);
+    EXPECT_TRUE(permissions.Match("/dev/devicename"));
+    EXPECT_TRUE(permissions.Match("/dev/device123name"));
+    EXPECT_TRUE(permissions.Match("/dev/deviceabcname"));
+    EXPECT_TRUE(permissions.Match("/dev/device123namesomething"));
+    // FNM_PATHNAME doesn't match '/' with *
+    EXPECT_FALSE(permissions.Match("/dev/device123name/something"));
+    EXPECT_FALSE(permissions.Match("/dev/deviceame"));
+    EXPECT_EQ(0666U, permissions.perm());
+    EXPECT_EQ(0U, permissions.uid());
+    EXPECT_EQ(1000U, permissions.gid());
+}
+
+TEST(devices, SysfsPermissionsMatchWithSubsystemNormal) {
+    // /sys/devices/virtual/input/input*   enable      0660  root   input
+    SysfsPermissions permissions("/sys/devices/virtual/input/input*", "enable", 0660, 0, 1001);
+    EXPECT_TRUE(permissions.MatchWithSubsystem("/sys/devices/virtual/input/input0", "input"));
+    EXPECT_FALSE(permissions.MatchWithSubsystem("/sys/devices/virtual/input/not_input0", "input"));
+    EXPECT_EQ(0660U, permissions.perm());
+    EXPECT_EQ(0U, permissions.uid());
+    EXPECT_EQ(1001U, permissions.gid());
+}
+
+TEST(devices, SysfsPermissionsMatchWithSubsystemClass) {
+    // /sys/class/input/event*   enable      0660  root   input
+    SysfsPermissions permissions("/sys/class/input/event*", "enable", 0660, 0, 1001);
+    EXPECT_TRUE(permissions.MatchWithSubsystem(
+        "/sys/devices/soc.0/f9924000.i2c/i2c-2/2-0020/input/input0/event0", "input"));
+    EXPECT_FALSE(permissions.MatchWithSubsystem(
+        "/sys/devices/soc.0/f9924000.i2c/i2c-2/2-0020/input/input0/not_event0", "input"));
+    EXPECT_FALSE(permissions.MatchWithSubsystem(
+        "/sys/devices/soc.0/f9924000.i2c/i2c-2/2-0020/input/input0/event0", "not_input"));
+    EXPECT_EQ(0660U, permissions.perm());
+    EXPECT_EQ(0U, permissions.uid());
+    EXPECT_EQ(1001U, permissions.gid());
+}
+
+TEST(devices, SysfsPermissionsMatchWithSubsystemBus) {
+    // /sys/bus/i2c/devices/i2c-*   enable      0660  root   input
+    SysfsPermissions permissions("/sys/bus/i2c/devices/i2c-*", "enable", 0660, 0, 1001);
+    EXPECT_TRUE(permissions.MatchWithSubsystem("/sys/devices/soc.0/f9967000.i2c/i2c-5", "i2c"));
+    EXPECT_FALSE(permissions.MatchWithSubsystem("/sys/devices/soc.0/f9967000.i2c/not-i2c", "i2c"));
+    EXPECT_FALSE(
+        permissions.MatchWithSubsystem("/sys/devices/soc.0/f9967000.i2c/i2c-5", "not_i2c"));
+    EXPECT_EQ(0660U, permissions.perm());
+    EXPECT_EQ(0U, permissions.uid());
+    EXPECT_EQ(1001U, permissions.gid());
+}
diff --git a/init/import_parser.cpp b/init/import_parser.cpp
index 8a2bcc2..f66b2ba 100644
--- a/init/import_parser.cpp
+++ b/init/import_parser.cpp
@@ -20,8 +20,8 @@
 
 #include "util.h"
 
-bool ImportParser::ParseSection(const std::vector<std::string>& args,
-                                std::string* err) {
+bool ImportParser::ParseSection(const std::vector<std::string>& args, const std::string& filename,
+                                int line, std::string* err) {
     if (args.size() != 2) {
         *err = "single argument needed for import\n";
         return false;
diff --git a/init/import_parser.h b/init/import_parser.h
index 0e91025..e15d555 100644
--- a/init/import_parser.h
+++ b/init/import_parser.h
@@ -23,20 +23,20 @@
 #include <vector>
 
 class ImportParser : public SectionParser {
-public:
+  public:
     ImportParser()  {
     }
-    bool ParseSection(const std::vector<std::string>& args,
+    bool ParseSection(const std::vector<std::string>& args, const std::string& filename, int line,
                       std::string* err) override;
-    bool ParseLineSection(const std::vector<std::string>& args,
-                          const std::string& filename, int line,
-                          std::string* err) const override {
+    bool ParseLineSection(const std::vector<std::string>& args, int line,
+                          std::string* err) override {
         return true;
     }
     void EndSection() override {
     }
     void EndFile(const std::string& filename) override;
-private:
+
+  private:
     std::vector<std::string> imports_;
 };
 
diff --git a/init/init.cpp b/init/init.cpp
index 6c1c541..9a4aa24 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -67,6 +67,7 @@
 #include "keychords.h"
 #include "log.h"
 #include "property_service.h"
+#include "reboot.h"
 #include "service.h"
 #include "signal_handler.h"
 #include "ueventd.h"
@@ -153,8 +154,13 @@
     return true;
 }
 
-void property_changed(const char *name, const char *value)
-{
+void property_changed(const std::string& name, const std::string& value) {
+    // If the property is sys.powerctl, we bypass the event queue and immediately handle it.
+    // This is to ensure that init will always and immediately shutdown/reboot, regardless of
+    // if there are other pending events to process or if init is waiting on an exec service or
+    // waiting on a property.
+    if (name == "sys.powerctl") HandlePowerctlMessage(value);
+
     if (property_triggers_enabled)
         ActionManager::GetInstance().QueuePropertyTrigger(name, value);
     if (waiting_for_prop) {
@@ -1010,10 +1016,11 @@
         return false;
     }
 
+    setenv("INIT_AVB_VERSION", avb_handle->avb_version().c_str(), 1);
     for (auto rec : fstab_recs) {
         bool need_create_dm_device = false;
         if (fs_mgr_is_avb(rec)) {
-            if (avb_handle->AvbHashtreeDisabled()) {
+            if (avb_handle->hashtree_disabled()) {
                 LOG(INFO) << "avb hashtree disabled for '" << rec->mount_point << "'";
             } else if (avb_handle->SetUpAvb(rec, false /* wait_for_verity_dev */)) {
                 need_create_dm_device = true;
@@ -1362,12 +1369,14 @@
     property_set("ro.boottime.init.selinux", getenv("INIT_SELINUX_TOOK"));
 
     // Set libavb version for Framework-only OTA match in Treble build.
-    property_set("ro.boot.init.avb_version", std::to_string(AVB_MAJOR_VERSION).c_str());
+    const char* avb_version = getenv("INIT_AVB_VERSION");
+    if (avb_version) property_set("ro.boot.avb_version", avb_version);
 
     // Clean up our environment.
     unsetenv("INIT_SECOND_STAGE");
     unsetenv("INIT_STARTED_AT");
     unsetenv("INIT_SELINUX_TOOK");
+    unsetenv("INIT_AVB_VERSION");
 
     // Now set up SELinux for second stage.
     selinux_initialize(false);
diff --git a/init/init.h b/init/init.h
index fe850ef..1da3350 100644
--- a/init/init.h
+++ b/init/init.h
@@ -26,7 +26,7 @@
 
 void handle_control_message(const std::string& msg, const std::string& arg);
 
-void property_changed(const char *name, const char *value);
+void property_changed(const std::string& name, const std::string& value);
 
 void register_epoll_handler(int fd, void (*fn)());
 
diff --git a/init/init_parser.cpp b/init/init_parser.cpp
index 53e670b..b425497 100644
--- a/init/init_parser.cpp
+++ b/init/init_parser.cpp
@@ -71,14 +71,13 @@
                 }
                 section_parser = section_parsers_[args[0]].get();
                 std::string ret_err;
-                if (!section_parser->ParseSection(args, &ret_err)) {
+                if (!section_parser->ParseSection(args, state.filename, state.line, &ret_err)) {
                     parse_error(&state, "%s\n", ret_err.c_str());
                     section_parser = nullptr;
                 }
             } else if (section_parser) {
                 std::string ret_err;
-                if (!section_parser->ParseLineSection(args, state.filename,
-                                                      state.line, &ret_err)) {
+                if (!section_parser->ParseLineSection(args, state.line, &ret_err)) {
                     parse_error(&state, "%s\n", ret_err.c_str());
                 }
             }
diff --git a/init/init_parser.h b/init/init_parser.h
index 6935fdf..64f0cac 100644
--- a/init/init_parser.h
+++ b/init/init_parser.h
@@ -26,11 +26,10 @@
 public:
     virtual ~SectionParser() {
     }
-    virtual bool ParseSection(const std::vector<std::string>& args,
-                              std::string* err) = 0;
-    virtual bool ParseLineSection(const std::vector<std::string>& args,
-                                  const std::string& filename, int line,
-                                  std::string* err) const = 0;
+    virtual bool ParseSection(const std::vector<std::string>& args, const std::string& filename,
+                              int line, std::string* err) = 0;
+    virtual bool ParseLineSection(const std::vector<std::string>& args, int line,
+                                  std::string* err) = 0;
     virtual void EndSection() = 0;
     virtual void EndFile(const std::string& filename) = 0;
 };
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 2aa89ff..20a2aa1 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -205,7 +205,7 @@
     if (persistent_properties_loaded && android::base::StartsWith(name, "persist.")) {
         write_persistent_property(name.c_str(), value.c_str());
     }
-    property_changed(name.c_str(), value.c_str());
+    property_changed(name, value);
     return PROP_SUCCESS;
 }
 
diff --git a/init/reboot.cpp b/init/reboot.cpp
index d9ebd91..4d65437 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -425,3 +425,54 @@
     RebootSystem(cmd, rebootTarget);
     abort();
 }
+
+bool HandlePowerctlMessage(const std::string& command) {
+    unsigned int cmd = 0;
+    std::vector<std::string> cmd_params = android::base::Split(command, ",");
+    std::string reason_string = cmd_params[0];
+    std::string reboot_target = "";
+    bool run_fsck = false;
+    bool command_invalid = false;
+
+    if (cmd_params.size() > 3) {
+        command_invalid = true;
+    } else if (cmd_params[0] == "shutdown") {
+        cmd = ANDROID_RB_POWEROFF;
+        if (cmd_params.size() == 2 && cmd_params[1] == "userrequested") {
+            // The shutdown reason is PowerManager.SHUTDOWN_USER_REQUESTED.
+            // Run fsck once the file system is remounted in read-only mode.
+            run_fsck = true;
+            reason_string = cmd_params[1];
+        }
+    } else if (cmd_params[0] == "reboot") {
+        cmd = ANDROID_RB_RESTART2;
+        if (cmd_params.size() >= 2) {
+            reboot_target = cmd_params[1];
+            // When rebooting to the bootloader notify the bootloader writing
+            // also the BCB.
+            if (reboot_target == "bootloader") {
+                std::string err;
+                if (!write_reboot_bootloader(&err)) {
+                    LOG(ERROR) << "reboot-bootloader: Error writing "
+                                  "bootloader_message: "
+                               << err;
+                }
+            }
+            // If there is an additional bootloader parameter, pass it along
+            if (cmd_params.size() == 3) {
+                reboot_target += "," + cmd_params[2];
+            }
+        }
+    } else if (command == "thermal-shutdown") {  // no additional parameter allowed
+        cmd = ANDROID_RB_THERMOFF;
+    } else {
+        command_invalid = true;
+    }
+    if (command_invalid) {
+        LOG(ERROR) << "powerctl: unrecognized command '" << command << "'";
+        return false;
+    }
+
+    DoReboot(cmd, reason_string, reboot_target, run_fsck);
+    return true;
+}
diff --git a/init/reboot.h b/init/reboot.h
index 6432fa5..b304b3c 100644
--- a/init/reboot.h
+++ b/init/reboot.h
@@ -29,4 +29,7 @@
 void DoReboot(unsigned int cmd, const std::string& reason, const std::string& rebootTarget,
               bool runFsck) __attribute__((__noreturn__));
 
+// Parses and handles a setprop sys.powerctl message.
+bool HandlePowerctlMessage(const std::string& command);
+
 #endif
diff --git a/init/service.cpp b/init/service.cpp
index 4adbbf0..caf5785 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -157,6 +157,7 @@
       gid_(0),
       namespace_flags_(0),
       seclabel_(""),
+      onrestart_(false, "<Service '" + name + "' onrestart>", 0),
       ioprio_class_(IoSchedClass_NONE),
       ioprio_pri_(0),
       priority_(0),
@@ -180,6 +181,7 @@
       capabilities_(capabilities),
       namespace_flags_(namespace_flags),
       seclabel_(seclabel),
+      onrestart_(false, "<Service '" + name + "' onrestart>", 0),
       ioprio_class_(IoSchedClass_NONE),
       ioprio_pri_(0),
       priority_(0),
@@ -438,7 +440,8 @@
 
 bool Service::ParseOnrestart(const std::vector<std::string>& args, std::string* err) {
     std::vector<std::string> str_args(args.begin() + 1, args.end());
-    onrestart_.AddCommand(str_args, "", 0, err);
+    int line = onrestart_.NumCommands() + 1;
+    onrestart_.AddCommand(str_args, line, err);
     return true;
 }
 
@@ -1092,8 +1095,8 @@
     }
 }
 
-bool ServiceParser::ParseSection(const std::vector<std::string>& args,
-                                 std::string* err) {
+bool ServiceParser::ParseSection(const std::vector<std::string>& args, const std::string& filename,
+                                 int line, std::string* err) {
     if (args.size() < 3) {
         *err = "services must have a name and a program";
         return false;
@@ -1110,9 +1113,8 @@
     return true;
 }
 
-bool ServiceParser::ParseLineSection(const std::vector<std::string>& args,
-                                     const std::string& filename, int line,
-                                     std::string* err) const {
+bool ServiceParser::ParseLineSection(const std::vector<std::string>& args, int line,
+                                     std::string* err) {
     return service_ ? service_->ParseLine(args, err) : false;
 }
 
diff --git a/init/service.h b/init/service.h
index 5e89b9f..6fe0258 100644
--- a/init/service.h
+++ b/init/service.h
@@ -212,18 +212,15 @@
 };
 
 class ServiceParser : public SectionParser {
-public:
-    ServiceParser() : service_(nullptr) {
-    }
-    bool ParseSection(const std::vector<std::string>& args,
+  public:
+    ServiceParser() : service_(nullptr) {}
+    bool ParseSection(const std::vector<std::string>& args, const std::string& filename, int line,
                       std::string* err) override;
-    bool ParseLineSection(const std::vector<std::string>& args,
-                          const std::string& filename, int line,
-                          std::string* err) const override;
+    bool ParseLineSection(const std::vector<std::string>& args, int line, std::string* err) override;
     void EndSection() override;
-    void EndFile(const std::string&) override {
-    }
-private:
+    void EndFile(const std::string&) override {}
+
+  private:
     bool IsValidName(const std::string& name) const;
 
     std::unique_ptr<Service> service_;
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index ea767a8..b6c6a01 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -101,8 +101,6 @@
     mode_t perm;
     uid_t uid;
     gid_t gid;
-    int prefix = 0;
-    int wildcard = 0;
     char *endptr;
 
     if (nargs == 0)
@@ -125,15 +123,6 @@
         return;
     }
 
-    int len = strlen(name);
-    char *wildcard_chr = strchr(name, '*');
-    if ((name[len - 1] == '*') && (wildcard_chr == (name + len - 1))) {
-        prefix = 1;
-        name[len - 1] = '\0';
-    } else if (wildcard_chr) {
-        wildcard = 1;
-    }
-
     perm = strtol(args[1], &endptr, 8);
     if (!endptr || *endptr != '\0') {
         LOG(ERROR) << "invalid mode (" << fn << ":" << line << ") '" << args[1] << "'";
@@ -154,13 +143,9 @@
     }
     gid = grp->gr_gid;
 
-    if (add_dev_perms(name, attr, perm, uid, gid, prefix, wildcard) != 0) {
-        PLOG(ERROR) << "add_dev_perms(name=" << name <<
-                       ", attr=" << attr <<
-                       ", perm=" << std::oct << perm << std::dec <<
-                       ", uid=" << uid << ", gid=" << gid <<
-                       ", prefix=" << prefix << ", wildcard=" << wildcard <<
-                       ")";
-        return;
+    if (attr) {
+        sysfs_permissions.emplace_back(name, attr, perm, uid, gid);
+    } else {
+        dev_permissions.emplace_back(name, perm, uid, gid);
     }
 }
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index 7de72a8..4a525be 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -62,6 +62,7 @@
 
 cc_library {
     name: "libbacktrace",
+    vendor_available: true,
     defaults: ["libbacktrace_common"],
     host_supported: true,
 
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index bb82f4d..58170ec 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -30,16 +30,14 @@
 ]
 
 cc_library_headers {
-    name: "libcutils_vndk_headers",
-    host_supported: true,
-    export_include_dirs: ["include_vndk"],
-}
-
-cc_library_headers {
     name: "libcutils_headers",
+    vendor_available: true,
     host_supported: true,
     export_include_dirs: ["include"],
     target: {
+        vendor: {
+            export_include_dirs: ["include_vndk"],
+        },
         linux_bionic: {
             enabled: true,
         },
@@ -51,6 +49,7 @@
 
 cc_library {
     name: "libcutils",
+    vendor_available: true,
     host_supported: true,
     srcs: [
         "config_utils.c",
@@ -143,7 +142,10 @@
     },
 
     shared_libs: ["liblog"],
-    header_libs: ["libcutils_headers"],
+    header_libs: [
+        "libcutils_headers",
+        "libutils_headers",
+    ],
     export_header_lib_headers: ["libcutils_headers"],
 
     cflags: [
diff --git a/libcutils/include/cutils/multiuser.h b/libcutils/include/cutils/multiuser.h
index 5bd9c7b..9a2305c 100644
--- a/libcutils/include/cutils/multiuser.h
+++ b/libcutils/include/cutils/multiuser.h
@@ -33,6 +33,7 @@
 
 extern gid_t multiuser_get_cache_gid(userid_t user_id, appid_t app_id);
 extern gid_t multiuser_get_ext_gid(userid_t user_id, appid_t app_id);
+extern gid_t multiuser_get_ext_cache_gid(userid_t user_id, appid_t app_id);
 extern gid_t multiuser_get_shared_gid(userid_t user_id, appid_t app_id);
 
 /* TODO: switch callers over to multiuser_get_shared_gid() */
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index 0037f15..bbba853 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -171,6 +171,9 @@
 #define AID_EXT_GID_START 30000 /* start of gids for apps to mark external data */
 #define AID_EXT_GID_END 39999   /* end of gids for apps to mark external data */
 
+#define AID_EXT_CACHE_GID_START 40000 /* start of gids for apps to mark external cached data */
+#define AID_EXT_CACHE_GID_END 49999   /* end of gids for apps to mark external cached data */
+
 #define AID_SHARED_GID_START 50000 /* start of gids for apps in each user to share */
 #define AID_SHARED_GID_END 59999   /* end of gids for apps in each user to share */
 
diff --git a/libcutils/include_vndk/cutils/log.h b/libcutils/include_vndk/cutils/log.h
index ae74024..21dc11e 100644
--- a/libcutils/include_vndk/cutils/log.h
+++ b/libcutils/include_vndk/cutils/log.h
@@ -16,6 +16,32 @@
 */
 #ifndef _LIBS_CUTIL_LOG_H
 #define _LIBS_CUTIL_LOG_H
-#warning "Deprecated: don't include cutils/log.h, use either android/log.h or log/log.h"
+
+/* We do not know if developer wanted log/log.h or subset android/log.h */
 #include <log/log.h>
+
+#if defined(__GNUC__)
+#if defined( __clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic warning "-W#warnings"
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpedantic"
+#elif (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR > 9))
+#pragma GCC diagnostic push
+#pragma GCC diagnostic warning "-W#warnings"
+#else
+#pragma GCC diagnostic push
+#pragma GCC diagnostic warning "-Wcpp"
+#endif
+#endif
+
+#warning "Deprecated: don't include cutils/log.h, use either android/log.h or log/log.h"
+
+#if defined(__GNUC__)
+#if defined( __clang__)
+#pragma clang diagnostic pop
+#endif
+#pragma GCC diagnostic pop
+#endif
+
 #endif /* _LIBS_CUTIL_LOG_H */
diff --git a/libcutils/multiuser.c b/libcutils/multiuser.c
index 08d4d6c..61403f4 100644
--- a/libcutils/multiuser.c
+++ b/libcutils/multiuser.c
@@ -45,6 +45,14 @@
     }
 }
 
+gid_t multiuser_get_ext_cache_gid(userid_t user_id, appid_t app_id) {
+    if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
+        return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_EXT_CACHE_GID_START);
+    } else {
+        return -1;
+    }
+}
+
 gid_t multiuser_get_shared_gid(userid_t user_id, appid_t app_id) {
     if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
         return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_SHARED_GID_START);
diff --git a/libcutils/sched_policy.cpp b/libcutils/sched_policy.cpp
index a9c061e..4a0b035 100644
--- a/libcutils/sched_policy.cpp
+++ b/libcutils/sched_policy.cpp
@@ -28,6 +28,13 @@
 
 #define UNUSED __attribute__((__unused__))
 
+#ifndef SLOGE
+#define SLOGE ALOGE
+#endif
+#ifndef SLOGW
+#define SLOGW ALOGW
+#endif
+
 /* Re-map SP_DEFAULT to the system default policy, and leave other values unchanged.
  * Call this any place a SchedPolicy is used as an input parameter.
  * Returns the possibly re-mapped policy.
diff --git a/libcutils/tests/multiuser_test.cpp b/libcutils/tests/multiuser_test.cpp
index ae5c416..2f9d854 100644
--- a/libcutils/tests/multiuser_test.cpp
+++ b/libcutils/tests/multiuser_test.cpp
@@ -68,6 +68,14 @@
     EXPECT_EQ(1030000U, multiuser_get_ext_gid(10, 10000));
 }
 
+TEST(MultiuserTest, TestExtCache) {
+    EXPECT_EQ(ERR_GID, multiuser_get_ext_cache_gid(0, 0));
+    EXPECT_EQ(ERR_GID, multiuser_get_ext_cache_gid(0, 1000));
+    EXPECT_EQ(40000U, multiuser_get_ext_cache_gid(0, 10000));
+    EXPECT_EQ(ERR_GID, multiuser_get_ext_cache_gid(0, 50000));
+    EXPECT_EQ(1040000U, multiuser_get_ext_cache_gid(10, 10000));
+}
+
 TEST(MultiuserTest, TestShared) {
     EXPECT_EQ(ERR_GID, multiuser_get_shared_gid(0, 0));
     EXPECT_EQ(ERR_GID, multiuser_get_shared_gid(0, 1000));
diff --git a/libcutils/trace-dev.c b/libcutils/trace-dev.c
index 113f423..d45e5a9 100644
--- a/libcutils/trace-dev.c
+++ b/libcutils/trace-dev.c
@@ -29,7 +29,8 @@
 #include <cutils/compiler.h>
 #include <cutils/properties.h>
 #include <cutils/trace.h>
-#include <private/android_logger.h>
+#include <log/log.h>
+#include <log/log_properties.h>
 
 /**
  * Maximum size of a message that can be logged to the trace buffer.
diff --git a/liblog/event_tag_map.cpp b/liblog/event_tag_map.cpp
index 73ed16f..5fc7e35 100644
--- a/liblog/event_tag_map.cpp
+++ b/liblog/event_tag_map.cpp
@@ -31,6 +31,7 @@
 #include <unordered_map>
 
 #include <log/event_tag_map.h>
+#include <log/log_properties.h>
 #include <private/android_logger.h>
 #include <utils/FastStrcmp.h>
 #include <utils/RWLock.h>
diff --git a/liblog/include/log/log_properties.h b/liblog/include/log/log_properties.h
new file mode 100644
index 0000000..7d398a6
--- /dev/null
+++ b/liblog/include/log/log_properties.h
@@ -0,0 +1,35 @@
+/*
+**
+** Copyright 2017, The Android Open Source Project
+**
+** This file is dual licensed.  It may be redistributed and/or modified
+** under the terms of the Apache 2.0 License OR version 2 of the GNU
+** General Public License.
+*/
+
+#ifndef _LIBS_LOG_PROPERTIES_H
+#define _LIBS_LOG_PROPERTIES_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef __ANDROID_USE_LIBLOG_IS_DEBUGGABLE_INTERFACE
+#ifndef __ANDROID_API__
+#define __ANDROID_USE_LIBLOG_IS_DEBUGGABLE_INTERFACE 1
+#elif __ANDROID_API__ > 24 /* > Nougat */
+#define __ANDROID_USE_LIBLOG_IS_DEBUGGABLE_INTERFACE 1
+#else
+#define __ANDROID_USE_LIBLOG_IS_DEBUGGABLE_INTERFACE 0
+#endif
+#endif
+
+#if __ANDROID_USE_LIBLOG_IS_DEBUGGABLE_INTERFACE
+int __android_log_is_debuggable();
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBS_LOG_PROPERTIES_H */
diff --git a/liblog/include/log/log_time.h b/liblog/include/log/log_time.h
index 5f70f7d..3764faf 100644
--- a/liblog/include/log/log_time.h
+++ b/liblog/include/log/log_time.h
@@ -22,6 +22,8 @@
 
 /* struct log_time is a wire-format variant of struct timespec */
 #define NS_PER_SEC 1000000000ULL
+#define US_PER_SEC 1000000ULL
+#define MS_PER_SEC 1000ULL
 
 #ifndef __struct_log_time_defined
 #define __struct_log_time_defined
@@ -41,13 +43,12 @@
   static const uint32_t tv_sec_max = 0xFFFFFFFFUL;
   static const uint32_t tv_nsec_max = 999999999UL;
 
-  log_time(const timespec& T) {
-    tv_sec = static_cast<uint32_t>(T.tv_sec);
-    tv_nsec = static_cast<uint32_t>(T.tv_nsec);
+  log_time(const timespec& T)
+      : tv_sec(static_cast<uint32_t>(T.tv_sec)),
+        tv_nsec(static_cast<uint32_t>(T.tv_nsec)) {
   }
-  log_time(uint32_t sec, uint32_t nsec) {
-    tv_sec = sec;
-    tv_nsec = nsec;
+  explicit log_time(uint32_t sec, uint32_t nsec = 0)
+      : tv_sec(sec), tv_nsec(nsec) {
   }
 #ifdef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
 #define __struct_log_time_private_defined
@@ -56,14 +57,14 @@
   log_time() {
   }
 #ifdef __linux__
-  log_time(clockid_t id) {
+  explicit log_time(clockid_t id) {
     timespec T;
     clock_gettime(id, &T);
     tv_sec = static_cast<uint32_t>(T.tv_sec);
     tv_nsec = static_cast<uint32_t>(T.tv_nsec);
   }
 #endif
-  log_time(const char* T) {
+  explicit log_time(const char* T) {
     const uint8_t* c = reinterpret_cast<const uint8_t*>(T);
     tv_sec = c[0] | (static_cast<uint32_t>(c[1]) << 8) |
              (static_cast<uint32_t>(c[2]) << 16) |
@@ -149,6 +150,14 @@
   uint64_t nsec() const {
     return static_cast<uint64_t>(tv_sec) * NS_PER_SEC + tv_nsec;
   }
+  uint64_t usec() const {
+    return static_cast<uint64_t>(tv_sec) * US_PER_SEC +
+           tv_nsec / (NS_PER_SEC / US_PER_SEC);
+  }
+  uint64_t msec() const {
+    return static_cast<uint64_t>(tv_sec) * MS_PER_SEC +
+           tv_nsec / (NS_PER_SEC / MS_PER_SEC);
+  }
 
 #ifdef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
   static const char default_format[];
diff --git a/liblog/include/private/android_logger.h b/liblog/include/private/android_logger.h
index e3ccfcf..965de37 100644
--- a/liblog/include/private/android_logger.h
+++ b/liblog/include/private/android_logger.h
@@ -135,8 +135,6 @@
 int __android_log_security_bswrite(int32_t tag, const char* payload);
 int __android_log_security(); /* Device Owner is present */
 
-int __android_log_is_debuggable();
-
 #define BOOL_DEFAULT_FLAG_TRUE_FALSE 0x1
 #define BOOL_DEFAULT_FALSE 0x0        /* false if property not present   */
 #define BOOL_DEFAULT_TRUE 0x1         /* true if property not present    */
diff --git a/liblog/include_vndk/log/log_properties.h b/liblog/include_vndk/log/log_properties.h
new file mode 120000
index 0000000..bbec426
--- /dev/null
+++ b/liblog/include_vndk/log/log_properties.h
@@ -0,0 +1 @@
+../../include/log/log_properties.h
\ No newline at end of file
diff --git a/liblog/liblog.map.txt b/liblog/liblog.map.txt
index c00f2a0..58fb148 100644
--- a/liblog/liblog.map.txt
+++ b/liblog/liblog.map.txt
@@ -39,4 +39,5 @@
 LIBLOG_O {
   global:
     __android_log_is_loggable_len;
+    __android_log_is_debuggable; # vndk
 };
diff --git a/liblog/pmsg_writer.c b/liblog/pmsg_writer.c
index e71c176..dc42856 100644
--- a/liblog/pmsg_writer.c
+++ b/liblog/pmsg_writer.c
@@ -26,6 +26,7 @@
 #include <sys/types.h>
 #include <time.h>
 
+#include <log/log_properties.h>
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
 
diff --git a/liblog/properties.c b/liblog/properties.c
index adf1900..11be827 100644
--- a/liblog/properties.c
+++ b/liblog/properties.c
@@ -277,7 +277,7 @@
   return logLevel >= 0 && prio >= logLevel;
 }
 
-LIBLOG_ABI_PRIVATE int __android_log_is_debuggable() {
+LIBLOG_ABI_PUBLIC int __android_log_is_debuggable() {
   static uint32_t serial;
   static struct cache_char tag_cache;
   static const char key[] = "ro.debuggable";
@@ -459,6 +459,9 @@
   if (check_flag(property.property, "false")) {
     return false;
   }
+  if (property.property[0]) {
+    flag &= ~(BOOL_DEFAULT_FLAG_ENG | BOOL_DEFAULT_FLAG_SVELTE);
+  }
   if (check_flag(property.property, "eng")) {
     flag |= BOOL_DEFAULT_FLAG_ENG;
   }
diff --git a/libprocessgroup/Android.bp b/libprocessgroup/Android.bp
new file mode 100644
index 0000000..37e4dbf
--- /dev/null
+++ b/libprocessgroup/Android.bp
@@ -0,0 +1,10 @@
+cc_library {
+    srcs: ["processgroup.cpp"],
+    name: "libprocessgroup",
+    shared_libs: ["libbase"],
+    export_include_dirs: ["include"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+}
diff --git a/libprocessgroup/Android.mk b/libprocessgroup/Android.mk
deleted file mode 100644
index 0bfc391..0000000
--- a/libprocessgroup/Android.mk
+++ /dev/null
@@ -1,19 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := processgroup.cpp
-LOCAL_MODULE := libprocessgroup
-LOCAL_STATIC_LIBRARIES := libbase
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Wall -Werror
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := processgroup.cpp
-LOCAL_MODULE := libprocessgroup
-LOCAL_SHARED_LIBRARIES := libbase
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Wall -Werror
-include $(BUILD_SHARED_LIBRARY)
diff --git a/libsparse/Android.bp b/libsparse/Android.bp
index dd8b5fd..6ec0991 100644
--- a/libsparse/Android.bp
+++ b/libsparse/Android.bp
@@ -10,17 +10,23 @@
         "sparse.c",
         "sparse_crc32.c",
         "sparse_err.c",
-        "sparse_read.c",
+        "sparse_read.cpp",
     ],
     cflags: ["-Werror"],
     local_include_dirs: ["include"],
     export_include_dirs: ["include"],
     target: {
         host: {
-            shared_libs: ["libz-host"],
+            shared_libs: [
+                "libz-host",
+                "libbase",
+            ],
         },
         android: {
-            shared_libs: ["libz"],
+            shared_libs: [
+                "libz",
+                "libbase",
+            ],
         },
         windows: {
             enabled: true,
@@ -38,6 +44,7 @@
     static_libs: [
         "libsparse",
         "libz",
+        "libbase",
     ],
 
     cflags: ["-Werror"],
@@ -50,6 +57,7 @@
     static_libs: [
         "libsparse",
         "libz",
+        "libbase",
     ],
 
     cflags: ["-Werror"],
@@ -61,6 +69,7 @@
     static_libs: [
         "libsparse",
         "libz",
+        "libbase",
     ],
 
     cflags: ["-Werror"],
diff --git a/libsparse/output_file.c b/libsparse/output_file.c
index 2115998..51e60ef 100644
--- a/libsparse/output_file.c
+++ b/libsparse/output_file.c
@@ -584,7 +584,7 @@
 				.file_hdr_sz = SPARSE_HEADER_LEN,
 				.chunk_hdr_sz = CHUNK_HEADER_LEN,
 				.blk_sz = out->block_size,
-				.total_blks = out->len / out->block_size,
+				.total_blks = DIV_ROUND_UP(out->len, out->block_size),
 				.total_chunks = chunks,
 				.image_checksum = 0
 		};
diff --git a/libsparse/output_file.h b/libsparse/output_file.h
index 474c1fc..b67e94e 100644
--- a/libsparse/output_file.h
+++ b/libsparse/output_file.h
@@ -17,6 +17,10 @@
 #ifndef _OUTPUT_FILE_H_
 #define _OUTPUT_FILE_H_
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 #include <sparse/sparse.h>
 
 struct output_file;
@@ -38,4 +42,8 @@
 
 int read_all(int fd, void *buf, size_t len);
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif
diff --git a/libsparse/sparse_file.h b/libsparse/sparse_file.h
index 91a12e6..763f43f 100644
--- a/libsparse/sparse_file.h
+++ b/libsparse/sparse_file.h
@@ -17,6 +17,10 @@
 #ifndef _LIBSPARSE_SPARSE_FILE_H_
 #define _LIBSPARSE_SPARSE_FILE_H_
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 #include <sparse/sparse.h>
 
 struct sparse_file {
@@ -28,5 +32,8 @@
 	struct output_file *out;
 };
 
+#ifdef __cplusplus
+}
+#endif
 
 #endif /* _LIBSPARSE_SPARSE_FILE_H_ */
diff --git a/libsparse/sparse_format.h b/libsparse/sparse_format.h
index c41f12a..779e038 100644
--- a/libsparse/sparse_format.h
+++ b/libsparse/sparse_format.h
@@ -18,6 +18,10 @@
 #define _LIBSPARSE_SPARSE_FORMAT_H_
 #include "sparse_defs.h"
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 typedef struct sparse_header {
   __le32	magic;		/* 0xed26ff3a */
   __le16	major_version;	/* (0x1) - reject images with higher major versions */
@@ -52,4 +56,8 @@
  *  For a CRC32 chunk, it's 4 bytes of CRC32
  */
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif
diff --git a/libsparse/sparse_read.c b/libsparse/sparse_read.cpp
similarity index 88%
rename from libsparse/sparse_read.c
rename to libsparse/sparse_read.cpp
index a188202..bd66873 100644
--- a/libsparse/sparse_read.c
+++ b/libsparse/sparse_read.cpp
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-#define _GNU_SOURCE
 #define _FILE_OFFSET_BITS 64
 #define _LARGEFILE64_SOURCE 1
 
+#include <algorithm>
 #include <inttypes.h>
 #include <fcntl.h>
 #include <stdarg.h>
@@ -25,17 +25,19 @@
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
+#include <string>
 #include <unistd.h>
 
 #include <sparse/sparse.h>
 
+#include "android-base/stringprintf.h"
 #include "defs.h"
 #include "output_file.h"
 #include "sparse_crc32.h"
 #include "sparse_file.h"
 #include "sparse_format.h"
 
+
 #if defined(__APPLE__) && defined(__MACH__)
 #define lseek64 lseek
 #define off64_t off_t
@@ -45,57 +47,30 @@
 #define SPARSE_HEADER_LEN       (sizeof(sparse_header_t))
 #define CHUNK_HEADER_LEN (sizeof(chunk_header_t))
 
-#define COPY_BUF_SIZE (1024U*1024U)
+static constexpr int64_t COPY_BUF_SIZE = 1024 * 1024;
 static char *copybuf;
 
-#define min(a, b) \
-	({ typeof(a) _a = (a); typeof(b) _b = (b); (_a < _b) ? _a : _b; })
+static std::string ErrorString(int err)
+{
+	if (err == -EOVERFLOW) return "EOF while reading file";
+	if (err == -EINVAL) return "Invalid sparse file format";
+	if (err == -ENOMEM) return "Failed allocation while reading file";
+	return android::base::StringPrintf("Unknown error %d", err);
+}
 
 static void verbose_error(bool verbose, int err, const char *fmt, ...)
 {
-	char *s = "";
-	char *at = "";
+	if (!verbose) return;
+
+	std::string msg = ErrorString(err);
 	if (fmt) {
+		msg += " at ";
 		va_list argp;
-		int size;
-
 		va_start(argp, fmt);
-		size = vsnprintf(NULL, 0, fmt, argp);
+		android::base::StringAppendV(&msg, fmt, argp);
 		va_end(argp);
-
-		if (size < 0) {
-			return;
-		}
-
-		at = malloc(size + 1);
-		if (at == NULL) {
-			return;
-		}
-
-		va_start(argp, fmt);
-		vsnprintf(at, size, fmt, argp);
-		va_end(argp);
-		at[size] = 0;
-		s = " at ";
 	}
-	if (verbose) {
-#ifndef _WIN32
-		if (err == -EOVERFLOW) {
-			sparse_print_verbose("EOF while reading file%s%s\n", s, at);
-		} else
-#endif
-		if (err == -EINVAL) {
-			sparse_print_verbose("Invalid sparse file format%s%s\n", s, at);
-		} else if (err == -ENOMEM) {
-			sparse_print_verbose("Failed allocation while reading file%s%s\n",
-					s, at);
-		} else {
-			sparse_print_verbose("Unknown error %d%s%s\n", err, s, at);
-		}
-	}
-	if (fmt) {
-		free(at);
-	}
+	sparse_print_verbose("%s\n", msg.c_str());
 }
 
 static int process_raw_chunk(struct sparse_file *s, unsigned int chunk_size,
@@ -104,7 +79,7 @@
 {
 	int ret;
 	int chunk;
-	unsigned int len = blocks * s->block_size;
+	int64_t len = blocks * s->block_size;
 
 	if (chunk_size % s->block_size != 0) {
 		return -EINVAL;
@@ -121,7 +96,7 @@
 
 	if (crc32) {
 		while (len) {
-			chunk = min(len, COPY_BUF_SIZE);
+			chunk = std::min(len, COPY_BUF_SIZE);
 			ret = read_all(fd, copybuf, chunk);
 			if (ret < 0) {
 				return ret;
@@ -168,7 +143,7 @@
 		}
 
 		while (len) {
-			chunk = min(len, COPY_BUF_SIZE);
+			chunk = std::min(len, COPY_BUF_SIZE);
 			*crc32 = sparse_crc32(*crc32, copybuf, chunk);
 			len -= chunk;
 		}
@@ -190,7 +165,7 @@
 		memset(copybuf, 0, COPY_BUF_SIZE);
 
 		while (len) {
-			int chunk = min(len, COPY_BUF_SIZE);
+			int chunk = std::min(len, COPY_BUF_SIZE);
 			*crc32 = sparse_crc32(*crc32, copybuf, chunk);
 			len -= chunk;
 		}
@@ -284,7 +259,7 @@
 	off64_t offset;
 
 	if (!copybuf) {
-		copybuf = malloc(COPY_BUF_SIZE);
+		copybuf = (char *)malloc(COPY_BUF_SIZE);
 	}
 
 	if (!copybuf) {
@@ -357,7 +332,7 @@
 static int sparse_file_read_normal(struct sparse_file *s, int fd)
 {
 	int ret;
-	uint32_t *buf = malloc(s->block_size);
+	uint32_t *buf = (uint32_t *)malloc(s->block_size);
 	unsigned int block = 0;
 	int64_t remain = s->len;
 	int64_t offset = 0;
@@ -370,7 +345,7 @@
 	}
 
 	while (remain > 0) {
-		to_read = min(remain, s->block_size);
+		to_read = std::min(remain, (int64_t)(s->block_size));
 		ret = read_all(fd, buf, to_read);
 		if (ret < 0) {
 			error("failed to read sparse file");
diff --git a/libsync/Android.bp b/libsync/Android.bp
index a4e5599..4bafb08 100644
--- a/libsync/Android.bp
+++ b/libsync/Android.bp
@@ -8,6 +8,7 @@
 
 cc_library_shared {
     name: "libsync",
+    vendor_available: true,
     defaults: ["libsync_defaults"],
 }
 
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index e35593f..05d0353 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -54,6 +54,7 @@
         "ElfInterfaceArm.cpp",
         "Log.cpp",
         "Regs.cpp",
+        "MapInfo.cpp",
         "Maps.cpp",
         "Memory.cpp",
         "Symbols.cpp",
@@ -98,6 +99,7 @@
         "tests/ElfInterfaceTest.cpp",
         "tests/ElfTest.cpp",
         "tests/LogFake.cpp",
+        "tests/MapInfoTest.cpp",
         "tests/MapsTest.cpp",
         "tests/MemoryFake.cpp",
         "tests/MemoryFileTest.cpp",
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
new file mode 100644
index 0000000..051f700
--- /dev/null
+++ b/libunwindstack/MapInfo.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2017 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/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+
+#include "Elf.h"
+#include "MapInfo.h"
+#include "Maps.h"
+#include "Memory.h"
+
+Memory* MapInfo::CreateMemory(pid_t pid) {
+  if (end <= start) {
+    return nullptr;
+  }
+
+  elf_offset = 0;
+
+  // First try and use the file associated with the info.
+  if (!name.empty()) {
+    // Fail on device maps.
+    if (flags & MAPS_FLAGS_DEVICE_MAP) {
+      return nullptr;
+    }
+
+    std::unique_ptr<MemoryFileAtOffset> file_memory(new MemoryFileAtOffset);
+    uint64_t map_size;
+    if (offset != 0) {
+      // Only map in a piece of the file.
+      map_size = end - start;
+    } else {
+      map_size = UINT64_MAX;
+    }
+    if (file_memory->Init(name, offset, map_size)) {
+      // It's possible that a non-zero offset might not be pointing to
+      // valid elf data. Check if this is a valid elf, and if not assume
+      // that this was meant to incorporate the entire file.
+      if (offset != 0 && !Elf::IsValidElf(file_memory.get())) {
+        // Don't bother checking the validity that will happen on the elf init.
+        if (file_memory->Init(name, 0)) {
+          elf_offset = offset;
+          return file_memory.release();
+        }
+        // Fall through if the init fails.
+      } else {
+        return file_memory.release();
+      }
+    }
+  }
+
+  Memory* memory = nullptr;
+  if (pid == getpid()) {
+    memory = new MemoryLocal();
+  } else {
+    memory = new MemoryRemote(pid);
+  }
+  return new MemoryRange(memory, start, end);
+}
+
+Elf* MapInfo::GetElf(pid_t pid, bool) {
+  if (elf) {
+    return elf;
+  }
+
+  elf = new Elf(CreateMemory(pid));
+  elf->Init();
+  // If the init fails, keep the elf around as an invalid object so we
+  // don't try to reinit the object.
+  return elf;
+}
diff --git a/libunwindstack/MapInfo.h b/libunwindstack/MapInfo.h
index 8342904..79a2ada 100644
--- a/libunwindstack/MapInfo.h
+++ b/libunwindstack/MapInfo.h
@@ -39,6 +39,7 @@
   uint64_t elf_offset;
 
   Memory* CreateMemory(pid_t pid);
+  Elf* GetElf(pid_t pid, bool init_gnu_debugdata = false);
 };
 
 #endif  // _LIBUNWINDSTACK_MAP_INFO_H
diff --git a/libunwindstack/tests/MapInfoTest.cpp b/libunwindstack/tests/MapInfoTest.cpp
new file mode 100644
index 0000000..c846ad7
--- /dev/null
+++ b/libunwindstack/tests/MapInfoTest.cpp
@@ -0,0 +1,220 @@
+/*
+ * 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 <elf.h>
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include "Elf.h"
+#include "MapInfo.h"
+#include "Memory.h"
+
+class MapInfoTest : public ::testing::Test {
+ protected:
+  static void SetUpTestCase() {
+    std::vector<uint8_t> buffer(1024);
+    memcpy(buffer.data(), ELFMAG, SELFMAG);
+    for (size_t i = SELFMAG; i < buffer.size(); i++) {
+      buffer[i] = i / 256 + 1;
+    }
+    ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
+
+    for (size_t i = 0; i < 0x100; i++) {
+      buffer[i] = i / 256 + 1;
+    }
+    memcpy(&buffer[0x100], ELFMAG, SELFMAG);
+    for (size_t i = 0x100 + SELFMAG; i < buffer.size(); i++) {
+      buffer[i] = i / 256 + 1;
+    }
+    ASSERT_TRUE(android::base::WriteFully(elf_at_100_.fd, buffer.data(), buffer.size()));
+  }
+
+  static TemporaryFile elf_;
+
+  static TemporaryFile elf_at_100_;
+};
+TemporaryFile MapInfoTest::elf_;
+TemporaryFile MapInfoTest::elf_at_100_;
+
+TEST_F(MapInfoTest, end_le_start) {
+  MapInfo info{.start = 0x100, .end = 0x100, .offset = 0, .name = elf_.path};
+
+  std::unique_ptr<Memory> memory;
+  memory.reset(info.CreateMemory(getpid()));
+  ASSERT_TRUE(memory.get() == nullptr);
+
+  info.end = 0xff;
+  memory.reset(info.CreateMemory(getpid()));
+  ASSERT_TRUE(memory.get() == nullptr);
+
+  // Make sure this test is valid.
+  info.end = 0x101;
+  memory.reset(info.CreateMemory(getpid()));
+  ASSERT_FALSE(info.CreateMemory(getpid()) == nullptr);
+}
+
+// Verify that if the offset is non-zero but there is no elf at the offset,
+// that the full file is used.
+TEST_F(MapInfoTest, create_memory_file_backed_non_zero_offset_full_file) {
+  MapInfo info{.start = 0x100, .end = 0x200, .offset = 0x100, .name = elf_.path};
+
+  std::unique_ptr<Memory> memory(info.CreateMemory(getpid()));
+  ASSERT_TRUE(memory.get() != nullptr);
+  ASSERT_EQ(0x100U, info.elf_offset);
+
+  // Read the entire file.
+  std::vector<uint8_t> buffer(1024);
+  ASSERT_TRUE(memory->Read(0, buffer.data(), 1024));
+  ASSERT_TRUE(memcmp(buffer.data(), ELFMAG, SELFMAG) == 0);
+  for (size_t i = SELFMAG; i < buffer.size(); i++) {
+    ASSERT_EQ(i / 256 + 1, buffer[i]) << "Failed at byte " << i;
+  }
+
+  ASSERT_FALSE(memory->Read(1024, buffer.data(), 1));
+}
+
+// Verify that if the offset is non-zero and there is an elf at that
+// offset, that only part of the file is used.
+TEST_F(MapInfoTest, create_memory_file_backed_non_zero_offset_partial_file) {
+  MapInfo info{.start = 0x100, .end = 0x200, .offset = 0x100, .name = elf_at_100_.path};
+
+  std::unique_ptr<Memory> memory(info.CreateMemory(getpid()));
+  ASSERT_TRUE(memory.get() != nullptr);
+  ASSERT_EQ(0U, info.elf_offset);
+
+  // Read the valid part of the file.
+  std::vector<uint8_t> buffer(0x100);
+  ASSERT_TRUE(memory->Read(0, buffer.data(), 0x100));
+  ASSERT_TRUE(memcmp(buffer.data(), ELFMAG, SELFMAG) == 0);
+  for (size_t i = SELFMAG; i < buffer.size(); i++) {
+    ASSERT_EQ(2, buffer[i]) << "Failed at byte " << i;
+  }
+
+  ASSERT_FALSE(memory->Read(0x100, buffer.data(), 1));
+}
+
+// Verify that device file names will never result in Memory object creation.
+TEST_F(MapInfoTest, create_memory_check_device_maps) {
+  // Set up some memory so that a valid local memory object would
+  // be returned if the file mapping fails, but the device check is incorrect.
+  std::vector<uint8_t> buffer(1024);
+  MapInfo info;
+  info.start = reinterpret_cast<uint64_t>(buffer.data());
+  info.end = info.start + buffer.size();
+  info.offset = 0;
+  std::unique_ptr<Memory> memory;
+
+  info.flags = 0x8000;
+  info.name = "/dev/something";
+  memory.reset(info.CreateMemory(getpid()));
+  ASSERT_TRUE(memory.get() == nullptr);
+}
+
+TEST_F(MapInfoTest, create_memory_local_memory) {
+  // Set up some memory for a valid local memory object.
+  std::vector<uint8_t> buffer(1024);
+  for (size_t i = 0; i < buffer.size(); i++) {
+    buffer[i] = i % 256;
+  }
+
+  MapInfo info;
+  info.start = reinterpret_cast<uint64_t>(buffer.data());
+  info.end = info.start + buffer.size();
+  info.offset = 0;
+
+  std::unique_ptr<Memory> memory;
+  memory.reset(info.CreateMemory(getpid()));
+  ASSERT_TRUE(memory.get() != nullptr);
+
+  std::vector<uint8_t> read_buffer(1024);
+  ASSERT_TRUE(memory->Read(0, read_buffer.data(), read_buffer.size()));
+  for (size_t i = 0; i < read_buffer.size(); i++) {
+    ASSERT_EQ(i % 256, read_buffer[i]) << "Failed at byte " << i;
+  }
+
+  ASSERT_FALSE(memory->Read(read_buffer.size(), read_buffer.data(), 1));
+}
+
+TEST_F(MapInfoTest, create_memory_remote_memory) {
+  std::vector<uint8_t> buffer(1024);
+  memset(buffer.data(), 0xa, buffer.size());
+
+  pid_t pid;
+  if ((pid = fork()) == 0) {
+    while (true)
+      ;
+    exit(1);
+  }
+  ASSERT_LT(0, pid);
+
+  ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) != -1);
+  uint64_t iterations = 0;
+  siginfo_t si;
+  while (TEMP_FAILURE_RETRY(ptrace(PTRACE_GETSIGINFO, pid, 0, &si)) < 0 && errno == ESRCH) {
+    usleep(30);
+    iterations++;
+    ASSERT_LT(iterations, 500000000ULL);
+  }
+
+  MapInfo info;
+  info.start = reinterpret_cast<uint64_t>(buffer.data());
+  info.end = info.start + buffer.size();
+  info.offset = 0;
+
+  std::unique_ptr<Memory> memory;
+  memory.reset(info.CreateMemory(pid));
+  ASSERT_TRUE(memory.get() != nullptr);
+  // Set the local memory to a different value to guarantee we are reading
+  // from the remote process.
+  memset(buffer.data(), 0x1, buffer.size());
+  std::vector<uint8_t> read_buffer(1024);
+  ASSERT_TRUE(memory->Read(0, read_buffer.data(), read_buffer.size()));
+  for (size_t i = 0; i < read_buffer.size(); i++) {
+    ASSERT_EQ(0xaU, read_buffer[i]) << "Failed at byte " << i;
+  }
+
+  ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
+
+  kill(pid, SIGKILL);
+}
+
+TEST_F(MapInfoTest, get_elf) {
+  // Create a map to use as initialization data.
+  void* map = mmap(nullptr, 1024, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+  ASSERT_NE(MAP_FAILED, map);
+
+  uint64_t start = reinterpret_cast<uint64_t>(map);
+  MapInfo info{.start = start, .end = start + 1024, .offset = 0, .name = ""};
+
+  // The map contains garbage, but this should still produce an elf object.
+  Elf* elf = info.GetElf(getpid(), false);
+  ASSERT_TRUE(elf != nullptr);
+  ASSERT_FALSE(elf->valid());
+
+  ASSERT_EQ(0, munmap(map, 1024));
+}
diff --git a/libusbhost/Android.bp b/libusbhost/Android.bp
new file mode 100644
index 0000000..a0d6b9b
--- /dev/null
+++ b/libusbhost/Android.bp
@@ -0,0 +1,35 @@
+//
+// Copyright (C) 2010 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.
+//
+
+cc_library {
+    name: "libusbhost",
+    host_supported: true,
+    srcs: ["usbhost.c"],
+    cflags: ["-Werror"],
+    export_include_dirs: ["include"],
+    target: {
+        android: {
+            cflags: [
+                "-g",
+                "-DUSE_LIBLOG",
+            ],
+            shared_libs: ["liblog"],
+        },
+        darwin: {
+            enabled: false,
+        },
+    },
+}
diff --git a/libusbhost/Android.mk b/libusbhost/Android.mk
deleted file mode 100644
index 9439846..0000000
--- a/libusbhost/Android.mk
+++ /dev/null
@@ -1,56 +0,0 @@
-#
-# Copyright (C) 2010 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-# Static library for Linux host
-# ========================================================
-
-ifeq ($(HOST_OS),linux)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libusbhost
-LOCAL_SRC_FILES := usbhost.c
-LOCAL_CFLAGS := -Werror
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-include $(BUILD_HOST_STATIC_LIBRARY)
-
-endif
-
-# Shared library for target
-# ========================================================
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libusbhost
-LOCAL_SRC_FILES := usbhost.c
-LOCAL_CFLAGS := -g -DUSE_LIBLOG -Werror
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-# needed for logcat
-LOCAL_SHARED_LIBRARIES := libcutils
-include $(BUILD_SHARED_LIBRARY)
-
-# Static library for target
-# ========================================================
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libusbhost
-LOCAL_SRC_FILES := usbhost.c
-LOCAL_CFLAGS := -Werror
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-include $(BUILD_STATIC_LIBRARY)
diff --git a/libusbhost/usbhost.c b/libusbhost/usbhost.c
index 68aca17..050fc2f 100644
--- a/libusbhost/usbhost.c
+++ b/libusbhost/usbhost.c
@@ -19,7 +19,7 @@
 
 #ifdef USE_LIBLOG
 #define LOG_TAG "usbhost"
-#include "utils/Log.h"
+#include "log/log.h"
 #define D ALOGD
 #else
 #define D printf
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index e487a97..9c07472 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -1216,7 +1216,12 @@
     signal(SIGALRM, caught_blocking_clear);
     alarm(2);
     while (fgets(buffer, sizeof(buffer), fp)) {
-        if (!strncmp(buffer, "clearLog: ", 10)) {
+        if (!strncmp(buffer, "clearLog: ", strlen("clearLog: "))) {
+            fprintf(stderr, "WARNING: Test lacks permission to run :-(\n");
+            count = signals = 1;
+            break;
+        }
+        if (!strncmp(buffer, "failed to clear", strlen("failed to clear"))) {
             fprintf(stderr, "WARNING: Test lacks permission to run :-(\n");
             count = signals = 1;
             break;
diff --git a/logd/FlushCommand.cpp b/logd/FlushCommand.cpp
index c67d2bf..a9edc3e 100644
--- a/logd/FlushCommand.cpp
+++ b/logd/FlushCommand.cpp
@@ -44,14 +44,14 @@
 // LogTimeEntrys, and spawn a transitory per-client thread to
 // work at filing data to the  socket.
 //
-// global LogTimeEntry::lock() is used to protect access,
+// global LogTimeEntry::wrlock() is used to protect access,
 // reference counts are used to ensure that individual
 // LogTimeEntry lifetime is managed when not protected.
 void FlushCommand::runSocketCommand(SocketClient* client) {
     LogTimeEntry* entry = NULL;
     LastLogTimes& times = mReader.logbuf().mTimes;
 
-    LogTimeEntry::lock();
+    LogTimeEntry::wrlock();
     LastLogTimes::iterator it = times.begin();
     while (it != times.end()) {
         entry = (*it);
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index 2d9024a..ee38d1c 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -277,7 +277,7 @@
             ++cp;
         }
         tid = pid;
-        logbuf->lock();
+        logbuf->wrlock();
         uid = logbuf->pidToUid(pid);
         logbuf->unlock();
         memmove(pidptr, cp, strlen(cp) + 1);
@@ -322,7 +322,7 @@
         pid = tid;
         comm = "auditd";
     } else {
-        logbuf->lock();
+        logbuf->wrlock();
         comm = commfree = logbuf->pidToName(pid);
         logbuf->unlock();
         if (!comm) {
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 0c7019a..d9ec081 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -43,6 +43,8 @@
 // Default
 #define log_buffer_size(id) mMaxSize[id]
 
+const log_time LogBuffer::pruneMargin(3, 0);
+
 void LogBuffer::init() {
     log_id_for_each(i) {
         mLastSet[i] = false;
@@ -70,7 +72,7 @@
         // as the act of mounting /data would trigger persist.logd.timestamp to
         // be corrected. 1/30 corner case YMMV.
         //
-        pthread_mutex_lock(&mLogElementsLock);
+        rdlock();
         LogBufferElementCollection::iterator it = mLogElements.begin();
         while ((it != mLogElements.end())) {
             LogBufferElement* e = *it;
@@ -85,7 +87,7 @@
             }
             ++it;
         }
-        pthread_mutex_unlock(&mLogElementsLock);
+        unlock();
     }
 
     // We may have been triggered by a SIGHUP. Release any sleeping reader
@@ -93,7 +95,7 @@
     //
     // NB: this is _not_ performed in the context of a SIGHUP, it is
     // performed during startup, and in context of reinit administrative thread
-    LogTimeEntry::lock();
+    LogTimeEntry::wrlock();
 
     LastLogTimes::iterator times = mTimes.begin();
     while (times != mTimes.end()) {
@@ -109,7 +111,7 @@
 
 LogBuffer::LogBuffer(LastLogTimes* times)
     : monotonic(android_log_clockid() == CLOCK_MONOTONIC), mTimes(*times) {
-    pthread_mutex_init(&mLogElementsLock, nullptr);
+    pthread_rwlock_init(&mLogElementsLock, nullptr);
 
     log_id_for_each(i) {
         lastLoggedElements[i] = nullptr;
@@ -205,16 +207,15 @@
         }
         if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
             // Log traffic received to total
-            pthread_mutex_lock(&mLogElementsLock);
-            stats.add(elem);
-            stats.subtract(elem);
-            pthread_mutex_unlock(&mLogElementsLock);
+            wrlock();
+            stats.addTotal(elem);
+            unlock();
             delete elem;
             return -EACCES;
         }
     }
 
-    pthread_mutex_lock(&mLogElementsLock);
+    wrlock();
     LogBufferElement* currentLast = lastLoggedElements[log_id];
     if (currentLast) {
         LogBufferElement* dropped = droppedElements[log_id];
@@ -315,15 +316,14 @@
                     // check for overflow
                     if (total >= UINT32_MAX) {
                         log(currentLast);
-                        pthread_mutex_unlock(&mLogElementsLock);
+                        unlock();
                         return len;
                     }
-                    stats.add(currentLast);
-                    stats.subtract(currentLast);
+                    stats.addTotal(currentLast);
                     delete currentLast;
                     swab = total;
                     event->payload.data = htole32(swab);
-                    pthread_mutex_unlock(&mLogElementsLock);
+                    unlock();
                     return len;
                 }
                 if (count == USHRT_MAX) {
@@ -335,13 +335,12 @@
                 }
             }
             if (count) {
-                stats.add(currentLast);
-                stats.subtract(currentLast);
+                stats.addTotal(currentLast);
                 currentLast->setDropped(count);
             }
             droppedElements[log_id] = currentLast;
             lastLoggedElements[log_id] = elem;
-            pthread_mutex_unlock(&mLogElementsLock);
+            unlock();
             return len;
         }
         if (dropped) {         // State 1 or 2
@@ -359,12 +358,12 @@
     lastLoggedElements[log_id] = new LogBufferElement(*elem);
 
     log(elem);
-    pthread_mutex_unlock(&mLogElementsLock);
+    unlock();
 
     return len;
 }
 
-// assumes mLogElementsLock held, owns elem, will look after garbage collection
+// assumes LogBuffer::wrlock() held, owns elem, look after garbage collection
 void LogBuffer::log(LogBufferElement* elem) {
     // cap on how far back we will sort in-place, otherwise append
     static uint32_t too_far_back = 5;  // five seconds
@@ -385,7 +384,7 @@
         bool end_set = false;
         bool end_always = false;
 
-        LogTimeEntry::lock();
+        LogTimeEntry::rdlock();
 
         LastLogTimes::iterator times = mTimes.begin();
         while (times != mTimes.end()) {
@@ -427,7 +426,7 @@
 
 // Prune at most 10% of the log entries or maxPrune, whichever is less.
 //
-// mLogElementsLock must be held when this function is called.
+// LogBuffer::wrlock() must be held when this function is called.
 void LogBuffer::maybePrune(log_id_t id) {
     size_t sizes = stats.sizes(id);
     unsigned long maxSize = log_buffer_size(id);
@@ -651,14 +650,14 @@
 // The third thread is optional, and only gets hit if there was a whitelist
 // and more needs to be pruned against the backstop of the region lock.
 //
-// mLogElementsLock must be held when this function is called.
+// LogBuffer::wrlock() must be held when this function is called.
 //
 bool LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
     LogTimeEntry* oldest = nullptr;
     bool busy = false;
     bool clearAll = pruneRows == ULONG_MAX;
 
-    LogTimeEntry::lock();
+    LogTimeEntry::rdlock();
 
     // Region locked?
     LastLogTimes::iterator times = mTimes.begin();
@@ -672,6 +671,8 @@
         }
         times++;
     }
+    log_time watermark(log_time::tv_sec_max, log_time::tv_nsec_max);
+    if (oldest) watermark = oldest->mStart - pruneMargin;
 
     LogBufferElementCollection::iterator it;
 
@@ -693,7 +694,7 @@
                 mLastSet[id] = true;
             }
 
-            if (oldest && (oldest->mStart <= element->getRealTime().nsec())) {
+            if (oldest && (watermark <= element->getRealTime())) {
                 busy = true;
                 if (oldest->mTimeout.tv_sec || oldest->mTimeout.tv_nsec) {
                     oldest->triggerReader_Locked();
@@ -785,7 +786,7 @@
         while (it != mLogElements.end()) {
             LogBufferElement* element = *it;
 
-            if (oldest && (oldest->mStart <= element->getRealTime().nsec())) {
+            if (oldest && (watermark <= element->getRealTime())) {
                 busy = true;
                 if (oldest->mTimeout.tv_sec || oldest->mTimeout.tv_nsec) {
                     oldest->triggerReader_Locked();
@@ -939,7 +940,7 @@
             mLastSet[id] = true;
         }
 
-        if (oldest && (oldest->mStart <= element->getRealTime().nsec())) {
+        if (oldest && (watermark <= element->getRealTime())) {
             busy = true;
             if (whitelist) {
                 break;
@@ -983,7 +984,7 @@
                 mLastSet[id] = true;
             }
 
-            if (oldest && (oldest->mStart <= element->getRealTime().nsec())) {
+            if (oldest && (watermark <= element->getRealTime())) {
                 busy = true;
                 if (stats.sizes(id) > (2 * log_buffer_size(id))) {
                     // kick a misbehaving log reader client off the island
@@ -1016,15 +1017,15 @@
             // one entry, not another clear run, so we are looking for
             // the quick side effect of the return value to tell us if
             // we have a _blocked_ reader.
-            pthread_mutex_lock(&mLogElementsLock);
+            wrlock();
             busy = prune(id, 1, uid);
-            pthread_mutex_unlock(&mLogElementsLock);
+            unlock();
             // It is still busy, blocked reader(s), lets kill them all!
             // otherwise, lets be a good citizen and preserve the slow
             // readers and let the clear run (below) deal with determining
             // if we are still blocked and return an error code to caller.
             if (busy) {
-                LogTimeEntry::lock();
+                LogTimeEntry::wrlock();
                 LastLogTimes::iterator times = mTimes.begin();
                 while (times != mTimes.end()) {
                     LogTimeEntry* entry = (*times);
@@ -1037,9 +1038,9 @@
                 LogTimeEntry::unlock();
             }
         }
-        pthread_mutex_lock(&mLogElementsLock);
+        wrlock();
         busy = prune(id, ULONG_MAX, uid);
-        pthread_mutex_unlock(&mLogElementsLock);
+        unlock();
         if (!busy || !--retry) {
             break;
         }
@@ -1050,9 +1051,9 @@
 
 // get the used space associated with "id".
 unsigned long LogBuffer::getSizeUsed(log_id_t id) {
-    pthread_mutex_lock(&mLogElementsLock);
+    rdlock();
     size_t retval = stats.sizes(id);
-    pthread_mutex_unlock(&mLogElementsLock);
+    unlock();
     return retval;
 }
 
@@ -1062,17 +1063,17 @@
     if (!__android_logger_valid_buffer_size(size)) {
         return -1;
     }
-    pthread_mutex_lock(&mLogElementsLock);
+    wrlock();
     log_buffer_size(id) = size;
-    pthread_mutex_unlock(&mLogElementsLock);
+    unlock();
     return 0;
 }
 
 // get the total space allocated to "id"
 unsigned long LogBuffer::getSize(log_id_t id) {
-    pthread_mutex_lock(&mLogElementsLock);
+    rdlock();
     size_t retval = log_buffer_size(id);
-    pthread_mutex_unlock(&mLogElementsLock);
+    unlock();
     return retval;
 }
 
@@ -1084,19 +1085,21 @@
     LogBufferElementCollection::iterator it;
     uid_t uid = reader->getUid();
 
-    pthread_mutex_lock(&mLogElementsLock);
+    rdlock();
 
     if (start == log_time::EPOCH) {
         // client wants to start from the beginning
         it = mLogElements.begin();
     } else {
-        LogBufferElementCollection::iterator last;
         // 3 second limit to continue search for out-of-order entries.
-        log_time min = start - log_time(3, 0);
+        log_time min = start - pruneMargin;
+
         // Cap to 300 iterations we look back for out-of-order entries.
         size_t count = 300;
+
         // Client wants to start from some specified time. Chances are
         // we are better off starting from the end of the time sorted list.
+        LogBufferElementCollection::iterator last;
         for (last = it = mLogElements.end(); it != mLogElements.begin();
              /* do nothing */) {
             --it;
@@ -1112,9 +1115,22 @@
 
     log_time max = start;
 
+    LogBufferElement* lastElement = nullptr;  // iterator corruption paranoia
+    static const size_t maxSkip = 4194304;    // maximum entries to skip
+    size_t skip = maxSkip;
     for (; it != mLogElements.end(); ++it) {
         LogBufferElement* element = *it;
 
+        if (!--skip) {
+            android::prdebug("reader.per: too many elements skipped");
+            break;
+        }
+        if (element == lastElement) {
+            android::prdebug("reader.per: identical elements");
+            break;
+        }
+        lastElement = element;
+
         if (!privileged && (element->getUid() != uid)) {
             continue;
         }
@@ -1127,7 +1143,7 @@
             continue;
         }
 
-        // NB: calling out to another object with mLogElementsLock held (safe)
+        // NB: calling out to another object with wrlock() held (safe)
         if (filter) {
             int ret = (*filter)(element, arg);
             if (ret == false) {
@@ -1150,7 +1166,7 @@
                 (element->getDropped() && !sameTid) ? 0 : element->getTid();
         }
 
-        pthread_mutex_unlock(&mLogElementsLock);
+        unlock();
 
         // range locking in LastLogTimes looks after us
         max = element->flushTo(reader, this, privileged, sameTid);
@@ -1159,20 +1175,21 @@
             return max;
         }
 
-        pthread_mutex_lock(&mLogElementsLock);
+        skip = maxSkip;
+        rdlock();
     }
-    pthread_mutex_unlock(&mLogElementsLock);
+    unlock();
 
     return max;
 }
 
 std::string LogBuffer::formatStatistics(uid_t uid, pid_t pid,
                                         unsigned int logMask) {
-    pthread_mutex_lock(&mLogElementsLock);
+    wrlock();
 
     std::string ret = stats.format(uid, pid, logMask);
 
-    pthread_mutex_unlock(&mLogElementsLock);
+    unlock();
 
     return ret;
 }
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index 19d11cb..51edd86 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -76,7 +76,7 @@
 
 class LogBuffer {
     LogBufferElementCollection mLogElements;
-    pthread_mutex_t mLogElementsLock;
+    pthread_rwlock_t mLogElementsLock;
 
     LogStatistics stats;
 
@@ -154,7 +154,7 @@
         return tags.tagToName(tag);
     }
 
-    // helper must be protected directly or implicitly by lock()/unlock()
+    // helper must be protected directly or implicitly by wrlock()/unlock()
     const char* pidToName(pid_t pid) {
         return stats.pidToName(pid);
     }
@@ -164,16 +164,20 @@
     const char* uidToName(uid_t uid) {
         return stats.uidToName(uid);
     }
-    void lock() {
-        pthread_mutex_lock(&mLogElementsLock);
+    void wrlock() {
+        pthread_rwlock_wrlock(&mLogElementsLock);
+    }
+    void rdlock() {
+        pthread_rwlock_rdlock(&mLogElementsLock);
     }
     void unlock() {
-        pthread_mutex_unlock(&mLogElementsLock);
+        pthread_rwlock_unlock(&mLogElementsLock);
     }
 
    private:
     static constexpr size_t minPrune = 4;
     static constexpr size_t maxPrune = 256;
+    static const log_time pruneMargin;
 
     void maybePrune(log_id_t id);
     bool prune(log_id_t id, unsigned long pruneRows, uid_t uid = AID_ROOT);
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 04a620c..381c974 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -121,7 +121,7 @@
     }
 
     static const char format_uid[] = "uid=%u%s%s %s %u line%s";
-    parent->lock();
+    parent->wrlock();
     const char* name = parent->uidToName(mUid);
     parent->unlock();
     const char* commName = android::tidToName(mTid);
@@ -129,7 +129,7 @@
         commName = android::tidToName(mPid);
     }
     if (!commName) {
-        parent->lock();
+        parent->wrlock();
         commName = parent->pidToName(mPid);
         parent->unlock();
     }
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
index d23254d..a7e7208 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -579,7 +579,7 @@
     const pid_t tid = pid;
     uid_t uid = AID_ROOT;
     if (pid) {
-        logbuf->lock();
+        logbuf->wrlock();
         uid = logbuf->pidToUid(pid);
         logbuf->unlock();
     }
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
index af19279..6d69316 100644
--- a/logd/LogReader.cpp
+++ b/logd/LogReader.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <ctype.h>
+#include <inttypes.h>
 #include <poll.h>
 #include <sys/prctl.h>
 #include <sys/socket.h>
@@ -110,7 +111,7 @@
     if (!fastcmp<strncmp>(buffer, "dumpAndClose", 12)) {
         // Allow writer to get some cycles, and wait for pending notifications
         sched_yield();
-        LogTimeEntry::lock();
+        LogTimeEntry::wrlock();
         LogTimeEntry::unlock();
         sched_yield();
         nonBlock = true;
@@ -192,6 +193,12 @@
         }
     }
 
+    android::prdebug(
+        "logdr: UID=%d GID=%d PID=%d %c tail=%lu logMask=%x pid=%d "
+        "start=%" PRIu64 "ns timeout=%" PRIu64 "ns\n",
+        cli->getUid(), cli->getGid(), cli->getPid(), nonBlock ? 'n' : 'b', tail,
+        logMask, (int)pid, sequence.nsec(), timeout);
+
     FlushCommand command(*this, nonBlock, tail, logMask, pid, sequence, timeout);
 
     // Set acceptable upper limit to wait for slow reader processing b/27242723
@@ -205,7 +212,7 @@
 
 void LogReader::doSocketDelete(SocketClient* cli) {
     LastLogTimes& times = mLogbuf.mTimes;
-    LogTimeEntry::lock();
+    LogTimeEntry::wrlock();
     LastLogTimes::iterator it = times.begin();
     while (it != times.end()) {
         LogTimeEntry* entry = (*it);
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index d3167ad..0b6b28c 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <fcntl.h>
+#include <inttypes.h>
 #include <pwd.h>
 #include <stdio.h>
 #include <string.h>
@@ -23,19 +24,26 @@
 
 #include <list>
 
-#include <android/log.h>
+#include <private/android_logger.h>
 
 #include "LogStatistics.h"
 
+static const uint64_t hourSec = 60 * 60;
+static const uint64_t monthSec = 31 * 24 * hourSec;
+
 size_t LogStatistics::SizesTotal;
 
 LogStatistics::LogStatistics() : enable(false) {
+    log_time now(CLOCK_REALTIME);
     log_id_for_each(id) {
         mSizes[id] = 0;
         mElements[id] = 0;
         mDroppedElements[id] = 0;
         mSizesTotal[id] = 0;
         mElementsTotal[id] = 0;
+        mOldest[id] = now;
+        mNewest[id] = now;
+        mNewestDropped[id] = now;
     }
 }
 
@@ -70,25 +78,56 @@
 }
 }
 
+void LogStatistics::addTotal(LogBufferElement* element) {
+    if (element->getDropped()) return;
+
+    log_id_t log_id = element->getLogId();
+    unsigned short size = element->getMsgLen();
+    mSizesTotal[log_id] += size;
+    SizesTotal += size;
+    ++mElementsTotal[log_id];
+}
+
 void LogStatistics::add(LogBufferElement* element) {
     log_id_t log_id = element->getLogId();
     unsigned short size = element->getMsgLen();
     mSizes[log_id] += size;
     ++mElements[log_id];
 
+    // When caller adding a chatty entry, they will have already
+    // called add() and subtract() for each entry as they are
+    // evaluated and trimmed, thus recording size and number of
+    // elements, but we must recognize the manufactured dropped
+    // entry as not contributing to the lifetime totals.
     if (element->getDropped()) {
         ++mDroppedElements[log_id];
     } else {
-        // When caller adding a chatty entry, they will have already
-        // called add() and subtract() for each entry as they are
-        // evaluated and trimmed, thus recording size and number of
-        // elements, but we must recognize the manufactured dropped
-        // entry as not contributing to the lifetime totals.
         mSizesTotal[log_id] += size;
         SizesTotal += size;
         ++mElementsTotal[log_id];
     }
 
+    log_time stamp(element->getRealTime());
+    if (mNewest[log_id] < stamp) {
+        // A major time update invalidates the statistics :-(
+        log_time diff = stamp - mNewest[log_id];
+        mNewest[log_id] = stamp;
+
+        if (diff.tv_sec > hourSec) {
+            // approximate Do-Your-Best fixup
+            diff += mOldest[log_id];
+            if ((diff > stamp) && ((diff - stamp).tv_sec < hourSec)) {
+                diff = stamp;
+            }
+            if (diff <= stamp) {
+                mOldest[log_id] = diff;
+                if (mNewestDropped[log_id] < diff) {
+                    mNewestDropped[log_id] = diff;
+                }
+            }
+        }
+    }
+
     if (log_id == LOG_ID_KERNEL) {
         return;
     }
@@ -124,6 +163,10 @@
         --mDroppedElements[log_id];
     }
 
+    if (mOldest[log_id] < element->getRealTime()) {
+        mOldest[log_id] = element->getRealTime();
+    }
+
     if (log_id == LOG_ID_KERNEL) {
         return;
     }
@@ -158,6 +201,10 @@
     mSizes[log_id] -= size;
     ++mDroppedElements[log_id];
 
+    if (mNewestDropped[log_id] < element->getRealTime()) {
+        mNewestDropped[log_id] = element->getRealTime();
+    }
+
     uidTable[log_id].drop(element->getUid(), element);
     if (element->getUid() == AID_SYSTEM) {
         pidSystemTable[log_id].drop(element->getPid(), element);
@@ -181,6 +228,7 @@
 }
 
 // caller must own and free character string
+// Requires parent LogBuffer::wrlock() to be held
 const char* LogStatistics::uidToName(uid_t uid) const {
     // Local hard coded favourites
     if (uid == AID_LOGD) {
@@ -457,6 +505,45 @@
     return formatLine(name, size, pruned);
 }
 
+static std::string formatMsec(uint64_t val) {
+    static const unsigned subsecDigits = 3;
+    static const uint64_t sec = MS_PER_SEC;
+
+    static const uint64_t minute = 60 * sec;
+    static const uint64_t hour = 60 * minute;
+    static const uint64_t day = 24 * hour;
+
+    std::string output;
+    if (val < sec) return output;
+
+    if (val >= day) {
+        output = android::base::StringPrintf("%" PRIu64 "d ", val / day);
+        val = (val % day) + day;
+    }
+    if (val >= minute) {
+        if (val >= hour) {
+            output += android::base::StringPrintf("%" PRIu64 ":",
+                                                  (val / hour) % (day / hour));
+        }
+        output += android::base::StringPrintf(
+            (val >= hour) ? "%02" PRIu64 ":" : "%" PRIu64 ":",
+            (val / minute) % (hour / minute));
+    }
+    output +=
+        android::base::StringPrintf((val >= minute) ? "%02" PRIu64 : "%" PRIu64,
+                                    (val / sec) % (minute / sec));
+    val %= sec;
+    unsigned digits = subsecDigits;
+    while (digits && ((val % 10) == 0)) {
+        val /= 10;
+        --digits;
+    }
+    if (digits) {
+        output += android::base::StringPrintf(".%0*" PRIu64, digits, val);
+    }
+    return output;
+}
+
 std::string LogStatistics::format(uid_t uid, pid_t pid,
                                   unsigned int logMask) const {
     static const unsigned short spaces_total = 19;
@@ -526,6 +613,67 @@
     output += android::base::StringPrintf("%*s%zu/%zu", spaces, "", totalSize,
                                           totalEls);
 
+    static const char SpanStr[] = "\nLogspan";
+    spaces = 10 - strlen(SpanStr);
+    output += SpanStr;
+
+    // Total reports the greater of the individual maximum time span, or the
+    // validated minimum start and maximum end time span if it makes sense.
+    uint64_t minTime = UINT64_MAX;
+    uint64_t maxTime = 0;
+    uint64_t maxSpan = 0;
+    totalSize = 0;
+
+    log_id_for_each(id) {
+        if (!(logMask & (1 << id))) continue;
+
+        // validity checking
+        uint64_t oldest = mOldest[id].msec();
+        uint64_t newest = mNewest[id].msec();
+        if (newest <= oldest) {
+            spaces += spaces_total;
+            continue;
+        }
+
+        uint64_t span = newest - oldest;
+        if (span > (monthSec * MS_PER_SEC)) {
+            spaces += spaces_total;
+            continue;
+        }
+
+        // total span
+        if (minTime > oldest) minTime = oldest;
+        if (maxTime < newest) maxTime = newest;
+        if (span > maxSpan) maxSpan = span;
+        totalSize += span;
+
+        uint64_t dropped = mNewestDropped[id].msec();
+        if (dropped < oldest) dropped = oldest;
+        if (dropped > newest) dropped = newest;
+
+        oldLength = output.length();
+        output += android::base::StringPrintf("%*s%s", spaces, "",
+                                              formatMsec(span).c_str());
+        unsigned permille = ((newest - dropped) * 1000 + (span / 2)) / span;
+        if ((permille > 1) && (permille < 999)) {
+            output += android::base::StringPrintf("(%u", permille / 10);
+            permille %= 10;
+            if (permille) {
+                output += android::base::StringPrintf(".%u", permille);
+            }
+            output += android::base::StringPrintf("%%)");
+        }
+        spaces -= output.length() - oldLength;
+        spaces += spaces_total;
+    }
+    if ((maxTime > minTime) && ((maxTime -= minTime) < totalSize) &&
+        (maxTime > maxSpan)) {
+        maxSpan = maxTime;
+    }
+    if (spaces < 0) spaces = 0;
+    output += android::base::StringPrintf("%*s%s", spaces, "",
+                                          formatMsec(maxSpan).c_str());
+
     static const char OverheadStr[] = "\nOverhead";
     spaces = 10 - strlen(OverheadStr);
     output += OverheadStr;
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index 066b7de..e6f01d6 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -28,6 +28,7 @@
 
 #include <android-base/stringprintf.h>
 #include <android/log.h>
+#include <log/log_time.h>
 #include <private/android_filesystem_config.h>
 
 #include "LogBufferElement.h"
@@ -520,6 +521,9 @@
     size_t mDroppedElements[LOG_ID_MAX];
     size_t mSizesTotal[LOG_ID_MAX];
     size_t mElementsTotal[LOG_ID_MAX];
+    log_time mOldest[LOG_ID_MAX];
+    log_time mNewest[LOG_ID_MAX];
+    log_time mNewestDropped[LOG_ID_MAX];
     static size_t SizesTotal;
     bool enable;
 
@@ -576,6 +580,7 @@
         enable = true;
     }
 
+    void addTotal(LogBufferElement* entry);
     void add(LogBufferElement* entry);
     void subtract(LogBufferElement* entry);
     // entry->setDropped(1) must follow this call
diff --git a/logd/LogTags.cpp b/logd/LogTags.cpp
index 67649b1..fcd45bd 100644
--- a/logd/LogTags.cpp
+++ b/logd/LogTags.cpp
@@ -32,8 +32,8 @@
 #include <android-base/macros.h>
 #include <android-base/stringprintf.h>
 #include <log/log_event_list.h>
+#include <log/log_properties.h>
 #include <private/android_filesystem_config.h>
-#include <private/android_logger.h>
 
 #include "LogTags.h"
 #include "LogUtils.h"
diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp
index ccc550a..25c2ad2 100644
--- a/logd/LogTimes.cpp
+++ b/logd/LogTimes.cpp
@@ -78,7 +78,7 @@
 void LogTimeEntry::threadStop(void* obj) {
     LogTimeEntry* me = reinterpret_cast<LogTimeEntry*>(obj);
 
-    lock();
+    wrlock();
 
     if (me->mNonBlock) {
         me->error_Locked();
@@ -134,7 +134,7 @@
 
     me->leadingDropped = true;
 
-    lock();
+    wrlock();
 
     log_time start = me->mStart;
 
@@ -160,7 +160,7 @@
         start = logbuf.flushTo(client, start, me->mLastTid, privileged,
                                security, FilterSecondPass, me);
 
-        lock();
+        wrlock();
 
         if (start == LogBufferElement::FLUSH_ERROR) {
             me->error_Locked();
@@ -191,7 +191,7 @@
 int LogTimeEntry::FilterFirstPass(const LogBufferElement* element, void* obj) {
     LogTimeEntry* me = reinterpret_cast<LogTimeEntry*>(obj);
 
-    LogTimeEntry::lock();
+    LogTimeEntry::wrlock();
 
     if (me->leadingDropped) {
         if (element->getDropped()) {
@@ -219,7 +219,7 @@
 int LogTimeEntry::FilterSecondPass(const LogBufferElement* element, void* obj) {
     LogTimeEntry* me = reinterpret_cast<LogTimeEntry*>(obj);
 
-    LogTimeEntry::lock();
+    LogTimeEntry::wrlock();
 
     me->mStart = element->getRealTime();
 
diff --git a/logd/LogTimes.h b/logd/LogTimes.h
index ec8252e..9ca2aea 100644
--- a/logd/LogTimes.h
+++ b/logd/LogTimes.h
@@ -61,7 +61,10 @@
     const log_time mEnd;  // only relevant if mNonBlock
 
     // Protect List manipulations
-    static void lock(void) {
+    static void wrlock(void) {
+        pthread_mutex_lock(&timesLock);
+    }
+    static void rdlock(void) {
         pthread_mutex_lock(&timesLock);
     }
     static void unlock(void) {
@@ -104,7 +107,7 @@
         mError = true;
     }
     void error(void) {
-        lock();
+        wrlock();
         error_Locked();
         unlock();
     }
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index 88cb67a..a1d154a 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -670,8 +670,12 @@
     while (--i) {
         int fd = socket_local_client("logdr", ANDROID_SOCKET_NAMESPACE_RESERVED,
                                      SOCK_SEQPACKET);
-        EXPECT_LT(0, fd);
-        if (fd < 0) _exit(fd);
+        int save_errno = errno;
+        if (fd < 0) {
+            fprintf(stderr, "failed to open /dev/socket/logdr %s\n",
+                    strerror(save_errno));
+            _exit(fd);
+        }
 
         std::string ask = android::base::StringPrintf(
             "dumpAndClose lids=0,1,2,3,4,5 timeout=6 start=%" PRIu32
@@ -723,8 +727,12 @@
         // active _or_ inactive during the test.
         if (content_timeout) {
             log_time msg(msg_timeout.entry.sec, msg_timeout.entry.nsec);
-            EXPECT_FALSE(msg < now);
-            if (msg < now) _exit(-1);
+            if (msg < now) {
+                fprintf(stderr, "%u.%09u < %u.%09u\n", msg_timeout.entry.sec,
+                        msg_timeout.entry.nsec, (unsigned)now.tv_sec,
+                        (unsigned)now.tv_nsec);
+                _exit(-1);
+            }
             if (msg > now) {
                 now = msg;
                 now.tv_sec += 30;
diff --git a/rootdir/init.rc b/rootdir/init.rc
index a43b0e1..a94a717 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -157,25 +157,25 @@
     # this ensures that the cpusets are present and usable, but the device's
     # init.rc must actually set the correct cpus
     mkdir /dev/cpuset/foreground
-    write /dev/cpuset/foreground/cpus 0
-    write /dev/cpuset/foreground/mems 0
+    copy /dev/cpuset/cpus /dev/cpuset/foreground/cpus
+    copy /dev/cpuset/mems /dev/cpuset/foreground/mems
     mkdir /dev/cpuset/foreground/boost
-    write /dev/cpuset/foreground/boost/cpus 0
-    write /dev/cpuset/foreground/boost/mems 0
+    copy /dev/cpuset/cpus /dev/cpuset/foreground/boost/cpus
+    copy /dev/cpuset/mems /dev/cpuset/foreground/boost/mems
     mkdir /dev/cpuset/background
-    write /dev/cpuset/background/cpus 0
-    write /dev/cpuset/background/mems 0
+    copy /dev/cpuset/cpus /dev/cpuset/background/cpus
+    copy /dev/cpuset/mems /dev/cpuset/background/mems
 
     # system-background is for system tasks that should only run on
     # little cores, not on bigs
     # to be used only by init, so don't change system-bg permissions
     mkdir /dev/cpuset/system-background
-    write /dev/cpuset/system-background/cpus 0
-    write /dev/cpuset/system-background/mems 0
+    copy /dev/cpuset/cpus /dev/cpuset/system-background/cpus
+    copy /dev/cpuset/mems /dev/cpuset/system-background/mems
 
     mkdir /dev/cpuset/top-app
-    write /dev/cpuset/top-app/cpus 0
-    write /dev/cpuset/top-app/mems 0
+    copy /dev/cpuset/cpus /dev/cpuset/top-app/cpus
+    copy /dev/cpuset/mems /dev/cpuset/top-app/mems
 
     # change permissions for all cpusets we'll touch at runtime
     chown system system /dev/cpuset
@@ -245,10 +245,6 @@
     class_stop charger
     trigger late-init
 
-# Load properties from /system/ + /factory after fs mount.
-on load_system_props_action
-    load_system_props
-
 on load_persist_props_action
     load_persist_props
     start logd
@@ -269,11 +265,6 @@
     trigger fs
     trigger post-fs
 
-    # Load properties from /system/ + /factory after fs mount. Place
-    # this in another action so that the load will be scheduled after the prior
-    # issued fs triggers have completed.
-    trigger load_system_props_action
-
     # Mount fstab in init.{$device}.rc by mount_all with '--late' parameter
     # to only mount entries with 'latemount'. This is needed if '--early' is
     # specified in the previous mount_all command on the fs stage.
@@ -298,6 +289,13 @@
     trigger boot
 
 on post-fs
+    # Load properties from
+    #     /system/build.prop,
+    #     /odm/build.prop,
+    #     /vendor/build.prop and
+    #     /factory/factory.prop
+    load_system_props
+    # start essential services
     start logd
     start hwservicemanager
 
@@ -638,9 +636,6 @@
     class_reset late_start
     class_reset main
 
-on property:sys.powerctl=*
-    powerctl ${sys.powerctl}
-
 on property:sys.boot_completed=1
     bootchart stop
 
diff --git a/shell_and_utilities/README.md b/shell_and_utilities/README.md
new file mode 100644
index 0000000..5d10c18
--- /dev/null
+++ b/shell_and_utilities/README.md
@@ -0,0 +1,157 @@
+Android's shell and utilities
+=============================
+
+Since IceCreamSandwich Android has used
+[mksh](https://www.mirbsd.org/mksh.htm) as its shell. Before then it used
+[ash](https://en.wikipedia.org/wiki/Almquist_shell) (which actually
+remained in the tree up to and including KitKat).
+
+Initially Android had a very limited command-line provided by its
+own "toolbox" binary. These days almost everything is supplied by
+[toybox](http://landley.net/toybox/) instead.
+
+We started moving a few of the more important tools to full
+BSD implementations in JellyBean before we started in earnest in
+Lollipop. Lollipop was a major break with the past in many ways (LP64
+support and the switch to ART both having lots of knock-on effects around
+the system), so although this was the beginning of the end of toolbox it
+(a) didn't stand out given all the other systems-level changes and (b)
+in Marshmallow we changed direction and started the move to toybox.
+
+The lists below show what tools were provided and where they came from in
+each release starting with Gingerbread. This doesn't tell the full story,
+because the toolbox implementations did have bugs fixed and options added
+over the years. Gingerbread's rm, for example, supported `-r`/`-R` but not
+`-f`. But this gives you an idea of what was available in any given release,
+and how usable it was likely to be.
+
+
+Android 2.3 (Gingerbread)
+-------------------------
+
+BSD: cat dd newfs\_msdos
+
+toolbox: chmod chown cmp date df dmesg getevent getprop hd id ifconfig
+iftop insmod ioctl ionice kill ln log ls lsmod lsof mkdir mount mv
+nandread netstat notify printenv ps reboot renice rm rmdir rmmod route
+schedtop sendevent setconsole setprop sleep smd start stop sync top
+umount uptime vmstat watchprops wipe
+
+
+Android 4.0 (IceCreamSandwich)
+------------------------------
+
+BSD: cat dd newfs\_msdos
+
+toolbox: chmod chown cmp date df dmesg getevent getprop hd id ifconfig
+iftop insmod ioctl ionice kill ln log ls lsmod lsof mkdir mount mv
+nandread netstat notify printenv ps reboot renice rm rmdir rmmod route
+schedtop sendevent setconsole setprop sleep smd start stop sync top
+touch umount uptime vmstat watchprops wipe
+
+
+Android 4.1-4.3 (JellyBean)
+---------------------------
+
+BSD: cat cp dd du grep newfs\_msdos
+
+toolbox: chcon chmod chown clear cmp date df dmesg getenforce getevent
+getprop getsebool hd id ifconfig iftop insmod ioctl ionice kill ln
+load\_policy log ls lsmod lsof md5 mkdir mount mv nandread netstat notify
+printenv ps reboot renice restorecon rm rmdir rmmod route runcon schedtop
+sendevent setconsole setenforce setprop setsebool sleep smd start stop
+sync top touch umount uptime vmstat watchprops wipe
+
+
+Android 4.4 (KitKat)
+--------------------
+
+BSD: cat cp dd du grep newfs\_msdos
+
+toolbox: chcon chmod chown clear cmp date df dmesg getenforce getevent
+getprop getsebool hd id ifconfig iftop insmod ioctl ionice kill ln
+load\_policy log ls lsmod lsof md5 mkdir mkswap mount mv nandread netstat
+notify printenv ps readlink renice restorecon rm rmdir rmmod route runcon
+schedtop sendevent setconsole setenforce setprop setsebool sleep smd start
+stop swapoff swapon sync top touch umount uptime vmstat watchprops wipe
+
+
+Android 5.0 (Lollipop)
+----------------------
+
+BSD: cat chown cp dd du grep kill ln mv printenv rm rmdir sleep sync
+
+toolbox: chcon chmod clear cmp date df dmesg getenforce getevent getprop
+getsebool hd id ifconfig iftop insmod ioctl ionice load\_policy log ls
+lsmod lsof md5 mkdir mknod mkswap mount nandread netstat newfs\_msdos
+nohup notify ps readlink renice restorecon rmmod route runcon schedtop
+sendevent setenforce setprop setsebool smd start stop swapoff swapon
+top touch umount uptime vmstat watchprops wipe
+
+
+Android 6.0 (Marshmallow)
+-------------------------
+
+BSD: dd du grep
+
+toolbox: df getevent iftop ioctl ionice log ls lsof mount nandread
+newfs\_msdos ps prlimit renice sendevent start stop top uptime watchprops
+
+toybox: acpi basename blockdev bzcat cal cat chcon chgrp chmod chown
+chroot cksum clear comm cmp cp cpio cut date dirname dmesg dos2unix echo
+env expand expr fallocate false find free getenforce getprop groups
+head hostname hwclock id ifconfig inotifyd insmod kill load\_policy ln
+logname losetup lsmod lsusb md5sum mkdir mknod mkswap mktemp modinfo
+more mountpoint mv netstat nice nl nohup od paste patch pgrep pidof
+pkill pmap printenv printf pwd readlink realpath restorecon rm rmdir
+rmmod route runcon sed seq setenforce setprop setsid sha1sum sleep sort
+split stat strings swapoff swapon sync sysctl tac tail tar taskset tee
+time timeout touch tr true truncate umount uname uniq unix2dos usleep
+vmstat wc which whoami xargs yes
+
+
+Android 7.0 (Nougat)
+--------------------
+
+BSD: dd grep
+
+toolbox: getevent iftop ioctl log nandread newfs\_msdos ps prlimit
+sendevent start stop top
+
+toybox: acpi base64 basename blockdev bzcat cal cat chcon chgrp chmod
+chown chroot cksum clear comm cmp cp cpio cut date df dirname dmesg
+dos2unix du echo env expand expr fallocate false find flock free
+getenforce getprop groups head hostname hwclock id ifconfig inotifyd
+insmod ionice iorenice kill killall load\_policy ln logname losetup ls
+lsmod lsof lsusb md5sum mkdir mknod mkswap mktemp modinfo more mount
+mountpoint mv netstat nice nl nohup od paste patch pgrep pidof pkill
+pmap printenv printf pwd readlink realpath renice restorecon rm rmdir
+rmmod route runcon sed seq setenforce setprop setsid sha1sum sleep sort
+split stat strings swapoff swapon sync sysctl tac tail tar taskset tee
+time timeout touch tr true truncate tty ulimit umount uname uniq unix2dos
+uptime usleep vmstat wc which whoami xargs xxd yes
+
+
+Current AOSP
+------------
+
+BSD: dd grep
+
+bzip2: bzcat bzip2 bunzip2
+
+toolbox: getevent gzip newfs\_msdos gunzip zcat
+
+toybox: acpi base64 basename blockdev cal cat chcon chgrp chmod chown
+chroot chrt cksum clear cmp comm cp cpio cut date df diff dirname dmesg
+dos2unix du echo env expand expr fallocate false file find flock free
+getenforce getprop groups head hostname hwclock id ifconfig inotifyd
+insmod ionice iorenice kill killall ln load\_policy log logname losetup
+ls lsmod lsof lsusb md5sum microcom mkdir mknod mkswap mktemp modinfo
+modprobe more mount mountpoint mv netstat nice nl nohup od paste patch
+pgrep pidof pkill pmap printenv printf ps pwd readlink realpath renice
+restorecon rm rmdir rmmod runcon sed sendevent seq setenforce setprop
+setsid sha1sum sha224sum sha256sum sha384sum sha512sum sleep sort split
+start stat stop strings swapoff swapon sync sysctl tac tail tar taskset
+tee time timeout top touch tr true truncate tty ulimit umount uname uniq
+unix2dos uptime usleep uudecode uuencode vmstat wc which whoami xargs
+xxd yes