Merge "libutils: Add UNEXPECTED_NULL status_t"
diff --git a/adb/adb.h b/adb/adb.h
index c284c8c..491fff3 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -50,7 +50,7 @@
 std::string adb_version();
 
 // Increment this when we want to force users to start a new adb server.
-#define ADB_SERVER_VERSION 34
+#define ADB_SERVER_VERSION 35
 
 class atransport;
 struct usb_handle;
diff --git a/adb/adb_trace.cpp b/adb/adb_trace.cpp
index 9586f7c..cf99df7 100644
--- a/adb/adb_trace.cpp
+++ b/adb/adb_trace.cpp
@@ -163,7 +163,7 @@
     }
 #endif
 
-    android::base::InitLogging(argv, AdbLogger);
+    android::base::InitLogging(argv, &AdbLogger);
     setup_trace_mask();
 
     VLOG(ADB) << adb_version();
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index fd61bda..42f1c7d 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -182,11 +182,8 @@
     line.push_back(' ');
 
     for (size_t i = 0; i < byte_count; ++i) {
-        int c = p[i];
-        if (c < 32 || c > 127) {
-            c = '.';
-        }
-        line.push_back(c);
+        int ch = p[i];
+        line.push_back(isprint(ch) ? ch : '.');
     }
 
     return line;
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index 785fef3..6e4c4e8 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -59,6 +59,8 @@
 static int install_app(TransportType t, const char* serial, int argc, const char** argv);
 static int install_multiple_app(TransportType t, const char* serial, int argc, const char** argv);
 static int uninstall_app(TransportType t, const char* serial, int argc, const char** argv);
+static int install_app_legacy(TransportType t, const char* serial, int argc, const char** argv);
+static int uninstall_app_legacy(TransportType t, const char* serial, int argc, const char** argv);
 
 static std::string gProductOutPath;
 extern int gListenAll;
@@ -110,11 +112,12 @@
         "                                 (-a preserves file timestamp and mode)\n"
         "  adb sync [ <directory> ]     - copy host->device only if changed\n"
         "                                 (-l means list but don't copy)\n"
-        "  adb shell [-Ttx]             - run remote shell interactively\n"
-        "  adb shell [-Ttx] <command>   - run remote shell command\n"
-        "                                 (-T disables PTY allocation)\n"
-        "                                 (-t forces PTY allocation)\n"
-        "                                 (-x disables remote exit codes and stdout/stderr separation)\n"
+        "  adb shell [-e escape] [-Tt] [-x] [command]\n"
+        "                               - run remote shell command (interactive shell if no command given)\n"
+        "                                 (-e: choose escape character, or \"none\"; default '~')\n"
+        "                                 (-T: disable PTY allocation)\n"
+        "                                 (-t: force PTY allocation)\n"
+        "                                 (-x: disable remote exit codes and stdout/stderr separation)\n"
         "  adb emu <command>            - run emulator console command\n"
         "  adb logcat [ <filter-spec> ] - View device log\n"
         "  adb forward --list           - list all forward socket connections.\n"
@@ -465,6 +468,7 @@
     int stdin_fd, write_fd;
     bool raw_stdin;
     std::unique_ptr<ShellProtocol> protocol;
+    char escape_char;
 };
 
 // Loops to read from stdin and push the data to the given FD.
@@ -472,7 +476,6 @@
 // will take ownership of the object and delete it when finished.
 static void* stdin_read_thread_loop(void* x) {
     std::unique_ptr<StdinReadArgs> args(reinterpret_cast<StdinReadArgs*>(x));
-    int state = 0;
 
 #if !defined(_WIN32)
     // Mask SIGTTIN in case we're in a backgrounded process.
@@ -494,7 +497,7 @@
     // Set up the initial window size.
     send_window_size_change(args->stdin_fd, args->protocol);
 
-    char raw_buffer[1024];
+    char raw_buffer[BUFSIZ];
     char* buffer_ptr = raw_buffer;
     size_t buffer_size = sizeof(raw_buffer);
     if (args->protocol != nullptr) {
@@ -502,6 +505,14 @@
         buffer_size = args->protocol->data_capacity();
     }
 
+    // If we need to parse escape sequences, make life easy.
+    if (args->raw_stdin && args->escape_char != '\0') {
+        buffer_size = 1;
+    }
+
+    enum EscapeState { kMidFlow, kStartOfLine, kInEscape };
+    EscapeState state = kStartOfLine;
+
     while (true) {
         // Use unix_read() rather than adb_read() for stdin.
         D("stdin_read_thread_loop(): pre unix_read(fdi=%d,...)", args->stdin_fd);
@@ -527,36 +538,36 @@
             }
             break;
         }
-        // If we made stdin raw, check input for the "~." escape sequence. In
+        // If we made stdin raw, check input for escape sequences. In
         // this situation signals like Ctrl+C are sent remotely rather than
         // interpreted locally so this provides an emergency out if the remote
         // process starts ignoring the signal. SSH also does this, see the
         // "escape characters" section on the ssh man page for more info.
-        if (args->raw_stdin) {
-            for (int n = 0; n < r; n++) {
-                switch (buffer_ptr[n]) {
-                case '\n':
-                    state = 1;
-                    break;
-                case '\r':
-                    state = 1;
-                    break;
-                case '~':
-                    if (state == 1) {
-                        state++;
-                    } else {
-                        state = 0;
-                    }
-                    break;
-                case '.':
-                    if (state == 2) {
-                        fprintf(stderr,"\r\n* disconnect *\r\n");
+        if (args->raw_stdin && args->escape_char != '\0') {
+            char ch = buffer_ptr[0];
+            if (ch == args->escape_char) {
+                if (state == kStartOfLine) {
+                    state = kInEscape;
+                    // Swallow the escape character.
+                    continue;
+                } else {
+                    state = kMidFlow;
+                }
+            } else {
+                if (state == kInEscape) {
+                    if (ch == '.') {
+                        fprintf(stderr,"\r\n[ disconnected ]\r\n");
                         stdin_raw_restore();
                         exit(0);
+                    } else {
+                        // We swallowed an escape character that wasn't part of
+                        // a valid escape sequence; time to cough it up.
+                        buffer_ptr[0] = args->escape_char;
+                        buffer_ptr[1] = ch;
+                        ++r;
                     }
-                default:
-                    state = 0;
                 }
+                state = (ch == '\n' || ch == '\r') ? kStartOfLine : kMidFlow;
             }
         }
         if (args->protocol) {
@@ -601,6 +612,7 @@
 // On success returns the remote exit code if |use_shell_protocol| is true,
 // 0 otherwise. On failure returns 1.
 static int RemoteShell(bool use_shell_protocol, const std::string& type_arg,
+                       char escape_char,
                        const std::string& command) {
     std::string service_string = ShellServiceString(use_shell_protocol,
                                                     type_arg, command);
@@ -626,6 +638,7 @@
     args->stdin_fd = STDIN_FILENO;
     args->write_fd = fd;
     args->raw_stdin = raw_stdin;
+    args->escape_char = escape_char;
     if (use_shell_protocol) {
         args->protocol.reset(new ShellProtocol(args->write_fd));
     }
@@ -687,8 +700,17 @@
     --argc;
     ++argv;
     int t_arg_count = 0;
+    char escape_char = '~';
     while (argc) {
-        if (!strcmp(argv[0], "-T") || !strcmp(argv[0], "-t")) {
+        if (!strcmp(argv[0], "-e")) {
+            if (argc < 2 || !(strlen(argv[1]) == 1 || strcmp(argv[1], "none") == 0)) {
+                fprintf(stderr, "error: -e requires a single-character argument or 'none'\n");
+                return 1;
+            }
+            escape_char = (strcmp(argv[1], "none") == 0) ? 0 : argv[1][0];
+            argc -= 2;
+            argv += 2;
+        } else if (!strcmp(argv[0], "-T") || !strcmp(argv[0], "-t")) {
             if (!CanUseFeature(features, kFeatureShell2)) {
                 fprintf(stderr, "error: target doesn't support PTY args -Tt\n");
                 return 1;
@@ -744,7 +766,7 @@
         command = android::base::Join(std::vector<const char*>(argv, argv + argc), ' ');
     }
 
-    return RemoteShell(use_shell_protocol, shell_type_arg, command);
+    return RemoteShell(use_shell_protocol, shell_type_arg, escape_char, command);
 }
 
 static int adb_download_buffer(const char *service, const char *fn, const void* data, unsigned sz,
@@ -1622,7 +1644,11 @@
     }
     else if (!strcmp(argv[0], "install")) {
         if (argc < 2) return usage();
-        return install_app(transport_type, serial, argc, argv);
+        FeatureSet features = GetFeatureSet(transport_type, serial);
+        if (CanUseFeature(features, kFeatureCmd)) {
+            return install_app(transport_type, serial, argc, argv);
+        }
+        return install_app_legacy(transport_type, serial, argc, argv);
     }
     else if (!strcmp(argv[0], "install-multiple")) {
         if (argc < 2) return usage();
@@ -1630,7 +1656,11 @@
     }
     else if (!strcmp(argv[0], "uninstall")) {
         if (argc < 2) return usage();
-        return uninstall_app(transport_type, serial, argc, argv);
+        FeatureSet features = GetFeatureSet(transport_type, serial);
+        if (CanUseFeature(features, kFeatureCmd)) {
+            return uninstall_app(transport_type, serial, argc, argv);
+        }
+        return uninstall_app_legacy(transport_type, serial, argc, argv);
     }
     else if (!strcmp(argv[0], "sync")) {
         std::string src;
@@ -1738,86 +1768,83 @@
     return 1;
 }
 
-static int pm_command(TransportType transport, const char* serial, int argc, const char** argv) {
-    std::string cmd = "pm";
-
+static int uninstall_app(TransportType transport, const char* serial, int argc, const char** argv) {
+    // 'adb uninstall' takes the same arguments as 'cmd package uninstall' on device
+    std::string cmd = "cmd package";
     while (argc-- > 0) {
+        // deny the '-k' option until the remaining data/cache can be removed with adb/UI
+        if (strcmp(*argv, "-k") == 0) {
+            printf(
+                "The -k option uninstalls the application while retaining the data/cache.\n"
+                "At the moment, there is no way to remove the remaining data.\n"
+                "You will have to reinstall the application with the same signature, and fully uninstall it.\n"
+                "If you truly wish to continue, execute 'adb shell cmd package uninstall -k'.\n");
+            return EXIT_FAILURE;
+        }
         cmd += " " + escape_arg(*argv++);
     }
 
-    // TODO(dpursell): add command-line arguments to install/uninstall to
-    // manually disable shell protocol if needed.
-    return send_shell_command(transport, serial, cmd, false);
-}
-
-static int uninstall_app(TransportType transport, const char* serial, int argc, const char** argv) {
-    /* if the user choose the -k option, we refuse to do it until devices are
-       out with the option to uninstall the remaining data somehow (adb/ui) */
-    if (argc == 3 && strcmp(argv[1], "-k") == 0)
-    {
-        printf(
-            "The -k option uninstalls the application while retaining the data/cache.\n"
-            "At the moment, there is no way to remove the remaining data.\n"
-            "You will have to reinstall the application with the same signature, and fully uninstall it.\n"
-            "If you truly wish to continue, execute 'adb shell pm uninstall -k %s'\n", argv[2]);
-        return -1;
-    }
-
-    /* 'adb uninstall' takes the same arguments as 'pm uninstall' on device */
-    return pm_command(transport, serial, argc, argv);
-}
-
-static int delete_file(TransportType transport, const char* serial, const std::string& filename) {
-    std::string cmd = "rm -f " + escape_arg(filename);
     return send_shell_command(transport, serial, cmd, false);
 }
 
 static int install_app(TransportType transport, const char* serial, int argc, const char** argv) {
-    static const char *const DATA_DEST = "/data/local/tmp/%s";
-    static const char *const SD_DEST = "/sdcard/tmp/%s";
-    const char* where = DATA_DEST;
-    int i;
+    // The last argument must be the APK file
+    const char* file = argv[argc - 1];
+    const char* dot = strrchr(file, '.');
+    bool found_apk = false;
     struct stat sb;
-
-    for (i = 1; i < argc; i++) {
-        if (!strcmp(argv[i], "-s")) {
-            where = SD_DEST;
+    if (dot && !strcasecmp(dot, ".apk")) {
+        if (stat(file, &sb) == -1 || !S_ISREG(sb.st_mode)) {
+            fprintf(stderr, "Invalid APK file: %s\n", file);
+            return EXIT_FAILURE;
         }
+        found_apk = true;
     }
 
-    // Find last APK argument.
-    // All other arguments passed through verbatim.
-    int last_apk = -1;
-    for (i = argc - 1; i >= 0; i--) {
-        const char* file = argv[i];
-        const char* dot = strrchr(file, '.');
-        if (dot && !strcasecmp(dot, ".apk")) {
-            if (stat(file, &sb) == -1 || !S_ISREG(sb.st_mode)) {
-                fprintf(stderr, "Invalid APK file: %s\n", file);
-                return -1;
-            }
-
-            last_apk = i;
-            break;
-        }
-    }
-
-    if (last_apk == -1) {
+    if (!found_apk) {
         fprintf(stderr, "Missing APK file\n");
-        return -1;
+        return EXIT_FAILURE;
     }
 
-    int result = -1;
-    std::vector<const char*> apk_file = {argv[last_apk]};
-    std::string apk_dest = android::base::StringPrintf(
-        where, adb_basename(argv[last_apk]).c_str());
-    if (!do_sync_push(apk_file, apk_dest.c_str())) goto cleanup_apk;
-    argv[last_apk] = apk_dest.c_str(); /* destination name, not source location */
-    result = pm_command(transport, serial, argc, argv);
+    int localFd = adb_open(file, O_RDONLY);
+    if (localFd < 0) {
+        fprintf(stderr, "Failed to open %s: %s\n", file, strerror(errno));
+        return 1;
+    }
 
-cleanup_apk:
-    delete_file(transport, serial, apk_dest);
-    return result;
+    std::string error;
+    std::string cmd = "exec:cmd package";
+
+    // don't copy the APK name, but, copy the rest of the arguments as-is
+    while (argc-- > 1) {
+        cmd += " " + escape_arg(std::string(*argv++));
+    }
+
+    // add size parameter [required for streaming installs]
+    // do last to override any user specified value
+    cmd += " " + android::base::StringPrintf("-S %" PRIu64, static_cast<uint64_t>(sb.st_size));
+
+    int remoteFd = adb_connect(cmd, &error);
+    if (remoteFd < 0) {
+        fprintf(stderr, "Connect error for write: %s\n", error.c_str());
+        adb_close(localFd);
+        return 1;
+    }
+
+    char buf[BUFSIZ];
+    copy_to_file(localFd, remoteFd);
+    read_status_line(remoteFd, buf, sizeof(buf));
+
+    adb_close(localFd);
+    adb_close(remoteFd);
+
+    if (strncmp("Success", buf, 7)) {
+        fprintf(stderr, "Failed to write %s\n", file);
+        fputs(buf, stderr);
+        return 1;
+    }
+    fputs(buf, stderr);
+    return 0;
 }
 
 static int install_multiple_app(TransportType transport, const char* serial, int argc,
@@ -1836,7 +1863,7 @@
         if (dot && !strcasecmp(dot, ".apk")) {
             if (stat(file, &sb) == -1 || !S_ISREG(sb.st_mode)) {
                 fprintf(stderr, "Invalid APK file: %s\n", file);
-                return -1;
+                return EXIT_FAILURE;
             }
 
             total_size += sb.st_size;
@@ -1861,7 +1888,7 @@
     int fd = adb_connect(cmd, &error);
     if (fd < 0) {
         fprintf(stderr, "Connect error for create: %s\n", error.c_str());
-        return -1;
+        return EXIT_FAILURE;
     }
     char buf[BUFSIZ];
     read_status_line(fd, buf, sizeof(buf));
@@ -1879,7 +1906,7 @@
     if (session_id < 0) {
         fprintf(stderr, "Failed to create session\n");
         fputs(buf, stderr);
-        return -1;
+        return EXIT_FAILURE;
     }
 
     // Valid session, now stream the APKs
@@ -1934,7 +1961,7 @@
     fd = adb_connect(service, &error);
     if (fd < 0) {
         fprintf(stderr, "Connect error for finalize: %s\n", error.c_str());
-        return -1;
+        return EXIT_FAILURE;
     }
     read_status_line(fd, buf, sizeof(buf));
     adb_close(fd);
@@ -1945,6 +1972,88 @@
     } else {
         fprintf(stderr, "Failed to finalize session\n");
         fputs(buf, stderr);
-        return -1;
+        return EXIT_FAILURE;
     }
 }
+
+static int pm_command(TransportType transport, const char* serial, int argc, const char** argv) {
+    std::string cmd = "pm";
+
+    while (argc-- > 0) {
+        cmd += " " + escape_arg(*argv++);
+    }
+
+    return send_shell_command(transport, serial, cmd, false);
+}
+
+static int uninstall_app_legacy(TransportType transport, const char* serial, int argc, const char** argv) {
+    /* if the user choose the -k option, we refuse to do it until devices are
+       out with the option to uninstall the remaining data somehow (adb/ui) */
+    int i;
+    for (i = 1; i < argc; i++) {
+        if (!strcmp(argv[i], "-k")) {
+            printf(
+                "The -k option uninstalls the application while retaining the data/cache.\n"
+                "At the moment, there is no way to remove the remaining data.\n"
+                "You will have to reinstall the application with the same signature, and fully uninstall it.\n"
+                "If you truly wish to continue, execute 'adb shell pm uninstall -k'\n.");
+            return EXIT_FAILURE;
+        }
+    }
+
+    /* 'adb uninstall' takes the same arguments as 'pm uninstall' on device */
+    return pm_command(transport, serial, argc, argv);
+}
+
+static int delete_file(TransportType transport, const char* serial, const std::string& filename) {
+    std::string cmd = "rm -f " + escape_arg(filename);
+    return send_shell_command(transport, serial, cmd, false);
+}
+
+static int install_app_legacy(TransportType transport, const char* serial, int argc, const char** argv) {
+    static const char *const DATA_DEST = "/data/local/tmp/%s";
+    static const char *const SD_DEST = "/sdcard/tmp/%s";
+    const char* where = DATA_DEST;
+    int i;
+    struct stat sb;
+
+    for (i = 1; i < argc; i++) {
+        if (!strcmp(argv[i], "-s")) {
+            where = SD_DEST;
+        }
+    }
+
+    // Find last APK argument.
+    // All other arguments passed through verbatim.
+    int last_apk = -1;
+    for (i = argc - 1; i >= 0; i--) {
+        const char* file = argv[i];
+        const char* dot = strrchr(file, '.');
+        if (dot && !strcasecmp(dot, ".apk")) {
+            if (stat(file, &sb) == -1 || !S_ISREG(sb.st_mode)) {
+                fprintf(stderr, "Invalid APK file: %s\n", file);
+                return EXIT_FAILURE;
+            }
+
+            last_apk = i;
+            break;
+        }
+    }
+
+    if (last_apk == -1) {
+        fprintf(stderr, "Missing APK file\n");
+        return EXIT_FAILURE;
+    }
+
+    int result = -1;
+    std::vector<const char*> apk_file = {argv[last_apk]};
+    std::string apk_dest = android::base::StringPrintf(
+        where, adb_basename(argv[last_apk]).c_str());
+    if (!do_sync_push(apk_file, apk_dest.c_str())) goto cleanup_apk;
+    argv[last_apk] = apk_dest.c_str(); /* destination name, not source location */
+    result = pm_command(transport, serial, argc, argv);
+
+cleanup_apk:
+    delete_file(transport, serial, apk_dest);
+    return result;
+}
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index 8c3ca63..b8d758f 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -142,9 +142,11 @@
     // AID_SDCARD_R to allow reading from the SD card
     // AID_SDCARD_RW to allow writing to the SD card
     // AID_NET_BW_STATS to read out qtaguid statistics
+    // AID_READPROC for reading /proc entries across UID boundaries
     gid_t groups[] = {AID_ADB,      AID_LOG,       AID_INPUT,
                       AID_INET,     AID_NET_BT,    AID_NET_BT_ADMIN,
-                      AID_SDCARD_R, AID_SDCARD_RW, AID_NET_BW_STATS};
+                      AID_SDCARD_R, AID_SDCARD_RW, AID_NET_BW_STATS,
+                      AID_READPROC };
     if (setgroups(sizeof(groups) / sizeof(groups[0]), groups) != 0) {
         PLOG(FATAL) << "Could not set supplental groups";
     }
diff --git a/adb/file_sync_client.cpp b/adb/file_sync_client.cpp
index d2bc3cd..3322763 100644
--- a/adb/file_sync_client.cpp
+++ b/adb/file_sync_client.cpp
@@ -209,6 +209,17 @@
         line_printer_.Print(s, LinePrinter::FULL);
     }
 
+    void Warning(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) {
+        std::string s = "adb: warning: ";
+
+        va_list ap;
+        va_start(ap, fmt);
+        android::base::StringAppendV(&s, fmt, ap);
+        va_end(ap);
+
+        line_printer_.Print(s, LinePrinter::FULL);
+    }
+
     uint64_t total_bytes;
 
     // TODO: add a char[max] buffer here, to replace syncsendbuf...
@@ -475,12 +486,28 @@
     bool skip;
 };
 
-static copyinfo mkcopyinfo(const char* spath, const char* dpath, const char* name, bool isdir) {
+static copyinfo mkcopyinfo(const std::string& spath, const std::string& dpath,
+                           const std::string& name, unsigned int mode) {
     copyinfo result;
-    result.src = android::base::StringPrintf(isdir ? "%s%s/" : "%s%s", spath, name);
-    result.dst = android::base::StringPrintf(isdir ? "%s%s/" : "%s%s", dpath, name);
+    result.src = spath;
+    result.dst = dpath;
+    if (result.src.back() != '/') {
+      result.src.push_back('/');
+    }
+    if (result.dst.back() != '/') {
+      result.dst.push_back('/');
+    }
+    result.src.append(name);
+    result.dst.append(name);
+
+    bool isdir = S_ISDIR(mode);
+    if (isdir) {
+        result.src.push_back('/');
+        result.dst.push_back('/');
+    }
+
     result.time = 0;
-    result.mode = 0;
+    result.mode = mode;
     result.size = 0;
     result.skip = false;
     return result;
@@ -490,89 +517,100 @@
     return name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'));
 }
 
-static int local_build_list(SyncConnection& sc, std::vector<copyinfo>* filelist,
-                            const char* lpath, const char* rpath) {
+static bool local_build_list(SyncConnection& sc, std::vector<copyinfo>* filelist,
+                             const std::string& lpath,
+                             const std::string& rpath) {
     std::vector<copyinfo> dirlist;
-    std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(lpath), closedir);
+    std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(lpath.c_str()), closedir);
     if (!dir) {
-        sc.Error("cannot open '%s': %s", lpath, strerror(errno));
-        return -1;
+        sc.Error("cannot open '%s': %s", lpath.c_str(), strerror(errno));
+        return false;
     }
 
+    bool empty_dir = true;
     dirent* de;
     while ((de = readdir(dir.get()))) {
-        if (IsDotOrDotDot(de->d_name)) continue;
-
-        char stat_path[PATH_MAX];
-        if (strlen(lpath) + strlen(de->d_name) + 1 > sizeof(stat_path)) {
-            sc.Error("skipping long path '%s%s'", lpath, de->d_name);
+        if (IsDotOrDotDot(de->d_name)) {
             continue;
         }
-        strcpy(stat_path, lpath);
-        strcat(stat_path, de->d_name);
+
+        empty_dir = false;
+        std::string stat_path = lpath + de->d_name;
 
         struct stat st;
-        if (!lstat(stat_path, &st)) {
+        if (!lstat(stat_path.c_str(), &st)) {
+            copyinfo ci = mkcopyinfo(lpath, rpath, de->d_name, st.st_mode);
             if (S_ISDIR(st.st_mode)) {
-                dirlist.push_back(mkcopyinfo(lpath, rpath, de->d_name, 1));
+                dirlist.push_back(ci);
             } else {
                 if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
-                    sc.Error("skipping special file '%s'", lpath);
+                    sc.Warning("skipping special file '%s'", lpath.c_str());
                 } else {
-                    copyinfo ci = mkcopyinfo(lpath, rpath, de->d_name, 0);
                     ci.time = st.st_mtime;
-                    ci.mode = st.st_mode;
                     ci.size = st.st_size;
                     filelist->push_back(ci);
                 }
             }
         } else {
-            sc.Error("cannot lstat '%s': %s", stat_path, strerror(errno));
+            sc.Error("cannot lstat '%s': %s", stat_path.c_str(),
+                     strerror(errno));
         }
     }
 
     // Close this directory and recurse.
     dir.reset();
+
+    // Add the current directory to the list if it was empty, to ensure that
+    // it gets created.
+    if (empty_dir) {
+        // TODO(b/25566053): Make pushing empty directories work.
+        // TODO(b/25457350): We don't preserve permissions on directories.
+        sc.Warning("skipping empty directory '%s'", lpath.c_str());
+        copyinfo ci = mkcopyinfo(adb_dirname(lpath), adb_dirname(rpath),
+                                 adb_basename(lpath), S_IFDIR);
+        ci.skip = true;
+        filelist->push_back(ci);
+        return true;
+    }
+
     for (const copyinfo& ci : dirlist) {
         local_build_list(sc, filelist, ci.src.c_str(), ci.dst.c_str());
     }
 
-    return 0;
+    return true;
 }
 
-static bool copy_local_dir_remote(SyncConnection& sc, const char* lpath, const char* rpath,
-                                  bool check_timestamps, bool list_only) {
+static bool copy_local_dir_remote(SyncConnection& sc, std::string lpath,
+                                  std::string rpath, bool check_timestamps,
+                                  bool list_only) {
+    // Make sure that both directory paths end in a slash.
+    // Both paths are known to exist, so they cannot be empty.
+    if (lpath.back() != '/') {
+        lpath.push_back('/');
+    }
+    if (rpath.back() != '/') {
+        rpath.push_back('/');
+    }
+
+    // Recursively build the list of files to copy.
     std::vector<copyinfo> filelist;
     int pushed = 0;
     int skipped = 0;
-
-    if ((lpath[0] == 0) || (rpath[0] == 0)) return false;
-    if (lpath[strlen(lpath) - 1] != '/') {
-        int  tmplen = strlen(lpath)+2;
-        char *tmp = reinterpret_cast<char*>(malloc(tmplen));
-        if(tmp == 0) return false;
-        snprintf(tmp, tmplen, "%s/",lpath);
-        lpath = tmp;
-    }
-    if (rpath[strlen(rpath) - 1] != '/') {
-        int tmplen = strlen(rpath)+2;
-        char *tmp = reinterpret_cast<char*>(malloc(tmplen));
-        if(tmp == 0) return false;
-        snprintf(tmp, tmplen, "%s/",rpath);
-        rpath = tmp;
-    }
-
-    if (local_build_list(sc, &filelist, lpath, rpath)) {
+    if (!local_build_list(sc, &filelist, lpath, rpath)) {
         return false;
     }
 
     if (check_timestamps) {
         for (const copyinfo& ci : filelist) {
-            if (!sc.SendRequest(ID_STAT, ci.dst.c_str())) return false;
+            if (!sc.SendRequest(ID_STAT, ci.dst.c_str())) {
+                return false;
+            }
         }
         for (copyinfo& ci : filelist) {
             unsigned int timestamp, mode, size;
-            if (!sync_finish_stat(sc, &timestamp, &mode, &size)) return false;
+            if (!sync_finish_stat(sc, &timestamp, &mode, &size)) {
+                return false;
+            }
             if (size == ci.size) {
                 /* for links, we cannot update the atime/mtime */
                 if ((S_ISREG(ci.mode & mode) && timestamp == ci.time) ||
@@ -586,11 +624,12 @@
     for (const copyinfo& ci : filelist) {
         if (!ci.skip) {
             if (list_only) {
-                fprintf(stderr, "would push: %s -> %s\n", ci.src.c_str(),
-                        ci.dst.c_str());
+                sc.Error("would push: %s -> %s", ci.src.c_str(),
+                         ci.dst.c_str());
             } else {
-                if (!sync_send(sc, ci.src.c_str(), ci.dst.c_str(), ci.time, ci.mode)) {
-                  return false;
+                if (!sync_send(sc, ci.src.c_str(), ci.dst.c_str(), ci.time,
+                               ci.mode)) {
+                    return false;
                 }
             }
             pushed++;
@@ -599,9 +638,9 @@
         }
     }
 
-    sc.Printf("%s: %d file%s pushed. %d file%s skipped.%s\n", rpath, pushed,
-              (pushed == 1) ? "" : "s", skipped, (skipped == 1) ? "" : "s",
-              sc.TransferRate().c_str());
+    sc.Printf("%s: %d file%s pushed. %d file%s skipped.%s\n", rpath.c_str(),
+              pushed, (pushed == 1) ? "" : "s", skipped,
+              (skipped == 1) ? "" : "s", sc.TransferRate().c_str());
     return true;
 }
 
@@ -658,33 +697,49 @@
 
 static bool remote_build_list(SyncConnection& sc,
                               std::vector<copyinfo>* filelist,
-                              const char* rpath, const char* lpath) {
+                              const std::string& rpath,
+                              const std::string& lpath) {
     std::vector<copyinfo> dirlist;
+    bool empty_dir = true;
 
     // Put the files/dirs in rpath on the lists.
     auto callback = [&](unsigned mode, unsigned size, unsigned time,
                         const char* name) {
-        if (S_ISDIR(mode)) {
-            // Don't try recursing down "." or "..".
-            if (IsDotOrDotDot(name)) return;
+        if (IsDotOrDotDot(name)) {
+            return;
+        }
 
-            dirlist.push_back(mkcopyinfo(rpath, lpath, name, 1));
+        // We found a child that isn't '.' or '..'.
+        empty_dir = false;
+
+        copyinfo ci = mkcopyinfo(rpath, lpath, name, mode);
+        if (S_ISDIR(mode)) {
+            dirlist.push_back(ci);
         } else if (S_ISREG(mode) || S_ISLNK(mode)) {
-            copyinfo ci = mkcopyinfo(rpath, lpath, name, 0);
             ci.time = time;
-            ci.mode = mode;
             ci.size = size;
             filelist->push_back(ci);
         } else {
-            sc.Print(android::base::StringPrintf("skipping special file '%s'\n",
-                                                 name));
+            sc.Warning("skipping special file '%s'\n", name);
         }
     };
 
-    if (!sync_ls(sc, rpath, callback)) {
+    if (!sync_ls(sc, rpath.c_str(), callback)) {
         return false;
     }
 
+    // Add the current directory to the list if it was empty, to ensure that
+    // it gets created.
+    if (empty_dir) {
+        auto rdname = adb_dirname(rpath);
+        auto ldname = adb_dirname(lpath);
+        auto rbasename = adb_basename(rpath);
+        auto lbasename = adb_basename(lpath);
+        filelist->push_back(mkcopyinfo(adb_dirname(rpath), adb_dirname(lpath),
+                                       adb_basename(rpath), S_IFDIR));
+        return true;
+    }
+
     // Recurse into each directory we found.
     while (!dirlist.empty()) {
         copyinfo current = dirlist.back();
@@ -708,23 +763,24 @@
     umask(mask);
     int r2 = chmod(lpath, mode & ~mask);
 
-    return r1 ? : r2;
+    return r1 ? r1 : r2;
 }
 
-static bool copy_remote_dir_local(SyncConnection& sc, const char* rpath, const char* lpath,
-                                  bool copy_attrs) {
+static bool copy_remote_dir_local(SyncConnection& sc, std::string rpath,
+                                  std::string lpath, bool copy_attrs) {
     // Make sure that both directory paths end in a slash.
-    std::string rpath_clean(rpath);
-    std::string lpath_clean(lpath);
-    if (rpath_clean.empty() || lpath_clean.empty()) return false;
-    if (rpath_clean.back() != '/') rpath_clean.push_back('/');
-    if (lpath_clean.back() != '/') lpath_clean.push_back('/');
+    // Both paths are known to exist, so they cannot be empty.
+    if (rpath.back() != '/') {
+        rpath.push_back('/');
+    }
+    if (lpath.back() != '/') {
+        lpath.push_back('/');
+    }
 
     // Recursively build the list of files to copy.
     sc.Print("pull: building file list...");
     std::vector<copyinfo> filelist;
-    if (!remote_build_list(sc, &filelist, rpath_clean.c_str(),
-                           lpath_clean.c_str())) {
+    if (!remote_build_list(sc, &filelist, rpath.c_str(), lpath.c_str())) {
         return false;
     }
 
@@ -733,6 +789,19 @@
     for (const copyinfo &ci : filelist) {
         if (!ci.skip) {
             sc.Printf("pull: %s -> %s", ci.src.c_str(), ci.dst.c_str());
+
+            if (S_ISDIR(ci.mode)) {
+                // Entry is for an empty directory, create it and continue.
+                // TODO(b/25457350): We don't preserve permissions on directories.
+                if (!mkdirs(ci.dst))  {
+                    sc.Error("failed to create directory '%s': %s",
+                             ci.dst.c_str(), strerror(errno));
+                    return false;
+                }
+                pulled++;
+                continue;
+            }
+
             if (!sync_recv(sc, ci.src.c_str(), ci.dst.c_str())) {
                 return false;
             }
@@ -747,9 +816,9 @@
         }
     }
 
-    sc.Printf("%s: %d file%s pulled. %d file%s skipped.%s\n", rpath, pulled,
-              (pulled == 1) ? "" : "s", skipped, (skipped == 1) ? "" : "s",
-              sc.TransferRate().c_str());
+    sc.Printf("%s: %d file%s pulled. %d file%s skipped.%s\n", rpath.c_str(),
+              pulled, (pulled == 1) ? "" : "s", skipped,
+              (skipped == 1) ? "" : "s", sc.TransferRate().c_str());
     return true;
 }
 
@@ -793,7 +862,8 @@
             continue;
         }
 
-        if (S_ISREG(mode) || S_ISLNK(mode) || S_ISCHR(mode) || S_ISBLK(mode)) {
+        if (S_ISREG(mode) || S_ISLNK(mode)) {
+            // TODO(b/25601283): symlinks shouldn't be handled as files.
             std::string path_holder;
             struct stat st;
             if (stat(dst_path, &st) == 0) {
@@ -833,5 +903,5 @@
     SyncConnection sc;
     if (!sc.IsValid()) return false;
 
-    return copy_local_dir_remote(sc, lpath.c_str(), rpath.c_str(), true, list_only);
+    return copy_local_dir_remote(sc, lpath, rpath, true, list_only);
 }
diff --git a/adb/mutex_list.h b/adb/mutex_list.h
index 79c48d8..b59c9f2 100644
--- a/adb/mutex_list.h
+++ b/adb/mutex_list.h
@@ -6,6 +6,7 @@
 #ifndef ADB_MUTEX
 #error ADB_MUTEX not defined when including this file
 #endif
+ADB_MUTEX(basename_lock)
 ADB_MUTEX(dirname_lock)
 ADB_MUTEX(socket_list_lock)
 ADB_MUTEX(transport_lock)
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index f8c2f64..eb0ce85 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -25,6 +25,8 @@
 #include <string.h>
 #include <unistd.h>
 
+#include <algorithm>
+
 #if !ADB_HOST
 #include "cutils/properties.h"
 #endif
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 1735627..9f4012a 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -309,7 +309,12 @@
 #define closedir adb_closedir
 #define rewinddir rewinddir_utf8_not_yet_implemented
 #define telldir telldir_utf8_not_yet_implemented
-#define seekdir seekdir_utf8_not_yet_implemented
+// Some compiler's C++ headers have members named seekdir, so we can't do the
+// macro technique and instead cause a link error if seekdir is called.
+inline void seekdir(DIR*, long) {
+    extern int seekdir_utf8_not_yet_implemented;
+    seekdir_utf8_not_yet_implemented = 1;
+}
 
 #define utime adb_utime
 #define chmod adb_chmod
diff --git a/adb/transport.cpp b/adb/transport.cpp
index e9e774f..4066889 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -26,6 +26,7 @@
 #include <string.h>
 #include <unistd.h>
 
+#include <algorithm>
 #include <list>
 
 #include <base/logging.h>
@@ -42,6 +43,9 @@
 
 ADB_MUTEX_DEFINE( transport_lock );
 
+const char* const kFeatureShell2 = "shell_v2";
+const char* const kFeatureCmd = "cmd";
+
 static std::string dump_packet(const char* name, const char* func, apacket* p) {
     unsigned  command = p->msg.command;
     int       len     = p->msg.data_length;
@@ -780,7 +784,10 @@
 const FeatureSet& supported_features() {
     // Local static allocation to avoid global non-POD variables.
     static const FeatureSet* features = new FeatureSet{
-        kFeatureShell2
+        kFeatureShell2,
+        // Internal master has 'cmd'. AOSP master doesn't.
+        // kFeatureCmd
+
         // Increment ADB_SERVER_VERSION whenever the feature list changes to
         // make sure that the adb client and server features stay in sync
         // (http://b/24370690).
diff --git a/adb/transport.h b/adb/transport.h
index f41a8d4..d9845b6 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -38,7 +38,9 @@
 
 // Do not use any of [:;=,] in feature strings, they have special meaning
 // in the connection banner.
-constexpr char kFeatureShell2[] = "shell_v2";
+extern const char* const kFeatureShell2;
+// The 'cmd' command is available
+extern const char* const kFeatureCmd;
 
 class atransport {
 public:
diff --git a/base/logging.cpp b/base/logging.cpp
index 6bfaaec..248cd06 100644
--- a/base/logging.cpp
+++ b/base/logging.cpp
@@ -212,8 +212,8 @@
   gInitialized = true;
 
   // Stash the command line for later use. We can use /proc/self/cmdline on
-  // Linux to recover this, but we don't have that luxury on the Mac, and there
-  // are a couple of argv[0] variants that are commonly used.
+  // Linux to recover this, but we don't have that luxury on the Mac/Windows,
+  // and there are a couple of argv[0] variants that are commonly used.
   if (argv != nullptr) {
     gProgramInvocationName.reset(new std::string(basename(argv[0])));
   }
@@ -264,11 +264,20 @@
   gLogger = std::move(logger);
 }
 
-// We can't use basename(3) because this code runs on the Mac, which doesn't
-// have a non-modifying basename.
 static const char* GetFileBasename(const char* file) {
+  // We can't use basename(3) even on Unix because the Mac doesn't
+  // have a non-modifying basename.
   const char* last_slash = strrchr(file, '/');
-  return (last_slash == nullptr) ? file : last_slash + 1;
+  if (last_slash != nullptr) {
+    return last_slash + 1;
+  }
+#if defined(_WIN32)
+  const char* last_backslash = strrchr(file, '\\');
+  if (last_backslash != nullptr) {
+    return last_backslash + 1;
+  }
+#endif
+  return file;
 }
 
 // This indirection greatly reduces the stack impact of having lots of
diff --git a/debuggerd/debuggerd.rc b/debuggerd/debuggerd.rc
index 4be2e5d..e43fe96 100644
--- a/debuggerd/debuggerd.rc
+++ b/debuggerd/debuggerd.rc
@@ -1,3 +1,4 @@
 service debuggerd /system/bin/debuggerd
     class main
+    group root readproc
     writepid /dev/cpuset/system-background/tasks
diff --git a/debuggerd/debuggerd64.rc b/debuggerd/debuggerd64.rc
index c6e7bf2..35b5af3 100644
--- a/debuggerd/debuggerd64.rc
+++ b/debuggerd/debuggerd64.rc
@@ -1,3 +1,4 @@
 service debuggerd64 /system/bin/debuggerd64
     class main
+    group root readproc
     writepid /dev/cpuset/system-background/tasks
diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h
index c7eb34b..e2133e9 100644
--- a/include/private/android_filesystem_config.h
+++ b/include/private/android_filesystem_config.h
@@ -101,6 +101,7 @@
 #define AID_NET_BW_STATS  3006  /* read bandwidth statistics */
 #define AID_NET_BW_ACCT   3007  /* change bandwidth statistics accounting */
 #define AID_NET_BT_STACK  3008  /* bluetooth: access config files */
+#define AID_READPROC      3009  /* Allow /proc read access */
 
 /* The range 5000-5999 is also reserved for OEM, and must never be used here. */
 #define AID_OEM_RESERVED_2_START 5000
@@ -191,6 +192,7 @@
     { "net_bw_stats",  AID_NET_BW_STATS, },
     { "net_bw_acct",   AID_NET_BW_ACCT, },
     { "net_bt_stack",  AID_NET_BT_STACK, },
+    { "readproc",      AID_READPROC, },
 
     { "everybody",     AID_EVERYBODY, },
     { "misc",          AID_MISC, },
diff --git a/init/init.cpp b/init/init.cpp
index a898b03..86aed9a 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -386,7 +386,7 @@
 
     struct dirent *dp;
     while ((dp = readdir(dir.get())) != NULL) {
-        if (dp->d_type != DT_REG || !strcmp(dp->d_name, "compatible")) {
+        if (dp->d_type != DT_REG || !strcmp(dp->d_name, "compatible") || !strcmp(dp->d_name, "name")) {
             continue;
         }
 
@@ -546,7 +546,8 @@
         mkdir("/dev/pts", 0755);
         mkdir("/dev/socket", 0755);
         mount("devpts", "/dev/pts", "devpts", 0, NULL);
-        mount("proc", "/proc", "proc", 0, NULL);
+        #define MAKE_STR(x) __STRING(x)
+        mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
         mount("sysfs", "/sys", "sysfs", 0, NULL);
     }
 
diff --git a/packagelistparser/Android.mk b/libpackagelistparser/Android.mk
similarity index 100%
rename from packagelistparser/Android.mk
rename to libpackagelistparser/Android.mk
diff --git a/packagelistparser/include/packagelistparser/packagelistparser.h b/libpackagelistparser/include/packagelistparser/packagelistparser.h
similarity index 100%
rename from packagelistparser/include/packagelistparser/packagelistparser.h
rename to libpackagelistparser/include/packagelistparser/packagelistparser.h
diff --git a/packagelistparser/packagelistparser.c b/libpackagelistparser/packagelistparser.c
similarity index 100%
rename from packagelistparser/packagelistparser.c
rename to libpackagelistparser/packagelistparser.c
diff --git a/libpixelflinger/codeflinger/MIPS64Assembler.h b/libpixelflinger/codeflinger/MIPS64Assembler.h
index 3da291a..b43e5da 100644
--- a/libpixelflinger/codeflinger/MIPS64Assembler.h
+++ b/libpixelflinger/codeflinger/MIPS64Assembler.h
@@ -303,32 +303,7 @@
 
 
 protected:
-    // void string_detab(char *s);
-    // void string_pad(char *s, int padded_len);
-
     ArmToMips64Assembler *mParent;
-    sp<Assembly>    mAssembly;
-    uint32_t*       mBase;
-    uint32_t*       mPC;
-    uint32_t*       mPrologPC;
-    int64_t         mDuration;
-#if defined(WITH_LIB_HARDWARE)
-    bool            mQemuTracing;
-#endif
-
-    struct branch_target_t {
-        inline branch_target_t() : label(0), pc(0) { }
-        inline branch_target_t(const char* l, uint32_t* p)
-            : label(l), pc(p) { }
-        const char* label;
-        uint32_t*   pc;
-    };
-
-    Vector<branch_target_t>                 mBranchTargets;
-    KeyedVector< const char*, uint32_t* >   mLabels;
-    KeyedVector< uint32_t*, const char* >   mLabelsInverseMapping;
-    KeyedVector< uint32_t*, const char* >   mComments;
-
 
     // opcode field of all instructions
     enum opcode_field {
diff --git a/libsync/tests/sync_test.cpp b/libsync/tests/sync_test.cpp
index 55cd687..2c409dc 100644
--- a/libsync/tests/sync_test.cpp
+++ b/libsync/tests/sync_test.cpp
@@ -50,7 +50,7 @@
     bool isValid() const {
         if (m_fdInitialized) {
             int status = fcntl(m_fd, F_GETFD, 0);
-            if (status == 0)
+            if (status >= 0)
                 return true;
             else
                 return false;
@@ -92,7 +92,7 @@
     bool isValid() const {
         if (m_fdInitialized) {
             int status = fcntl(m_fd, F_GETFD, 0);
-            if (status == 0)
+            if (status >= 0)
                 return true;
             else
                 return false;
diff --git a/lmkd/lmkd.rc b/lmkd/lmkd.rc
index 7d6cb11..3bb84ab 100644
--- a/lmkd/lmkd.rc
+++ b/lmkd/lmkd.rc
@@ -1,5 +1,6 @@
 service lmkd /system/bin/lmkd
     class core
+    group root readproc
     critical
     socket lmkd seqpacket 0660 system system
     writepid /dev/cpuset/system-background/tasks
diff --git a/logd/logd.rc b/logd/logd.rc
index ecd2f0a..10f3553 100644
--- a/logd/logd.rc
+++ b/logd/logd.rc
@@ -3,7 +3,7 @@
     socket logd stream 0666 logd logd
     socket logdr seqpacket 0666 logd logd
     socket logdw dgram 0222 logd logd
-    group root system
+    group root system readproc
     writepid /dev/cpuset/system-background/tasks
 
 service logd-reinit /system/bin/logd --reinit
diff --git a/logd/main.cpp b/logd/main.cpp
index ad577d2..8e75b37 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -106,7 +106,9 @@
         return -1;
     }
 
-    if (setgroups(0, NULL) == -1) {
+    gid_t groups[] = { AID_READPROC };
+
+    if (setgroups(sizeof(groups) / sizeof(groups[0]), groups) == -1) {
         return -1;
     }
 
diff --git a/metricsd/include/metrics/metrics_library.h b/metricsd/include/metrics/metrics_library.h
index b766194..d2e98c8 100644
--- a/metricsd/include/metrics/metrics_library.h
+++ b/metricsd/include/metrics/metrics_library.h
@@ -34,6 +34,7 @@
   virtual bool SendToUMA(const std::string& name, int sample,
                          int min, int max, int nbuckets) = 0;
   virtual bool SendEnumToUMA(const std::string& name, int sample, int max) = 0;
+  virtual bool SendBoolToUMA(const std::string& name, bool sample) = 0;
   virtual bool SendSparseToUMA(const std::string& name, int sample) = 0;
   virtual bool SendUserActionToUMA(const std::string& action) = 0;
   virtual ~MetricsLibraryInterface() {}
@@ -96,6 +97,9 @@
   // normal, while 100 is high).
   bool SendEnumToUMA(const std::string& name, int sample, int max) override;
 
+  // Specialization of SendEnumToUMA for boolean values.
+  bool SendBoolToUMA(const std::string& name, bool sample) override;
+
   // Sends sparse histogram sample to Chrome for transport to UMA.  Returns
   // true on success.
   //
diff --git a/metricsd/include/metrics/metrics_library_mock.h b/metricsd/include/metrics/metrics_library_mock.h
index 3de87a9..db56f9e 100644
--- a/metricsd/include/metrics/metrics_library_mock.h
+++ b/metricsd/include/metrics/metrics_library_mock.h
@@ -32,6 +32,7 @@
                                int min, int max, int nbuckets));
   MOCK_METHOD3(SendEnumToUMA, bool(const std::string& name, int sample,
                                    int max));
+  MOCK_METHOD2(SendBoolToUMA, bool(const std::string& name, bool sample));
   MOCK_METHOD2(SendSparseToUMA, bool(const std::string& name, int sample));
   MOCK_METHOD1(SendUserActionToUMA, bool(const std::string& action));
 
diff --git a/metricsd/metrics_library.cc b/metricsd/metrics_library.cc
index a651b76..735d39f 100644
--- a/metricsd/metrics_library.cc
+++ b/metricsd/metrics_library.cc
@@ -173,6 +173,13 @@
       uma_events_file_.value());
 }
 
+bool MetricsLibrary::SendBoolToUMA(const std::string& name, bool sample) {
+  return metrics::SerializationUtils::WriteMetricToFile(
+      *metrics::MetricSample::LinearHistogramSample(name,
+                                                    sample ? 1 : 0, 2).get(),
+      uma_events_file_.value());
+}
+
 bool MetricsLibrary::SendSparseToUMA(const std::string& name, int sample) {
   return metrics::SerializationUtils::WriteMetricToFile(
       *metrics::MetricSample::SparseHistogramSample(name, sample).get(),
diff --git a/rootdir/init.rc b/rootdir/init.rc
index b80c454..17e87da 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -556,7 +556,7 @@
     console
     disabled
     user shell
-    group shell log
+    group shell log readproc
     seclabel u:r:shell:s0
 
 on property:ro.debuggable=1