Merge "libvndksupport: Do not lookup default namespace for sphal failure"
diff --git a/adb/Android.bp b/adb/Android.bp
new file mode 100644
index 0000000..41f7b89
--- /dev/null
+++ b/adb/Android.bp
@@ -0,0 +1,51 @@
+// Copyright (C) 2017 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.
+
+python_binary_host {
+  name: "adb_integration_test_adb",
+  main: "test_adb.py",
+  srcs: [
+    "test_adb.py",
+  ],
+  libs: [
+    "adb_py",
+  ],
+  version: {
+    py2: {
+      enabled: true,
+    },
+    py3: {
+      enabled: false,
+    },
+  },
+}
+
+python_binary_host {
+  name: "adb_integration_test_device",
+  main: "test_device.py",
+  srcs: [
+    "test_device.py",
+  ],
+  libs: [
+    "adb_py",
+  ],
+  version: {
+    py2: {
+      enabled: true,
+    },
+    py3: {
+      enabled: false,
+    },
+  },
+}
diff --git a/adb/Android.mk b/adb/Android.mk
index 4d7d487..6ed01fa 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -388,4 +388,9 @@
 
 include $(BUILD_EXECUTABLE)
 
+# adb integration test
+# =========================================================
+$(call dist-for-goals,sdk,$(ALL_MODULES.adb_integration_test_adb.BUILT))
+$(call dist-for-goals,sdk,$(ALL_MODULES.adb_integration_test_device.BUILT))
+
 include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 808d8ff..a7706a0 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -1140,6 +1140,7 @@
         if (should_use_libusb()) {
             features.insert(kFeatureLibusb);
         }
+        features.insert(kFeaturePushSync);
         SendOkay(reply_fd, FeatureSetToString(features));
         return 0;
     }
diff --git a/adb/bugreport.cpp b/adb/bugreport.cpp
index d0cc072..372a3b4 100644
--- a/adb/bugreport.cpp
+++ b/adb/bugreport.cpp
@@ -149,7 +149,7 @@
             int progress = std::stoi(line.substr(idx1, (idx2 - idx1)));
             int total = std::stoi(line.substr(idx2 + 1));
             int progress_percentage = (progress * 100 / total);
-            if (progress_percentage <= last_progress_percentage_) {
+            if (progress_percentage != 0 && progress_percentage <= last_progress_percentage_) {
                 // Ignore.
                 return;
             }
diff --git a/adb/bugreport_test.cpp b/adb/bugreport_test.cpp
index 2b368d7..d3787b4 100644
--- a/adb/bugreport_test.cpp
+++ b/adb/bugreport_test.cpp
@@ -311,6 +311,29 @@
     ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
 }
 
+// Tests 'adb bugreport file.zip' when it succeeds and displays the initial progress of 0%
+TEST_F(BugreportTest, OkProgressZeroPercentIsNotIgnored) {
+    ExpectBugreportzVersion("1.1");
+    ExpectProgress(0);
+    ExpectProgress(1);
+    // clang-format off
+    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
+        // NOTE: DoAll accepts at most 10 arguments, and we're almost reached that limit...
+        .WillOnce(DoAll(
+            WithArg<4>(WriteOnStdout("BEGIN:/device/bugreport.zip\n")),
+            WithArg<4>(WriteOnStdout("PROGRESS:1/100000\n")),
+            WithArg<4>(WriteOnStdout("PROGRESS:1/100\n")), // 1%
+            WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip")),
+            WithArg<4>(ReturnCallbackDone())));
+    // clang-format on
+    EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
+                                true, StrEq("pulling file.zip")))
+        .WillOnce(Return(true));
+
+    const char* args[] = {"bugreport", "file.zip"};
+    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+}
+
 // Tests 'adb bugreport dir' when it succeeds and destination is a directory.
 TEST_F(BugreportTest, OkDirectory) {
     ExpectBugreportzVersion("1.1");
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index bd9ad01..62798cd 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -125,6 +125,11 @@
     });
 #endif
 
+    if (is_daemon) {
+        close_stdin();
+        setup_daemon_logging();
+    }
+
     android::base::at_quick_exit(adb_server_cleanup);
 
     init_transport_registration();
@@ -148,11 +153,6 @@
         std::this_thread::sleep_for(100ms);
     }
 
-    if (is_daemon) {
-        close_stdin();
-        setup_daemon_logging();
-    }
-
     adb_auth_init();
 
     if (is_daemon) {
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 13cfaa1..308ee8d 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -55,6 +55,7 @@
 const char* const kFeatureCmd = "cmd";
 const char* const kFeatureStat2 = "stat_v2";
 const char* const kFeatureLibusb = "libusb";
+const char* const kFeaturePushSync = "push_sync";
 
 static std::string dump_packet(const char* name, const char* func, apacket* p) {
     unsigned command = p->msg.command;
@@ -1050,27 +1051,24 @@
         [usb](atransport* t) { return t->usb == usb && t->GetConnectionState() == kCsNoPerm; });
 }
 
-int check_header(apacket* p, atransport* t) {
+bool check_header(apacket* p, atransport* t) {
     if (p->msg.magic != (p->msg.command ^ 0xffffffff)) {
         VLOG(RWX) << "check_header(): invalid magic command = " << std::hex << p->msg.command
                   << ", magic = " << p->msg.magic;
-        return -1;
+        return false;
     }
 
     if (p->msg.data_length > t->get_max_payload()) {
         VLOG(RWX) << "check_header(): " << p->msg.data_length
                   << " atransport::max_payload = " << t->get_max_payload();
-        return -1;
+        return false;
     }
 
-    return 0;
+    return true;
 }
 
-int check_data(apacket* p) {
-    if (calculate_apacket_checksum(p) != p->msg.data_check) {
-        return -1;
-    }
-    return 0;
+bool check_data(apacket* p) {
+    return calculate_apacket_checksum(p) == p->msg.data_check;
 }
 
 #if ADB_HOST
diff --git a/adb/transport.h b/adb/transport.h
index 006aaf4..57fc988 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -51,6 +51,8 @@
 extern const char* const kFeatureStat2;
 // The server is running with libusb enabled.
 extern const char* const kFeatureLibusb;
+// The server supports `push --sync`.
+extern const char* const kFeaturePushSync;
 
 class atransport {
 public:
@@ -221,8 +223,8 @@
 // This should only be used for transports with connection_state == kCsNoPerm.
 void unregister_usb_transport(usb_handle* usb);
 
-int check_header(apacket* p, atransport* t);
-int check_data(apacket* p);
+bool check_header(apacket* p, atransport* t);
+bool check_data(apacket* p);
 
 void close_usb_devices();
 void close_usb_devices(std::function<bool(const atransport*)> predicate);
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index e5992b1..809ed89 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -62,22 +62,22 @@
 
 static int remote_read(apacket *p, atransport *t)
 {
-    if(!ReadFdExactly(t->sfd, &p->msg, sizeof(amessage))){
+    if (!ReadFdExactly(t->sfd, &p->msg, sizeof(amessage))) {
         D("remote local: read terminated (message)");
         return -1;
     }
 
-    if(check_header(p, t)) {
+    if (!check_header(p, t)) {
         D("bad header: terminated (data)");
         return -1;
     }
 
-    if(!ReadFdExactly(t->sfd, p->data, p->msg.data_length)){
+    if (!ReadFdExactly(t->sfd, p->data, p->msg.data_length)) {
         D("remote local: terminated (data)");
         return -1;
     }
 
-    if(check_data(p)) {
+    if (!check_data(p)) {
         D("bad data: terminated (data)");
         return -1;
     }
diff --git a/adb/transport_usb.cpp b/adb/transport_usb.cpp
index 885d723..47094b8 100644
--- a/adb/transport_usb.cpp
+++ b/adb/transport_usb.cpp
@@ -72,7 +72,7 @@
         D("remote usb: read terminated (message)");
         return -1;
     }
-    if (static_cast<size_t>(n) != sizeof(p->msg) || check_header(p, t)) {
+    if (static_cast<size_t>(n) != sizeof(p->msg) || !check_header(p, t)) {
         D("remote usb: check_header failed, skip it");
         goto err_msg;
     }
@@ -95,7 +95,7 @@
             goto err_msg;
         }
     }
-    if (check_data(p)) {
+    if (!check_data(p)) {
         D("remote usb: check_data failed, skip it");
         goto err_msg;
     }
@@ -124,19 +124,19 @@
         return -1;
     }
 
-    if(check_header(p, t)) {
+    if (!check_header(p, t)) {
         D("remote usb: check_header failed");
         return -1;
     }
 
-    if(p->msg.data_length) {
+    if (p->msg.data_length) {
         if (usb_read(t->usb, p->data, p->msg.data_length)) {
             D("remote usb: terminated (data)");
             return -1;
         }
     }
 
-    if(check_data(p)) {
+    if (!check_data(p)) {
         D("remote usb: check_data failed");
         return -1;
     }
diff --git a/bootstat/.clang-format b/bootstat/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/bootstat/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 6f25d96..d4e215e 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -221,19 +221,24 @@
   }
 }
 
-// Parses and records the set of bootloader stages and associated boot times
-// from the ro.boot.boottime system property.
-void RecordBootloaderTimings(BootEventRecordStore* boot_event_store) {
-  // |ro.boot.boottime| is of the form 'stage1:time1,...,stageN:timeN'.
+// A map from bootloader timing stage to the time that stage took during boot.
+typedef std::map<std::string, int32_t> BootloaderTimingMap;
+
+// Returns a mapping from bootloader stage names to the time those stages
+// took to boot.
+const BootloaderTimingMap GetBootLoaderTimings() {
+  BootloaderTimingMap timings;
+
+  // |ro.boot.boottime| is of the form 'stage1:time1,...,stageN:timeN',
+  // where timeN is in milliseconds.
   std::string value = GetProperty("ro.boot.boottime");
   if (value.empty()) {
     // ro.boot.boottime is not reported on all devices.
-    return;
+    return BootloaderTimingMap();
   }
 
-  int32_t total_time = 0;
   auto stages = android::base::Split(value, ",");
-  for (auto const &stageTiming : stages) {
+  for (const auto& stageTiming : stages) {
     // |stageTiming| is of the form 'stage:time'.
     auto stageTimingValues = android::base::Split(stageTiming, ":");
     DCHECK_EQ(2, stageTimingValues.size());
@@ -241,23 +246,53 @@
     std::string stageName = stageTimingValues[0];
     int32_t time_ms;
     if (android::base::ParseInt(stageTimingValues[1], &time_ms)) {
-      total_time += time_ms;
-      boot_event_store->AddBootEventWithValue(
-          "boottime.bootloader." + stageName, time_ms);
+      timings[stageName] = time_ms;
     }
   }
 
+  return timings;
+}
+
+// Parses and records the set of bootloader stages and associated boot times
+// from the ro.boot.boottime system property.
+void RecordBootloaderTimings(BootEventRecordStore* boot_event_store,
+                             const BootloaderTimingMap& bootloader_timings) {
+  int32_t total_time = 0;
+  for (const auto& timing : bootloader_timings) {
+    total_time += timing.second;
+    boot_event_store->AddBootEventWithValue("boottime.bootloader." + timing.first, timing.second);
+  }
+
   boot_event_store->AddBootEventWithValue("boottime.bootloader.total", total_time);
 }
 
+// Records the closest estimation to the absolute device boot time, i.e.,
+// from power on to boot_complete, including bootloader times.
+void RecordAbsoluteBootTime(BootEventRecordStore* boot_event_store,
+                            const BootloaderTimingMap& bootloader_timings,
+                            std::chrono::milliseconds uptime) {
+  int32_t bootloader_time_ms = 0;
+
+  for (const auto& timing : bootloader_timings) {
+    if (timing.first.compare("SW") != 0) {
+      bootloader_time_ms += timing.second;
+    }
+  }
+
+  auto bootloader_duration = std::chrono::milliseconds(bootloader_time_ms);
+  auto absolute_total =
+      std::chrono::duration_cast<std::chrono::seconds>(bootloader_duration + uptime);
+  boot_event_store->AddBootEventWithValue("absolute_boot_time", absolute_total.count());
+}
+
 // Records several metrics related to the time it takes to boot the device,
 // including disambiguating boot time on encrypted or non-encrypted devices.
 void RecordBootComplete() {
   BootEventRecordStore boot_event_store;
   BootEventRecordStore::BootEventRecord record;
 
-  auto uptime = std::chrono::duration_cast<std::chrono::seconds>(
-      android::base::boot_clock::now().time_since_epoch());
+  auto time_since_epoch = android::base::boot_clock::now().time_since_epoch();
+  auto uptime = std::chrono::duration_cast<std::chrono::seconds>(time_since_epoch);
   time_t current_time_utc = time(nullptr);
 
   if (boot_event_store.GetBootEvent("last_boot_time_utc", &record)) {
@@ -290,7 +325,6 @@
     std::chrono::seconds boot_complete = std::chrono::seconds(uptime.count() - record.second);
     boot_event_store.AddBootEventWithValue(boot_complete_prefix + "_post_decrypt",
                                            boot_complete.count());
-
   } else {
       boot_event_store.AddBootEventWithValue(boot_complete_prefix + "_no_encryption",
                                              uptime.count());
@@ -304,7 +338,11 @@
   RecordInitBootTimeProp(&boot_event_store, "ro.boottime.init.selinux");
   RecordInitBootTimeProp(&boot_event_store, "ro.boottime.init.cold_boot_wait");
 
-  RecordBootloaderTimings(&boot_event_store);
+  const BootloaderTimingMap bootloader_timings = GetBootLoaderTimings();
+  RecordBootloaderTimings(&boot_event_store, bootloader_timings);
+
+  auto uptime_ms = std::chrono::duration_cast<std::chrono::milliseconds>(time_since_epoch);
+  RecordAbsoluteBootTime(&boot_event_store, bootloader_timings, uptime_ms);
 }
 
 // Records the boot_reason metric by querying the ro.boot.bootreason system
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 37d54d7..3a80b50 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -11,12 +11,32 @@
     local_include_dirs: ["include"],
 }
 
-// Utility library to tombstoned and get an output fd.
-cc_library_static {
+cc_library_shared {
     name: "libtombstoned_client",
     defaults: ["debuggerd_defaults"],
     srcs: [
-        "tombstoned_client.cpp",
+        "tombstoned/tombstoned_client.cpp",
+        "util.cpp",
+    ],
+
+    static_libs: [
+        "libasync_safe"
+    ],
+
+    shared_libs: [
+        "libcutils",
+        "libbase",
+    ],
+
+    export_include_dirs: ["tombstoned/include"]
+}
+
+// Utility library to tombstoned and get an output fd.
+cc_library_static {
+    name: "libtombstoned_client_static",
+    defaults: ["debuggerd_defaults"],
+    srcs: [
+        "tombstoned/tombstoned_client.cpp",
         "util.cpp",
     ],
 
@@ -25,6 +45,8 @@
         "libcutils",
         "libbase",
     ],
+
+    export_include_dirs: ["tombstoned/include"]
 }
 
 // Core implementation, linked into libdebuggerd_handler and the dynamic linker.
@@ -64,7 +86,7 @@
 
     whole_static_libs: [
         "libdebuggerd_handler_core",
-        "libtombstoned_client",
+        "libtombstoned_client_static",
         "libasync_safe",
         "libbase",
         "libdebuggerd",
@@ -159,10 +181,8 @@
             srcs: [
                 "client/debuggerd_client_test.cpp",
                 "debuggerd_test.cpp",
-                "tombstoned_client.cpp",
-                "util.cpp"
             ],
-            static_libs: ["libasync_safe"],
+            static_libs: ["libasync_safe", "libtombstoned_client_static"],
         },
     },
 
@@ -171,6 +191,7 @@
         "libbase",
         "libcutils",
         "libdebuggerd_client",
+        "liblog"
     ],
 
     static_libs: [
@@ -211,7 +232,7 @@
     },
 
     static_libs: [
-        "libtombstoned_client",
+        "libtombstoned_client_static",
         "libdebuggerd",
         "libcutils",
     ],
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index 2be13c6..4ce038c 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -31,9 +31,10 @@
 #include <android-base/stringprintf.h>
 #include <android-base/unique_fd.h>
 #include <cutils/sockets.h>
-#include <debuggerd/handler.h>
-#include <debuggerd/protocol.h>
-#include <debuggerd/util.h>
+
+#include "debuggerd/handler.h"
+#include "protocol.h"
+#include "util.h"
 
 using namespace std::chrono_literals;
 
diff --git a/debuggerd/client/debuggerd_client_test.cpp b/debuggerd/client/debuggerd_client_test.cpp
index aff03e5..8f97db1 100644
--- a/debuggerd/client/debuggerd_client_test.cpp
+++ b/debuggerd/client/debuggerd_client_test.cpp
@@ -31,7 +31,7 @@
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 
-#include <debuggerd/util.h>
+#include "util.h"
 
 using namespace std::chrono_literals;
 using android::base::unique_fd;
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index d2a4239..be28079 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -48,9 +48,9 @@
 #include "utility.h"
 
 #include "debuggerd/handler.h"
-#include "debuggerd/protocol.h"
-#include "debuggerd/tombstoned.h"
-#include "debuggerd/util.h"
+#include "protocol.h"
+#include "tombstoned/tombstoned.h"
+#include "util.h"
 
 using android::base::unique_fd;
 using android::base::ReadFileToString;
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index 492e9f0..4997dd6 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -27,8 +27,8 @@
 #include <android-base/parseint.h>
 #include <android-base/unique_fd.h>
 #include <debuggerd/client.h>
-#include <debuggerd/util.h>
 #include <selinux/selinux.h>
+#include "util.h"
 
 using android::base::unique_fd;
 
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 0b4bbfb..f17724a 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -35,12 +35,13 @@
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <cutils/sockets.h>
-#include <debuggerd/handler.h>
-#include <debuggerd/protocol.h>
-#include <debuggerd/tombstoned.h>
-#include <debuggerd/util.h>
 #include <gtest/gtest.h>
 
+#include "debuggerd/handler.h"
+#include "protocol.h"
+#include "tombstoned/tombstoned.h"
+#include "util.h"
+
 using namespace std::chrono_literals;
 using android::base::unique_fd;
 
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index 47c98d1..a9c9862 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -42,8 +42,8 @@
 #include <async_safe/log.h>
 
 #include "debuggerd/handler.h"
-#include "debuggerd/tombstoned.h"
-#include "debuggerd/util.h"
+#include "tombstoned/tombstoned.h"
+#include "util.h"
 
 #include "backtrace.h"
 #include "tombstone.h"
diff --git a/debuggerd/include/debuggerd/protocol.h b/debuggerd/protocol.h
similarity index 100%
rename from debuggerd/include/debuggerd/protocol.h
rename to debuggerd/protocol.h
diff --git a/debuggerd/include/debuggerd/tombstoned.h b/debuggerd/tombstoned/include/tombstoned/tombstoned.h
similarity index 100%
rename from debuggerd/include/debuggerd/tombstoned.h
rename to debuggerd/tombstoned/include/tombstoned/tombstoned.h
diff --git a/debuggerd/tombstoned/intercept_manager.cpp b/debuggerd/tombstoned/intercept_manager.cpp
index dff942c..4d4eb9e 100644
--- a/debuggerd/tombstoned/intercept_manager.cpp
+++ b/debuggerd/tombstoned/intercept_manager.cpp
@@ -28,8 +28,8 @@
 #include <android-base/unique_fd.h>
 #include <cutils/sockets.h>
 
-#include "debuggerd/protocol.h"
-#include "debuggerd/util.h"
+#include "protocol.h"
+#include "util.h"
 
 using android::base::unique_fd;
 
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index 80dbef5..05df9f2 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -35,8 +35,8 @@
 #include <cutils/sockets.h>
 
 #include "debuggerd/handler.h"
-#include "debuggerd/protocol.h"
-#include "debuggerd/util.h"
+#include "protocol.h"
+#include "util.h"
 
 #include "intercept_manager.h"
 
diff --git a/debuggerd/tombstoned_client.cpp b/debuggerd/tombstoned/tombstoned_client.cpp
similarity index 96%
rename from debuggerd/tombstoned_client.cpp
rename to debuggerd/tombstoned/tombstoned_client.cpp
index e878b6a..39dc6eb 100644
--- a/debuggerd/tombstoned_client.cpp
+++ b/debuggerd/tombstoned/tombstoned_client.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "debuggerd/tombstoned.h"
+#include "tombstoned/tombstoned.h"
 
 #include <fcntl.h>
 #include <unistd.h>
@@ -25,8 +25,8 @@
 #include <async_safe/log.h>
 #include <cutils/sockets.h>
 
-#include "debuggerd/protocol.h"
-#include "debuggerd/util.h"
+#include "protocol.h"
+#include "util.h"
 
 using android::base::unique_fd;
 
diff --git a/debuggerd/util.cpp b/debuggerd/util.cpp
index 32d2f18..c6a997b 100644
--- a/debuggerd/util.cpp
+++ b/debuggerd/util.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "debuggerd/util.h"
+#include "util.h"
 
 #include <sys/socket.h>
 
@@ -22,7 +22,7 @@
 
 #include <android-base/unique_fd.h>
 #include <cutils/sockets.h>
-#include <debuggerd/protocol.h>
+#include "protocol.h"
 
 using android::base::unique_fd;
 
diff --git a/debuggerd/include/debuggerd/util.h b/debuggerd/util.h
similarity index 100%
rename from debuggerd/include/debuggerd/util.h
rename to debuggerd/util.h
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 94733c4..c189eee 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -965,16 +965,20 @@
                    should_use_metadata_encryption(&fstab->recs[attempted_idx])) {
             encryptable = FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED;
         } else {
+            // fs_options might be null so we cannot use PERROR << directly.
+            // Use StringPrintf to output "(null)" instead.
             if (fs_mgr_is_nofail(&fstab->recs[attempted_idx])) {
-                PERROR << "Ignoring failure to mount an un-encryptable or wiped partition on"
-                       << fstab->recs[attempted_idx].blk_device << " at "
-                       << fstab->recs[attempted_idx].mount_point << " options: "
-                       << fstab->recs[attempted_idx].fs_options;
+                PERROR << android::base::StringPrintf(
+                    "Ignoring failure to mount an un-encryptable or wiped "
+                    "partition on %s at %s options: %s",
+                    fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point,
+                    fstab->recs[attempted_idx].fs_options);
             } else {
-                PERROR << "Failed to mount an un-encryptable or wiped partition on"
-                       << fstab->recs[attempted_idx].blk_device << " at "
-                       << fstab->recs[attempted_idx].mount_point << " options: "
-                       << fstab->recs[attempted_idx].fs_options;
+                PERROR << android::base::StringPrintf(
+                    "Failed to mount an un-encryptable or wiped partition "
+                    "on %s at %s options: %s",
+                    fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point,
+                    fstab->recs[attempted_idx].fs_options);
                 ++error_count;
             }
             continue;
diff --git a/include/nativebridge/native_bridge.h b/include/nativebridge/native_bridge.h
index 929b8ae..9bfc935 100644
--- a/include/nativebridge/native_bridge.h
+++ b/include/nativebridge/native_bridge.h
@@ -161,6 +161,9 @@
 // Use NativeBridgeLoadLibrary() instead in non-namespace scenario.
 void* NativeBridgeLoadLibraryExt(const char* libpath, int flag, native_bridge_namespace_t* ns);
 
+// Returns vendor namespace if it is enabled for the device and null otherwise
+native_bridge_namespace_t* NativeBridgeGetVendorNamespace();
+
 // Native bridge interfaces to runtime.
 struct NativeBridgeCallbacks {
   // Version number of the interface.
@@ -348,6 +351,15 @@
   // Starting with v3, NativeBridge has two scenarios: with/without namespace.
   // Use loadLibrary instead in non-namespace scenario.
   void* (*loadLibraryExt)(const char* libpath, int flag, native_bridge_namespace_t* ns);
+
+  // Get native bridge version of vendor namespace.
+  // The vendor namespace is the namespace used to load vendor public libraries.
+  // With O release this namespace can be different from the default namespace.
+  // For the devices without enable vendor namespaces this function should return null
+  //
+  // Returns:
+  //   vendor namespace or null if it was not set up for the device
+  native_bridge_namespace_t* (*getVendorNamespace)();
 };
 
 // Runtime interfaces to native bridge.
diff --git a/init/Android.bp b/init/Android.bp
new file mode 100644
index 0000000..af1e9d3
--- /dev/null
+++ b/init/Android.bp
@@ -0,0 +1,167 @@
+//
+// Copyright (C) 2017 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.
+//
+
+cc_defaults {
+    name: "init_defaults",
+    cpp_std: "experimental",
+    sanitize: {
+        misc_undefined: ["integer"],
+    },
+    tidy_checks: ["-misc-forwarding-reference-overload"],
+    cppflags: [
+        "-DLOG_UEVENTS=0",
+        "-Wall",
+        "-Wextra",
+        "-Wno-unused-parameter",
+        "-Werror",
+        "-DALLOW_LOCAL_PROP_OVERRIDE=0",
+        "-DALLOW_PERMISSIVE_SELINUX=0",
+        "-DREBOOT_BOOTLOADER_ON_PANIC=0",
+        "-DWORLD_WRITABLE_KMSG=0",
+        "-DDUMP_ON_UMOUNT_FAILURE=0",
+        "-DSHUTDOWN_ZERO_TIMEOUT=0",
+    ],
+    product_variables: {
+        debuggable: {
+            cppflags: [
+                "-UALLOW_LOCAL_PROP_OVERRIDE",
+                "-DALLOW_LOCAL_PROP_OVERRIDE=1",
+                "-UALLOW_PERMISSIVE_SELINUX",
+                "-DALLOW_PERMISSIVE_SELINUX=1",
+                "-UREBOOT_BOOTLOADER_ON_PANIC",
+                "-DREBOOT_BOOTLOADER_ON_PANIC=1",
+                "-UWORLD_WRITABLE_KMSG",
+                "-DWORLD_WRITABLE_KMSG=1",
+                "-UDUMP_ON_UMOUNT_FAILURE",
+                "-DDUMP_ON_UMOUNT_FAILURE=1",
+            ],
+        },
+        eng: {
+            cppflags: [
+                "-USHUTDOWN_ZERO_TIMEOUT",
+                "-DSHUTDOWN_ZERO_TIMEOUT=1",
+            ],
+        },
+    },
+}
+
+cc_library_static {
+    name: "libinit",
+    defaults: ["init_defaults"],
+    srcs: [
+        "action.cpp",
+        "capabilities.cpp",
+        "descriptors.cpp",
+        "devices.cpp",
+        "firmware_handler.cpp",
+        "import_parser.cpp",
+        "init_parser.cpp",
+        "log.cpp",
+        "parser.cpp",
+        "service.cpp",
+        "uevent_listener.cpp",
+        "ueventd_parser.cpp",
+        "util.cpp",
+    ],
+    whole_static_libs: ["libcap"],
+    static_libs: [
+        "libbase",
+        "libselinux",
+        "liblog",
+        "libprocessgroup",
+    ],
+}
+
+/*
+This is not yet ready, see the below TODOs for what is missing
+
+cc_binary {
+    // TODO: Missing,
+    //LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
+    //LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)
+
+    name: "init",
+    defaults: ["init_defaults"],
+    static_executable: true,
+    srcs: [
+        "bootchart.cpp",
+        "builtins.cpp",
+        "init.cpp",
+        "init_first_stage.cpp",
+        "keychords.cpp",
+        "property_service.cpp",
+        "reboot.cpp",
+        "signal_handler.cpp",
+        "ueventd.cpp",
+        "watchdogd.cpp",
+    ],
+    include_dirs: [
+        "system/core/mkbootimg"
+    ],
+    static_libs: [
+        "libinit",
+        "libbootloader_message",
+        "libfs_mgr",
+        "libfec",
+        "libfec_rs",
+        "libsquashfs_utils",
+        "liblogwrap",
+        "libext4_utils",
+        "libcutils",
+        "libbase",
+        "libc",
+        "libselinux",
+        "liblog",
+        "libcrypto_utils",
+        "libcrypto",
+        "libc++_static",
+        "libdl",
+        "libsparse",
+        "libz",
+        "libprocessgroup",
+        "libavb",
+        "libkeyutils",
+    ],
+    symlinks: [
+        "sbin/ueventd",
+        "sbin/watchdogd",
+    ],
+}
+*/
+
+// Tests
+// ------------------------------------------------------------------------------
+
+cc_test {
+    name: "init_tests",
+    defaults: ["init_defaults"],
+    srcs: [
+        "devices_test.cpp",
+        "init_parser_test.cpp",
+        "init_test.cpp",
+        "property_service_test.cpp",
+        "service_test.cpp",
+        "util_test.cpp",
+    ],
+    shared_libs: [
+        "libbase",
+        "libcutils",
+        "libselinux",
+    ],
+    static_libs: ["libinit"],
+}
+
+subdirs = ["*"]
diff --git a/init/Android.mk b/init/Android.mk
index 617a809..489d076 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -39,50 +39,6 @@
 
 # --
 
-# If building on Linux, then build unit test for the host.
-ifeq ($(HOST_OS),linux)
-include $(CLEAR_VARS)
-LOCAL_CPPFLAGS := $(init_cflags)
-LOCAL_SRC_FILES:= \
-    parser/tokenizer.cpp \
-
-LOCAL_MODULE := libinit_parser
-LOCAL_CLANG := true
-include $(BUILD_HOST_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := init_parser_tests
-LOCAL_SRC_FILES := \
-    parser/tokenizer_test.cpp \
-
-LOCAL_STATIC_LIBRARIES := libinit_parser
-LOCAL_CLANG := true
-include $(BUILD_HOST_NATIVE_TEST)
-endif
-
-include $(CLEAR_VARS)
-# b/38002385, work around clang-tidy segmentation fault.
-LOCAL_TIDY_CHECKS := -misc-forwarding-reference-overload
-LOCAL_CPPFLAGS := $(init_cflags)
-LOCAL_SRC_FILES:= \
-    action.cpp \
-    capabilities.cpp \
-    descriptors.cpp \
-    devices.cpp \
-    import_parser.cpp \
-    init_parser.cpp \
-    log.cpp \
-    parser.cpp \
-    service.cpp \
-    util.cpp \
-
-LOCAL_STATIC_LIBRARIES := libbase libselinux liblog libprocessgroup
-LOCAL_WHOLE_STATIC_LIBRARIES := libcap
-LOCAL_MODULE := libinit
-LOCAL_SANITIZE := integer
-LOCAL_CLANG := true
-include $(BUILD_STATIC_LIBRARY)
-
 include $(CLEAR_VARS)
 # b/38002385, work around clang-tidy segmentation fault.
 LOCAL_TIDY_CHECKS := -misc-forwarding-reference-overload
@@ -139,35 +95,3 @@
 LOCAL_SANITIZE := integer
 LOCAL_CLANG := true
 include $(BUILD_EXECUTABLE)
-
-
-# Unit tests.
-# =========================================================
-include $(CLEAR_VARS)
-# b/38002385, work around clang-tidy segmentation fault.
-LOCAL_TIDY_CHECKS := -misc-forwarding-reference-overload
-LOCAL_MODULE := init_tests
-LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_SRC_FILES := \
-    devices_test.cpp \
-    init_parser_test.cpp \
-    init_test.cpp \
-    property_service_test.cpp \
-    service_test.cpp \
-    util_test.cpp \
-
-LOCAL_SHARED_LIBRARIES += \
-    libbase \
-    libcutils \
-    libselinux \
-
-LOCAL_STATIC_LIBRARIES := libinit
-LOCAL_SANITIZE := integer
-LOCAL_CLANG := true
-LOCAL_CPPFLAGS := -Wall -Wextra -Werror -std=gnu++1z
-include $(BUILD_NATIVE_TEST)
-
-
-# Include targets in subdirs.
-# =========================================================
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/init/AndroidTest.xml b/init/AndroidTest.xml
deleted file mode 100644
index 3de69ed..0000000
--- a/init/AndroidTest.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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 init_tests">
-    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
-        <option name="cleanup" value="true" />
-        <option name="push" value="init_tests->/data/local/tmp/init_tests" />
-    </target_preparer>
-    <option name="test-suite-tag" value="apct" />
-    <test class="com.android.tradefed.testtype.GTest" >
-        <option name="native-test-device-path" value="/data/local/tmp" />
-        <option name="module-name" value="init_tests" />
-    </test>
-</configuration>
diff --git a/init/devices.cpp b/init/devices.cpp
index e0351a3..40f9740 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007-2014 The Android Open Source Project
+ * Copyright (C) 2007 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.
@@ -16,42 +16,20 @@
 
 #include "devices.h"
 
-#include <dirent.h>
 #include <errno.h>
-#include <fcntl.h>
 #include <fnmatch.h>
-#include <grp.h>
-#include <libgen.h>
-#include <linux/netlink.h>
-#include <pwd.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/sendfile.h>
-#include <sys/socket.h>
 #include <sys/sysmacros.h>
-#include <sys/time.h>
-#include <sys/un.h>
-#include <sys/wait.h>
 #include <unistd.h>
 
-#include <algorithm>
 #include <memory>
-#include <thread>
 
-#include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <cutils/uevent.h>
 #include <private/android_filesystem_config.h>
 #include <selinux/android.h>
-#include <selinux/label.h>
 #include <selinux/selinux.h>
 
-#include "keyword_map.h"
 #include "ueventd.h"
 #include "util.h"
 
@@ -59,302 +37,10 @@
 #error "Do not include init.h in files used by ueventd or watchdogd; it will expose init's globals"
 #endif
 
-static selabel_handle* sehandle;
-
-static android::base::unique_fd device_fd;
-
-Permissions::Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid)
-    : name_(name), perm_(perm), uid_(uid), gid_(gid), prefix_(false), wildcard_(false) {
-    // If the first * is the last character, then we'll treat name_ as a prefix
-    // Otherwise, if a * is present, then we do a full fnmatch().
-    auto wildcard_position = name_.find('*');
-    if (wildcard_position == name_.length() - 1) {
-        prefix_ = true;
-        name_.pop_back();
-    } else if (wildcard_position != std::string::npos) {
-        wildcard_ = true;
-    }
-}
-
-bool Permissions::Match(const std::string& path) const {
-    if (prefix_) {
-        return android::base::StartsWith(path, name_.c_str());
-    } else if (wildcard_) {
-        return fnmatch(name_.c_str(), path.c_str(), FNM_PATHNAME) == 0;
-    } else {
-        return path == name_;
-    }
-
-    return false;
-}
-
-bool SysfsPermissions::MatchWithSubsystem(const std::string& path,
-                                          const std::string& subsystem) const {
-    std::string path_basename = android::base::Basename(path);
-    if (name().find(subsystem) != std::string::npos) {
-        if (Match("/sys/class/" + subsystem + "/" + path_basename)) return true;
-        if (Match("/sys/bus/" + subsystem + "/devices/" + path_basename)) return true;
-    }
-    return Match(path);
-}
-
-void SysfsPermissions::SetPermissions(const std::string& path) const {
-    std::string attribute_file = path + "/" + attribute_;
-    LOG(INFO) << "fixup " << attribute_file << " " << uid() << " " << gid() << " " << std::oct
-              << perm();
-    chown(attribute_file.c_str(), uid(), gid());
-    chmod(attribute_file.c_str(), perm());
-}
-
-// TODO: Move these to be member variables of a future devices class.
-std::vector<Permissions> dev_permissions;
-std::vector<SysfsPermissions> sysfs_permissions;
-
-bool ParsePermissionsLine(std::vector<std::string>&& args, std::string* err, bool is_sysfs) {
-    if (is_sysfs && args.size() != 5) {
-        *err = "/sys/ lines must have 5 entries";
-        return false;
-    }
-
-    if (!is_sysfs && args.size() != 4) {
-        *err = "/dev/ lines must have 4 entries";
-        return false;
-    }
-
-    auto it = args.begin();
-    const std::string& name = *it++;
-
-    std::string sysfs_attribute;
-    if (is_sysfs) sysfs_attribute = *it++;
-
-    // args is now common to both sys and dev entries and contains: <perm> <uid> <gid>
-    std::string& perm_string = *it++;
-    char* end_pointer = 0;
-    mode_t perm = strtol(perm_string.c_str(), &end_pointer, 8);
-    if (end_pointer == nullptr || *end_pointer != '\0') {
-        *err = "invalid mode '" + perm_string + "'";
-        return false;
-    }
-
-    std::string& uid_string = *it++;
-    passwd* pwd = getpwnam(uid_string.c_str());
-    if (!pwd) {
-        *err = "invalid uid '" + uid_string + "'";
-        return false;
-    }
-    uid_t uid = pwd->pw_uid;
-
-    std::string& gid_string = *it++;
-    struct group* grp = getgrnam(gid_string.c_str());
-    if (!grp) {
-        *err = "invalid gid '" + gid_string + "'";
-        return false;
-    }
-    gid_t gid = grp->gr_gid;
-
-    if (is_sysfs) {
-        sysfs_permissions.emplace_back(name, sysfs_attribute, perm, uid, gid);
-    } else {
-        dev_permissions.emplace_back(name, perm, uid, gid);
-    }
-    return true;
-}
-
-// TODO: Move this to be a member variable of a future devices class.
-static std::vector<Subsystem> subsystems;
-
-std::string Subsystem::ParseDevPath(uevent* uevent) const {
-    std::string devname = devname_source_ == DevnameSource::DEVNAME_UEVENT_DEVNAME
-                              ? uevent->device_name
-                              : android::base::Basename(uevent->path);
-
-    return dir_name_ + "/" + devname;
-}
-
-bool SubsystemParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
-                                   int line, std::string* err) {
-    if (args.size() != 2) {
-        *err = "subsystems must have exactly one name";
-        return false;
-    }
-
-    if (std::find(subsystems.begin(), subsystems.end(), args[1]) != subsystems.end()) {
-        *err = "ignoring duplicate subsystem entry";
-        return false;
-    }
-
-    subsystem_.name_ = args[1];
-
-    return true;
-}
-
-bool SubsystemParser::ParseDevName(std::vector<std::string>&& args, std::string* err) {
-    if (args[1] == "uevent_devname") {
-        subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVNAME;
-        return true;
-    }
-    if (args[1] == "uevent_devpath") {
-        subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVPATH;
-        return true;
-    }
-
-    *err = "invalid devname '" + args[1] + "'";
-    return false;
-}
-
-bool SubsystemParser::ParseDirName(std::vector<std::string>&& args, std::string* err) {
-    if (args[1].front() != '/') {
-        *err = "dirname '" + args[1] + " ' does not start with '/'";
-        return false;
-    }
-
-    subsystem_.dir_name_ = args[1];
-    return true;
-}
-
-bool SubsystemParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) {
-    using OptionParser =
-        bool (SubsystemParser::*)(std::vector<std::string> && args, std::string * err);
-    static class OptionParserMap : public KeywordMap<OptionParser> {
-      private:
-        const Map& map() const override {
-            // clang-format off
-            static const Map option_parsers = {
-                {"devname",     {1,     1,      &SubsystemParser::ParseDevName}},
-                {"dirname",     {1,     1,      &SubsystemParser::ParseDirName}},
-            };
-            // clang-format on
-            return option_parsers;
-        }
-    } parser_map;
-
-    auto parser = parser_map.FindFunction(args, err);
-
-    if (!parser) {
-        return false;
-    }
-
-    return (this->*parser)(std::move(args), err);
-}
-
-void SubsystemParser::EndSection() {
-    subsystems.emplace_back(std::move(subsystem_));
-}
-
-static void fixup_sys_permissions(const std::string& upath, const std::string& subsystem) {
-    // upaths omit the "/sys" that paths in this list
-    // contain, so we prepend it...
-    std::string path = "/sys" + upath;
-
-    for (const auto& s : sysfs_permissions) {
-        if (s.MatchWithSubsystem(path, subsystem)) s.SetPermissions(path);
-    }
-
-    if (access(path.c_str(), F_OK) == 0) {
-        LOG(VERBOSE) << "restorecon_recursive: " << path;
-        selinux_android_restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE);
-    }
-}
-
-static std::tuple<mode_t, uid_t, gid_t> get_device_permissions(
-    const std::string& path, const std::vector<std::string>& links) {
-    // Search the perms list in reverse so that ueventd.$hardware can override ueventd.rc.
-    for (auto it = dev_permissions.rbegin(); it != dev_permissions.rend(); ++it) {
-        if (it->Match(path) || std::any_of(links.begin(), links.end(),
-                                           [it](const auto& link) { return it->Match(link); })) {
-            return {it->perm(), it->uid(), it->gid()};
-        }
-    }
-    /* Default if nothing found. */
-    return {0600, 0, 0};
-}
-
-static void make_device(const std::string& path, int block, int major, int minor,
-                        const std::vector<std::string>& links) {
-    dev_t dev;
-    char *secontext = NULL;
-
-    auto [mode, uid, gid] = get_device_permissions(path, links);
-    mode |= (block ? S_IFBLK : S_IFCHR);
-
-    if (sehandle) {
-        std::vector<const char*> c_links;
-        for (const auto& link : links) {
-            c_links.emplace_back(link.c_str());
-        }
-        c_links.emplace_back(nullptr);
-        if (selabel_lookup_best_match(sehandle, &secontext, path.c_str(), &c_links[0], mode)) {
-            PLOG(ERROR) << "Device '" << path << "' not created; cannot find SELinux label";
-            return;
-        }
-        setfscreatecon(secontext);
-    }
-
-    dev = makedev(major, minor);
-    /* Temporarily change egid to avoid race condition setting the gid of the
-     * device node. Unforunately changing the euid would prevent creation of
-     * some device nodes, so the uid has to be set with chown() and is still
-     * racy. Fixing the gid race at least fixed the issue with system_server
-     * opening dynamic input devices under the AID_INPUT gid. */
-    if (setegid(gid)) {
-        PLOG(ERROR) << "setegid(" << gid << ") for " << path << " device failed";
-        goto out;
-    }
-    /* If the node already exists update its SELinux label to handle cases when
-     * it was created with the wrong context during coldboot procedure. */
-    if (mknod(path.c_str(), mode, dev) && (errno == EEXIST) && secontext) {
-        char* fcon = nullptr;
-        int rc = lgetfilecon(path.c_str(), &fcon);
-        if (rc < 0) {
-            PLOG(ERROR) << "Cannot get SELinux label on '" << path << "' device";
-            goto out;
-        }
-
-        bool different = strcmp(fcon, secontext) != 0;
-        freecon(fcon);
-
-        if (different && lsetfilecon(path.c_str(), secontext)) {
-            PLOG(ERROR) << "Cannot set '" << secontext << "' SELinux label on '" << path << "' device";
-        }
-    }
-
-out:
-    chown(path.c_str(), uid, -1);
-    if (setegid(AID_ROOT)) {
-        PLOG(FATAL) << "setegid(AID_ROOT) failed";
-    }
-
-    if (secontext) {
-        freecon(secontext);
-        setfscreatecon(NULL);
-    }
-}
-
-// TODO: Move this to be a member variable of a future devices class.
-std::vector<std::string> platform_devices;
-
-// Given a path that may start with a platform device, find the length of the
-// platform device prefix.  If it doesn't start with a platform device, return false
-bool find_platform_device(const std::string& path, std::string* out_path) {
-    out_path->clear();
-    // platform_devices is searched backwards, since parents are added before their children,
-    // and we want to match as deep of a child as we can.
-    for (auto it = platform_devices.rbegin(); it != platform_devices.rend(); ++it) {
-        auto platform_device_path_length = it->length();
-        if (platform_device_path_length < path.length() &&
-            path[platform_device_path_length] == '/' &&
-            android::base::StartsWith(path, it->c_str())) {
-            *out_path = *it;
-            return true;
-        }
-    }
-    return false;
-}
-
 /* Given a path that may start with a PCI device, populate the supplied buffer
  * with the PCI domain/bus number and the peripheral ID and return 0.
  * If it doesn't start with a PCI device, or there is some error, return -1 */
-static bool find_pci_device_prefix(const std::string& path, std::string* result) {
+static bool FindPciDevicePrefix(const std::string& path, std::string* result) {
     result->clear();
 
     if (!android::base::StartsWith(path, "/devices/pci")) return false;
@@ -385,7 +71,7 @@
  * the supplied buffer with the virtual block device ID and return 0.
  * If it doesn't start with a virtual block device, or there is some
  * error, return -1 */
-static bool find_vbd_device_prefix(const std::string& path, std::string* result) {
+static bool FindVbdDevicePrefix(const std::string& path, std::string* result) {
     result->clear();
 
     if (!android::base::StartsWith(path, "/devices/vbd-")) return false;
@@ -405,59 +91,177 @@
     return true;
 }
 
-void parse_event(const char* msg, uevent* uevent) {
-    uevent->partition_num = -1;
-    uevent->major = -1;
-    uevent->minor = -1;
-    // currently ignoring SEQNUM
-    while(*msg) {
-        if(!strncmp(msg, "ACTION=", 7)) {
-            msg += 7;
-            uevent->action = msg;
-        } else if(!strncmp(msg, "DEVPATH=", 8)) {
-            msg += 8;
-            uevent->path = msg;
-        } else if(!strncmp(msg, "SUBSYSTEM=", 10)) {
-            msg += 10;
-            uevent->subsystem = msg;
-        } else if(!strncmp(msg, "FIRMWARE=", 9)) {
-            msg += 9;
-            uevent->firmware = msg;
-        } else if(!strncmp(msg, "MAJOR=", 6)) {
-            msg += 6;
-            uevent->major = atoi(msg);
-        } else if(!strncmp(msg, "MINOR=", 6)) {
-            msg += 6;
-            uevent->minor = atoi(msg);
-        } else if(!strncmp(msg, "PARTN=", 6)) {
-            msg += 6;
-            uevent->partition_num = atoi(msg);
-        } else if(!strncmp(msg, "PARTNAME=", 9)) {
-            msg += 9;
-            uevent->partition_name = msg;
-        } else if(!strncmp(msg, "DEVNAME=", 8)) {
-            msg += 8;
-            uevent->device_name = msg;
+Permissions::Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid)
+    : name_(name), perm_(perm), uid_(uid), gid_(gid), prefix_(false), wildcard_(false) {
+    // Set 'prefix_' or 'wildcard_' based on the below cases:
+    //
+    // 1) No '*' in 'name' -> Neither are set and Match() checks a given path for strict
+    //    equality with 'name'
+    //
+    // 2) '*' only appears as the last character in 'name' -> 'prefix'_ is set to true and
+    //    Match() checks if 'name' is a prefix of a given path.
+    //
+    // 3) '*' appears elsewhere -> 'wildcard_' is set to true and Match() uses fnmatch()
+    //    with FNM_PATHNAME to compare 'name' to a given path.
+
+    auto wildcard_position = name_.find('*');
+    if (wildcard_position != std::string::npos) {
+        if (wildcard_position == name_.length() - 1) {
+            prefix_ = true;
+            name_.pop_back();
+        } else {
+            wildcard_ = true;
         }
-
-        // advance to after the next \0
-        while(*msg++)
-            ;
-    }
-
-    if (LOG_UEVENTS) {
-        LOG(INFO) << "event { '" << uevent->action << "', '" << uevent->path << "', '"
-                  << uevent->subsystem << "', '" << uevent->firmware << "', " << uevent->major
-                  << ", " << uevent->minor << " }";
     }
 }
 
-std::vector<std::string> get_character_device_symlinks(uevent* uevent) {
+bool Permissions::Match(const std::string& path) const {
+    if (prefix_) return android::base::StartsWith(path, name_.c_str());
+    if (wildcard_) return fnmatch(name_.c_str(), path.c_str(), FNM_PATHNAME) == 0;
+    return path == name_;
+}
+
+bool SysfsPermissions::MatchWithSubsystem(const std::string& path,
+                                          const std::string& subsystem) const {
+    std::string path_basename = android::base::Basename(path);
+    if (name().find(subsystem) != std::string::npos) {
+        if (Match("/sys/class/" + subsystem + "/" + path_basename)) return true;
+        if (Match("/sys/bus/" + subsystem + "/devices/" + path_basename)) return true;
+    }
+    return Match(path);
+}
+
+void SysfsPermissions::SetPermissions(const std::string& path) const {
+    std::string attribute_file = path + "/" + attribute_;
+    LOG(VERBOSE) << "fixup " << attribute_file << " " << uid() << " " << gid() << " " << std::oct
+                 << perm();
+
+    if (access(attribute_file.c_str(), F_OK) == 0) {
+        if (chown(attribute_file.c_str(), uid(), gid()) != 0) {
+            PLOG(ERROR) << "chown(" << attribute_file << ", " << uid() << ", " << gid()
+                        << ") failed";
+        }
+        if (chmod(attribute_file.c_str(), perm()) != 0) {
+            PLOG(ERROR) << "chmod(" << attribute_file << ", " << perm() << ") failed";
+        }
+    }
+}
+
+// Given a path that may start with a platform device, find the length of the
+// platform device prefix.  If it doesn't start with a platform device, return false
+bool PlatformDeviceList::Find(const std::string& path, std::string* out_path) const {
+    out_path->clear();
+    // platform_devices is searched backwards, since parents are added before their children,
+    // and we want to match as deep of a child as we can.
+    for (auto it = platform_devices_.crbegin(); it != platform_devices_.crend(); ++it) {
+        auto platform_device_path_length = it->length();
+        if (platform_device_path_length < path.length() &&
+            path[platform_device_path_length] == '/' &&
+            android::base::StartsWith(path, it->c_str())) {
+            *out_path = *it;
+            return true;
+        }
+    }
+    return false;
+}
+
+void DeviceHandler::FixupSysPermissions(const std::string& upath,
+                                        const std::string& subsystem) const {
+    // upaths omit the "/sys" that paths in this list
+    // contain, so we prepend it...
+    std::string path = "/sys" + upath;
+
+    for (const auto& s : sysfs_permissions_) {
+        if (s.MatchWithSubsystem(path, subsystem)) s.SetPermissions(path);
+    }
+
+    if (access(path.c_str(), F_OK) == 0) {
+        LOG(VERBOSE) << "restorecon_recursive: " << path;
+        if (selinux_android_restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
+            PLOG(ERROR) << "selinux_android_restorecon(" << path << ") failed";
+        }
+    }
+}
+
+std::tuple<mode_t, uid_t, gid_t> DeviceHandler::GetDevicePermissions(
+    const std::string& path, const std::vector<std::string>& links) const {
+    // Search the perms list in reverse so that ueventd.$hardware can override ueventd.rc.
+    for (auto it = dev_permissions_.crbegin(); it != dev_permissions_.crend(); ++it) {
+        if (it->Match(path) || std::any_of(links.cbegin(), links.cend(),
+                                           [it](const auto& link) { return it->Match(link); })) {
+            return {it->perm(), it->uid(), it->gid()};
+        }
+    }
+    /* Default if nothing found. */
+    return {0600, 0, 0};
+}
+
+void DeviceHandler::MakeDevice(const std::string& path, int block, int major, int minor,
+                               const std::vector<std::string>& links) const {
+    auto[mode, uid, gid] = GetDevicePermissions(path, links);
+    mode |= (block ? S_IFBLK : S_IFCHR);
+
+    char* secontext = nullptr;
+    if (sehandle_) {
+        std::vector<const char*> c_links;
+        for (const auto& link : links) {
+            c_links.emplace_back(link.c_str());
+        }
+        c_links.emplace_back(nullptr);
+        if (selabel_lookup_best_match(sehandle_, &secontext, path.c_str(), &c_links[0], mode)) {
+            PLOG(ERROR) << "Device '" << path << "' not created; cannot find SELinux label";
+            return;
+        }
+        setfscreatecon(secontext);
+    }
+
+    dev_t dev = makedev(major, minor);
+    /* Temporarily change egid to avoid race condition setting the gid of the
+     * device node. Unforunately changing the euid would prevent creation of
+     * some device nodes, so the uid has to be set with chown() and is still
+     * racy. Fixing the gid race at least fixed the issue with system_server
+     * opening dynamic input devices under the AID_INPUT gid. */
+    if (setegid(gid)) {
+        PLOG(ERROR) << "setegid(" << gid << ") for " << path << " device failed";
+        goto out;
+    }
+    /* If the node already exists update its SELinux label to handle cases when
+     * it was created with the wrong context during coldboot procedure. */
+    if (mknod(path.c_str(), mode, dev) && (errno == EEXIST) && secontext) {
+        char* fcon = nullptr;
+        int rc = lgetfilecon(path.c_str(), &fcon);
+        if (rc < 0) {
+            PLOG(ERROR) << "Cannot get SELinux label on '" << path << "' device";
+            goto out;
+        }
+
+        bool different = strcmp(fcon, secontext) != 0;
+        freecon(fcon);
+
+        if (different && lsetfilecon(path.c_str(), secontext)) {
+            PLOG(ERROR) << "Cannot set '" << secontext << "' SELinux label on '" << path
+                        << "' device";
+        }
+    }
+
+out:
+    chown(path.c_str(), uid, -1);
+    if (setegid(AID_ROOT)) {
+        PLOG(FATAL) << "setegid(AID_ROOT) failed";
+    }
+
+    if (secontext) {
+        freecon(secontext);
+        setfscreatecon(nullptr);
+    }
+}
+
+std::vector<std::string> DeviceHandler::GetCharacterDeviceSymlinks(const Uevent& uevent) const {
     std::string parent_device;
-    if (!find_platform_device(uevent->path, &parent_device)) return {};
+    if (!platform_devices_.Find(uevent.path, &parent_device)) return {};
 
     // skip path to the parent driver
-    std::string path = uevent->path.substr(parent_device.length());
+    std::string path = uevent.path.substr(parent_device.length());
 
     if (!android::base::StartsWith(path, "/usb")) return {};
 
@@ -484,7 +288,7 @@
     auto name_string = path.substr(start, length);
 
     std::vector<std::string> links;
-    links.emplace_back("/dev/usb/" + uevent->subsystem + name_string);
+    links.emplace_back("/dev/usb/" + uevent.subsystem + name_string);
 
     mkdir("/dev/usb", 0755);
 
@@ -493,7 +297,7 @@
 
 // replaces any unacceptable characters with '_', the
 // length of the resulting string is equal to the input string
-void sanitize_partition_name(std::string* string) {
+void SanitizePartitionName(std::string* string) {
     const char* accept =
         "abcdefghijklmnopqrstuvwxyz"
         "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
@@ -508,11 +312,11 @@
     }
 }
 
-std::vector<std::string> get_block_device_symlinks(uevent* uevent) {
+std::vector<std::string> DeviceHandler::GetBlockDeviceSymlinks(const Uevent& uevent) const {
     std::string device;
     std::string type;
 
-    if (find_platform_device(uevent->path, &device)) {
+    if (platform_devices_.Find(uevent.path, &device)) {
         // Skip /devices/platform or /devices/ if present
         static const std::string devices_platform_prefix = "/devices/platform/";
         static const std::string devices_prefix = "/devices/";
@@ -524,9 +328,9 @@
         }
 
         type = "platform";
-    } else if (find_pci_device_prefix(uevent->path, &device)) {
+    } else if (FindPciDevicePrefix(uevent.path, &device)) {
         type = "pci";
-    } else if (find_vbd_device_prefix(uevent->path, &device)) {
+    } else if (FindVbdDevicePrefix(uevent.path, &device)) {
         type = "vbd";
     } else {
         return {};
@@ -538,379 +342,137 @@
 
     auto link_path = "/dev/block/" + type + "/" + device;
 
-    if (!uevent->partition_name.empty()) {
-        std::string partition_name_sanitized(uevent->partition_name);
-        sanitize_partition_name(&partition_name_sanitized);
-        if (partition_name_sanitized != uevent->partition_name) {
-            LOG(VERBOSE) << "Linking partition '" << uevent->partition_name << "' as '"
+    if (!uevent.partition_name.empty()) {
+        std::string partition_name_sanitized(uevent.partition_name);
+        SanitizePartitionName(&partition_name_sanitized);
+        if (partition_name_sanitized != uevent.partition_name) {
+            LOG(VERBOSE) << "Linking partition '" << uevent.partition_name << "' as '"
                          << partition_name_sanitized << "'";
         }
         links.emplace_back(link_path + "/by-name/" + partition_name_sanitized);
     }
 
-    if (uevent->partition_num >= 0) {
-        links.emplace_back(link_path + "/by-num/p" + std::to_string(uevent->partition_num));
+    if (uevent.partition_num >= 0) {
+        links.emplace_back(link_path + "/by-num/p" + std::to_string(uevent.partition_num));
     }
 
-    auto last_slash = uevent->path.rfind('/');
-    links.emplace_back(link_path + "/" + uevent->path.substr(last_slash + 1));
+    auto last_slash = uevent.path.rfind('/');
+    links.emplace_back(link_path + "/" + uevent.path.substr(last_slash + 1));
 
     return links;
 }
 
-static void make_link_init(const std::string& oldpath, const std::string& newpath) {
-    if (mkdir_recursive(dirname(newpath.c_str()), 0755, sehandle)) {
-        PLOG(ERROR) << "Failed to create directory " << dirname(newpath.c_str());
-    }
-
-    if (symlink(oldpath.c_str(), newpath.c_str()) && errno != EEXIST) {
-        PLOG(ERROR) << "Failed to symlink " << oldpath << " to " << newpath;
-    }
-}
-
-static void remove_link(const std::string& oldpath, const std::string& newpath) {
-    std::string path;
-    if (android::base::Readlink(newpath, &path) && path == oldpath) unlink(newpath.c_str());
-}
-
-static void handle_device(const std::string& action, const std::string& devpath, int block,
-                          int major, int minor, const std::vector<std::string>& links) {
+void DeviceHandler::HandleDevice(const std::string& action, const std::string& devpath, int block,
+                                 int major, int minor, const std::vector<std::string>& links) const {
     if (action == "add") {
-        make_device(devpath, block, major, minor, links);
+        MakeDevice(devpath, block, major, minor, links);
         for (const auto& link : links) {
-            make_link_init(devpath, link);
+            if (mkdir_recursive(android::base::Dirname(link), 0755, sehandle_)) {
+                PLOG(ERROR) << "Failed to create directory " << android::base::Dirname(link);
+            }
+
+            if (symlink(devpath.c_str(), link.c_str()) && errno != EEXIST) {
+                PLOG(ERROR) << "Failed to symlink " << devpath << " to " << link;
+            }
         }
     }
 
     if (action == "remove") {
         for (const auto& link : links) {
-            remove_link(devpath, link);
+            std::string link_path;
+            if (android::base::Readlink(link, &link_path) && link_path == devpath) {
+                unlink(link.c_str());
+            }
         }
         unlink(devpath.c_str());
     }
 }
 
-void handle_platform_device_event(uevent* uevent) {
-    if (uevent->action == "add") {
-        platform_devices.emplace_back(uevent->path);
-    } else if (uevent->action == "remove") {
-        auto it = std::find(platform_devices.begin(), platform_devices.end(), uevent->path);
-        if (it != platform_devices.end()) platform_devices.erase(it);
+void DeviceHandler::HandlePlatformDeviceEvent(const Uevent& uevent) {
+    if (uevent.action == "add") {
+        platform_devices_.Add(uevent.path);
+    } else if (uevent.action == "remove") {
+        platform_devices_.Remove(uevent.path);
     }
 }
 
-static void handle_block_device_event(uevent* uevent) {
+void DeviceHandler::HandleBlockDeviceEvent(const Uevent& uevent) const {
     // if it's not a /dev device, nothing to do
-    if (uevent->major < 0 || uevent->minor < 0) return;
+    if (uevent.major < 0 || uevent.minor < 0) return;
 
     const char* base = "/dev/block/";
-    make_dir(base, 0755, sehandle);
+    make_dir(base, 0755, sehandle_);
 
-    std::string name = android::base::Basename(uevent->path);
+    std::string name = android::base::Basename(uevent.path);
     std::string devpath = base + name;
 
     std::vector<std::string> links;
-    if (android::base::StartsWith(uevent->path, "/devices")) {
-        links = get_block_device_symlinks(uevent);
+    if (android::base::StartsWith(uevent.path, "/devices")) {
+        links = GetBlockDeviceSymlinks(uevent);
     }
 
-    handle_device(uevent->action, devpath, 1, uevent->major, uevent->minor, links);
+    HandleDevice(uevent.action, devpath, 1, uevent.major, uevent.minor, links);
 }
 
-static void handle_generic_device_event(uevent* uevent) {
+void DeviceHandler::HandleGenericDeviceEvent(const Uevent& uevent) const {
     // if it's not a /dev device, nothing to do
-    if (uevent->major < 0 || uevent->minor < 0) return;
+    if (uevent.major < 0 || uevent.minor < 0) return;
 
     std::string devpath;
 
-    if (android::base::StartsWith(uevent->subsystem, "usb")) {
-        if (uevent->subsystem == "usb") {
-            if (!uevent->device_name.empty()) {
-                devpath = "/dev/" + uevent->device_name;
+    if (android::base::StartsWith(uevent.subsystem, "usb")) {
+        if (uevent.subsystem == "usb") {
+            if (!uevent.device_name.empty()) {
+                devpath = "/dev/" + uevent.device_name;
             } else {
                 // This imitates the file system that would be created
                 // if we were using devfs instead.
                 // Minors are broken up into groups of 128, starting at "001"
-                int bus_id = uevent->minor / 128 + 1;
-                int device_id = uevent->minor % 128 + 1;
+                int bus_id = uevent.minor / 128 + 1;
+                int device_id = uevent.minor % 128 + 1;
                 devpath = android::base::StringPrintf("/dev/bus/usb/%03d/%03d", bus_id, device_id);
             }
         } else {
             // ignore other USB events
             return;
         }
-    } else if (auto subsystem = std::find(subsystems.begin(), subsystems.end(), uevent->subsystem);
-               subsystem != subsystems.end()) {
+    } else if (const auto subsystem =
+                   std::find(subsystems_.cbegin(), subsystems_.cend(), uevent.subsystem);
+               subsystem != subsystems_.cend()) {
         devpath = subsystem->ParseDevPath(uevent);
     } else {
-        devpath = "/dev/" + android::base::Basename(uevent->path);
+        devpath = "/dev/" + android::base::Basename(uevent.path);
     }
 
-    mkdir_recursive(android::base::Dirname(devpath), 0755, sehandle);
+    mkdir_recursive(android::base::Dirname(devpath), 0755, sehandle_);
 
-    auto links = get_character_device_symlinks(uevent);
+    auto links = GetCharacterDeviceSymlinks(uevent);
 
-    handle_device(uevent->action, devpath, 0, uevent->major, uevent->minor, links);
+    HandleDevice(uevent.action, devpath, 0, uevent.major, uevent.minor, links);
 }
 
-static void handle_device_event(struct uevent *uevent)
-{
-    if (uevent->action == "add" || uevent->action == "change" || uevent->action == "online") {
-        fixup_sys_permissions(uevent->path, uevent->subsystem);
+void DeviceHandler::HandleDeviceEvent(const Uevent& uevent) {
+    if (uevent.action == "add" || uevent.action == "change" || uevent.action == "online") {
+        FixupSysPermissions(uevent.path, uevent.subsystem);
     }
 
-    if (uevent->subsystem == "block") {
-        handle_block_device_event(uevent);
-    } else if (uevent->subsystem == "platform") {
-        handle_platform_device_event(uevent);
+    if (uevent.subsystem == "block") {
+        HandleBlockDeviceEvent(uevent);
+    } else if (uevent.subsystem == "platform") {
+        HandlePlatformDeviceEvent(uevent);
     } else {
-        handle_generic_device_event(uevent);
+        HandleGenericDeviceEvent(uevent);
     }
 }
 
-static void load_firmware(uevent* uevent, const std::string& root,
-                          int fw_fd, size_t fw_size,
-                          int loading_fd, int data_fd) {
-    // Start transfer.
-    android::base::WriteFully(loading_fd, "1", 1);
+DeviceHandler::DeviceHandler(std::vector<Permissions> dev_permissions,
+                             std::vector<SysfsPermissions> sysfs_permissions,
+                             std::vector<Subsystem> subsystems)
+    : dev_permissions_(std::move(dev_permissions)),
+      sysfs_permissions_(std::move(sysfs_permissions)),
+      subsystems_(std::move(subsystems)),
+      sehandle_(selinux_android_file_context_handle()) {}
 
-    // Copy the firmware.
-    int rc = sendfile(data_fd, fw_fd, nullptr, fw_size);
-    if (rc == -1) {
-        PLOG(ERROR) << "firmware: sendfile failed { '" << root << "', '" << uevent->firmware << "' }";
-    }
-
-    // Tell the firmware whether to abort or commit.
-    const char* response = (rc != -1) ? "0" : "-1";
-    android::base::WriteFully(loading_fd, response, strlen(response));
-}
-
-static int is_booting() {
-    return access("/dev/.booting", F_OK) == 0;
-}
-
-static void process_firmware_event(uevent* uevent) {
-    int booting = is_booting();
-
-    LOG(INFO) << "firmware: loading '" << uevent->firmware << "' for '" << uevent->path << "'";
-
-    std::string root = "/sys" + uevent->path;
-    std::string loading = root + "/loading";
-    std::string data = root + "/data";
-
-    android::base::unique_fd loading_fd(open(loading.c_str(), O_WRONLY|O_CLOEXEC));
-    if (loading_fd == -1) {
-        PLOG(ERROR) << "couldn't open firmware loading fd for " << uevent->firmware;
-        return;
-    }
-
-    android::base::unique_fd data_fd(open(data.c_str(), O_WRONLY|O_CLOEXEC));
-    if (data_fd == -1) {
-        PLOG(ERROR) << "couldn't open firmware data fd for " << uevent->firmware;
-        return;
-    }
-
-    static const char* firmware_dirs[] = {"/etc/firmware/", "/vendor/firmware/",
-                                          "/firmware/image/"};
-
-try_loading_again:
-    for (size_t i = 0; i < arraysize(firmware_dirs); i++) {
-        std::string file = firmware_dirs[i] + uevent->firmware;
-        android::base::unique_fd fw_fd(open(file.c_str(), O_RDONLY|O_CLOEXEC));
-        struct stat sb;
-        if (fw_fd != -1 && fstat(fw_fd, &sb) != -1) {
-            load_firmware(uevent, root, fw_fd, sb.st_size, loading_fd, data_fd);
-            return;
-        }
-    }
-
-    if (booting) {
-        // If we're not fully booted, we may be missing
-        // filesystems needed for firmware, wait and retry.
-        std::this_thread::sleep_for(100ms);
-        booting = is_booting();
-        goto try_loading_again;
-    }
-
-    LOG(ERROR) << "firmware: could not find firmware for " << uevent->firmware;
-
-    // Write "-1" as our response to the kernel's firmware request, since we have nothing for it.
-    write(loading_fd, "-1", 2);
-}
-
-static void handle_firmware_event(uevent* uevent) {
-    if (uevent->subsystem != "firmware" || uevent->action != "add") return;
-
-    // Loading the firmware in a child means we can do that in parallel...
-    // (We ignore SIGCHLD rather than wait for our children.)
-    pid_t pid = fork();
-    if (pid == 0) {
-        Timer t;
-        process_firmware_event(uevent);
-        LOG(INFO) << "loading " << uevent->path << " took " << t;
-        _exit(EXIT_SUCCESS);
-    } else if (pid == -1) {
-        PLOG(ERROR) << "could not fork to process firmware event for " << uevent->firmware;
-    }
-}
-
-static bool inline should_stop_coldboot(coldboot_action_t act)
-{
-    return (act == COLDBOOT_STOP || act == COLDBOOT_FINISH);
-}
-
-#define UEVENT_MSG_LEN  2048
-
-static inline coldboot_action_t handle_device_fd_with(
-        std::function<coldboot_action_t(uevent* uevent)> handle_uevent)
-{
-    char msg[UEVENT_MSG_LEN+2];
-    int n;
-    while ((n = uevent_kernel_multicast_recv(device_fd, msg, UEVENT_MSG_LEN)) > 0) {
-        if(n >= UEVENT_MSG_LEN)   /* overflow -- discard */
-            continue;
-
-        msg[n] = '\0';
-        msg[n+1] = '\0';
-
-        uevent uevent;
-        parse_event(msg, &uevent);
-        coldboot_action_t act = handle_uevent(&uevent);
-        if (should_stop_coldboot(act))
-            return act;
-    }
-
-    return COLDBOOT_CONTINUE;
-}
-
-coldboot_action_t handle_device_fd(coldboot_callback fn)
-{
-    coldboot_action_t ret = handle_device_fd_with(
-        [&](uevent* uevent) -> coldboot_action_t {
-            // default is to always create the devices
-            coldboot_action_t act = COLDBOOT_CREATE;
-            if (fn) {
-                act = fn(uevent);
-            }
-
-            if (act == COLDBOOT_CREATE || act == COLDBOOT_STOP) {
-                handle_device_event(uevent);
-                handle_firmware_event(uevent);
-            }
-
-            return act;
-        });
-
-    return ret;
-}
-
-/* Coldboot walks parts of the /sys tree and pokes the uevent files
-** to cause the kernel to regenerate device add events that happened
-** before init's device manager was started
-**
-** We drain any pending events from the netlink socket every time
-** we poke another uevent file to make sure we don't overrun the
-** socket's buffer.
-*/
-
-static coldboot_action_t do_coldboot(DIR *d, coldboot_callback fn)
-{
-    struct dirent *de;
-    int dfd, fd;
-    coldboot_action_t act = COLDBOOT_CONTINUE;
-
-    dfd = dirfd(d);
-
-    fd = openat(dfd, "uevent", O_WRONLY);
-    if (fd >= 0) {
-        write(fd, "add\n", 4);
-        close(fd);
-        act = handle_device_fd(fn);
-        if (should_stop_coldboot(act))
-            return act;
-    }
-
-    while (!should_stop_coldboot(act) && (de = readdir(d))) {
-        DIR *d2;
-
-        if(de->d_type != DT_DIR || de->d_name[0] == '.')
-            continue;
-
-        fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
-        if(fd < 0)
-            continue;
-
-        d2 = fdopendir(fd);
-        if(d2 == 0)
-            close(fd);
-        else {
-            act = do_coldboot(d2, fn);
-            closedir(d2);
-        }
-    }
-
-    // default is always to continue looking for uevents
-    return act;
-}
-
-static coldboot_action_t coldboot(const char *path, coldboot_callback fn)
-{
-    std::unique_ptr<DIR, decltype(&closedir)> d(opendir(path), closedir);
-    if (d) {
-        return do_coldboot(d.get(), fn);
-    }
-
-    return COLDBOOT_CONTINUE;
-}
-
-void device_init(const char* path, coldboot_callback fn) {
-    if (!sehandle) {
-        sehandle = selinux_android_file_context_handle();
-    }
-    // open uevent socket and selinux status only if it hasn't been
-    // done before
-    if (device_fd == -1) {
-        /* is 256K enough? udev uses 16MB! */
-        device_fd.reset(uevent_open_socket(256 * 1024, true));
-        if (device_fd == -1) {
-            return;
-        }
-        fcntl(device_fd, F_SETFL, O_NONBLOCK);
-    }
-
-    if (access(COLDBOOT_DONE, F_OK) == 0) {
-        LOG(VERBOSE) << "Skipping coldboot, already done!";
-        return;
-    }
-
-    Timer t;
-    coldboot_action_t act;
-    if (!path) {
-        act = coldboot("/sys/class", fn);
-        if (!should_stop_coldboot(act)) {
-            act = coldboot("/sys/block", fn);
-            if (!should_stop_coldboot(act)) {
-                act = coldboot("/sys/devices", fn);
-            }
-        }
-    } else {
-        act = coldboot(path, fn);
-    }
-
-    // If we have a callback, then do as it says. If no, then the default is
-    // to always create COLDBOOT_DONE file.
-    if (!fn || (act == COLDBOOT_FINISH)) {
-        close(open(COLDBOOT_DONE, O_WRONLY|O_CREAT|O_CLOEXEC, 0000));
-    }
-
-    LOG(INFO) << "Coldboot took " << t;
-}
-
-void device_close() {
-    platform_devices.clear();
-    device_fd.reset();
-}
-
-int get_device_fd() {
-    return device_fd;
-}
+DeviceHandler::DeviceHandler()
+    : DeviceHandler(std::vector<Permissions>{}, std::vector<SysfsPermissions>{},
+                    std::vector<Subsystem>{}) {}
diff --git a/init/devices.h b/init/devices.h
index 647b4c4..50f49fc 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -20,35 +20,14 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 
-#include <functional>
+#include <algorithm>
 #include <string>
 #include <vector>
 
-#include "init_parser.h"
+#include <android-base/file.h>
+#include <selinux/label.h>
 
-enum coldboot_action_t {
-    // coldboot continues without creating the device for the uevent
-    COLDBOOT_CONTINUE = 0,
-    // coldboot continues after creating the device for the uevent
-    COLDBOOT_CREATE,
-    // coldboot stops after creating the device for uevent but doesn't
-    // create the COLDBOOT_DONE file
-    COLDBOOT_STOP,
-    // same as COLDBOOT_STOP, but creates the COLDBOOT_DONE file
-    COLDBOOT_FINISH
-};
-
-struct uevent {
-    std::string action;
-    std::string path;
-    std::string subsystem;
-    std::string firmware;
-    std::string partition_name;
-    std::string device_name;
-    int partition_num;
-    int major;
-    int minor;
-};
+#include "uevent.h"
 
 class Permissions {
   public:
@@ -93,9 +72,15 @@
 
     // Returns the full path for a uevent of a device that is a member of this subsystem,
     // according to the rules parsed from ueventd.rc
-    std::string ParseDevPath(uevent* uevent) const;
+    std::string ParseDevPath(const Uevent& uevent) const {
+        std::string devname = devname_source_ == DevnameSource::DEVNAME_UEVENT_DEVNAME
+                                  ? uevent.device_name
+                                  : android::base::Basename(uevent.path);
 
-    bool operator==(const std::string& string_name) { return name_ == string_name; }
+        return dir_name_ + "/" + devname;
+    }
+
+    bool operator==(const std::string& string_name) const { return name_ == string_name; }
 
   private:
     enum class DevnameSource {
@@ -108,34 +93,54 @@
     DevnameSource devname_source_;
 };
 
-class SubsystemParser : public SectionParser {
+class PlatformDeviceList {
   public:
-    SubsystemParser() {}
-    bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
-                      std::string* err) override;
-    bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) override;
-    void EndSection() override;
+    void Add(const std::string& path) { platform_devices_.emplace_back(path); }
+    void Remove(const std::string& path) {
+        auto it = std::find(platform_devices_.begin(), platform_devices_.end(), path);
+        if (it != platform_devices_.end()) platform_devices_.erase(it);
+    }
+    bool Find(const std::string& path, std::string* out_path) const;
+    auto size() const { return platform_devices_.size(); }
 
   private:
-    bool ParseDevName(std::vector<std::string>&& args, std::string* err);
-    bool ParseDirName(std::vector<std::string>&& args, std::string* err);
-
-    Subsystem subsystem_;
+    std::vector<std::string> platform_devices_;
 };
 
-bool ParsePermissionsLine(std::vector<std::string>&& args, std::string* err, bool is_sysfs);
-typedef std::function<coldboot_action_t(struct uevent* uevent)> coldboot_callback;
-extern coldboot_action_t handle_device_fd(coldboot_callback fn = nullptr);
-extern void device_init(const char* path = nullptr, coldboot_callback fn = nullptr);
-extern void device_close();
-int get_device_fd();
+class DeviceHandler {
+  public:
+    friend class DeviceHandlerTester;
+
+    DeviceHandler();
+    DeviceHandler(std::vector<Permissions> dev_permissions,
+                  std::vector<SysfsPermissions> sysfs_permissions,
+                  std::vector<Subsystem> subsystems);
+    ~DeviceHandler(){};
+
+    void HandleDeviceEvent(const Uevent& uevent);
+    std::vector<std::string> GetBlockDeviceSymlinks(const Uevent& uevent) const;
+
+  private:
+    void FixupSysPermissions(const std::string& upath, const std::string& subsystem) const;
+    std::tuple<mode_t, uid_t, gid_t> GetDevicePermissions(
+        const std::string& path, const std::vector<std::string>& links) const;
+    void MakeDevice(const std::string& path, int block, int major, int minor,
+                    const std::vector<std::string>& links) const;
+    std::vector<std::string> GetCharacterDeviceSymlinks(const Uevent& uevent) const;
+    void HandleDevice(const std::string& action, const std::string& devpath, int block, int major,
+                      int minor, const std::vector<std::string>& links) const;
+    void HandlePlatformDeviceEvent(const Uevent& uevent);
+    void HandleBlockDeviceEvent(const Uevent& uevent) const;
+    void HandleGenericDeviceEvent(const Uevent& uevent) const;
+
+    std::vector<Permissions> dev_permissions_;
+    std::vector<SysfsPermissions> sysfs_permissions_;
+    std::vector<Subsystem> subsystems_;
+    PlatformDeviceList platform_devices_;
+    selabel_handle* sehandle_;
+};
 
 // Exposed for testing
-extern std::vector<std::string> platform_devices;
-bool find_platform_device(const std::string& path, std::string* out_path);
-std::vector<std::string> get_character_device_symlinks(uevent* uevent);
-std::vector<std::string> get_block_device_symlinks(uevent* uevent);
-void sanitize_partition_name(std::string* string);
-void handle_platform_device_event(uevent* uevent);
+void SanitizePartitionName(std::string* string);
 
-#endif /* _INIT_DEVICES_H */
+#endif
diff --git a/init/devices_test.cpp b/init/devices_test.cpp
index 66521db..41b101b 100644
--- a/init/devices_test.cpp
+++ b/init/devices_test.cpp
@@ -22,167 +22,180 @@
 #include <android-base/scopeguard.h>
 #include <gtest/gtest.h>
 
-void add_platform_device(const std::string& path) {
-    uevent uevent = {
-        .action = "add", .subsystem = "platform", .path = path,
-    };
-    handle_platform_device_event(&uevent);
-}
-
-void remove_platform_device(const std::string& path) {
-    uevent uevent = {
-        .action = "remove", .subsystem = "platform", .path = path,
-    };
-    handle_platform_device_event(&uevent);
-}
-
-template <std::vector<std::string> (*Function)(uevent*)>
-void test_get_symlinks(const std::string& platform_device_name, uevent* uevent,
-                       const std::vector<std::string> expected_links) {
-    add_platform_device(platform_device_name);
-    auto platform_device_remover = android::base::make_scope_guard(
-        [&platform_device_name]() { remove_platform_device(platform_device_name); });
-
-    std::vector<std::string> result = Function(uevent);
-
-    auto expected_size = expected_links.size();
-    ASSERT_EQ(expected_size, result.size());
-    if (expected_size == 0) return;
-
-    // Explicitly iterate so the results are visible if a failure occurs
-    for (unsigned int i = 0; i < expected_size; ++i) {
-        EXPECT_EQ(expected_links[i], result[i]);
+class DeviceHandlerTester {
+  public:
+    void AddPlatformDevice(const std::string& path) {
+        Uevent uevent = {
+            .action = "add", .subsystem = "platform", .path = path,
+        };
+        device_handler_.HandlePlatformDeviceEvent(uevent);
     }
-}
 
-TEST(devices, handle_platform_device_event) {
-    platform_devices.clear();
-    add_platform_device("/devices/platform/some_device_name");
-    ASSERT_EQ(1U, platform_devices.size());
-    remove_platform_device("/devices/platform/some_device_name");
-    ASSERT_EQ(0U, platform_devices.size());
-}
+    void RemovePlatformDevice(const std::string& path) {
+        Uevent uevent = {
+            .action = "remove", .subsystem = "platform", .path = path,
+        };
+        device_handler_.HandlePlatformDeviceEvent(uevent);
+    }
 
-TEST(devices, find_platform_device) {
-    platform_devices.clear();
-    add_platform_device("/devices/platform/some_device_name");
-    add_platform_device("/devices/platform/some_device_name/longer");
-    add_platform_device("/devices/platform/other_device_name");
-    EXPECT_EQ(3U, platform_devices.size());
+    void TestGetSymlinks(const std::string& platform_device_name, const Uevent& uevent,
+                         const std::vector<std::string> expected_links, bool block) {
+        AddPlatformDevice(platform_device_name);
+        auto platform_device_remover = android::base::make_scope_guard(
+            [this, &platform_device_name]() { RemovePlatformDevice(platform_device_name); });
+
+        std::vector<std::string> result;
+        if (block) {
+            result = device_handler_.GetBlockDeviceSymlinks(uevent);
+        } else {
+            result = device_handler_.GetCharacterDeviceSymlinks(uevent);
+        }
+
+        auto expected_size = expected_links.size();
+        ASSERT_EQ(expected_size, result.size());
+        if (expected_size == 0) return;
+
+        // Explicitly iterate so the results are visible if a failure occurs
+        for (unsigned int i = 0; i < expected_size; ++i) {
+            EXPECT_EQ(expected_links[i], result[i]);
+        }
+    }
+
+  private:
+    DeviceHandler device_handler_;
+};
+
+TEST(device_handler, PlatformDeviceList) {
+    PlatformDeviceList platform_device_list;
+
+    platform_device_list.Add("/devices/platform/some_device_name");
+    platform_device_list.Add("/devices/platform/some_device_name/longer");
+    platform_device_list.Add("/devices/platform/other_device_name");
+    EXPECT_EQ(3U, platform_device_list.size());
 
     std::string out_path;
-    EXPECT_FALSE(find_platform_device("/devices/platform/not_found", &out_path));
+    EXPECT_FALSE(platform_device_list.Find("/devices/platform/not_found", &out_path));
     EXPECT_EQ("", out_path);
 
-    EXPECT_FALSE(
-        find_platform_device("/devices/platform/some_device_name_with_same_prefix", &out_path));
+    EXPECT_FALSE(platform_device_list.Find("/devices/platform/some_device_name_with_same_prefix",
+                                           &out_path));
 
-    EXPECT_TRUE(
-        find_platform_device("/devices/platform/some_device_name/longer/longer_child", &out_path));
+    EXPECT_TRUE(platform_device_list.Find("/devices/platform/some_device_name/longer/longer_child",
+                                          &out_path));
     EXPECT_EQ("/devices/platform/some_device_name/longer", out_path);
 
-    EXPECT_TRUE(find_platform_device("/devices/platform/some_device_name/other_child", &out_path));
+    EXPECT_TRUE(
+        platform_device_list.Find("/devices/platform/some_device_name/other_child", &out_path));
     EXPECT_EQ("/devices/platform/some_device_name", out_path);
 }
 
-TEST(devices, get_character_device_symlinks_success) {
+TEST(device_handler, get_character_device_symlinks_success) {
     const char* platform_device = "/devices/platform/some_device_name";
-    uevent uevent = {
+    Uevent uevent = {
         .path = "/devices/platform/some_device_name/usb/usb_device/name/tty2-1:1.0",
         .subsystem = "tty",
     };
     std::vector<std::string> expected_result{"/dev/usb/ttyname"};
 
-    test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+    DeviceHandlerTester device_handler_tester_;
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
 }
 
-TEST(devices, get_character_device_symlinks_no_pdev_match) {
+TEST(device_handler, get_character_device_symlinks_no_pdev_match) {
     const char* platform_device = "/devices/platform/some_device_name";
-    uevent uevent = {
+    Uevent uevent = {
         .path = "/device/name/tty2-1:1.0", .subsystem = "tty",
     };
     std::vector<std::string> expected_result;
 
-    test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+    DeviceHandlerTester device_handler_tester_;
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
 }
 
-TEST(devices, get_character_device_symlinks_nothing_after_platform_device) {
+TEST(device_handler, get_character_device_symlinks_nothing_after_platform_device) {
     const char* platform_device = "/devices/platform/some_device_name";
-    uevent uevent = {
+    Uevent uevent = {
         .path = "/devices/platform/some_device_name", .subsystem = "tty",
     };
     std::vector<std::string> expected_result;
 
-    test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+    DeviceHandlerTester device_handler_tester_;
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
 }
 
-TEST(devices, get_character_device_symlinks_no_usb_found) {
+TEST(device_handler, get_character_device_symlinks_no_usb_found) {
     const char* platform_device = "/devices/platform/some_device_name";
-    uevent uevent = {
+    Uevent uevent = {
         .path = "/devices/platform/some_device_name/bad/bad/", .subsystem = "tty",
     };
     std::vector<std::string> expected_result;
 
-    test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+    DeviceHandlerTester device_handler_tester_;
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
 }
 
-TEST(devices, get_character_device_symlinks_no_roothub) {
+TEST(device_handler, get_character_device_symlinks_no_roothub) {
     const char* platform_device = "/devices/platform/some_device_name";
-    uevent uevent = {
+    Uevent uevent = {
         .path = "/devices/platform/some_device_name/usb/", .subsystem = "tty",
     };
     std::vector<std::string> expected_result;
 
-    test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+    DeviceHandlerTester device_handler_tester_;
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
 }
 
-TEST(devices, get_character_device_symlinks_no_usb_device) {
+TEST(device_handler, get_character_device_symlinks_no_usb_device) {
     const char* platform_device = "/devices/platform/some_device_name";
-    uevent uevent = {
+    Uevent uevent = {
         .path = "/devices/platform/some_device_name/usb/usb_device/", .subsystem = "tty",
     };
     std::vector<std::string> expected_result;
 
-    test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+    DeviceHandlerTester device_handler_tester_;
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
 }
 
-TEST(devices, get_character_device_symlinks_no_final_slash) {
+TEST(device_handler, get_character_device_symlinks_no_final_slash) {
     const char* platform_device = "/devices/platform/some_device_name";
-    uevent uevent = {
+    Uevent uevent = {
         .path = "/devices/platform/some_device_name/usb/usb_device/name", .subsystem = "tty",
     };
     std::vector<std::string> expected_result;
 
-    test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+    DeviceHandlerTester device_handler_tester_;
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
 }
 
-TEST(devices, get_character_device_symlinks_no_final_name) {
+TEST(device_handler, get_character_device_symlinks_no_final_name) {
     const char* platform_device = "/devices/platform/some_device_name";
-    uevent uevent = {
+    Uevent uevent = {
         .path = "/devices/platform/some_device_name/usb/usb_device//", .subsystem = "tty",
     };
     std::vector<std::string> expected_result;
 
-    test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+    DeviceHandlerTester device_handler_tester_;
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
 }
 
-TEST(devices, get_block_device_symlinks_success_platform) {
+TEST(device_handler, get_block_device_symlinks_success_platform) {
     // These are actual paths from bullhead
     const char* platform_device = "/devices/soc.0/f9824900.sdhci";
-    uevent uevent = {
+    Uevent uevent = {
         .path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0",
         .partition_name = "",
         .partition_num = -1,
     };
     std::vector<std::string> expected_result{"/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0"};
 
-    test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+    DeviceHandlerTester device_handler_tester_;
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
 }
 
-TEST(devices, get_block_device_symlinks_success_platform_with_partition) {
+TEST(device_handler, get_block_device_symlinks_success_platform_with_partition) {
     // These are actual paths from bullhead
     const char* platform_device = "/devices/soc.0/f9824900.sdhci";
-    uevent uevent = {
+    Uevent uevent = {
         .path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
         .partition_name = "modem",
         .partition_num = 1,
@@ -193,12 +206,13 @@
         "/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1",
     };
 
-    test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+    DeviceHandlerTester device_handler_tester_;
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
 }
 
-TEST(devices, get_block_device_symlinks_success_platform_with_partition_only_num) {
+TEST(device_handler, get_block_device_symlinks_success_platform_with_partition_only_num) {
     const char* platform_device = "/devices/soc.0/f9824900.sdhci";
-    uevent uevent = {
+    Uevent uevent = {
         .path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
         .partition_name = "",
         .partition_num = 1,
@@ -208,12 +222,13 @@
         "/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1",
     };
 
-    test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+    DeviceHandlerTester device_handler_tester_;
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
 }
 
-TEST(devices, get_block_device_symlinks_success_platform_with_partition_only_name) {
+TEST(device_handler, get_block_device_symlinks_success_platform_with_partition_only_name) {
     const char* platform_device = "/devices/soc.0/f9824900.sdhci";
-    uevent uevent = {
+    Uevent uevent = {
         .path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
         .partition_name = "modem",
         .partition_num = -1,
@@ -223,101 +238,107 @@
         "/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1",
     };
 
-    test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+    DeviceHandlerTester device_handler_tester_;
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
 }
 
-TEST(devices, get_block_device_symlinks_success_pci) {
+TEST(device_handler, get_block_device_symlinks_success_pci) {
     const char* platform_device = "/devices/do/not/match";
-    uevent uevent = {
+    Uevent uevent = {
         .path = "/devices/pci0000:00/0000:00:1f.2/mmcblk0", .partition_name = "", .partition_num = -1,
     };
     std::vector<std::string> expected_result{"/dev/block/pci/pci0000:00/0000:00:1f.2/mmcblk0"};
 
-    test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+    DeviceHandlerTester device_handler_tester_;
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
 }
 
-TEST(devices, get_block_device_symlinks_pci_bad_format) {
+TEST(device_handler, get_block_device_symlinks_pci_bad_format) {
     const char* platform_device = "/devices/do/not/match";
-    uevent uevent = {
+    Uevent uevent = {
         .path = "/devices/pci//mmcblk0", .partition_name = "", .partition_num = -1,
     };
     std::vector<std::string> expected_result{};
 
-    test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+    DeviceHandlerTester device_handler_tester_;
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
 }
 
-TEST(devices, get_block_device_symlinks_success_vbd) {
+TEST(device_handler, get_block_device_symlinks_success_vbd) {
     const char* platform_device = "/devices/do/not/match";
-    uevent uevent = {
+    Uevent uevent = {
         .path = "/devices/vbd-1234/mmcblk0", .partition_name = "", .partition_num = -1,
     };
     std::vector<std::string> expected_result{"/dev/block/vbd/1234/mmcblk0"};
 
-    test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+    DeviceHandlerTester device_handler_tester_;
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
 }
 
-TEST(devices, get_block_device_symlinks_vbd_bad_format) {
+TEST(device_handler, get_block_device_symlinks_vbd_bad_format) {
     const char* platform_device = "/devices/do/not/match";
-    uevent uevent = {
+    Uevent uevent = {
         .path = "/devices/vbd-/mmcblk0", .partition_name = "", .partition_num = -1,
     };
     std::vector<std::string> expected_result{};
 
-    test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+    DeviceHandlerTester device_handler_tester_;
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
 }
 
-TEST(devices, get_block_device_symlinks_no_matches) {
+TEST(device_handler, get_block_device_symlinks_no_matches) {
     const char* platform_device = "/devices/soc.0/f9824900.sdhci";
-    uevent uevent = {
+    Uevent uevent = {
         .path = "/devices/soc.0/not_the_device/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
         .partition_name = "",
         .partition_num = -1,
     };
     std::vector<std::string> expected_result;
 
-    test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+    DeviceHandlerTester device_handler_tester_;
+    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
 }
 
-TEST(devices, sanitize_null) {
-    sanitize_partition_name(nullptr);
+TEST(device_handler, sanitize_null) {
+    SanitizePartitionName(nullptr);
 }
 
-TEST(devices, sanitize_empty) {
+TEST(device_handler, sanitize_empty) {
     std::string empty;
-    sanitize_partition_name(&empty);
+    SanitizePartitionName(&empty);
     EXPECT_EQ(0u, empty.size());
 }
 
-TEST(devices, sanitize_allgood) {
+TEST(device_handler, sanitize_allgood) {
     std::string good =
         "abcdefghijklmnopqrstuvwxyz"
         "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
         "0123456789"
         "_-.";
     std::string good_copy = good;
-    sanitize_partition_name(&good);
+    SanitizePartitionName(&good);
     EXPECT_EQ(good_copy, good);
 }
 
-TEST(devices, sanitize_somebad) {
+TEST(device_handler, sanitize_somebad) {
     std::string string = "abc!@#$%^&*()";
-    sanitize_partition_name(&string);
+    SanitizePartitionName(&string);
     EXPECT_EQ("abc__________", string);
 }
 
-TEST(devices, sanitize_allbad) {
+TEST(device_handler, sanitize_allbad) {
     std::string string = "!@#$%^&*()";
-    sanitize_partition_name(&string);
+    SanitizePartitionName(&string);
     EXPECT_EQ("__________", string);
 }
 
-TEST(devices, sanitize_onebad) {
+TEST(device_handler, sanitize_onebad) {
     std::string string = ")";
-    sanitize_partition_name(&string);
+    SanitizePartitionName(&string);
     EXPECT_EQ("_", string);
 }
 
-TEST(devices, DevPermissionsMatchNormal) {
+TEST(device_handler, DevPermissionsMatchNormal) {
     // Basic from ueventd.rc
     // /dev/null                 0666   root       root
     Permissions permissions("/dev/null", 0666, 0, 0);
@@ -329,7 +350,7 @@
     EXPECT_EQ(0U, permissions.gid());
 }
 
-TEST(devices, DevPermissionsMatchPrefix) {
+TEST(device_handler, DevPermissionsMatchPrefix) {
     // Prefix from ueventd.rc
     // /dev/dri/*                0666   root       graphics
     Permissions permissions("/dev/dri/*", 0666, 0, 1000);
@@ -342,7 +363,7 @@
     EXPECT_EQ(1000U, permissions.gid());
 }
 
-TEST(devices, DevPermissionsMatchWildcard) {
+TEST(device_handler, DevPermissionsMatchWildcard) {
     // Wildcard example
     // /dev/device*name                0666   root       graphics
     Permissions permissions("/dev/device*name", 0666, 0, 1000);
@@ -356,7 +377,7 @@
     EXPECT_EQ(1000U, permissions.gid());
 }
 
-TEST(devices, DevPermissionsMatchWildcardPrefix) {
+TEST(device_handler, DevPermissionsMatchWildcardPrefix) {
     // Wildcard+Prefix example
     // /dev/device*name*                0666   root       graphics
     Permissions permissions("/dev/device*name*", 0666, 0, 1000);
@@ -372,7 +393,7 @@
     EXPECT_EQ(1000U, permissions.gid());
 }
 
-TEST(devices, SysfsPermissionsMatchWithSubsystemNormal) {
+TEST(device_handler, SysfsPermissionsMatchWithSubsystemNormal) {
     // /sys/devices/virtual/input/input*   enable      0660  root   input
     SysfsPermissions permissions("/sys/devices/virtual/input/input*", "enable", 0660, 0, 1001);
     EXPECT_TRUE(permissions.MatchWithSubsystem("/sys/devices/virtual/input/input0", "input"));
@@ -382,7 +403,7 @@
     EXPECT_EQ(1001U, permissions.gid());
 }
 
-TEST(devices, SysfsPermissionsMatchWithSubsystemClass) {
+TEST(device_handler, SysfsPermissionsMatchWithSubsystemClass) {
     // /sys/class/input/event*   enable      0660  root   input
     SysfsPermissions permissions("/sys/class/input/event*", "enable", 0660, 0, 1001);
     EXPECT_TRUE(permissions.MatchWithSubsystem(
@@ -396,7 +417,7 @@
     EXPECT_EQ(1001U, permissions.gid());
 }
 
-TEST(devices, SysfsPermissionsMatchWithSubsystemBus) {
+TEST(device_handler, SysfsPermissionsMatchWithSubsystemBus) {
     // /sys/bus/i2c/devices/i2c-*   enable      0660  root   input
     SysfsPermissions permissions("/sys/bus/i2c/devices/i2c-*", "enable", 0660, 0, 1001);
     EXPECT_TRUE(permissions.MatchWithSubsystem("/sys/devices/soc.0/f9967000.i2c/i2c-5", "i2c"));
diff --git a/init/firmware_handler.cpp b/init/firmware_handler.cpp
new file mode 100644
index 0000000..1471aeb
--- /dev/null
+++ b/init/firmware_handler.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2017 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 "firmware_handler.h"
+
+#include <fcntl.h>
+#include <sys/sendfile.h>
+#include <unistd.h>
+
+#include <string>
+#include <thread>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+
+#include "util.h"
+
+static void LoadFirmware(const Uevent& uevent, const std::string& root, int fw_fd, size_t fw_size,
+                         int loading_fd, int data_fd) {
+    // Start transfer.
+    android::base::WriteFully(loading_fd, "1", 1);
+
+    // Copy the firmware.
+    int rc = sendfile(data_fd, fw_fd, nullptr, fw_size);
+    if (rc == -1) {
+        PLOG(ERROR) << "firmware: sendfile failed { '" << root << "', '" << uevent.firmware
+                    << "' }";
+    }
+
+    // Tell the firmware whether to abort or commit.
+    const char* response = (rc != -1) ? "0" : "-1";
+    android::base::WriteFully(loading_fd, response, strlen(response));
+}
+
+static bool IsBooting() {
+    return access("/dev/.booting", F_OK) == 0;
+}
+
+static void ProcessFirmwareEvent(const Uevent& uevent) {
+    int booting = IsBooting();
+
+    LOG(INFO) << "firmware: loading '" << uevent.firmware << "' for '" << uevent.path << "'";
+
+    std::string root = "/sys" + uevent.path;
+    std::string loading = root + "/loading";
+    std::string data = root + "/data";
+
+    android::base::unique_fd loading_fd(open(loading.c_str(), O_WRONLY | O_CLOEXEC));
+    if (loading_fd == -1) {
+        PLOG(ERROR) << "couldn't open firmware loading fd for " << uevent.firmware;
+        return;
+    }
+
+    android::base::unique_fd data_fd(open(data.c_str(), O_WRONLY | O_CLOEXEC));
+    if (data_fd == -1) {
+        PLOG(ERROR) << "couldn't open firmware data fd for " << uevent.firmware;
+        return;
+    }
+
+    static const char* firmware_dirs[] = {"/etc/firmware/", "/vendor/firmware/",
+                                          "/firmware/image/"};
+
+try_loading_again:
+    for (size_t i = 0; i < arraysize(firmware_dirs); i++) {
+        std::string file = firmware_dirs[i] + uevent.firmware;
+        android::base::unique_fd fw_fd(open(file.c_str(), O_RDONLY | O_CLOEXEC));
+        struct stat sb;
+        if (fw_fd != -1 && fstat(fw_fd, &sb) != -1) {
+            LoadFirmware(uevent, root, fw_fd, sb.st_size, loading_fd, data_fd);
+            return;
+        }
+    }
+
+    if (booting) {
+        // If we're not fully booted, we may be missing
+        // filesystems needed for firmware, wait and retry.
+        std::this_thread::sleep_for(100ms);
+        booting = IsBooting();
+        goto try_loading_again;
+    }
+
+    LOG(ERROR) << "firmware: could not find firmware for " << uevent.firmware;
+
+    // Write "-1" as our response to the kernel's firmware request, since we have nothing for it.
+    write(loading_fd, "-1", 2);
+}
+
+void HandleFirmwareEvent(const Uevent& uevent) {
+    if (uevent.subsystem != "firmware" || uevent.action != "add") return;
+
+    // Loading the firmware in a child means we can do that in parallel...
+    // (We ignore SIGCHLD rather than wait for our children.)
+    pid_t pid = fork();
+    if (pid == 0) {
+        Timer t;
+        ProcessFirmwareEvent(uevent);
+        LOG(INFO) << "loading " << uevent.path << " took " << t;
+        _exit(EXIT_SUCCESS);
+    } else if (pid == -1) {
+        PLOG(ERROR) << "could not fork to process firmware event for " << uevent.firmware;
+    }
+}
diff --git a/init/firmware_handler.h b/init/firmware_handler.h
new file mode 100644
index 0000000..be9daae
--- /dev/null
+++ b/init/firmware_handler.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INIT_FIRMWARE_HANDLER_H
+#define _INIT_FIRMWARE_HANDLER_H
+
+#include "uevent.h"
+
+void HandleFirmwareEvent(const Uevent& uevent);
+
+#endif
diff --git a/init/init.cpp b/init/init.cpp
index 8ee438f..ee87145 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -58,7 +58,6 @@
 
 #include "action.h"
 #include "bootchart.h"
-#include "devices.h"
 #include "import_parser.h"
 #include "init_first_stage.h"
 #include "init_parser.h"
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
index 9d2a0d1..8a7d9a2 100644
--- a/init/init_first_stage.cpp
+++ b/init/init_first_stage.cpp
@@ -31,6 +31,8 @@
 #include "devices.h"
 #include "fs_mgr.h"
 #include "fs_mgr_avb.h"
+#include "uevent.h"
+#include "uevent_listener.h"
 #include "util.h"
 
 // Class Declarations
@@ -51,7 +53,7 @@
     void InitVerityDevice(const std::string& verity_device);
     bool MountPartitions();
 
-    virtual coldboot_action_t ColdbootCallback(uevent* uevent);
+    virtual RegenerationAction UeventCallback(const Uevent& uevent);
 
     // Pure virtual functions.
     virtual bool GetRequiredDevices() = 0;
@@ -63,6 +65,8 @@
     // Eligible first stage mount candidates, only allow /system, /vendor and/or /odm.
     std::vector<fstab_rec*> mount_fstab_recs_;
     std::set<std::string> required_devices_partition_names_;
+    DeviceHandler device_handler_;
+    UeventListener uevent_listener_;
 };
 
 class FirstStageMountVBootV1 : public FirstStageMount {
@@ -83,7 +87,7 @@
     ~FirstStageMountVBootV2() override = default;
 
   protected:
-    coldboot_action_t ColdbootCallback(uevent* uevent) override;
+    RegenerationAction UeventCallback(const Uevent& uevent) override;
     bool GetRequiredDevices() override;
     bool SetUpDmVerity(fstab_rec* fstab_rec) override;
     bool InitAvbHandle();
@@ -165,46 +169,50 @@
 
     if (need_dm_verity_) {
         const std::string dm_path = "/devices/virtual/misc/device-mapper";
-        device_init(("/sys" + dm_path).c_str(), [&dm_path](uevent* uevent) -> coldboot_action_t {
-            if (uevent->path == dm_path) return COLDBOOT_STOP;
-            return COLDBOOT_CONTINUE;  // dm_path not found, continue to find it.
-        });
+        uevent_listener_.RegenerateUeventsForPath("/sys" + dm_path,
+                                                  [this, &dm_path](const Uevent& uevent) {
+                                                      if (uevent.path == dm_path) {
+                                                          device_handler_.HandleDeviceEvent(uevent);
+                                                          return RegenerationAction::kStop;
+                                                      }
+                                                      return RegenerationAction::kContinue;
+                                                  });
     }
 
-    device_init(nullptr,
-                [this](uevent* uevent) -> coldboot_action_t { return ColdbootCallback(uevent); });
-
-    device_close();
+    uevent_listener_.RegenerateUevents(
+        [this](const Uevent& uevent) { return UeventCallback(uevent); });
 }
 
-coldboot_action_t FirstStageMount::ColdbootCallback(uevent* uevent) {
+RegenerationAction FirstStageMount::UeventCallback(const Uevent& uevent) {
     // We need platform devices to create symlinks.
-    if (uevent->subsystem == "platform") {
-        return COLDBOOT_CREATE;
+    if (uevent.subsystem == "platform") {
+        device_handler_.HandleDeviceEvent(uevent);
+        return RegenerationAction::kContinue;
     }
 
     // Ignores everything that is not a block device.
-    if (uevent->subsystem != "block") {
-        return COLDBOOT_CONTINUE;
+    if (uevent.subsystem != "block") {
+        return RegenerationAction::kContinue;
     }
 
-    if (!uevent->partition_name.empty()) {
+    if (!uevent.partition_name.empty()) {
         // Matches partition name to create device nodes.
         // Both required_devices_partition_names_ and uevent->partition_name have A/B
         // suffix when A/B is used.
-        auto iter = required_devices_partition_names_.find(uevent->partition_name);
+        auto iter = required_devices_partition_names_.find(uevent.partition_name);
         if (iter != required_devices_partition_names_.end()) {
             LOG(VERBOSE) << __FUNCTION__ << "(): found partition: " << *iter;
             required_devices_partition_names_.erase(iter);
+            device_handler_.HandleDeviceEvent(uevent);
             if (required_devices_partition_names_.empty()) {
-                return COLDBOOT_STOP;  // Found all partitions, stop coldboot.
+                return RegenerationAction::kStop;
             } else {
-                return COLDBOOT_CREATE;  // Creates this device and continue to find others.
+                return RegenerationAction::kContinue;
             }
         }
     }
     // Not found a partition or find an unneeded partition, continue to find others.
-    return COLDBOOT_CONTINUE;
+    return RegenerationAction::kContinue;
 }
 
 // Creates "/dev/block/dm-XX" for dm-verity by running coldboot on /sys/block/dm-XX.
@@ -212,14 +220,15 @@
     const std::string device_name(basename(verity_device.c_str()));
     const std::string syspath = "/sys/block/" + device_name;
 
-    device_init(syspath.c_str(), [&](uevent* uevent) -> coldboot_action_t {
-        if (uevent->device_name == device_name) {
-            LOG(VERBOSE) << "Creating dm-verity device : " << verity_device;
-            return COLDBOOT_STOP;
-        }
-        return COLDBOOT_CONTINUE;
-    });
-    device_close();
+    uevent_listener_.RegenerateUeventsForPath(
+        syspath, [&device_name, &verity_device, this](const Uevent& uevent) {
+            if (uevent.device_name == device_name) {
+                LOG(VERBOSE) << "Creating dm-verity device : " << verity_device;
+                device_handler_.HandleDeviceEvent(uevent);
+                return RegenerationAction::kStop;
+            }
+            return RegenerationAction::kContinue;
+        });
 }
 
 bool FirstStageMount::MountPartitions() {
@@ -345,33 +354,32 @@
     return true;
 }
 
-coldboot_action_t FirstStageMountVBootV2::ColdbootCallback(uevent* uevent) {
-    // Invokes the parent function to see if any desired partition has been found.
-    // If yes, record the by-name symlink for creating FsManagerAvbHandle later.
-    coldboot_action_t parent_callback_ret = FirstStageMount::ColdbootCallback(uevent);
-
-    // Skips the uevent if the parent function returns COLDBOOT_CONTINUE (meaning
-    // that the uevent was skipped) or there is no uevent->partition_name to
-    // create the by-name symlink.
-    if (parent_callback_ret != COLDBOOT_CONTINUE && !uevent->partition_name.empty()) {
-        // get_block_device_symlinks() will return three symlinks at most, depending on
+RegenerationAction FirstStageMountVBootV2::UeventCallback(const Uevent& uevent) {
+    // Check if this uevent corresponds to one of the required partitions and store its symlinks if
+    // so, in order to create FsManagerAvbHandle later.
+    // Note that the parent callback removes partitions from the list of required partitions
+    // as it finds them, so this must happen first.
+    if (!uevent.partition_name.empty() &&
+        required_devices_partition_names_.find(uevent.partition_name) !=
+            required_devices_partition_names_.end()) {
+        // GetBlockDeviceSymlinks() will return three symlinks at most, depending on
         // the content of uevent. by-name symlink will be at [0] if uevent->partition_name
         // is not empty. e.g.,
         //   - /dev/block/platform/soc.0/f9824900.sdhci/by-name/modem
         //   - /dev/block/platform/soc.0/f9824900.sdhci/by-num/p1
         //   - /dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1
-        std::vector<std::string> links = get_block_device_symlinks(uevent);
+        std::vector<std::string> links = device_handler_.GetBlockDeviceSymlinks(uevent);
         if (!links.empty()) {
-            auto[it, inserted] = by_name_symlink_map_.emplace(uevent->partition_name, links[0]);
+            auto[it, inserted] = by_name_symlink_map_.emplace(uevent.partition_name, links[0]);
             if (!inserted) {
-                LOG(ERROR) << "Partition '" << uevent->partition_name
+                LOG(ERROR) << "Partition '" << uevent.partition_name
                            << "' already existed in the by-name symlink map with a value of '"
                            << it->second << "', new value '" << links[0] << "' will be ignored.";
             }
         }
     }
 
-    return parent_callback_ret;
+    return FirstStageMount::UeventCallback(uevent);
 }
 
 bool FirstStageMountVBootV2::SetUpDmVerity(fstab_rec* fstab_rec) {
diff --git a/init/init_parser.h b/init/init_parser.h
index bd8a178..722ebb2 100644
--- a/init/init_parser.h
+++ b/init/init_parser.h
@@ -66,9 +66,9 @@
     //  be written.
     using LineCallback = std::function<bool(std::vector<std::string>&&, std::string*)>;
 
+    // TODO: init is the only user of this as a singleton; remove it.
     static Parser& GetInstance();
 
-    // Exposed for testing
     Parser();
 
     bool ParseConfig(const std::string& path);
diff --git a/init/test_service/Android.bp b/init/test_service/Android.bp
new file mode 100644
index 0000000..9bd6f27
--- /dev/null
+++ b/init/test_service/Android.bp
@@ -0,0 +1,22 @@
+//
+// Copyright (C) 2017 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.
+//
+
+cc_binary {
+    name: "test_service",
+    srcs: ["test_service.cpp"],
+    shared_libs: ["libbase"],
+    init_rc: ["test_service.rc"],
+}
diff --git a/init/test_service/Android.mk b/init/test_service/Android.mk
deleted file mode 100644
index 30c9e9d..0000000
--- a/init/test_service/Android.mk
+++ /dev/null
@@ -1,27 +0,0 @@
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http:#www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-
-# Sample service for testing.
-# =========================================================
-include $(CLEAR_VARS)
-LOCAL_MODULE := test_service
-LOCAL_SRC_FILES := test_service.cpp
-
-LOCAL_SHARED_LIBRARIES += libbase
-
-LOCAL_INIT_RC := test_service.rc
-
-include $(BUILD_EXECUTABLE)
diff --git a/init/uevent.h b/init/uevent.h
new file mode 100644
index 0000000..1095665
--- /dev/null
+++ b/init/uevent.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INIT_UEVENT_H
+#define _INIT_UEVENT_H
+
+#include <string>
+
+struct Uevent {
+    std::string action;
+    std::string path;
+    std::string subsystem;
+    std::string firmware;
+    std::string partition_name;
+    std::string device_name;
+    int partition_num;
+    int major;
+    int minor;
+};
+
+#endif
diff --git a/init/uevent_listener.cpp b/init/uevent_listener.cpp
new file mode 100644
index 0000000..27c5d23
--- /dev/null
+++ b/init/uevent_listener.cpp
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2017 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 "uevent_listener.h"
+
+#include <fcntl.h>
+#include <poll.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <memory>
+
+#include <android-base/logging.h>
+#include <cutils/uevent.h>
+
+static void ParseEvent(const char* msg, Uevent* uevent) {
+    uevent->partition_num = -1;
+    uevent->major = -1;
+    uevent->minor = -1;
+    uevent->action.clear();
+    uevent->path.clear();
+    uevent->subsystem.clear();
+    uevent->firmware.clear();
+    uevent->partition_name.clear();
+    uevent->device_name.clear();
+    // currently ignoring SEQNUM
+    while (*msg) {
+        if (!strncmp(msg, "ACTION=", 7)) {
+            msg += 7;
+            uevent->action = msg;
+        } else if (!strncmp(msg, "DEVPATH=", 8)) {
+            msg += 8;
+            uevent->path = msg;
+        } else if (!strncmp(msg, "SUBSYSTEM=", 10)) {
+            msg += 10;
+            uevent->subsystem = msg;
+        } else if (!strncmp(msg, "FIRMWARE=", 9)) {
+            msg += 9;
+            uevent->firmware = msg;
+        } else if (!strncmp(msg, "MAJOR=", 6)) {
+            msg += 6;
+            uevent->major = atoi(msg);
+        } else if (!strncmp(msg, "MINOR=", 6)) {
+            msg += 6;
+            uevent->minor = atoi(msg);
+        } else if (!strncmp(msg, "PARTN=", 6)) {
+            msg += 6;
+            uevent->partition_num = atoi(msg);
+        } else if (!strncmp(msg, "PARTNAME=", 9)) {
+            msg += 9;
+            uevent->partition_name = msg;
+        } else if (!strncmp(msg, "DEVNAME=", 8)) {
+            msg += 8;
+            uevent->device_name = msg;
+        }
+
+        // advance to after the next \0
+        while (*msg++)
+            ;
+    }
+
+    if (LOG_UEVENTS) {
+        LOG(INFO) << "event { '" << uevent->action << "', '" << uevent->path << "', '"
+                  << uevent->subsystem << "', '" << uevent->firmware << "', " << uevent->major
+                  << ", " << uevent->minor << " }";
+    }
+}
+
+UeventListener::UeventListener() {
+    // is 256K enough? udev uses 16MB!
+    device_fd_.reset(uevent_open_socket(256 * 1024, true));
+    if (device_fd_ == -1) {
+        LOG(FATAL) << "Could not open uevent socket";
+    }
+
+    fcntl(device_fd_, F_SETFL, O_NONBLOCK);
+}
+
+bool UeventListener::ReadUevent(Uevent* uevent) const {
+    char msg[UEVENT_MSG_LEN + 2];
+    int n = uevent_kernel_multicast_recv(device_fd_, msg, UEVENT_MSG_LEN);
+    if (n <= 0) {
+        if (errno != EAGAIN && errno != EWOULDBLOCK) {
+            LOG(ERROR) << "Error reading from Uevent Fd";
+        }
+        return false;
+    }
+    if (n >= UEVENT_MSG_LEN) {
+        LOG(ERROR) << "Uevent overflowed buffer, discarding";
+        // Return true here even if we discard as we may have more uevents pending and we
+        // want to keep processing them.
+        return true;
+    }
+
+    msg[n] = '\0';
+    msg[n + 1] = '\0';
+
+    ParseEvent(msg, uevent);
+
+    return true;
+}
+
+// RegenerateUevents*() walks parts of the /sys tree and pokes the uevent files to cause the kernel
+// to regenerate device add uevents that have already happened.  This is particularly useful when
+// starting ueventd, to regenerate all of the uevents that it had previously missed.
+//
+// We drain any pending events from the netlink socket every time we poke another uevent file to
+// make sure we don't overrun the socket's buffer.
+//
+
+RegenerationAction UeventListener::RegenerateUeventsForDir(DIR* d,
+                                                           RegenerateCallback callback) const {
+    int dfd = dirfd(d);
+
+    int fd = openat(dfd, "uevent", O_WRONLY);
+    if (fd >= 0) {
+        write(fd, "add\n", 4);
+        close(fd);
+
+        Uevent uevent;
+        while (ReadUevent(&uevent)) {
+            if (callback(uevent) == RegenerationAction::kStop) return RegenerationAction::kStop;
+        }
+    }
+
+    dirent* de;
+    while ((de = readdir(d)) != nullptr) {
+        if (de->d_type != DT_DIR || de->d_name[0] == '.') continue;
+
+        fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
+        if (fd < 0) continue;
+
+        std::unique_ptr<DIR, decltype(&closedir)> d2(fdopendir(fd), closedir);
+        if (d2 == 0) {
+            close(fd);
+        } else {
+            if (RegenerateUeventsForDir(d2.get(), callback) == RegenerationAction::kStop) {
+                return RegenerationAction::kStop;
+            }
+        }
+    }
+
+    // default is always to continue looking for uevents
+    return RegenerationAction::kContinue;
+}
+
+RegenerationAction UeventListener::RegenerateUeventsForPath(const std::string& path,
+                                                            RegenerateCallback callback) const {
+    std::unique_ptr<DIR, decltype(&closedir)> d(opendir(path.c_str()), closedir);
+    if (!d) return RegenerationAction::kContinue;
+
+    return RegenerateUeventsForDir(d.get(), callback);
+}
+
+static const char* kRegenerationPaths[] = {"/sys/class", "/sys/block", "/sys/devices"};
+
+void UeventListener::RegenerateUevents(RegenerateCallback callback) const {
+    for (const auto path : kRegenerationPaths) {
+        if (RegenerateUeventsForPath(path, callback) == RegenerationAction::kStop) return;
+    }
+}
+
+void UeventListener::DoPolling(PollCallback callback) const {
+    pollfd ufd;
+    ufd.events = POLLIN;
+    ufd.fd = device_fd_;
+
+    while (true) {
+        ufd.revents = 0;
+        int nr = poll(&ufd, 1, -1);
+        if (nr <= 0) {
+            continue;
+        }
+        if (ufd.revents & POLLIN) {
+            // We're non-blocking, so if we receive a poll event keep processing until there
+            // we have exhausted all uevent messages.
+            Uevent uevent;
+            while (ReadUevent(&uevent)) {
+                callback(uevent);
+            }
+        }
+    }
+}
diff --git a/init/uevent_listener.h b/init/uevent_listener.h
new file mode 100644
index 0000000..ba31aaa
--- /dev/null
+++ b/init/uevent_listener.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INIT_UEVENT_LISTENER_H
+#define _INIT_UEVENT_LISTENER_H
+
+#include <dirent.h>
+
+#include <functional>
+
+#include <android-base/unique_fd.h>
+
+#include "uevent.h"
+
+#define UEVENT_MSG_LEN 2048
+
+enum class RegenerationAction {
+    kStop = 0,  // Stop regenerating uevents as we've handled the one(s) we're interested in.
+    kContinue,  // Continue regenerating uevents as we haven't seen the one(s) we're interested in.
+};
+
+using RegenerateCallback = std::function<RegenerationAction(const Uevent&)>;
+using PollCallback = std::function<void(const Uevent&)>;
+
+class UeventListener {
+  public:
+    UeventListener();
+
+    void RegenerateUevents(RegenerateCallback callback) const;
+    RegenerationAction RegenerateUeventsForPath(const std::string& path,
+                                                RegenerateCallback callback) const;
+    void DoPolling(PollCallback callback) const;
+
+  private:
+    bool ReadUevent(Uevent* uevent) const;
+    RegenerationAction RegenerateUeventsForDir(DIR* d, RegenerateCallback callback) const;
+
+    android::base::unique_fd device_fd_;
+};
+
+#endif
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 8c0c574..bd21a3e 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -18,7 +18,6 @@
 
 #include <ctype.h>
 #include <fcntl.h>
-#include <poll.h>
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -30,9 +29,44 @@
 #include <selinux/selinux.h>
 
 #include "devices.h"
+#include "firmware_handler.h"
 #include "log.h"
+#include "uevent_listener.h"
+#include "ueventd_parser.h"
 #include "util.h"
 
+DeviceHandler CreateDeviceHandler() {
+    Parser parser;
+
+    std::vector<Subsystem> subsystems;
+    parser.AddSectionParser("subsystem", std::make_unique<SubsystemParser>(&subsystems));
+
+    using namespace std::placeholders;
+    std::vector<SysfsPermissions> sysfs_permissions;
+    std::vector<Permissions> dev_permissions;
+    parser.AddSingleLineParser(
+        "/sys/", std::bind(ParsePermissionsLine, _1, _2, &sysfs_permissions, nullptr));
+    parser.AddSingleLineParser("/dev/",
+                               std::bind(ParsePermissionsLine, _1, _2, nullptr, &dev_permissions));
+
+    parser.ParseConfig("/ueventd.rc");
+    parser.ParseConfig("/vendor/ueventd.rc");
+    parser.ParseConfig("/odm/ueventd.rc");
+
+    /*
+     * keep the current product name base configuration so
+     * we remain backwards compatible and allow it to override
+     * everything
+     * TODO: cleanup platform ueventd.rc to remove vendor specific
+     * device node entries (b/34968103)
+     */
+    std::string hardware = android::base::GetProperty("ro.hardware", "");
+    parser.ParseConfig("/ueventd." + hardware + ".rc");
+
+    return DeviceHandler(std::move(dev_permissions), std::move(sysfs_permissions),
+                         std::move(subsystems));
+}
+
 int ueventd_main(int argc, char **argv)
 {
     /*
@@ -57,41 +91,26 @@
     cb.func_log = selinux_klog_callback;
     selinux_set_callback(SELINUX_CB_LOG, cb);
 
-    Parser& parser = Parser::GetInstance();
-    parser.AddSectionParser("subsystem", std::make_unique<SubsystemParser>());
-    using namespace std::placeholders;
-    parser.AddSingleLineParser("/sys/", std::bind(ParsePermissionsLine, _1, _2, true));
-    parser.AddSingleLineParser("/dev/", std::bind(ParsePermissionsLine, _1, _2, false));
-    parser.ParseConfig("/ueventd.rc");
-    parser.ParseConfig("/vendor/ueventd.rc");
-    parser.ParseConfig("/odm/ueventd.rc");
+    DeviceHandler device_handler = CreateDeviceHandler();
+    UeventListener uevent_listener;
 
-    /*
-     * keep the current product name base configuration so
-     * we remain backwards compatible and allow it to override
-     * everything
-     * TODO: cleanup platform ueventd.rc to remove vendor specific
-     * device node entries (b/34968103)
-     */
-    std::string hardware = android::base::GetProperty("ro.hardware", "");
-    parser.ParseConfig("/ueventd." + hardware + ".rc");
+    if (access(COLDBOOT_DONE, F_OK) != 0) {
+        Timer t;
 
-    device_init();
+        uevent_listener.RegenerateUevents([&device_handler](const Uevent& uevent) {
+            HandleFirmwareEvent(uevent);
+            device_handler.HandleDeviceEvent(uevent);
+            return RegenerationAction::kContinue;
+        });
 
-    pollfd ufd;
-    ufd.events = POLLIN;
-    ufd.fd = get_device_fd();
-
-    while (true) {
-        ufd.revents = 0;
-        int nr = poll(&ufd, 1, -1);
-        if (nr <= 0) {
-            continue;
-        }
-        if (ufd.revents & POLLIN) {
-            handle_device_fd();
-        }
+        close(open(COLDBOOT_DONE, O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
+        LOG(INFO) << "Coldboot took " << t;
     }
 
+    uevent_listener.DoPolling([&device_handler](const Uevent& uevent) {
+        HandleFirmwareEvent(uevent);
+        device_handler.HandleDeviceEvent(uevent);
+    });
+
     return 0;
 }
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
new file mode 100644
index 0000000..7156e76
--- /dev/null
+++ b/init/ueventd_parser.cpp
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2017 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 "ueventd_parser.h"
+
+#include <grp.h>
+#include <pwd.h>
+
+#include "keyword_map.h"
+
+bool ParsePermissionsLine(std::vector<std::string>&& args, std::string* err,
+                          std::vector<SysfsPermissions>* out_sysfs_permissions,
+                          std::vector<Permissions>* out_dev_permissions) {
+    bool is_sysfs = out_sysfs_permissions != nullptr;
+    if (is_sysfs && args.size() != 5) {
+        *err = "/sys/ lines must have 5 entries";
+        return false;
+    }
+
+    if (!is_sysfs && args.size() != 4) {
+        *err = "/dev/ lines must have 4 entries";
+        return false;
+    }
+
+    auto it = args.begin();
+    const std::string& name = *it++;
+
+    std::string sysfs_attribute;
+    if (is_sysfs) sysfs_attribute = *it++;
+
+    // args is now common to both sys and dev entries and contains: <perm> <uid> <gid>
+    std::string& perm_string = *it++;
+    char* end_pointer = 0;
+    mode_t perm = strtol(perm_string.c_str(), &end_pointer, 8);
+    if (end_pointer == nullptr || *end_pointer != '\0') {
+        *err = "invalid mode '" + perm_string + "'";
+        return false;
+    }
+
+    std::string& uid_string = *it++;
+    passwd* pwd = getpwnam(uid_string.c_str());
+    if (!pwd) {
+        *err = "invalid uid '" + uid_string + "'";
+        return false;
+    }
+    uid_t uid = pwd->pw_uid;
+
+    std::string& gid_string = *it++;
+    struct group* grp = getgrnam(gid_string.c_str());
+    if (!grp) {
+        *err = "invalid gid '" + gid_string + "'";
+        return false;
+    }
+    gid_t gid = grp->gr_gid;
+
+    if (is_sysfs) {
+        out_sysfs_permissions->emplace_back(name, sysfs_attribute, perm, uid, gid);
+    } else {
+        out_dev_permissions->emplace_back(name, perm, uid, gid);
+    }
+    return true;
+}
+
+bool SubsystemParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
+                                   int line, std::string* err) {
+    if (args.size() != 2) {
+        *err = "subsystems must have exactly one name";
+        return false;
+    }
+
+    if (std::find(subsystems_->begin(), subsystems_->end(), args[1]) != subsystems_->end()) {
+        *err = "ignoring duplicate subsystem entry";
+        return false;
+    }
+
+    subsystem_.name_ = args[1];
+
+    return true;
+}
+
+bool SubsystemParser::ParseDevName(std::vector<std::string>&& args, std::string* err) {
+    if (args[1] == "uevent_devname") {
+        subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVNAME;
+        return true;
+    }
+    if (args[1] == "uevent_devpath") {
+        subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVPATH;
+        return true;
+    }
+
+    *err = "invalid devname '" + args[1] + "'";
+    return false;
+}
+
+bool SubsystemParser::ParseDirName(std::vector<std::string>&& args, std::string* err) {
+    if (args[1].front() != '/') {
+        *err = "dirname '" + args[1] + " ' does not start with '/'";
+        return false;
+    }
+
+    subsystem_.dir_name_ = args[1];
+    return true;
+}
+
+bool SubsystemParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) {
+    using OptionParser =
+        bool (SubsystemParser::*)(std::vector<std::string> && args, std::string * err);
+    static class OptionParserMap : public KeywordMap<OptionParser> {
+      private:
+        const Map& map() const override {
+            // clang-format off
+            static const Map option_parsers = {
+                {"devname",     {1,     1,      &SubsystemParser::ParseDevName}},
+                {"dirname",     {1,     1,      &SubsystemParser::ParseDirName}},
+            };
+            // clang-format on
+            return option_parsers;
+        }
+    } parser_map;
+
+    auto parser = parser_map.FindFunction(args, err);
+
+    if (!parser) {
+        return false;
+    }
+
+    return (this->*parser)(std::move(args), err);
+}
+
+void SubsystemParser::EndSection() {
+    subsystems_->emplace_back(std::move(subsystem_));
+}
diff --git a/init/ueventd_parser.h b/init/ueventd_parser.h
new file mode 100644
index 0000000..c1ce976
--- /dev/null
+++ b/init/ueventd_parser.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INIT_UEVENTD_PARSER_H
+#define _INIT_UEVENTD_PARSER_H
+
+#include <string>
+#include <vector>
+
+#include "devices.h"
+#include "init_parser.h"
+
+class SubsystemParser : public SectionParser {
+  public:
+    SubsystemParser(std::vector<Subsystem>* subsystems) : subsystems_(subsystems) {}
+    bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
+                      std::string* err) override;
+    bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) override;
+    void EndSection() override;
+
+  private:
+    bool ParseDevName(std::vector<std::string>&& args, std::string* err);
+    bool ParseDirName(std::vector<std::string>&& args, std::string* err);
+
+    Subsystem subsystem_;
+    std::vector<Subsystem>* subsystems_;
+};
+
+bool ParsePermissionsLine(std::vector<std::string>&& args, std::string* err,
+                          std::vector<SysfsPermissions>* out_sysfs_permissions,
+                          std::vector<Permissions>* out_dev_permissions);
+
+#endif
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index 48b50a5..02141d6 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -130,6 +130,7 @@
 #define AID_MEDIA_OBB 1059       /* GID for OBB files on internal media storage */
 #define AID_ESE 1060             /* embedded secure element (eSE) subsystem */
 #define AID_OTA_UPDATE 1061      /* resource tracking UID for OTA updates */
+#define AID_AUTOMOTIVE_EVS 1062  /* Automotive rear and surround view system */
 /* Changes to this file must be made in AOSP, *not* in internal branches. */
 
 #define AID_SHELL 2000 /* adb and debug shell user */
diff --git a/libdiskconfig/Android.bp b/libdiskconfig/Android.bp
index 041fd63..088981a 100644
--- a/libdiskconfig/Android.bp
+++ b/libdiskconfig/Android.bp
@@ -1,5 +1,6 @@
 cc_library {
     name: "libdiskconfig",
+    vendor_available: true,
     srcs: [
         "diskconfig.c",
         "diskutils.c",
diff --git a/libion/Android.bp b/libion/Android.bp
index da98111..6f267e4 100644
--- a/libion/Android.bp
+++ b/libion/Android.bp
@@ -1,6 +1,7 @@
 
 cc_library {
     name: "libion",
+		vendor_available: true,
     srcs: ["ion.c"],
     shared_libs: ["liblog"],
     local_include_dirs: [
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index ec32da0..46ec5ef 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -36,6 +36,7 @@
 #endif
 #include <gtest/gtest.h>
 #include <log/log_event_list.h>
+#include <log/log_properties.h>
 #include <log/log_transport.h>
 #include <log/logprint.h>
 #include <private/android_filesystem_config.h>
@@ -1719,6 +1720,7 @@
 // Kills logd and toss all collected data, equivalent to logcat -b all -c,
 // except we also return errors to the logging callers.
 #ifdef USING_LOGGER_DEFAULT
+#ifdef __ANDROID__
 #ifdef TEST_PREFIX
 // helper to liblog.enoent to count end-to-end matching logging messages.
 static int count_matching_ts(log_time ts) {
@@ -1786,6 +1788,12 @@
         stderr,
         "WARNING: test conditions request being run as root and not AID=%d\n",
         getuid());
+    if (!__android_log_is_debuggable()) {
+      fprintf(
+          stderr,
+          "WARNING: can not run test on a \"user\" build, bypassing test\n");
+      return;
+    }
   }
 
   system((getuid() == AID_ROOT) ? "stop logd" : "su 0 stop logd");
@@ -1825,7 +1833,8 @@
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
-#endif  // USING_LOCAL_LOGD
+#endif  // __ANDROID__
+#endif  // USING_LOGGER_DEFAULT
 
 // Below this point we run risks of setuid(AID_SYSTEM) which may affect others.
 
diff --git a/liblog/tests/log_read_test.cpp b/liblog/tests/log_read_test.cpp
index a63ab8e..6ed568a 100644
--- a/liblog/tests/log_read_test.cpp
+++ b/liblog/tests/log_read_test.cpp
@@ -23,6 +23,7 @@
 #include <android-base/stringprintf.h>
 #include <android/log.h>  // minimal logging API
 #include <gtest/gtest.h>
+#include <log/log_properties.h>
 // Test the APIs in this standalone include file
 #include <log/log_read.h>
 // Do not use anything in log/log_time.h despite side effects of the above.
@@ -97,9 +98,12 @@
     /* security buffer is allowed to be denied */
     if (strcmp("security", name)) {
       EXPECT_LT(0, get_log_size);
-      /* crash buffer is allowed to be empty, that is actually healthy! */
-      EXPECT_LE((strcmp("crash", name)) != 0,
-                android_logger_get_log_readable_size(logger));
+      // crash buffer is allowed to be empty, that is actually healthy!
+      // kernel buffer is allowed to be empty on "user" builds
+      EXPECT_LE(  // boolean 1 or 0 depending on expected content or empty
+          !!((strcmp("crash", name) != 0) &&
+             ((strcmp("kernel", name) != 0) || __android_log_is_debuggable())),
+          android_logger_get_log_readable_size(logger));
     } else {
       EXPECT_NE(0, get_log_size);
       if (get_log_size < 0) {
diff --git a/libmemunreachable/Android.bp b/libmemunreachable/Android.bp
index cdac76b..4269eaa 100644
--- a/libmemunreachable/Android.bp
+++ b/libmemunreachable/Android.bp
@@ -23,6 +23,7 @@
 
 cc_library_shared {
     name: "libmemunreachable",
+    vendor_available: true,
     defaults: ["libmemunreachable_defaults"],
     srcs: [
         "Allocator.cpp",
diff --git a/libmetricslogger/Android.bp b/libmetricslogger/Android.bp
index 26a041a..38859d1 100644
--- a/libmetricslogger/Android.bp
+++ b/libmetricslogger/Android.bp
@@ -31,6 +31,7 @@
 // -----------------------------------------------------------------------------
 cc_library_shared {
     name: "libmetricslogger",
+    vendor_available: true,
     srcs: metricslogger_lib_src_files,
     defaults: ["metricslogger_defaults"],
 }
diff --git a/libnativebridge/native_bridge.cc b/libnativebridge/native_bridge.cc
index 050373a..02b4fe7 100644
--- a/libnativebridge/native_bridge.cc
+++ b/libnativebridge/native_bridge.cc
@@ -85,12 +85,14 @@
 // Nativebridge implementation.
 // Used by isCompatibleWith() which is introduced in v2.
 enum NativeBridgeImplementationVersion {
-    // first version, not used.
-    DEFAULT_VERSION = 1,
-    // The version which signal semantic is introduced.
-    SIGNAL_VERSION = 2,
-    // The version which namespace semantic is introduced.
-    NAMESPACE_VERSION = 3,
+  // first version, not used.
+  DEFAULT_VERSION = 1,
+  // The version which signal semantic is introduced.
+  SIGNAL_VERSION = 2,
+  // The version which namespace semantic is introduced.
+  NAMESPACE_VERSION = 3,
+  // The version with vendor namespaces
+  VENDOR_NAMESPACE_VERSION = 4,
 };
 
 // Whether we had an error at some point.
@@ -621,6 +623,14 @@
   return false;
 }
 
+native_bridge_namespace_t* NativeBridgeGetVendorNamespace() {
+  if (!NativeBridgeInitialized() || !isCompatibleWith(VENDOR_NAMESPACE_VERSION)) {
+    return nullptr;
+  }
+
+  return callbacks->getVendorNamespace();
+}
+
 void* NativeBridgeLoadLibraryExt(const char* libpath, int flag, native_bridge_namespace_t* ns) {
   if (NativeBridgeInitialized()) {
     if (isCompatibleWith(NAMESPACE_VERSION)) {
diff --git a/libnativeloader/include/nativeloader/dlext_namespaces.h b/libnativeloader/include/nativeloader/dlext_namespaces.h
index 9121277..43c9329 100644
--- a/libnativeloader/include/nativeloader/dlext_namespaces.h
+++ b/libnativeloader/include/nativeloader/dlext_namespaces.h
@@ -124,6 +124,8 @@
  */
 extern void android_get_LD_LIBRARY_PATH(char* buffer, size_t buffer_size);
 
+extern android_namespace_t* android_get_exported_namespace(const char* name);
+
 __END_DECLS
 
 #endif /* __ANDROID_DLEXT_NAMESPACES_H__ */
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index d9cb90d..36a2e44 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -83,6 +83,12 @@
 static constexpr const char* kPublicNativeLibrariesVendorConfig =
                                   "/vendor/etc/public.libraries.txt";
 
+// The device may be configured to have the vendor libraries loaded to a separate namespace.
+// For historical reasons this namespace was named sphal but effectively it is intended
+// to use to load vendor libraries to separate namespace with controlled interface between
+// vendor and system namespaces.
+static constexpr const char* kVendorNamespaceName = "sphal";
+
 // (http://b/27588281) This is a workaround for apps using custom classloaders and calling
 // System.load() with an absolute path which is outside of the classloader library search path.
 // This list includes all directories app is allowed to access this way.
@@ -170,11 +176,23 @@
         return false;
       }
 
-      if (!android_link_namespaces(ns, nullptr, public_libraries_.c_str())) {
+      // Note that when vendor_ns is not configured this function will return nullptr
+      // and it will result in linking vendor_public_libraries_ to the default namespace
+      // which is expected behavior in this case.
+      android_namespace_t* vendor_ns = android_get_exported_namespace(kVendorNamespaceName);
+
+      if (!android_link_namespaces(ns, nullptr, system_public_libraries_.c_str())) {
         *error_msg = dlerror();
         return false;
       }
 
+      if (!vendor_public_libraries_.empty()) {
+        if (!android_link_namespaces(ns, vendor_ns, vendor_public_libraries_.c_str())) {
+          *error_msg = dlerror();
+          return false;
+        }
+      }
+
       native_loader_ns = NativeLoaderNamespace(ns);
     } else {
       native_bridge_namespace_t* ns = NativeBridgeCreateNamespace("classloader-namespace",
@@ -183,16 +201,26 @@
                                                                   namespace_type,
                                                                   permitted_path.c_str(),
                                                                   parent_ns.get_native_bridge_ns());
+
       if (ns == nullptr) {
         *error_msg = NativeBridgeGetError();
         return false;
       }
 
-      if (!NativeBridgeLinkNamespaces(ns, nullptr, public_libraries_.c_str())) {
+      native_bridge_namespace_t* vendor_ns = NativeBridgeGetVendorNamespace();
+
+      if (!NativeBridgeLinkNamespaces(ns, nullptr, system_public_libraries_.c_str())) {
         *error_msg = NativeBridgeGetError();
         return false;
       }
 
+      if (!vendor_public_libraries_.empty()) {
+        if (!NativeBridgeLinkNamespaces(ns, vendor_ns, vendor_public_libraries_.c_str())) {
+          *error_msg = NativeBridgeGetError();
+          return false;
+        }
+      }
+
       native_loader_ns = NativeLoaderNamespace(ns);
     }
 
@@ -249,9 +277,6 @@
       }
     }
 
-    // This file is optional, quietly ignore if the file does not exist.
-    ReadConfig(kPublicNativeLibrariesVendorConfig, &sonames);
-
     // android_init_namespaces() expects all the public libraries
     // to be loaded so that they can be found by soname alone.
     //
@@ -266,7 +291,13 @@
                           soname.c_str(), dlerror());
     }
 
-    public_libraries_ = base::Join(sonames, ':');
+    system_public_libraries_ = base::Join(sonames, ':');
+
+    sonames.clear();
+    // This file is optional, quietly ignore if the file does not exist.
+    ReadConfig(kPublicNativeLibrariesVendorConfig, &sonames);
+
+    vendor_public_libraries_ = base::Join(sonames, ':');
   }
 
   void Reset() {
@@ -325,7 +356,7 @@
     // code is one example) unknown to linker in which  case linker uses anonymous
     // namespace. The second argument specifies the search path for the anonymous
     // namespace which is the library_path of the classloader.
-    initialized_ = android_init_anonymous_namespace(public_libraries_.c_str(),
+    initialized_ = android_init_anonymous_namespace(system_public_libraries_.c_str(),
                                                     is_native_bridge ? nullptr : library_path);
     if (!initialized_) {
       *error_msg = dlerror();
@@ -334,7 +365,7 @@
 
     // and now initialize native bridge namespaces if necessary.
     if (NativeBridgeInitialized()) {
-      initialized_ = NativeBridgeInitAnonymousNamespace(public_libraries_.c_str(),
+      initialized_ = NativeBridgeInitAnonymousNamespace(system_public_libraries_.c_str(),
                                                         is_native_bridge ? library_path : nullptr);
       if (!initialized_) {
         *error_msg = NativeBridgeGetError();
@@ -371,8 +402,8 @@
 
   bool initialized_;
   std::vector<std::pair<jweak, NativeLoaderNamespace>> namespaces_;
-  std::string public_libraries_;
-
+  std::string system_public_libraries_;
+  std::string vendor_public_libraries_;
 
   DISALLOW_COPY_AND_ASSIGN(LibraryNamespaces);
 };
diff --git a/libnetutils/Android.bp b/libnetutils/Android.bp
index 0caf145..9967ef8 100644
--- a/libnetutils/Android.bp
+++ b/libnetutils/Android.bp
@@ -1,5 +1,6 @@
 cc_library_shared {
     name: "libnetutils",
+    vendor_available: true,
 
     srcs: [
         "dhcpclient.c",
diff --git a/libprocinfo/Android.bp b/libprocinfo/Android.bp
index c13ffe9..aedaa38 100644
--- a/libprocinfo/Android.bp
+++ b/libprocinfo/Android.bp
@@ -22,6 +22,7 @@
 
 cc_library {
     name: "libprocinfo",
+    vendor_available: true,
     host_supported: true,
     srcs: [
         "process.cpp",
diff --git a/libsuspend/Android.bp b/libsuspend/Android.bp
index d442c94..130800e 100644
--- a/libsuspend/Android.bp
+++ b/libsuspend/Android.bp
@@ -2,6 +2,8 @@
 
 cc_library {
     name: "libsuspend",
+    vendor_available: true,
+
     srcs: [
         "autosuspend.c",
         "autosuspend_wakeup_count.c",
diff --git a/libsysutils/Android.bp b/libsysutils/Android.bp
index 296bd26..550ef42 100644
--- a/libsysutils/Android.bp
+++ b/libsysutils/Android.bp
@@ -1,5 +1,7 @@
 cc_library_shared {
     name: "libsysutils",
+    vendor_available: true,
+
     srcs: [
         "src/SocketListener.cpp",
         "src/FrameworkListener.cpp",
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index 44daf36..0a4f088 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -56,6 +56,8 @@
 cc_library {
     name: "libziparchive",
     host_supported: true,
+    vendor_available:true,
+
     defaults: ["libziparchive_defaults", "libziparchive_flags"],
     shared_libs: ["liblog", "libbase"],
     target: {
diff --git a/platform_tools_tool_version.mk b/platform_tools_tool_version.mk
index 73229e6..eed2ab5 100644
--- a/platform_tools_tool_version.mk
+++ b/platform_tools_tool_version.mk
@@ -17,6 +17,6 @@
 # literal instead. Using 0 lets us easily distinguish non-canonical builds.
 platform_tools_version := $(shell sed \
     's/$${PLATFORM_SDK_VERSION}/0/ ; s/^Pkg.Revision=\(.*\)/\1/p ; d' \
-    $(ANDROID_BUILD_TOP)/development/sdk/plat_tools_source.prop_template \
+    development/sdk/plat_tools_source.prop_template \
   )
 tool_version := $(platform_tools_version)-$(BUILD_NUMBER_FROM_FILE)
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index 9774efb..773ca06 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -60,10 +60,10 @@
 namespace.sphal.links = default,vndk,rs
 
 # WARNING: only NDK libs can be listed here.
-namespace.sphal.link.default.shared_libs = libc.so:libz.so:libm.so:libdl.so:libstdc++.so:liblog.so:libnativewindow.so:libEGL.so:libsync.so:libGLESv1_CM.so:libGLESv2.so:libvndksupport.so
+namespace.sphal.link.default.shared_libs = libc.so:libm.so:libdl.so:libstdc++.so:liblog.so:libnativewindow.so:libEGL.so:libsync.so:libGLESv1_CM.so:libGLESv2.so:libvndksupport.so
 
 # WARNING: only VNDK-SP libs can be listed here. DO NOT EDIT this line.
-namespace.sphal.link.vndk.shared_libs = android.hardware.renderscript@1.0.so:android.hardware.graphics.allocator@2.0.so:android.hardware.graphics.mapper@2.0.so:android.hardware.graphics.common@1.0.so:libhwbinder.so:libbase.so:libcutils.so:libhardware.so:libhidlbase.so:libhidltransport.so:libutils.so:libc++.so
+namespace.sphal.link.vndk.shared_libs = android.hardware.renderscript@1.0.so:android.hardware.graphics.allocator@2.0.so:android.hardware.graphics.mapper@2.0.so:android.hardware.graphics.common@1.0.so:libhwbinder.so:libbase.so:libcutils.so:libhardware.so:libhidlbase.so:libhidltransport.so:libutils.so:libc++.so:libz.so
 
 # Renderscript gets separate namespace
 namespace.sphal.link.rs.shared_libs = libRS_internal.so
@@ -84,8 +84,8 @@
 namespace.rs.asan.permitted.paths = /data/asan/vendor/${LIB}:/vendor/${LIB}:/data
 
 namespace.rs.links = default,vndk
-namespace.rs.link.default.shared_libs = libc.so:libz.so:libm.so:libdl.so:libstdc++.so:liblog.so:libnativewindow.so:libEGL.so:libsync.so:libGLESv1_CM.so:libGLESv2.so:libmediandk.so:libui.so:libvndksupport.so
-namespace.rs.link.vndk.shared_libs = android.hardware.renderscript@1.0.so:android.hardware.graphics.allocator@2.0.so:android.hardware.graphics.mapper@2.0.so:android.hardware.graphics.common@1.0.so:libhwbinder.so:libbase.so:libcutils.so:libhardware.so:libhidlbase.so:libhidltransport.so:libutils.so:libc++.so
+namespace.rs.link.default.shared_libs = libc.so:libm.so:libdl.so:libstdc++.so:liblog.so:libnativewindow.so:libEGL.so:libsync.so:libGLESv1_CM.so:libGLESv2.so:libmediandk.so:libui.so:libvndksupport.so
+namespace.rs.link.vndk.shared_libs = android.hardware.renderscript@1.0.so:android.hardware.graphics.allocator@2.0.so:android.hardware.graphics.mapper@2.0.so:android.hardware.graphics.common@1.0.so:libhwbinder.so:libbase.so:libcutils.so:libhardware.so:libhidlbase.so:libhidltransport.so:libutils.so:libc++.so:libz.so
 
 ###############################################################################
 # "vndk" namespace
@@ -103,7 +103,7 @@
 # to the default namespace. This is possible since their ABI is stable across
 # Android releases.
 namespace.vndk.links = default
-namespace.vndk.link.default.shared_libs = libc.so:libz.so:libm.so:libdl.so:libstdc++.so:liblog.so:libnativewindow.so:libEGL.so:libsync.so:libvndksupport.so
+namespace.vndk.link.default.shared_libs = libc.so:libm.so:libdl.so:libstdc++.so:liblog.so:libnativewindow.so:libEGL.so:libsync.so:libvndksupport.so
 
 
 [vendor]
diff --git a/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp
index 81cf315..4f4fc5d 100644
--- a/shell_and_utilities/Android.bp
+++ b/shell_and_utilities/Android.bp
@@ -5,9 +5,12 @@
         "grep",
         "gzip",
         "mkshrc",
+        "mkshrc_vendor",
         "reboot",
         "sh",
+        "sh_vendor",
         "toolbox",
         "toybox",
+        "toybox_vendor",
     ],
 }
diff --git a/toolbox/Android.bp b/toolbox/Android.bp
new file mode 100644
index 0000000..1c9fb20
--- /dev/null
+++ b/toolbox/Android.bp
@@ -0,0 +1,44 @@
+common_cflags = [
+    "-Werror",
+    "-Wno-unused-parameter",
+    "-Wno-unused-const-variable",
+    "-include bsd-compatibility.h"
+]
+
+cc_library_static {
+    srcs: [
+        "upstream-netbsd/bin/dd/args.c",
+        "upstream-netbsd/bin/dd/conv.c",
+        "upstream-netbsd/bin/dd/dd.c",
+        "upstream-netbsd/bin/dd/dd_hostops.c",
+        "upstream-netbsd/bin/dd/misc.c",
+        "upstream-netbsd/bin/dd/position.c",
+        "upstream-netbsd/lib/libc/gen/getbsize.c",
+        "upstream-netbsd/lib/libc/gen/humanize_number.c",
+        "upstream-netbsd/lib/libc/stdlib/strsuftoll.c",
+        "upstream-netbsd/lib/libc/string/swab.c",
+        "upstream-netbsd/lib/libutil/raise_default_signal.c",
+    ],
+    cflags: common_cflags + [
+        "-Dmain=dd_main",
+        "-DNO_CONV",
+    ],
+    local_include_dirs: ["upstream-netbsd/include/"],
+    name: "libtoolbox_dd",
+}
+
+// We build BSD grep separately, so it can provide egrep and fgrep too.
+cc_binary {
+    name: "grep",
+    srcs: [
+        "upstream-netbsd/usr.bin/grep/fastgrep.c",
+        "upstream-netbsd/usr.bin/grep/file.c",
+        "upstream-netbsd/usr.bin/grep/grep.c",
+        "upstream-netbsd/usr.bin/grep/queue.c",
+        "upstream-netbsd/usr.bin/grep/util.c",
+    ],
+    cflags: common_cflags,
+    local_include_dirs: ["upstream-netbsd/include/"],
+    symlinks: ["egrep", "fgrep"],
+
+}
diff --git a/toolbox/Android.mk b/toolbox/Android.mk
index d6ead1a..94029d8 100644
--- a/toolbox/Android.mk
+++ b/toolbox/Android.mk
@@ -1,30 +1,9 @@
 LOCAL_PATH:= $(call my-dir)
 
-
 common_cflags := \
     -Werror -Wno-unused-parameter -Wno-unused-const-variable \
     -include bsd-compatibility.h \
 
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := \
-    upstream-netbsd/bin/dd/args.c \
-    upstream-netbsd/bin/dd/conv.c \
-    upstream-netbsd/bin/dd/dd.c \
-    upstream-netbsd/bin/dd/dd_hostops.c \
-    upstream-netbsd/bin/dd/misc.c \
-    upstream-netbsd/bin/dd/position.c \
-    upstream-netbsd/lib/libc/gen/getbsize.c \
-    upstream-netbsd/lib/libc/gen/humanize_number.c \
-    upstream-netbsd/lib/libc/stdlib/strsuftoll.c \
-    upstream-netbsd/lib/libc/string/swab.c \
-    upstream-netbsd/lib/libutil/raise_default_signal.c
-LOCAL_CFLAGS += $(common_cflags) -Dmain=dd_main -DNO_CONV
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/upstream-netbsd/include/
-LOCAL_MODULE := libtoolbox_dd
-include $(BUILD_STATIC_LIBRARY)
-
-
 include $(CLEAR_VARS)
 
 BSD_TOOLS := \
@@ -80,18 +59,3 @@
 $(INPUT_H_LABELS_H): $(LOCAL_PATH)/Android.mk $(LOCAL_PATH)/generate-input.h-labels.py $(UAPI_INPUT_EVENT_CODES_H)
 $(INPUT_H_LABELS_H):
 	$(transform-generated-source)
-
-
-# We build BSD grep separately, so it can provide egrep and fgrep too.
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := \
-    upstream-netbsd/usr.bin/grep/fastgrep.c \
-    upstream-netbsd/usr.bin/grep/file.c \
-    upstream-netbsd/usr.bin/grep/grep.c \
-    upstream-netbsd/usr.bin/grep/queue.c \
-    upstream-netbsd/usr.bin/grep/util.c
-LOCAL_CFLAGS += $(common_cflags)
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/upstream-netbsd/include/
-LOCAL_MODULE := grep
-LOCAL_POST_INSTALL_CMD := $(hide) $(foreach t,egrep fgrep,ln -sf grep $(TARGET_OUT)/bin/$(t);)
-include $(BUILD_EXECUTABLE)