Merge "Add system_ext/apex/*/bin to fs_config"
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 51d5755..d6945e3 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -25,6 +25,9 @@
       "name": "libcutils_test"
     },
     {
+      "name": "libmodprobe_tests"
+    },
+    {
       "name": "libprocinfo_test"
     },
     {
diff --git a/adb/daemon/shell_service.cpp b/adb/daemon/shell_service.cpp
index 0fb14c4..f62032d 100644
--- a/adb/daemon/shell_service.cpp
+++ b/adb/daemon/shell_service.cpp
@@ -85,7 +85,6 @@
 #include <paths.h>
 #include <pty.h>
 #include <pwd.h>
-#include <sys/select.h>
 #include <termios.h>
 
 #include <memory>
@@ -141,6 +140,20 @@
     return true;
 }
 
+struct SubprocessPollfds {
+    adb_pollfd pfds[3];
+
+    adb_pollfd* data() { return pfds; }
+    size_t size() { return 3; }
+
+    adb_pollfd* begin() { return pfds; }
+    adb_pollfd* end() { return pfds + size(); }
+
+    adb_pollfd& stdinout_pfd() { return pfds[0]; }
+    adb_pollfd& stderr_pfd() { return pfds[1]; }
+    adb_pollfd& protocol_pfd() { return pfds[2]; }
+};
+
 class Subprocess {
   public:
     Subprocess(std::string command, const char* terminal_type, SubprocessType type,
@@ -176,8 +189,7 @@
     void PassDataStreams();
     void WaitForExit();
 
-    unique_fd* SelectLoop(fd_set* master_read_set_ptr,
-                          fd_set* master_write_set_ptr);
+    unique_fd* PollLoop(SubprocessPollfds* pfds);
 
     // Input/output stream handlers. Success returns nullptr, failure returns
     // a pointer to the failed FD.
@@ -545,23 +557,23 @@
     }
 
     // Start by trying to read from the protocol FD, stdout, and stderr.
-    fd_set master_read_set, master_write_set;
-    FD_ZERO(&master_read_set);
-    FD_ZERO(&master_write_set);
-    for (unique_fd* sfd : {&protocol_sfd_, &stdinout_sfd_, &stderr_sfd_}) {
-        if (*sfd != -1) {
-            FD_SET(sfd->get(), &master_read_set);
-        }
-    }
+    SubprocessPollfds pfds;
+    pfds.stdinout_pfd() = {.fd = stdinout_sfd_.get(), .events = POLLIN};
+    pfds.stderr_pfd() = {.fd = stderr_sfd_.get(), .events = POLLIN};
+    pfds.protocol_pfd() = {.fd = protocol_sfd_.get(), .events = POLLIN};
 
     // Pass data until the protocol FD or both the subprocess pipes die, at
     // which point we can't pass any more data.
     while (protocol_sfd_ != -1 && (stdinout_sfd_ != -1 || stderr_sfd_ != -1)) {
-        unique_fd* dead_sfd = SelectLoop(&master_read_set, &master_write_set);
+        unique_fd* dead_sfd = PollLoop(&pfds);
         if (dead_sfd) {
             D("closing FD %d", dead_sfd->get());
-            FD_CLR(dead_sfd->get(), &master_read_set);
-            FD_CLR(dead_sfd->get(), &master_write_set);
+            auto it = std::find_if(pfds.begin(), pfds.end(), [=](const adb_pollfd& pfd) {
+                return pfd.fd == dead_sfd->get();
+            });
+            CHECK(it != pfds.end());
+            it->fd = -1;
+            it->events = 0;
             if (dead_sfd == &protocol_sfd_) {
                 // Using SIGHUP is a decent general way to indicate that the
                 // controlling process is going away. If specific signals are
@@ -583,30 +595,19 @@
     }
 }
 
-namespace {
-
-inline bool ValidAndInSet(const unique_fd& sfd, fd_set* set) {
-    return sfd != -1 && FD_ISSET(sfd.get(), set);
-}
-
-}   // namespace
-
-unique_fd* Subprocess::SelectLoop(fd_set* master_read_set_ptr,
-                                  fd_set* master_write_set_ptr) {
-    fd_set read_set, write_set;
-    int select_n =
-            std::max(std::max(protocol_sfd_.get(), stdinout_sfd_.get()), stderr_sfd_.get()) + 1;
+unique_fd* Subprocess::PollLoop(SubprocessPollfds* pfds) {
     unique_fd* dead_sfd = nullptr;
+    adb_pollfd& stdinout_pfd = pfds->stdinout_pfd();
+    adb_pollfd& stderr_pfd = pfds->stderr_pfd();
+    adb_pollfd& protocol_pfd = pfds->protocol_pfd();
 
-    // Keep calling select() and passing data until an FD closes/errors.
+    // Keep calling poll() and passing data until an FD closes/errors.
     while (!dead_sfd) {
-        memcpy(&read_set, master_read_set_ptr, sizeof(read_set));
-        memcpy(&write_set, master_write_set_ptr, sizeof(write_set));
-        if (select(select_n, &read_set, &write_set, nullptr, nullptr) < 0) {
+        if (adb_poll(pfds->data(), pfds->size(), -1) < 0) {
             if (errno == EINTR) {
                 continue;
             } else {
-                PLOG(ERROR) << "select failed, closing subprocess pipes";
+                PLOG(ERROR) << "poll failed, closing subprocess pipes";
                 stdinout_sfd_.reset(-1);
                 stderr_sfd_.reset(-1);
                 return nullptr;
@@ -614,34 +615,47 @@
         }
 
         // Read stdout, write to protocol FD.
-        if (ValidAndInSet(stdinout_sfd_, &read_set)) {
+        if (stdinout_pfd.fd != -1 && (stdinout_pfd.revents & POLLIN)) {
             dead_sfd = PassOutput(&stdinout_sfd_, ShellProtocol::kIdStdout);
         }
 
         // Read stderr, write to protocol FD.
-        if (!dead_sfd && ValidAndInSet(stderr_sfd_, &read_set)) {
+        if (!dead_sfd && stderr_pfd.fd != 1 && (stderr_pfd.revents & POLLIN)) {
             dead_sfd = PassOutput(&stderr_sfd_, ShellProtocol::kIdStderr);
         }
 
         // Read protocol FD, write to stdin.
-        if (!dead_sfd && ValidAndInSet(protocol_sfd_, &read_set)) {
+        if (!dead_sfd && protocol_pfd.fd != -1 && (protocol_pfd.revents & POLLIN)) {
             dead_sfd = PassInput();
             // If we didn't finish writing, block on stdin write.
             if (input_bytes_left_) {
-                FD_CLR(protocol_sfd_.get(), master_read_set_ptr);
-                FD_SET(stdinout_sfd_.get(), master_write_set_ptr);
+                protocol_pfd.events &= ~POLLIN;
+                stdinout_pfd.events |= POLLOUT;
             }
         }
 
         // Continue writing to stdin; only happens if a previous write blocked.
-        if (!dead_sfd && ValidAndInSet(stdinout_sfd_, &write_set)) {
+        if (!dead_sfd && stdinout_pfd.fd != -1 && (stdinout_pfd.revents & POLLOUT)) {
             dead_sfd = PassInput();
             // If we finished writing, go back to blocking on protocol read.
             if (!input_bytes_left_) {
-                FD_SET(protocol_sfd_.get(), master_read_set_ptr);
-                FD_CLR(stdinout_sfd_.get(), master_write_set_ptr);
+                protocol_pfd.events |= POLLIN;
+                stdinout_pfd.events &= ~POLLOUT;
             }
         }
+
+        // After handling all of the events we've received, check to see if any fds have died.
+        if (stdinout_pfd.revents & (POLLHUP | POLLRDHUP | POLLERR | POLLNVAL)) {
+            return &stdinout_sfd_;
+        }
+
+        if (stderr_pfd.revents & (POLLHUP | POLLRDHUP | POLLERR | POLLNVAL)) {
+            return &stderr_sfd_;
+        }
+
+        if (protocol_pfd.revents & (POLLHUP | POLLRDHUP | POLLERR | POLLNVAL)) {
+            return &protocol_sfd_;
+        }
     }  // while (!dead_sfd)
 
     return dead_sfd;
diff --git a/adb/test_device.py b/adb/test_device.py
index 57925e8..083adce 100755
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -536,6 +536,36 @@
         for i, success in result.iteritems():
             self.assertTrue(success)
 
+    def disabled_test_parallel(self):
+        """Spawn a bunch of `adb shell` instances in parallel.
+
+        This was broken historically due to the use of select, which only works
+        for fds that are numerically less than 1024.
+
+        Bug: http://b/141955761"""
+
+        n_procs = 2048
+        procs = dict()
+        for i in xrange(0, n_procs):
+            procs[i] = subprocess.Popen(
+                ['adb', 'shell', 'read foo; echo $foo; read rc; exit $rc'],
+                stdin=subprocess.PIPE,
+                stdout=subprocess.PIPE
+            )
+
+        for i in xrange(0, n_procs):
+            procs[i].stdin.write("%d\n" % i)
+
+        for i in xrange(0, n_procs):
+            response = procs[i].stdout.readline()
+            assert(response == "%d\n" % i)
+
+        for i in xrange(0, n_procs):
+            procs[i].stdin.write("%d\n" % (i % 256))
+
+        for i in xrange(0, n_procs):
+            assert(procs[i].wait() == i % 256)
+
 
 class ArgumentEscapingTest(DeviceTest):
     def test_shell_escaping(self):
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 5c0401e..48ce4cd 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -34,6 +34,7 @@
 #include <time.h>
 #include <unistd.h>
 
+#include <chrono>
 #include <functional>
 #include <map>
 #include <memory>
@@ -42,6 +43,7 @@
 #include <utility>
 #include <vector>
 
+#include <android-base/chrono_utils.h>
 #include <android-base/file.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
@@ -94,6 +96,7 @@
 using android::base::GetBoolProperty;
 using android::base::Realpath;
 using android::base::StartsWith;
+using android::base::Timer;
 using android::base::unique_fd;
 using android::dm::DeviceMapper;
 using android::dm::DmDeviceState;
@@ -1358,6 +1361,36 @@
     return ret;
 }
 
+static bool fs_mgr_unmount_all_data_mounts(const std::string& block_device) {
+    Timer t;
+    // TODO(b/135984674): should be configured via a read-only property.
+    std::chrono::milliseconds timeout = 5s;
+    while (true) {
+        bool umount_done = true;
+        Fstab proc_mounts;
+        if (!ReadFstabFromFile("/proc/mounts", &proc_mounts)) {
+            LERROR << "Can't read /proc/mounts";
+            return -1;
+        }
+        // Now proceed with other bind mounts on top of /data.
+        for (const auto& entry : proc_mounts) {
+            if (entry.blk_device == block_device) {
+                if (umount2(entry.mount_point.c_str(), 0) != 0) {
+                    umount_done = false;
+                }
+            }
+        }
+        if (umount_done) {
+            LINFO << "Unmounting /data took " << t;
+            return true;
+        }
+        if (t.duration() > timeout) {
+            return false;
+        }
+        std::this_thread::sleep_for(50ms);
+    }
+}
+
 // TODO(b/143970043): return different error codes based on which step failed.
 int fs_mgr_remount_userdata_into_checkpointing(Fstab* fstab) {
     Fstab proc_mounts;
@@ -1401,17 +1434,8 @@
         }
     } else {
         LINFO << "Unmounting /data before remounting into checkpointing mode";
-        // First make sure that all the bind-mounts on top of /data are unmounted.
-        for (const auto& entry : proc_mounts) {
-            if (entry.blk_device == block_device && entry.mount_point != "/data") {
-                LINFO << "Unmounting bind-mount " << entry.mount_point;
-                if (umount2(entry.mount_point.c_str(), UMOUNT_NOFOLLOW) != 0) {
-                    PWARNING << "Failed to unmount " << entry.mount_point;
-                }
-            }
-        }
-        if (umount2("/data", UMOUNT_NOFOLLOW) != 0) {
-            PERROR << "Failed to umount /data";
+        if (!fs_mgr_unmount_all_data_mounts(block_device)) {
+            LERROR << "Failed to umount /data";
             return -1;
         }
         DeviceMapper& dm = DeviceMapper::Instance();
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 62a19ab..3c32d8b 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -59,6 +59,7 @@
 #include <fs_mgr.h>
 #include <fscrypt/fscrypt.h>
 #include <libgsi/libgsi.h>
+#include <logwrap/logwrap.h>
 #include <selinux/android.h>
 #include <selinux/label.h>
 #include <selinux/selinux.h>
@@ -1176,6 +1177,42 @@
     return {};
 }
 
+static Result<void> GenerateLinkerConfiguration() {
+    const char* linkerconfig_binary = "/system/bin/linkerconfig";
+    const char* linkerconfig_target = "/linkerconfig/ld.config.txt";
+    const char* arguments[] = {linkerconfig_binary, "--target", linkerconfig_target};
+
+    if (logwrap_fork_execvp(arraysize(arguments), arguments, nullptr, false, LOG_KLOG, false,
+                            nullptr) != 0) {
+        return ErrnoError() << "failed to execute linkerconfig";
+    }
+
+    mode_t mode = get_mode("0444");
+    if (fchmodat(AT_FDCWD, linkerconfig_target, mode, AT_SYMLINK_NOFOLLOW) < 0) {
+        return ErrnoErrorIgnoreEnoent() << "fchmodat() failed";
+    }
+
+    LOG(INFO) << "linkerconfig generated " << linkerconfig_target
+              << " with mounted APEX modules info";
+
+    return {};
+}
+
+static bool IsApexUpdatable() {
+    static bool updatable = android::sysprop::ApexProperties::updatable().value_or(false);
+    return updatable;
+}
+
+static Result<void> do_update_linker_config(const BuiltinArguments&) {
+    // If APEX is not updatable, then all APEX information are already included in the first
+    // linker config generation, so there is no need to update linker configuration again.
+    if (IsApexUpdatable()) {
+        return GenerateLinkerConfiguration();
+    }
+
+    return {};
+}
+
 static Result<void> parse_apex_configs() {
     glob_t glob_result;
     static constexpr char glob_pattern[] = "/apex/*/etc/*.rc";
@@ -1251,6 +1288,12 @@
     if (!parse_configs) {
         return parse_configs.error();
     }
+
+    auto update_linker_config = do_update_linker_config(args);
+    if (!update_linker_config) {
+        return update_linker_config.error();
+    }
+
     return {};
 }
 
@@ -1317,6 +1360,7 @@
         {"perform_apex_config",     {0,     0,    {false,  do_perform_apex_config}}},
         {"umount",                  {1,     1,    {false,  do_umount}}},
         {"umount_all",              {1,     1,    {false,  do_umount_all}}},
+        {"update_linker_config",    {0,     0,    {false,  do_update_linker_config}}},
         {"readahead",               {1,     2,    {true,   do_readahead}}},
         {"remount_userdata",        {0,     0,    {false,  do_remount_userdata}}},
         {"restart",                 {1,     1,    {false,  do_restart}}},
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
index 648b3bb..93eb244 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -151,6 +151,20 @@
     return true;
 }
 
+static Result<void> MountLinkerConfigForDefaultNamespace() {
+    // No need to mount linkerconfig for default mount namespace if the path does not exist (which
+    // would mean it is already mounted)
+    if (access("/linkerconfig/default", 0) != 0) {
+        return {};
+    }
+
+    if (mount("/linkerconfig/default", "/linkerconfig", nullptr, MS_BIND | MS_REC, nullptr) != 0) {
+        return ErrnoError() << "Failed to mount linker configuration for default mount namespace.";
+    }
+
+    return {};
+}
+
 static android::base::unique_fd bootstrap_ns_fd;
 static android::base::unique_fd default_ns_fd;
 
@@ -222,6 +236,11 @@
             PLOG(ERROR) << "Failed to switch back to the default mount namespace.";
             return false;
         }
+
+        if (auto result = MountLinkerConfigForDefaultNamespace(); !result) {
+            LOG(ERROR) << result.error();
+            return false;
+        }
     }
 
     LOG(INFO) << "Switched to default mount namespace";
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 0e61234..5ca1fee 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -834,6 +834,10 @@
 }
 
 static void HandleUserspaceReboot() {
+    if (!android::sysprop::InitProperties::userspace_reboot_in_progress().value_or(false)) {
+        LOG(ERROR) << "Attempted a userspace reboot on a device that doesn't support it";
+        return;
+    }
     // Spinnig up a separate thread will fail the setns call later in the boot sequence.
     // Fork a new process to monitor userspace reboot while we are investigating a better solution.
     pid_t pid = fork();
diff --git a/init/sysprop/InitProperties.sysprop b/init/sysprop/InitProperties.sysprop
index d6a1ab6..c856358 100644
--- a/init/sysprop/InitProperties.sysprop
+++ b/init/sysprop/InitProperties.sysprop
@@ -25,3 +25,12 @@
     integer_as_bool: true
 }
 
+# Shows whenever the device supports userspace reboot or not.
+prop {
+    api_name: "is_userspace_reboot_supported"
+    type: Boolean
+    scope: System
+    access: Readonly
+    prop_name: "ro.init.userspace_reboot.is_supported"
+    integer_as_bool: true
+}
diff --git a/init/sysprop/api/com.android.sysprop.init-current.txt b/init/sysprop/api/com.android.sysprop.init-current.txt
index 8da50e0..b8bcef9 100644
--- a/init/sysprop/api/com.android.sysprop.init-current.txt
+++ b/init/sysprop/api/com.android.sysprop.init-current.txt
@@ -1,6 +1,11 @@
 props {
   module: "android.sysprop.InitProperties"
   prop {
+    api_name: "is_userspace_reboot_supported"
+    prop_name: "ro.init.userspace_reboot.is_supported"
+    integer_as_bool: true
+  }
+  prop {
     api_name: "userspace_reboot_in_progress"
     access: ReadWrite
     prop_name: "sys.init.userspace_reboot.in_progress"
diff --git a/libstats/Android.bp b/libstats/Android.bp
deleted file mode 100644
index 0440087..0000000
--- a/libstats/Android.bp
+++ /dev/null
@@ -1,40 +0,0 @@
-//
-// Copyright (C) 2018 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.
-//
-
-// ==========================================================
-// Native library to write stats log to statsd socket
-// ==========================================================
-cc_library {
-    name: "libstatssocket",
-    srcs: [
-        "stats_event_list.c",
-        "statsd_writer.c",
-        "stats_event.c",
-    ],
-    host_supported: true,
-    cflags: [
-        "-Wall",
-        "-Werror",
-        "-DLIBLOG_LOG_TAG=1006",
-        "-DWRITE_TO_STATSD=1",
-        "-DWRITE_TO_LOGD=0",
-    ],
-    export_include_dirs: ["include"],
-    shared_libs: [
-        "libcutils",
-        "liblog",
-    ],
-}
diff --git a/libstats/push_compat/Android.bp b/libstats/push_compat/Android.bp
new file mode 100644
index 0000000..465c05a
--- /dev/null
+++ b/libstats/push_compat/Android.bp
@@ -0,0 +1,59 @@
+//
+// 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.
+//
+
+// =========================================================================
+// Native library that toggles between the old and new statsd socket
+// protocols. This library should only be used by DNS resolver or other
+// native modules on Q that log pushed atoms to statsd.
+// =========================================================================
+cc_defaults {
+    name: "libstatspush_compat_defaults",
+    srcs: [
+        "statsd_writer.c",
+        "stats_event_list.c",
+        "StatsEventCompat.cpp"
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-DWRITE_TO_STATSD=1",
+        "-DWRITE_TO_LOGD=0",
+    ],
+    header_libs: ["libstatssocket_headers"],
+    static_libs: [
+        "libbase",
+        "liblog",
+        "libutils",
+    ],
+}
+
+cc_library {
+    name: "libstatspush_compat",
+    defaults: ["libstatspush_compat_defaults"],
+    export_include_dirs: ["include"],
+    static_libs: ["libgtest_prod"],
+}
+
+cc_test {
+    name: "libstatspush_compat_test",
+    defaults: ["libstatspush_compat_defaults"],
+    test_suites: ["device_tests"],
+    srcs: [
+        "tests/StatsEventCompat_test.cpp",
+    ],
+    static_libs: ["libgmock"],
+}
+
diff --git a/libstats/push_compat/StatsEventCompat.cpp b/libstats/push_compat/StatsEventCompat.cpp
new file mode 100644
index 0000000..edfa070
--- /dev/null
+++ b/libstats/push_compat/StatsEventCompat.cpp
@@ -0,0 +1,221 @@
+/*
+ * 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 "include/StatsEventCompat.h"
+#include <android-base/properties.h>
+#include <android/api-level.h>
+#include <android/log.h>
+#include <dlfcn.h>
+#include <utils/SystemClock.h>
+
+using android::base::GetProperty;
+
+const static int kStatsEventTag = 1937006964;
+
+/* Checking ro.build.version.release is fragile, as the release field is
+ * an opaque string without structural guarantees. However, testing confirms
+ * that on Q devices, the property is "10," and on R, it is "R." Until
+ * android_get_device_api_level() is updated, this is the only solution.
+ *
+ * TODO(b/146019024): migrate to android_get_device_api_level()
+ */
+const bool StatsEventCompat::mPlatformAtLeastR =
+        GetProperty("ro.build.version.codename", "") == "R" ||
+        android_get_device_api_level() > __ANDROID_API_Q__;
+
+// definitions of static class variables
+bool StatsEventCompat::mAttemptedLoad = false;
+struct stats_event_api_table* StatsEventCompat::mStatsEventApi = nullptr;
+std::mutex StatsEventCompat::mLoadLock;
+
+StatsEventCompat::StatsEventCompat() : mEventQ(kStatsEventTag) {
+    // guard loading because StatsEventCompat might be called from multithreaded
+    // environment
+    {
+        std::lock_guard<std::mutex> lg(mLoadLock);
+        if (!mAttemptedLoad) {
+            void* handle = dlopen("libstatssocket.so", RTLD_NOW);
+            if (handle) {
+                mStatsEventApi = (struct stats_event_api_table*)dlsym(handle, "table");
+            } else {
+                ALOGE("dlopen failed: %s\n", dlerror());
+            }
+        }
+        mAttemptedLoad = true;
+    }
+
+    if (mStatsEventApi) {
+        mEventR = mStatsEventApi->obtain();
+    } else if (!mPlatformAtLeastR) {
+        mEventQ << android::elapsedRealtimeNano();
+    }
+}
+
+StatsEventCompat::~StatsEventCompat() {
+    if (mStatsEventApi) mStatsEventApi->release(mEventR);
+}
+
+void StatsEventCompat::setAtomId(int32_t atomId) {
+    if (mStatsEventApi) {
+        mStatsEventApi->set_atom_id(mEventR, (uint32_t)atomId);
+    } else if (!mPlatformAtLeastR) {
+        mEventQ << atomId;
+    }
+}
+
+void StatsEventCompat::writeInt32(int32_t value) {
+    if (mStatsEventApi) {
+        mStatsEventApi->write_int32(mEventR, value);
+    } else if (!mPlatformAtLeastR) {
+        mEventQ << value;
+    }
+}
+
+void StatsEventCompat::writeInt64(int64_t value) {
+    if (mStatsEventApi) {
+        mStatsEventApi->write_int64(mEventR, value);
+    } else if (!mPlatformAtLeastR) {
+        mEventQ << value;
+    }
+}
+
+void StatsEventCompat::writeFloat(float value) {
+    if (mStatsEventApi) {
+        mStatsEventApi->write_float(mEventR, value);
+    } else if (!mPlatformAtLeastR) {
+        mEventQ << value;
+    }
+}
+
+void StatsEventCompat::writeBool(bool value) {
+    if (mStatsEventApi) {
+        mStatsEventApi->write_bool(mEventR, value);
+    } else if (!mPlatformAtLeastR) {
+        mEventQ << value;
+    }
+}
+
+void StatsEventCompat::writeByteArray(const char* buffer, size_t length) {
+    if (mStatsEventApi) {
+        mStatsEventApi->write_byte_array(mEventR, (const uint8_t*)buffer, length);
+    } else if (!mPlatformAtLeastR) {
+        mEventQ.AppendCharArray(buffer, length);
+    }
+}
+
+void StatsEventCompat::writeString(const char* value) {
+    if (value == nullptr) value = "";
+
+    if (mStatsEventApi) {
+        mStatsEventApi->write_string8(mEventR, value);
+    } else if (!mPlatformAtLeastR) {
+        mEventQ << value;
+    }
+}
+
+void StatsEventCompat::writeAttributionChain(const int32_t* uids, size_t numUids,
+                                             const vector<const char*>& tags) {
+    if (mStatsEventApi) {
+        mStatsEventApi->write_attribution_chain(mEventR, (const uint32_t*)uids, tags.data(),
+                                                (uint8_t)numUids);
+    } else if (!mPlatformAtLeastR) {
+        mEventQ.begin();
+        for (size_t i = 0; i < numUids; i++) {
+            mEventQ.begin();
+            mEventQ << uids[i];
+            const char* tag = tags[i] ? tags[i] : "";
+            mEventQ << tag;
+            mEventQ.end();
+        }
+        mEventQ.end();
+    }
+}
+
+void StatsEventCompat::writeKeyValuePairs(const map<int, int32_t>& int32Map,
+                                          const map<int, int64_t>& int64Map,
+                                          const map<int, const char*>& stringMap,
+                                          const map<int, float>& floatMap) {
+    if (mStatsEventApi) {
+        vector<struct key_value_pair> pairs;
+
+        for (const auto& it : int32Map) {
+            pairs.push_back({.key = it.first, .valueType = INT32_TYPE, .int32Value = it.second});
+        }
+        for (const auto& it : int64Map) {
+            pairs.push_back({.key = it.first, .valueType = INT64_TYPE, .int64Value = it.second});
+        }
+        for (const auto& it : stringMap) {
+            pairs.push_back({.key = it.first, .valueType = STRING_TYPE, .stringValue = it.second});
+        }
+        for (const auto& it : floatMap) {
+            pairs.push_back({.key = it.first, .valueType = FLOAT_TYPE, .floatValue = it.second});
+        }
+
+        mStatsEventApi->write_key_value_pairs(mEventR, pairs.data(), (uint8_t)pairs.size());
+    }
+
+    else if (!mPlatformAtLeastR) {
+        mEventQ.begin();
+        writeKeyValuePairMap(int32Map);
+        writeKeyValuePairMap(int64Map);
+        writeKeyValuePairMap(stringMap);
+        writeKeyValuePairMap(floatMap);
+        mEventQ.end();
+    }
+}
+
+template <class T>
+void StatsEventCompat::writeKeyValuePairMap(const map<int, T>& keyValuePairMap) {
+    for (const auto& it : keyValuePairMap) {
+        mEventQ.begin();
+        mEventQ << it.first;
+        mEventQ << it.second;
+        mEventQ.end();
+    }
+}
+
+// explicitly specify which types we're going to use
+template void StatsEventCompat::writeKeyValuePairMap<int32_t>(const map<int, int32_t>&);
+template void StatsEventCompat::writeKeyValuePairMap<int64_t>(const map<int, int64_t>&);
+template void StatsEventCompat::writeKeyValuePairMap<float>(const map<int, float>&);
+template void StatsEventCompat::writeKeyValuePairMap<const char*>(const map<int, const char*>&);
+
+void StatsEventCompat::addBoolAnnotation(uint8_t annotationId, bool value) {
+    if (mStatsEventApi) mStatsEventApi->add_bool_annotation(mEventR, annotationId, value);
+    // Don't do anything if on Q.
+}
+
+void StatsEventCompat::addInt32Annotation(uint8_t annotationId, int32_t value) {
+    if (mStatsEventApi) mStatsEventApi->add_int32_annotation(mEventR, annotationId, value);
+    // Don't do anything if on Q.
+}
+
+int StatsEventCompat::writeToSocket() {
+    if (mStatsEventApi) {
+        mStatsEventApi->build(mEventR);
+        return mStatsEventApi->write(mEventR);
+    }
+
+    if (!mPlatformAtLeastR) return mEventQ.write(LOG_ID_STATS);
+
+    // We reach here only if we're on R, but libstatspush_compat was unable to
+    // be loaded using dlopen.
+    return -ENOLINK;
+}
+
+bool StatsEventCompat::usesNewSchema() {
+    return mStatsEventApi != nullptr;
+}
diff --git a/libstats/push_compat/include/StatsEventCompat.h b/libstats/push_compat/include/StatsEventCompat.h
new file mode 100644
index 0000000..a8cde68
--- /dev/null
+++ b/libstats/push_compat/include/StatsEventCompat.h
@@ -0,0 +1,71 @@
+/*
+ * 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 <gtest/gtest_prod.h>
+#include <map>
+#include <mutex>
+#include <vector>
+#include "stats_event.h"
+#include "stats_event_list.h"
+
+using std::map;
+using std::vector;
+
+class StatsEventCompat {
+  public:
+    StatsEventCompat();
+    ~StatsEventCompat();
+
+    void setAtomId(int32_t atomId);
+    void writeInt32(int32_t value);
+    void writeInt64(int64_t value);
+    void writeFloat(float value);
+    void writeBool(bool value);
+    void writeByteArray(const char* buffer, size_t length);
+    void writeString(const char* value);
+
+    // Pre-condition: numUids == tags.size()
+    void writeAttributionChain(const int32_t* uids, size_t numUids,
+                               const vector<const char*>& tags);
+
+    void writeKeyValuePairs(const map<int, int32_t>& int32Map, const map<int, int64_t>& int64Map,
+                            const map<int, const char*>& stringMap,
+                            const map<int, float>& floatMap);
+
+    void addBoolAnnotation(uint8_t annotationId, bool value);
+    void addInt32Annotation(uint8_t annotationId, int32_t value);
+
+    int writeToSocket();
+
+  private:
+    // static member variables
+    const static bool mPlatformAtLeastR;
+    static bool mAttemptedLoad;
+    static std::mutex mLoadLock;
+    static struct stats_event_api_table* mStatsEventApi;
+
+    // non-static member variables
+    struct stats_event* mEventR = nullptr;
+    stats_event_list mEventQ;
+
+    template <class T>
+    void writeKeyValuePairMap(const map<int, T>& keyValuePairMap);
+
+    bool usesNewSchema();
+    FRIEND_TEST(StatsEventCompatTest, TestDynamicLoading);
+};
diff --git a/libstats/include/stats_event_list.h b/libstats/push_compat/include/stats_event_list.h
similarity index 100%
rename from libstats/include/stats_event_list.h
rename to libstats/push_compat/include/stats_event_list.h
diff --git a/libstats/stats_event_list.c b/libstats/push_compat/stats_event_list.c
similarity index 100%
rename from libstats/stats_event_list.c
rename to libstats/push_compat/stats_event_list.c
diff --git a/libstats/statsd_writer.c b/libstats/push_compat/statsd_writer.c
similarity index 98%
rename from libstats/statsd_writer.c
rename to libstats/push_compat/statsd_writer.c
index 073b67f..04d3b46 100644
--- a/libstats/statsd_writer.c
+++ b/libstats/push_compat/statsd_writer.c
@@ -101,7 +101,7 @@
             strcpy(un.sun_path, "/dev/socket/statsdw");
 
             if (TEMP_FAILURE_RETRY(
-                    connect(sock, (struct sockaddr*)&un, sizeof(struct sockaddr_un))) < 0) {
+                        connect(sock, (struct sockaddr*)&un, sizeof(struct sockaddr_un))) < 0) {
                 ret = -errno;
                 switch (ret) {
                     case -ENOTCONN:
diff --git a/libstats/statsd_writer.h b/libstats/push_compat/statsd_writer.h
similarity index 100%
copy from libstats/statsd_writer.h
copy to libstats/push_compat/statsd_writer.h
diff --git a/libstats/push_compat/tests/StatsEventCompat_test.cpp b/libstats/push_compat/tests/StatsEventCompat_test.cpp
new file mode 100644
index 0000000..2be24ec
--- /dev/null
+++ b/libstats/push_compat/tests/StatsEventCompat_test.cpp
@@ -0,0 +1,38 @@
+/*
+ * 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 "include/StatsEventCompat.h"
+#include <android-base/properties.h>
+#include <android/api-level.h>
+#include <gtest/gtest.h>
+
+using android::base::GetProperty;
+
+/* Checking ro.build.version.release is fragile, as the release field is
+ * an opaque string without structural guarantees. However, testing confirms
+ * that on Q devices, the property is "10," and on R, it is "R." Until
+ * android_get_device_api_level() is updated, this is the only solution.
+ *
+ *
+ * TODO(b/146019024): migrate to android_get_device_api_level()
+ */
+const static bool mPlatformAtLeastR = GetProperty("ro.build.version.release", "") == "R" ||
+                                      android_get_device_api_level() > __ANDROID_API_Q__;
+
+TEST(StatsEventCompatTest, TestDynamicLoading) {
+    StatsEventCompat event;
+    EXPECT_EQ(mPlatformAtLeastR, event.usesNewSchema());
+}
diff --git a/libstats/socket/Android.bp b/libstats/socket/Android.bp
new file mode 100644
index 0000000..94c405d
--- /dev/null
+++ b/libstats/socket/Android.bp
@@ -0,0 +1,69 @@
+//
+// Copyright (C) 2018 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.
+//
+
+// =========================================================================
+// Native library to write stats log to statsd socket on Android R and later
+// =========================================================================
+cc_library {
+    name: "libstatssocket",
+    srcs: [
+        "stats_buffer_writer.c",
+        "stats_event.c",
+        // TODO(b/145573568): Remove stats_event_list once stats_event
+        // migration is complete.
+        "stats_event_list.c",
+        "statsd_writer.c",
+    ],
+    host_supported: true,
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-DLIBLOG_LOG_TAG=1006",
+        "-DWRITE_TO_STATSD=1",
+        "-DWRITE_TO_LOGD=0",
+    ],
+    export_include_dirs: ["include"],
+    shared_libs: [
+        "libcutils",
+        "liblog",
+    ],
+}
+
+cc_library_headers {
+    name: "libstatssocket_headers",
+    export_include_dirs: ["include"],
+    host_supported: true,
+}
+
+cc_benchmark {
+    name: "libstatssocket_benchmark",
+    srcs: [
+        "benchmark/main.cpp",
+        "benchmark/stats_event_benchmark.cpp",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    static_libs: [
+        "libstatssocket",
+    ],
+    shared_libs: [
+        "libcutils",
+        "liblog",
+        "libgtest_prod",
+    ],
+}
diff --git a/libstats/socket/benchmark/main.cpp b/libstats/socket/benchmark/main.cpp
new file mode 100644
index 0000000..5ebdf6e
--- /dev/null
+++ b/libstats/socket/benchmark/main.cpp
@@ -0,0 +1,19 @@
+/*
+ * 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 <benchmark/benchmark.h>
+
+BENCHMARK_MAIN();
diff --git a/libstats/socket/benchmark/stats_event_benchmark.cpp b/libstats/socket/benchmark/stats_event_benchmark.cpp
new file mode 100644
index 0000000..b487c4d
--- /dev/null
+++ b/libstats/socket/benchmark/stats_event_benchmark.cpp
@@ -0,0 +1,53 @@
+/*
+ * 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 "benchmark/benchmark.h"
+#include "stats_event.h"
+
+static struct stats_event* constructStatsEvent() {
+    struct stats_event* event = stats_event_obtain();
+    stats_event_set_atom_id(event, 100);
+
+    // randomly sample atom size
+    for (int i = 0; i < rand() % 800; i++) {
+        stats_event_write_int32(event, i);
+    }
+
+    return event;
+}
+
+static void BM_stats_event_truncate_buffer(benchmark::State& state) {
+    while (state.KeepRunning()) {
+        struct stats_event* event = constructStatsEvent();
+        stats_event_build(event);
+        stats_event_write(event);
+        stats_event_release(event);
+    }
+}
+
+BENCHMARK(BM_stats_event_truncate_buffer);
+
+static void BM_stats_event_full_buffer(benchmark::State& state) {
+    while (state.KeepRunning()) {
+        struct stats_event* event = constructStatsEvent();
+        stats_event_truncate_buffer(event, false);
+        stats_event_build(event);
+        stats_event_write(event);
+        stats_event_release(event);
+    }
+}
+
+BENCHMARK(BM_stats_event_full_buffer);
diff --git a/libstats/socket/include/stats_buffer_writer.h b/libstats/socket/include/stats_buffer_writer.h
new file mode 100644
index 0000000..de4a5e2
--- /dev/null
+++ b/libstats/socket/include/stats_buffer_writer.h
@@ -0,0 +1,29 @@
+/*
+ * 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 <stddef.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif  // __CPLUSPLUS
+void stats_log_close();
+int write_buffer_to_statsd(void* buffer, size_t size, uint32_t atomId);
+#ifdef __cplusplus
+}
+#endif  // __CPLUSPLUS
diff --git a/libstats/include/stats_event.h b/libstats/socket/include/stats_event.h
similarity index 73%
rename from libstats/include/stats_event.h
rename to libstats/socket/include/stats_event.h
index 89cb420..080e957 100644
--- a/libstats/include/stats_event.h
+++ b/libstats/socket/include/stats_event.h
@@ -76,7 +76,7 @@
 
 #ifdef __cplusplus
 extern "C" {
-#endif
+#endif  // __CPLUSPLUS
 
 struct stats_event;
 
@@ -85,7 +85,7 @@
 // The build function can be called multiple times without error. If the event
 // has been built before, this function is a no-op.
 void stats_event_build(struct stats_event* event);
-void stats_event_write(struct stats_event* event);
+int stats_event_write(struct stats_event* event);
 void stats_event_release(struct stats_event* event);
 
 void stats_event_set_atom_id(struct stats_event* event, uint32_t atomId);
@@ -95,14 +95,14 @@
 void stats_event_write_float(struct stats_event* event, float value);
 void stats_event_write_bool(struct stats_event* event, bool value);
 
-void stats_event_write_byte_array(struct stats_event* event, uint8_t* buf, size_t numBytes);
+void stats_event_write_byte_array(struct stats_event* event, const uint8_t* buf, size_t numBytes);
 
 // Buf must be null-terminated.
-void stats_event_write_string8(struct stats_event* event, const char* buf);
+void stats_event_write_string8(struct stats_event* event, const char* value);
 
 // Tags must be null-terminated.
-void stats_event_write_attribution_chain(struct stats_event* event, uint32_t* uids,
-                                         const char** tags, uint8_t numNodes);
+void stats_event_write_attribution_chain(struct stats_event* event, const uint32_t* uids,
+                                         const char* const* tags, uint8_t numNodes);
 
 /* key_value_pair struct can be constructed as follows:
  *    struct key_value_pair pair = {.key = key, .valueType = STRING_TYPE,
@@ -127,11 +127,38 @@
                                       int32_t value);
 
 uint32_t stats_event_get_atom_id(struct stats_event* event);
+// Size is an output parameter.
 uint8_t* stats_event_get_buffer(struct stats_event* event, size_t* size);
 uint32_t stats_event_get_errors(struct stats_event* event);
 
+// This table is used by StatsEventCompat to access the stats_event API.
+struct stats_event_api_table {
+    struct stats_event* (*obtain)(void);
+    void (*build)(struct stats_event*);
+    int (*write)(struct stats_event*);
+    void (*release)(struct stats_event*);
+    void (*set_atom_id)(struct stats_event*, uint32_t);
+    void (*write_int32)(struct stats_event*, int32_t);
+    void (*write_int64)(struct stats_event*, int64_t);
+    void (*write_float)(struct stats_event*, float);
+    void (*write_bool)(struct stats_event*, bool);
+    void (*write_byte_array)(struct stats_event*, const uint8_t*, size_t);
+    void (*write_string8)(struct stats_event*, const char*);
+    void (*write_attribution_chain)(struct stats_event*, const uint32_t*, const char* const*,
+                                    uint8_t);
+    void (*write_key_value_pairs)(struct stats_event*, struct key_value_pair*, uint8_t);
+    void (*add_bool_annotation)(struct stats_event*, uint8_t, bool);
+    void (*add_int32_annotation)(struct stats_event*, uint8_t, int32_t);
+    uint32_t (*get_atom_id)(struct stats_event*);
+    uint8_t* (*get_buffer)(struct stats_event*, size_t*);
+    uint32_t (*get_errors)(struct stats_event*);
+};
+
+// exposed for benchmarking only
+void stats_event_truncate_buffer(struct stats_event* event, bool truncate);
+
 #ifdef __cplusplus
 }
-#endif
+#endif  // __CPLUSPLUS
 
 #endif  // ANDROID_STATS_LOG_STATS_EVENT_H
diff --git a/libstats/socket/include/stats_event_list.h b/libstats/socket/include/stats_event_list.h
new file mode 100644
index 0000000..7a26536
--- /dev/null
+++ b/libstats/socket/include/stats_event_list.h
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2018, 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 <log/log_event_list.h>
+#include <sys/uio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+void reset_log_context(android_log_context ctx);
+int write_to_logger(android_log_context context, log_id_t id);
+void note_log_drop(int error, int atomId);
+void stats_log_close();
+int android_log_write_char_array(android_log_context ctx, const char* value, size_t len);
+#ifdef __cplusplus
+}
+#endif
+
+#ifdef __cplusplus
+/**
+ * A copy of android_log_event_list class.
+ *
+ * android_log_event_list is going to be deprecated soon, so copy it here to
+ * avoid creating dependency on upstream code. TODO(b/78304629): Rewrite this
+ * code.
+ */
+class stats_event_list {
+  private:
+    android_log_context ctx;
+    int ret;
+
+    stats_event_list(const stats_event_list&) = delete;
+    void operator=(const stats_event_list&) = delete;
+
+  public:
+    explicit stats_event_list(int tag) : ret(0) {
+        ctx = create_android_logger(static_cast<uint32_t>(tag));
+    }
+    ~stats_event_list() { android_log_destroy(&ctx); }
+
+    int close() {
+        int retval = android_log_destroy(&ctx);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return retval;
+    }
+
+    /* To allow above C calls to use this class as parameter */
+    operator android_log_context() const { return ctx; }
+
+    /* return errors or transmit status */
+    int status() const { return ret; }
+
+    int begin() {
+        int retval = android_log_write_list_begin(ctx);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return ret;
+    }
+    int end() {
+        int retval = android_log_write_list_end(ctx);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return ret;
+    }
+
+    stats_event_list& operator<<(int32_t value) {
+        int retval = android_log_write_int32(ctx, value);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return *this;
+    }
+
+    stats_event_list& operator<<(uint32_t value) {
+        int retval = android_log_write_int32(ctx, static_cast<int32_t>(value));
+        if (retval < 0) {
+            ret = retval;
+        }
+        return *this;
+    }
+
+    stats_event_list& operator<<(bool value) {
+        int retval = android_log_write_int32(ctx, value ? 1 : 0);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return *this;
+    }
+
+    stats_event_list& operator<<(int64_t value) {
+        int retval = android_log_write_int64(ctx, value);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return *this;
+    }
+
+    stats_event_list& operator<<(uint64_t value) {
+        int retval = android_log_write_int64(ctx, static_cast<int64_t>(value));
+        if (retval < 0) {
+            ret = retval;
+        }
+        return *this;
+    }
+
+    stats_event_list& operator<<(const char* value) {
+        int retval = android_log_write_string8(ctx, value);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return *this;
+    }
+
+    stats_event_list& operator<<(const std::string& value) {
+        int retval = android_log_write_string8_len(ctx, value.data(), value.length());
+        if (retval < 0) {
+            ret = retval;
+        }
+        return *this;
+    }
+
+    stats_event_list& operator<<(float value) {
+        int retval = android_log_write_float32(ctx, value);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return *this;
+    }
+
+    int write(log_id_t id = LOG_ID_EVENTS) {
+        /* facilitate -EBUSY retry */
+        if ((ret == -EBUSY) || (ret > 0)) {
+            ret = 0;
+        }
+        int retval = write_to_logger(ctx, id);
+        /* existing errors trump transmission errors */
+        if (!ret) {
+            ret = retval;
+        }
+        return ret;
+    }
+
+    /*
+     * Append<Type> methods removes any integer promotion
+     * confusion, and adds access to string with length.
+     * Append methods are also added for all types for
+     * convenience.
+     */
+
+    bool AppendInt(int32_t value) {
+        int retval = android_log_write_int32(ctx, value);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return ret >= 0;
+    }
+
+    bool AppendLong(int64_t value) {
+        int retval = android_log_write_int64(ctx, value);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return ret >= 0;
+    }
+
+    bool AppendString(const char* value) {
+        int retval = android_log_write_string8(ctx, value);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return ret >= 0;
+    }
+
+    bool AppendString(const char* value, size_t len) {
+        int retval = android_log_write_string8_len(ctx, value, len);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return ret >= 0;
+    }
+
+    bool AppendString(const std::string& value) {
+        int retval = android_log_write_string8_len(ctx, value.data(), value.length());
+        if (retval < 0) {
+            ret = retval;
+        }
+        return ret;
+    }
+
+    bool Append(const std::string& value) {
+        int retval = android_log_write_string8_len(ctx, value.data(), value.length());
+        if (retval < 0) {
+            ret = retval;
+        }
+        return ret;
+    }
+
+    bool AppendFloat(float value) {
+        int retval = android_log_write_float32(ctx, value);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return ret >= 0;
+    }
+
+    template <typename Tvalue>
+    bool Append(Tvalue value) {
+        *this << value;
+        return ret >= 0;
+    }
+
+    bool Append(const char* value, size_t len) {
+        int retval = android_log_write_string8_len(ctx, value, len);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return ret >= 0;
+    }
+
+    bool AppendCharArray(const char* value, size_t len) {
+        int retval = android_log_write_char_array(ctx, value, len);
+        if (retval < 0) {
+            ret = retval;
+        }
+        return ret >= 0;
+    }
+};
+
+#endif
diff --git a/libstats/socket/stats_buffer_writer.c b/libstats/socket/stats_buffer_writer.c
new file mode 100644
index 0000000..c5c591d
--- /dev/null
+++ b/libstats/socket/stats_buffer_writer.c
@@ -0,0 +1,130 @@
+/*
+ * 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 "include/stats_buffer_writer.h"
+#ifdef __ANDROID__
+#include <cutils/properties.h>
+#endif
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include "statsd_writer.h"
+
+static const uint32_t kStatsEventTag = 1937006964;
+
+extern struct android_log_transport_write statsdLoggerWrite;
+
+static int __write_to_statsd_init(struct iovec* vec, size_t nr);
+static int (*__write_to_statsd)(struct iovec* vec, size_t nr) = __write_to_statsd_init;
+
+void note_log_drop(int error, int atomId) {
+    statsdLoggerWrite.noteDrop(error, atomId);
+}
+
+void stats_log_close() {
+    statsd_writer_init_lock();
+    __write_to_statsd = __write_to_statsd_init;
+    if (statsdLoggerWrite.close) {
+        (*statsdLoggerWrite.close)();
+    }
+    statsd_writer_init_unlock();
+}
+
+int write_buffer_to_statsd(void* buffer, size_t size, uint32_t atomId) {
+    int ret = 1;
+
+#ifdef __ANDROID__
+    bool statsdEnabled = property_get_bool("ro.statsd.enable", true);
+#else
+    bool statsdEnabled = false;
+#endif
+
+    if (statsdEnabled) {
+        struct iovec vecs[2];
+        vecs[0].iov_base = (void*)&kStatsEventTag;
+        vecs[0].iov_len = sizeof(kStatsEventTag);
+        vecs[1].iov_base = buffer;
+        vecs[1].iov_len = size;
+
+        ret = __write_to_statsd(vecs, 2);
+
+        if (ret < 0) {
+            note_log_drop(ret, atomId);
+        }
+    }
+
+    return ret;
+}
+
+static int __write_to_stats_daemon(struct iovec* vec, size_t nr) {
+    int save_errno;
+    struct timespec ts;
+    size_t len, i;
+
+    for (len = i = 0; i < nr; ++i) {
+        len += vec[i].iov_len;
+    }
+    if (!len) {
+        return -EINVAL;
+    }
+
+    save_errno = errno;
+#if defined(__ANDROID__)
+    clock_gettime(CLOCK_REALTIME, &ts);
+#else
+    struct timeval tv;
+    gettimeofday(&tv, NULL);
+    ts.tv_sec = tv.tv_sec;
+    ts.tv_nsec = tv.tv_usec * 1000;
+#endif
+
+    int ret = (int)(*statsdLoggerWrite.write)(&ts, vec, nr);
+    errno = save_errno;
+    return ret;
+}
+
+static int __write_to_statsd_initialize_locked() {
+    if (!statsdLoggerWrite.open || ((*statsdLoggerWrite.open)() < 0)) {
+        if (statsdLoggerWrite.close) {
+            (*statsdLoggerWrite.close)();
+            return -ENODEV;
+        }
+    }
+    return 1;
+}
+
+static int __write_to_statsd_init(struct iovec* vec, size_t nr) {
+    int ret, save_errno = errno;
+
+    statsd_writer_init_lock();
+
+    if (__write_to_statsd == __write_to_statsd_init) {
+        ret = __write_to_statsd_initialize_locked();
+        if (ret < 0) {
+            statsd_writer_init_unlock();
+            errno = save_errno;
+            return ret;
+        }
+
+        __write_to_statsd = __write_to_stats_daemon;
+    }
+
+    statsd_writer_init_unlock();
+
+    ret = __write_to_statsd(vec, nr);
+    errno = save_errno;
+    return ret;
+}
diff --git a/libstats/stats_event.c b/libstats/socket/stats_event.c
similarity index 84%
rename from libstats/stats_event.c
rename to libstats/socket/stats_event.c
index 35081dc..15039c6 100644
--- a/libstats/stats_event.c
+++ b/libstats/socket/stats_event.c
@@ -18,9 +18,8 @@
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
-#include "include/stats_event_list.h"
+#include "stats_buffer_writer.h"
 
-#define STATS_EVENT_TAG 1937006964
 #define LOGGER_ENTRY_MAX_PAYLOAD 4068
 // Max payload size is 4 bytes less as 4 bytes are reserved for stats_eventTag.
 // See android_util_Stats_Log.cpp
@@ -39,13 +38,13 @@
 // The stats_event struct holds the serialized encoding of an event
 // within a buf. Also includes other required fields.
 struct stats_event {
-    uint8_t buf[MAX_EVENT_PAYLOAD];
+    uint8_t* buf;
     size_t lastFieldPos;  // location of last field within the buf
     size_t size;          // number of valid bytes within buffer
     uint32_t numElements;
     uint32_t atomId;
     uint32_t errors;
-    uint32_t tag;
+    bool truncate;
     bool built;
 };
 
@@ -58,12 +57,11 @@
 
 struct stats_event* stats_event_obtain() {
     struct stats_event* event = malloc(sizeof(struct stats_event));
-
-    memset(event->buf, 0, MAX_EVENT_PAYLOAD);
+    event->buf = (uint8_t*)calloc(MAX_EVENT_PAYLOAD, 1);
     event->buf[0] = OBJECT_TYPE;
     event->atomId = 0;
     event->errors = 0;
-    event->tag = STATS_EVENT_TAG;
+    event->truncate = true;  // truncate for both pulled and pushed atoms
     event->built = false;
 
     // place the timestamp
@@ -79,6 +77,7 @@
 }
 
 void stats_event_release(struct stats_event* event) {
+    free(event->buf);
     free(event);
 }
 
@@ -132,7 +131,7 @@
     }
 }
 
-static void append_byte_array(struct stats_event* event, uint8_t* buf, size_t size) {
+static void append_byte_array(struct stats_event* event, const uint8_t* buf, size_t size) {
     if (!overflows(event, size)) {
         memcpy(&event->buf[event->size], buf, size);
         event->size += size;
@@ -142,7 +141,7 @@
 // Side-effect: modifies event->errors if buf is not properly null-terminated
 static void append_string(struct stats_event* event, const char* buf) {
     size_t size = strnlen(buf, MAX_EVENT_PAYLOAD);
-    if (event->errors) {
+    if (size == MAX_EVENT_PAYLOAD) {
         event->errors |= ERROR_STRING_NOT_NULL_TERMINATED;
         return;
     }
@@ -185,7 +184,7 @@
     append_bool(event, value);
 }
 
-void stats_event_write_byte_array(struct stats_event* event, uint8_t* buf, size_t numBytes) {
+void stats_event_write_byte_array(struct stats_event* event, const uint8_t* buf, size_t numBytes) {
     if (event->errors) return;
 
     start_field(event, BYTE_ARRAY_TYPE);
@@ -193,17 +192,17 @@
     append_byte_array(event, buf, numBytes);
 }
 
-// Buf is assumed to be encoded using UTF8
-void stats_event_write_string8(struct stats_event* event, const char* buf) {
+// Value is assumed to be encoded using UTF8
+void stats_event_write_string8(struct stats_event* event, const char* value) {
     if (event->errors) return;
 
     start_field(event, STRING_TYPE);
-    append_string(event, buf);
+    append_string(event, value);
 }
 
 // Tags are assumed to be encoded using UTF8
-void stats_event_write_attribution_chain(struct stats_event* event, uint32_t* uids,
-                                         const char** tags, uint8_t numNodes) {
+void stats_event_write_attribution_chain(struct stats_event* event, const uint32_t* uids,
+                                         const char* const* tags, uint8_t numNodes) {
     if (numNodes > MAX_BYTE_VALUE) event->errors |= ERROR_ATTRIBUTION_CHAIN_TOO_LONG;
     if (event->errors) return;
 
@@ -297,6 +296,10 @@
     return event->errors;
 }
 
+void stats_event_truncate_buffer(struct stats_event* event, bool truncate) {
+    event->truncate = truncate;
+}
+
 void stats_event_build(struct stats_event* event) {
     if (event->built) return;
 
@@ -317,17 +320,35 @@
         event->size = POS_FIRST_FIELD + sizeof(uint8_t) + sizeof(uint32_t);
     }
 
+    // Truncate the buffer to the appropriate length in order to limit our
+    // memory usage.
+    if (event->truncate) event->buf = (uint8_t*)realloc(event->buf, event->size);
+
     event->built = true;
 }
 
-void stats_event_write(struct stats_event* event) {
+int stats_event_write(struct stats_event* event) {
     stats_event_build(event);
-
-    // Prepare iovecs for write to statsd.
-    struct iovec vecs[2];
-    vecs[0].iov_base = &event->tag;
-    vecs[0].iov_len = sizeof(event->tag);
-    vecs[1].iov_base = &event->buf;
-    vecs[1].iov_len = event->size;
-    write_to_statsd(vecs, 2);
+    return write_buffer_to_statsd(&event->buf, event->size, event->atomId);
 }
+
+struct stats_event_api_table table = {
+        stats_event_obtain,
+        stats_event_build,
+        stats_event_write,
+        stats_event_release,
+        stats_event_set_atom_id,
+        stats_event_write_int32,
+        stats_event_write_int64,
+        stats_event_write_float,
+        stats_event_write_bool,
+        stats_event_write_byte_array,
+        stats_event_write_string8,
+        stats_event_write_attribution_chain,
+        stats_event_write_key_value_pairs,
+        stats_event_add_bool_annotation,
+        stats_event_add_int32_annotation,
+        stats_event_get_atom_id,
+        stats_event_get_buffer,
+        stats_event_get_errors,
+};
diff --git a/libstats/socket/stats_event_list.c b/libstats/socket/stats_event_list.c
new file mode 100644
index 0000000..661a223
--- /dev/null
+++ b/libstats/socket/stats_event_list.c
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2018, 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 "include/stats_event_list.h"
+
+#include <string.h>
+#include <sys/time.h>
+#include "stats_buffer_writer.h"
+
+#define MAX_EVENT_PAYLOAD (LOGGER_ENTRY_MAX_PAYLOAD - sizeof(int32_t))
+
+typedef struct {
+    uint32_t tag;
+    unsigned pos;                                    /* Read/write position into buffer */
+    unsigned count[ANDROID_MAX_LIST_NEST_DEPTH + 1]; /* Number of elements   */
+    unsigned list[ANDROID_MAX_LIST_NEST_DEPTH + 1];  /* pos for list counter */
+    unsigned list_nest_depth;
+    unsigned len; /* Length or raw buffer. */
+    bool overflow;
+    bool list_stop; /* next call decrement list_nest_depth and issue a stop */
+    enum {
+        kAndroidLoggerRead = 1,
+        kAndroidLoggerWrite = 2,
+    } read_write_flag;
+    uint8_t storage[LOGGER_ENTRY_MAX_PAYLOAD];
+} android_log_context_internal;
+
+// Similar to create_android_logger(), but instead of allocation a new buffer,
+// this function resets the buffer for resuse.
+void reset_log_context(android_log_context ctx) {
+    if (!ctx) {
+        return;
+    }
+    android_log_context_internal* context = (android_log_context_internal*)(ctx);
+    uint32_t tag = context->tag;
+    memset(context, 0, sizeof(android_log_context_internal));
+
+    context->tag = tag;
+    context->read_write_flag = kAndroidLoggerWrite;
+    size_t needed = sizeof(uint8_t) + sizeof(uint8_t);
+    if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+        context->overflow = true;
+    }
+    /* Everything is a list */
+    context->storage[context->pos + 0] = EVENT_TYPE_LIST;
+    context->list[0] = context->pos + 1;
+    context->pos += needed;
+}
+
+int stats_write_list(android_log_context ctx) {
+    android_log_context_internal* context;
+    const char* msg;
+    ssize_t len;
+
+    context = (android_log_context_internal*)(ctx);
+    if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+        return -EBADF;
+    }
+
+    if (context->list_nest_depth) {
+        return -EIO;
+    }
+
+    /* NB: if there was overflow, then log is truncated. Nothing reported */
+    context->storage[1] = context->count[0];
+    len = context->len = context->pos;
+    msg = (const char*)context->storage;
+    /* it's not a list */
+    if (context->count[0] <= 1) {
+        len -= sizeof(uint8_t) + sizeof(uint8_t);
+        if (len < 0) {
+            len = 0;
+        }
+        msg += sizeof(uint8_t) + sizeof(uint8_t);
+    }
+
+    return write_buffer_to_statsd((void*)msg, len, 0);
+}
+
+int write_to_logger(android_log_context ctx, log_id_t id) {
+    int retValue = 0;
+
+    if (WRITE_TO_LOGD) {
+        retValue = android_log_write_list(ctx, id);
+    }
+
+    if (WRITE_TO_STATSD) {
+        // log_event_list's cast operator is overloaded.
+        int ret = stats_write_list(ctx);
+        // In debugging phase, we may write to both logd and statsd. Prefer to
+        // return statsd socket write error code here.
+        if (ret < 0) {
+            retValue = ret;
+        }
+    }
+
+    return retValue;
+}
+
+static inline void copy4LE(uint8_t* buf, uint32_t val) {
+    buf[0] = val & 0xFF;
+    buf[1] = (val >> 8) & 0xFF;
+    buf[2] = (val >> 16) & 0xFF;
+    buf[3] = (val >> 24) & 0xFF;
+}
+
+// Note: this function differs from android_log_write_string8_len in that the length passed in
+// should be treated as actual length and not max length.
+int android_log_write_char_array(android_log_context ctx, const char* value, size_t actual_len) {
+    size_t needed;
+    ssize_t len = actual_len;
+    android_log_context_internal* context;
+
+    context = (android_log_context_internal*)ctx;
+    if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+        return -EBADF;
+    }
+    if (context->overflow) {
+        return -EIO;
+    }
+    if (!value) {
+        value = "";
+        len = 0;
+    }
+    needed = sizeof(uint8_t) + sizeof(int32_t) + len;
+    if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+        /* Truncate string for delivery */
+        len = MAX_EVENT_PAYLOAD - context->pos - 1 - sizeof(int32_t);
+        if (len <= 0) {
+            context->overflow = true;
+            return -EIO;
+        }
+    }
+    context->count[context->list_nest_depth]++;
+    context->storage[context->pos + 0] = EVENT_TYPE_STRING;
+    copy4LE(&context->storage[context->pos + 1], len);
+    if (len) {
+        memcpy(&context->storage[context->pos + 5], value, len);
+    }
+    context->pos += needed;
+    return len;
+}
diff --git a/libstats/statsd_writer.c b/libstats/socket/statsd_writer.c
similarity index 98%
copy from libstats/statsd_writer.c
copy to libstats/socket/statsd_writer.c
index 073b67f..04d3b46 100644
--- a/libstats/statsd_writer.c
+++ b/libstats/socket/statsd_writer.c
@@ -101,7 +101,7 @@
             strcpy(un.sun_path, "/dev/socket/statsdw");
 
             if (TEMP_FAILURE_RETRY(
-                    connect(sock, (struct sockaddr*)&un, sizeof(struct sockaddr_un))) < 0) {
+                        connect(sock, (struct sockaddr*)&un, sizeof(struct sockaddr_un))) < 0) {
                 ret = -errno;
                 switch (ret) {
                     case -ENOTCONN:
diff --git a/libstats/statsd_writer.h b/libstats/socket/statsd_writer.h
similarity index 100%
rename from libstats/statsd_writer.h
rename to libstats/socket/statsd_writer.h
diff --git a/libvndksupport/linker.cpp b/libvndksupport/linker.cpp
index cf0f618..30b9c2e 100644
--- a/libvndksupport/linker.cpp
+++ b/libvndksupport/linker.cpp
@@ -26,9 +26,7 @@
 
 #include <initializer_list>
 
-__attribute__((weak)) extern "C" android_namespace_t* android_get_exported_namespace(const char*);
-__attribute__((weak)) extern "C" void* android_dlopen_ext(const char*, int,
-                                                          const android_dlextinfo*);
+extern "C" android_namespace_t* android_get_exported_namespace(const char*);
 
 namespace {
 
@@ -42,10 +40,8 @@
 static VendorNamespace get_vendor_namespace() {
     static VendorNamespace result = ([] {
         for (const char* name : {"sphal", "default"}) {
-            if (android_get_exported_namespace != nullptr) {
-                if (android_namespace_t* ns = android_get_exported_namespace(name)) {
-                    return VendorNamespace{ns, name};
-                }
+            if (android_namespace_t* ns = android_get_exported_namespace(name)) {
+                return VendorNamespace{ns, name};
             }
         }
         return VendorNamespace{};
@@ -59,10 +55,6 @@
     if (getpid() == 1) {
         return 0;
     }
-    if (android_get_exported_namespace == nullptr) {
-        ALOGD("android_get_exported_namespace() not available. Assuming system process.");
-        return 0;
-    }
 
     // In vendor process, 'vndk' namespace is not visible, whereas in system
     // process, it is.
@@ -76,10 +68,7 @@
                 .flags = ANDROID_DLEXT_USE_NAMESPACE,
                 .library_namespace = vendor_namespace.ptr,
         };
-        void* handle = nullptr;
-        if (android_dlopen_ext != nullptr) {
-            handle = android_dlopen_ext(name, flag, &dlextinfo);
-        }
+        void* handle = android_dlopen_ext(name, flag, &dlextinfo);
         if (!handle) {
             ALOGE("Could not load %s from %s namespace: %s.", name, vendor_namespace.name,
                   dlerror());
diff --git a/rootdir/init.rc b/rootdir/init.rc
index b89c45e..9cf11a4 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -38,9 +38,18 @@
     # Allow up to 32K FDs per process
     setrlimit nofile 32768 32768
 
+    # Set up linker config subdirectories based on mount namespaces
+    mkdir /linkerconfig/bootstrap 0755
+    mkdir /linkerconfig/default 0755
+
     # Generate ld.config.txt for early executed processes
-    exec -- /system/bin/linkerconfig --target /linkerconfig/ld.config.txt
-    chmod 444 /linkerconfig/ld.config.txt
+    exec -- /system/bin/linkerconfig --target /linkerconfig/bootstrap/ld.config.txt
+    chmod 644 /linkerconfig/bootstrap/ld.config.txt
+    copy /linkerconfig/bootstrap/ld.config.txt /linkerconfig/default/ld.config.txt
+    chmod 644 /linkerconfig/default/ld.config.txt
+
+    # Mount bootstrap linker configuration as current
+    mount none /linkerconfig/bootstrap /linkerconfig bind rec
 
     start ueventd
 
@@ -49,6 +58,9 @@
     # the libraries are available to the processes started after this statement.
     exec_start apexd-bootstrap
 
+    # Generate linker config based on apex mounted in bootstrap namespace
+    update_linker_config
+
     # These must already exist by the time boringssl_self_test32 / boringssl_self_test64 run.
     mkdir /dev/boringssl 0755 root root
     mkdir /dev/boringssl/selftest 0755 root root
@@ -688,6 +700,9 @@
     mkdir /data/rollback 0700 system system encryption=DeleteIfNecessary
     mkdir /data/rollback-observer 0700 system system encryption=DeleteIfNecessary
 
+    # Create root dir for Incremental Service
+    mkdir /data/incremental 0770 system system encryption=None
+
     # Wait for apexd to finish activating APEXes before starting more processes.
     wait_for_prop apexd.status ready
     perform_apex_config