Merge "Address const issue in preparation for libcxx rebase."
diff --git a/base/include/android-base/errors.h b/base/include/android-base/errors.h
index ca621fa..04c299c 100644
--- a/base/include/android-base/errors.h
+++ b/base/include/android-base/errors.h
@@ -27,8 +27,8 @@
 // special handling to get the error string. Refer to Microsoft documentation
 // to determine which error code to check for each function.
 
-#ifndef BASE_ERRORS_H
-#define BASE_ERRORS_H
+#ifndef ANDROID_BASE_ERRORS_H
+#define ANDROID_BASE_ERRORS_H
 
 #include <string>
 
@@ -43,4 +43,4 @@
 }  // namespace base
 }  // namespace android
 
-#endif  // BASE_ERRORS_H
+#endif  // ANDROID_BASE_ERRORS_H
diff --git a/base/include/android-base/file.h b/base/include/android-base/file.h
index 5342d98..aa18ea7 100644
--- a/base/include/android-base/file.h
+++ b/base/include/android-base/file.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef BASE_FILE_H
-#define BASE_FILE_H
+#ifndef ANDROID_BASE_FILE_H
+#define ANDROID_BASE_FILE_H
 
 #include <sys/stat.h>
 #include <string>
@@ -46,4 +46,4 @@
 }  // namespace base
 }  // namespace android
 
-#endif  // BASE_FILE_H
+#endif // ANDROID_BASE_FILE_H
diff --git a/base/include/android-base/logging.h b/base/include/android-base/logging.h
index 41fe4b3..b86c232 100644
--- a/base/include/android-base/logging.h
+++ b/base/include/android-base/logging.h
@@ -13,8 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#ifndef BASE_LOGGING_H
-#define BASE_LOGGING_H
+
+#ifndef ANDROID_BASE_LOGGING_H
+#define ANDROID_BASE_LOGGING_H
 
 // NOTE: For Windows, you must include logging.h after windows.h to allow the
 // following code to suppress the evil ERROR macro:
@@ -346,4 +347,4 @@
 }  // namespace base
 }  // namespace android
 
-#endif  // BASE_LOGGING_H
+#endif  // ANDROID_BASE_LOGGING_H
diff --git a/base/include/android-base/macros.h b/base/include/android-base/macros.h
index b1ce7c6..913a9a0 100644
--- a/base/include/android-base/macros.h
+++ b/base/include/android-base/macros.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef UTILS_MACROS_H
-#define UTILS_MACROS_H
+#ifndef ANDROID_BASE_MACROS_H
+#define ANDROID_BASE_MACROS_H
 
 #include <stddef.h>  // for size_t
 #include <unistd.h>  // for TEMP_FAILURE_RETRY
@@ -185,4 +185,4 @@
   } while (0)
 #endif
 
-#endif  // UTILS_MACROS_H
+#endif  // ANDROID_BASE_MACROS_H
diff --git a/base/include/android-base/memory.h b/base/include/android-base/memory.h
index 882582f..3a2f8fa 100644
--- a/base/include/android-base/memory.h
+++ b/base/include/android-base/memory.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef BASE_MEMORY_H
-#define BASE_MEMORY_H
+#ifndef ANDROID_BASE_MEMORY_H
+#define ANDROID_BASE_MEMORY_H
 
 namespace android {
 namespace base {
@@ -44,4 +44,4 @@
 } // namespace base
 } // namespace android
 
-#endif // BASE_MEMORY_H
+#endif  // ANDROID_BASE_MEMORY_H
diff --git a/base/include/android-base/parseint.h b/base/include/android-base/parseint.h
index 0543795..ed75e2d 100644
--- a/base/include/android-base/parseint.h
+++ b/base/include/android-base/parseint.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef BASE_PARSEINT_H
-#define BASE_PARSEINT_H
+#ifndef ANDROID_BASE_PARSEINT_H
+#define ANDROID_BASE_PARSEINT_H
 
 #include <errno.h>
 #include <stdlib.h>
@@ -70,4 +70,4 @@
 }  // namespace base
 }  // namespace android
 
-#endif  // BASE_PARSEINT_H
+#endif  // ANDROID_BASE_PARSEINT_H
diff --git a/base/include/android-base/parsenetaddress.h b/base/include/android-base/parsenetaddress.h
index 2de5ac9..b4ac025 100644
--- a/base/include/android-base/parsenetaddress.h
+++ b/base/include/android-base/parsenetaddress.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef BASE_PARSENETADDRESS_H
-#define BASE_PARSENETADDRESS_H
+#ifndef ANDROID_BASE_PARSENETADDRESS_H
+#define ANDROID_BASE_PARSENETADDRESS_H
 
 #include <string>
 
@@ -35,4 +35,4 @@
 }  // namespace base
 }  // namespace android
 
-#endif  // BASE_PARSENETADDRESS_H
+#endif  // ANDROID_BASE_PARSENETADDRESS_H
diff --git a/base/include/android-base/stringprintf.h b/base/include/android-base/stringprintf.h
index d68af87..cf666ab 100644
--- a/base/include/android-base/stringprintf.h
+++ b/base/include/android-base/stringprintf.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef BASE_STRINGPRINTF_H
-#define BASE_STRINGPRINTF_H
+#ifndef ANDROID_BASE_STRINGPRINTF_H
+#define ANDROID_BASE_STRINGPRINTF_H
 
 #include <stdarg.h>
 #include <string>
@@ -53,4 +53,4 @@
 }  // namespace base
 }  // namespace android
 
-#endif  // BASE_STRINGPRINTF_H
+#endif  // ANDROID_BASE_STRINGPRINTF_H
diff --git a/base/include/android-base/strings.h b/base/include/android-base/strings.h
index 20da144..69781cd 100644
--- a/base/include/android-base/strings.h
+++ b/base/include/android-base/strings.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef BASE_STRINGS_H
-#define BASE_STRINGS_H
+#ifndef ANDROID_BASE_STRINGS_H
+#define ANDROID_BASE_STRINGS_H
 
 #include <sstream>
 #include <string>
@@ -65,4 +65,4 @@
 }  // namespace base
 }  // namespace android
 
-#endif  // BASE_STRINGS_H
+#endif  // ANDROID_BASE_STRINGS_H
diff --git a/base/include/android-base/test_utils.h b/base/include/android-base/test_utils.h
index 3f6872c..4ea3c8e 100644
--- a/base/include/android-base/test_utils.h
+++ b/base/include/android-base/test_utils.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef TEST_UTILS_H
-#define TEST_UTILS_H
+#ifndef ANDROID_BASE_TEST_UTILS_H
+#define ANDROID_BASE_TEST_UTILS_H
 
 #include <string>
 
@@ -48,4 +48,4 @@
   DISALLOW_COPY_AND_ASSIGN(TemporaryDir);
 };
 
-#endif // TEST_UTILS_H
+#endif  // ANDROID_BASE_TEST_UTILS_H
diff --git a/base/include/android-base/thread_annotations.h b/base/include/android-base/thread_annotations.h
index 90979df..2422102 100644
--- a/base/include/android-base/thread_annotations.h
+++ b/base/include/android-base/thread_annotations.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef UTILS_THREAD_ANNOTATIONS_H
-#define UTILS_THREAD_ANNOTATIONS_H
+#ifndef ANDROID_BASE_THREAD_ANNOTATIONS_H
+#define ANDROID_BASE_THREAD_ANNOTATIONS_H
 
 #if defined(__SUPPORT_TS_ANNOTATION__) || defined(__clang__)
 #define THREAD_ANNOTATION_ATTRIBUTE__(x)   __attribute__((x))
@@ -80,4 +80,4 @@
 #define NO_THREAD_SAFETY_ANALYSIS \
       THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
 
-#endif  // UTILS_THREAD_ANNOTATIONS_H
+#endif  // ANDROID_BASE_THREAD_ANNOTATIONS_H
diff --git a/base/include/android-base/unique_fd.h b/base/include/android-base/unique_fd.h
index d3b27ca..8bc49ce 100644
--- a/base/include/android-base/unique_fd.h
+++ b/base/include/android-base/unique_fd.h
@@ -21,18 +21,18 @@
 
 #include <android-base/macros.h>
 
-/* Container for a file descriptor that automatically closes the descriptor as
- * it goes out of scope.
- *
- *      unique_fd ufd(open("/some/path", "r"));
- *
- *      if (ufd.get() < 0) // invalid descriptor
- *          return error;
- *
- *      // Do something useful
- *
- *      return 0; // descriptor is closed here
- */
+// Container for a file descriptor that automatically closes the descriptor as
+// it goes out of scope.
+//
+//      unique_fd ufd(open("/some/path", "r"));
+//      if (ufd.get() == -1) return error;
+//
+//      // Do something useful, possibly including 'return'.
+//
+//      return 0; // Descriptor is closed for you.
+//
+// unique_fd is also known as ScopedFd/ScopedFD/scoped_fd; mentioned here to help
+// you find this class if you're searching for one of those names.
 namespace android {
 namespace base {
 
@@ -44,14 +44,18 @@
   ~unique_fd() { clear(); }
 
   unique_fd(unique_fd&& other) : value_(other.release()) {}
-  unique_fd& operator = (unique_fd&& s) {
+  unique_fd& operator=(unique_fd&& s) {
     reset(s.release());
     return *this;
   }
 
   void reset(int new_value) {
-    if (value_ >= 0)
+    if (value_ != -1) {
+      // Even if close(2) fails with EINTR, the fd will have been closed.
+      // Using TEMP_FAILURE_RETRY will either lead to EBADF or closing someone else's fd.
+      // http://lkml.indiana.edu/hypermail/linux/kernel/0509.1/0877.html
       close(value_);
+    }
     value_ = new_value;
   }
 
@@ -60,8 +64,9 @@
   }
 
   int get() const { return value_; }
+  operator int() const { return get(); }
 
-  int release() {
+  int release() __attribute__((warn_unused_result)) {
     int ret = value_;
     value_ = -1;
     return ret;
@@ -76,4 +81,4 @@
 }  // namespace base
 }  // namespace android
 
-#endif // ANDROID_BASE_UNIQUE_FD_H
+#endif  // ANDROID_BASE_UNIQUE_FD_H
diff --git a/base/include/android-base/utf8.h b/base/include/android-base/utf8.h
index 3b0ed0a..2d5a6f6 100755
--- a/base/include/android-base/utf8.h
+++ b/base/include/android-base/utf8.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef BASE_UTF8_H
-#define BASE_UTF8_H
+#ifndef ANDROID_BASE_UTF8_H
+#define ANDROID_BASE_UTF8_H
 
 #ifdef _WIN32
 #include <string>
@@ -84,4 +84,4 @@
 }  // namespace base
 }  // namespace android
 
-#endif  // BASE_UTF8_H
+#endif  // ANDROID_BASE_UTF8_H
diff --git a/bootstat/boot_event_record_store.cpp b/bootstat/boot_event_record_store.cpp
index 5d1fae9..ef4f68e 100644
--- a/bootstat/boot_event_record_store.cpp
+++ b/bootstat/boot_event_record_store.cpp
@@ -55,8 +55,11 @@
     return false;
   }
 
-  int32_t value = std::stoi(content);
-  bootstat::LogHistogram("bootstat_mtime_matches_content", value == *uptime);
+  // Ignore existing bootstat records (which do not contain file content).
+  if (!content.empty()) {
+    int32_t value = std::stoi(content);
+    bootstat::LogHistogram("bootstat_mtime_matches_content", value == *uptime);
+  }
 
   return true;
 }
diff --git a/bootstat/boot_event_record_store.h b/bootstat/boot_event_record_store.h
index 4d5deee..a2b8318 100644
--- a/bootstat/boot_event_record_store.h
+++ b/bootstat/boot_event_record_store.h
@@ -55,6 +55,7 @@
   FRIEND_TEST(BootEventRecordStoreTest, AddMultipleBootEvents);
   FRIEND_TEST(BootEventRecordStoreTest, AddBootEventWithValue);
   FRIEND_TEST(BootEventRecordStoreTest, GetBootEvent);
+  FRIEND_TEST(BootEventRecordStoreTest, GetBootEventNoFileContent);
 
   // Sets the filesystem path of the record store.
   void SetStorePath(const std::string& path);
diff --git a/bootstat/boot_event_record_store_test.cpp b/bootstat/boot_event_record_store_test.cpp
index 343f9d0..01c2cc1 100644
--- a/bootstat/boot_event_record_store_test.cpp
+++ b/bootstat/boot_event_record_store_test.cpp
@@ -17,12 +17,16 @@
 #include "boot_event_record_store.h"
 
 #include <dirent.h>
+#include <fcntl.h>
 #include <sys/stat.h>
+#include <sys/time.h>
 #include <unistd.h>
 #include <cstdint>
 #include <cstdlib>
 #include <android-base/file.h>
+#include <android-base/logging.h>
 #include <android-base/test_utils.h>
+#include <android-base/unique_fd.h>
 #include <gtest/gtest.h>
 #include <gmock/gmock.h>
 #include "uptime_parser.h"
@@ -31,6 +35,36 @@
 
 namespace {
 
+// Creates a fake boot event record file at |record_path| containing the boot
+// record |value|. This method is necessary as truncating a
+// BootEventRecordStore-created file would modify the mtime, which would alter
+// the value of the record.
+bool CreateEmptyBootEventRecord(const std::string& record_path, int32_t value) {
+  android::base::unique_fd record_fd(creat(record_path.c_str(), S_IRUSR | S_IWUSR));
+  if (record_fd == -1) {
+    return false;
+  }
+
+  // Writing the value as content in the record file is a debug measure to
+  // ensure the validity of the file mtime value, i.e., to check that the record
+  // file mtime values are not changed once set.
+  // TODO(jhawkins): Remove this block.
+  if (!android::base::WriteStringToFd(std::to_string(value), record_fd)) {
+    return false;
+  }
+
+  // Set the |mtime| of the file to store the value of the boot event while
+  // preserving the |atime|.
+  struct timeval atime = {/* tv_sec */ 0, /* tv_usec */ 0};
+  struct timeval mtime = {/* tv_sec */ value, /* tv_usec */ 0};
+  const struct timeval times[] = {atime, mtime};
+  if (utimes(record_path.c_str(), times) != 0) {
+    return false;
+  }
+
+  return true;
+}
+
 // Returns true if the time difference between |a| and |b| is no larger
 // than 10 seconds.  This allow for a relatively large fuzz when comparing
 // two timestamps taken back-to-back.
@@ -178,4 +212,19 @@
 
   // Null |record|.
   EXPECT_DEATH(store.GetBootEvent("carboniferous", nullptr), std::string());
-}
\ No newline at end of file
+}
+
+// Tests that the BootEventRecordStore is capable of handling an older record
+// protocol which does not contain file contents.
+TEST_F(BootEventRecordStoreTest, GetBootEventNoFileContent) {
+  BootEventRecordStore store;
+  store.SetStorePath(GetStorePathForTesting());
+
+  EXPECT_TRUE(CreateEmptyBootEventRecord(store.GetBootEventPath("devonian"), 2718));
+
+  BootEventRecordStore::BootEventRecord record;
+  bool result = store.GetBootEvent("devonian", &record);
+  EXPECT_EQ(true, result);
+  EXPECT_EQ("devonian", record.first);
+  EXPECT_EQ(2718, record.second);
+}
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 2639d05..b819797 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -224,13 +224,23 @@
 
   time_t current_time_utc = time(nullptr);
 
-  static const char* factory_reset_current_time = "factory_reset_current_time";
   if (current_time_utc < 0) {
     // UMA does not display negative values in buckets, so convert to positive.
-    bootstat::LogHistogram(factory_reset_current_time, std::abs(current_time_utc));
+    bootstat::LogHistogram(
+        "factory_reset_current_time_failure", std::abs(current_time_utc));
+
+    // Logging via BootEventRecordStore to see if using bootstat::LogHistogram
+    // is losing records somehow.
+    boot_event_store.AddBootEventWithValue(
+        "factory_reset_current_time_failure", std::abs(current_time_utc));
     return;
   } else {
-    bootstat::LogHistogram(factory_reset_current_time, current_time_utc);
+    bootstat::LogHistogram("factory_reset_current_time", current_time_utc);
+
+    // Logging via BootEventRecordStore to see if using bootstat::LogHistogram
+    // is losing records somehow.
+    boot_event_store.AddBootEventWithValue(
+        "factory_reset_current_time", current_time_utc);
   }
 
   // The factory_reset boot event does not exist after the device is reset, so
@@ -247,6 +257,12 @@
   // factory_reset time.
   time_t factory_reset_utc = record.second;
   bootstat::LogHistogram("factory_reset_record_value", factory_reset_utc);
+
+  // Logging via BootEventRecordStore to see if using bootstat::LogHistogram
+  // is losing records somehow.
+  boot_event_store.AddBootEventWithValue(
+      "factory_reset_record_value", factory_reset_utc);
+
   time_t time_since_factory_reset = difftime(current_time_utc,
                                              factory_reset_utc);
   boot_event_store.AddBootEventWithValue("time_since_factory_reset",
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 7e60a72..fa8f19a 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -355,8 +355,8 @@
             "                                           formatting.\n"
             "  -s <specific device>                     Specify a device. For USB, provide either\n"
             "                                           a serial number or path to device port.\n"
-            "                                           For ethernet, provide an address in the"
-            "                                           form <protocol>:<hostname>[:port] where"
+            "                                           For ethernet, provide an address in the\n"
+            "                                           form <protocol>:<hostname>[:port] where\n"
             "                                           <protocol> is either tcp or udp.\n"
             "  -p <product>                             Specify product name.\n"
             "  -c <cmdline>                             Override kernel commandline.\n"
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 35f1a9e..6469ec4 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -27,6 +27,7 @@
 #include <sys/socket.h>
 #include <sys/mount.h>
 #include <sys/resource.h>
+#include <sys/syscall.h>
 #include <sys/time.h>
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -61,19 +62,20 @@
 #define UNMOUNT_CHECK_MS 5000
 #define UNMOUNT_CHECK_TIMES 10
 
-// System call provided by bionic but not in any header file.
-extern "C" int init_module(void *, unsigned long, const char *);
-
 static const int kTerminateServiceDelayMicroSeconds = 50000;
 
 static int insmod(const char *filename, const char *options) {
-    std::string module;
-    if (!read_file(filename, &module)) {
+    int fd = open(filename, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
+    if (fd == -1) {
+        ERROR("insmod: open(\"%s\") failed: %s", filename, strerror(errno));
         return -1;
     }
-
-    // TODO: use finit_module for >= 3.8 kernels.
-    return init_module(&module[0], module.size(), options);
+    int rc = syscall(__NR_finit_module, fd, options, 0);
+    if (rc == -1) {
+        ERROR("finit_module for \"%s\" failed: %s", filename, strerror(errno));
+    }
+    close(fd);
+    return rc;
 }
 
 static int __ifupdown(const char *interface, int up) {
diff --git a/init/devices.cpp b/init/devices.cpp
index 557a6ac..e74140b 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -413,7 +413,7 @@
 static char **get_character_device_symlinks(struct uevent *uevent)
 {
     const char *parent;
-    char *slash;
+    const char *slash;
     char **links;
     int link_num = 0;
     int width;
@@ -467,7 +467,7 @@
 {
     const char *device;
     struct platform_node *pdev;
-    char *slash;
+    const char *slash;
     const char *type;
     char buf[256];
     char link_path[256];
diff --git a/init/init.cpp b/init/init.cpp
index 69f0595..9b7d108 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -18,7 +18,6 @@
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
-#include <fstream>
 #include <libgen.h>
 #include <paths.h>
 #include <signal.h>
@@ -290,100 +289,6 @@
     return result;
 }
 
-static void security_failure() {
-    ERROR("Security failure; rebooting into recovery mode...\n");
-    android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
-    while (true) { pause(); }  // never reached
-}
-
-#define MMAP_RND_PATH "/proc/sys/vm/mmap_rnd_bits"
-#define MMAP_RND_COMPAT_PATH "/proc/sys/vm/mmap_rnd_compat_bits"
-
-/* __attribute__((unused)) due to lack of mips support: see mips block
- * in set_mmap_rnd_bits_action */
-static bool __attribute__((unused)) set_mmap_rnd_bits_min(int start, int min, bool compat) {
-    std::string path;
-    if (compat) {
-        path = MMAP_RND_COMPAT_PATH;
-    } else {
-        path = MMAP_RND_PATH;
-    }
-    std::ifstream inf(path, std::fstream::in);
-    if (!inf) {
-        return false;
-    }
-    while (start >= min) {
-        // try to write out new value
-        std::string str_val = std::to_string(start);
-        std::ofstream of(path, std::fstream::out);
-        if (!of) {
-            return false;
-        }
-        of << str_val << std::endl;
-        of.close();
-
-        // check to make sure it was recorded
-        inf.seekg(0);
-        std::string str_rec;
-        inf >> str_rec;
-        if (str_val.compare(str_rec) == 0) {
-            break;
-        }
-        start--;
-    }
-    inf.close();
-    return (start >= min);
-}
-
-/*
- * Set /proc/sys/vm/mmap_rnd_bits and potentially
- * /proc/sys/vm/mmap_rnd_compat_bits to the maximum supported values.
- * Returns -1 if unable to set these to an acceptable value.  Apply
- * upstream patch-sets https://lkml.org/lkml/2015/12/21/337 and
- * https://lkml.org/lkml/2016/2/4/831 to enable this.
- */
-static int set_mmap_rnd_bits_action(const std::vector<std::string>& args)
-{
-    int ret = -1;
-
-    /* values are arch-dependent */
-#if defined(__aarch64__)
-    /* arm64 supports 18 - 33 bits depending on pagesize and VA_SIZE */
-    if (set_mmap_rnd_bits_min(33, 24, false)
-            && set_mmap_rnd_bits_min(16, 16, true)) {
-        ret = 0;
-    }
-#elif defined(__x86_64__)
-    /* x86_64 supports 28 - 32 bits */
-    if (set_mmap_rnd_bits_min(32, 32, false)
-            && set_mmap_rnd_bits_min(16, 16, true)) {
-        ret = 0;
-    }
-#elif defined(__arm__) || defined(__i386__)
-    /* check to see if we're running on 64-bit kernel */
-    bool h64 = !access(MMAP_RND_COMPAT_PATH, F_OK);
-    /* supported 32-bit architecture must have 16 bits set */
-    if (set_mmap_rnd_bits_min(16, 16, h64)) {
-        ret = 0;
-    }
-#elif defined(__mips__) || defined(__mips64__)
-    // TODO: add mips support b/27788820
-    ret = 0;
-#else
-    ERROR("Unknown architecture\n");
-#endif
-
-#ifdef __BRILLO__
-    // TODO: b/27794137
-    ret = 0;
-#endif
-    if (ret == -1) {
-        ERROR("Unable to set adequate mmap entropy value!\n");
-        security_failure();
-    }
-    return ret;
-}
-
 static int keychord_init_action(const std::vector<std::string>& args)
 {
     keychord_init();
@@ -540,6 +445,12 @@
     return 0;
 }
 
+static void security_failure() {
+    ERROR("Security failure; rebooting into recovery mode...\n");
+    android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
+    while (true) { pause(); }  // never reached
+}
+
 static void selinux_initialize(bool in_kernel_domain) {
     Timer t;
 
@@ -689,7 +600,6 @@
     am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
     // ... so that we can start queuing up actions that require stuff from /dev.
     am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
-    am.QueueBuiltinAction(set_mmap_rnd_bits_action, "set_mmap_rnd_bits");
     am.QueueBuiltinAction(keychord_init_action, "keychord_init");
     am.QueueBuiltinAction(console_init_action, "console_init");
 
diff --git a/init/util.cpp b/init/util.cpp
index bddc3b2..4d36dfd 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -355,7 +355,7 @@
 {
     int ret;
     char buf[256];
-    char *slash;
+    const char *slash;
     int width;
 
     slash = strrchr(newpath, '/');
diff --git a/libbacktrace/Android.mk b/libbacktrace/Android.mk
index 397dfda..a8a7c4a 100644
--- a/libbacktrace/Android.mk
+++ b/libbacktrace/Android.mk
@@ -94,18 +94,34 @@
 libbacktrace_offline_src_files := \
 	BacktraceOffline.cpp \
 
+# Use shared llvm library on device to save space.
 libbacktrace_offline_shared_libraries := \
 	libbacktrace \
+	libbase \
 	liblog \
 	libunwind \
-
-# Use shared llvm library on device to save space.
-libbacktrace_offline_shared_libraries_target := \
+	libutils \
 	libLLVM \
 
+libbacktrace_offline_static_libraries := \
+	libziparchive \
+	libz \
+
+module := libbacktrace_offline
+build_type := target
+build_target := SHARED_LIBRARY
+include $(LOCAL_PATH)/Android.build.mk
+
+libbacktrace_offline_shared_libraries := \
+	libbacktrace \
+	libbase \
+	liblog \
+	libunwind \
+	libziparchive-host \
+
 # Use static llvm libraries on host to remove dependency on 32-bit llvm shared library
 # which is not included in the prebuilt.
-libbacktrace_offline_static_libraries_host := \
+libbacktrace_offline_static_libraries := \
 	libLLVMObject \
 	libLLVMBitReader \
 	libLLVMMC \
@@ -114,9 +130,6 @@
 	libLLVMSupport \
 
 module := libbacktrace_offline
-build_type := target
-build_target := SHARED_LIBRARY
-include $(LOCAL_PATH)/Android.build.mk
 build_type := host
 libbacktrace_multilib := both
 include $(LOCAL_PATH)/Android.build.mk
@@ -129,6 +142,8 @@
 	liblog \
 	libunwind \
 	liblzma \
+	libziparchive \
+	libz \
 	libLLVMObject \
 	libLLVMBitReader \
 	libLLVMMC \
diff --git a/libbacktrace/BacktraceOffline.cpp b/libbacktrace/BacktraceOffline.cpp
index dc3ed67..9a4f622 100644
--- a/libbacktrace/BacktraceOffline.cpp
+++ b/libbacktrace/BacktraceOffline.cpp
@@ -29,11 +29,14 @@
 #include <ucontext.h>
 #include <unistd.h>
 
+#include <memory>
 #include <string>
 #include <vector>
 
+#include <android-base/file.h>
 #include <backtrace/Backtrace.h>
 #include <backtrace/BacktraceMap.h>
+#include <ziparchive/zip_archive.h>
 
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wunused-parameter"
@@ -641,15 +644,103 @@
   return memcmp(buf, elf_magic, 4) == 0;
 }
 
+static bool IsValidApkPath(const std::string& apk_path) {
+  static const char zip_preamble[] = {0x50, 0x4b, 0x03, 0x04};
+  struct stat st;
+  if (stat(apk_path.c_str(), &st) != 0 || !S_ISREG(st.st_mode)) {
+    return false;
+  }
+  FILE* fp = fopen(apk_path.c_str(), "reb");
+  if (fp == nullptr) {
+    return false;
+  }
+  char buf[4];
+  if (fread(buf, 4, 1, fp) != 1) {
+    fclose(fp);
+    return false;
+  }
+  fclose(fp);
+  return memcmp(buf, zip_preamble, 4) == 0;
+}
+
+class ScopedZiparchiveHandle {
+ public:
+  ScopedZiparchiveHandle(ZipArchiveHandle handle) : handle_(handle) {
+  }
+
+  ~ScopedZiparchiveHandle() {
+    CloseArchive(handle_);
+  }
+
+ private:
+  ZipArchiveHandle handle_;
+};
+
+llvm::object::OwningBinary<llvm::object::Binary> OpenEmbeddedElfFile(const std::string& filename) {
+  llvm::object::OwningBinary<llvm::object::Binary> nothing;
+  size_t pos = filename.find("!/");
+  if (pos == std::string::npos) {
+    return nothing;
+  }
+  std::string apk_file = filename.substr(0, pos);
+  std::string elf_file = filename.substr(pos + 2);
+  if (!IsValidApkPath(apk_file)) {
+    BACK_LOGW("%s is not a valid apk file", apk_file.c_str());
+    return nothing;
+  }
+  ZipArchiveHandle handle;
+  int32_t ret_code = OpenArchive(apk_file.c_str(), &handle);
+  if (ret_code != 0) {
+    CloseArchive(handle);
+    BACK_LOGW("failed to open archive %s: %s", apk_file.c_str(), ErrorCodeString(ret_code));
+    return nothing;
+  }
+  ScopedZiparchiveHandle scoped_handle(handle);
+  ZipEntry zentry;
+  ret_code = FindEntry(handle, ZipString(elf_file.c_str()), &zentry);
+  if (ret_code != 0) {
+    BACK_LOGW("failed to find %s in %s: %s", elf_file.c_str(), apk_file.c_str(),
+              ErrorCodeString(ret_code));
+    return nothing;
+  }
+  if (zentry.method != kCompressStored || zentry.compressed_length != zentry.uncompressed_length) {
+    BACK_LOGW("%s is compressed in %s, which doesn't support running directly", elf_file.c_str(),
+              apk_file.c_str());
+    return nothing;
+  }
+  auto buffer_or_err = llvm::MemoryBuffer::getOpenFileSlice(GetFileDescriptor(handle), apk_file,
+                                                            zentry.uncompressed_length,
+                                                            zentry.offset);
+  if (!buffer_or_err) {
+    BACK_LOGW("failed to read %s in %s: %s", elf_file.c_str(), apk_file.c_str(),
+              buffer_or_err.getError().message().c_str());
+    return nothing;
+  }
+  auto binary_or_err = llvm::object::createBinary(buffer_or_err.get()->getMemBufferRef());
+  if (!binary_or_err) {
+    BACK_LOGW("failed to create binary for %s in %s: %s", elf_file.c_str(), apk_file.c_str(),
+              binary_or_err.getError().message().c_str());
+    return nothing;
+  }
+  return llvm::object::OwningBinary<llvm::object::Binary>(std::move(binary_or_err.get()),
+                                                          std::move(buffer_or_err.get()));
+}
+
 static DebugFrameInfo* ReadDebugFrameFromFile(const std::string& filename) {
-  if (!IsValidElfPath(filename)) {
-    return nullptr;
+  llvm::object::OwningBinary<llvm::object::Binary> owning_binary;
+  if (filename.find("!/") != std::string::npos) {
+    owning_binary = OpenEmbeddedElfFile(filename);
+  } else {
+    if (!IsValidElfPath(filename)) {
+      return nullptr;
+    }
+    auto binary_or_err = llvm::object::createBinary(llvm::StringRef(filename));
+    if (!binary_or_err) {
+      return nullptr;
+    }
+    owning_binary = std::move(binary_or_err.get());
   }
-  auto owning_binary = llvm::object::createBinary(llvm::StringRef(filename));
-  if (owning_binary.getError()) {
-    return nullptr;
-  }
-  llvm::object::Binary* binary = owning_binary.get().getBinary();
+  llvm::object::Binary* binary = owning_binary.getBinary();
   auto obj = llvm::dyn_cast<llvm::object::ObjectFile>(binary);
   if (obj == nullptr) {
     return nullptr;
diff --git a/liblog/logd_writer.c b/liblog/logd_writer.c
index 696237d..059f170 100644
--- a/liblog/logd_writer.c
+++ b/liblog/logd_writer.c
@@ -105,12 +105,6 @@
     if (logId > LOG_ID_SECURITY) {
         return -EINVAL;
     }
-    if (logId == LOG_ID_SECURITY) {
-        uid_t uid = __android_log_uid();
-        if ((uid != AID_LOG) && (uid != AID_ROOT) && (uid != AID_SYSTEM)) {
-            return -EPERM;
-        }
-    }
     if (logdLoggerWrite.context.sock < 0) {
         if (access("/dev/socket/logdw", W_OK) == 0) {
             return 0;
diff --git a/liblog/logger.h b/liblog/logger.h
index 61bc396..5d031d7 100644
--- a/liblog/logger.h
+++ b/liblog/logger.h
@@ -40,6 +40,7 @@
 struct android_log_transport_write {
   struct listnode node;
   const char *name;
+  unsigned logMask; /* cache of available success */
   union android_log_context context; /* Initialized by static allocation */
 
   int (*available)(log_id_t logId);
diff --git a/liblog/logger_read.c b/liblog/logger_read.c
index f15c7cd..0d6ba08 100644
--- a/liblog/logger_read.c
+++ b/liblog/logger_read.c
@@ -26,6 +26,7 @@
 #include <cutils/list.h>
 #include <log/log.h>
 #include <log/logger.h>
+#include <private/android_filesystem_config.h>
 
 #include "config_read.h"
 #include "log_portability.h"
@@ -91,6 +92,10 @@
         logger_for_each(logger, logger_list) {
             log_id_t logId = logger->logId;
 
+            if ((logId == LOG_ID_SECURITY) &&
+                    (__android_log_uid() != AID_SYSTEM)) {
+                continue;
+            }
             if (transport->read &&
                     (!transport->available ||
                         (transport->available(logId) >= 0))) {
diff --git a/liblog/logger_write.c b/liblog/logger_write.c
index a4155e9..b802ed7 100644
--- a/liblog/logger_write.c
+++ b/liblog/logger_write.c
@@ -49,20 +49,85 @@
     kLogUninitialized, kLogNotAvailable, kLogAvailable
 } g_log_status = kLogUninitialized;
 
+static int check_log_uid_permissions()
+{
+#if defined(__BIONIC__)
+    uid_t uid = __android_log_uid();
+
+    /* Matches clientHasLogCredentials() in logd */
+    if ((uid != AID_SYSTEM) && (uid != AID_ROOT) && (uid != AID_LOG)) {
+        uid = geteuid();
+        if ((uid != AID_SYSTEM) && (uid != AID_ROOT) && (uid != AID_LOG)) {
+            gid_t gid = getgid();
+            if ((gid != AID_SYSTEM) &&
+                    (gid != AID_ROOT) &&
+                    (gid != AID_LOG)) {
+                gid = getegid();
+                if ((gid != AID_SYSTEM) &&
+                        (gid != AID_ROOT) &&
+                        (gid != AID_LOG)) {
+                    int num_groups;
+                    gid_t *groups;
+
+                    num_groups = getgroups(0, NULL);
+                    if (num_groups <= 0) {
+                        return -EPERM;
+                    }
+                    groups = calloc(num_groups, sizeof(gid_t));
+                    if (!groups) {
+                        return -ENOMEM;
+                    }
+                    num_groups = getgroups(num_groups, groups);
+                    while (num_groups > 0) {
+                        if (groups[num_groups - 1] == AID_LOG) {
+                            break;
+                        }
+                        --num_groups;
+                    }
+                    free(groups);
+                    if (num_groups <= 0) {
+                        return -EPERM;
+                    }
+                }
+            }
+        }
+    }
+#endif
+    return 0;
+}
+
+static void __android_log_cache_available(
+        struct android_log_transport_write *node)
+{
+    size_t i;
+
+    if (node->logMask) {
+        return;
+    }
+
+    for (i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
+        if (node->write &&
+                (i != LOG_ID_KERNEL) &&
+                ((i != LOG_ID_SECURITY) ||
+                    (check_log_uid_permissions() == 0)) &&
+                (!node->available || ((*node->available)(i) >= 0))) {
+            node->logMask |= 1 << i;
+        }
+    }
+}
+
 LIBLOG_ABI_PUBLIC int __android_log_dev_available()
 {
     struct android_log_transport_write *node;
-    size_t i;
 
     if (list_empty(&__android_log_transport_write)) {
         return kLogUninitialized;
     }
-    for (i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
-        write_transport_for_each(node, &__android_log_transport_write) {
-            if (node->write &&
-                    (!node->available || ((*node->available)(i) >= 0))) {
-                return kLogAvailable;
-            }
+
+    write_transport_for_each(node, &__android_log_transport_write) {
+        __android_log_cache_available(node);
+        if (node->logMask) {
+            return kLogAvailable;
         }
     }
     return kLogNotAvailable;
@@ -77,6 +142,11 @@
 
     __android_log_config_write();
     write_transport_for_each_safe(transport, n, &__android_log_transport_write) {
+        __android_log_cache_available(transport);
+        if (!transport->logMask) {
+            list_remove(&transport->node);
+            continue;
+        }
         if (!transport->open || ((*transport->open)() < 0)) {
             if (transport->close) {
                 (*transport->close)();
@@ -87,6 +157,11 @@
         ++ret;
     }
     write_transport_for_each_safe(transport, n, &__android_log_persist_write) {
+        __android_log_cache_available(transport);
+        if (!transport->logMask) {
+            list_remove(&transport->node);
+            continue;
+        }
         if (!transport->open || ((*transport->open)() < 0)) {
             if (transport->close) {
                 (*transport->close)();
@@ -127,50 +202,13 @@
 
 #if defined(__BIONIC__)
     if (log_id == LOG_ID_SECURITY) {
-        uid_t uid;
-
         if (vec[0].iov_len < 4) {
             return -EINVAL;
         }
 
-        uid = __android_log_uid();
-        /* Matches clientHasLogCredentials() in logd */
-        if ((uid != AID_SYSTEM) && (uid != AID_ROOT) && (uid != AID_LOG)) {
-            uid = geteuid();
-            if ((uid != AID_SYSTEM) && (uid != AID_ROOT) && (uid != AID_LOG)) {
-                gid_t gid = getgid();
-                if ((gid != AID_SYSTEM) &&
-                        (gid != AID_ROOT) &&
-                        (gid != AID_LOG)) {
-                    gid = getegid();
-                    if ((gid != AID_SYSTEM) &&
-                            (gid != AID_ROOT) &&
-                            (gid != AID_LOG)) {
-                        int num_groups;
-                        gid_t *groups;
-
-                        num_groups = getgroups(0, NULL);
-                        if (num_groups <= 0) {
-                            return -EPERM;
-                        }
-                        groups = calloc(num_groups, sizeof(gid_t));
-                        if (!groups) {
-                            return -ENOMEM;
-                        }
-                        num_groups = getgroups(num_groups, groups);
-                        while (num_groups > 0) {
-                            if (groups[num_groups - 1] == AID_LOG) {
-                                break;
-                            }
-                            --num_groups;
-                        }
-                        free(groups);
-                        if (num_groups <= 0) {
-                            return -EPERM;
-                        }
-                    }
-                }
-            }
+        ret = check_log_uid_permissions();
+        if (ret < 0) {
+            return ret;
         }
         if (!__android_log_security()) {
             /* If only we could reset downstream logd counter */
@@ -262,8 +300,9 @@
 #endif
 
     ret = 0;
+    i = 1 << log_id;
     write_transport_for_each(node, &__android_log_transport_write) {
-        if (node->write) {
+        if (node->logMask & i) {
             ssize_t retval;
             retval = (*node->write)(log_id, &ts, vec, nr);
             if (ret >= 0) {
@@ -273,7 +312,7 @@
     }
 
     write_transport_for_each(node, &__android_log_persist_write) {
-        if (node->write) {
+        if (node->logMask & i) {
             (void)(*node->write)(log_id, &ts, vec, nr);
         }
     }
@@ -343,12 +382,12 @@
     }
 #endif
 
-    vec[0].iov_base   = (unsigned char *) &prio;
-    vec[0].iov_len    = 1;
-    vec[1].iov_base   = (void *) tag;
-    vec[1].iov_len    = strlen(tag) + 1;
-    vec[2].iov_base   = (void *) msg;
-    vec[2].iov_len    = strlen(msg) + 1;
+    vec[0].iov_base = (unsigned char *)&prio;
+    vec[0].iov_len  = 1;
+    vec[1].iov_base = (void *)tag;
+    vec[1].iov_len  = strlen(tag) + 1;
+    vec[2].iov_base = (void *)msg;
+    vec[2].iov_len  = strlen(msg) + 1;
 
     return write_to_log(bufID, vec, 3);
 }
diff --git a/liblog/logprint.c b/liblog/logprint.c
index d7de864..9b60d4a 100644
--- a/liblog/logprint.c
+++ b/liblog/logprint.c
@@ -512,8 +512,18 @@
     }
 
     if (msgStart == -1) {
-        fprintf(stderr, "+++ LOG: malformed log message\n");
-        return -1;
+        /* +++ LOG: malformed log message, DYB */
+        for (i = 1; i < buf->len; i++) {
+            /* odd characters in tag? */
+            if ((msg[i] <= ' ') || (msg[i] == ':') || (msg[i] >= 0x7f)) {
+                msg[i] = '\0';
+                msgStart = i + 1;
+                break;
+            }
+        }
+        if (msgStart == -1) {
+            msgStart = buf->len - 1; /* All tag, no message, print truncates */
+        }
     }
     if (msgEnd == -1) {
         /* incoming message not null-terminated; force it */
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 941b9b9..456f8b3 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -1065,8 +1065,10 @@
             fflush(stderr);
             int printLogLine =
                     android_log_printLogLine(logformat, fileno(stderr), &entry);
+            // Legacy tag truncation
             EXPECT_LE(128, printLogLine);
-            EXPECT_GT(LOGGER_ENTRY_MAX_PAYLOAD, printLogLine);
+            // Measured maximum if we try to print part of the tag as message
+            EXPECT_GT(LOGGER_ENTRY_MAX_PAYLOAD * 13 / 8, printLogLine);
         }
         android_log_format_free(logformat);
     }
diff --git a/libmemunreachable/ProcessMappings.cpp b/libmemunreachable/ProcessMappings.cpp
index 7cca7c1..57b2321 100644
--- a/libmemunreachable/ProcessMappings.cpp
+++ b/libmemunreachable/ProcessMappings.cpp
@@ -30,11 +30,10 @@
 bool ProcessMappings(pid_t pid, allocator::vector<Mapping>& mappings) {
   char map_buffer[1024];
   snprintf(map_buffer, sizeof(map_buffer), "/proc/%d/maps", pid);
-  int fd = open(map_buffer, O_RDONLY);
-  if (fd < 0) {
+  android::base::unique_fd fd(open(map_buffer, O_RDONLY));
+  if (fd == -1) {
     return false;
   }
-  android::base::unique_fd fd_guard{fd};
 
   LineBuffer line_buf(fd, map_buffer, sizeof(map_buffer));
   char* line;
diff --git a/libmemunreachable/ThreadCapture.cpp b/libmemunreachable/ThreadCapture.cpp
index e8a8392..9155c29 100644
--- a/libmemunreachable/ThreadCapture.cpp
+++ b/libmemunreachable/ThreadCapture.cpp
@@ -108,12 +108,11 @@
   strlcat(path, pid_str, sizeof(path));
   strlcat(path, "/task", sizeof(path));
 
-  int fd = open(path, O_CLOEXEC | O_DIRECTORY | O_RDONLY);
-  if (fd < 0) {
+  android::base::unique_fd fd(open(path, O_CLOEXEC | O_DIRECTORY | O_RDONLY));
+  if (fd == -1) {
     ALOGE("failed to open %s: %s", path, strerror(errno));
     return false;
   }
-  android::base::unique_fd fd_guard{fd};
 
   struct linux_dirent64 {
     uint64_t  d_ino;
@@ -125,7 +124,7 @@
   char dirent_buf[4096];
   ssize_t nread;
   do {
-    nread = syscall(SYS_getdents64, fd, dirent_buf, sizeof(dirent_buf));
+    nread = syscall(SYS_getdents64, fd.get(), dirent_buf, sizeof(dirent_buf));
     if (nread < 0) {
       ALOGE("failed to get directory entries from %s: %s", path, strerror(errno));
       return false;
diff --git a/libmemunreachable/tests/ThreadCapture_test.cpp b/libmemunreachable/tests/ThreadCapture_test.cpp
index cefe94e..41ed84e 100644
--- a/libmemunreachable/tests/ThreadCapture_test.cpp
+++ b/libmemunreachable/tests/ThreadCapture_test.cpp
@@ -28,8 +28,6 @@
 
 #include <gtest/gtest.h>
 
-#include <android-base/unique_fd.h>
-
 #include "Allocator.h"
 #include "ScopedDisableMalloc.h"
 #include "ScopedPipe.h"
diff --git a/logcat/Android.mk b/logcat/Android.mk
index b828a9f..1dacbe1 100644
--- a/logcat/Android.mk
+++ b/logcat/Android.mk
@@ -5,7 +5,7 @@
 
 LOCAL_SRC_FILES:= logcat.cpp event.logtags
 
-LOCAL_SHARED_LIBRARIES := liblog libbase libcutils
+LOCAL_SHARED_LIBRARIES := liblog libbase libcutils libpcrecpp
 
 LOCAL_MODULE := logcat
 
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index c148d89..cc3333c 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -37,6 +37,8 @@
 #include <log/logprint.h>
 #include <system/thread_defs.h>
 
+#include <pcrecpp.h>
+
 #define DEFAULT_MAX_ROTATED_LOGS 4
 
 static AndroidLogFormat * g_logformat;
@@ -76,6 +78,10 @@
 static size_t g_outByteCount = 0;
 static int g_printBinary = 0;
 static int g_devCount = 0;                              // >1 means multiple
+static pcrecpp::RE* g_regex;
+// 0 means "infinite"
+static size_t g_maxCount = 0;
+static size_t g_printCount = 0;
 
 __noreturn static void logcat_panic(bool showHelp, const char *fmt, ...) __printflike(2,3);
 
@@ -143,6 +149,21 @@
     TEMP_FAILURE_RETRY(write(g_outFD, buf, size));
 }
 
+static bool regexOk(const AndroidLogEntry& entry, log_id_t id)
+{
+    if (! g_regex) {
+        return true;
+    }
+
+    if (id == LOG_ID_EVENTS || id == LOG_ID_SECURITY) {
+        return false;
+    }
+
+    std::string messageString(entry.message, entry.messageLen);
+
+    return g_regex->PartialMatch(messageString);
+}
+
 static void processBuffer(log_device_t* dev, struct log_msg *buf)
 {
     int bytesWritten = 0;
@@ -171,9 +192,12 @@
         goto error;
     }
 
-    if (android_log_shouldPrintLine(g_logformat, entry.tag, entry.priority)) {
+    if (android_log_shouldPrintLine(g_logformat, entry.tag, entry.priority) &&
+        regexOk(entry, buf->id())) {
         bytesWritten = android_log_printLogLine(g_logformat, g_outFD, &entry);
 
+        g_printCount++;
+
         if (bytesWritten < 0) {
             logcat_panic(false, "output error");
         }
@@ -271,6 +295,10 @@
                     "  -c              clear (flush) the entire log and exit\n"
                     "  --clear\n"
                     "  -d              dump the log and then exit (don't block)\n"
+                    "  -e <expr>       only print lines where the log message matches <expr>\n"
+                    "  --regex <expr>  where <expr> is a regular expression\n"
+                    "  -m <count>      quit after printing <count> lines. This is meant to be\n"
+                    "  --max-count=<count> paired with --regex, but will work on its own.\n"
                     "  -t <count>      print only the most recent <count> lines (implies -d)\n"
                     "  -t '<time>'     print most recent lines since specified time (implies -d)\n"
                     "  -T <count>      print only the most recent <count> lines (does not imply -d)\n"
@@ -521,6 +549,7 @@
     size_t tail_lines = 0;
     log_time tail_time(log_time::EPOCH);
     size_t pid = 0;
+    bool got_t = false;
 
     signal(SIGPIPE, exit);
 
@@ -547,7 +576,9 @@
           { "format",        required_argument, NULL,   'v' },
           { "last",          no_argument,       NULL,   'L' },
           { pid_str,         required_argument, NULL,   0 },
+          { "max-count",     required_argument, NULL,   'm' },
           { "prune",         optional_argument, NULL,   'p' },
+          { "regex",         required_argument, NULL,   'e' },
           { "rotate_count",  required_argument, NULL,   'n' },
           { "rotate_kbytes", required_argument, NULL,   'r' },
           { "statistics",    no_argument,       NULL,   'S' },
@@ -556,7 +587,7 @@
           { NULL,            0,                 NULL,   0 }
         };
 
-        ret = getopt_long(argc, argv, ":cdDLt:T:gG:sQf:r:n:v:b:BSpP:",
+        ret = getopt_long(argc, argv, ":cdDLt:T:gG:sQf:r:n:v:b:BSpP:m:e:",
                           long_options, &option_index);
 
         if (ret < 0) {
@@ -613,6 +644,7 @@
             break;
 
             case 't':
+                got_t = true;
                 mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
                 /* FALLTHRU */
             case 'T':
@@ -644,6 +676,19 @@
                 printDividers = true;
             break;
 
+            case 'e':
+                g_regex = new pcrecpp::RE(optarg);
+            break;
+
+            case 'm': {
+                char *end = NULL;
+                if (!getSizeTArg(optarg, &g_maxCount)) {
+                    logcat_panic(false, "-%c \"%s\" isn't an "
+                                 "integer greater than zero\n", ret, optarg);
+                }
+            }
+            break;
+
             case 'g':
                 if (!optarg) {
                     getLogSize = 1;
@@ -920,6 +965,10 @@
         }
     }
 
+    if (g_maxCount && got_t) {
+        logcat_panic(true, "Cannot use -m (--max-count) and -t together");
+    }
+
     if (!devices) {
         dev = devices = new log_device_t("main", false);
         g_devCount = 1;
@@ -1135,7 +1184,8 @@
 
     dev = NULL;
     log_device_t unexpected("unexpected", false);
-    while (1) {
+
+    while (!g_maxCount || g_printCount < g_maxCount) {
         struct log_msg log_msg;
         log_device_t* d;
         int ret = android_logger_list_read(logger_list, &log_msg);
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index 8459bd3..c6eb7f8 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -953,3 +953,67 @@
     free(list);
     list = NULL;
 }
+
+TEST(logcat, regex) {
+    FILE *fp;
+    int count = 0;
+
+    char buffer[5120];
+
+    snprintf(buffer, sizeof(buffer), "logcat --pid %d -d -e logcat_test_a+b", getpid());
+
+    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test_ab"));
+    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test_b"));
+    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test_aaaab"));
+    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test_aaaa"));
+
+    // Let the logs settle
+    sleep(1);
+
+    ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
+
+    while (fgets(buffer, sizeof(buffer), fp)) {
+        if (!strncmp(begin, buffer, sizeof(begin) - 1)) {
+            continue;
+        }
+
+        EXPECT_TRUE(strstr(buffer, "logcat_test_") != NULL);
+
+        count++;
+    }
+
+    pclose(fp);
+
+    ASSERT_EQ(2, count);
+}
+
+TEST(logcat, maxcount) {
+    FILE *fp;
+    int count = 0;
+
+    char buffer[5120];
+
+    snprintf(buffer, sizeof(buffer), "logcat --pid %d -d --max-count 3", getpid());
+
+    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
+    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
+    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
+    LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
+
+    // Let the logs settle
+    sleep(1);
+
+    ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
+
+    while (fgets(buffer, sizeof(buffer), fp)) {
+        if (!strncmp(begin, buffer, sizeof(begin) - 1)) {
+            continue;
+        }
+
+        count++;
+    }
+
+    pclose(fp);
+
+    ASSERT_EQ(3, count);
+}
diff --git a/mkbootimg/mkbootimg b/mkbootimg/mkbootimg
index 7b04bcc..5a13da2 100755
--- a/mkbootimg/mkbootimg
+++ b/mkbootimg/mkbootimg
@@ -102,12 +102,12 @@
 def parse_os_version(x):
     match = re.search(r'^(\d{1,3})(?:\.(\d{1,3})(?:\.(\d{1,3}))?)?', x)
     if match:
-        a = parse_int(match.group(1))
+        a = int(match.group(1))
         b = c = 0
         if match.lastindex >= 2:
-            b = parse_int(match.group(2))
+            b = int(match.group(2))
         if match.lastindex == 3:
-            c = parse_int(match.group(3))
+            c = int(match.group(3))
         # 7 bits allocated for each field
         assert a < 128
         assert b < 128
@@ -118,8 +118,8 @@
 def parse_os_patch_level(x):
     match = re.search(r'^(\d{4})-(\d{2})-(\d{2})', x)
     if match:
-        y = parse_int(match.group(1)) - 2000
-        m = parse_int(match.group(2))
+        y = int(match.group(1)) - 2000
+        m = int(match.group(2))
         # 7 bits allocated for the year, 4 bits for the month
         assert y >= 0 and y < 128
         assert m > 0 and m <= 12