Merge "crash_reporter: Support crashes from arbitrary users"
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 1c1683e..1eb3a3c 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -245,7 +245,7 @@
 
     if (pieces.size() > 2) {
         const std::string& props = pieces[2];
-        for (auto& prop : android::base::Split(props, ";")) {
+        for (const auto& prop : android::base::Split(props, ";")) {
             // The list of properties was traditionally ;-terminated rather than ;-separated.
             if (prop.empty()) continue;
 
@@ -904,7 +904,7 @@
         }
 
         std::string error_msg;
-        atransport* transport = acquire_one_transport(kCsAny, type, serial, &error_msg);
+        atransport* transport = acquire_one_transport(type, serial, nullptr, &error_msg);
         if (!transport) {
             SendFail(reply_fd, error_msg);
             return 1;
@@ -990,13 +990,13 @@
             serial = service;
         }
 
-        std::string error_msg;
-        atransport* t = acquire_one_transport(kCsAny, type, serial, &error_msg);
+        std::string error;
+        atransport* t = acquire_one_transport(type, serial, nullptr, &error);
         if (t != nullptr) {
             s->transport = t;
             SendOkay(reply_fd);
         } else {
-            SendFail(reply_fd, error_msg);
+            SendFail(reply_fd, error);
         }
         return 1;
     }
@@ -1014,12 +1014,12 @@
     }
 
     if (!strcmp(service, "features")) {
-        std::string error_msg;
-        atransport* t = acquire_one_transport(kCsAny, type, serial, &error_msg);
+        std::string error;
+        atransport* t = acquire_one_transport(type, serial, nullptr, &error);
         if (t != nullptr) {
             SendOkay(reply_fd, FeatureSetToString(t->features()));
         } else {
-            SendFail(reply_fd, error_msg);
+            SendFail(reply_fd, error);
         }
         return 0;
     }
@@ -1049,29 +1049,41 @@
         return SendOkay(reply_fd, android::base::StringPrintf("disconnected %s", address.c_str()));
     }
 
-    // returns our value for ADB_SERVER_VERSION
+    // Returns our value for ADB_SERVER_VERSION.
     if (!strcmp(service, "version")) {
         return SendOkay(reply_fd, android::base::StringPrintf("%04x", ADB_SERVER_VERSION));
     }
 
     // These always report "unknown" rather than the actual error, for scripts.
     if (!strcmp(service, "get-serialno")) {
-        std::string ignored;
-        atransport* t = acquire_one_transport(kCsAny, type, serial, &ignored);
-        return SendOkay(reply_fd, (t && t->serial) ? t->serial : "unknown");
+        std::string error;
+        atransport* t = acquire_one_transport(type, serial, nullptr, &error);
+        if (t) {
+            return SendOkay(reply_fd, t->serial ? t->serial : "unknown");
+        } else {
+            return SendFail(reply_fd, error);
+        }
     }
     if (!strcmp(service, "get-devpath")) {
-        std::string ignored;
-        atransport* t = acquire_one_transport(kCsAny, type, serial, &ignored);
-        return SendOkay(reply_fd, (t && t->devpath) ? t->devpath : "unknown");
+        std::string error;
+        atransport* t = acquire_one_transport(type, serial, nullptr, &error);
+        if (t) {
+            return SendOkay(reply_fd, t->devpath ? t->devpath : "unknown");
+        } else {
+            return SendFail(reply_fd, error);
+        }
     }
     if (!strcmp(service, "get-state")) {
-        std::string ignored;
-        atransport* t = acquire_one_transport(kCsAny, type, serial, &ignored);
-        return SendOkay(reply_fd, t ? t->connection_state_name() : "unknown");
+        std::string error;
+        atransport* t = acquire_one_transport(type, serial, nullptr, &error);
+        if (t) {
+            return SendOkay(reply_fd, t->connection_state_name());
+        } else {
+            return SendFail(reply_fd, error);
+        }
     }
 
-    // indicates a new emulator instance has started
+    // Indicates a new emulator instance has started.
     if (!strncmp(service, "emulator:", 9)) {
         int  port = atoi(service+9);
         local_connect(port);
diff --git a/adb/adb_auth_host.cpp b/adb/adb_auth_host.cpp
index 7b314c3..5309519 100644
--- a/adb/adb_auth_host.cpp
+++ b/adb/adb_auth_host.cpp
@@ -368,7 +368,7 @@
         return;
     }
 
-    for (auto& path : android::base::Split(adb_keys_path, ENV_PATH_SEPARATOR_STR)) {
+    for (const auto& path : android::base::Split(adb_keys_path, ENV_PATH_SEPARATOR_STR)) {
         if (!read_key(path.c_str(), key_list)) {
             D("Failed to read '%s'", path.c_str());
         }
diff --git a/adb/adb_client.cpp b/adb/adb_client.cpp
index 984910d..fa8ce9e 100644
--- a/adb/adb_client.cpp
+++ b/adb/adb_client.cpp
@@ -209,9 +209,8 @@
             adb_close(fd);
 
             if (sscanf(&version_string[0], "%04x", &version) != 1) {
-                *error = android::base::StringPrintf(
-                        "cannot parse version string: %s",
-                        version_string.c_str());
+                *error = android::base::StringPrintf("cannot parse version string: %s",
+                                                     version_string.c_str());
                 return -1;
             }
         } else {
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index 5e5ca7f..0531cf9 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -1093,8 +1093,6 @@
     }
     // TODO: also try TARGET_PRODUCT/TARGET_DEVICE as a hint
 
-    const char* serial = getenv("ANDROID_SERIAL");
-
     /* Validate and assign the server port */
     const char* server_port_str = getenv("ANDROID_ADB_SERVER_PORT");
     int server_port = DEFAULT_ADB_PORT;
@@ -1108,7 +1106,9 @@
         }
     }
 
-    /* modifiers and flags */
+    // We need to check for -d and -e before we look at $ANDROID_SERIAL.
+    const char* serial = nullptr;
+
     while (argc > 0) {
         if (!strcmp(argv[0],"server")) {
             is_server = 1;
@@ -1199,6 +1199,11 @@
         argv++;
     }
 
+    // If none of -d, -e, or -s were specified, try $ANDROID_SERIAL.
+    if (transport_type == kTransportAny && serial == nullptr) {
+        serial = getenv("ANDROID_SERIAL");
+    }
+
     adb_set_transport(transport_type, serial);
     adb_set_tcp_specifics(server_port);
 
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index 18b64f6..ddd15a2 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -199,7 +199,7 @@
 
 static std::string dump_pollfds(const std::vector<pollfd>& pollfds) {
     std::string result;
-    for (auto& pollfd : pollfds) {
+    for (const auto& pollfd : pollfds) {
         std::string op;
         if (pollfd.events & POLLIN) {
             op += "R";
@@ -214,8 +214,8 @@
 
 static void fdevent_process() {
     std::vector<pollfd> pollfds;
-    for (auto it = g_poll_node_map.begin(); it != g_poll_node_map.end(); ++it) {
-        pollfds.push_back(it->second.pollfd);
+    for (const auto& pair : g_poll_node_map) {
+        pollfds.push_back(pair.second.pollfd);
     }
     CHECK_GT(pollfds.size(), 0u);
     D("poll(), pollfds = %s", dump_pollfds(pollfds).c_str());
@@ -224,7 +224,7 @@
         PLOG(ERROR) << "poll(), ret = " << ret;
         return;
     }
-    for (auto& pollfd : pollfds) {
+    for (const auto& pollfd : pollfds) {
         if (pollfd.revents != 0) {
             D("for fd %d, revents = %x", pollfd.fd, pollfd.revents);
         }
diff --git a/adb/file_sync_service.cpp b/adb/file_sync_service.cpp
index 9b56392..298ed82 100644
--- a/adb/file_sync_service.cpp
+++ b/adb/file_sync_service.cpp
@@ -56,7 +56,7 @@
     path_components.pop_back(); // For "/system/bin/sh", only create "/system/bin".
 
     std::string partial_path;
-    for (auto& path_component : path_components) {
+    for (const auto& path_component : path_components) {
         if (partial_path.back() != OS_PATH_SEPARATOR) partial_path += OS_PATH_SEPARATOR;
         partial_path += path_component;
 
diff --git a/adb/services.cpp b/adb/services.cpp
index e832b1e..e24b470 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -363,23 +363,31 @@
     ConnectionState state;
 };
 
-static void wait_for_state(int fd, void* cookie)
-{
+static void wait_for_state(int fd, void* cookie) {
     state_info* sinfo = reinterpret_cast<state_info*>(cookie);
 
     D("wait_for_state %d", sinfo->state);
 
-    std::string error_msg = "unknown error";
-    atransport* t = acquire_one_transport(sinfo->state, sinfo->transport_type, sinfo->serial,
-                                          &error_msg);
-    if (t != nullptr) {
-        SendOkay(fd);
-    } else {
-        SendFail(fd, error_msg);
+    while (true) {
+        bool is_ambiguous = false;
+        std::string error = "unknown error";
+        atransport* t = acquire_one_transport(sinfo->transport_type, sinfo->serial,
+                                              &is_ambiguous, &error);
+        if (t != nullptr && t->connection_state == sinfo->state) {
+            SendOkay(fd);
+            break;
+        } else if (!is_ambiguous) {
+            adb_sleep_ms(1000);
+            // Try again...
+        } else {
+            SendFail(fd, error);
+            break;
+        }
     }
 
-    if (sinfo->serial)
+    if (sinfo->serial) {
         free(sinfo->serial);
+    }
     free(sinfo);
     adb_close(fd);
     D("wait_for_state is done");
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index 8562496..f8c2f64 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -671,8 +671,8 @@
 {
     unsigned len;
 #if ADB_HOST
-    char *service = NULL;
-    char* serial = NULL;
+    char *service = nullptr;
+    char* serial = nullptr;
     TransportType type = kTransportAny;
 #endif
 
@@ -739,7 +739,7 @@
         type = kTransportAny;
         service += strlen("host:");
     } else {
-        service = NULL;
+        service = nullptr;
     }
 
     if (service) {
@@ -782,7 +782,7 @@
         SendOkay(s->peer->fd);
 
         s->peer->ready = local_socket_ready;
-        s->peer->shutdown = NULL;
+        s->peer->shutdown = nullptr;
         s->peer->close = local_socket_close;
         s->peer->peer = s2;
         s2->peer = s->peer;
@@ -795,12 +795,10 @@
         return 0;
     }
 #else /* !ADB_HOST */
-    if (s->transport == NULL) {
+    if (s->transport == nullptr) {
         std::string error_msg = "unknown failure";
-        s->transport =
-            acquire_one_transport(kCsAny, kTransportAny, NULL, &error_msg);
-
-        if (s->transport == NULL) {
+        s->transport = acquire_one_transport(kTransportAny, nullptr, nullptr, &error_msg);
+        if (s->transport == nullptr) {
             SendFail(s->peer->fd, error_msg);
             goto fail;
         }
@@ -822,7 +820,7 @@
         ** tear down
         */
     s->peer->ready = local_socket_ready_notify;
-    s->peer->shutdown = NULL;
+    s->peer->shutdown = nullptr;
     s->peer->close = local_socket_close_notify;
     s->peer->peer = 0;
         /* give him our transport and upref it */
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 501a39a..e9e774f 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -653,22 +653,28 @@
     return !*to_test;
 }
 
-atransport* acquire_one_transport(ConnectionState state, TransportType type,
-                                  const char* serial, std::string* error_out) {
-    atransport *result = NULL;
-    int ambiguous = 0;
+atransport* acquire_one_transport(TransportType type, const char* serial,
+                                  bool* is_ambiguous, std::string* error_out) {
+    atransport* result = nullptr;
 
-retry:
-    *error_out = serial ? android::base::StringPrintf("device '%s' not found", serial) : "no devices found";
+    if (serial) {
+        *error_out = android::base::StringPrintf("device '%s' not found", serial);
+    } else if (type == kTransportLocal) {
+        *error_out = "no emulators found";
+    } else if (type == kTransportAny) {
+        *error_out = "no devices/emulators found";
+    } else {
+        *error_out = "no devices found";
+    }
 
     adb_mutex_lock(&transport_lock);
-    for (auto t : transport_list) {
+    for (const auto& t : transport_list) {
         if (t->connection_state == kCsNoPerm) {
             *error_out = "insufficient permissions for device";
             continue;
         }
 
-        /* check for matching serial number */
+        // Check for matching serial number.
         if (serial) {
             if ((t->serial && !strcmp(serial, t->serial)) ||
                 (t->devpath && !strcmp(serial, t->devpath)) ||
@@ -677,8 +683,8 @@
                 qual_match(serial, "device:", t->device, false)) {
                 if (result) {
                     *error_out = "more than one device";
-                    ambiguous = 1;
-                    result = NULL;
+                    if (is_ambiguous) *is_ambiguous = true;
+                    result = nullptr;
                     break;
                 }
                 result = t;
@@ -687,24 +693,24 @@
             if (type == kTransportUsb && t->type == kTransportUsb) {
                 if (result) {
                     *error_out = "more than one device";
-                    ambiguous = 1;
-                    result = NULL;
+                    if (is_ambiguous) *is_ambiguous = true;
+                    result = nullptr;
                     break;
                 }
                 result = t;
             } else if (type == kTransportLocal && t->type == kTransportLocal) {
                 if (result) {
                     *error_out = "more than one emulator";
-                    ambiguous = 1;
-                    result = NULL;
+                    if (is_ambiguous) *is_ambiguous = true;
+                    result = nullptr;
                     break;
                 }
                 result = t;
             } else if (type == kTransportAny) {
                 if (result) {
                     *error_out = "more than one device/emulator";
-                    ambiguous = 1;
-                    result = NULL;
+                    if (is_ambiguous) *is_ambiguous = true;
+                    result = nullptr;
                     break;
                 }
                 result = t;
@@ -713,37 +719,26 @@
     }
     adb_mutex_unlock(&transport_lock);
 
-    if (result) {
-        if (result->connection_state == kCsUnauthorized) {
-            *error_out = "device unauthorized.\n";
-            char* ADB_VENDOR_KEYS = getenv("ADB_VENDOR_KEYS");
-            *error_out += "This adb server's $ADB_VENDOR_KEYS is ";
-            *error_out += ADB_VENDOR_KEYS ? ADB_VENDOR_KEYS : "not set";
-            *error_out += "\n";
-            *error_out += "Try 'adb kill-server' if that seems wrong.\n";
-            *error_out += "Otherwise check for a confirmation dialog on your device.";
-            result = NULL;
-        }
+    // Don't return unauthorized devices; the caller can't do anything with them.
+    if (result && result->connection_state == kCsUnauthorized) {
+        *error_out = "device unauthorized.\n";
+        char* ADB_VENDOR_KEYS = getenv("ADB_VENDOR_KEYS");
+        *error_out += "This adb server's $ADB_VENDOR_KEYS is ";
+        *error_out += ADB_VENDOR_KEYS ? ADB_VENDOR_KEYS : "not set";
+        *error_out += "\n";
+        *error_out += "Try 'adb kill-server' if that seems wrong.\n";
+        *error_out += "Otherwise check for a confirmation dialog on your device.";
+        result = nullptr;
+    }
 
-        /* offline devices are ignored -- they are either being born or dying */
-        if (result && result->connection_state == kCsOffline) {
-            *error_out = "device offline";
-            result = NULL;
-        }
-
-        /* check for required connection state */
-        if (result && state != kCsAny && result->connection_state != state) {
-            *error_out = "invalid device state";
-            result = NULL;
-        }
+    // Don't return offline devices; the caller can't do anything with them.
+    if (result && result->connection_state == kCsOffline) {
+        *error_out = "device offline";
+        result = nullptr;
     }
 
     if (result) {
-        /* found one that we can take */
         *error_out = "success";
-    } else if (state != kCsAny && (serial || !ambiguous)) {
-        adb_sleep_ms(1000);
-        goto retry;
     }
 
     return result;
@@ -830,7 +825,7 @@
 }
 
 void atransport::RunDisconnects() {
-    for (auto& disconnect : disconnects_) {
+    for (const auto& disconnect : disconnects_) {
         disconnect->func(disconnect->opaque, this);
     }
     disconnects_.clear();
@@ -877,7 +872,7 @@
 std::string list_transports(bool long_listing) {
     std::string result;
     adb_mutex_lock(&transport_lock);
-    for (const auto t : transport_list) {
+    for (const auto& t : transport_list) {
         append_transport(t, &result, long_listing);
     }
     adb_mutex_unlock(&transport_lock);
@@ -887,7 +882,7 @@
 /* hack for osx */
 void close_usb_devices() {
     adb_mutex_lock(&transport_lock);
-    for (auto t : transport_list) {
+    for (const auto& t : transport_list) {
         if (!t->kicked) {
             t->kicked = 1;
             t->kick(t);
@@ -913,7 +908,7 @@
     }
 
     adb_mutex_lock(&transport_lock);
-    for (auto transport : pending_list) {
+    for (const auto& transport : pending_list) {
         if (transport->serial && strcmp(serial, transport->serial) == 0) {
             adb_mutex_unlock(&transport_lock);
             delete t;
@@ -921,7 +916,7 @@
         }
     }
 
-    for (auto transport : transport_list) {
+    for (const auto& transport : transport_list) {
         if (transport->serial && strcmp(serial, transport->serial) == 0) {
             adb_mutex_unlock(&transport_lock);
             delete t;
diff --git a/adb/transport.h b/adb/transport.h
index dfe8875..f41a8d4 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -122,12 +122,13 @@
 
 /*
  * Obtain a transport from the available transports.
- * If state is != kCsAny, only transports in that state are considered.
- * If serial is non-NULL then only the device with that serial will be chosen.
- * If no suitable transport is found, error is set.
+ * If serial is non-null then only the device with that serial will be chosen.
+ * If multiple devices/emulators would match, *is_ambiguous (if non-null)
+ * is set to true and nullptr returned.
+ * If no suitable transport is found, error is set and nullptr returned.
  */
-atransport* acquire_one_transport(ConnectionState state, TransportType type,
-                                  const char* serial, std::string* error_out);
+atransport* acquire_one_transport(TransportType type, const char* serial,
+                                  bool* is_ambiguous, std::string* error_out);
 void kick_transport(atransport* t);
 void update_transports(void);
 
diff --git a/libcutils/fs_config.c b/libcutils/fs_config.c
index 681c57f..970b386 100644
--- a/libcutils/fs_config.c
+++ b/libcutils/fs_config.c
@@ -126,11 +126,10 @@
     { 00644, AID_SYSTEM,    AID_SYSTEM,    0, "data/app-private/*" },
     { 00644, AID_APP,       AID_APP,       0, "data/data/*" },
 
-    /* the following five files are INTENTIONALLY set-uid, but they
+    /* the following four files are INTENTIONALLY set-uid, but they
      * are NOT included on user builds. */
     { 04750, AID_ROOT,      AID_SHELL,     0, "system/xbin/su" },
     { 06755, AID_ROOT,      AID_ROOT,      0, "system/xbin/librank" },
-    { 06755, AID_ROOT,      AID_ROOT,      0, "system/xbin/procrank" },
     { 06755, AID_ROOT,      AID_ROOT,      0, "system/xbin/procmem" },
     { 04770, AID_ROOT,      AID_RADIO,     0, "system/bin/pppd-ril" },
 
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 9440b68..ddbfb3e 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -656,7 +656,7 @@
             break;
 
             case 'f':
-                if ((tail_time == log_time::EPOCH) && (tail_lines != 0)) {
+                if ((tail_time == log_time::EPOCH) && (tail_lines == 0)) {
                     tail_time = lastLogTime(optarg);
                 }
                 // redirect output to a file
@@ -832,49 +832,66 @@
     } else {
         logger_list = android_logger_list_alloc(mode, tail_lines, 0);
     }
+    const char *openDeviceFail = NULL;
+    const char *clearFail = NULL;
+    const char *setSizeFail = NULL;
+    const char *getSizeFail = NULL;
+    // We have three orthogonal actions below to clear, set log size and
+    // get log size. All sharing the same iteration loop.
     while (dev) {
         dev->logger_list = logger_list;
         dev->logger = android_logger_open(logger_list,
                                           android_name_to_log_id(dev->device));
         if (!dev->logger) {
-            logcat_panic(false, "Unable to open log device '%s'\n",
-                         dev->device);
+            openDeviceFail = openDeviceFail ?: dev->device;
+            dev = dev->next;
+            continue;
         }
 
         if (clearLog) {
-            int ret;
-            ret = android_logger_clear(dev->logger);
-            if (ret) {
-                logcat_panic(false, "failed to clear the log");
+            if (android_logger_clear(dev->logger)) {
+                clearFail = clearFail ?: dev->device;
             }
         }
 
-        if (setLogSize && android_logger_set_log_size(dev->logger, setLogSize)) {
-            logcat_panic(false, "failed to set the log size");
+        if (setLogSize) {
+            if (android_logger_set_log_size(dev->logger, setLogSize)) {
+                setSizeFail = setSizeFail ?: dev->device;
+            }
         }
 
         if (getLogSize) {
-            long size, readable;
+            long size = android_logger_get_log_size(dev->logger);
+            long readable = android_logger_get_log_readable_size(dev->logger);
 
-            size = android_logger_get_log_size(dev->logger);
-            if (size < 0) {
-                logcat_panic(false, "failed to get the log size");
+            if ((size < 0) || (readable < 0)) {
+                getSizeFail = getSizeFail ?: dev->device;
+            } else {
+                printf("%s: ring buffer is %ld%sb (%ld%sb consumed), "
+                       "max entry is %db, max payload is %db\n", dev->device,
+                       value_of_size(size), multiplier_of_size(size),
+                       value_of_size(readable), multiplier_of_size(readable),
+                       (int) LOGGER_ENTRY_MAX_LEN,
+                       (int) LOGGER_ENTRY_MAX_PAYLOAD);
             }
-
-            readable = android_logger_get_log_readable_size(dev->logger);
-            if (readable < 0) {
-                logcat_panic(false, "failed to get the readable log size");
-            }
-
-            printf("%s: ring buffer is %ld%sb (%ld%sb consumed), "
-                   "max entry is %db, max payload is %db\n", dev->device,
-                   value_of_size(size), multiplier_of_size(size),
-                   value_of_size(readable), multiplier_of_size(readable),
-                   (int) LOGGER_ENTRY_MAX_LEN, (int) LOGGER_ENTRY_MAX_PAYLOAD);
         }
 
         dev = dev->next;
     }
+    // report any errors in the above loop and exit
+    if (openDeviceFail) {
+        logcat_panic(false, "Unable to open log device '%s'\n", openDeviceFail);
+    }
+    if (clearFail) {
+        logcat_panic(false, "failed to clear the '%s' log\n", clearFail);
+    }
+    if (setSizeFail) {
+        logcat_panic(false, "failed to set the '%s' log size\n", setSizeFail);
+    }
+    if (getSizeFail) {
+        logcat_panic(false, "failed to get the readable '%s' log size",
+                     getSizeFail);
+    }
 
     if (setPruneList) {
         size_t len = strlen(setPruneList);
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index bcf8d82..610a6ec 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -15,10 +15,12 @@
  */
 
 #include <ctype.h>
+#include <dirent.h>
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/types.h>
 
 #include <gtest/gtest.h>
 #include <log/log.h>
@@ -368,7 +370,7 @@
 
     while (fgets(buffer, sizeof(buffer), fp)) {
         int size, consumed, max, payload;
-        char size_mult[2], consumed_mult[2];
+        char size_mult[3], consumed_mult[3];
         long full_size, full_consumed;
 
         size = consumed = max = payload = 0;
@@ -573,12 +575,12 @@
     static const char comm[] = "logcat -b radio -b events -b system -b main"
                                      " -d -f %s/log.txt -n 7 -r 1";
     char command[sizeof(buf) + sizeof(comm)];
-    sprintf(command, comm, buf);
+    snprintf(command, sizeof(command), comm, buf);
 
     int ret;
     EXPECT_FALSE((ret = system(command)));
     if (!ret) {
-        sprintf(command, "ls -s %s 2>/dev/null", buf);
+        snprintf(command, sizeof(command), "ls -s %s 2>/dev/null", buf);
 
         FILE *fp;
         EXPECT_TRUE(NULL != (fp = popen(command, "r")));
@@ -587,16 +589,12 @@
             int count = 0;
 
             while (fgets(buffer, sizeof(buffer), fp)) {
-                static const char match_1[] = "4 log.txt";
-                static const char match_2[] = "8 log.txt";
-                static const char match_3[] = "12 log.txt";
-                static const char match_4[] = "16 log.txt";
                 static const char total[] = "total ";
+                int num;
+                char c;
 
-                if (!strncmp(buffer, match_1, sizeof(match_1) - 1)
-                 || !strncmp(buffer, match_2, sizeof(match_2) - 1)
-                 || !strncmp(buffer, match_3, sizeof(match_3) - 1)
-                 || !strncmp(buffer, match_4, sizeof(match_4) - 1)) {
+                if ((2 == sscanf(buffer, "%d log.tx%c", &num, &c)) &&
+                        (num <= 24)) {
                     ++count;
                 } else if (strncmp(buffer, total, sizeof(total) - 1)) {
                     fprintf(stderr, "WARNING: Parse error: %s", buffer);
@@ -606,7 +604,7 @@
             EXPECT_TRUE(count == 7 || count == 8);
         }
     }
-    sprintf(command, "rm -rf %s", buf);
+    snprintf(command, sizeof(command), "rm -rf %s", buf);
     EXPECT_FALSE(system(command));
 }
 
@@ -618,12 +616,12 @@
     static const char logcat_cmd[] = "logcat -b radio -b events -b system -b main"
                                      " -d -f %s/log.txt -n 10 -r 1";
     char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd)];
-    sprintf(command, logcat_cmd, tmp_out_dir);
+    snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir);
 
     int ret;
     EXPECT_FALSE((ret = system(command)));
     if (!ret) {
-        sprintf(command, "ls %s 2>/dev/null", tmp_out_dir);
+        snprintf(command, sizeof(command), "ls %s 2>/dev/null", tmp_out_dir);
 
         FILE *fp;
         EXPECT_TRUE(NULL != (fp = popen(command, "r")));
@@ -659,7 +657,113 @@
         pclose(fp);
         EXPECT_EQ(11, log_file_count);
     }
-    sprintf(command, "rm -rf %s", tmp_out_dir);
+    snprintf(command, sizeof(command), "rm -rf %s", tmp_out_dir);
+    EXPECT_FALSE(system(command));
+}
+
+TEST(logcat, logrotate_continue) {
+    static const char tmp_out_dir_form[] = "/data/local/tmp/logcat.logrotate.XXXXXX";
+    char tmp_out_dir[sizeof(tmp_out_dir_form)];
+    ASSERT_TRUE(NULL != mkdtemp(strcpy(tmp_out_dir, tmp_out_dir_form)));
+
+    static const char log_filename[] = "log.txt";
+    static const char logcat_cmd[] = "logcat -b all -d -f %s/%s -n 256 -r 1024";
+    static const char cleanup_cmd[] = "rm -rf %s";
+    char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd) + sizeof(log_filename)];
+    snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename);
+
+    int ret;
+    EXPECT_FALSE((ret = system(command)));
+    if (ret) {
+        snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+        EXPECT_FALSE(system(command));
+        return;
+    }
+    FILE *fp;
+    snprintf(command, sizeof(command), "%s/%s", tmp_out_dir, log_filename);
+    EXPECT_TRUE(NULL != ((fp = fopen(command, "r"))));
+    if (!fp) {
+        snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+        EXPECT_FALSE(system(command));
+        return;
+    }
+    char *line = NULL;
+    char *last_line = NULL; // this line is allowed to stutter, one-line overlap
+    char *second_last_line = NULL;
+    size_t len = 0;
+    while (getline(&line, &len, fp) != -1) {
+        free(second_last_line);
+        second_last_line = last_line;
+        last_line = line;
+        line = NULL;
+    }
+    fclose(fp);
+    free(line);
+    if (second_last_line == NULL) {
+        fprintf(stderr, "No second to last line, using last, test may fail\n");
+        second_last_line = last_line;
+        last_line = NULL;
+    }
+    free(last_line);
+    EXPECT_TRUE(NULL != second_last_line);
+    if (!second_last_line) {
+        snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+        EXPECT_FALSE(system(command));
+        return;
+    }
+    // re-run the command, it should only add a few lines more content if it
+    // continues where it left off.
+    snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename);
+    EXPECT_FALSE((ret = system(command)));
+    if (ret) {
+        snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+        EXPECT_FALSE(system(command));
+        return;
+    }
+    DIR *dir;
+    EXPECT_TRUE(NULL != (dir = opendir(tmp_out_dir)));
+    if (!dir) {
+        snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+        EXPECT_FALSE(system(command));
+        return;
+    }
+    struct dirent *entry;
+    unsigned count = 0;
+    while ((entry = readdir(dir))) {
+        if (strncmp(entry->d_name, log_filename, sizeof(log_filename) - 1)) {
+            continue;
+        }
+        snprintf(command, sizeof(command), "%s/%s", tmp_out_dir, entry->d_name);
+        EXPECT_TRUE(NULL != ((fp = fopen(command, "r"))));
+        if (!fp) {
+            fprintf(stderr, "%s ?\n", command);
+            continue;
+        }
+        line = NULL;
+        size_t number = 0;
+        while (getline(&line, &len, fp) != -1) {
+            ++number;
+            if (!strcmp(line, second_last_line)) {
+                EXPECT_TRUE(++count <= 1);
+                fprintf(stderr, "%s(%zu):\n", entry->d_name, number);
+            }
+        }
+        fclose(fp);
+        free(line);
+        unlink(command);
+    }
+    closedir(dir);
+    if (count > 1) {
+        char *brk = strpbrk(second_last_line, "\r\n");
+        if (!brk) {
+            brk = second_last_line + strlen(second_last_line);
+        }
+        fprintf(stderr, "\"%.*s\" occured %u times\n",
+            (int)(brk - second_last_line), second_last_line, count);
+    }
+    free(second_last_line);
+
+    snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
     EXPECT_FALSE(system(command));
 }
 
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp
index 031c740..eafa28f 100644
--- a/logd/CommandListener.cpp
+++ b/logd/CommandListener.cpp
@@ -96,8 +96,7 @@
         return 0;
     }
 
-    mBuf.clear((log_id_t) id, uid);
-    cli->sendMsg("success");
+    cli->sendMsg(mBuf.clear((log_id_t) id, uid) ? "busy" : "success");
     return 0;
 }
 
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index c2f846e..5cfc02f 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -374,8 +374,10 @@
 //
 // mLogElementsLock must be held when this function is called.
 //
-void LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
+bool LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
     LogTimeEntry *oldest = NULL;
+    bool busy = false;
+    bool clearAll = pruneRows == ULONG_MAX;
 
     LogTimeEntry::lock();
 
@@ -393,35 +395,31 @@
     LogBufferElementCollection::iterator it;
 
     if (caller_uid != AID_ROOT) {
+        // Only here if clearAll condition (pruneRows == ULONG_MAX)
         for(it = mLogElements.begin(); it != mLogElements.end();) {
             LogBufferElement *e = *it;
 
-            if (oldest && (oldest->mStart <= e->getSequence())) {
-                break;
-            }
-
-            if (e->getLogId() != id) {
+            if ((e->getLogId() != id) || (e->getUid() != caller_uid)) {
                 ++it;
                 continue;
             }
 
-            if (e->getUid() == caller_uid) {
-                it = erase(it);
-                pruneRows--;
-                if (pruneRows == 0) {
-                    break;
-                }
-            } else {
-                ++it;
+            if (oldest && (oldest->mStart <= e->getSequence())) {
+                oldest->triggerSkip_Locked(id, pruneRows);
+                busy = true;
+                break;
             }
+
+            it = erase(it);
+            pruneRows--;
         }
         LogTimeEntry::unlock();
-        return;
+        return busy;
     }
 
     // prune by worst offender by uid
     bool hasBlacklist = mPrune.naughty();
-    while (pruneRows > 0) {
+    while (!clearAll && (pruneRows > 0)) {
         // recalculate the worst offender on every batched pass
         uid_t worst = (uid_t) -1;
         size_t worst_sizes = 0;
@@ -478,6 +476,7 @@
             LogBufferElement *e = *it;
 
             if (oldest && (oldest->mStart <= e->getSequence())) {
+                busy = true;
                 break;
             }
 
@@ -585,7 +584,7 @@
     }
 
     bool whitelist = false;
-    bool hasWhitelist = mPrune.nice();
+    bool hasWhitelist = mPrune.nice() && !clearAll;
     it = mLogElements.begin();
     while((pruneRows > 0) && (it != mLogElements.end())) {
         LogBufferElement *e = *it;
@@ -596,6 +595,8 @@
         }
 
         if (oldest && (oldest->mStart <= e->getSequence())) {
+            busy = true;
+
             if (whitelist) {
                 break;
             }
@@ -631,6 +632,7 @@
             }
 
             if (oldest && (oldest->mStart <= e->getSequence())) {
+                busy = true;
                 if (stats.sizes(id) > (2 * log_buffer_size(id))) {
                     // kick a misbehaving log reader client off the island
                     oldest->release_Locked();
@@ -646,13 +648,50 @@
     }
 
     LogTimeEntry::unlock();
+
+    return (pruneRows > 0) && busy;
 }
 
 // clear all rows of type "id" from the buffer.
-void LogBuffer::clear(log_id_t id, uid_t uid) {
-    pthread_mutex_lock(&mLogElementsLock);
-    prune(id, ULONG_MAX, uid);
-    pthread_mutex_unlock(&mLogElementsLock);
+bool LogBuffer::clear(log_id_t id, uid_t uid) {
+    bool busy = true;
+    // If it takes more than 4 tries (seconds) to clear, then kill reader(s)
+    for (int retry = 4;;) {
+        if (retry == 1) { // last pass
+            // Check if it is still busy after the sleep, we say prune
+            // 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);
+            busy = prune(id, 1, uid);
+            pthread_mutex_unlock(&mLogElementsLock);
+            // 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();
+                LastLogTimes::iterator times = mTimes.begin();
+                while (times != mTimes.end()) {
+                    LogTimeEntry *entry = (*times);
+                    // Killer punch
+                    if (entry->owned_Locked() && entry->isWatching(id)) {
+                        entry->release_Locked();
+                    }
+                    times++;
+                }
+                LogTimeEntry::unlock();
+            }
+        }
+        pthread_mutex_lock(&mLogElementsLock);
+        busy = prune(id, ULONG_MAX, uid);
+        pthread_mutex_unlock(&mLogElementsLock);
+        if (!busy || !--retry) {
+            break;
+        }
+        sleep (1); // Let reader(s) catch up after notification
+    }
+    return busy;
 }
 
 // get the used space associated with "id".
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index de76693..d8e0b90 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -63,7 +63,7 @@
                      int (*filter)(const LogBufferElement *element, void *arg) = NULL,
                      void *arg = NULL);
 
-    void clear(log_id_t id, uid_t uid = AID_ROOT);
+    bool clear(log_id_t id, uid_t uid = AID_ROOT);
     unsigned long getSize(log_id_t id);
     int setSize(log_id_t id, unsigned long size);
     unsigned long getSizeUsed(log_id_t id);
@@ -86,7 +86,7 @@
 
 private:
     void maybePrune(log_id_t id);
-    void prune(log_id_t id, unsigned long pruneRows, uid_t uid = AID_ROOT);
+    bool prune(log_id_t id, unsigned long pruneRows, uid_t uid = AID_ROOT);
     LogBufferElementCollection::iterator erase(
         LogBufferElementCollection::iterator it, bool engageStats = true);
 };
diff --git a/logd/LogCommand.cpp b/logd/LogCommand.cpp
index 06d865c..6d0e92e 100644
--- a/logd/LogCommand.cpp
+++ b/logd/LogCommand.cpp
@@ -42,7 +42,6 @@
 static bool groupIsLog(char *buf) {
     char *ptr;
     static const char ws[] = " \n";
-    bool ret = false;
 
     for (buf = strtok_r(buf, ws, &ptr); buf; buf = strtok_r(NULL, ws, &ptr)) {
         errno = 0;
@@ -51,10 +50,10 @@
             return false;
         }
         if (Gid == AID_LOG) {
-            ret = true;
+            return true;
         }
     }
-    return ret;
+    return false;
 }
 
 bool clientHasLogCredentials(SocketClient * cli) {
@@ -69,61 +68,79 @@
     }
 
     // FYI We will typically be here for 'adb logcat'
-    bool ret = false;
+    char filename[256];
+    snprintf(filename, sizeof(filename), "/proc/%u/status", cli->getPid());
 
-    char filename[1024];
-    snprintf(filename, sizeof(filename), "/proc/%d/status", cli->getPid());
-
-    FILE *file = fopen(filename, "r");
-    if (!file) {
-        return ret;
-    }
-
+    bool ret;
+    bool foundLog = false;
     bool foundGid = false;
     bool foundUid = false;
 
-    char line[1024];
-    while (fgets(line, sizeof(line), file)) {
-        static const char groups_string[] = "Groups:\t";
-        static const char uid_string[] = "Uid:\t";
-        static const char gid_string[] = "Gid:\t";
-
-        if (strncmp(groups_string, line, strlen(groups_string)) == 0) {
-            ret = groupIsLog(line + strlen(groups_string));
-            if (!ret) {
-                break;
-            }
-        } else if (strncmp(uid_string, line, strlen(uid_string)) == 0) {
-            uid_t u[4] = { (uid_t) -1, (uid_t) -1, (uid_t) -1, (uid_t) -1};
-
-            sscanf(line + strlen(uid_string), "%u\t%u\t%u\t%u",
-                   &u[0], &u[1], &u[2], &u[3]);
-
-            // Protect against PID reuse by checking that the UID is the same
-            if ((uid != u[0]) || (uid != u[1]) || (uid != u[2]) || (uid != u[3])) {
-                ret = false;
-                break;
-            }
-            foundUid = true;
-        } else if (strncmp(gid_string, line, strlen(gid_string)) == 0) {
-            gid_t g[4] = { (gid_t) -1, (gid_t) -1, (gid_t) -1, (gid_t) -1};
-
-            sscanf(line + strlen(gid_string), "%u\t%u\t%u\t%u",
-                   &g[0], &g[1], &g[2], &g[3]);
-
-            // Protect against PID reuse by checking that the GID is the same
-            if ((gid != g[0]) || (gid != g[1]) || (gid != g[2]) || (gid != g[3])) {
-                ret = false;
-                break;
-            }
-            foundGid = true;
+    //
+    // Reading /proc/<pid>/status is rife with race conditions. All of /proc
+    // suffers from this and its use should be minimized. However, we have no
+    // choice.
+    //
+    // Notably the content from one 4KB page to the next 4KB page can be from a
+    // change in shape even if we are gracious enough to attempt to read
+    // atomically. getline can not even guarantee a page read is not split up
+    // and in effect can read from different vintages of the content.
+    //
+    // We are finding out in the field that a 'logcat -c' via adb occasionally
+    // is returned with permission denied when we did only one pass and thus
+    // breaking scripts. For security we still err on denying access if in
+    // doubt, but we expect the falses  should be reduced significantly as
+    // three times is a charm.
+    //
+    for (int retry = 3;
+            !(ret = foundGid && foundUid && foundLog) && retry;
+            --retry) {
+        FILE *file = fopen(filename, "r");
+        if (!file) {
+            continue;
         }
-    }
 
-    fclose(file);
+        char *line = NULL;
+        size_t len = 0;
+        while (getline(&line, &len, file) > 0) {
+            static const char groups_string[] = "Groups:\t";
+            static const char uid_string[] = "Uid:\t";
+            static const char gid_string[] = "Gid:\t";
 
-    if (!foundGid || !foundUid) {
-        ret = false;
+            if (strncmp(groups_string, line, sizeof(groups_string) - 1) == 0) {
+                if (groupIsLog(line + sizeof(groups_string) - 1)) {
+                    foundLog = true;
+                }
+            } else if (strncmp(uid_string, line, sizeof(uid_string) - 1) == 0) {
+                uid_t u[4] = { (uid_t) -1, (uid_t) -1, (uid_t) -1, (uid_t) -1};
+
+                sscanf(line + sizeof(uid_string) - 1, "%u\t%u\t%u\t%u",
+                       &u[0], &u[1], &u[2], &u[3]);
+
+                // Protect against PID reuse by checking that UID is the same
+                if ((uid == u[0])
+                        && (uid == u[1])
+                        && (uid == u[2])
+                        && (uid == u[3])) {
+                    foundUid = true;
+                }
+            } else if (strncmp(gid_string, line, sizeof(gid_string) - 1) == 0) {
+                gid_t g[4] = { (gid_t) -1, (gid_t) -1, (gid_t) -1, (gid_t) -1};
+
+                sscanf(line + sizeof(gid_string) - 1, "%u\t%u\t%u\t%u",
+                       &g[0], &g[1], &g[2], &g[3]);
+
+                // Protect against PID reuse by checking that GID is the same
+                if ((gid == g[0])
+                        && (gid == g[1])
+                        && (gid == g[2])
+                        && (gid == g[3])) {
+                    foundGid = true;
+                }
+            }
+        }
+        free(line);
+        fclose(file);
     }
 
     return ret;
diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp
index 68a0680..229be3c 100644
--- a/logd/LogTimes.cpp
+++ b/logd/LogTimes.cpp
@@ -128,9 +128,9 @@
 
     lock();
 
-    while (me->threadRunning && !me->isError_Locked()) {
-        uint64_t start = me->mStart;
+    uint64_t start = me->mStart;
 
+    while (me->threadRunning && !me->isError_Locked()) {
         unlock();
 
         if (me->mTail) {
@@ -143,8 +143,11 @@
 
         if (start == LogBufferElement::FLUSH_ERROR) {
             me->error_Locked();
+            break;
         }
 
+        me->mStart = start + 1;
+
         if (me->mNonBlock || !me->threadRunning || me->isError_Locked()) {
             break;
         }
diff --git a/metricsd/uploader/proto/system_profile.proto b/metricsd/uploader/proto/system_profile.proto
index 4cab0d9..bac828b 100644
--- a/metricsd/uploader/proto/system_profile.proto
+++ b/metricsd/uploader/proto/system_profile.proto
@@ -88,7 +88,7 @@
   optional string application_locale = 4;
 
   message BrilloDeviceData {
-    optional string build_target_id = 1;
+    optional string product_id = 1;
   }
   optional BrilloDeviceData brillo = 21;
 
diff --git a/metricsd/uploader/system_profile_cache.cc b/metricsd/uploader/system_profile_cache.cc
index 2437b56..1d87be5 100644
--- a/metricsd/uploader/system_profile_cache.cc
+++ b/metricsd/uploader/system_profile_cache.cc
@@ -136,7 +136,7 @@
   profile_proto->set_channel(profile_.channel);
   metrics::SystemProfileProto_BrilloDeviceData* device_data =
       profile_proto->mutable_brillo();
-  device_data->set_build_target_id(profile_.product_id);
+  device_data->set_product_id(profile_.product_id);
 
   return true;
 }
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 10635d3..bd4d156 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -296,6 +296,7 @@
     mkdir /data/local 0751 root root
     mkdir /data/misc/media 0700 media media
     mkdir /data/misc/boottrace 0771 system shell
+    mkdir /data/misc/update_engine 0700 root root
 
     # For security reasons, /data/local/tmp should always be empty.
     # Do not place files or directories in /data/local/tmp