Merge "Revert "Revert "HACK: Run e2fsck with -f""" into nyc-dev
diff --git a/base/include/android-base/logging.h b/base/include/android-base/logging.h
index cd526d0..c82f858 100644
--- a/base/include/android-base/logging.h
+++ b/base/include/android-base/logging.h
@@ -100,9 +100,9 @@
     errno = saved_errno_;
   }
 
-  // Allow this object to evaluate to false which is useful in macros.
+  // Allow this object to be used as part of && operation.
   operator bool() const {
-    return false;
+    return true;
   }
 
  private:
@@ -123,13 +123,11 @@
 // else statement after LOG() macro, it won't bind to the if statement in the macro.
 // do-while(0) statement doesn't work here. Because we need to support << operator
 // following the macro, like "LOG(DEBUG) << xxx;".
-#define LOG_TO(dest, severity)                                                      \
-  if (LIKELY(::android::base::severity < ::android::base::GetMinimumLogSeverity())) \
-    ;                                                                               \
-  else                                                                              \
-    ::android::base::ErrnoRestorer() ? *(std::ostream*)nullptr :                    \
-      ::android::base::LogMessage(__FILE__, __LINE__,                               \
-          ::android::base::dest,                                                    \
+#define LOG_TO(dest, severity)                                                        \
+  UNLIKELY(::android::base::severity >= ::android::base::GetMinimumLogSeverity()) &&  \
+    ::android::base::ErrnoRestorer() &&                                               \
+      ::android::base::LogMessage(__FILE__, __LINE__,                                 \
+          ::android::base::dest,                                                      \
           ::android::base::severity, -1).stream()
 
 // A variant of LOG that also logs the current errno value. To be used when
@@ -137,13 +135,11 @@
 #define PLOG(severity) PLOG_TO(DEFAULT, severity)
 
 // Behaves like PLOG, but logs to the specified log ID.
-#define PLOG_TO(dest, severity)                                                     \
-  if (LIKELY(::android::base::severity < ::android::base::GetMinimumLogSeverity())) \
-    ;                                                                               \
-  else                                                                              \
-    ::android::base::ErrnoRestorer() ? *(std::ostream*)nullptr :                    \
-      ::android::base::LogMessage(__FILE__, __LINE__,                               \
-          ::android::base::dest,                                                    \
+#define PLOG_TO(dest, severity)                                                      \
+  UNLIKELY(::android::base::severity >= ::android::base::GetMinimumLogSeverity()) && \
+    ::android::base::ErrnoRestorer() &&                                              \
+      ::android::base::LogMessage(__FILE__, __LINE__,                                \
+          ::android::base::dest,                                                     \
           ::android::base::severity, errno).stream()
 
 // Marker that code is yet to be implemented.
@@ -157,9 +153,7 @@
 //     CHECK(false == true) results in a log message of
 //       "Check failed: false == true".
 #define CHECK(x)                                                              \
-  if (LIKELY((x)))                                                            \
-    ;                                                                         \
-  else                                                                        \
+  LIKELY((x)) ||                                                              \
     ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \
                                 ::android::base::FATAL, -1).stream()          \
         << "Check failed: " #x << " "
diff --git a/base/include/android-base/thread_annotations.h b/base/include/android-base/thread_annotations.h
new file mode 100644
index 0000000..90979df
--- /dev/null
+++ b/base/include/android-base/thread_annotations.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef UTILS_THREAD_ANNOTATIONS_H
+#define UTILS_THREAD_ANNOTATIONS_H
+
+#if defined(__SUPPORT_TS_ANNOTATION__) || defined(__clang__)
+#define THREAD_ANNOTATION_ATTRIBUTE__(x)   __attribute__((x))
+#else
+#define THREAD_ANNOTATION_ATTRIBUTE__(x)   // no-op
+#endif
+
+#define CAPABILITY(x) \
+      THREAD_ANNOTATION_ATTRIBUTE__(capability(x))
+
+#define SCOPED_CAPABILITY \
+      THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
+
+#define GUARDED_BY(x) \
+      THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
+
+#define PT_GUARDED_BY(x) \
+      THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))
+
+#define ACQUIRED_BEFORE(...) \
+      THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__))
+
+#define ACQUIRED_AFTER(...) \
+      THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__))
+
+#define REQUIRES(...) \
+      THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__))
+
+#define REQUIRES_SHARED(...) \
+      THREAD_ANNOTATION_ATTRIBUTE__(requires_shared_capability(__VA_ARGS__))
+
+#define ACQUIRE(...) \
+      THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__))
+
+#define ACQUIRE_SHARED(...) \
+      THREAD_ANNOTATION_ATTRIBUTE__(acquire_shared_capability(__VA_ARGS__))
+
+#define RELEASE(...) \
+      THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__))
+
+#define RELEASE_SHARED(...) \
+      THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__))
+
+#define TRY_ACQUIRE(...) \
+      THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_capability(__VA_ARGS__))
+
+#define TRY_ACQUIRE_SHARED(...) \
+      THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(__VA_ARGS__))
+
+#define EXCLUDES(...) \
+      THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))
+
+#define ASSERT_CAPABILITY(x) \
+      THREAD_ANNOTATION_ATTRIBUTE__(assert_capability(x))
+
+#define ASSERT_SHARED_CAPABILITY(x) \
+      THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_capability(x))
+
+#define RETURN_CAPABILITY(x) \
+      THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
+
+#define NO_THREAD_SAFETY_ANALYSIS \
+      THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
+
+#endif  // UTILS_THREAD_ANNOTATIONS_H
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 360447f..fa6f594 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -184,13 +184,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
@@ -207,6 +217,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/debuggerd/signal_sender.cpp b/debuggerd/signal_sender.cpp
index 1cfb704..4be7e6e 100644
--- a/debuggerd/signal_sender.cpp
+++ b/debuggerd/signal_sender.cpp
@@ -16,6 +16,7 @@
 
 #include <errno.h>
 #include <signal.h>
+#include <stdlib.h>
 #include <string.h>
 #include <sys/socket.h>
 #include <sys/syscall.h>
@@ -35,6 +36,31 @@
   int signal;
 };
 
+static void set_signal_sender_process_name() {
+#if defined(__LP64__)
+  static constexpr char long_process_name[] = "debuggerd64:signaller";
+  static constexpr char short_process_name[] = "debuggerd64:sig";
+  static_assert(sizeof(long_process_name) <= sizeof("/system/bin/debuggerd64"), "");
+#else
+  static constexpr char long_process_name[] = "debuggerd:signaller";
+  static constexpr char short_process_name[] = "debuggerd:sig";
+  static_assert(sizeof(long_process_name) <= sizeof("/system/bin/debuggerd"), "");
+#endif
+
+  // pthread_setname_np has a maximum length of 16 chars, including null terminator.
+  static_assert(sizeof(short_process_name) <= 16, "");
+  pthread_setname_np(pthread_self(), short_process_name);
+
+  char* progname = const_cast<char*>(getprogname());
+  if (strlen(progname) <= strlen(long_process_name)) {
+    ALOGE("debuggerd: unexpected progname %s", progname);
+    return;
+  }
+
+  memset(progname, 0, strlen(progname));
+  strcpy(progname, long_process_name);
+}
+
 // Fork a process to send signals for the worker processes to use after they've dropped privileges.
 bool start_signal_sender() {
   if (signal_pid != 0) {
@@ -56,6 +82,8 @@
   } else if (fork_pid == 0) {
     close(sfd[1]);
 
+    set_signal_sender_process_name();
+
     while (true) {
       signal_message msg;
       int rc = TEMP_FAILURE_RETRY(read(sfd[0], &msg, sizeof(msg)));
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 2e72832..30d648b 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -21,6 +21,7 @@
 #include <mntent.h>
 #include <net/if.h>
 #include <signal.h>
+#include <sched.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -127,6 +128,16 @@
     ServiceManager::GetInstance().ForEachService([] (Service* s) { s->Stop(); });
     TEMP_FAILURE_RETRY(kill(-1, SIGKILL));
 
+    // Restart Watchdogd to allow us to complete umounting and fsck
+    Service *svc = ServiceManager::GetInstance().FindServiceByName("watchdogd");
+    if (svc) {
+        do {
+            sched_yield(); // do not be so eager, let cleanup have priority
+            ServiceManager::GetInstance().ReapAnyOutstandingChildren();
+        } while (svc->flags() & SVC_RUNNING); // Paranoid Cargo
+        svc->Start();
+    }
+
     int count = 0;
     while (count++ < UNMOUNT_CHECK_TIMES) {
         int fd = TEMP_FAILURE_RETRY(open(entry->mnt_fsname, O_RDONLY | O_EXCL));
@@ -147,6 +158,11 @@
         }
     }
 
+    // NB: With watchdog still running, there is no cap on the time it takes
+    // to complete the fsck, from the users perspective the device graphics
+    // and responses are locked-up and they may choose to hold the power
+    // button in frustration if it drags out.
+
     int st;
     if (!strcmp(entry->mnt_type, "f2fs")) {
         const char *f2fs_argv[] = {
diff --git a/libbacktrace/Android.mk b/libbacktrace/Android.mk
index ee56a5e..d5a7e06 100644
--- a/libbacktrace/Android.mk
+++ b/libbacktrace/Android.mk
@@ -86,18 +86,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 \
@@ -106,10 +122,6 @@
 	libLLVMSupport \
 
 module := libbacktrace_offline
-module_tag := optional
-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
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/Android.bp b/liblog/Android.bp
index 9e8491b..9c68fca 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -84,6 +84,7 @@
         //       $(LOCAL_PATH)/event.logtags)
         // so make sure we do not regret hard-coding it as follows:
         "-DLIBLOG_LOG_TAG=1005",
+        "-DSNET_EVENT_LOG_TAG=1397638484",
     ],
     compile_multilib: "both",
     stl: "none",
diff --git a/liblog/Android.mk b/liblog/Android.mk
index 01c8e77..b24b489 100644
--- a/liblog/Android.mk
+++ b/liblog/Android.mk
@@ -23,6 +23,7 @@
 #       $(LOCAL_PATH)/event.logtags)
 # so make sure we do not regret hard-coding it as follows:
 liblog_cflags := -DLIBLOG_LOG_TAG=1005
+liblog_cflags += -DSNET_EVENT_LOG_TAG=1397638484
 
 liblog_sources := log_event_list.c log_event_write.c logger_write.c
 liblog_sources += config_write.c logger_name.c logger_lock.c
diff --git a/liblog/log_is_loggable.c b/liblog/log_is_loggable.c
index 551fa76..79a5670 100644
--- a/liblog/log_is_loggable.c
+++ b/liblog/log_is_loggable.c
@@ -259,6 +259,37 @@
     return logLevel >= 0 && prio >= logLevel;
 }
 
+LIBLOG_HIDDEN int __android_log_is_debuggable()
+{
+    static uint32_t serial;
+    static struct cache tag_cache;
+    static const char key[] = "ro.debuggable";
+    int ret;
+
+    if (tag_cache.c) { /* ro property does not change after set */
+        ret = tag_cache.c == '1';
+    } else if (lock()) {
+        struct cache temp_cache = { NULL, -1, '\0' };
+        refresh_cache(&temp_cache, key);
+        ret = temp_cache.c == '1';
+    } else {
+        int change_detected = check_cache(&tag_cache);
+        uint32_t current_serial = __system_property_area_serial();
+        if (current_serial != serial) {
+            change_detected = 1;
+        }
+        if (change_detected) {
+            refresh_cache(&tag_cache, key);
+            serial = current_serial;
+        }
+        ret = tag_cache.c == '1';
+
+        unlock();
+    }
+
+    return ret;
+}
+
 /*
  * For properties that are read often, but generally remain constant.
  * Since a change is rare, we will accept a trylock failure gracefully.
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..c727f29 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);
@@ -153,6 +154,7 @@
 LIBLOG_HIDDEN void __android_log_lock();
 LIBLOG_HIDDEN int __android_log_trylock();
 LIBLOG_HIDDEN void __android_log_unlock();
+LIBLOG_HIDDEN int __android_log_is_debuggable();
 
 __END_DECLS
 
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/pmsg_writer.c b/liblog/pmsg_writer.c
index 7034ceb..9cd3c48 100644
--- a/liblog/pmsg_writer.c
+++ b/liblog/pmsg_writer.c
@@ -73,6 +73,11 @@
     if (logId > LOG_ID_SECURITY) {
         return -EINVAL;
     }
+    if ((logId != LOG_ID_SECURITY) &&
+            (logId != LOG_ID_EVENTS) &&
+            !__android_log_is_debuggable()) {
+        return -EINVAL;
+    }
     if (pmsgLoggerWrite.context.fd < 0) {
         if (access("/dev/pmsg0", W_OK) == 0) {
             return 0;
@@ -82,6 +87,14 @@
     return 1;
 }
 
+/*
+ * Extract a 4-byte value from a byte stream.
+ */
+static inline uint32_t get4LE(const uint8_t* src)
+{
+    return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+}
+
 static int pmsgWrite(log_id_t logId, struct timespec *ts,
                       struct iovec *vec, size_t nr)
 {
@@ -92,6 +105,16 @@
     size_t i, payloadSize;
     ssize_t ret;
 
+    if ((logId == LOG_ID_EVENTS) && !__android_log_is_debuggable()) {
+        if (vec[0].iov_len < 4) {
+            return -EINVAL;
+        }
+
+        if (SNET_EVENT_LOG_TAG != get4LE(vec[0].iov_base)) {
+            return -EPERM;
+        }
+    }
+
     if (pmsgLoggerWrite.context.fd < 0) {
         return -EBADF;
     }
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index e6b37ed..ec7a941 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -21,6 +21,7 @@
 #ifdef __ANDROID__
 #include <android/dlext.h>
 #include "cutils/properties.h"
+#define LOG_TAG "libnativeloader"
 #include "log/log.h"
 #endif
 
@@ -38,6 +39,10 @@
 #if defined(__ANDROID__)
 static constexpr const char* kPublicNativeLibrariesConfig = "/system/etc/public.libraries.txt";
 
+static bool namespace_workaround_enabled(int32_t target_sdk_version) {
+  return target_sdk_version <= 23;
+}
+
 class LibraryNamespaces {
  public:
   LibraryNamespaces() : initialized_(false) { }
@@ -54,6 +59,14 @@
     if (java_permitted_path != nullptr) {
       ScopedUtfChars path(env, java_permitted_path);
       permitted_path = path.c_str();
+    } else {
+      // (http://b/27588281) This is a workaround for apps using custom
+      // classloaders and calling System.load() with an absolute path which
+      // is outside of the classloader library search path.
+      //
+      // This part effectively allows such a classloader to access anything
+      // under /data
+      permitted_path = "/data";
     }
 
     if (!initialized_ && !InitPublicNamespace(library_path.c_str(), target_sdk_version)) {
@@ -74,7 +87,7 @@
                                   nullptr,
                                   library_path.c_str(),
                                   namespace_type,
-                                  java_permitted_path != nullptr ?
+                                  !permitted_path.empty() ?
                                       permitted_path.c_str() :
                                       nullptr);
 
@@ -128,7 +141,7 @@
 
     // TODO (dimitry): This is a workaround for http://b/26436837
     // will be removed before the release.
-    if (target_sdk_version <= 23) {
+    if (namespace_workaround_enabled(target_sdk_version)) {
       // check if libart.so is loaded.
       void* handle = dlopen("libart.so", RTLD_NOW | RTLD_NOLOAD);
       if (handle != nullptr) {
diff --git a/libnetutils/ifc_utils.c b/libnetutils/ifc_utils.c
index 85ff070..eae32ce 100644
--- a/libnetutils/ifc_utils.c
+++ b/libnetutils/ifc_utils.c
@@ -267,10 +267,12 @@
     struct {
         struct nlmsghdr n;
         struct ifaddrmsg r;
-        // Allow for IPv6 address, headers, and padding.
+        // Allow for IPv6 address, headers, IPv4 broadcast addr and padding.
         char attrbuf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
                      NLMSG_ALIGN(sizeof(struct rtattr)) +
-                     NLMSG_ALIGN(INET6_ADDRLEN)];
+                     NLMSG_ALIGN(INET6_ADDRLEN) +
+                     NLMSG_ALIGN(sizeof(struct rtattr)) +
+                     NLMSG_ALIGN(INET_ADDRLEN)];
     } req;
     struct rtattr *rta;
     struct nlmsghdr *nh;
@@ -325,6 +327,16 @@
     req.n.nlmsg_len = NLMSG_ALIGN(req.n.nlmsg_len) + RTA_LENGTH(addrlen);
     memcpy(RTA_DATA(rta), addr, addrlen);
 
+    // Add an explicit IFA_BROADCAST for IPv4 RTM_NEWADDRs.
+    if (ss.ss_family == AF_INET && action == RTM_NEWADDR) {
+        rta = (struct rtattr *) (((char *) &req) + NLMSG_ALIGN(req.n.nlmsg_len));
+        rta->rta_type = IFA_BROADCAST;
+        rta->rta_len = RTA_LENGTH(addrlen);
+        req.n.nlmsg_len = NLMSG_ALIGN(req.n.nlmsg_len) + RTA_LENGTH(addrlen);
+        ((struct in_addr *)addr)->s_addr |= htonl((1<<(32-prefixlen))-1);
+        memcpy(RTA_DATA(rta), addr, addrlen);
+    }
+
     s = socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
     if (s < 0) {
         return -errno;
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 ddc91ca..37f4f4f 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -37,6 +37,8 @@
 #include <log/logprint.h>
 #include <utils/threads.h>
 
+#include <pcrecpp.h>
+
 #define DEFAULT_MAX_ROTATED_LOGS 4
 
 static AndroidLogFormat * g_logformat;
@@ -67,16 +69,22 @@
 
 /* Global Variables */
 
-static const char * g_outputFileName = NULL;
+static const char * g_outputFileName;
 // 0 means "no log rotation"
-static size_t g_logRotateSizeKBytes = 0;
+static size_t g_logRotateSizeKBytes;
 // 0 means "unbounded"
 static size_t g_maxRotatedLogs = DEFAULT_MAX_ROTATED_LOGS;
 static int g_outFD = -1;
-static size_t g_outByteCount = 0;
-static int g_printBinary = 0;
-static int g_devCount = 0;                              // >1 means multiple
+static size_t g_outByteCount;
+static int g_printBinary;
+static int g_devCount;                              // >1 means multiple
+static pcrecpp::RE* g_regex;
+// 0 means "infinite"
+static size_t g_maxCount;
+static size_t g_printCount;
+static bool g_printItAnyways;
 
+// if showHelp is set, newline required in fmt statement to transition to usage
 __noreturn static void logcat_panic(bool showHelp, const char *fmt, ...) __printflike(2,3);
 
 static int openLogFile (const char *pathname)
@@ -143,6 +151,17 @@
     TEMP_FAILURE_RETRY(write(g_outFD, buf, size));
 }
 
+static bool regexOk(const AndroidLogEntry& entry)
+{
+    if (!g_regex) {
+        return true;
+    }
+
+    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;
@@ -172,10 +191,15 @@
     }
 
     if (android_log_shouldPrintLine(g_logformat, entry.tag, entry.priority)) {
-        bytesWritten = android_log_printLogLine(g_logformat, g_outFD, &entry);
+        bool match = regexOk(entry);
 
-        if (bytesWritten < 0) {
-            logcat_panic(false, "output error");
+        g_printCount += match;
+        if (match || g_printItAnyways) {
+            bytesWritten = android_log_printLogLine(g_logformat, g_outFD, &entry);
+
+            if (bytesWritten < 0) {
+                logcat_panic(false, "output error");
+            }
         }
     }
 
@@ -259,9 +283,9 @@
                     "  -f <filename>   Log to file. Default is stdout\n"
                     "  --file=<filename>\n"
                     "  -r <kbytes>     Rotate log every kbytes. Requires -f\n"
-                    "  --rotate_kbytes=<kbytes>\n"
+                    "  --rotate-kbytes=<kbytes>\n"
                     "  -n <count>      Sets max number of rotated logs to <count>, default 4\n"
-                    "  --rotate_count=<count>\n"
+                    "  --rotate-count=<count>\n"
                     "  -v <format>     Sets the log print format, where <format> is:\n"
                     "  --format=<format>\n"
                     "                      brief color epoch long monotonic printable process raw\n"
@@ -271,6 +295,12 @@
                     "  -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"
+                    "  --print         paired with --regex and --max-count to let content bypass\n"
+                    "                  regex filter but still stop at number of matches.\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"
@@ -278,9 +308,9 @@
                     "                  count is pure numerical, time is 'MM-DD hh:mm:ss.mmm...'\n"
                     "                  'YYYY-MM-DD hh:mm:ss.mmm...' or 'sssss.mmm...' format\n"
                     "  -g              get the size of the log's ring buffer and exit\n"
-                    "  --buffer_size\n"
+                    "  --buffer-size\n"
                     "  -G <size>       set size of log ring buffer, may suffix with K or M.\n"
-                    "  --buffer_size=<size>\n"
+                    "  --buffer-size=<size>\n"
                     "  -L              dump logs from prior to last reboot\n"
                     "  --last\n"
                     // Leave security (Device Owner only installations) and
@@ -421,10 +451,6 @@
         return retval;
     }
 
-    clockid_t clock_type = android_log_clockid();
-    log_time now(clock_type);
-    bool monotonic = clock_type == CLOCK_MONOTONIC;
-
     std::string directory;
     char *file = strrchr(outputFileName, '/');
     if (!file) {
@@ -436,10 +462,21 @@
         *file = '/';
         ++file;
     }
+
+    std::unique_ptr<DIR, int(*)(DIR*)>
+            dir(opendir(directory.c_str()), closedir);
+    if (!dir.get()) {
+        return retval;
+    }
+
+    clockid_t clock_type = android_log_clockid();
+    log_time now(clock_type);
+    bool monotonic = clock_type == CLOCK_MONOTONIC;
+
     size_t len = strlen(file);
     log_time modulo(0, NS_PER_SEC);
-    std::unique_ptr<DIR, int(*)(DIR*)>dir(opendir(directory.c_str()), closedir);
     struct dirent *dp;
+
     while ((dp = readdir(dir.get())) != NULL) {
         if ((dp->d_type != DT_REG)
                 // If we are using realtime, check all files that match the
@@ -521,6 +558,7 @@
     size_t tail_lines = 0;
     log_time tail_time(log_time::EPOCH);
     size_t pid = 0;
+    bool got_t = false;
 
     signal(SIGPIPE, exit);
 
@@ -535,28 +573,39 @@
         int ret;
 
         int option_index = 0;
+        // list of long-argument only strings for later comparison
         static const char pid_str[] = "pid";
         static const char wrap_str[] = "wrap";
+        static const char print_str[] = "print";
         static const struct option long_options[] = {
           { "binary",        no_argument,       NULL,   'B' },
           { "buffer",        required_argument, NULL,   'b' },
-          { "buffer_size",   optional_argument, NULL,   'g' },
+          { "buffer-size",   optional_argument, NULL,   'g' },
           { "clear",         no_argument,       NULL,   'c' },
           { "dividers",      no_argument,       NULL,   'D' },
           { "file",          required_argument, NULL,   'f' },
           { "format",        required_argument, NULL,   'v' },
+          // hidden and undocumented reserved alias for --regex
+          { "grep",          required_argument, NULL,   'e' },
+          // hidden and undocumented reserved alias for --max-count
+          { "head",          required_argument, NULL,   'm' },
           { "last",          no_argument,       NULL,   'L' },
+          { "max-count",     required_argument, NULL,   'm' },
           { pid_str,         required_argument, NULL,   0 },
+          { print_str,       no_argument,       NULL,   0 },
           { "prune",         optional_argument, NULL,   'p' },
-          { "rotate_count",  required_argument, NULL,   'n' },
-          { "rotate_kbytes", required_argument, NULL,   'r' },
+          { "regex",         required_argument, NULL,   'e' },
+          { "rotate-count",  required_argument, NULL,   'n' },
+          { "rotate-kbytes", required_argument, NULL,   'r' },
           { "statistics",    no_argument,       NULL,   'S' },
+          // hidden and undocumented reserved alias for -t
+          { "tail",          required_argument, NULL,   't' },
           // support, but ignore and do not document, the optional argument
           { wrap_str,        optional_argument, NULL,   0 },
           { 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) {
@@ -592,6 +641,10 @@
                     }
                     break;
                 }
+                if (long_options[option_index].name == print_str) {
+                    g_printItAnyways = true;
+                    break;
+                }
             break;
 
             case 's':
@@ -613,6 +666,7 @@
             break;
 
             case 't':
+                got_t = true;
                 mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
                 /* FALLTHRU */
             case 'T':
@@ -644,6 +698,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 +987,19 @@
         }
     }
 
+    if (g_maxCount && got_t) {
+        logcat_panic(true, "Cannot use -m (--max-count) and -t together\n");
+    }
+    if (g_printItAnyways && (!g_regex || !g_maxCount)) {
+        // One day it would be nice if --print -v color and --regex <expr>
+        // could play with each other and show regex highlighted content.
+        fprintf(stderr, "WARNING: "
+                            "--print ignored, to be used in combination with\n"
+                        "         "
+                            "--regex <expr> and --max-count <N>\n");
+        g_printItAnyways = false;
+    }
+
     if (!devices) {
         dev = devices = new log_device_t("main", false);
         g_devCount = 1;
@@ -1135,7 +1215,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 4f517bb..921a461 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -21,6 +21,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
+#include <sys/wait.h>
 
 #include <gtest/gtest.h>
 #include <log/log.h>
@@ -781,8 +782,15 @@
     EXPECT_FALSE(system(command));
 }
 
-static void caught_blocking_clear(int /*signum*/)
-{
+TEST(logcat, logrotate_nodir) {
+    // expect logcat to error out on writing content and exit(1) for nodir
+    EXPECT_EQ(W_EXITCODE(1, 0),
+              system("logcat -b all -d"
+                     " -f /das/nein/gerfingerpoken/logcat/log.txt"
+                     " -n 256 -r 1024"));
+}
+
+static void caught_blocking_clear(int /*signum*/) {
     unsigned long long v = 0xDEADBEEFA55C0000ULL;
 
     v += getpid() & 0xFFFF;
@@ -953,3 +961,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