Merge "init: Ignore "ro." restrictions when reading prop files"
diff --git a/TEST_MAPPING b/TEST_MAPPING
index cc85408..716378b 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -10,6 +10,9 @@
       "name": "fs_mgr_unit_test"
     },
     {
+      "name": "fs_mgr_vendor_overlay_test"
+    },
+    {
       "name": "init_tests"
     },
     {
diff --git a/adb/adb_io.cpp b/adb/adb_io.cpp
index 605d27d..91b0d1f 100644
--- a/adb/adb_io.cpp
+++ b/adb/adb_io.cpp
@@ -187,79 +187,3 @@
         return false;
     }
 }
-
-#if defined(__linux__)
-bool SendFileDescriptor(int socket_fd, int fd) {
-    struct msghdr msg;
-    struct iovec iov;
-    char dummy = '!';
-    union {
-        cmsghdr cm;
-        char buffer[CMSG_SPACE(sizeof(int))];
-    } cm_un;
-
-    iov.iov_base = &dummy;
-    iov.iov_len = 1;
-    msg.msg_name = nullptr;
-    msg.msg_namelen = 0;
-    msg.msg_iov = &iov;
-    msg.msg_iovlen = 1;
-    msg.msg_flags = 0;
-    msg.msg_control = cm_un.buffer;
-    msg.msg_controllen = sizeof(cm_un.buffer);
-
-    cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
-    cmsg->cmsg_len = CMSG_LEN(sizeof(int));
-    cmsg->cmsg_level = SOL_SOCKET;
-    cmsg->cmsg_type = SCM_RIGHTS;
-    ((int*)CMSG_DATA(cmsg))[0] = fd;
-
-    int ret = TEMP_FAILURE_RETRY(sendmsg(socket_fd, &msg, 0));
-    if (ret < 0) {
-        D("sending file descriptor via socket %d failed: %s", socket_fd, strerror(errno));
-        return false;
-    }
-
-    D("sent file descriptor %d to via socket %d", fd, socket_fd);
-    return true;
-}
-
-bool ReceiveFileDescriptor(int socket_fd, unique_fd* fd, std::string* error) {
-    char dummy = '!';
-    union {
-        cmsghdr cm;
-        char buffer[CMSG_SPACE(sizeof(int))];
-    } cm_un;
-
-    iovec iov;
-    iov.iov_base = &dummy;
-    iov.iov_len = 1;
-
-    msghdr msg;
-    msg.msg_name = nullptr;
-    msg.msg_namelen = 0;
-    msg.msg_iov = &iov;
-    msg.msg_iovlen = 1;
-    msg.msg_flags = 0;
-    msg.msg_control = cm_un.buffer;
-    msg.msg_controllen = sizeof(cm_un.buffer);
-
-    cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
-    cmsg->cmsg_len = CMSG_LEN(sizeof(int));
-    cmsg->cmsg_level = SOL_SOCKET;
-    cmsg->cmsg_type = SCM_RIGHTS;
-    ((int*)(CMSG_DATA(cmsg)))[0] = -1;
-
-    int rc = TEMP_FAILURE_RETRY(recvmsg(socket_fd, &msg, 0));
-    if (rc <= 0) {
-        *error = perror_str("receiving file descriptor via socket failed");
-        D("receiving file descriptor via socket %d failed: %s", socket_fd, strerror(errno));
-        return false;
-    }
-
-    fd->reset(((int*)(CMSG_DATA(cmsg)))[0]);
-    D("received file descriptor %d to via socket %d", fd->get(), socket_fd);
-
-    return true;
-}
-#endif
diff --git a/adb/adb_io.h b/adb/adb_io.h
index 2ccaa32..e2df1b1 100644
--- a/adb/adb_io.h
+++ b/adb/adb_io.h
@@ -74,13 +74,4 @@
 
 // Same as above, but formats the string to send.
 bool WriteFdFmt(int fd, const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3)));
-
-#if !ADB_HOST
-// Sends an FD via Unix domain socket.
-bool SendFileDescriptor(int socket_fd, int fd);
-
-// Receives an FD via Unix domain socket.
-bool ReceiveFileDescriptor(int socket_fd, unique_fd* fd, std::string* error);
-#endif
-
 #endif /* ADB_IO_H */
diff --git a/adb/daemon/abb.cpp b/adb/daemon/abb.cpp
index f69babe..d949dd1 100644
--- a/adb/daemon/abb.cpp
+++ b/adb/daemon/abb.cpp
@@ -22,6 +22,8 @@
 
 #include <sys/wait.h>
 
+#include <android-base/cmsg.h>
+
 namespace {
 
 class AdbFdTextOutput : public android::TextOutput {
@@ -83,8 +85,8 @@
             break;
         }
 
-        auto result = StartCommandInProcess(std::move(data), &execCmd);
-        if (!SendFileDescriptor(fd, result)) {
+        unique_fd result = StartCommandInProcess(std::move(data), &execCmd);
+        if (android::base::SendFileDescriptors(fd, "", 1, result.get()) != 1) {
             PLOG(ERROR) << "Failed to send an inprocess fd for command: " << data;
             break;
         }
diff --git a/adb/daemon/abb_service.cpp b/adb/daemon/abb_service.cpp
index 817aea1..d32bf52 100644
--- a/adb/daemon/abb_service.cpp
+++ b/adb/daemon/abb_service.cpp
@@ -20,6 +20,8 @@
 #include "adb_utils.h"
 #include "shell_service.h"
 
+#include <android-base/cmsg.h>
+
 namespace {
 
 struct AbbProcess;
@@ -59,8 +61,9 @@
 
         unique_fd fd;
         std::string error;
-        if (!ReceiveFileDescriptor(socket_fd_, &fd, &error)) {
-            LOG(ERROR) << "failed to receive FD from abb: " << error;
+        char buf;
+        if (android::base::ReceiveFileDescriptors(socket_fd_, &buf, 1, &fd) != 1) {
+            PLOG(ERROR) << "failed to receive FD from abb";
             socket_fd_.reset();
             continue;
         }
diff --git a/adb/daemon/jdwp_service.cpp b/adb/daemon/jdwp_service.cpp
index 032ee42..66bfc0d 100644
--- a/adb/daemon/jdwp_service.cpp
+++ b/adb/daemon/jdwp_service.cpp
@@ -32,6 +32,8 @@
 #include <memory>
 #include <vector>
 
+#include <android-base/cmsg.h>
+
 #include "adb.h"
 #include "adb_io.h"
 #include "adb_unique_fd.h"
@@ -237,7 +239,7 @@
         CHECK(!proc->out_fds.empty());
 
         int fd = proc->out_fds.back().get();
-        if (!SendFileDescriptor(socket, fd)) {
+        if (android::base::SendFileDescriptors(socket, "", 1, fd) != 1) {
             D("sending new file descriptor to JDWP %d failed: %s", proc->pid, strerror(errno));
             goto CloseProcess;
         }
diff --git a/adb/daemon/restart_service.cpp b/adb/daemon/restart_service.cpp
index 6803d93..16d2627 100644
--- a/adb/daemon/restart_service.cpp
+++ b/adb/daemon/restart_service.cpp
@@ -20,6 +20,7 @@
 
 #include <unistd.h>
 
+#include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <log/log_properties.h>
@@ -37,6 +38,7 @@
         return;
     }
 
+    LOG(INFO) << "adbd restarting as root";
     android::base::SetProperty("service.adb.root", "1");
     WriteFdExactly(fd.get(), "restarting adbd as root\n");
 }
@@ -46,6 +48,8 @@
         WriteFdExactly(fd.get(), "adbd not running as root\n");
         return;
     }
+
+    LOG(INFO) << "adbd restarting as nonroot";
     android::base::SetProperty("service.adb.root", "0");
     WriteFdExactly(fd.get(), "restarting adbd as non root\n");
 }
@@ -56,11 +60,13 @@
         return;
     }
 
+    LOG(INFO) << "adbd restarting in TCP mode (port = " << port << ")";
     android::base::SetProperty("service.adb.tcp.port", android::base::StringPrintf("%d", port));
     WriteFdFmt(fd.get(), "restarting in TCP mode port: %d\n", port);
 }
 
 void restart_usb_service(unique_fd fd) {
+    LOG(INFO) << "adbd restarting in USB mode";
     android::base::SetProperty("service.adb.tcp.port", "0");
     WriteFdExactly(fd.get(), "restarting in USB mode\n");
 }
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index f603d13..f0e2861 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -53,6 +53,9 @@
 
 using android::base::StringPrintf;
 
+// We can't find out whether we have support for AIO on ffs endpoints until we submit a read.
+static std::optional<bool> gFfsAioSupported;
+
 static constexpr size_t kUsbReadQueueDepth = 16;
 static constexpr size_t kUsbReadSize = 16384;
 
@@ -169,8 +172,13 @@
           read_fd_(std::move(read)),
           write_fd_(std::move(write)) {
         LOG(INFO) << "UsbFfsConnection constructed";
-        event_fd_.reset(eventfd(0, EFD_CLOEXEC));
-        if (event_fd_ == -1) {
+        worker_event_fd_.reset(eventfd(0, EFD_CLOEXEC));
+        if (worker_event_fd_ == -1) {
+            PLOG(FATAL) << "failed to create eventfd";
+        }
+
+        monitor_event_fd_.reset(eventfd(0, EFD_CLOEXEC));
+        if (monitor_event_fd_ == -1) {
             PLOG(FATAL) << "failed to create eventfd";
         }
 
@@ -181,6 +189,13 @@
         LOG(INFO) << "UsbFfsConnection being destroyed";
         Stop();
         monitor_thread_.join();
+
+        // We need to explicitly close our file descriptors before we notify our destruction,
+        // because the thread listening on the future will immediately try to reopen the endpoint.
+        control_fd_.reset();
+        read_fd_.reset();
+        write_fd_.reset();
+
         destruction_notifier_.set_value();
     }
 
@@ -207,11 +222,18 @@
         }
         stopped_ = true;
         uint64_t notify = 1;
-        ssize_t rc = adb_write(event_fd_.get(), &notify, sizeof(notify));
+        ssize_t rc = adb_write(worker_event_fd_.get(), &notify, sizeof(notify));
         if (rc < 0) {
-            PLOG(FATAL) << "failed to notify eventfd to stop UsbFfsConnection";
+            PLOG(FATAL) << "failed to notify worker eventfd to stop UsbFfsConnection";
         }
         CHECK_EQ(static_cast<size_t>(rc), sizeof(notify));
+
+        rc = adb_write(monitor_event_fd_.get(), &notify, sizeof(notify));
+        if (rc < 0) {
+            PLOG(FATAL) << "failed to notify monitor eventfd to stop UsbFfsConnection";
+        }
+
+        CHECK_EQ(static_cast<size_t>(rc), sizeof(notify));
     }
 
   private:
@@ -235,22 +257,33 @@
             bool started = false;
             bool running = true;
             while (running) {
+                int timeout = -1;
                 if (!bound || !started) {
-                    adb_pollfd pfd = {.fd = control_fd_.get(), .events = POLLIN, .revents = 0};
-                    int rc = TEMP_FAILURE_RETRY(adb_poll(&pfd, 1, 5000 /*ms*/));
-                    if (rc == -1) {
-                        PLOG(FATAL) << "poll on USB control fd failed";
-                    } else if (rc == 0) {
-                        // Something in the kernel presumably went wrong.
-                        // Close our endpoints, wait for a bit, and then try again.
-                        aio_context_.reset();
-                        read_fd_.reset();
-                        write_fd_.reset();
-                        control_fd_.reset();
-                        std::this_thread::sleep_for(5s);
-                        HandleError("didn't receive FUNCTIONFS_ENABLE, retrying");
-                        return;
-                    }
+                    timeout = 5000 /*ms*/;
+                }
+
+                adb_pollfd pfd[2] = {
+                  { .fd = control_fd_.get(), .events = POLLIN, .revents = 0 },
+                  { .fd = monitor_event_fd_.get(), .events = POLLIN, .revents = 0 },
+                };
+                int rc = TEMP_FAILURE_RETRY(adb_poll(pfd, 2, timeout));
+                if (rc == -1) {
+                    PLOG(FATAL) << "poll on USB control fd failed";
+                } else if (rc == 0) {
+                    // Something in the kernel presumably went wrong.
+                    // Close our endpoints, wait for a bit, and then try again.
+                    aio_context_.reset();
+                    read_fd_.reset();
+                    write_fd_.reset();
+                    control_fd_.reset();
+                    std::this_thread::sleep_for(5s);
+                    HandleError("didn't receive FUNCTIONFS_ENABLE, retrying");
+                    return;
+                }
+
+                if (pfd[1].revents) {
+                    // We were told to die.
+                    break;
                 }
 
                 struct usb_functionfs_event event;
@@ -313,12 +346,14 @@
             adb_thread_setname("UsbFfs-worker");
             for (size_t i = 0; i < kUsbReadQueueDepth; ++i) {
                 read_requests_[i] = CreateReadBlock(next_read_id_++);
-                SubmitRead(&read_requests_[i]);
+                if (!SubmitRead(&read_requests_[i])) {
+                    return;
+                }
             }
 
             while (!stopped_) {
                 uint64_t dummy;
-                ssize_t rc = adb_read(event_fd_.get(), &dummy, sizeof(dummy));
+                ssize_t rc = adb_read(worker_event_fd_.get(), &dummy, sizeof(dummy));
                 if (rc == -1) {
                     PLOG(FATAL) << "failed to read from eventfd";
                 } else if (rc == 0) {
@@ -347,7 +382,7 @@
         block.control.aio_fildes = read_fd_.get();
         block.control.aio_offset = 0;
         block.control.aio_flags = IOCB_FLAG_RESFD;
-        block.control.aio_resfd = event_fd_.get();
+        block.control.aio_resfd = worker_event_fd_.get();
         return block;
     }
 
@@ -438,13 +473,22 @@
         SubmitRead(block);
     }
 
-    void SubmitRead(IoBlock* block) {
+    bool SubmitRead(IoBlock* block) {
         block->pending = true;
         struct iocb* iocb = &block->control;
         if (io_submit(aio_context_.get(), 1, &iocb) != 1) {
+            if (errno == EINVAL && !gFfsAioSupported.has_value()) {
+                HandleError("failed to submit first read, AIO on FFS not supported");
+                gFfsAioSupported = false;
+                return false;
+            }
+
             HandleError(StringPrintf("failed to submit read: %s", strerror(errno)));
-            return;
+            return false;
         }
+
+        gFfsAioSupported = true;
+        return true;
     }
 
     void HandleWrite(TransferId id) {
@@ -474,7 +518,7 @@
         block->control.aio_nbytes = block->payload.size();
         block->control.aio_offset = 0;
         block->control.aio_flags = IOCB_FLAG_RESFD;
-        block->control.aio_resfd = event_fd_.get();
+        block->control.aio_resfd = worker_event_fd_.get();
         return block;
     }
 
@@ -526,7 +570,8 @@
     std::promise<void> destruction_notifier_;
     std::once_flag error_flag_;
 
-    unique_fd event_fd_;
+    unique_fd worker_event_fd_;
+    unique_fd monitor_event_fd_;
 
     ScopedAioContext aio_context_;
     unique_fd control_fd_;
@@ -551,10 +596,17 @@
     size_t writes_submitted_ GUARDED_BY(write_mutex_) = 0;
 };
 
+void usb_init_legacy();
+
 static void usb_ffs_open_thread() {
     adb_thread_setname("usb ffs open");
 
     while (true) {
+        if (gFfsAioSupported.has_value() && !gFfsAioSupported.value()) {
+            LOG(INFO) << "failed to use nonblocking ffs, falling back to legacy";
+            return usb_init_legacy();
+        }
+
         unique_fd control;
         unique_fd bulk_out;
         unique_fd bulk_in;
@@ -575,7 +627,6 @@
     }
 }
 
-void usb_init_legacy();
 void usb_init() {
     if (!android::base::GetBoolProperty("persist.adb.nonblocking_ffs", false)) {
         usb_init_legacy();
diff --git a/adb/transport.cpp b/adb/transport.cpp
index ae53597..90f94ee 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -52,6 +52,8 @@
 #include "fdevent.h"
 #include "sysdeps/chrono.h"
 
+using android::base::ScopedLockAssertion;
+
 static void remove_transport(atransport* transport);
 static void transport_unref(atransport* transport);
 
@@ -72,17 +74,6 @@
 
 namespace {
 
-// A class that helps the Clang Thread Safety Analysis deal with
-// std::unique_lock. Given that std::unique_lock is movable, and the analysis
-// can not currently perform alias analysis, it is not annotated. In order to
-// assert that the mutex is held, a ScopedAssumeLocked can be created just after
-// the std::unique_lock.
-class SCOPED_CAPABILITY ScopedAssumeLocked {
-  public:
-    ScopedAssumeLocked(std::mutex& mutex) ACQUIRE(mutex) {}
-    ~ScopedAssumeLocked() RELEASE() {}
-};
-
 #if ADB_HOST
 // Tracks and handles atransport*s that are attempting reconnection.
 class ReconnectHandler {
@@ -180,7 +171,7 @@
         ReconnectAttempt attempt;
         {
             std::unique_lock<std::mutex> lock(reconnect_mutex_);
-            ScopedAssumeLocked assume_lock(reconnect_mutex_);
+            ScopedLockAssertion assume_lock(reconnect_mutex_);
 
             if (!reconnect_queue_.empty()) {
                 // FIXME: libstdc++ (used on Windows) implements condition_variable with
@@ -296,7 +287,7 @@
         LOG(INFO) << this->transport_name_ << ": write thread spawning";
         while (true) {
             std::unique_lock<std::mutex> lock(mutex_);
-            ScopedAssumeLocked assume_locked(mutex_);
+            ScopedLockAssertion assume_locked(mutex_);
             cv_.wait(lock, [this]() REQUIRES(mutex_) {
                 return this->stopped_ || !this->write_queue_.empty();
             });
@@ -923,7 +914,7 @@
 
 bool ConnectionWaitable::WaitForConnection(std::chrono::milliseconds timeout) {
     std::unique_lock<std::mutex> lock(mutex_);
-    ScopedAssumeLocked assume_locked(mutex_);
+    ScopedLockAssertion assume_locked(mutex_);
     return cv_.wait_for(lock, timeout, [&]() REQUIRES(mutex_) {
         return connection_established_ready_;
     }) && connection_established_;
diff --git a/base/Android.bp b/base/Android.bp
index b0181aa..38f301a 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -46,6 +46,7 @@
     defaults: ["libbase_cflags_defaults"],
     srcs: [
         "chrono_utils.cpp",
+        "cmsg.cpp",
         "file.cpp",
         "logging.cpp",
         "mapped_file.cpp",
@@ -85,6 +86,9 @@
                 "errors_windows.cpp",
                 "utf8.cpp",
             ],
+            exclude_srcs: [
+                "cmsg.cpp",
+            ],
             enabled: true,
         },
     },
@@ -121,6 +125,7 @@
     defaults: ["libbase_cflags_defaults"],
     host_supported: true,
     srcs: [
+        "cmsg_test.cpp",
         "endian_test.cpp",
         "errors_test.cpp",
         "file_test.cpp",
diff --git a/base/cmsg.cpp b/base/cmsg.cpp
new file mode 100644
index 0000000..42866f8
--- /dev/null
+++ b/base/cmsg.cpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#include <android-base/cmsg.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/user.h>
+
+#include <memory>
+
+#include <android-base/logging.h>
+
+namespace android {
+namespace base {
+
+ssize_t SendFileDescriptorVector(int sockfd, const void* data, size_t len,
+                                 const std::vector<int>& fds) {
+  size_t cmsg_space = CMSG_SPACE(sizeof(int) * fds.size());
+  size_t cmsg_len = CMSG_LEN(sizeof(int) * fds.size());
+  if (cmsg_space >= PAGE_SIZE) {
+    errno = ENOMEM;
+    return -1;
+  }
+
+  alignas(struct cmsghdr) char cmsg_buf[cmsg_space];
+  iovec iov = {.iov_base = const_cast<void*>(data), .iov_len = len};
+  msghdr msg = {
+      .msg_name = nullptr,
+      .msg_namelen = 0,
+      .msg_iov = &iov,
+      .msg_iovlen = 1,
+      .msg_control = cmsg_buf,
+      // We can't cast to the actual type of the field, because it's different across platforms.
+      .msg_controllen = static_cast<unsigned int>(cmsg_space),
+      .msg_flags = 0,
+  };
+
+  struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+  cmsg->cmsg_level = SOL_SOCKET;
+  cmsg->cmsg_type = SCM_RIGHTS;
+  cmsg->cmsg_len = cmsg_len;
+
+  int* cmsg_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
+  for (size_t i = 0; i < fds.size(); ++i) {
+    cmsg_fds[i] = fds[i];
+  }
+
+#if defined(__linux__)
+  int flags = MSG_NOSIGNAL;
+#else
+  int flags = 0;
+#endif
+
+  return TEMP_FAILURE_RETRY(sendmsg(sockfd, &msg, flags));
+}
+
+ssize_t ReceiveFileDescriptorVector(int sockfd, void* data, size_t len, size_t max_fds,
+                                    std::vector<unique_fd>* fds) {
+  fds->clear();
+
+  size_t cmsg_space = CMSG_SPACE(sizeof(int) * max_fds);
+  if (cmsg_space >= PAGE_SIZE) {
+    errno = ENOMEM;
+    return -1;
+  }
+
+  alignas(struct cmsghdr) char cmsg_buf[cmsg_space];
+  iovec iov = {.iov_base = const_cast<void*>(data), .iov_len = len};
+  msghdr msg = {
+      .msg_name = nullptr,
+      .msg_namelen = 0,
+      .msg_iov = &iov,
+      .msg_iovlen = 1,
+      .msg_control = cmsg_buf,
+      // We can't cast to the actual type of the field, because it's different across platforms.
+      .msg_controllen = static_cast<unsigned int>(cmsg_space),
+      .msg_flags = 0,
+  };
+
+  int flags = MSG_TRUNC | MSG_CTRUNC;
+#if defined(__linux__)
+  flags |= MSG_CMSG_CLOEXEC | MSG_NOSIGNAL;
+#endif
+
+  ssize_t rc = TEMP_FAILURE_RETRY(recvmsg(sockfd, &msg, flags));
+
+  if (rc == -1) {
+    return -1;
+  }
+
+  int error = 0;
+  if ((msg.msg_flags & MSG_TRUNC)) {
+    LOG(ERROR) << "message was truncated when receiving file descriptors";
+    error = EMSGSIZE;
+  } else if ((msg.msg_flags & MSG_CTRUNC)) {
+    LOG(ERROR) << "control message was truncated when receiving file descriptors";
+    error = EMSGSIZE;
+  }
+
+  std::vector<unique_fd> received_fds;
+  struct cmsghdr* cmsg;
+  for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != nullptr; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+    if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {
+      LOG(ERROR) << "received unexpected cmsg: [" << cmsg->cmsg_level << ", " << cmsg->cmsg_type
+                 << "]";
+      error = EBADMSG;
+      continue;
+    }
+
+    // There isn't a macro that does the inverse of CMSG_LEN, so hack around it ourselves, with
+    // some asserts to ensure that CMSG_LEN behaves as we expect.
+#if defined(__linux__)
+#define CMSG_ASSERT static_assert
+#else
+// CMSG_LEN is somehow not constexpr on darwin.
+#define CMSG_ASSERT CHECK
+#endif
+    CMSG_ASSERT(CMSG_LEN(0) + 1 * sizeof(int) == CMSG_LEN(1 * sizeof(int)));
+    CMSG_ASSERT(CMSG_LEN(0) + 2 * sizeof(int) == CMSG_LEN(2 * sizeof(int)));
+    CMSG_ASSERT(CMSG_LEN(0) + 3 * sizeof(int) == CMSG_LEN(3 * sizeof(int)));
+    CMSG_ASSERT(CMSG_LEN(0) + 4 * sizeof(int) == CMSG_LEN(4 * sizeof(int)));
+
+    if (cmsg->cmsg_len % sizeof(int) != 0) {
+      LOG(FATAL) << "cmsg_len(" << cmsg->cmsg_len << ") not aligned to sizeof(int)";
+    } else if (cmsg->cmsg_len <= CMSG_LEN(0)) {
+      LOG(FATAL) << "cmsg_len(" << cmsg->cmsg_len << ") not long enough to hold any data";
+    }
+
+    int* cmsg_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
+    size_t cmsg_fdcount = static_cast<size_t>(cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
+    for (size_t i = 0; i < cmsg_fdcount; ++i) {
+#if !defined(__linux__)
+      // Linux uses MSG_CMSG_CLOEXEC instead of doing this manually.
+      fcntl(cmsg_fds[i], F_SETFD, FD_CLOEXEC);
+#endif
+      received_fds.emplace_back(cmsg_fds[i]);
+    }
+  }
+
+  if (error != 0) {
+    errno = error;
+    return -1;
+  }
+
+  if (received_fds.size() > max_fds) {
+    LOG(ERROR) << "received too many file descriptors, expected " << fds->size() << ", received "
+               << received_fds.size();
+    errno = EMSGSIZE;
+    return -1;
+  }
+
+  *fds = std::move(received_fds);
+  return rc;
+}
+
+}  // namespace base
+}  // namespace android
diff --git a/base/cmsg_test.cpp b/base/cmsg_test.cpp
new file mode 100644
index 0000000..9ee5c82
--- /dev/null
+++ b/base/cmsg_test.cpp
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#include <android-base/cmsg.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+
+#if !defined(_WIN32)
+
+using android::base::ReceiveFileDescriptors;
+using android::base::SendFileDescriptors;
+using android::base::unique_fd;
+
+static ino_t GetInode(int fd) {
+  struct stat st;
+  if (fstat(fd, &st) != 0) {
+    PLOG(FATAL) << "fstat failed";
+  }
+
+  return st.st_ino;
+}
+
+struct CmsgTest : ::testing::TestWithParam<bool> {
+  bool Seqpacket() { return GetParam(); }
+
+  void SetUp() override {
+    ASSERT_TRUE(
+        android::base::Socketpair(Seqpacket() ? SOCK_SEQPACKET : SOCK_STREAM, &send, &recv));
+    int dup1 = dup(tmp1.fd);
+    ASSERT_NE(-1, dup1);
+    int dup2 = dup(tmp2.fd);
+    ASSERT_NE(-1, dup2);
+
+    fd1.reset(dup1);
+    fd2.reset(dup2);
+
+    ino1 = GetInode(dup1);
+    ino2 = GetInode(dup2);
+  }
+
+  unique_fd send;
+  unique_fd recv;
+
+  TemporaryFile tmp1;
+  TemporaryFile tmp2;
+
+  unique_fd fd1;
+  unique_fd fd2;
+
+  ino_t ino1;
+  ino_t ino2;
+};
+
+TEST_P(CmsgTest, smoke) {
+  ASSERT_EQ(1, SendFileDescriptors(send.get(), "x", 1, fd1.get()));
+
+  char buf[2];
+  unique_fd received;
+  ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 2, &received));
+  ASSERT_EQ('x', buf[0]);
+  ASSERT_NE(-1, received.get());
+
+  ASSERT_EQ(ino1, GetInode(received.get()));
+}
+
+TEST_P(CmsgTest, msg_trunc) {
+  ASSERT_EQ(2, SendFileDescriptors(send.get(), "ab", 2, fd1.get(), fd2.get()));
+
+  char buf[2];
+  unique_fd received1, received2;
+
+  ssize_t rc = ReceiveFileDescriptors(recv.get(), buf, 1, &received1, &received2);
+  if (Seqpacket()) {
+    ASSERT_EQ(-1, rc);
+    ASSERT_EQ(EMSGSIZE, errno);
+    ASSERT_EQ(-1, received1.get());
+    ASSERT_EQ(-1, received2.get());
+  } else {
+    ASSERT_EQ(1, rc);
+    ASSERT_NE(-1, received1.get());
+    ASSERT_NE(-1, received2.get());
+    ASSERT_EQ(ino1, GetInode(received1.get()));
+    ASSERT_EQ(ino2, GetInode(received2.get()));
+    ASSERT_EQ(1, read(recv.get(), buf, 2));
+  }
+}
+
+TEST_P(CmsgTest, msg_ctrunc) {
+  ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get(), fd2.get()));
+
+  char buf[2];
+  unique_fd received;
+  ASSERT_EQ(-1, ReceiveFileDescriptors(recv.get(), buf, 1, &received));
+  ASSERT_EQ(EMSGSIZE, errno);
+  ASSERT_EQ(-1, received.get());
+}
+
+TEST_P(CmsgTest, peek) {
+  ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get()));
+
+  char buf[2];
+  ASSERT_EQ(1, ::recv(recv.get(), buf, sizeof(buf), MSG_PEEK));
+  ASSERT_EQ('a', buf[0]);
+
+  unique_fd received;
+  ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received));
+  ASSERT_EQ(ino1, GetInode(received.get()));
+}
+
+TEST_P(CmsgTest, stream_fd_association) {
+  if (Seqpacket()) {
+    return;
+  }
+
+  // fds are associated with the first byte of the write.
+  ASSERT_EQ(1, TEMP_FAILURE_RETRY(write(send.get(), "a", 1)));
+  ASSERT_EQ(2, SendFileDescriptors(send.get(), "bc", 2, fd1.get()));
+  ASSERT_EQ(1, SendFileDescriptors(send.get(), "d", 1, fd2.get()));
+  char buf[2];
+  ASSERT_EQ(2, TEMP_FAILURE_RETRY(read(recv.get(), buf, 2)));
+  ASSERT_EQ(0, memcmp(buf, "ab", 2));
+
+  std::vector<unique_fd> received1;
+  ssize_t rc = ReceiveFileDescriptorVector(recv.get(), buf, 1, 1, &received1);
+  ASSERT_EQ(1, rc);
+  ASSERT_EQ('c', buf[0]);
+  ASSERT_TRUE(received1.empty());
+
+  unique_fd received2;
+  rc = ReceiveFileDescriptors(recv.get(), buf, 1, &received2);
+  ASSERT_EQ(1, rc);
+  ASSERT_EQ('d', buf[0]);
+  ASSERT_EQ(ino2, GetInode(received2.get()));
+}
+
+TEST_P(CmsgTest, multiple_fd_ordering) {
+  ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get(), fd2.get()));
+
+  char buf[2];
+  unique_fd received1, received2;
+  ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received1, &received2));
+
+  ASSERT_NE(-1, received1.get());
+  ASSERT_NE(-1, received2.get());
+
+  ASSERT_EQ(ino1, GetInode(received1.get()));
+  ASSERT_EQ(ino2, GetInode(received2.get()));
+}
+
+TEST_P(CmsgTest, separate_fd_ordering) {
+  ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get()));
+  ASSERT_EQ(1, SendFileDescriptors(send.get(), "b", 1, fd2.get()));
+
+  char buf[2];
+  unique_fd received1, received2;
+  ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received1));
+  ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received2));
+
+  ASSERT_NE(-1, received1.get());
+  ASSERT_NE(-1, received2.get());
+
+  ASSERT_EQ(ino1, GetInode(received1.get()));
+  ASSERT_EQ(ino2, GetInode(received2.get()));
+}
+
+TEST_P(CmsgTest, separate_fds_no_coalescing) {
+  unique_fd sent1(dup(tmp1.fd));
+  unique_fd sent2(dup(tmp2.fd));
+
+  ASSERT_EQ(1, SendFileDescriptors(send.get(), "", 1, fd1.get()));
+  ASSERT_EQ(1, SendFileDescriptors(send.get(), "", 1, fd2.get()));
+
+  char buf[2];
+  std::vector<unique_fd> received;
+  ASSERT_EQ(1, ReceiveFileDescriptorVector(recv.get(), buf, 2, 2, &received));
+  ASSERT_EQ(1U, received.size());
+  ASSERT_EQ(ino1, GetInode(received[0].get()));
+
+  ASSERT_EQ(1, ReceiveFileDescriptorVector(recv.get(), buf, 2, 2, &received));
+  ASSERT_EQ(1U, received.size());
+  ASSERT_EQ(ino2, GetInode(received[0].get()));
+}
+
+INSTANTIATE_TEST_CASE_P(CmsgTest, CmsgTest, testing::Bool());
+
+#endif
diff --git a/base/include/android-base/cmsg.h b/base/include/android-base/cmsg.h
new file mode 100644
index 0000000..7f93ddc
--- /dev/null
+++ b/base/include/android-base/cmsg.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <type_traits>
+#include <vector>
+
+#include <android-base/collections.h>
+#include <android-base/macros.h>
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace base {
+
+#if !defined(_WIN32)
+
+// Helpers for sending and receiving file descriptors across Unix domain sockets.
+//
+// The cmsg(3) API is very hard to get right, with multiple landmines that can
+// lead to death. Almost all of the uses of cmsg in Android make at least one of
+// the following mistakes:
+//
+//   - not aligning the cmsg buffer
+//   - leaking fds if more fds are received than expected
+//   - blindly dereferencing CMSG_DATA without checking the header
+//   - using CMSG_SPACE instead of CMSG_LEN for .cmsg_len
+//   - using CMSG_LEN instead of CMSG_SPACE for .msg_controllen
+//   - using a length specified in number of fds instead of bytes
+//
+// These functions wrap the hard-to-use cmsg API with an easier to use abstraction.
+
+// Send file descriptors across a Unix domain socket.
+//
+// Note that the write can return short if the socket type is SOCK_STREAM. When
+// this happens, file descriptors are still sent to the other end, but with
+// truncated data. For this reason, using SOCK_SEQPACKET or SOCK_DGRAM is recommended.
+ssize_t SendFileDescriptorVector(int sock, const void* data, size_t len,
+                                 const std::vector<int>& fds);
+
+// Receive file descriptors from a Unix domain socket.
+//
+// If more FDs (or bytes, for datagram sockets) are received than expected,
+// -1 is returned with errno set to EMSGSIZE, and all received FDs are thrown away.
+ssize_t ReceiveFileDescriptorVector(int sock, void* data, size_t len, size_t max_fds,
+                                    std::vector<android::base::unique_fd>* fds);
+
+// Helper for SendFileDescriptorVector that constructs a std::vector for you, e.g.:
+//   SendFileDescriptors(sock, "foo", 3, std::move(fd1), std::move(fd2))
+template <typename... Args>
+ssize_t SendFileDescriptors(int sock, const void* data, size_t len, Args&&... sent_fds) {
+  // Do not allow implicit conversion to int: people might try to do something along the lines of:
+  //   SendFileDescriptors(..., std::move(a_unique_fd))
+  // and be surprised when the unique_fd isn't closed afterwards.
+  AssertType<int>(std::forward<Args>(sent_fds)...);
+  std::vector<int> fds;
+  Append(fds, std::forward<Args>(sent_fds)...);
+  return SendFileDescriptorVector(sock, data, len, fds);
+}
+
+// Helper for ReceiveFileDescriptorVector that receives an exact number of file descriptors.
+// If more file descriptors are received than requested, -1 is returned with errno set to EMSGSIZE.
+// If fewer file descriptors are received than requested, -1 is returned with errno set to ENOMSG.
+// In both cases, all arguments are cleared and any received FDs are thrown away.
+template <typename... Args>
+ssize_t ReceiveFileDescriptors(int sock, void* data, size_t len, Args&&... received_fds) {
+  std::vector<unique_fd*> fds;
+  Append(fds, std::forward<Args>(received_fds)...);
+
+  std::vector<unique_fd> result;
+  ssize_t rc = ReceiveFileDescriptorVector(sock, data, len, fds.size(), &result);
+  if (rc == -1 || result.size() != fds.size()) {
+    int err = rc == -1 ? errno : ENOMSG;
+    for (unique_fd* fd : fds) {
+      fd->reset();
+    }
+    errno = err;
+    return -1;
+  }
+
+  for (size_t i = 0; i < fds.size(); ++i) {
+    *fds[i] = std::move(result[i]);
+  }
+  return rc;
+}
+
+#endif
+
+}  // namespace base
+}  // namespace android
diff --git a/base/include/android-base/collections.h b/base/include/android-base/collections.h
new file mode 100644
index 0000000..be0683a
--- /dev/null
+++ b/base/include/android-base/collections.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#include <utility>
+
+namespace android {
+namespace base {
+
+// Helpers for converting a variadic template parameter pack to a homogeneous collection.
+// Parameters must be implictly convertible to the contained type (including via move/copy ctors).
+//
+// Use as follows:
+//
+//   template <typename... Args>
+//   std::vector<int> CreateVector(Args&&... args) {
+//     std::vector<int> result;
+//     Append(result, std::forward<Args>(args)...);
+//     return result;
+//   }
+template <typename CollectionType, typename T>
+void Append(CollectionType& collection, T&& arg) {
+  collection.push_back(std::forward<T>(arg));
+}
+
+template <typename CollectionType, typename T, typename... Args>
+void Append(CollectionType& collection, T&& arg, Args&&... args) {
+  collection.push_back(std::forward<T>(arg));
+  return Append(collection, std::forward<Args>(args)...);
+}
+
+// Assert that all of the arguments in a variadic template parameter pack are of a given type
+// after std::decay.
+template <typename T, typename Arg, typename... Args>
+void AssertType(Arg&&) {
+  static_assert(std::is_same<T, typename std::decay<Arg>::type>::value);
+}
+
+template <typename T, typename Arg, typename... Args>
+void AssertType(Arg&&, Args&&... args) {
+  static_assert(std::is_same<T, typename std::decay<Arg>::type>::value);
+  AssertType<T>(std::forward<Args>(args)...);
+}
+
+}  // namespace base
+}  // namespace android
diff --git a/base/include/android-base/thread_annotations.h b/base/include/android-base/thread_annotations.h
index 5c55e63..53fe6da 100644
--- a/base/include/android-base/thread_annotations.h
+++ b/base/include/android-base/thread_annotations.h
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#include <mutex>
+
 #define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
 
 #define CAPABILITY(x) \
@@ -104,3 +106,39 @@
 
 #define NO_THREAD_SAFETY_ANALYSIS \
       THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
+
+namespace android {
+namespace base {
+
+// A class to help thread safety analysis deal with std::unique_lock and condition_variable.
+//
+// Clang's thread safety analysis currently doesn't perform alias analysis, so movable types
+// like std::unique_lock can't be marked with thread safety annotations. This helper allows
+// for manual assertion of lock state in a scope.
+//
+// For example:
+//
+//   std::mutex mutex;
+//   std::condition_variable cv;
+//   std::vector<int> vec GUARDED_BY(mutex);
+//
+//   int pop() {
+//     std::unique_lock lock(mutex);
+//     ScopedLockAssertion lock_assertion(mutex);
+//     cv.wait(lock, []() {
+//       ScopedLockAssertion lock_assertion(mutex);
+//       return !vec.empty();
+//     });
+//
+//     int result = vec.back();
+//     vec.pop_back();
+//     return result;
+//   }
+class SCOPED_CAPABILITY ScopedLockAssertion {
+ public:
+  ScopedLockAssertion(std::mutex& mutex) ACQUIRE(mutex) {}
+  ~ScopedLockAssertion() RELEASE() {}
+};
+
+}  // namespace base
+}  // namespace android
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index 610b96b..60eb241 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -26,6 +26,7 @@
 
 #include <chrono>
 
+#include <android-base/cmsg.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
@@ -41,6 +42,7 @@
 
 using namespace std::chrono_literals;
 
+using android::base::SendFileDescriptors;
 using android::base::unique_fd;
 
 static bool send_signal(pid_t pid, const DebuggerdDumpType dump_type) {
@@ -146,15 +148,16 @@
     PLOG(ERROR) << "failed to set pipe buffer size";
   }
 
-  if (send_fd(set_timeout(sockfd), &req, sizeof(req), std::move(pipe_write)) != sizeof(req)) {
+  ssize_t rc = SendFileDescriptors(set_timeout(sockfd), &req, sizeof(req), pipe_write.get());
+  pipe_write.reset();
+  if (rc != sizeof(req)) {
     PLOG(ERROR) << "libdebuggerd_client: failed to send output fd to tombstoned";
     return false;
   }
 
   // Check to make sure we've successfully registered.
   InterceptResponse response;
-  ssize_t rc =
-      TEMP_FAILURE_RETRY(recv(set_timeout(sockfd.get()), &response, sizeof(response), MSG_TRUNC));
+  rc = TEMP_FAILURE_RETRY(recv(set_timeout(sockfd.get()), &response, sizeof(response), MSG_TRUNC));
   if (rc == 0) {
     LOG(ERROR) << "libdebuggerd_client: failed to read initial response from tombstoned: "
                << "timeout reached?";
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index bea8b43..64df53e 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -32,6 +32,7 @@
 #include <android/fdsan.h>
 #include <android/set_abort_message.h>
 
+#include <android-base/cmsg.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/macros.h>
@@ -53,6 +54,8 @@
 #include "util.h"
 
 using namespace std::chrono_literals;
+
+using android::base::SendFileDescriptors;
 using android::base::unique_fd;
 
 #if defined(__LP64__)
@@ -123,12 +126,14 @@
 
   ASSERT_GE(pipe_buffer_size, 1024 * 1024);
 
-  if (send_fd(intercept_fd->get(), &req, sizeof(req), std::move(output_pipe_write)) != sizeof(req)) {
+  ssize_t rc = SendFileDescriptors(intercept_fd->get(), &req, sizeof(req), output_pipe_write.get());
+  output_pipe_write.reset();
+  if (rc != sizeof(req)) {
     FAIL() << "failed to send output fd to tombstoned: " << strerror(errno);
   }
 
   InterceptResponse response;
-  ssize_t rc = TEMP_FAILURE_RETRY(read(intercept_fd->get(), &response, sizeof(response)));
+  rc = TEMP_FAILURE_RETRY(read(intercept_fd->get(), &response, sizeof(response)));
   if (rc == -1) {
     FAIL() << "failed to read response from tombstoned: " << strerror(errno);
   } else if (rc == 0) {
diff --git a/debuggerd/tombstoned/intercept_manager.cpp b/debuggerd/tombstoned/intercept_manager.cpp
index c446dbb..7d25c50 100644
--- a/debuggerd/tombstoned/intercept_manager.cpp
+++ b/debuggerd/tombstoned/intercept_manager.cpp
@@ -24,6 +24,7 @@
 #include <event2/event.h>
 #include <event2/listener.h>
 
+#include <android-base/cmsg.h>
 #include <android-base/logging.h>
 #include <android-base/unique_fd.h>
 #include <cutils/sockets.h>
@@ -31,6 +32,7 @@
 #include "protocol.h"
 #include "util.h"
 
+using android::base::ReceiveFileDescriptors;
 using android::base::unique_fd;
 
 static void intercept_close_cb(evutil_socket_t sockfd, short event, void* arg) {
@@ -96,7 +98,8 @@
   {
     unique_fd rcv_fd;
     InterceptRequest intercept_request;
-    ssize_t result = recv_fd(sockfd, &intercept_request, sizeof(intercept_request), &rcv_fd);
+    ssize_t result =
+        ReceiveFileDescriptors(sockfd, &intercept_request, sizeof(intercept_request), &rcv_fd);
 
     if (result == -1) {
       PLOG(WARNING) << "failed to read from intercept socket";
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index ad92067..bbeb181 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -31,6 +31,7 @@
 #include <event2/listener.h>
 #include <event2/thread.h>
 
+#include <android-base/cmsg.h>
 #include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
@@ -45,6 +46,7 @@
 #include "intercept_manager.h"
 
 using android::base::GetIntProperty;
+using android::base::SendFileDescriptors;
 using android::base::StringPrintf;
 using android::base::unique_fd;
 
@@ -224,7 +226,10 @@
   TombstonedCrashPacket response = {
     .packet_type = CrashPacketType::kPerformDump
   };
-  ssize_t rc = send_fd(crash->crash_socket_fd, &response, sizeof(response), std::move(output_fd));
+  ssize_t rc =
+      SendFileDescriptors(crash->crash_socket_fd, &response, sizeof(response), output_fd.get());
+  output_fd.reset();
+
   if (rc == -1) {
     PLOG(WARNING) << "failed to send response to CrashRequest";
     goto fail;
diff --git a/debuggerd/tombstoned/tombstoned_client.cpp b/debuggerd/tombstoned/tombstoned_client.cpp
index bdb4c1a..2c23c98 100644
--- a/debuggerd/tombstoned/tombstoned_client.cpp
+++ b/debuggerd/tombstoned/tombstoned_client.cpp
@@ -21,6 +21,7 @@
 
 #include <utility>
 
+#include <android-base/cmsg.h>
 #include <android-base/unique_fd.h>
 #include <async_safe/log.h>
 #include <cutils/sockets.h>
@@ -28,6 +29,7 @@
 #include "protocol.h"
 #include "util.h"
 
+using android::base::ReceiveFileDescriptors;
 using android::base::unique_fd;
 
 bool tombstoned_connect(pid_t pid, unique_fd* tombstoned_socket, unique_fd* output_fd,
@@ -53,7 +55,7 @@
   }
 
   unique_fd tmp_output_fd;
-  ssize_t rc = recv_fd(sockfd, &packet, sizeof(packet), &tmp_output_fd);
+  ssize_t rc = ReceiveFileDescriptors(sockfd, &packet, sizeof(packet), &tmp_output_fd);
   if (rc == -1) {
     async_safe_format_log(ANDROID_LOG_ERROR, "libc",
                           "failed to read response to DumpRequest packet: %s", strerror(errno));
diff --git a/debuggerd/util.cpp b/debuggerd/util.cpp
index 50c5efc..a37b3b9 100644
--- a/debuggerd/util.cpp
+++ b/debuggerd/util.cpp
@@ -24,73 +24,9 @@
 #include <android-base/file.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
-#include <android-base/unique_fd.h>
 #include <cutils/sockets.h>
 #include "protocol.h"
 
-using android::base::unique_fd;
-
-ssize_t send_fd(int sockfd, const void* data, size_t len, unique_fd fd) {
-  char cmsg_buf[CMSG_SPACE(sizeof(int))];
-
-  iovec iov = { .iov_base = const_cast<void*>(data), .iov_len = len };
-  msghdr msg = {
-    .msg_iov = &iov, .msg_iovlen = 1, .msg_control = cmsg_buf, .msg_controllen = sizeof(cmsg_buf),
-  };
-  auto cmsg = CMSG_FIRSTHDR(&msg);
-  cmsg->cmsg_level = SOL_SOCKET;
-  cmsg->cmsg_type = SCM_RIGHTS;
-  cmsg->cmsg_len = CMSG_LEN(sizeof(int));
-  *reinterpret_cast<int*>(CMSG_DATA(cmsg)) = fd.get();
-
-  return TEMP_FAILURE_RETRY(sendmsg(sockfd, &msg, 0));
-}
-
-ssize_t recv_fd(int sockfd, void* _Nonnull data, size_t len, unique_fd* _Nullable out_fd) {
-  char cmsg_buf[CMSG_SPACE(sizeof(int))];
-
-  iovec iov = { .iov_base = const_cast<void*>(data), .iov_len = len };
-  msghdr msg = {
-    .msg_iov = &iov,
-    .msg_iovlen = 1,
-    .msg_control = cmsg_buf,
-    .msg_controllen = sizeof(cmsg_buf),
-    .msg_flags = 0,
-  };
-  auto cmsg = CMSG_FIRSTHDR(&msg);
-  cmsg->cmsg_level = SOL_SOCKET;
-  cmsg->cmsg_type = SCM_RIGHTS;
-  cmsg->cmsg_len = CMSG_LEN(sizeof(int));
-
-  ssize_t result = TEMP_FAILURE_RETRY(recvmsg(sockfd, &msg, 0));
-  if (result == -1) {
-    return -1;
-  }
-
-  unique_fd fd;
-  bool received_fd = msg.msg_controllen == sizeof(cmsg_buf);
-  if (received_fd) {
-    fd.reset(*reinterpret_cast<int*>(CMSG_DATA(cmsg)));
-  }
-
-  if ((msg.msg_flags & MSG_TRUNC) != 0) {
-    errno = EFBIG;
-    return -1;
-  } else if ((msg.msg_flags & MSG_CTRUNC) != 0) {
-    errno = ERANGE;
-    return -1;
-  }
-
-  if (out_fd) {
-    *out_fd = std::move(fd);
-  } else if (received_fd) {
-    errno = ERANGE;
-    return -1;
-  }
-
-  return result;
-}
-
 std::string get_process_name(pid_t pid) {
   std::string result = "<unknown>";
   android::base::ReadFileToString(android::base::StringPrintf("/proc/%d/cmdline", pid), &result);
diff --git a/debuggerd/util.h b/debuggerd/util.h
index 8260b44..e964423 100644
--- a/debuggerd/util.h
+++ b/debuggerd/util.h
@@ -21,29 +21,5 @@
 #include <sys/cdefs.h>
 #include <sys/types.h>
 
-#include <android-base/unique_fd.h>
-
-// *** WARNING ***
-// tombstoned's sockets are SOCK_SEQPACKET sockets.
-// Short reads are treated as errors and short writes are assumed to not happen.
-
-// Sends a packet with an attached fd.
-ssize_t send_fd(int sockfd, const void* _Nonnull data, size_t len, android::base::unique_fd fd);
-
-// Receives a packet and optionally, its attached fd.
-// If out_fd is non-null, packets can optionally have an attached fd.
-// If out_fd is null, received packets must not have an attached fd.
-//
-// Errors:
-//   EOVERFLOW: sockfd is SOCK_DGRAM or SOCK_SEQPACKET and buffer is too small.
-//              The first len bytes of the packet are stored in data, but the
-//              rest of the packet is dropped.
-//   ERANGE:    too many file descriptors were attached to the packet.
-//   ENOMSG:    not enough file descriptors were attached to the packet.
-//
-//   plus any errors returned by the underlying recvmsg.
-ssize_t recv_fd(int sockfd, void* _Nonnull data, size_t len,
-                android::base::unique_fd* _Nullable out_fd);
-
 std::string get_process_name(pid_t pid);
 std::string get_thread_name(pid_t tid);
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 4659add..82d9144 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -607,10 +607,14 @@
     return userdata;
 }
 
-void EraseFstabEntry(Fstab* fstab, const std::string& mount_point) {
+bool EraseFstabEntry(Fstab* fstab, const std::string& mount_point) {
     auto iter = std::remove_if(fstab->begin(), fstab->end(),
                                [&](const auto& entry) { return entry.mount_point == mount_point; });
-    fstab->erase(iter, fstab->end());
+    if (iter != fstab->end()) {
+        fstab->erase(iter, fstab->end());
+        return true;
+    }
+    return false;
 }
 
 void TransformFstabForGsi(Fstab* fstab) {
@@ -628,11 +632,13 @@
         userdata = BuildGsiUserdataFstabEntry();
     }
 
-    EraseFstabEntry(fstab, "/system");
-    EraseFstabEntry(fstab, "/data");
+    if (EraseFstabEntry(fstab, "/system")) {
+        fstab->emplace_back(BuildGsiSystemFstabEntry());
+    }
 
-    fstab->emplace_back(BuildGsiSystemFstabEntry());
-    fstab->emplace_back(userdata);
+    if (EraseFstabEntry(fstab, "/data")) {
+        fstab->emplace_back(userdata);
+    }
 }
 
 }  // namespace
diff --git a/fs_mgr/tests/Android.bp b/fs_mgr/tests/Android.bp
index 565597a..59af924 100644
--- a/fs_mgr/tests/Android.bp
+++ b/fs_mgr/tests/Android.bp
@@ -53,3 +53,15 @@
     },
     host_supported: true,
 }
+
+java_test_host {
+    name: "fs_mgr_vendor_overlay_test",
+
+    srcs:  ["src/**/VendorOverlayHostTest.java"],
+
+    libs: ["tradefed"],
+
+    test_config: "vendor-overlay-test.xml",
+
+    test_suites: ["general-tests"],
+}
diff --git a/fs_mgr/tests/src/com/android/tests/vendoroverlay/VendorOverlayHostTest.java b/fs_mgr/tests/src/com/android/tests/vendoroverlay/VendorOverlayHostTest.java
new file mode 100644
index 0000000..f08cab2
--- /dev/null
+++ b/fs_mgr/tests/src/com/android/tests/vendoroverlay/VendorOverlayHostTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+package com.android.tests.vendoroverlay;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.util.CommandResult;
+import com.android.tradefed.util.CommandStatus;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test the vendor overlay feature. Requires adb remount with OverlayFS.
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class VendorOverlayHostTest extends BaseHostJUnit4Test {
+  boolean wasRoot = false;
+
+  @Before
+  public void setup() throws DeviceNotAvailableException {
+    wasRoot = getDevice().isAdbRoot();
+    if (!wasRoot) {
+      Assume.assumeTrue("Test requires root", getDevice().enableAdbRoot());
+    }
+
+    Assume.assumeTrue("Skipping vendor overlay test due to lack of necessary OverlayFS support",
+        testConditionsMet());
+
+    getDevice().remountSystemWritable();
+    // Was OverlayFS used by adb remount? Without it we can't safely re-enable dm-verity.
+    Pattern vendorPattern = Pattern.compile("^overlay .+ /vendor$", Pattern.MULTILINE);
+    Pattern productPattern = Pattern.compile("^overlay .+ /product$", Pattern.MULTILINE);
+    CommandResult result = getDevice().executeShellV2Command("df");
+    Assume.assumeTrue("OverlayFS not used for adb remount on /vendor",
+        vendorPattern.matcher(result.getStdout()).find());
+    Assume.assumeTrue("OverlayFS not used for adb remount on /product",
+        productPattern.matcher(result.getStdout()).find());
+  }
+
+  private boolean cmdSucceeded(CommandResult result) {
+    return result.getStatus() == CommandStatus.SUCCESS;
+  }
+
+  private void assumeMkdirSuccess(String dir) throws DeviceNotAvailableException {
+    CommandResult result = getDevice().executeShellV2Command("mkdir -p " + dir);
+    Assume.assumeTrue("Couldn't create " + dir, cmdSucceeded(result));
+  }
+
+  /**
+   * Tests that files in the appropriate /product/vendor_overlay dir are overlaid onto /vendor.
+   */
+  @Test
+  public void testVendorOverlay() throws DeviceNotAvailableException {
+    String vndkVersion = getDevice().executeShellV2Command("getprop ro.vndk.version").getStdout();
+
+    // Create files and modify policy
+    CommandResult result = getDevice().executeShellV2Command(
+        "echo '/(product|system/product)/vendor_overlay/" + vndkVersion +
+        "/.* u:object_r:vendor_file:s0'" + " >> /system/etc/selinux/plat_file_contexts");
+    Assume.assumeTrue("Couldn't modify plat_file_contexts", cmdSucceeded(result));
+    assumeMkdirSuccess("/vendor/testdir");
+    assumeMkdirSuccess("/vendor/diffcontext");
+    assumeMkdirSuccess("/product/vendor_overlay/'" + vndkVersion + "'/testdir");
+    result = getDevice().executeShellV2Command(
+        "echo overlay > /product/vendor_overlay/'" + vndkVersion + "'/testdir/test");
+    Assume.assumeTrue("Couldn't create text file in testdir", cmdSucceeded(result));
+    assumeMkdirSuccess("/product/vendor_overlay/'" + vndkVersion + "'/noexist/test");
+    assumeMkdirSuccess("/product/vendor_overlay/'" + vndkVersion + "'/diffcontext/test");
+    result = getDevice().executeShellV2Command(
+        "restorecon -r /product/vendor_overlay/'" + vndkVersion + "'/testdir");
+    Assume.assumeTrue("Couldn't write testdir context", cmdSucceeded(result));
+
+    getDevice().reboot();
+
+    // Test that the file was overlaid properly
+    result = getDevice().executeShellV2Command("[ $(cat /vendor/testdir/test) = overlay ]");
+    Assert.assertTrue("test file was not overlaid onto /vendor/", cmdSucceeded(result));
+    result = getDevice().executeShellV2Command("[ ! -d /vendor/noexist/test ]");
+    Assert.assertTrue("noexist dir shouldn't exist on /vendor", cmdSucceeded(result));
+    result = getDevice().executeShellV2Command("[ ! -d /vendor/diffcontext/test ]");
+    Assert.assertTrue("diffcontext dir shouldn't exist on /vendor", cmdSucceeded(result));
+  }
+
+  // Duplicate of fs_mgr_overlayfs_valid() logic
+  // Requires root
+  public boolean testConditionsMet() throws DeviceNotAvailableException {
+    if (cmdSucceeded(getDevice().executeShellV2Command(
+        "[ -e /sys/module/overlay/parameters/override_creds ]"))) {
+      return true;
+    }
+    if (cmdSucceeded(getDevice().executeShellV2Command("[ ! -e /sys/module/overlay ]"))) {
+      return false;
+    }
+    CommandResult result = getDevice().executeShellV2Command("awk '{ print $3 }' /proc/version");
+    Pattern kernelVersionPattern = Pattern.compile("([1-9])[.]([0-9]+).*");
+    Matcher kernelVersionMatcher = kernelVersionPattern.matcher(result.getStdout());
+    kernelVersionMatcher.find();
+    int majorKernelVersion;
+    int minorKernelVersion;
+    try {
+      majorKernelVersion = Integer.parseInt(kernelVersionMatcher.group(1));
+      minorKernelVersion = Integer.parseInt(kernelVersionMatcher.group(2));
+    } catch (Exception e) {
+      return false;
+    }
+    if (majorKernelVersion < 4) {
+      return true;
+    }
+    if (majorKernelVersion > 4) {
+      return false;
+    }
+    if (minorKernelVersion > 6) {
+      return false;
+    }
+    return true;
+  }
+
+  @After
+  public void tearDown() throws DeviceNotAvailableException {
+    if (getDevice().executeAdbCommand("enable-verity").contains("Now reboot your device")) {
+      getDevice().reboot();
+    }
+    if (!wasRoot) {
+      getDevice().disableAdbRoot();
+    }
+  }
+}
+
diff --git a/fs_mgr/tests/vendor-overlay-test.xml b/fs_mgr/tests/vendor-overlay-test.xml
new file mode 100644
index 0000000..0b5c8cc
--- /dev/null
+++ b/fs_mgr/tests/vendor-overlay-test.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+<configuration description="Config for vendor overlay test cases">
+    <test class="com.android.tradefed.testtype.HostTest" >
+        <option name="jar" value="fs_mgr_vendor_overlay_test.jar" />
+    </test>
+</configuration>
+
diff --git a/init/property_service.cpp b/init/property_service.cpp
index d60b654..4bc857a 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -950,6 +950,13 @@
             LoadPropertyInfoFromFile("/vendor/etc/selinux/nonplat_property_contexts",
                                      &property_infos);
         }
+        if (access("/product/etc/selinux/product_property_contexts", R_OK) != -1) {
+            LoadPropertyInfoFromFile("/product/etc/selinux/product_property_contexts",
+                                     &property_infos);
+        }
+        if (access("/odm/etc/selinux/odm_property_contexts", R_OK) != -1) {
+            LoadPropertyInfoFromFile("/odm/etc/selinux/odm_property_contexts", &property_infos);
+        }
     } else {
         if (!LoadPropertyInfoFromFile("/plat_property_contexts", &property_infos)) {
             return;
@@ -958,6 +965,8 @@
             // Fallback to nonplat_* if vendor_* doesn't exist.
             LoadPropertyInfoFromFile("/nonplat_property_contexts", &property_infos);
         }
+        LoadPropertyInfoFromFile("/product_property_contexts", &property_infos);
+        LoadPropertyInfoFromFile("/odm_property_contexts", &property_infos);
     }
 
     auto serialized_contexts = std::string();
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 5394d7e..09998f0 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -137,6 +137,12 @@
 
 static constexpr const char* kApexPath = "/apex/";
 
+#if defined(__LP64__)
+static constexpr const char* kRuntimeApexLibPath = "/apex/com.android.runtime/lib64";
+#else
+static constexpr const char* kRuntimeApexLibPath = "/apex/com.android.runtime/lib";
+#endif
+
 static bool is_debuggable() {
   char debuggable[PROP_VALUE_MAX];
   property_get("ro.debuggable", debuggable, "0");
@@ -408,6 +414,14 @@
       }
     }
 
+    // Remove the public libs in the runtime namespace.
+    // These libs are listed in public.android.txt, but we don't want the rest of android
+    // in default namespace to dlopen the libs.
+    // For example, libicuuc.so is exposed to classloader namespace from runtime namespace.
+    // Unfortunately, it does not have stable C symbols, and default namespace should only use
+    // stable symbols in libandroidicu.so. http://b/120786417
+    removePublicLibsIfExistsInRuntimeApex(sonames);
+
     // android_init_namespaces() expects all the public libraries
     // to be loaded so that they can be found by soname alone.
     //
@@ -502,6 +516,27 @@
     }
   }
 
+  /**
+   * Remove the public libs in runtime namespace
+   */
+  void removePublicLibsIfExistsInRuntimeApex(std::vector<std::string>& sonames) {
+    for (const std::string& lib_name : kRuntimePublicLibraries) {
+      std::string path(kRuntimeApexLibPath);
+      path.append("/").append(lib_name);
+
+      struct stat s;
+      // Do nothing if the path in /apex does not exist.
+      // Runtime APEX must be mounted since libnativeloader is in the same APEX
+      if (stat(path.c_str(), &s) != 0) {
+        continue;
+      }
+
+      auto it = std::find(sonames.begin(), sonames.end(), lib_name);
+      if (it != sonames.end()) {
+        sonames.erase(it);
+      }
+    }
+  }
 
   bool ReadConfig(const std::string& configFile, std::vector<std::string>* sonames,
                   const std::function<bool(const std::string& /* soname */,
diff --git a/rootdir/etc/TEST_MAPPING b/rootdir/etc/TEST_MAPPING
index af2ec0f..e4d3d5e 100644
--- a/rootdir/etc/TEST_MAPPING
+++ b/rootdir/etc/TEST_MAPPING
@@ -1,7 +1,7 @@
 {
   "presubmit": [
     {
-      "name": "bionic-unit-tests"
+      "name": "CtsBionicTestCases"
     }
   ]
 }
diff --git a/rootdir/etc/ld.config.legacy.txt b/rootdir/etc/ld.config.legacy.txt
index e897d81..2d44bb6 100644
--- a/rootdir/etc/ld.config.legacy.txt
+++ b/rootdir/etc/ld.config.legacy.txt
@@ -54,6 +54,10 @@
 namespace.default.link.runtime.shared_libs += libnativebridge.so
 namespace.default.link.runtime.shared_libs += libnativehelper.so
 namespace.default.link.runtime.shared_libs += libnativeloader.so
+namespace.default.link.runtime.shared_libs += libandroidicu.so
+
+# TODO(b/122876336): Remove libpac.so once it's migrated to Webview
+namespace.default.link.runtime.shared_libs += libpac.so
 
 # When libnetd_resolv.so can't be found in the default namespace, search for it
 # in the resolv namespace. Don't allow any other libraries from the resolv namespace
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index df9abbd..cdfcfe1 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -132,6 +132,10 @@
 namespace.default.link.runtime.shared_libs += libnativebridge.so
 namespace.default.link.runtime.shared_libs += libnativehelper.so
 namespace.default.link.runtime.shared_libs += libnativeloader.so
+namespace.default.link.runtime.shared_libs += libandroidicu.so
+
+# TODO(b/122876336): Remove libpac.so once it's migrated to Webview
+namespace.default.link.runtime.shared_libs += libpac.so
 
 # When libnetd_resolv.so can't be found in the default namespace, search for it
 # in the resolv namespace. Don't allow any other libraries from the resolv namespace
@@ -481,7 +485,8 @@
 namespace.system.link.runtime.shared_libs += libnativebridge.so
 namespace.system.link.runtime.shared_libs += libnativehelper.so
 namespace.system.link.runtime.shared_libs += libnativeloader.so
-
+# Workaround for b/124772622
+namespace.system.link.runtime.shared_libs += libandroidicu.so
 
 ###############################################################################
 # Namespace config for native tests that need access to both system and vendor
diff --git a/rootdir/etc/ld.config.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt
index 3c97a49..59fd568 100644
--- a/rootdir/etc/ld.config.vndk_lite.txt
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -72,6 +72,10 @@
 namespace.default.link.runtime.shared_libs += libnativebridge.so
 namespace.default.link.runtime.shared_libs += libnativehelper.so
 namespace.default.link.runtime.shared_libs += libnativeloader.so
+namespace.default.link.runtime.shared_libs += libandroidicu.so
+
+# TODO(b/122876336): Remove libpac.so once it's migrated to Webview
+namespace.default.link.runtime.shared_libs += libpac.so
 
 # When libnetd_resolv.so can't be found in the default namespace, search for it
 # in the resolv namespace. Don't allow any other libraries from the resolv namespace
@@ -340,6 +344,8 @@
 namespace.default.link.runtime.shared_libs += libnativebridge.so
 namespace.default.link.runtime.shared_libs += libnativehelper.so
 namespace.default.link.runtime.shared_libs += libnativeloader.so
+# Workaround for b/124772622
+namespace.default.link.runtime.shared_libs += libandroidicu.so
 
 ###############################################################################
 # "runtime" APEX namespace
diff --git a/rootdir/init.environ.rc.in b/rootdir/init.environ.rc.in
index d10f7c1..d5665f2 100644
--- a/rootdir/init.environ.rc.in
+++ b/rootdir/init.environ.rc.in
@@ -1,5 +1,5 @@
 # set up the global environment
-on init
+on early-init
     export ANDROID_BOOTLOGO 1
     export ANDROID_ROOT /system
     export ANDROID_ASSETS /system/app
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 57032bc..8bd7c4c 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -538,7 +538,7 @@
     mkdir /data/apex/active 0750 root system
     mkdir /data/apex/backup 0700 root system
     mkdir /data/apex/sessions 0700 root system
-    mkdir /data/staging 0750 system system
+    mkdir /data/pkg_staging 0750 system system
 
     # NFC: create data/nfc for nv storage
     mkdir /data/nfc 0770 nfc nfc