Merge "Add --sync support to push."
diff --git a/debuggerd/include/debuggerd/protocol.h b/debuggerd/include/debuggerd/protocol.h
index 0756876..144efc8 100644
--- a/debuggerd/include/debuggerd/protocol.h
+++ b/debuggerd/include/debuggerd/protocol.h
@@ -21,6 +21,7 @@
 // Sockets in the ANDROID_SOCKET_NAMESPACE_RESERVED namespace.
 // Both sockets are SOCK_SEQPACKET sockets, so no explicit length field is needed.
 constexpr char kTombstonedCrashSocketName[] = "tombstoned_crash";
+constexpr char kTombstonedJavaTraceSocketName[] = "tombstoned_java_trace";
 constexpr char kTombstonedInterceptSocketName[] = "tombstoned_intercept";
 
 enum class CrashPacketType : uint8_t {
diff --git a/debuggerd/include/debuggerd/tombstoned.h b/debuggerd/include/debuggerd/tombstoned.h
index d158d50..908517d 100644
--- a/debuggerd/include/debuggerd/tombstoned.h
+++ b/debuggerd/include/debuggerd/tombstoned.h
@@ -21,6 +21,6 @@
 #include <android-base/unique_fd.h>
 
 bool tombstoned_connect(pid_t pid, android::base::unique_fd* tombstoned_socket,
-                        android::base::unique_fd* output_fd);
+                        android::base::unique_fd* output_fd, bool is_native_crash = true);
 
 bool tombstoned_notify_completion(int tombstoned_socket);
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index 2248a21..80dbef5 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -50,86 +50,154 @@
   kCrashStatusQueued,
 };
 
+struct Crash;
+
+class CrashType {
+ public:
+  CrashType(const std::string& dir_path, const std::string& file_name_prefix, size_t max_artifacts,
+            size_t max_concurrent_dumps)
+      : file_name_prefix_(file_name_prefix),
+        dir_path_(dir_path),
+        dir_fd_(open(dir_path.c_str(), O_DIRECTORY | O_RDONLY | O_CLOEXEC)),
+        max_artifacts_(max_artifacts),
+        next_artifact_(0),
+        max_concurrent_dumps_(max_concurrent_dumps),
+        num_concurrent_dumps_(0) {
+    if (dir_fd_ == -1) {
+      PLOG(FATAL) << "failed to open directory: " << dir_path;
+    }
+
+    // NOTE: If max_artifacts_ <= max_concurrent_dumps_, then theoretically the
+    // same filename could be handed out to multiple processes.
+    CHECK(max_artifacts_ > max_concurrent_dumps_);
+
+    find_oldest_artifact();
+  }
+
+  unique_fd get_output_fd() {
+    unique_fd result;
+    char buf[PATH_MAX];
+    snprintf(buf, sizeof(buf), "%s%02d", file_name_prefix_.c_str(), next_artifact_);
+    // Unlink and create the file, instead of using O_TRUNC, to avoid two processes
+    // interleaving their output in case we ever get into that situation.
+    if (unlinkat(dir_fd_, buf, 0) != 0 && errno != ENOENT) {
+      PLOG(FATAL) << "failed to unlink tombstone at " << dir_path_ << buf;
+    }
+
+    result.reset(openat(dir_fd_, buf, O_CREAT | O_EXCL | O_WRONLY | O_APPEND | O_CLOEXEC, 0640));
+    if (result == -1) {
+      PLOG(FATAL) << "failed to create tombstone at " << dir_path_ << buf;
+    }
+
+    next_artifact_ = (next_artifact_ + 1) % max_artifacts_;
+    return result;
+  }
+
+  bool maybe_enqueue_crash(Crash* crash) {
+    if (num_concurrent_dumps_ == max_concurrent_dumps_) {
+      queued_requests_.push_back(crash);
+      return true;
+    }
+
+    return false;
+  }
+
+  void maybe_dequeue_crashes(void (*handler)(Crash* crash)) {
+    while (!queued_requests_.empty() && num_concurrent_dumps_ < max_concurrent_dumps_) {
+      Crash* next_crash = queued_requests_.front();
+      queued_requests_.pop_front();
+      handler(next_crash);
+    }
+  }
+
+  void on_crash_started() { ++num_concurrent_dumps_; }
+
+  void on_crash_completed() { --num_concurrent_dumps_; }
+
+  static CrashType* const tombstone;
+  static CrashType* const java_trace;
+
+ private:
+  void find_oldest_artifact() {
+    size_t oldest_tombstone = 0;
+    time_t oldest_time = std::numeric_limits<time_t>::max();
+
+    for (size_t i = 0; i < max_artifacts_; ++i) {
+      std::string path = android::base::StringPrintf("%s/%s%02zu", dir_path_.c_str(),
+                                                     file_name_prefix_.c_str(), i);
+      struct stat st;
+      if (stat(path.c_str(), &st) != 0) {
+        if (errno == ENOENT) {
+          oldest_tombstone = i;
+          break;
+        } else {
+          PLOG(ERROR) << "failed to stat " << path;
+          continue;
+        }
+      }
+
+      if (st.st_mtime < oldest_time) {
+        oldest_tombstone = i;
+        oldest_time = st.st_mtime;
+      }
+    }
+
+    next_artifact_ = oldest_tombstone;
+  }
+
+  const std::string file_name_prefix_;
+
+  const std::string dir_path_;
+  const int dir_fd_;
+
+  const size_t max_artifacts_;
+  int next_artifact_;
+
+  const size_t max_concurrent_dumps_;
+  size_t num_concurrent_dumps_;
+
+  std::deque<Crash*> queued_requests_;
+
+  DISALLOW_COPY_AND_ASSIGN(CrashType);
+};
+
+// Whether java trace dumps are produced via tombstoned.
+static constexpr bool kJavaTraceDumpsEnabled = false;
+
+/* static */ CrashType* const CrashType::tombstone =
+    new CrashType("/data/tombstones", "tombstone_" /* file_name_prefix */, 10 /* max_artifacts */,
+                  1 /* max_concurrent_dumps */);
+
+/* static */ CrashType* const CrashType::java_trace =
+    (kJavaTraceDumpsEnabled ? new CrashType("/data/anr", "anr_" /* file_name_prefix */,
+                                            64 /* max_artifacts */, 4 /* max_concurrent_dumps */)
+                            : nullptr);
+
 // Ownership of Crash is a bit messy.
 // It's either owned by an active event that must have a timeout, or owned by
 // queued_requests, in the case that multiple crashes come in at the same time.
 struct Crash {
-  ~Crash() {
-    event_free(crash_event);
-  }
+  ~Crash() { event_free(crash_event); }
 
   unique_fd crash_fd;
   pid_t crash_pid;
   event* crash_event = nullptr;
+
+  // Not owned by |Crash|.
+  CrashType* crash_type = nullptr;
 };
 
-static constexpr char kTombstoneDirectory[] = "/data/tombstones/";
-static constexpr size_t kTombstoneCount = 10;
-static int tombstone_directory_fd = -1;
-static int next_tombstone = 0;
-
-static constexpr size_t kMaxConcurrentDumps = 1;
-static size_t num_concurrent_dumps = 0;
-
-static std::deque<Crash*> queued_requests;
-
 // Forward declare the callbacks so they can be placed in a sensible order.
 static void crash_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, sockaddr*, int, void*);
 static void crash_request_cb(evutil_socket_t sockfd, short ev, void* arg);
 static void crash_completed_cb(evutil_socket_t sockfd, short ev, void* arg);
 
-static void find_oldest_tombstone() {
-  size_t oldest_tombstone = 0;
-  time_t oldest_time = std::numeric_limits<time_t>::max();
-
-  for (size_t i = 0; i < kTombstoneCount; ++i) {
-    std::string path = android::base::StringPrintf("%stombstone_%02zu", kTombstoneDirectory, i);
-    struct stat st;
-    if (stat(path.c_str(), &st) != 0) {
-      if (errno == ENOENT) {
-        oldest_tombstone = i;
-        break;
-      } else {
-        PLOG(ERROR) << "failed to stat " << path;
-        continue;
-      }
-    }
-
-    if (st.st_mtime < oldest_time) {
-      oldest_tombstone = i;
-      oldest_time = st.st_mtime;
-    }
-  }
-
-  next_tombstone = oldest_tombstone;
-}
-
-static unique_fd get_tombstone_fd() {
-  // If kMaxConcurrentDumps is greater than 1, then theoretically the same
-  // filename could be handed out to multiple processes. Unlink and create the
-  // file, instead of using O_TRUNC, to avoid two processes interleaving their
-  // output.
-  unique_fd result;
-  char buf[PATH_MAX];
-  snprintf(buf, sizeof(buf), "tombstone_%02d", next_tombstone);
-  if (unlinkat(tombstone_directory_fd, buf, 0) != 0 && errno != ENOENT) {
-    PLOG(FATAL) << "failed to unlink tombstone at " << kTombstoneDirectory << buf;
-  }
-
-  result.reset(
-    openat(tombstone_directory_fd, buf, O_CREAT | O_EXCL | O_WRONLY | O_APPEND | O_CLOEXEC, 0640));
-  if (result == -1) {
-    PLOG(FATAL) << "failed to create tombstone at " << kTombstoneDirectory << buf;
-  }
-
-  next_tombstone = (next_tombstone + 1) % kTombstoneCount;
-  return result;
-}
-
 static void perform_request(Crash* crash) {
   unique_fd output_fd;
-  if (!intercept_manager->GetIntercept(crash->crash_pid, &output_fd)) {
-    output_fd = get_tombstone_fd();
+  // Note that java traces are not interceptible.
+  if ((crash->crash_type == CrashType::java_trace) ||
+      !intercept_manager->GetIntercept(crash->crash_pid, &output_fd)) {
+    output_fd = crash->crash_type->get_output_fd();
   }
 
   TombstonedCrashPacket response = {
@@ -152,23 +220,15 @@
     event_add(crash->crash_event, &timeout);
   }
 
-  ++num_concurrent_dumps;
+  crash->crash_type->on_crash_started();
   return;
 
 fail:
   delete crash;
 }
 
-static void dequeue_requests() {
-  while (!queued_requests.empty() && num_concurrent_dumps < kMaxConcurrentDumps) {
-    Crash* next_crash = queued_requests.front();
-    queued_requests.pop_front();
-    perform_request(next_crash);
-  }
-}
-
 static void crash_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, sockaddr*, int,
-                            void*) {
+                            void* crash_type) {
   event_base* base = evconnlistener_get_base(listener);
   Crash* crash = new Crash();
 
@@ -176,12 +236,15 @@
   event* crash_event = event_new(base, sockfd, EV_TIMEOUT | EV_READ, crash_request_cb, crash);
   crash->crash_fd.reset(sockfd);
   crash->crash_event = crash_event;
+  crash->crash_type = static_cast<CrashType*>(crash_type);
   event_add(crash_event, &timeout);
 }
 
 static void crash_request_cb(evutil_socket_t sockfd, short ev, void* arg) {
   ssize_t rc;
   Crash* crash = static_cast<Crash*>(arg);
+  CrashType* type = crash->crash_type;
+
   TombstonedCrashPacket request = {};
 
   if ((ev & EV_TIMEOUT) != 0) {
@@ -208,12 +271,27 @@
     goto fail;
   }
 
-  crash->crash_pid = request.packet.dump_request.pid;
+  if (type == CrashType::tombstone) {
+    crash->crash_pid = request.packet.dump_request.pid;
+  } else {
+    // Requests for java traces are sent from untrusted processes, so we
+    // must not trust the PID sent down with the request. Instead, we ask the
+    // kernel.
+    ucred cr = {};
+    socklen_t len = sizeof(cr);
+    int ret = getsockopt(sockfd, SOL_SOCKET, SO_PEERCRED, &cr, &len);
+    if (ret != 0) {
+      PLOG(ERROR) << "Failed to getsockopt(..SO_PEERCRED)";
+      goto fail;
+    }
+
+    crash->crash_pid = cr.pid;
+  }
+
   LOG(INFO) << "received crash request for pid " << crash->crash_pid;
 
-  if (num_concurrent_dumps == kMaxConcurrentDumps) {
+  if (type->maybe_enqueue_crash(crash)) {
     LOG(INFO) << "enqueueing crash request for pid " << crash->crash_pid;
-    queued_requests.push_back(crash);
   } else {
     perform_request(crash);
   }
@@ -229,7 +307,7 @@
   Crash* crash = static_cast<Crash*>(arg);
   TombstonedCrashPacket request = {};
 
-  --num_concurrent_dumps;
+  crash->crash_type->on_crash_completed();
 
   if ((ev & EV_READ) == 0) {
     goto fail;
@@ -252,10 +330,11 @@
   }
 
 fail:
+  CrashType* type = crash->crash_type;
   delete crash;
 
   // If there's something queued up, let them proceed.
-  dequeue_requests();
+  type->maybe_dequeue_crashes(perform_request);
 }
 
 int main(int, char* []) {
@@ -269,13 +348,6 @@
   };
   debuggerd_register_handlers(&action);
 
-  tombstone_directory_fd = open(kTombstoneDirectory, O_DIRECTORY | O_RDONLY | O_CLOEXEC);
-  if (tombstone_directory_fd == -1) {
-    PLOG(FATAL) << "failed to open tombstone directory";
-  }
-
-  find_oldest_tombstone();
-
   int intercept_socket = android_get_control_socket(kTombstonedInterceptSocketName);
   int crash_socket = android_get_control_socket(kTombstonedCrashSocketName);
 
@@ -293,10 +365,24 @@
 
   intercept_manager = new InterceptManager(base, intercept_socket);
 
-  evconnlistener* listener =
-    evconnlistener_new(base, crash_accept_cb, nullptr, -1, LEV_OPT_CLOSE_ON_FREE, crash_socket);
-  if (!listener) {
-    LOG(FATAL) << "failed to create evconnlistener";
+  evconnlistener* tombstone_listener = evconnlistener_new(
+      base, crash_accept_cb, CrashType::tombstone, -1, LEV_OPT_CLOSE_ON_FREE, crash_socket);
+  if (!tombstone_listener) {
+    LOG(FATAL) << "failed to create evconnlistener for tombstones.";
+  }
+
+  if (kJavaTraceDumpsEnabled) {
+    const int java_trace_socket = android_get_control_socket(kTombstonedJavaTraceSocketName);
+    if (java_trace_socket == -1) {
+      PLOG(FATAL) << "failed to get socket from init";
+    }
+
+    evutil_make_socket_nonblocking(java_trace_socket);
+    evconnlistener* java_trace_listener = evconnlistener_new(
+        base, crash_accept_cb, CrashType::java_trace, -1, LEV_OPT_CLOSE_ON_FREE, java_trace_socket);
+    if (!java_trace_listener) {
+      LOG(FATAL) << "failed to create evconnlistener for java traces.";
+    }
   }
 
   LOG(INFO) << "tombstoned successfully initialized";
diff --git a/debuggerd/tombstoned/tombstoned.rc b/debuggerd/tombstoned/tombstoned.rc
index b8345ca..53ef01c 100644
--- a/debuggerd/tombstoned/tombstoned.rc
+++ b/debuggerd/tombstoned/tombstoned.rc
@@ -7,4 +7,5 @@
 
     socket tombstoned_crash seqpacket 0666 system system
     socket tombstoned_intercept seqpacket 0666 system system
+    socket tombstoned_java_trace seqpacket 0666 system system
     writepid /dev/cpuset/system-background/tasks
diff --git a/debuggerd/tombstoned_client.cpp b/debuggerd/tombstoned_client.cpp
index 4741fa6..e878b6a 100644
--- a/debuggerd/tombstoned_client.cpp
+++ b/debuggerd/tombstoned_client.cpp
@@ -30,9 +30,11 @@
 
 using android::base::unique_fd;
 
-bool tombstoned_connect(pid_t pid, unique_fd* tombstoned_socket, unique_fd* output_fd) {
-  unique_fd sockfd(socket_local_client(kTombstonedCrashSocketName,
-                                       ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
+bool tombstoned_connect(pid_t pid, unique_fd* tombstoned_socket, unique_fd* output_fd,
+                        bool is_native_crash) {
+  unique_fd sockfd(socket_local_client(
+      (is_native_crash ? kTombstonedCrashSocketName : kTombstonedJavaTraceSocketName),
+      ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
   if (sockfd == -1) {
     async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to connect to tombstoned: %s",
                           strerror(errno));
diff --git a/init/devices.cpp b/init/devices.cpp
index d8258cf..e0351a3 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -30,6 +30,7 @@
 #include <string.h>
 #include <sys/sendfile.h>
 #include <sys/socket.h>
+#include <sys/sysmacros.h>
 #include <sys/time.h>
 #include <sys/un.h>
 #include <sys/wait.h>
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 838406d..cdfc698 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -331,7 +331,7 @@
     // keep debugging tools until non critical ones are all gone.
     const std::set<std::string> kill_after_apps{"tombstoned", "logd", "adbd"};
     // watchdogd is a vendor specific component but should be alive to complete shutdown safely.
-    const std::set<std::string> to_starts{"watchdogd", "vold"};
+    const std::set<std::string> to_starts{"watchdogd", "vold", "ueventd"};
     ServiceManager::GetInstance().ForEachService([&kill_after_apps, &to_starts](Service* s) {
         if (kill_after_apps.count(s->name())) {
             s->SetShutdownCritical();
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index 2b3443f..919b65b 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -139,14 +139,8 @@
     { 00600, AID_ROOT,      AID_ROOT,      0, "odm/default.prop" },
     { 00444, AID_ROOT,      AID_ROOT,      0, odm_conf_dir + 1 },
     { 00444, AID_ROOT,      AID_ROOT,      0, odm_conf_file + 1 },
-    { 00600, AID_ROOT,      AID_ROOT,      0, "system/odm/build.prop" },
-    { 00600, AID_ROOT,      AID_ROOT,      0, "system/odm/default.prop" },
-    { 00444, AID_ROOT,      AID_ROOT,      0, "system/odm/etc/fs_config_dirs" },
-    { 00444, AID_ROOT,      AID_ROOT,      0, "system/odm/etc/fs_config_files" },
     { 00444, AID_ROOT,      AID_ROOT,      0, oem_conf_dir + 1 },
     { 00444, AID_ROOT,      AID_ROOT,      0, oem_conf_file + 1 },
-    { 00444, AID_ROOT,      AID_ROOT,      0, "system/oem/etc/fs_config_dirs" },
-    { 00444, AID_ROOT,      AID_ROOT,      0, "system/oem/etc/fs_config_files" },
     { 00750, AID_ROOT,      AID_SHELL,     0, "sbin/fs_mgr" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/bin/crash_dump32" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/bin/crash_dump64" },
@@ -163,10 +157,6 @@
     { 00555, AID_ROOT,      AID_ROOT,      0, "system/etc/ppp/*" },
     { 00555, AID_ROOT,      AID_ROOT,      0, "system/etc/rc.*" },
     { 00440, AID_ROOT,      AID_ROOT,      0, "system/etc/recovery.img" },
-    { 00600, AID_ROOT,      AID_ROOT,      0, "system/vendor/build.prop" },
-    { 00600, AID_ROOT,      AID_ROOT,      0, "system/vendor/default.prop" },
-    { 00444, AID_ROOT,      AID_ROOT,      0, "system/vendor/etc/fs_config_dirs" },
-    { 00444, AID_ROOT,      AID_ROOT,      0, "system/vendor/etc/fs_config_files" },
     { 00600, AID_ROOT,      AID_ROOT,      0, "vendor/build.prop" },
     { 00600, AID_ROOT,      AID_ROOT,      0, "vendor/default.prop" },
     { 00444, AID_ROOT,      AID_ROOT,      0, ven_conf_dir + 1 },
@@ -202,17 +192,11 @@
     // Support RT scheduling in Bluetooth
     { 00700, AID_BLUETOOTH, AID_BLUETOOTH, CAP_MASK_LONG(CAP_NET_ADMIN) |
                                            CAP_MASK_LONG(CAP_SYS_NICE),
-                                              "system/vendor/bin/hw/android.hardware.bluetooth@1.0-service" },
-    { 00700, AID_BLUETOOTH, AID_BLUETOOTH, CAP_MASK_LONG(CAP_NET_ADMIN) |
-                                           CAP_MASK_LONG(CAP_SYS_NICE),
                                               "vendor/bin/hw/android.hardware.bluetooth@1.0-service" },
 
     // Support wifi_hal_legacy administering a network interface.
     { 00755, AID_WIFI,      AID_WIFI,      CAP_MASK_LONG(CAP_NET_ADMIN) |
                                            CAP_MASK_LONG(CAP_NET_RAW),
-                                              "system/vendor/bin/hw/android.hardware.wifi@1.0-service" },
-    { 00755, AID_WIFI,      AID_WIFI,      CAP_MASK_LONG(CAP_NET_ADMIN) |
-                                           CAP_MASK_LONG(CAP_NET_RAW),
                                               "vendor/bin/hw/android.hardware.wifi@1.0-service" },
 
     // A non-privileged zygote that spawns isolated processes for web rendering.
@@ -233,8 +217,6 @@
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/bin/*" },
     { 00755, AID_ROOT,      AID_ROOT,      0, "system/lib/valgrind/*" },
     { 00755, AID_ROOT,      AID_ROOT,      0, "system/lib64/valgrind/*" },
-    { 00755, AID_ROOT,      AID_SHELL,     0, "system/vendor/bin/*" },
-    { 00755, AID_ROOT,      AID_SHELL,     0, "system/vendor/xbin/*" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/xbin/*" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "vendor/bin/*" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "vendor/xbin/*" },
@@ -273,6 +255,36 @@
     return fd;
 }
 
+// if path is "vendor/<stuff>", "oem/<stuff>" or "odm/<stuff>"
+static bool is_partition(const char* path, size_t len) {
+    static const char* partitions[] = {"vendor/", "oem/", "odm/"};
+    for (size_t i = 0; i < (sizeof(partitions) / sizeof(partitions[0])); ++i) {
+        size_t plen = strlen(partitions[i]);
+        if (len <= plen) continue;
+        if (!strncmp(path, partitions[i], plen)) return true;
+    }
+    return false;
+}
+
+// alias prefixes of "<partition>/<stuff>" to "system/<partition>/<stuff>" or
+// "system/<partition>/<stuff>" to "<partition>/<stuff>"
+static bool prefix_cmp(const char* prefix, const char* path, size_t len) {
+    if (!strncmp(prefix, path, len)) return true;
+
+    static const char system[] = "system/";
+    if (!strncmp(path, system, strlen(system))) {
+        path += strlen(system);
+    } else if (len <= strlen(system)) {
+        return false;
+    } else if (strncmp(prefix, system, strlen(system))) {
+        return false;
+    } else {
+        prefix += strlen(system);
+        len -= strlen(system);
+    }
+    return is_partition(prefix, len) && !strncmp(prefix, path, len);
+}
+
 static bool fs_config_cmp(bool dir, const char* prefix, size_t len, const char* path, size_t plen) {
     if (dir) {
         if (plen < len) {
@@ -281,13 +293,13 @@
     } else {
         // If name ends in * then allow partial matches.
         if (prefix[len - 1] == '*') {
-            return !strncmp(prefix, path, len - 1);
+            return prefix_cmp(prefix, path, len - 1);
         }
         if (plen != len) {
             return false;
         }
     }
-    return !strncmp(prefix, path, len);
+    return prefix_cmp(prefix, path, len);
 }
 
 void fs_config(const char* path, int dir, const char* target_out_path, unsigned* uid, unsigned* gid,
diff --git a/libcutils/tests/fs_config.cpp b/libcutils/tests/fs_config.cpp
index 3917a0b..a62cd51 100644
--- a/libcutils/tests/fs_config.cpp
+++ b/libcutils/tests/fs_config.cpp
@@ -14,63 +14,188 @@
  * limitations under the License.
  */
 
+#include <inttypes.h>
+
 #include <string>
 
 #include <gtest/gtest.h>
 
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 
 #include <private/android_filesystem_config.h>
+#include <private/fs_config.h>
 
-extern const struct fs_path_config* __for_testing_only__android_dirs;
-extern const struct fs_path_config* __for_testing_only__android_files;
+extern const fs_path_config* __for_testing_only__android_dirs;
+extern const fs_path_config* __for_testing_only__android_files;
 
-static void check_one(const struct fs_path_config* paths, const std::string& prefix,
-                      const std::string& alternate) {
-    for (size_t idx = 0; paths[idx].prefix; ++idx) {
-        std::string path(paths[idx].prefix);
-        if (android::base::StartsWith(path, prefix.c_str())) {
-            path = alternate + path.substr(prefix.length());
-            size_t second;
-            for (second = 0; paths[second].prefix; ++second) {
-                if (path == paths[second].prefix) break;
+// Maximum entries in system/core/libcutils/fs_config.cpp:android_* before we
+// hit a nullptr termination, before we declare the list is just too big or
+// could be missing the nullptr.
+static constexpr size_t max_idx = 4096;
+
+static bool check_unique(std::vector<const char*>& paths, const std::string& config_name,
+                         const std::string& prefix) {
+    bool retval = false;
+
+    std::string alternate = "system/" + prefix;
+
+    for (size_t idx = 0; idx < paths.size(); ++idx) {
+        size_t second;
+        std::string path(paths[idx]);
+        // check if there are multiple identical paths
+        for (second = idx + 1; second < paths.size(); ++second) {
+            if (path == paths[second]) {
+                GTEST_LOG_(ERROR) << "duplicate paths in " << config_name << ": " << paths[idx];
+                retval = true;
+                break;
             }
-            if (!paths[second].prefix) {
-                // guaranteed to fail expectations, trigger test failure with
-                // a message that reports the violation as an inequality.
-                EXPECT_STREQ((prefix + path.substr(alternate.length())).c_str(), path.c_str());
+        }
+
+        // check if path is <partition>/
+        if (android::base::StartsWith(path, prefix.c_str())) {
+            // rebuild path to be system/<partition>/... to check for alias
+            path = alternate + path.substr(prefix.size());
+            for (second = 0; second < paths.size(); ++second) {
+                if (path == paths[second]) {
+                    GTEST_LOG_(ERROR) << "duplicate alias paths in " << config_name << ": "
+                                      << paths[idx] << " and " << paths[second]
+                                      << " (remove latter)";
+                    retval = true;
+                    break;
+                }
+            }
+            continue;
+        }
+
+        // check if path is system/<partition>/
+        if (android::base::StartsWith(path, alternate.c_str())) {
+            // rebuild path to be <partition>/... to check for alias
+            path = prefix + path.substr(alternate.size());
+            for (second = 0; second < paths.size(); ++second) {
+                if (path == paths[second]) break;
+            }
+            if (second >= paths.size()) {
+                GTEST_LOG_(ERROR) << "replace path in " << config_name << ": " << paths[idx]
+                                  << " with " << path;
+                retval = true;
             }
         }
     }
+    return retval;
 }
 
-static void check_two(const struct fs_path_config* paths, const std::string& prefix) {
+static bool check_unique(const fs_path_config* paths, const char* type_name,
+                         const std::string& prefix) {
+    std::string config("system/core/libcutils/fs_config.cpp:android_");
+    config += type_name;
+    config += "[]";
+
+    bool retval = false;
+    std::vector<const char*> paths_tmp;
+    for (size_t idx = 0; paths[idx].prefix; ++idx) {
+        if (idx > max_idx) {
+            GTEST_LOG_(WARNING) << config << ": has no end (missing null prefix)";
+            retval = true;
+            break;
+        }
+        paths_tmp.push_back(paths[idx].prefix);
+    }
+
+    return check_unique(paths_tmp, config, prefix) || retval;
+}
+
+#define endof(pointer, field) (offsetof(typeof(*(pointer)), field) + sizeof((pointer)->field))
+
+static bool check_unique(const std::string& config, const std::string& prefix) {
+    int retval = false;
+
+    std::string data;
+    if (!android::base::ReadFileToString(config, &data)) return retval;
+
+    const fs_path_config_from_file* pc =
+        reinterpret_cast<const fs_path_config_from_file*>(data.c_str());
+    size_t len = data.size();
+
+    std::vector<const char*> paths_tmp;
+    size_t entry_number = 0;
+    while (len > 0) {
+        uint16_t host_len = (len >= endof(pc, len)) ? pc->len : INT16_MAX;
+        if (host_len > len) {
+            GTEST_LOG_(WARNING) << config << ": truncated at entry " << entry_number << " ("
+                                << host_len << " > " << len << ")";
+            const std::string unknown("?");
+            GTEST_LOG_(WARNING)
+                << config << ": entry[" << entry_number << "]={ "
+                << "len=" << ((len >= endof(pc, len))
+                                  ? android::base::StringPrintf("%" PRIu16, pc->len)
+                                  : unknown)
+                << ", mode=" << ((len >= endof(pc, mode))
+                                     ? android::base::StringPrintf("0%" PRIo16, pc->mode)
+                                     : unknown)
+                << ", uid=" << ((len >= endof(pc, uid))
+                                    ? android::base::StringPrintf("%" PRIu16, pc->uid)
+                                    : unknown)
+                << ", gid=" << ((len >= endof(pc, gid))
+                                    ? android::base::StringPrintf("%" PRIu16, pc->gid)
+                                    : unknown)
+                << ", capabilities="
+                << ((len >= endof(pc, capabilities))
+                        ? android::base::StringPrintf("0x%" PRIx64, pc->capabilities)
+                        : unknown)
+                << ", prefix="
+                << ((len >= offsetof(fs_path_config_from_file, prefix))
+                        ? android::base::StringPrintf(
+                              "\"%.*s...", (int)(len - offsetof(fs_path_config_from_file, prefix)),
+                              pc->prefix)
+                        : unknown)
+                << " }";
+            retval = true;
+            break;
+        }
+        paths_tmp.push_back(pc->prefix);
+
+        pc = reinterpret_cast<const fs_path_config_from_file*>(reinterpret_cast<const char*>(pc) +
+                                                               host_len);
+        len -= host_len;
+        ++entry_number;
+    }
+
+    return check_unique(paths_tmp, config, prefix) || retval;
+}
+
+void check_two(const fs_path_config* paths, const char* type_name, const char* prefix) {
     ASSERT_FALSE(paths == nullptr);
-    std::string alternate = "system/" + prefix;
-    check_one(paths, prefix, alternate);
-    check_one(paths, alternate, prefix);
+    ASSERT_FALSE(type_name == nullptr);
+    ASSERT_FALSE(prefix == nullptr);
+    bool check_internal = check_unique(paths, type_name, prefix);
+    EXPECT_FALSE(check_internal);
+    bool check_overrides =
+        check_unique(std::string("/") + prefix + "etc/fs_config_" + type_name, prefix);
+    EXPECT_FALSE(check_overrides);
 }
 
 TEST(fs_config, vendor_dirs_alias) {
-    check_two(__for_testing_only__android_dirs, "vendor/");
+    check_two(__for_testing_only__android_dirs, "dirs", "vendor/");
 }
 
 TEST(fs_config, vendor_files_alias) {
-    check_two(__for_testing_only__android_files, "vendor/");
+    check_two(__for_testing_only__android_files, "files", "vendor/");
 }
 
 TEST(fs_config, oem_dirs_alias) {
-    check_two(__for_testing_only__android_dirs, "oem/");
+    check_two(__for_testing_only__android_dirs, "dirs", "oem/");
 }
 
 TEST(fs_config, oem_files_alias) {
-    check_two(__for_testing_only__android_files, "oem/");
+    check_two(__for_testing_only__android_files, "files", "oem/");
 }
 
 TEST(fs_config, odm_dirs_alias) {
-    check_two(__for_testing_only__android_dirs, "odm/");
+    check_two(__for_testing_only__android_dirs, "dirs", "odm/");
 }
 
 TEST(fs_config, odm_files_alias) {
-    check_two(__for_testing_only__android_files, "odm/");
+    check_two(__for_testing_only__android_files, "files", "odm/");
 }
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index ded6c8c..76a4aff 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -79,10 +79,16 @@
             if (monotonic) {
                 if (!android::isMonotonic(e->mRealTime)) {
                     LogKlog::convertRealToMonotonic(e->mRealTime);
+                    if ((e->mRealTime.tv_nsec % 1000) == 0) {
+                        e->mRealTime.tv_nsec++;
+                    }
                 }
             } else {
                 if (android::isMonotonic(e->mRealTime)) {
                     LogKlog::convertMonotonicToReal(e->mRealTime);
+                    if ((e->mRealTime.tv_nsec % 1000) == 0) {
+                        e->mRealTime.tv_nsec++;
+                    }
                 }
             }
             ++it;
@@ -194,6 +200,11 @@
         return -EINVAL;
     }
 
+    // Slip the time by 1 nsec if the incoming lands on xxxxxx000 ns.
+    // This prevents any chance that an outside source can request an
+    // exact entry with time specified in ms or us precision.
+    if ((realtime.tv_nsec % 1000) == 0) ++realtime.tv_nsec;
+
     LogBufferElement* elem =
         new LogBufferElement(log_id, realtime, uid, pid, tid, msg, len);
     if (log_id != LOG_ID_SECURITY) {
@@ -1109,6 +1120,9 @@
             LogBufferElement* element = *it;
             if (element->getRealTime() > start) {
                 last = it;
+            } else if (element->getRealTime() == start) {
+                last = ++it;
+                break;
             } else if (!--count || (element->getRealTime() < min)) {
                 break;
             }
@@ -1116,7 +1130,7 @@
         it = last;
     }
 
-    log_time max = start;
+    log_time curr = start;
 
     LogBufferElement* lastElement = nullptr;  // iterator corruption paranoia
     static const size_t maxSkip = 4194304;    // maximum entries to skip
@@ -1142,10 +1156,6 @@
             continue;
         }
 
-        if (element->getRealTime() <= start) {
-            continue;
-        }
-
         // NB: calling out to another object with wrlock() held (safe)
         if (filter) {
             int ret = (*filter)(element, arg);
@@ -1172,10 +1182,10 @@
         unlock();
 
         // range locking in LastLogTimes looks after us
-        max = element->flushTo(reader, this, privileged, sameTid);
+        curr = element->flushTo(reader, this, privileged, sameTid);
 
-        if (max == element->FLUSH_ERROR) {
-            return max;
+        if (curr == element->FLUSH_ERROR) {
+            return curr;
         }
 
         skip = maxSkip;
@@ -1183,7 +1193,7 @@
     }
     unlock();
 
-    return max;
+    return curr;
 }
 
 std::string LogBuffer::formatStatistics(uid_t uid, pid_t pid,