Merge "Decrease size of LogBufferElements to 32 bytes."
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index f86aaa0..7d17cd9 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -106,6 +106,7 @@
         "libdebuggerd",
         "libbacktrace",
         "libunwind",
+        "libunwindstack",
         "liblzma",
         "libcutils",
     ],
@@ -171,6 +172,7 @@
     static_libs: [
         "libbacktrace",
         "libunwind",
+        "libunwindstack",
         "liblzma",
         "libbase",
         "libcutils",
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 4b1e51d..34ad7de 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -329,6 +329,11 @@
       LOG(FATAL) << "failed to create backtrace map";
     }
   }
+  std::unique_ptr<BacktraceMap> backtrace_map_new;
+  backtrace_map_new.reset(BacktraceMap::CreateNew(main_tid));
+  if (!backtrace_map_new) {
+    LOG(FATAL) << "failed to create backtrace map new";
+  }
 
   // Collect the list of open files.
   OpenFilesList open_files;
@@ -408,8 +413,9 @@
     dump_backtrace(output_fd.get(), backtrace_map.get(), target, main_tid, process_name, threads, 0);
   } else {
     ATRACE_NAME("engrave_tombstone");
-    engrave_tombstone(output_fd.get(), backtrace_map.get(), &open_files, target, main_tid,
-                      process_name, threads, abort_address, fatal_signal ? &amfd_data : nullptr);
+    engrave_tombstone(output_fd.get(), backtrace_map.get(), backtrace_map_new.get(), &open_files,
+                      target, main_tid, process_name, threads, abort_address,
+                      fatal_signal ? &amfd_data : nullptr);
   }
 
   // We don't actually need to PTRACE_DETACH, as long as our tracees aren't in
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index 55cd03e..1275229 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -116,6 +116,26 @@
   fatal("%s: %s", buf, strerror(err));
 }
 
+static bool get_main_thread_name(char* buf, size_t len) {
+  int fd = open("/proc/self/comm", O_RDONLY | O_CLOEXEC);
+  if (fd == -1) {
+    return false;
+  }
+
+  ssize_t rc = read(fd, buf, len);
+  close(fd);
+  if (rc == -1) {
+    return false;
+  } else if (rc == 0) {
+    // Should never happen?
+    return false;
+  }
+
+  // There's a trailing newline, replace it with a NUL.
+  buf[rc - 1] = '\0';
+  return true;
+}
+
 /*
  * Writes a summary of the signal to the log file.  We do this so that, if
  * for some reason we're not able to contact debuggerd, there is still some
@@ -188,8 +208,14 @@
     }
   }
 
-  async_safe_format_log(ANDROID_LOG_FATAL, "libc", "Fatal signal %d (%s)%s%s in tid %d (%s)",
-                        signum, signal_name, code_desc, addr_desc, __gettid(), thread_name);
+  char main_thread_name[MAX_TASK_NAME_LEN + 1];
+  if (!get_main_thread_name(main_thread_name, sizeof(main_thread_name))) {
+    strncpy(main_thread_name, "<unknown>", sizeof(main_thread_name));
+  }
+
+  async_safe_format_log(
+      ANDROID_LOG_FATAL, "libc", "Fatal signal %d (%s)%s%s in tid %d (%s), pid %d (%s)", signum,
+      signal_name, code_desc, addr_desc, __gettid(), thread_name, __getpid(), main_thread_name);
 }
 
 /*
diff --git a/debuggerd/libdebuggerd/include/tombstone.h b/debuggerd/libdebuggerd/include/tombstone.h
index 79743b6..45740df 100644
--- a/debuggerd/libdebuggerd/include/tombstone.h
+++ b/debuggerd/libdebuggerd/include/tombstone.h
@@ -35,10 +35,10 @@
 int open_tombstone(std::string* path);
 
 /* Creates a tombstone file and writes the crash dump to it. */
-void engrave_tombstone(int tombstone_fd, BacktraceMap* map, const OpenFilesList* open_files,
-                       pid_t pid, pid_t tid, const std::string& process_name,
-                       const std::map<pid_t, std::string>& threads, uintptr_t abort_msg_address,
-                       std::string* amfd_data);
+void engrave_tombstone(int tombstone_fd, BacktraceMap* map, BacktraceMap* map_new,
+                       const OpenFilesList* open_files, pid_t pid, pid_t tid,
+                       const std::string& process_name, const std::map<pid_t, std::string>& threads,
+                       uintptr_t abort_msg_address, std::string* amfd_data);
 
 void engrave_tombstone_ucontext(int tombstone_fd, uintptr_t abort_msg_address, siginfo_t* siginfo,
                                 ucontext_t* ucontext);
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 0113131..b809ed4 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -495,8 +495,55 @@
   _LOG(log, logtype::REGISTERS, "    register dumping unimplemented on this architecture");
 }
 
-static void dump_thread(log_t* log, pid_t pid, pid_t tid, const std::string& process_name,
-                        const std::string& thread_name, BacktraceMap* map,
+static bool verify_backtraces_equal(Backtrace* back1, Backtrace* back2) {
+  if (back1->NumFrames() != back2->NumFrames()) {
+    return false;
+  }
+  std::string back1_str;
+  std::string back2_str;
+  for (size_t i = 0; i < back1->NumFrames(); i++) {
+    back1_str += back1->FormatFrameData(i);
+    back2_str += back2->FormatFrameData(i);
+  }
+  return back1_str == back2_str;
+}
+
+static void log_mismatch_data(log_t* log, Backtrace* backtrace) {
+  _LOG(log, logtype::THREAD, "MISMATCH: This unwind is different.\n");
+  if (backtrace->NumFrames() == 0) {
+    _LOG(log, logtype::THREAD, "MISMATCH: No frames in new backtrace.\n");
+    return;
+  }
+  _LOG(log, logtype::THREAD, "MISMATCH: Backtrace from new unwinder.\n");
+  for (size_t i = 0; i < backtrace->NumFrames(); i++) {
+    _LOG(log, logtype::THREAD, "MISMATCH: %s\n", backtrace->FormatFrameData(i).c_str());
+  }
+
+  // Get the stack trace up to 8192 bytes.
+  std::vector<uint64_t> buffer(8192 / sizeof(uint64_t));
+  size_t bytes =
+      backtrace->Read(backtrace->GetFrame(0)->sp, reinterpret_cast<uint8_t*>(buffer.data()),
+                      buffer.size() * sizeof(uint64_t));
+  std::string log_data;
+  for (size_t i = 0; i < bytes / sizeof(uint64_t); i++) {
+    if ((i % 4) == 0) {
+      if (!log_data.empty()) {
+        _LOG(log, logtype::THREAD, "MISMATCH: stack_data%s\n", log_data.c_str());
+        log_data = "";
+      }
+    }
+    log_data += android::base::StringPrintf(" 0x%016" PRIx64, buffer[i]);
+  }
+
+  if (!log_data.empty()) {
+    _LOG(log, logtype::THREAD, "MISMATCH: data%s\n", log_data.c_str());
+  }
+
+  // If there is any leftover (bytes % sizeof(uint64_t) != 0, ignore it for now.
+}
+
+static bool dump_thread(log_t* log, pid_t pid, pid_t tid, const std::string& process_name,
+                        const std::string& thread_name, BacktraceMap* map, BacktraceMap* map_new,
                         uintptr_t abort_msg_address, bool primary_thread) {
   log->current_tid = tid;
   if (!primary_thread) {
@@ -510,7 +557,18 @@
     dump_abort_message(backtrace.get(), log, abort_msg_address);
   }
   dump_registers(log, tid);
+  bool matches = true;
   if (backtrace->Unwind(0)) {
+    // Use the new method and verify it is the same as old.
+    std::unique_ptr<Backtrace> backtrace_new(Backtrace::CreateNew(pid, tid, map_new));
+    if (!backtrace_new->Unwind(0)) {
+      _LOG(log, logtype::THREAD, "Failed to unwind with new unwinder: %s\n",
+           backtrace_new->GetErrorString(backtrace_new->GetError()).c_str());
+      matches = false;
+    } else if (!verify_backtraces_equal(backtrace.get(), backtrace_new.get())) {
+      log_mismatch_data(log, backtrace_new.get());
+      matches = false;
+    }
     dump_backtrace_and_stack(backtrace.get(), log);
   } else {
     ALOGE("Unwind failed: pid = %d, tid = %d", pid, tid);
@@ -524,6 +582,8 @@
   }
 
   log->current_tid = log->crashed_tid;
+
+  return matches;
 }
 
 // Reads the contents of the specified log device, filters out the entries
@@ -657,9 +717,10 @@
 }
 
 // Dumps all information about the specified pid to the tombstone.
-static void dump_crash(log_t* log, BacktraceMap* map, const OpenFilesList* open_files, pid_t pid,
-                       pid_t tid, const std::string& process_name,
-                       const std::map<pid_t, std::string>& threads, uintptr_t abort_msg_address) {
+static void dump_crash(log_t* log, BacktraceMap* map, BacktraceMap* map_new,
+                       const OpenFilesList* open_files, pid_t pid, pid_t tid,
+                       const std::string& process_name, const std::map<pid_t, std::string>& threads,
+                       uintptr_t abort_msg_address) {
   // don't copy log messages to tombstone unless this is a dev device
   char value[PROPERTY_VALUE_MAX];
   property_get("ro.debuggable", value, "0");
@@ -668,7 +729,8 @@
   _LOG(log, logtype::HEADER,
        "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
   dump_header_info(log);
-  dump_thread(log, pid, tid, process_name, threads.find(tid)->second, map, abort_msg_address, true);
+  bool new_unwind_matches = dump_thread(log, pid, tid, process_name, threads.find(tid)->second, map,
+                                        map_new, abort_msg_address, true);
   if (want_logs) {
     dump_logs(log, pid, 5);
   }
@@ -678,7 +740,9 @@
     const std::string& thread_name = it.second;
 
     if (thread_tid != tid) {
-      dump_thread(log, pid, thread_tid, process_name, thread_name, map, 0, false);
+      bool match =
+          dump_thread(log, pid, thread_tid, process_name, thread_name, map, map_new, 0, false);
+      new_unwind_matches = new_unwind_matches && match;
     }
   }
 
@@ -690,6 +754,14 @@
   if (want_logs) {
     dump_logs(log, pid, 0);
   }
+  if (!new_unwind_matches) {
+    _LOG(log, logtype::THREAD, "MISMATCH: New and old unwinder do not agree.\n");
+    _LOG(log, logtype::THREAD, "MISMATCH: If you see this please file a bug in:\n");
+    _LOG(log, logtype::THREAD,
+         "MISMATCH: Android > Android OS & Apps > Runtime > native > tools "
+         "(debuggerd/gdb/init/simpleperf/strace/valgrind)\n");
+    _LOG(log, logtype::THREAD, "MISMATCH: and attach this tombstone.\n");
+  }
 }
 
 // open_tombstone - find an available tombstone slot, if any, of the
@@ -745,16 +817,16 @@
   return fd;
 }
 
-void engrave_tombstone(int tombstone_fd, BacktraceMap* map, const OpenFilesList* open_files,
-                       pid_t pid, pid_t tid, const std::string& process_name,
-                       const std::map<pid_t, std::string>& threads, uintptr_t abort_msg_address,
-                       std::string* amfd_data) {
+void engrave_tombstone(int tombstone_fd, BacktraceMap* map, BacktraceMap* map_new,
+                       const OpenFilesList* open_files, pid_t pid, pid_t tid,
+                       const std::string& process_name, const std::map<pid_t, std::string>& threads,
+                       uintptr_t abort_msg_address, std::string* amfd_data) {
   log_t log;
   log.current_tid = tid;
   log.crashed_tid = tid;
   log.tfd = tombstone_fd;
   log.amfd_data = amfd_data;
-  dump_crash(&log, map, open_files, pid, tid, process_name, threads, abort_msg_address);
+  dump_crash(&log, map, map_new, open_files, pid, tid, process_name, threads, abort_msg_address);
 }
 
 void engrave_tombstone_ucontext(int tombstone_fd, uintptr_t abort_msg_address, siginfo_t* siginfo,
diff --git a/init/Android.bp b/init/Android.bp
index 432c298..b1f0279 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -70,6 +70,8 @@
         "log.cpp",
         "parser.cpp",
         "property_service.cpp",
+        "security.cpp",
+        "selinux.cpp",
         "service.cpp",
         "tokenizer.cpp",
         "uevent_listener.cpp",
@@ -157,6 +159,7 @@
         "devices_test.cpp",
         "init_test.cpp",
         "property_service_test.cpp",
+        "result_test.cpp",
         "service_test.cpp",
         "ueventd_test.cpp",
         "util_test.cpp",
diff --git a/init/README.md b/init/README.md
index f3b57bc..0ea00fb 100644
--- a/init/README.md
+++ b/init/README.md
@@ -447,6 +447,9 @@
 `rmdir <path>`
 > Calls rmdir(2) on the given path.
 
+`readahead <file|dir>`
+> Calls readahead(2) on the file or files within given directory.
+
 `setprop <name> <value>`
 > Set system property _name_ to _value_. Properties are expanded
   within _value_.
diff --git a/init/action.cpp b/init/action.cpp
index 4ec5f17..f687074 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -31,14 +31,13 @@
 Command::Command(BuiltinFunction f, const std::vector<std::string>& args, int line)
     : func_(f), args_(args), line_(line) {}
 
-int Command::InvokeFunc() const {
+Result<Success> Command::InvokeFunc() const {
     std::vector<std::string> expanded_args;
     expanded_args.resize(args_.size());
     expanded_args[0] = args_[0];
     for (std::size_t i = 1; i < args_.size(); ++i) {
         if (!expand_props(args_[i], &expanded_args[i])) {
-            LOG(ERROR) << args_[0] << ": cannot expand '" << args_[i] << "'";
-            return -EINVAL;
+            return Error() << "cannot expand '" << args_[i] << "'";
         }
     }
 
@@ -54,19 +53,16 @@
 
 const KeywordMap<BuiltinFunction>* Action::function_map_ = nullptr;
 
-bool Action::AddCommand(const std::vector<std::string>& args, int line, std::string* err) {
+Result<Success> Action::AddCommand(const std::vector<std::string>& args, int line) {
     if (!function_map_) {
-        *err = "no function map available";
-        return false;
+        return Error() << "no function map available";
     }
 
-    auto function = function_map_->FindFunction(args, err);
-    if (!function) {
-        return false;
-    }
+    auto function = function_map_->FindFunction(args);
+    if (!function) return Error() << function.error();
 
-    AddCommand(function, args, line);
-    return true;
+    AddCommand(*function, args, line);
+    return Success();
 }
 
 void Action::AddCommand(BuiltinFunction f, const std::vector<std::string>& args, int line) {
@@ -92,81 +88,74 @@
 
 void Action::ExecuteCommand(const Command& command) const {
     android::base::Timer t;
-    int result = command.InvokeFunc();
-
+    auto result = command.InvokeFunc();
     auto duration = t.duration();
+
     // Any action longer than 50ms will be warned to user as slow operation
     if (duration > 50ms || android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
         std::string trigger_name = BuildTriggersString();
         std::string cmd_str = command.BuildCommandString();
 
         LOG(INFO) << "Command '" << cmd_str << "' action=" << trigger_name << " (" << filename_
-                  << ":" << command.line() << ") returned " << result << " took "
-                  << duration.count() << "ms.";
+                  << ":" << command.line() << ") took " << duration.count() << "ms and "
+                  << (result ? "succeeded" : "failed: " + result.error());
     }
 }
 
-bool Action::ParsePropertyTrigger(const std::string& trigger, std::string* err) {
+Result<Success> Action::ParsePropertyTrigger(const std::string& trigger) {
     const static std::string prop_str("property:");
     std::string prop_name(trigger.substr(prop_str.length()));
     size_t equal_pos = prop_name.find('=');
     if (equal_pos == std::string::npos) {
-        *err = "property trigger found without matching '='";
-        return false;
+        return Error() << "property trigger found without matching '='";
     }
 
     std::string prop_value(prop_name.substr(equal_pos + 1));
     prop_name.erase(equal_pos);
 
     if (auto [it, inserted] = property_triggers_.emplace(prop_name, prop_value); !inserted) {
-        *err = "multiple property triggers found for same property";
-        return false;
+        return Error() << "multiple property triggers found for same property";
     }
-    return true;
+    return Success();
 }
 
-bool Action::InitTriggers(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Action::InitTriggers(const std::vector<std::string>& args) {
     const static std::string prop_str("property:");
     for (std::size_t i = 0; i < args.size(); ++i) {
         if (args[i].empty()) {
-            *err = "empty trigger is not valid";
-            return false;
+            return Error() << "empty trigger is not valid";
         }
 
         if (i % 2) {
             if (args[i] != "&&") {
-                *err = "&& is the only symbol allowed to concatenate actions";
-                return false;
+                return Error() << "&& is the only symbol allowed to concatenate actions";
             } else {
                 continue;
             }
         }
 
         if (!args[i].compare(0, prop_str.length(), prop_str)) {
-            if (!ParsePropertyTrigger(args[i], err)) {
-                return false;
+            if (auto result = ParsePropertyTrigger(args[i]); !result) {
+                return result;
             }
         } else {
             if (!event_trigger_.empty()) {
-                *err = "multiple event triggers are not allowed";
-                return false;
+                return Error() << "multiple event triggers are not allowed";
             }
 
             event_trigger_ = args[i];
         }
     }
 
-    return true;
+    return Success();
 }
 
-bool Action::InitSingleTrigger(const std::string& trigger) {
+Result<Success> Action::InitSingleTrigger(const std::string& trigger) {
     std::vector<std::string> name_vector{trigger};
-    std::string err;
-    bool ret = InitTriggers(name_vector, &err);
-    if (!ret) {
-        LOG(ERROR) << "InitSingleTrigger failed due to: " << err;
+    if (auto result = InitTriggers(name_vector); !result) {
+        return Error() << "InitTriggers() failed: " << result.error();
     }
-    return ret;
+    return Success();
 }
 
 // This function checks that all property triggers are satisfied, that is
@@ -264,7 +253,8 @@
     auto action = std::make_unique<Action>(true, "<Builtin Action>", 0);
     std::vector<std::string> name_vector{name};
 
-    if (!action->InitSingleTrigger(name)) {
+    if (auto result = action->InitSingleTrigger(name); !result) {
+        LOG(ERROR) << "Cannot queue BuiltinAction for " << name << ": " << result.error();
         return;
     }
 
@@ -333,25 +323,25 @@
     current_command_ = 0;
 }
 
-bool ActionParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
-                                int line, std::string* err) {
+Result<Success> ActionParser::ParseSection(std::vector<std::string>&& args,
+                                           const std::string& filename, int line) {
     std::vector<std::string> triggers(args.begin() + 1, args.end());
     if (triggers.size() < 1) {
-        *err = "actions must have a trigger";
-        return false;
+        return Error() << "Actions must have a trigger";
     }
 
     auto action = std::make_unique<Action>(false, filename, line);
-    if (!action->InitTriggers(triggers, err)) {
-        return false;
+
+    if (auto result = action->InitTriggers(triggers); !result) {
+        return Error() << "InitTriggers() failed: " << result.error();
     }
 
     action_ = std::move(action);
-    return true;
+    return Success();
 }
 
-bool ActionParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) {
-    return action_ ? action_->AddCommand(std::move(args), line, err) : false;
+Result<Success> ActionParser::ParseLineSection(std::vector<std::string>&& args, int line) {
+    return action_ ? action_->AddCommand(std::move(args), line) : Success();
 }
 
 void ActionParser::EndSection() {
diff --git a/init/action.h b/init/action.h
index 50cae71..d977f82 100644
--- a/init/action.h
+++ b/init/action.h
@@ -26,6 +26,7 @@
 #include "builtins.h"
 #include "keyword_map.h"
 #include "parser.h"
+#include "result.h"
 
 namespace android {
 namespace init {
@@ -34,7 +35,7 @@
   public:
     Command(BuiltinFunction f, const std::vector<std::string>& args, int line);
 
-    int InvokeFunc() const;
+    Result<Success> InvokeFunc() const;
     std::string BuildCommandString() const;
 
     int line() const { return line_; }
@@ -53,10 +54,10 @@
   public:
     explicit Action(bool oneshot, const std::string& filename, int line);
 
-    bool AddCommand(const std::vector<std::string>& args, int line, std::string* err);
+    Result<Success> AddCommand(const std::vector<std::string>& args, int line);
     void AddCommand(BuiltinFunction f, const std::vector<std::string>& args, int line);
-    bool InitTriggers(const std::vector<std::string>& args, std::string* err);
-    bool InitSingleTrigger(const std::string& trigger);
+    Result<Success> InitTriggers(const std::vector<std::string>& args);
+    Result<Success> InitSingleTrigger(const std::string& trigger);
     std::size_t NumCommands() const;
     void ExecuteOneCommand(std::size_t command) const;
     void ExecuteAllCommands() const;
@@ -78,7 +79,7 @@
     void ExecuteCommand(const Command& command) const;
     bool CheckPropertyTriggers(const std::string& name = "",
                                const std::string& value = "") const;
-    bool ParsePropertyTrigger(const std::string& trigger, std::string* err);
+    Result<Success> ParsePropertyTrigger(const std::string& trigger);
 
     std::map<std::string, std::string> property_triggers_;
     std::string event_trigger_;
@@ -120,9 +121,9 @@
   public:
     ActionParser(ActionManager* action_manager)
         : action_manager_(action_manager), action_(nullptr) {}
-    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;
+    Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+                                 int line) override;
+    Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
     void EndSection() override;
 
   private:
diff --git a/init/bootchart.cpp b/init/bootchart.cpp
index 4727f92..ec84317 100644
--- a/init/bootchart.cpp
+++ b/init/bootchart.cpp
@@ -163,37 +163,37 @@
   LOG(INFO) << "Bootcharting finished";
 }
 
-static int do_bootchart_start() {
-  // We don't care about the content, but we do care that /data/bootchart/enabled actually exists.
-  std::string start;
-  if (!android::base::ReadFileToString("/data/bootchart/enabled", &start)) {
-    LOG(VERBOSE) << "Not bootcharting";
-    return 0;
-  }
+static Result<Success> do_bootchart_start() {
+    // We don't care about the content, but we do care that /data/bootchart/enabled actually exists.
+    std::string start;
+    if (!android::base::ReadFileToString("/data/bootchart/enabled", &start)) {
+        LOG(VERBOSE) << "Not bootcharting";
+        return Success();
+    }
 
-  g_bootcharting_thread = new std::thread(bootchart_thread_main);
-  return 0;
+    g_bootcharting_thread = new std::thread(bootchart_thread_main);
+    return Success();
 }
 
-static int do_bootchart_stop() {
-  if (!g_bootcharting_thread) return 0;
+static Result<Success> do_bootchart_stop() {
+    if (!g_bootcharting_thread) return Success();
 
-  // Tell the worker thread it's time to quit.
-  {
-    std::lock_guard<std::mutex> lock(g_bootcharting_finished_mutex);
-    g_bootcharting_finished = true;
-    g_bootcharting_finished_cv.notify_one();
-  }
+    // Tell the worker thread it's time to quit.
+    {
+        std::lock_guard<std::mutex> lock(g_bootcharting_finished_mutex);
+        g_bootcharting_finished = true;
+        g_bootcharting_finished_cv.notify_one();
+    }
 
-  g_bootcharting_thread->join();
-  delete g_bootcharting_thread;
-  g_bootcharting_thread = nullptr;
-  return 0;
+    g_bootcharting_thread->join();
+    delete g_bootcharting_thread;
+    g_bootcharting_thread = nullptr;
+    return Success();
 }
 
-int do_bootchart(const std::vector<std::string>& args) {
-  if (args[1] == "start") return do_bootchart_start();
-  return do_bootchart_stop();
+Result<Success> do_bootchart(const std::vector<std::string>& args) {
+    if (args[1] == "start") return do_bootchart_start();
+    return do_bootchart_stop();
 }
 
 }  // namespace init
diff --git a/init/bootchart.h b/init/bootchart.h
index e4f7b59..f614f71 100644
--- a/init/bootchart.h
+++ b/init/bootchart.h
@@ -20,10 +20,12 @@
 #include <string>
 #include <vector>
 
+#include "result.h"
+
 namespace android {
 namespace init {
 
-int do_bootchart(const std::vector<std::string>& args);
+Result<Success> do_bootchart(const std::vector<std::string>& args);
 
 }  // namespace init
 }  // namespace android
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 5335608..f807343 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -19,6 +19,7 @@
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <fts.h>
 #include <linux/loop.h>
 #include <linux/module.h>
 #include <mntent.h>
@@ -77,47 +78,13 @@
 
 static constexpr std::chrono::nanoseconds kCommandRetryTimeout = 5s;
 
-static int insmod(const char *filename, const char *options, int flags) {
-    unique_fd fd(TEMP_FAILURE_RETRY(open(filename, O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
-    if (fd == -1) {
-        PLOG(ERROR) << "insmod: open(\"" << filename << "\") failed";
-        return -1;
-    }
-    int rc = syscall(__NR_finit_module, fd.get(), options, flags);
-    if (rc == -1) {
-        PLOG(ERROR) << "finit_module for \"" << filename << "\" failed";
-    }
-    return rc;
-}
-
-static int __ifupdown(const char *interface, int up) {
-    struct ifreq ifr;
-
-    strlcpy(ifr.ifr_name, interface, IFNAMSIZ);
-
-    unique_fd s(TEMP_FAILURE_RETRY(socket(AF_INET, SOCK_DGRAM, 0)));
-    if (s < 0) return -1;
-
-    int ret = ioctl(s, SIOCGIFFLAGS, &ifr);
-    if (ret < 0) return ret;
-
-    if (up) {
-        ifr.ifr_flags |= IFF_UP;
-    } else {
-        ifr.ifr_flags &= ~IFF_UP;
-    }
-
-    return ioctl(s, SIOCSIFFLAGS, &ifr);
-}
-
-static int reboot_into_recovery(const std::vector<std::string>& options) {
+static Result<Success> reboot_into_recovery(const std::vector<std::string>& options) {
     std::string err;
     if (!write_bootloader_message(options, &err)) {
-        LOG(ERROR) << "failed to set bootloader message: " << err;
-        return -1;
+        return Error() << "Failed to set bootloader message: " << err;
     }
     property_set("sys.powerctl", "reboot,recovery");
-    return 0;
+    return Success();
 }
 
 template <typename F>
@@ -127,90 +94,106 @@
     }
 }
 
-static int do_class_start(const std::vector<std::string>& args) {
+static Result<Success> do_class_start(const std::vector<std::string>& args) {
     // Starting a class does not start services which are explicitly disabled.
     // They must  be started individually.
     ForEachServiceInClass(args[1], &Service::StartIfNotDisabled);
-    return 0;
+    return Success();
 }
 
-static int do_class_stop(const std::vector<std::string>& args) {
+static Result<Success> do_class_stop(const std::vector<std::string>& args) {
     ForEachServiceInClass(args[1], &Service::Stop);
-    return 0;
+    return Success();
 }
 
-static int do_class_reset(const std::vector<std::string>& args) {
+static Result<Success> do_class_reset(const std::vector<std::string>& args) {
     ForEachServiceInClass(args[1], &Service::Reset);
-    return 0;
+    return Success();
 }
 
-static int do_class_restart(const std::vector<std::string>& args) {
+static Result<Success> do_class_restart(const std::vector<std::string>& args) {
     ForEachServiceInClass(args[1], &Service::Restart);
-    return 0;
+    return Success();
 }
 
-static int do_domainname(const std::vector<std::string>& args) {
-    std::string err;
-    if (!WriteFile("/proc/sys/kernel/domainname", args[1], &err)) {
-        LOG(ERROR) << err;
-        return -1;
+static Result<Success> do_domainname(const std::vector<std::string>& args) {
+    if (auto result = WriteFile("/proc/sys/kernel/domainname", args[1]); !result) {
+        return Error() << "Unable to write to /proc/sys/kernel/domainname: " << result.error();
     }
-    return 0;
+    return Success();
 }
 
-static int do_enable(const std::vector<std::string>& args) {
+static Result<Success> do_enable(const std::vector<std::string>& args) {
     Service* svc = ServiceList::GetInstance().FindService(args[1]);
-    if (!svc) {
-        return -1;
-    }
-    return svc->Enable();
+    if (!svc) return Error() << "Could not find service";
+
+    if (!svc->Enable()) return Error() << "Could not enable service";
+
+    return Success();
 }
 
-static int do_exec(const std::vector<std::string>& args) {
+static Result<Success> do_exec(const std::vector<std::string>& args) {
     auto service = Service::MakeTemporaryOneshotService(args);
     if (!service) {
-        LOG(ERROR) << "Failed to create exec service: " << android::base::Join(args, " ");
-        return -1;
+        return Error() << "Could not create exec service";
     }
     if (!service->ExecStart()) {
-        LOG(ERROR) << "Failed to Start exec service";
-        return -1;
+        return Error() << "Could not start exec service";
     }
+
     ServiceList::GetInstance().AddService(std::move(service));
-    return 0;
+    return Success();
 }
 
-static int do_exec_start(const std::vector<std::string>& args) {
+static Result<Success> do_exec_start(const std::vector<std::string>& args) {
     Service* service = ServiceList::GetInstance().FindService(args[1]);
     if (!service) {
-        LOG(ERROR) << "ExecStart(" << args[1] << "): Service not found";
-        return -1;
+        return Error() << "Service not found";
     }
+
     if (!service->ExecStart()) {
-        LOG(ERROR) << "ExecStart(" << args[1] << "): Could not start Service";
-        return -1;
+        return Error() << "Could not start Service";
     }
-    return 0;
+
+    return Success();
 }
 
-static int do_export(const std::vector<std::string>& args) {
-    return add_environment(args[1].c_str(), args[2].c_str());
-}
-
-static int do_hostname(const std::vector<std::string>& args) {
-    std::string err;
-    if (!WriteFile("/proc/sys/kernel/hostname", args[1], &err)) {
-        LOG(ERROR) << err;
-        return -1;
+static Result<Success> do_export(const std::vector<std::string>& args) {
+    if (!add_environment(args[1].c_str(), args[2].c_str())) {
+        return Error();
     }
-    return 0;
+    return Success();
 }
 
-static int do_ifup(const std::vector<std::string>& args) {
-    return __ifupdown(args[1].c_str(), 1);
+static Result<Success> do_hostname(const std::vector<std::string>& args) {
+    if (auto result = WriteFile("/proc/sys/kernel/hostname", args[1]); !result) {
+        return Error() << "Unable to write to /proc/sys/kernel/hostname: " << result.error();
+    }
+    return Success();
 }
 
-static int do_insmod(const std::vector<std::string>& args) {
+static Result<Success> do_ifup(const std::vector<std::string>& args) {
+    struct ifreq ifr;
+
+    strlcpy(ifr.ifr_name, args[1].c_str(), IFNAMSIZ);
+
+    unique_fd s(TEMP_FAILURE_RETRY(socket(AF_INET, SOCK_DGRAM, 0)));
+    if (s < 0) return ErrnoError() << "opening socket failed";
+
+    if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) {
+        return ErrnoError() << "ioctl(..., SIOCGIFFLAGS, ...) failed";
+    }
+
+    ifr.ifr_flags |= IFF_UP;
+
+    if (ioctl(s, SIOCSIFFLAGS, &ifr) < 0) {
+        return ErrnoError() << "ioctl(..., SIOCSIFFLAGS, ...) failed";
+    }
+
+    return Success();
+}
+
+static Result<Success> do_insmod(const std::vector<std::string>& args) {
     int flags = 0;
     auto it = args.begin() + 1;
 
@@ -221,53 +204,56 @@
 
     std::string filename = *it++;
     std::string options = android::base::Join(std::vector<std::string>(it, args.end()), ' ');
-    return insmod(filename.c_str(), options.c_str(), flags);
+
+    unique_fd fd(TEMP_FAILURE_RETRY(open(filename.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
+    if (fd == -1) return ErrnoError() << "open(\"" << filename << "\") failed";
+
+    int rc = syscall(__NR_finit_module, fd.get(), options.c_str(), flags);
+    if (rc == -1) return ErrnoError() << "finit_module for \"" << filename << "\" failed";
+
+    return Success();
 }
 
-static int do_mkdir(const std::vector<std::string>& args) {
+// mkdir <path> [mode] [owner] [group]
+static Result<Success> do_mkdir(const std::vector<std::string>& args) {
     mode_t mode = 0755;
-    int ret;
-
-    /* mkdir <path> [mode] [owner] [group] */
-
     if (args.size() >= 3) {
         mode = std::strtoul(args[2].c_str(), 0, 8);
     }
 
-    ret = make_dir(args[1].c_str(), mode, sehandle);
-    /* chmod in case the directory already exists */
-    if (ret == -1 && errno == EEXIST) {
-        ret = fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW);
-    }
-    if (ret == -1) {
-        return -errno;
+    if (!make_dir(args[1], mode)) {
+        /* chmod in case the directory already exists */
+        if (errno == EEXIST) {
+            if (fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW) == -1) {
+                return ErrnoError() << "fchmodat() failed";
+            }
+        } else {
+            return ErrnoError() << "mkdir() failed";
+        }
     }
 
     if (args.size() >= 4) {
-        uid_t uid;
-        std::string decode_uid_err;
-        if (!DecodeUid(args[3], &uid, &decode_uid_err)) {
-            LOG(ERROR) << "Unable to find UID for '" << args[3] << "': " << decode_uid_err;
-            return -1;
+        auto uid = DecodeUid(args[3]);
+        if (!uid) {
+            return Error() << "Unable to decode UID for '" << args[3] << "': " << uid.error();
         }
-        gid_t gid = -1;
+        Result<gid_t> gid = -1;
 
         if (args.size() == 5) {
-            if (!DecodeUid(args[4], &gid, &decode_uid_err)) {
-                LOG(ERROR) << "Unable to find GID for '" << args[3] << "': " << decode_uid_err;
-                return -1;
+            gid = DecodeUid(args[4]);
+            if (!gid) {
+                return Error() << "Unable to decode GID for '" << args[3] << "': " << gid.error();
             }
         }
 
-        if (lchown(args[1].c_str(), uid, gid) == -1) {
-            return -errno;
+        if (lchown(args[1].c_str(), *uid, *gid) == -1) {
+            return ErrnoError() << "lchown failed";
         }
 
         /* chown may have cleared S_ISUID and S_ISGID, chmod again */
         if (mode & (S_ISUID | S_ISGID)) {
-            ret = fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW);
-            if (ret == -1) {
-                return -errno;
+            if (fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW) == -1) {
+                return ErrnoError() << "fchmodat failed";
             }
         }
     }
@@ -278,15 +264,18 @@
                 "--prompt_and_wipe_data",
                 "--reason=set_policy_failed:"s + args[1]};
             reboot_into_recovery(options);
-            return -1;
+            return Error() << "reboot into recovery failed";
         }
     }
-    return 0;
+    return Success();
 }
 
 /* umount <path> */
-static int do_umount(const std::vector<std::string>& args) {
-  return umount(args[1].c_str());
+static Result<Success> do_umount(const std::vector<std::string>& args) {
+    if (umount(args[1].c_str()) < 0) {
+        return ErrnoError() << "umount() failed";
+    }
+    return Success();
 }
 
 static struct {
@@ -314,7 +303,7 @@
 #define DATA_MNT_POINT "/data"
 
 /* mount <type> <device> <path> <flags ...> <options> */
-static int do_mount(const std::vector<std::string>& args) {
+static Result<Success> do_mount(const std::vector<std::string>& args) {
     const char* options = nullptr;
     unsigned flags = 0;
     bool wait = false;
@@ -345,12 +334,12 @@
     if (android::base::StartsWith(source, "loop@")) {
         int mode = (flags & MS_RDONLY) ? O_RDONLY : O_RDWR;
         unique_fd fd(TEMP_FAILURE_RETRY(open(source + 5, mode | O_CLOEXEC)));
-        if (fd < 0) return -1;
+        if (fd < 0) return ErrnoError() << "open(" << source + 5 << ", " << mode << ") failed";
 
         for (size_t n = 0;; n++) {
             std::string tmp = android::base::StringPrintf("/dev/block/loop%zu", n);
             unique_fd loop(TEMP_FAILURE_RETRY(open(tmp.c_str(), mode | O_CLOEXEC)));
-            if (loop < 0) return -1;
+            if (loop < 0) return ErrnoError() << "open(" << tmp << ", " << mode << ") failed";
 
             loop_info info;
             /* if it is a blank loop device */
@@ -359,26 +348,24 @@
                 if (ioctl(loop, LOOP_SET_FD, fd.get()) >= 0) {
                     if (mount(tmp.c_str(), target, system, flags, options) < 0) {
                         ioctl(loop, LOOP_CLR_FD, 0);
-                        return -1;
+                        return ErrnoError() << "mount() failed";
                     }
-                    return 0;
+                    return Success();
                 }
             }
         }
 
-        LOG(ERROR) << "out of loopback devices";
-        return -1;
+        return Error() << "out of loopback devices";
     } else {
         if (wait)
             wait_for_file(source, kCommandRetryTimeout);
         if (mount(source, target, system, flags, options) < 0) {
-            return -1;
+            return ErrnoError() << "mount() failed";
         }
 
     }
 
-    return 0;
-
+    return Success();
 }
 
 /* Imports .rc files from the specified paths. Default ones are applied if none is given.
@@ -410,9 +397,7 @@
  *
  *  Call fs_mgr_mount_all() to mount the given fstab
  */
-static int mount_fstab(const char* fstabfile, int mount_mode) {
-    int ret = -1;
-
+static Result<int> mount_fstab(const char* fstabfile, int mount_mode) {
     /*
      * Call fs_mgr_mount_all() to mount all filesystems.  We fork(2) and
      * do the call in the child to provide protection to the main init
@@ -430,9 +415,9 @@
         }
 
         if (WIFEXITED(status)) {
-            ret = WEXITSTATUS(status);
+            return WEXITSTATUS(status);
         } else {
-            ret = -1;
+            return Error() << "child aborted";
         }
     } else if (pid == 0) {
         /* child, call fs_mgr_mount_all() */
@@ -449,10 +434,8 @@
         }
         _exit(child_ret);
     } else {
-        /* fork failed, return an error */
-        return -1;
+        return Error() << "fork() failed";
     }
-    return ret;
 }
 
 /* Queue event based on fs_mgr return code.
@@ -464,29 +447,33 @@
  *
  * return code is processed based on input code
  */
-static int queue_fs_event(int code) {
-    int ret = code;
+static Result<Success> queue_fs_event(int code) {
     if (code == FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION) {
         ActionManager::GetInstance().QueueEventTrigger("encrypt");
+        return Success();
     } else if (code == FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED) {
         property_set("ro.crypto.state", "encrypted");
         property_set("ro.crypto.type", "block");
         ActionManager::GetInstance().QueueEventTrigger("defaultcrypto");
+        return Success();
     } else if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {
         property_set("ro.crypto.state", "unencrypted");
         ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
+        return Success();
     } else if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE) {
         property_set("ro.crypto.state", "unsupported");
         ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
+        return Success();
     } else if (code == FS_MGR_MNTALL_DEV_NEEDS_RECOVERY) {
         /* Setup a wipe via recovery, and reboot into recovery */
         PLOG(ERROR) << "fs_mgr_mount_all suggested recovery, so wiping data via recovery.";
         const std::vector<std::string> options = {"--wipe_data", "--reason=fs_mgr_mount_all" };
-        ret = reboot_into_recovery(options);
+        reboot_into_recovery(options);
+        return Error() << "reboot_into_recovery() failed";
         /* If reboot worked, there is no return. */
     } else if (code == FS_MGR_MNTALL_DEV_FILE_ENCRYPTED) {
         if (e4crypt_install_keyring()) {
-            return -1;
+            return Error() << "e4crypt_install_keyring() failed";
         }
         property_set("ro.crypto.state", "encrypted");
         property_set("ro.crypto.type", "file");
@@ -494,12 +481,13 @@
         // Although encrypted, we have device key, so we do not need to
         // do anything different from the nonencrypted case.
         ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
+        return Success();
     } else if (code > 0) {
-        PLOG(ERROR) << "fs_mgr_mount_all returned unexpected error " << code;
+        Error() << "fs_mgr_mount_all() returned unexpected error " << code;
     }
     /* else ... < 0: error */
 
-    return ret;
+    return Error() << "Invalid code: " << code;
 }
 
 /* mount_all <fstab> [ <path> ]* [--<options>]*
@@ -507,7 +495,7 @@
  * This function might request a reboot, in which case it will
  * not return.
  */
-static int do_mount_all(const std::vector<std::string>& args) {
+static Result<Success> do_mount_all(const std::vector<std::string>& args) {
     std::size_t na = 0;
     bool import_rc = true;
     bool queue_event = true;
@@ -532,7 +520,10 @@
 
     std::string prop_name = "ro.boottime.init.mount_all."s + prop_post_fix;
     android::base::Timer t;
-    int ret =  mount_fstab(fstabfile, mount_mode);
+    auto mount_fstab_return_code = mount_fstab(fstabfile, mount_mode);
+    if (!mount_fstab_return_code) {
+        return Error() << "mount_fstab() failed " << mount_fstab_return_code.error();
+    }
     property_set(prop_name, std::to_string(t.duration().count()));
 
     if (import_rc) {
@@ -543,13 +534,16 @@
     if (queue_event) {
         /* queue_fs_event will queue event based on mount_fstab return code
          * and return processed return code*/
-        ret = queue_fs_event(ret);
+        auto queue_fs_result = queue_fs_event(*mount_fstab_return_code);
+        if (!queue_fs_result) {
+            return Error() << "queue_fs_event() failed: " << queue_fs_result.error();
+        }
     }
 
-    return ret;
+    return Success();
 }
 
-static int do_swapon_all(const std::vector<std::string>& args) {
+static Result<Success> do_swapon_all(const std::vector<std::string>& args) {
     struct fstab *fstab;
     int ret;
 
@@ -557,89 +551,103 @@
     ret = fs_mgr_swapon_all(fstab);
     fs_mgr_free_fstab(fstab);
 
-    return ret;
+    if (ret != 0) return Error() << "fs_mgr_swapon_all() failed";
+    return Success();
 }
 
-static int do_setprop(const std::vector<std::string>& args) {
+static Result<Success> do_setprop(const std::vector<std::string>& args) {
     property_set(args[1], args[2]);
-    return 0;
+    return Success();
 }
 
-static int do_setrlimit(const std::vector<std::string>& args) {
-    struct rlimit limit;
+static Result<Success> do_setrlimit(const std::vector<std::string>& args) {
     int resource;
-    if (android::base::ParseInt(args[1], &resource) &&
-        android::base::ParseUint(args[2], &limit.rlim_cur) &&
-        android::base::ParseUint(args[3], &limit.rlim_max)) {
-        return setrlimit(resource, &limit);
+    if (!android::base::ParseInt(args[1], &resource)) {
+        return Error() << "unable to parse resource, " << args[1];
     }
-    LOG(WARNING) << "ignoring setrlimit " << args[1] << " " << args[2] << " " << args[3];
-    return -1;
+
+    struct rlimit limit;
+    if (!android::base::ParseUint(args[2], &limit.rlim_cur)) {
+        return Error() << "unable to parse rlim_cur, " << args[2];
+    }
+    if (!android::base::ParseUint(args[3], &limit.rlim_max)) {
+        return Error() << "unable to parse rlim_max, " << args[3];
+    }
+
+    if (setrlimit(resource, &limit) == -1) {
+        return ErrnoError() << "setrlimit failed";
+    }
+    return Success();
 }
 
-static int do_start(const std::vector<std::string>& args) {
+static Result<Success> do_start(const std::vector<std::string>& args) {
     Service* svc = ServiceList::GetInstance().FindService(args[1]);
-    if (!svc) {
-        LOG(ERROR) << "do_start: Service " << args[1] << " not found";
-        return -1;
-    }
-    if (!svc->Start())
-        return -1;
-    return 0;
+    if (!svc) return Error() << "service " << args[1] << " not found";
+    if (!svc->Start()) return Error() << "failed to start service";
+    return Success();
 }
 
-static int do_stop(const std::vector<std::string>& args) {
+static Result<Success> do_stop(const std::vector<std::string>& args) {
     Service* svc = ServiceList::GetInstance().FindService(args[1]);
-    if (!svc) {
-        LOG(ERROR) << "do_stop: Service " << args[1] << " not found";
-        return -1;
-    }
+    if (!svc) return Error() << "service " << args[1] << " not found";
     svc->Stop();
-    return 0;
+    return Success();
 }
 
-static int do_restart(const std::vector<std::string>& args) {
+static Result<Success> do_restart(const std::vector<std::string>& args) {
     Service* svc = ServiceList::GetInstance().FindService(args[1]);
-    if (!svc) {
-        LOG(ERROR) << "do_restart: Service " << args[1] << " not found";
-        return -1;
-    }
+    if (!svc) return Error() << "service " << args[1] << " not found";
     svc->Restart();
-    return 0;
+    return Success();
 }
 
-static int do_trigger(const std::vector<std::string>& args) {
+static Result<Success> do_trigger(const std::vector<std::string>& args) {
     ActionManager::GetInstance().QueueEventTrigger(args[1]);
-    return 0;
+    return Success();
 }
 
-static int do_symlink(const std::vector<std::string>& args) {
-    return symlink(args[1].c_str(), args[2].c_str());
-}
-
-static int do_rm(const std::vector<std::string>& args) {
-    return unlink(args[1].c_str());
-}
-
-static int do_rmdir(const std::vector<std::string>& args) {
-    return rmdir(args[1].c_str());
-}
-
-static int do_sysclktz(const std::vector<std::string>& args) {
-    struct timezone tz = {};
-    if (android::base::ParseInt(args[1], &tz.tz_minuteswest) && settimeofday(NULL, &tz) != -1) {
-        return 0;
+static Result<Success> do_symlink(const std::vector<std::string>& args) {
+    if (symlink(args[1].c_str(), args[2].c_str()) < 0) {
+        return ErrnoError() << "symlink() failed";
     }
-    return -1;
+    return Success();
 }
 
-static int do_verity_load_state(const std::vector<std::string>& args) {
+static Result<Success> do_rm(const std::vector<std::string>& args) {
+    if (unlink(args[1].c_str()) < 0) {
+        return ErrnoError() << "unlink() failed";
+    }
+    return Success();
+}
+
+static Result<Success> do_rmdir(const std::vector<std::string>& args) {
+    if (rmdir(args[1].c_str()) < 0) {
+        return ErrnoError() << "rmdir() failed";
+    }
+    return Success();
+}
+
+static Result<Success> do_sysclktz(const std::vector<std::string>& args) {
+    struct timezone tz = {};
+    if (!android::base::ParseInt(args[1], &tz.tz_minuteswest)) {
+        return Error() << "Unable to parse mins_west_of_gmt";
+    }
+
+    if (settimeofday(nullptr, &tz) == -1) {
+        return ErrnoError() << "settimeofday() failed";
+    }
+    return Success();
+}
+
+static Result<Success> do_verity_load_state(const std::vector<std::string>& args) {
     int mode = -1;
     bool loaded = fs_mgr_load_verity_state(&mode);
     if (loaded && mode != VERITY_MODE_DEFAULT) {
         ActionManager::GetInstance().QueueEventTrigger("verity-logging");
     }
-    return loaded ? 0 : 1;
+    if (!loaded) return Error() << "Could not load verity state";
+
+    return Success();
 }
 
 static void verity_update_property(fstab_rec *fstab, const char *mount_point,
@@ -647,55 +655,113 @@
     property_set("partition."s + mount_point + ".verified", std::to_string(mode));
 }
 
-static int do_verity_update_state(const std::vector<std::string>& args) {
-    return fs_mgr_update_verity_state(verity_update_property) ? 0 : 1;
+static Result<Success> do_verity_update_state(const std::vector<std::string>& args) {
+    if (!fs_mgr_update_verity_state(verity_update_property)) {
+        return Error() << "fs_mgr_update_verity_state() failed";
+    }
+    return Success();
 }
 
-static int do_write(const std::vector<std::string>& args) {
-    std::string err;
-    if (!WriteFile(args[1], args[2], &err)) {
-        LOG(ERROR) << err;
-        return -1;
+static Result<Success> do_write(const std::vector<std::string>& args) {
+    if (auto result = WriteFile(args[1], args[2]); !result) {
+        return Error() << "Unable to write to file '" << args[1] << "': " << result.error();
     }
-    return 0;
+
+    return Success();
 }
 
-static int do_copy(const std::vector<std::string>& args) {
-    std::string data;
-    std::string err;
-    if (!ReadFile(args[1], &data, &err)) {
-        LOG(ERROR) << err;
-        return -1;
+static Result<Success> do_readahead(const std::vector<std::string>& args) {
+    struct stat sb;
+
+    if (stat(args[1].c_str(), &sb)) {
+        return ErrnoError() << "Error opening " << args[1];
     }
-    if (!WriteFile(args[2], data, &err)) {
-        LOG(ERROR) << err;
-        return -1;
+
+    // We will do readahead in a forked process in order not to block init
+    // since it may block while it reads the
+    // filesystem metadata needed to locate the requested blocks.  This
+    // occurs frequently with ext[234] on large files using indirect blocks
+    // instead of extents, giving the appearance that the call blocks until
+    // the requested data has been read.
+    pid_t pid = fork();
+    if (pid == 0) {
+        android::base::Timer t;
+        if (S_ISREG(sb.st_mode)) {
+            android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(args[1].c_str(), O_RDONLY)));
+            if (fd == -1) {
+                PLOG(ERROR) << "Error opening file: " << args[1];
+                _exit(EXIT_FAILURE);
+            }
+            if (readahead(fd, 0, std::numeric_limits<size_t>::max())) {
+                PLOG(ERROR) << "Error readahead file: " << args[1];
+                _exit(EXIT_FAILURE);
+            }
+        } else if (S_ISDIR(sb.st_mode)) {
+            char* paths[] = {const_cast<char*>(args[1].data()), nullptr};
+            std::unique_ptr<FTS, decltype(&fts_close)> fts(
+                fts_open(paths, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr), fts_close);
+            if (!fts) {
+                PLOG(ERROR) << "Error opening directory: " << args[1];
+                _exit(EXIT_FAILURE);
+            }
+            // Traverse the entire hierarchy and do readahead
+            for (FTSENT* ftsent = fts_read(fts.get()); ftsent != nullptr;
+                 ftsent = fts_read(fts.get())) {
+                if (ftsent->fts_info & FTS_F) {
+                    android::base::unique_fd fd(
+                        TEMP_FAILURE_RETRY(open(ftsent->fts_accpath, O_RDONLY)));
+                    if (fd == -1) {
+                        PLOG(ERROR) << "Error opening file: " << args[1];
+                        continue;
+                    }
+                    if (readahead(fd, 0, std::numeric_limits<size_t>::max())) {
+                        PLOG(ERROR) << "Unable to readahead on file: " << ftsent->fts_accpath;
+                    }
+                }
+            }
+        }
+        LOG(INFO) << "Readahead " << args[1] << " took " << t;
+        _exit(0);
+    } else if (pid < 0) {
+        return ErrnoError() << "Fork failed";
     }
-    return 0;
+    return Success();
 }
 
-static int do_chown(const std::vector<std::string>& args) {
-    uid_t uid;
-    std::string decode_uid_err;
-    if (!DecodeUid(args[1], &uid, &decode_uid_err)) {
-        LOG(ERROR) << "Unable to find UID for '" << args[1] << "': " << decode_uid_err;
-        return -1;
+static Result<Success> do_copy(const std::vector<std::string>& args) {
+    auto file_contents = ReadFile(args[1]);
+    if (!file_contents) {
+        return Error() << "Could not read input file '" << args[1] << "': " << file_contents.error();
+    }
+    if (auto result = WriteFile(args[2], *file_contents); !result) {
+        return Error() << "Could not write to output file '" << args[2] << "': " << result.error();
+    }
+
+    return Success();
+}
+
+static Result<Success> do_chown(const std::vector<std::string>& args) {
+    auto uid = DecodeUid(args[1]);
+    if (!uid) {
+        return Error() << "Unable to decode UID for '" << args[1] << "': " << uid.error();
     }
 
     // GID is optional and pushes the index of path out by one if specified.
     const std::string& path = (args.size() == 4) ? args[3] : args[2];
-    gid_t gid = -1;
+    Result<gid_t> gid = -1;
 
     if (args.size() == 4) {
-        if (!DecodeUid(args[2], &gid, &decode_uid_err)) {
-            LOG(ERROR) << "Unable to find GID for '" << args[2] << "': " << decode_uid_err;
-            return -1;
+        gid = DecodeUid(args[2]);
+        if (!gid) {
+            return Error() << "Unable to decode GID for '" << args[2] << "': " << gid.error();
         }
     }
 
-    if (lchown(path.c_str(), uid, gid) == -1) return -errno;
+    if (lchown(path.c_str(), *uid, *gid) == -1) {
+        return ErrnoError() << "lchown() failed";
+    }
 
-    return 0;
+    return Success();
 }
 
 static mode_t get_mode(const char *s) {
@@ -711,15 +777,15 @@
     return mode;
 }
 
-static int do_chmod(const std::vector<std::string>& args) {
+static Result<Success> do_chmod(const std::vector<std::string>& args) {
     mode_t mode = get_mode(args[1].c_str());
     if (fchmodat(AT_FDCWD, args[2].c_str(), mode, AT_SYMLINK_NOFOLLOW) < 0) {
-        return -errno;
+        return ErrnoError() << "fchmodat() failed";
     }
-    return 0;
+    return Success();
 }
 
-static int do_restorecon(const std::vector<std::string>& args) {
+static Result<Success> do_restorecon(const std::vector<std::string>& args) {
     int ret = 0;
 
     struct flag_type {const char* name; int value;};
@@ -736,8 +802,7 @@
     for (size_t i = 1; i < args.size(); ++i) {
         if (android::base::StartsWith(args[i], "--")) {
             if (!in_flags) {
-                LOG(ERROR) << "restorecon - flags must precede paths";
-                return -1;
+                return Error() << "flags must precede paths";
             }
             bool found = false;
             for (size_t j = 0; flags[j].name; ++j) {
@@ -748,26 +813,27 @@
                 }
             }
             if (!found) {
-                LOG(ERROR) << "restorecon - bad flag " << args[i];
-                return -1;
+                return Error() << "bad flag " << args[i];
             }
         } else {
             in_flags = false;
             if (selinux_android_restorecon(args[i].c_str(), flag) < 0) {
-                ret = -errno;
+                ret = errno;
             }
         }
     }
-    return ret;
+
+    if (ret) return ErrnoError() << "selinux_android_restorecon() failed";
+    return Success();
 }
 
-static int do_restorecon_recursive(const std::vector<std::string>& args) {
+static Result<Success> do_restorecon_recursive(const std::vector<std::string>& args) {
     std::vector<std::string> non_const_args(args);
     non_const_args.insert(std::next(non_const_args.begin()), "--recursive");
     return do_restorecon(non_const_args);
 }
 
-static int do_loglevel(const std::vector<std::string>& args) {
+static Result<Success> do_loglevel(const std::vector<std::string>& args) {
     // TODO: support names instead/as well?
     int log_level = -1;
     android::base::ParseInt(args[1], &log_level);
@@ -782,88 +848,73 @@
         case 1:
         case 0: severity = android::base::FATAL; break;
         default:
-            LOG(ERROR) << "loglevel: invalid log level " << log_level;
-            return -EINVAL;
+            return Error() << "invalid log level " << log_level;
     }
     android::base::SetMinimumLogSeverity(severity);
-    return 0;
+    return Success();
 }
 
-static int do_load_persist_props(const std::vector<std::string>& args) {
+static Result<Success> do_load_persist_props(const std::vector<std::string>& args) {
     load_persist_props();
-    return 0;
+    return Success();
 }
 
-static int do_load_system_props(const std::vector<std::string>& args) {
+static Result<Success> do_load_system_props(const std::vector<std::string>& args) {
     load_system_props();
-    return 0;
+    return Success();
 }
 
-static int do_wait(const std::vector<std::string>& args) {
-    if (args.size() == 2) {
-        return wait_for_file(args[1].c_str(), kCommandRetryTimeout);
-    } else if (args.size() == 3) {
-        int timeout;
-        if (android::base::ParseInt(args[2], &timeout)) {
-            return wait_for_file(args[1].c_str(), std::chrono::seconds(timeout));
+static Result<Success> do_wait(const std::vector<std::string>& args) {
+    auto timeout = kCommandRetryTimeout;
+    if (args.size() == 3) {
+        int timeout_int;
+        if (!android::base::ParseInt(args[2], &timeout_int)) {
+            return Error() << "failed to parse timeout";
         }
+        timeout = std::chrono::seconds(timeout_int);
     }
-    return -1;
+
+    if (wait_for_file(args[1].c_str(), timeout) != 0) {
+        return Error() << "wait_for_file() failed";
+    }
+
+    return Success();
 }
 
-static int do_wait_for_prop(const std::vector<std::string>& args) {
+static Result<Success> do_wait_for_prop(const std::vector<std::string>& args) {
     const char* name = args[1].c_str();
     const char* value = args[2].c_str();
     size_t value_len = strlen(value);
 
     if (!is_legal_property_name(name)) {
-        LOG(ERROR) << "do_wait_for_prop(\"" << name << "\", \"" << value
-                   << "\") failed: bad name";
-        return -1;
+        return Error() << "is_legal_property_name(" << name << ") failed";
     }
     if (value_len >= PROP_VALUE_MAX) {
-        LOG(ERROR) << "do_wait_for_prop(\"" << name << "\", \"" << value
-                   << "\") failed: value too long";
-        return -1;
+        return Error() << "value too long";
     }
     if (!start_waiting_for_property(name, value)) {
-        LOG(ERROR) << "do_wait_for_prop(\"" << name << "\", \"" << value
-                   << "\") failed: init already in waiting";
-        return -1;
+        return Error() << "already waiting for a property";
     }
-    return 0;
-}
-
-/*
- * Callback to make a directory from the ext4 code
- */
-static int do_installkeys_ensure_dir_exists(const char* dir) {
-    if (make_dir(dir, 0700, sehandle) && errno != EEXIST) {
-        return -1;
-    }
-
-    return 0;
+    return Success();
 }
 
 static bool is_file_crypto() {
     return android::base::GetProperty("ro.crypto.type", "") == "file";
 }
 
-static int do_installkey(const std::vector<std::string>& args) {
-    if (!is_file_crypto()) {
-        return 0;
-    }
+static Result<Success> do_installkey(const std::vector<std::string>& args) {
+    if (!is_file_crypto()) return Success();
+
     auto unencrypted_dir = args[1] + e4crypt_unencrypted_folder;
-    if (do_installkeys_ensure_dir_exists(unencrypted_dir.c_str())) {
-        PLOG(ERROR) << "Failed to create " << unencrypted_dir;
-        return -1;
+    if (!make_dir(unencrypted_dir, 0700) && errno != EEXIST) {
+        return ErrnoError() << "Failed to create " << unencrypted_dir;
     }
     std::vector<std::string> exec_args = {"exec", "/system/bin/vdc", "--wait", "cryptfs",
                                           "enablefilecrypto"};
     return do_exec(exec_args);
 }
 
-static int do_init_user0(const std::vector<std::string>& args) {
+static Result<Success> do_init_user0(const std::vector<std::string>& args) {
     std::vector<std::string> exec_args = {"exec", "/system/bin/vdc", "--wait", "cryptfs",
                                           "init_user0"};
     return do_exec(exec_args);
@@ -898,6 +949,7 @@
         {"mount_all",               {1,     kMax, do_mount_all}},
         {"mount",                   {3,     kMax, do_mount}},
         {"umount",                  {1,     1,    do_umount}},
+        {"readahead",               {1,     1,    do_readahead}},
         {"restart",                 {1,     1,    do_restart}},
         {"restorecon",              {1,     kMax, do_restorecon}},
         {"restorecon_recursive",    {1,     kMax, do_restorecon_recursive}},
diff --git a/init/builtins.h b/init/builtins.h
index b110f61..f66ae19 100644
--- a/init/builtins.h
+++ b/init/builtins.h
@@ -23,11 +23,12 @@
 #include <vector>
 
 #include "keyword_map.h"
+#include "result.h"
 
 namespace android {
 namespace init {
 
-using BuiltinFunction = std::function<int(const std::vector<std::string>&)>;
+using BuiltinFunction = std::function<Result<Success>(const std::vector<std::string>&)>;
 class BuiltinFunctionMap : public KeywordMap<BuiltinFunction> {
   public:
     BuiltinFunctionMap() {}
diff --git a/init/descriptors.cpp b/init/descriptors.cpp
index 0cb639a..cc5b948 100644
--- a/init/descriptors.cpp
+++ b/init/descriptors.cpp
@@ -86,8 +86,7 @@
     int flags =
         ((types[0] == "stream" ? SOCK_STREAM : (types[0] == "dgram" ? SOCK_DGRAM : SOCK_SEQPACKET)));
     bool passcred = types.size() > 1 && types[1] == "passcred";
-    return CreateSocket(name().c_str(), flags, passcred, perm(), uid(), gid(), context.c_str(),
-                        sehandle);
+    return CreateSocket(name().c_str(), flags, passcred, perm(), uid(), gid(), context.c_str());
 }
 
 const std::string SocketInfo::key() const {
diff --git a/init/devices.cpp b/init/devices.cpp
index 13cf991..d59f53c 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -30,6 +30,7 @@
 #include <selinux/android.h>
 #include <selinux/selinux.h>
 
+#include "selinux.h"
 #include "ueventd.h"
 #include "util.h"
 
@@ -224,18 +225,13 @@
     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);
+    std::string secontext;
+    if (!SelabelLookupFileContextBestMatch(path, links, mode, &secontext)) {
+        PLOG(ERROR) << "Device '" << path << "' not created; cannot find SELinux label";
+        return;
+    }
+    if (!secontext.empty()) {
+        setfscreatecon(secontext.c_str());
     }
 
     dev_t dev = makedev(major, minor);
@@ -250,7 +246,7 @@
     }
     /* 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) {
+    if (mknod(path.c_str(), mode, dev) && (errno == EEXIST) && !secontext.empty()) {
         char* fcon = nullptr;
         int rc = lgetfilecon(path.c_str(), &fcon);
         if (rc < 0) {
@@ -258,10 +254,10 @@
             goto out;
         }
 
-        bool different = strcmp(fcon, secontext) != 0;
+        bool different = fcon != secontext;
         freecon(fcon);
 
-        if (different && lsetfilecon(path.c_str(), secontext)) {
+        if (different && lsetfilecon(path.c_str(), secontext.c_str())) {
             PLOG(ERROR) << "Cannot set '" << secontext << "' SELinux label on '" << path
                         << "' device";
         }
@@ -273,8 +269,7 @@
         PLOG(FATAL) << "setegid(AID_ROOT) failed";
     }
 
-    if (secontext) {
-        freecon(secontext);
+    if (!secontext.empty()) {
         setfscreatecon(nullptr);
     }
 }
@@ -351,7 +346,7 @@
     if (action == "add") {
         MakeDevice(devpath, block, major, minor, links);
         for (const auto& link : links) {
-            if (mkdir_recursive(Dirname(link), 0755, sehandle_)) {
+            if (!mkdir_recursive(Dirname(link), 0755)) {
                 PLOG(ERROR) << "Failed to create directory " << Dirname(link);
             }
 
@@ -415,7 +410,7 @@
         devpath = "/dev/" + Basename(uevent.path);
     }
 
-    mkdir_recursive(Dirname(devpath), 0755, sehandle_);
+    mkdir_recursive(Dirname(devpath), 0755);
 
     HandleDevice(uevent.action, devpath, block, uevent.major, uevent.minor, links);
 }
@@ -426,7 +421,6 @@
     : dev_permissions_(std::move(dev_permissions)),
       sysfs_permissions_(std::move(sysfs_permissions)),
       subsystems_(std::move(subsystems)),
-      sehandle_(selinux_android_file_context_handle()),
       skip_restorecon_(skip_restorecon),
       sysfs_mount_point_("/sys") {}
 
diff --git a/init/devices.h b/init/devices.h
index c64f5fb..dd44337 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -124,7 +124,6 @@
     std::vector<Permissions> dev_permissions_;
     std::vector<SysfsPermissions> sysfs_permissions_;
     std::vector<Subsystem> subsystems_;
-    selabel_handle* sehandle_;
     bool skip_restorecon_;
     std::string sysfs_mount_point_;
 };
diff --git a/init/devices_test.cpp b/init/devices_test.cpp
index ac4ab9b..eba00cb 100644
--- a/init/devices_test.cpp
+++ b/init/devices_test.cpp
@@ -35,13 +35,13 @@
         device_handler_.sysfs_mount_point_ = fake_sys_root.path;
 
         std::string platform_device_dir = fake_sys_root.path + platform_device;
-        mkdir_recursive(platform_device_dir, 0777, nullptr);
+        mkdir_recursive(platform_device_dir, 0777);
 
         std::string platform_bus = fake_sys_root.path + "/bus/platform"s;
-        mkdir_recursive(platform_bus, 0777, nullptr);
+        mkdir_recursive(platform_bus, 0777);
         symlink(platform_bus.c_str(), (platform_device_dir + "/subsystem").c_str());
 
-        mkdir_recursive(android::base::Dirname(fake_sys_root.path + uevent.path), 0777, nullptr);
+        mkdir_recursive(android::base::Dirname(fake_sys_root.path + uevent.path), 0777);
 
         std::vector<std::string> result;
         result = device_handler_.GetBlockDeviceSymlinks(uevent);
diff --git a/init/import_parser.cpp b/init/import_parser.cpp
index b9fa2ce..e335fd1 100644
--- a/init/import_parser.cpp
+++ b/init/import_parser.cpp
@@ -23,24 +23,22 @@
 namespace android {
 namespace init {
 
-bool ImportParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
-                                int line, std::string* err) {
+Result<Success> ImportParser::ParseSection(std::vector<std::string>&& args,
+                                           const std::string& filename, int line) {
     if (args.size() != 2) {
-        *err = "single argument needed for import\n";
-        return false;
+        return Error() << "single argument needed for import\n";
     }
 
     std::string conf_file;
     bool ret = expand_props(args[1], &conf_file);
     if (!ret) {
-        *err = "error while expanding import";
-        return false;
+        return Error() << "error while expanding import";
     }
 
     LOG(INFO) << "Added '" << conf_file << "' to import list";
     if (filename_.empty()) filename_ = filename;
     imports_.emplace_back(std::move(conf_file), line);
-    return true;
+    return Success();
 }
 
 void ImportParser::EndFile() {
diff --git a/init/import_parser.h b/init/import_parser.h
index 0d04e0e..5a2f894 100644
--- a/init/import_parser.h
+++ b/init/import_parser.h
@@ -28,8 +28,8 @@
 class ImportParser : public SectionParser {
   public:
     ImportParser(Parser* parser) : parser_(parser) {}
-    bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
-                      std::string* err) override;
+    Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+                                 int line) override;
     void EndFile() override;
 
   private:
diff --git a/init/init.cpp b/init/init.cpp
index 69d1564..c65d846 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -16,27 +16,17 @@
 
 #include "init.h"
 
-#include <ctype.h>
 #include <dirent.h>
-#include <errno.h>
 #include <fcntl.h>
-#include <inttypes.h>
-#include <libgen.h>
 #include <paths.h>
 #include <seccomp_policy.h>
 #include <signal.h>
-#include <stdarg.h>
-#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/epoll.h>
 #include <sys/mount.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
 #include <sys/sysmacros.h>
 #include <sys/types.h>
-#include <sys/un.h>
-#include <sys/wait.h>
 #include <unistd.h>
 
 #include <android-base/chrono_utils.h>
@@ -44,25 +34,22 @@
 #include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <android-base/strings.h>
-#include <android-base/unique_fd.h>
 #include <keyutils.h>
 #include <libavb/libavb.h>
 #include <private/android_filesystem_config.h>
 #include <selinux/android.h>
-#include <selinux/selinux.h>
 
-#include <fstream>
 #include <memory>
 #include <optional>
-#include <vector>
 
-#include "bootchart.h"
 #include "import_parser.h"
 #include "init_first_stage.h"
 #include "keychords.h"
 #include "log.h"
 #include "property_service.h"
 #include "reboot.h"
+#include "security.h"
+#include "selinux.h"
 #include "signal_handler.h"
 #include "ueventd.h"
 #include "util.h"
@@ -73,14 +60,10 @@
 using android::base::boot_clock;
 using android::base::GetProperty;
 using android::base::Timer;
-using android::base::unique_fd;
 
 namespace android {
 namespace init {
 
-struct selabel_handle *sehandle;
-struct selabel_handle *sehandle_prop;
-
 static int property_triggers_enabled = 0;
 
 static char qemu[32];
@@ -255,7 +238,7 @@
     }
 }
 
-static int wait_for_coldboot_done_action(const std::vector<std::string>& args) {
+static Result<Success> wait_for_coldboot_done_action(const std::vector<std::string>& args) {
     Timer t;
 
     LOG(VERBOSE) << "Waiting for " COLDBOOT_DONE "...";
@@ -274,210 +257,20 @@
     }
 
     property_set("ro.boottime.init.cold_boot_wait", std::to_string(t.duration().count()));
-    return 0;
+    return Success();
 }
 
-/*
- * Writes 512 bytes of output from Hardware RNG (/dev/hw_random, backed
- * by Linux kernel's hw_random framework) into Linux RNG's via /dev/urandom.
- * Does nothing if Hardware RNG is not present.
- *
- * Since we don't yet trust the quality of Hardware RNG, these bytes are not
- * mixed into the primary pool of Linux RNG and the entropy estimate is left
- * unmodified.
- *
- * If the HW RNG device /dev/hw_random is present, we require that at least
- * 512 bytes read from it are written into Linux RNG. QA is expected to catch
- * devices/configurations where these I/O operations are blocking for a long
- * time. We do not reboot or halt on failures, as this is a best-effort
- * attempt.
- */
-static int mix_hwrng_into_linux_rng_action(const std::vector<std::string>& args) {
-    unique_fd hwrandom_fd(
-        TEMP_FAILURE_RETRY(open("/dev/hw_random", O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
-    if (hwrandom_fd == -1) {
-        if (errno == ENOENT) {
-            LOG(INFO) << "/dev/hw_random not found";
-            // It's not an error to not have a Hardware RNG.
-            return 0;
-        }
-        PLOG(ERROR) << "Failed to open /dev/hw_random";
-        return -1;
-    }
-
-    unique_fd urandom_fd(
-        TEMP_FAILURE_RETRY(open("/dev/urandom", O_WRONLY | O_NOFOLLOW | O_CLOEXEC)));
-    if (urandom_fd == -1) {
-        PLOG(ERROR) << "Failed to open /dev/urandom";
-        return -1;
-    }
-
-    char buf[512];
-    size_t total_bytes_written = 0;
-    while (total_bytes_written < sizeof(buf)) {
-        ssize_t chunk_size =
-            TEMP_FAILURE_RETRY(read(hwrandom_fd, buf, sizeof(buf) - total_bytes_written));
-        if (chunk_size == -1) {
-            PLOG(ERROR) << "Failed to read from /dev/hw_random";
-            return -1;
-        } else if (chunk_size == 0) {
-            LOG(ERROR) << "Failed to read from /dev/hw_random: EOF";
-            return -1;
-        }
-
-        chunk_size = TEMP_FAILURE_RETRY(write(urandom_fd, buf, chunk_size));
-        if (chunk_size == -1) {
-            PLOG(ERROR) << "Failed to write to /dev/urandom";
-            return -1;
-        }
-        total_bytes_written += chunk_size;
-    }
-
-    LOG(INFO) << "Mixed " << total_bytes_written << " bytes from /dev/hw_random into /dev/urandom";
-    return 0;
-}
-
-static void security_failure() {
-    LOG(ERROR) << "Security failure...";
-    panic();
-}
-
-static bool set_highest_available_option_value(std::string path, int min, int max)
-{
-    std::ifstream inf(path, std::fstream::in);
-    if (!inf) {
-        LOG(ERROR) << "Cannot open for reading: " << path;
-        return false;
-    }
-
-    int current = max;
-    while (current >= min) {
-        // try to write out new value
-        std::string str_val = std::to_string(current);
-        std::ofstream of(path, std::fstream::out);
-        if (!of) {
-            LOG(ERROR) << "Cannot open for writing: " << path;
-            return false;
-        }
-        of << str_val << std::endl;
-        of.close();
-
-        // check to make sure it was recorded
-        inf.seekg(0);
-        std::string str_rec;
-        inf >> str_rec;
-        if (str_val.compare(str_rec) == 0) {
-            break;
-        }
-        current--;
-    }
-    inf.close();
-
-    if (current < min) {
-        LOG(ERROR) << "Unable to set minimum option value " << min << " in " << path;
-        return false;
-    }
-    return true;
-}
-
-#define MMAP_RND_PATH "/proc/sys/vm/mmap_rnd_bits"
-#define MMAP_RND_COMPAT_PATH "/proc/sys/vm/mmap_rnd_compat_bits"
-
-/* __attribute__((unused)) due to lack of mips support: see mips block
- * in set_mmap_rnd_bits_action */
-static bool __attribute__((unused)) set_mmap_rnd_bits_min(int start, int min, bool compat) {
-    std::string path;
-    if (compat) {
-        path = MMAP_RND_COMPAT_PATH;
-    } else {
-        path = MMAP_RND_PATH;
-    }
-
-    return set_highest_available_option_value(path, min, start);
-}
-
-/*
- * Set /proc/sys/vm/mmap_rnd_bits and potentially
- * /proc/sys/vm/mmap_rnd_compat_bits to the maximum supported values.
- * Returns -1 if unable to set these to an acceptable value.
- *
- * To support this sysctl, the following upstream commits are needed:
- *
- * d07e22597d1d mm: mmap: add new /proc tunable for mmap_base ASLR
- * e0c25d958f78 arm: mm: support ARCH_MMAP_RND_BITS
- * 8f0d3aa9de57 arm64: mm: support ARCH_MMAP_RND_BITS
- * 9e08f57d684a x86: mm: support ARCH_MMAP_RND_BITS
- * ec9ee4acd97c drivers: char: random: add get_random_long()
- * 5ef11c35ce86 mm: ASLR: use get_random_long()
- */
-static int set_mmap_rnd_bits_action(const std::vector<std::string>& args) {
-/* values are arch-dependent */
-#if defined(USER_MODE_LINUX)
-    /* uml does not support mmap_rnd_bits */
-    return 0;
-#elif defined(__aarch64__)
-    /* arm64 supports 18 - 33 bits depending on pagesize and VA_SIZE */
-    if (set_mmap_rnd_bits_min(33, 24, false)
-            && set_mmap_rnd_bits_min(16, 16, true)) {
-        return 0;
-    }
-#elif defined(__x86_64__)
-    /* x86_64 supports 28 - 32 bits */
-    if (set_mmap_rnd_bits_min(32, 32, false)
-            && set_mmap_rnd_bits_min(16, 16, true)) {
-        return 0;
-    }
-#elif defined(__arm__) || defined(__i386__)
-    /* check to see if we're running on 64-bit kernel */
-    bool h64 = !access(MMAP_RND_COMPAT_PATH, F_OK);
-    /* supported 32-bit architecture must have 16 bits set */
-    if (set_mmap_rnd_bits_min(16, 16, h64)) {
-        return 0;
-    }
-#elif defined(__mips__) || defined(__mips64__)
-    // TODO: add mips support b/27788820
-    return 0;
-#else
-    LOG(ERROR) << "Unknown architecture";
-#endif
-
-    LOG(ERROR) << "Unable to set adequate mmap entropy value!";
-    security_failure();
-    return -1;
-}
-
-#define KPTR_RESTRICT_PATH "/proc/sys/kernel/kptr_restrict"
-#define KPTR_RESTRICT_MINVALUE 2
-#define KPTR_RESTRICT_MAXVALUE 4
-
-/* Set kptr_restrict to the highest available level.
- *
- * Aborts if unable to set this to an acceptable value.
- */
-static int set_kptr_restrict_action(const std::vector<std::string>& args)
-{
-    std::string path = KPTR_RESTRICT_PATH;
-
-    if (!set_highest_available_option_value(path, KPTR_RESTRICT_MINVALUE, KPTR_RESTRICT_MAXVALUE)) {
-        LOG(ERROR) << "Unable to set adequate kptr_restrict value!";
-        security_failure();
-    }
-    return 0;
-}
-
-static int keychord_init_action(const std::vector<std::string>& args)
-{
+static Result<Success> keychord_init_action(const std::vector<std::string>& args) {
     keychord_init();
-    return 0;
+    return Success();
 }
 
-static int console_init_action(const std::vector<std::string>& args)
-{
+static Result<Success> console_init_action(const std::vector<std::string>& args) {
     std::string console = GetProperty("ro.boot.console", "");
     if (!console.empty()) {
         default_console = "/dev/" + console;
     }
-    return 0;
+    return Success();
 }
 
 static void import_kernel_nv(const std::string& key, const std::string& value, bool for_emulator) {
@@ -559,18 +352,16 @@
     if (qemu[0]) import_kernel_cmdline(true, import_kernel_nv);
 }
 
-static int property_enable_triggers_action(const std::vector<std::string>& args)
-{
+static Result<Success> property_enable_triggers_action(const std::vector<std::string>& args) {
     /* Enable property triggers. */
     property_triggers_enabled = 1;
-    return 0;
+    return Success();
 }
 
-static int queue_property_triggers_action(const std::vector<std::string>& args)
-{
+static Result<Success> queue_property_triggers_action(const std::vector<std::string>& args) {
     ActionManager::GetInstance().QueueBuiltinAction(property_enable_triggers_action, "enable_property_trigger");
     ActionManager::GetInstance().QueueAllPropertyActions();
-    return 0;
+    return Success();
 }
 
 static void global_seccomp() {
@@ -582,355 +373,6 @@
     });
 }
 
-static void selinux_init_all_handles(void)
-{
-    sehandle = selinux_android_file_context_handle();
-    selinux_android_set_sehandle(sehandle);
-    sehandle_prop = selinux_android_prop_context_handle();
-}
-
-enum selinux_enforcing_status { SELINUX_PERMISSIVE, SELINUX_ENFORCING };
-
-static selinux_enforcing_status selinux_status_from_cmdline() {
-    selinux_enforcing_status status = SELINUX_ENFORCING;
-
-    import_kernel_cmdline(false, [&](const std::string& key, const std::string& value, bool in_qemu) {
-        if (key == "androidboot.selinux" && value == "permissive") {
-            status = SELINUX_PERMISSIVE;
-        }
-    });
-
-    return status;
-}
-
-static bool selinux_is_enforcing(void)
-{
-    if (ALLOW_PERMISSIVE_SELINUX) {
-        return selinux_status_from_cmdline() == SELINUX_ENFORCING;
-    }
-    return true;
-}
-
-static int audit_callback(void *data, security_class_t /*cls*/, char *buf, size_t len) {
-
-    property_audit_data *d = reinterpret_cast<property_audit_data*>(data);
-
-    if (!d || !d->name || !d->cr) {
-        LOG(ERROR) << "audit_callback invoked with null data arguments!";
-        return 0;
-    }
-
-    snprintf(buf, len, "property=%s pid=%d uid=%d gid=%d", d->name,
-            d->cr->pid, d->cr->uid, d->cr->gid);
-    return 0;
-}
-
-/*
- * Forks, executes the provided program in the child, and waits for the completion in the parent.
- * Child's stderr is captured and logged using LOG(ERROR).
- *
- * Returns true if the child exited with status code 0, returns false otherwise.
- */
-static bool fork_execve_and_wait_for_completion(const char* filename, char* const argv[],
-                                                char* const envp[]) {
-    // Create a pipe used for redirecting child process's output.
-    // * pipe_fds[0] is the FD the parent will use for reading.
-    // * pipe_fds[1] is the FD the child will use for writing.
-    int pipe_fds[2];
-    if (pipe(pipe_fds) == -1) {
-        PLOG(ERROR) << "Failed to create pipe";
-        return false;
-    }
-
-    pid_t child_pid = fork();
-    if (child_pid == -1) {
-        PLOG(ERROR) << "Failed to fork for " << filename;
-        return false;
-    }
-
-    if (child_pid == 0) {
-        // fork succeeded -- this is executing in the child process
-
-        // Close the pipe FD not used by this process
-        TEMP_FAILURE_RETRY(close(pipe_fds[0]));
-
-        // Redirect stderr to the pipe FD provided by the parent
-        if (TEMP_FAILURE_RETRY(dup2(pipe_fds[1], STDERR_FILENO)) == -1) {
-            PLOG(ERROR) << "Failed to redirect stderr of " << filename;
-            _exit(127);
-            return false;
-        }
-        TEMP_FAILURE_RETRY(close(pipe_fds[1]));
-
-        if (execve(filename, argv, envp) == -1) {
-            PLOG(ERROR) << "Failed to execve " << filename;
-            return false;
-        }
-        // Unreachable because execve will have succeeded and replaced this code
-        // with child process's code.
-        _exit(127);
-        return false;
-    } else {
-        // fork succeeded -- this is executing in the original/parent process
-
-        // Close the pipe FD not used by this process
-        TEMP_FAILURE_RETRY(close(pipe_fds[1]));
-
-        // Log the redirected output of the child process.
-        // It's unfortunate that there's no standard way to obtain an istream for a file descriptor.
-        // As a result, we're buffering all output and logging it in one go at the end of the
-        // invocation, instead of logging it as it comes in.
-        const int child_out_fd = pipe_fds[0];
-        std::string child_output;
-        if (!android::base::ReadFdToString(child_out_fd, &child_output)) {
-            PLOG(ERROR) << "Failed to capture full output of " << filename;
-        }
-        TEMP_FAILURE_RETRY(close(child_out_fd));
-        if (!child_output.empty()) {
-            // Log captured output, line by line, because LOG expects to be invoked for each line
-            std::istringstream in(child_output);
-            std::string line;
-            while (std::getline(in, line)) {
-                LOG(ERROR) << filename << ": " << line;
-            }
-        }
-
-        // Wait for child to terminate
-        int status;
-        if (TEMP_FAILURE_RETRY(waitpid(child_pid, &status, 0)) != child_pid) {
-            PLOG(ERROR) << "Failed to wait for " << filename;
-            return false;
-        }
-
-        if (WIFEXITED(status)) {
-            int status_code = WEXITSTATUS(status);
-            if (status_code == 0) {
-                return true;
-            } else {
-                LOG(ERROR) << filename << " exited with status " << status_code;
-            }
-        } else if (WIFSIGNALED(status)) {
-            LOG(ERROR) << filename << " killed by signal " << WTERMSIG(status);
-        } else if (WIFSTOPPED(status)) {
-            LOG(ERROR) << filename << " stopped by signal " << WSTOPSIG(status);
-        } else {
-            LOG(ERROR) << "waitpid for " << filename << " returned unexpected status: " << status;
-        }
-
-        return false;
-    }
-}
-
-static bool read_first_line(const char* file, std::string* line) {
-    line->clear();
-
-    std::string contents;
-    if (!android::base::ReadFileToString(file, &contents, true /* follow symlinks */)) {
-        return false;
-    }
-    std::istringstream in(contents);
-    std::getline(in, *line);
-    return true;
-}
-
-static bool selinux_find_precompiled_split_policy(std::string* file) {
-    file->clear();
-
-    static constexpr const char precompiled_sepolicy[] = "/vendor/etc/selinux/precompiled_sepolicy";
-    if (access(precompiled_sepolicy, R_OK) == -1) {
-        return false;
-    }
-    std::string actual_plat_id;
-    if (!read_first_line("/system/etc/selinux/plat_and_mapping_sepolicy.cil.sha256",
-                         &actual_plat_id)) {
-        PLOG(INFO) << "Failed to read "
-                      "/system/etc/selinux/plat_and_mapping_sepolicy.cil.sha256";
-        return false;
-    }
-    std::string precompiled_plat_id;
-    if (!read_first_line("/vendor/etc/selinux/precompiled_sepolicy.plat_and_mapping.sha256",
-                         &precompiled_plat_id)) {
-        PLOG(INFO) << "Failed to read "
-                      "/vendor/etc/selinux/"
-                      "precompiled_sepolicy.plat_and_mapping.sha256";
-        return false;
-    }
-    if ((actual_plat_id.empty()) || (actual_plat_id != precompiled_plat_id)) {
-        return false;
-    }
-
-    *file = precompiled_sepolicy;
-    return true;
-}
-
-static constexpr const char plat_policy_cil_file[] = "/system/etc/selinux/plat_sepolicy.cil";
-
-static bool selinux_is_split_policy_device() { return access(plat_policy_cil_file, R_OK) != -1; }
-
-/*
- * Loads SELinux policy split across platform/system and non-platform/vendor files.
- *
- * Returns true upon success, false otherwise (failure cause is logged).
- */
-static bool selinux_load_split_policy() {
-    // IMPLEMENTATION NOTE: Split policy consists of three CIL files:
-    // * platform -- policy needed due to logic contained in the system image,
-    // * non-platform -- policy needed due to logic contained in the vendor image,
-    // * mapping -- mapping policy which helps preserve forward-compatibility of non-platform policy
-    //   with newer versions of platform policy.
-    //
-    // secilc is invoked to compile the above three policy files into a single monolithic policy
-    // file. This file is then loaded into the kernel.
-
-    // Load precompiled policy from vendor image, if a matching policy is found there. The policy
-    // must match the platform policy on the system image.
-    std::string precompiled_sepolicy_file;
-    if (selinux_find_precompiled_split_policy(&precompiled_sepolicy_file)) {
-        android::base::unique_fd fd(
-            open(precompiled_sepolicy_file.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));
-        if (fd != -1) {
-            if (selinux_android_load_policy_from_fd(fd, precompiled_sepolicy_file.c_str()) < 0) {
-                LOG(ERROR) << "Failed to load SELinux policy from " << precompiled_sepolicy_file;
-                return false;
-            }
-            return true;
-        }
-    }
-    // No suitable precompiled policy could be loaded
-
-    LOG(INFO) << "Compiling SELinux policy";
-
-    // Determine the highest policy language version supported by the kernel
-    set_selinuxmnt("/sys/fs/selinux");
-    int max_policy_version = security_policyvers();
-    if (max_policy_version == -1) {
-        PLOG(ERROR) << "Failed to determine highest policy version supported by kernel";
-        return false;
-    }
-
-    // We store the output of the compilation on /dev because this is the most convenient tmpfs
-    // storage mount available this early in the boot sequence.
-    char compiled_sepolicy[] = "/dev/sepolicy.XXXXXX";
-    android::base::unique_fd compiled_sepolicy_fd(mkostemp(compiled_sepolicy, O_CLOEXEC));
-    if (compiled_sepolicy_fd < 0) {
-        PLOG(ERROR) << "Failed to create temporary file " << compiled_sepolicy;
-        return false;
-    }
-
-    // clang-format off
-    const char* compile_args[] = {
-        "/system/bin/secilc",
-        plat_policy_cil_file,
-        "-M", "true",
-        // Target the highest policy language version supported by the kernel
-        "-c", std::to_string(max_policy_version).c_str(),
-        "/system/etc/selinux/mapping_sepolicy.cil",
-        "/vendor/etc/selinux/nonplat_sepolicy.cil",
-        "-o", compiled_sepolicy,
-        // We don't care about file_contexts output by the compiler
-        "-f", "/sys/fs/selinux/null",  // /dev/null is not yet available
-        nullptr};
-    // clang-format on
-
-    if (!fork_execve_and_wait_for_completion(compile_args[0], (char**)compile_args, (char**)ENV)) {
-        unlink(compiled_sepolicy);
-        return false;
-    }
-    unlink(compiled_sepolicy);
-
-    LOG(INFO) << "Loading compiled SELinux policy";
-    if (selinux_android_load_policy_from_fd(compiled_sepolicy_fd, compiled_sepolicy) < 0) {
-        LOG(ERROR) << "Failed to load SELinux policy from " << compiled_sepolicy;
-        return false;
-    }
-
-    return true;
-}
-
-/*
- * Loads SELinux policy from a monolithic file.
- *
- * Returns true upon success, false otherwise (failure cause is logged).
- */
-static bool selinux_load_monolithic_policy() {
-    LOG(VERBOSE) << "Loading SELinux policy from monolithic file";
-    if (selinux_android_load_policy() < 0) {
-        PLOG(ERROR) << "Failed to load monolithic SELinux policy";
-        return false;
-    }
-    return true;
-}
-
-/*
- * Loads SELinux policy into the kernel.
- *
- * Returns true upon success, false otherwise (failure cause is logged).
- */
-static bool selinux_load_policy() {
-    return selinux_is_split_policy_device() ? selinux_load_split_policy()
-                                            : selinux_load_monolithic_policy();
-}
-
-static void selinux_initialize(bool in_kernel_domain) {
-    Timer t;
-
-    selinux_callback cb;
-    cb.func_log = selinux_klog_callback;
-    selinux_set_callback(SELINUX_CB_LOG, cb);
-    cb.func_audit = audit_callback;
-    selinux_set_callback(SELINUX_CB_AUDIT, cb);
-
-    if (in_kernel_domain) {
-        LOG(INFO) << "Loading SELinux policy";
-        if (!selinux_load_policy()) {
-            panic();
-        }
-
-        bool kernel_enforcing = (security_getenforce() == 1);
-        bool is_enforcing = selinux_is_enforcing();
-        if (kernel_enforcing != is_enforcing) {
-            if (security_setenforce(is_enforcing)) {
-                PLOG(ERROR) << "security_setenforce(%s) failed" << (is_enforcing ? "true" : "false");
-                security_failure();
-            }
-        }
-
-        std::string err;
-        if (!WriteFile("/sys/fs/selinux/checkreqprot", "0", &err)) {
-            LOG(ERROR) << err;
-            security_failure();
-        }
-
-        // init's first stage can't set properties, so pass the time to the second stage.
-        setenv("INIT_SELINUX_TOOK", std::to_string(t.duration().count()).c_str(), 1);
-    } else {
-        selinux_init_all_handles();
-    }
-}
-
-// The files and directories that were created before initial sepolicy load or
-// files on ramdisk need to have their security context restored to the proper
-// value. This must happen before /dev is populated by ueventd.
-static void selinux_restore_context() {
-    LOG(INFO) << "Running restorecon...";
-    selinux_android_restorecon("/dev", 0);
-    selinux_android_restorecon("/dev/kmsg", 0);
-    if constexpr (WORLD_WRITABLE_KMSG) {
-      selinux_android_restorecon("/dev/kmsg_debug", 0);
-    }
-    selinux_android_restorecon("/dev/socket", 0);
-    selinux_android_restorecon("/dev/random", 0);
-    selinux_android_restorecon("/dev/urandom", 0);
-    selinux_android_restorecon("/dev/__properties__", 0);
-    selinux_android_restorecon("/plat_property_contexts", 0);
-    selinux_android_restorecon("/nonplat_property_contexts", 0);
-    selinux_android_restorecon("/dev/block", SELINUX_ANDROID_RESTORECON_RECURSE);
-    selinux_android_restorecon("/dev/device-mapper", 0);
-
-    selinux_android_restorecon("/sbin/mke2fs_static", 0);
-    selinux_android_restorecon("/sbin/e2fsdroid_static", 0);
-}
-
 // Set the UDC controller for the ConfigFS USB Gadgets.
 // Read the UDC controller in use from "/sys/class/udc".
 // In case of multiple UDC controllers select the first one.
@@ -1013,7 +455,7 @@
         mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11));
 
         if constexpr (WORLD_WRITABLE_KMSG) {
-          mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11));
+            mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11));
         }
 
         mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8));
@@ -1036,13 +478,14 @@
         global_seccomp();
 
         // Set up SELinux, loading the SELinux policy.
-        selinux_initialize(true);
+        SelinuxSetupKernelLogging();
+        SelinuxInitialize();
 
         // We're in the kernel domain, so re-exec init to transition to the init domain now
         // that the SELinux policy has been loaded.
         if (selinux_android_restorecon("/init", 0) == -1) {
-            PLOG(ERROR) << "restorecon failed";
-            security_failure();
+            PLOG(ERROR) << "restorecon failed of /init failed";
+            panic();
         }
 
         setenv("INIT_SECOND_STAGE", "true", 1);
@@ -1058,7 +501,7 @@
         // execv() only returns if an error happened, in which case we
         // panic and never fall through this conditional.
         PLOG(ERROR) << "execv(\"" << path << "\") failed";
-        security_failure();
+        panic();
     }
 
     // At this point we're in the second stage of init.
@@ -1099,8 +542,9 @@
     unsetenv("INIT_AVB_VERSION");
 
     // Now set up SELinux for second stage.
-    selinux_initialize(false);
-    selinux_restore_context();
+    SelinuxSetupKernelLogging();
+    SelabelInitialize();
+    SelinuxRestoreContext();
 
     epoll_fd = epoll_create1(EPOLL_CLOEXEC);
     if (epoll_fd == -1) {
@@ -1132,9 +576,9 @@
     // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
     am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
     // ... so that we can start queuing up actions that require stuff from /dev.
-    am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
-    am.QueueBuiltinAction(set_mmap_rnd_bits_action, "set_mmap_rnd_bits");
-    am.QueueBuiltinAction(set_kptr_restrict_action, "set_kptr_restrict");
+    am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
+    am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
+    am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
     am.QueueBuiltinAction(keychord_init_action, "keychord_init");
     am.QueueBuiltinAction(console_init_action, "console_init");
 
@@ -1143,7 +587,7 @@
 
     // Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
     // wasn't ready immediately after wait_for_coldboot_done
-    am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
+    am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
 
     // Don't mount filesystems or start core system services in charger mode.
     std::string bootmode = GetProperty("ro.bootmode", "");
diff --git a/init/init.h b/init/init.h
index 92b9b70..50a7c83 100644
--- a/init/init.h
+++ b/init/init.h
@@ -18,8 +18,7 @@
 #define _INIT_INIT_H
 
 #include <string>
-
-#include <selinux/label.h>
+#include <vector>
 
 #include "action.h"
 #include "parser.h"
@@ -33,8 +32,6 @@
 // TODO: Have an Init class and remove all globals.
 extern const char *ENV[32];
 extern std::string default_console;
-extern struct selabel_handle *sehandle;
-extern struct selabel_handle *sehandle_prop;
 
 extern std::vector<std::string> late_import_paths;
 
diff --git a/init/init_test.cpp b/init/init_test.cpp
index 2062290..27659f9 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -37,7 +37,7 @@
     void Add(const std::string& name, const BuiltinFunctionNoArgs function) {
         Add(name, 0, 0, [function](const std::vector<std::string>&) {
             function();
-            return 0;
+            return Success();
         });
     }
 
@@ -156,10 +156,9 @@
                                "execute 3";
     // clang-format on
     // WriteFile() ensures the right mode is set
-    std::string err;
-    ASSERT_TRUE(WriteFile(std::string(dir.path) + "/a.rc", dir_a_script, &err));
+    ASSERT_TRUE(WriteFile(std::string(dir.path) + "/a.rc", dir_a_script));
 
-    ASSERT_TRUE(WriteFile(std::string(dir.path) + "/b.rc", "on boot\nexecute 5", &err));
+    ASSERT_TRUE(WriteFile(std::string(dir.path) + "/b.rc", "on boot\nexecute 5"));
 
     // clang-format off
     std::string start_script = "import " + std::string(first_import.path) + "\n"
@@ -175,7 +174,7 @@
     auto execute_command = [&num_executed](const std::vector<std::string>& args) {
         EXPECT_EQ(2U, args.size());
         EXPECT_EQ(++num_executed, std::stoi(args[1]));
-        return 0;
+        return Success();
     };
 
     TestFunctionMap test_function_map;
diff --git a/init/keyword_map.h b/init/keyword_map.h
index 481d637..c95fc73 100644
--- a/init/keyword_map.h
+++ b/init/keyword_map.h
@@ -22,6 +22,8 @@
 
 #include <android-base/stringprintf.h>
 
+#include "result.h"
+
 namespace android {
 namespace init {
 
@@ -34,20 +36,17 @@
     virtual ~KeywordMap() {
     }
 
-    const Function FindFunction(const std::vector<std::string>& args, std::string* err) const {
+    const Result<Function> FindFunction(const std::vector<std::string>& args) const {
         using android::base::StringPrintf;
 
-        if (args.empty()) {
-            *err = "keyword needed, but not provided";
-            return nullptr;
-        }
+        if (args.empty()) return Error() << "Keyword needed, but not provided";
+
         auto& keyword = args[0];
         auto num_args = args.size() - 1;
 
         auto function_info_it = map().find(keyword);
         if (function_info_it == map().end()) {
-            *err = StringPrintf("invalid keyword '%s'", keyword.c_str());
-            return nullptr;
+            return Error() << StringPrintf("Invalid keyword '%s'", keyword.c_str());
         }
 
         auto function_info = function_info_it->second;
@@ -55,22 +54,18 @@
         auto min_args = std::get<0>(function_info);
         auto max_args = std::get<1>(function_info);
         if (min_args == max_args && num_args != min_args) {
-            *err = StringPrintf("%s requires %zu argument%s",
-                                keyword.c_str(), min_args,
-                                (min_args > 1 || min_args == 0) ? "s" : "");
-            return nullptr;
+            return Error() << StringPrintf("%s requires %zu argument%s", keyword.c_str(), min_args,
+                                           (min_args > 1 || min_args == 0) ? "s" : "");
         }
 
         if (num_args < min_args || num_args > max_args) {
             if (max_args == std::numeric_limits<decltype(max_args)>::max()) {
-                *err = StringPrintf("%s requires at least %zu argument%s",
-                                    keyword.c_str(), min_args,
-                                    min_args > 1 ? "s" : "");
+                return Error() << StringPrintf("%s requires at least %zu argument%s",
+                                               keyword.c_str(), min_args, min_args > 1 ? "s" : "");
             } else {
-                *err = StringPrintf("%s requires between %zu and %zu arguments",
-                                    keyword.c_str(), min_args, max_args);
+                return Error() << StringPrintf("%s requires between %zu and %zu arguments",
+                                               keyword.c_str(), min_args, max_args);
             }
-            return nullptr;
         }
 
         return std::get<Function>(function_info);
diff --git a/init/parser.cpp b/init/parser.cpp
index c6f4f45..8a4e798 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -67,9 +67,8 @@
                     if (android::base::StartsWith(args[0], prefix.c_str())) {
                         if (section_parser) section_parser->EndSection();
 
-                        std::string ret_err;
-                        if (!callback(std::move(args), &ret_err)) {
-                            LOG(ERROR) << filename << ": " << state.line << ": " << ret_err;
+                        if (auto result = callback(std::move(args)); !result) {
+                            LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
                         }
                         section_parser = nullptr;
                         break;
@@ -78,16 +77,16 @@
                 if (section_parsers_.count(args[0])) {
                     if (section_parser) section_parser->EndSection();
                     section_parser = section_parsers_[args[0]].get();
-                    std::string ret_err;
-                    if (!section_parser->ParseSection(std::move(args), filename, state.line,
-                                                      &ret_err)) {
-                        LOG(ERROR) << filename << ": " << state.line << ": " << ret_err;
+                    if (auto result =
+                            section_parser->ParseSection(std::move(args), filename, state.line);
+                        !result) {
+                        LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
                         section_parser = nullptr;
                     }
                 } else if (section_parser) {
-                    std::string ret_err;
-                    if (!section_parser->ParseLineSection(std::move(args), state.line, &ret_err)) {
-                        LOG(ERROR) << filename << ": " << state.line << ": " << ret_err;
+                    if (auto result = section_parser->ParseLineSection(std::move(args), state.line);
+                        !result) {
+                        LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
                     }
                 }
                 args.clear();
@@ -102,15 +101,14 @@
 bool Parser::ParseConfigFile(const std::string& path) {
     LOG(INFO) << "Parsing file " << path << "...";
     android::base::Timer t;
-    std::string data;
-    std::string err;
-    if (!ReadFile(path, &data, &err)) {
-        LOG(ERROR) << err;
+    auto config_contents = ReadFile(path);
+    if (!config_contents) {
+        LOG(ERROR) << "Unable to read config file '" << path << "': " << config_contents.error();
         return false;
     }
 
-    data.push_back('\n');  // TODO: fix parse_config.
-    ParseData(path, data);
+    config_contents->push_back('\n');  // TODO: fix parse_config.
+    ParseData(path, *config_contents);
     for (const auto& [section_name, section_parser] : section_parsers_) {
         section_parser->EndFile();
     }
diff --git a/init/parser.h b/init/parser.h
index fd65ad6..4ab24a4 100644
--- a/init/parser.h
+++ b/init/parser.h
@@ -22,6 +22,8 @@
 #include <string>
 #include <vector>
 
+#include "result.h"
+
 //  SectionParser is an interface that can parse a given 'section' in init.
 //
 //  You can implement up to 4 functions below, with ParseSection() being mandatory.
@@ -51,9 +53,9 @@
 class SectionParser {
   public:
     virtual ~SectionParser() {}
-    virtual bool ParseSection(std::vector<std::string>&& args, const std::string& filename,
-                              int line, std::string* err) = 0;
-    virtual bool ParseLineSection(std::vector<std::string>&&, int, std::string*) { return true; };
+    virtual Result<Success> ParseSection(std::vector<std::string>&& args,
+                                         const std::string& filename, int line) = 0;
+    virtual Result<Success> ParseLineSection(std::vector<std::string>&&, int) { return Success(); };
     virtual void EndSection(){};
     virtual void EndFile(){};
 };
@@ -67,7 +69,7 @@
     //  Similar to ParseSection() and ParseLineSection(), this function returns bool with false
     //  indicating a failure and has an std::string* err parameter into which an error string can
     //  be written.
-    using LineCallback = std::function<bool(std::vector<std::string>&&, std::string*)>;
+    using LineCallback = std::function<Result<Success>(std::vector<std::string>&&)>;
 
     Parser();
 
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 925cc9b..e07550a 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -68,6 +68,8 @@
 
 static int property_set_fd = -1;
 
+static struct selabel_handle* sehandle_prop;
+
 void property_init() {
     if (__system_property_area_init()) {
         LOG(ERROR) << "Failed to initialize property area";
@@ -586,14 +588,14 @@
 // "ro.foo.*" is a prefix match, and "ro.foo.bar" is an exact match.
 static void load_properties_from_file(const char* filename, const char* filter) {
     Timer t;
-    std::string data;
-    std::string err;
-    if (!ReadFile(filename, &data, &err)) {
-        PLOG(WARNING) << "Couldn't load property file: " << err;
+    auto file_contents = ReadFile(filename);
+    if (!file_contents) {
+        PLOG(WARNING) << "Couldn't load property file '" << filename
+                      << "': " << file_contents.error();
         return;
     }
-    data.push_back('\n');
-    load_properties(&data[0], filter);
+    file_contents->push_back('\n');
+    load_properties(file_contents->data(), filter);
     LOG(VERBOSE) << "(Loading properties from " << filename << " took " << t << ".)";
 }
 
@@ -733,11 +735,30 @@
     load_recovery_id_prop();
 }
 
+static int SelinuxAuditCallback(void* data, security_class_t /*cls*/, char* buf, size_t len) {
+    property_audit_data* d = reinterpret_cast<property_audit_data*>(data);
+
+    if (!d || !d->name || !d->cr) {
+        LOG(ERROR) << "AuditCallback invoked with null data arguments!";
+        return 0;
+    }
+
+    snprintf(buf, len, "property=%s pid=%d uid=%d gid=%d", d->name, d->cr->pid, d->cr->uid,
+             d->cr->gid);
+    return 0;
+}
+
 void start_property_service() {
+    sehandle_prop = selinux_android_prop_context_handle();
+
+    selinux_callback cb;
+    cb.func_audit = SelinuxAuditCallback;
+    selinux_set_callback(SELINUX_CB_AUDIT, cb);
+
     property_set("ro.property_service.version", "2");
 
     property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
-                                   false, 0666, 0, 0, nullptr, sehandle);
+                                   false, 0666, 0, 0, nullptr);
     if (property_set_fd == -1) {
         PLOG(ERROR) << "start_property_service socket creation failed";
         exit(1);
diff --git a/init/reboot.cpp b/init/reboot.cpp
index cfd703e..24ccdfc 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -48,6 +48,7 @@
 #include <fs_mgr.h>
 #include <logwrap/logwrap.h>
 #include <private/android_filesystem_config.h>
+#include <selinux/selinux.h>
 
 #include "capabilities.h"
 #include "init.h"
@@ -519,7 +520,7 @@
     auto shutdown_handler = [cmd, command, reboot_target,
                              run_fsck](const std::vector<std::string>&) {
         DoReboot(cmd, command, reboot_target, run_fsck);
-        return 0;
+        return Success();
     };
     ActionManager::GetInstance().QueueBuiltinAction(shutdown_handler, "shutdown_done");
 
diff --git a/init/result.h b/init/result.h
new file mode 100644
index 0000000..64fa69f
--- /dev/null
+++ b/init/result.h
@@ -0,0 +1,142 @@
+/*
+ * 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.
+ */
+
+// This file contains classes for returning a successful result along with an optional
+// arbitrarily typed return value or for returning a failure result along with an optional string
+// indicating why the function failed.
+
+// There are 3 classes that implement this functionality and one additional helper type.
+//
+// Result<T> either contains a member of type T that can be accessed using similar semantics as
+// std::optional<T> or it contains a std::string describing an error, which can be accessed via
+// Result<T>::error().
+//
+// Success is a typedef that aids in creating Result<T> that do not contain a return value.
+// Result<Success> is the correct return type for a function that either returns successfully or
+// returns an error value.  Returning Success() from a function that returns Result<Success> is the
+// correct way to indicate that a function without a return type has completed successfully.
+//
+// A successful Result<T> is constructed implicitly from any type that can be implicitly converted
+// to T or from the constructor arguments for T.  This allows you to return a type T directly from
+// a function that returns Result<T>.
+//
+// Error and ErrnoError are used to construct a Result<T> that has failed.  Each of these classes
+// take an ostream as an input and are implicitly cast to a Result<T> containing that failure.
+// ErrnoError() additionally appends ": " + strerror(errno) to the end of the failure string to aid
+// in interacting with C APIs.
+
+// An example of how to use these is below:
+// Result<U> CalculateResult(const T& input) {
+//   U output;
+//   if (!SomeOtherCppFunction(input, &output)) {
+//     return Error() << "SomeOtherCppFunction(" << input << ") failed";
+//   }
+//   if (!c_api_function(output)) {
+//     return ErrnoError() << "c_api_function(" << output << ") failed";
+//   }
+//   return output;
+// }
+//
+// auto output = CalculateResult(input);
+// if (!output) return Error() << "CalculateResult failed: " << output.error();
+// UseOutput(*output);
+
+#ifndef _INIT_RESULT_H
+#define _INIT_RESULT_H
+
+#include <errno.h>
+
+#include <sstream>
+#include <string>
+#include <variant>
+
+namespace android {
+namespace init {
+
+class Error {
+  public:
+    Error() : append_errno_(0) {}
+
+    template <typename T>
+    Error&& operator<<(T&& t) {
+        ss_ << std::forward<T>(t);
+        return std::move(*this);
+    }
+
+    const std::string str() const {
+        if (append_errno_) {
+            return ss_.str() + ": " + strerror(append_errno_);
+        }
+        return ss_.str();
+    }
+
+    Error(const Error&) = delete;
+    Error(Error&&) = delete;
+    Error& operator=(const Error&) = delete;
+    Error& operator=(Error&&) = delete;
+
+  protected:
+    Error(int append_errno) : append_errno_(append_errno) {}
+
+  private:
+    std::stringstream ss_;
+    int append_errno_;
+};
+
+class ErrnoError : public Error {
+  public:
+    ErrnoError() : Error(errno) {}
+};
+
+template <typename T>
+class Result {
+  public:
+    template <typename... U>
+    Result(U&&... result) : contents_(std::in_place_index_t<0>(), std::forward<U>(result)...) {}
+
+    Result(Error&& fb) : contents_(std::in_place_index_t<1>(), fb.str()) {}
+
+    bool has_value() const { return contents_.index() == 0; }
+
+    T& value() & { return std::get<0>(contents_); }
+    const T& value() const & { return std::get<0>(contents_); }
+    T&& value() && { return std::get<0>(std::move(contents_)); }
+    const T&& value() const && { return std::get<0>(std::move(contents_)); }
+
+    const std::string& error() const & { return std::get<1>(contents_); }
+    std::string&& error() && { return std::get<1>(std::move(contents_)); }
+    const std::string&& error() const && { return std::get<1>(std::move(contents_)); }
+
+    explicit operator bool() const { return has_value(); }
+
+    T& operator*() & { return value(); }
+    const T& operator*() const & { return value(); }
+    T&& operator*() && { return std::move(value()); }
+    const T&& operator*() const && { return std::move(value()); }
+
+    T* operator->() { return &value(); }
+    const T* operator->() const { return &value(); }
+
+  private:
+    std::variant<T, std::string> contents_;
+};
+
+using Success = std::monostate;
+
+}  // namespace init
+}  // namespace android
+
+#endif
diff --git a/init/result_test.cpp b/init/result_test.cpp
new file mode 100644
index 0000000..ca65013
--- /dev/null
+++ b/init/result_test.cpp
@@ -0,0 +1,222 @@
+/*
+ * 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 "result.h"
+
+#include "errno.h"
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+using namespace std::string_literals;
+
+namespace android {
+namespace init {
+
+TEST(result, result_accessors) {
+    Result<std::string> result = "success";
+    ASSERT_TRUE(result);
+    ASSERT_TRUE(result.has_value());
+
+    EXPECT_EQ("success", *result);
+    EXPECT_EQ("success", result.value());
+
+    EXPECT_EQ('s', result->data()[0]);
+}
+
+TEST(result, result_accessors_rvalue) {
+    ASSERT_TRUE(Result<std::string>("success"));
+    ASSERT_TRUE(Result<std::string>("success").has_value());
+
+    EXPECT_EQ("success", *Result<std::string>("success"));
+    EXPECT_EQ("success", Result<std::string>("success").value());
+
+    EXPECT_EQ('s', Result<std::string>("success")->data()[0]);
+}
+
+TEST(result, result_success) {
+    Result<Success> result = Success();
+    ASSERT_TRUE(result);
+    ASSERT_TRUE(result.has_value());
+
+    EXPECT_EQ(Success(), *result);
+    EXPECT_EQ(Success(), result.value());
+}
+
+TEST(result, result_success_rvalue) {
+    // Success() doesn't actually create a Result<Success> object, but rather an object that can be
+    // implicitly constructed into a Result<Success> object.
+
+    auto MakeRvalueSuccessResult = []() -> Result<Success> { return Success(); };
+    ASSERT_TRUE(MakeRvalueSuccessResult());
+    ASSERT_TRUE(MakeRvalueSuccessResult().has_value());
+
+    EXPECT_EQ(Success(), *MakeRvalueSuccessResult());
+    EXPECT_EQ(Success(), MakeRvalueSuccessResult().value());
+}
+
+TEST(result, result_error) {
+    Result<Success> result = Error() << "failure" << 1;
+    ASSERT_FALSE(result);
+    ASSERT_FALSE(result.has_value());
+
+    EXPECT_EQ("failure1", result.error());
+}
+
+TEST(result, result_error_empty) {
+    Result<Success> result = Error();
+    ASSERT_FALSE(result);
+    ASSERT_FALSE(result.has_value());
+
+    EXPECT_EQ("", result.error());
+}
+
+TEST(result, result_error_rvalue) {
+    // Error() and ErrnoError() aren't actually used to create a Result<T> object.
+    // Under the hood, they are an intermediate class that can be implicitly constructed into a
+    // Result<T>.  This is needed both to create the ostream and because Error() itself, by
+    // definition will not know what the type, T, of the underlying Result<T> object that it would
+    // create is.
+
+    auto MakeRvalueErrorResult = []() -> Result<Success> { return Error() << "failure" << 1; };
+    ASSERT_FALSE(MakeRvalueErrorResult());
+    ASSERT_FALSE(MakeRvalueErrorResult().has_value());
+
+    EXPECT_EQ("failure1", MakeRvalueErrorResult().error());
+}
+
+TEST(result, result_errno_error) {
+    constexpr int test_errno = 6;
+    errno = test_errno;
+    Result<Success> result = ErrnoError() << "failure" << 1;
+
+    ASSERT_FALSE(result);
+    ASSERT_FALSE(result.has_value());
+
+    EXPECT_EQ("failure1: "s + strerror(test_errno), result.error());
+}
+
+TEST(result, constructor_forwarding) {
+    auto result = Result<std::string>(5, 'a');
+
+    ASSERT_TRUE(result);
+    ASSERT_TRUE(result.has_value());
+
+    EXPECT_EQ("aaaaa", *result);
+}
+
+struct ConstructorTracker {
+    static size_t constructor_called;
+    static size_t copy_constructor_called;
+    static size_t move_constructor_called;
+    static size_t copy_assignment_called;
+    static size_t move_assignment_called;
+
+    template <typename T>
+    ConstructorTracker(T&& string) : string(string) {
+        ++constructor_called;
+    }
+
+    ConstructorTracker(const ConstructorTracker& ct) {
+        ++copy_constructor_called;
+        string = ct.string;
+    }
+    ConstructorTracker(ConstructorTracker&& ct) noexcept {
+        ++move_constructor_called;
+        string = std::move(ct.string);
+    }
+    ConstructorTracker& operator=(const ConstructorTracker& ct) {
+        ++copy_assignment_called;
+        string = ct.string;
+        return *this;
+    }
+    ConstructorTracker& operator=(ConstructorTracker&& ct) noexcept {
+        ++move_assignment_called;
+        string = std::move(ct.string);
+        return *this;
+    }
+
+    std::string string;
+};
+
+size_t ConstructorTracker::constructor_called = 0;
+size_t ConstructorTracker::copy_constructor_called = 0;
+size_t ConstructorTracker::move_constructor_called = 0;
+size_t ConstructorTracker::copy_assignment_called = 0;
+size_t ConstructorTracker::move_assignment_called = 0;
+
+Result<ConstructorTracker> ReturnConstructorTracker(const std::string& in) {
+    if (in.empty()) {
+        return "literal string";
+    }
+    if (in == "test2") {
+        return ConstructorTracker(in + in + "2");
+    }
+    ConstructorTracker result(in + " " + in);
+    return result;
+};
+
+TEST(result, no_copy_on_return) {
+    // If returning parameters that may be used to implicitly construct the type T of Result<T>,
+    // then those parameters are forwarded to the construction of Result<T>.
+
+    // If returning an prvalue or xvalue, it will be move constructed during the construction of
+    // Result<T>.
+
+    // This check ensures that that is the case, and particularly that no copy constructors
+    // are called.
+
+    auto result1 = ReturnConstructorTracker("");
+    ASSERT_TRUE(result1);
+    EXPECT_EQ("literal string", result1->string);
+    EXPECT_EQ(1U, ConstructorTracker::constructor_called);
+    EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+    EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
+    EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+    EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+
+    auto result2 = ReturnConstructorTracker("test2");
+    ASSERT_TRUE(result2);
+    EXPECT_EQ("test2test22", result2->string);
+    EXPECT_EQ(2U, ConstructorTracker::constructor_called);
+    EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+    EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
+    EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+    EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+
+    auto result3 = ReturnConstructorTracker("test3");
+    ASSERT_TRUE(result3);
+    EXPECT_EQ("test3 test3", result3->string);
+    EXPECT_EQ(3U, ConstructorTracker::constructor_called);
+    EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+    EXPECT_EQ(2U, ConstructorTracker::move_constructor_called);
+    EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+    EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+}
+
+TEST(result, die_on_access_failed_result) {
+    Result<std::string> result = Error();
+    ASSERT_DEATH(*result, "");
+}
+
+TEST(result, die_on_get_error_succesful_result) {
+    Result<std::string> result = "success";
+    ASSERT_DEATH(result.error(), "");
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/security.cpp b/init/security.cpp
new file mode 100644
index 0000000..f8976de
--- /dev/null
+++ b/init/security.cpp
@@ -0,0 +1,205 @@
+/*
+ * 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 "security.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <fstream>
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+
+#include "util.h"
+
+using android::base::unique_fd;
+
+namespace android {
+namespace init {
+
+// Writes 512 bytes of output from Hardware RNG (/dev/hw_random, backed
+// by Linux kernel's hw_random framework) into Linux RNG's via /dev/urandom.
+// Does nothing if Hardware RNG is not present.
+//
+// Since we don't yet trust the quality of Hardware RNG, these bytes are not
+// mixed into the primary pool of Linux RNG and the entropy estimate is left
+// unmodified.
+//
+// If the HW RNG device /dev/hw_random is present, we require that at least
+// 512 bytes read from it are written into Linux RNG. QA is expected to catch
+// devices/configurations where these I/O operations are blocking for a long
+// time. We do not reboot or halt on failures, as this is a best-effort
+// attempt.
+Result<Success> MixHwrngIntoLinuxRngAction(const std::vector<std::string>& args) {
+    unique_fd hwrandom_fd(
+        TEMP_FAILURE_RETRY(open("/dev/hw_random", O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
+    if (hwrandom_fd == -1) {
+        if (errno == ENOENT) {
+            LOG(INFO) << "/dev/hw_random not found";
+            // It's not an error to not have a Hardware RNG.
+            return Success();
+        }
+        return ErrnoError() << "Failed to open /dev/hw_random";
+    }
+
+    unique_fd urandom_fd(
+        TEMP_FAILURE_RETRY(open("/dev/urandom", O_WRONLY | O_NOFOLLOW | O_CLOEXEC)));
+    if (urandom_fd == -1) {
+        return ErrnoError() << "Failed to open /dev/urandom";
+    }
+
+    char buf[512];
+    size_t total_bytes_written = 0;
+    while (total_bytes_written < sizeof(buf)) {
+        ssize_t chunk_size =
+            TEMP_FAILURE_RETRY(read(hwrandom_fd, buf, sizeof(buf) - total_bytes_written));
+        if (chunk_size == -1) {
+            return ErrnoError() << "Failed to read from /dev/hw_random";
+        } else if (chunk_size == 0) {
+            return Error() << "Failed to read from /dev/hw_random: EOF";
+        }
+
+        chunk_size = TEMP_FAILURE_RETRY(write(urandom_fd, buf, chunk_size));
+        if (chunk_size == -1) {
+            return ErrnoError() << "Failed to write to /dev/urandom";
+        }
+        total_bytes_written += chunk_size;
+    }
+
+    LOG(INFO) << "Mixed " << total_bytes_written << " bytes from /dev/hw_random into /dev/urandom";
+    return Success();
+}
+
+static bool SetHighestAvailableOptionValue(std::string path, int min, int max) {
+    std::ifstream inf(path, std::fstream::in);
+    if (!inf) {
+        LOG(ERROR) << "Cannot open for reading: " << path;
+        return false;
+    }
+
+    int current = max;
+    while (current >= min) {
+        // try to write out new value
+        std::string str_val = std::to_string(current);
+        std::ofstream of(path, std::fstream::out);
+        if (!of) {
+            LOG(ERROR) << "Cannot open for writing: " << path;
+            return false;
+        }
+        of << str_val << std::endl;
+        of.close();
+
+        // check to make sure it was recorded
+        inf.seekg(0);
+        std::string str_rec;
+        inf >> str_rec;
+        if (str_val.compare(str_rec) == 0) {
+            break;
+        }
+        current--;
+    }
+    inf.close();
+
+    if (current < min) {
+        LOG(ERROR) << "Unable to set minimum option value " << min << " in " << path;
+        return false;
+    }
+    return true;
+}
+
+#define MMAP_RND_PATH "/proc/sys/vm/mmap_rnd_bits"
+#define MMAP_RND_COMPAT_PATH "/proc/sys/vm/mmap_rnd_compat_bits"
+
+// __attribute__((unused)) due to lack of mips support: see mips block in SetMmapRndBitsAction
+static bool __attribute__((unused)) SetMmapRndBitsMin(int start, int min, bool compat) {
+    std::string path;
+    if (compat) {
+        path = MMAP_RND_COMPAT_PATH;
+    } else {
+        path = MMAP_RND_PATH;
+    }
+
+    return SetHighestAvailableOptionValue(path, min, start);
+}
+
+// Set /proc/sys/vm/mmap_rnd_bits and potentially
+// /proc/sys/vm/mmap_rnd_compat_bits to the maximum supported values.
+// Returns -1 if unable to set these to an acceptable value.
+//
+// To support this sysctl, the following upstream commits are needed:
+//
+// d07e22597d1d mm: mmap: add new /proc tunable for mmap_base ASLR
+// e0c25d958f78 arm: mm: support ARCH_MMAP_RND_BITS
+// 8f0d3aa9de57 arm64: mm: support ARCH_MMAP_RND_BITS
+// 9e08f57d684a x86: mm: support ARCH_MMAP_RND_BITS
+// ec9ee4acd97c drivers: char: random: add get_random_long()
+// 5ef11c35ce86 mm: ASLR: use get_random_long()
+Result<Success> SetMmapRndBitsAction(const std::vector<std::string>& args) {
+// values are arch-dependent
+#if defined(USER_MODE_LINUX)
+    // uml does not support mmap_rnd_bits
+    return Success();
+#elif defined(__aarch64__)
+    // arm64 supports 18 - 33 bits depending on pagesize and VA_SIZE
+    if (SetMmapRndBitsMin(33, 24, false) && SetMmapRndBitsMin(16, 16, true)) {
+        return Success();
+    }
+#elif defined(__x86_64__)
+    // x86_64 supports 28 - 32 bits
+    if (SetMmapRndBitsMin(32, 32, false) && SetMmapRndBitsMin(16, 16, true)) {
+        return Success();
+    }
+#elif defined(__arm__) || defined(__i386__)
+    // check to see if we're running on 64-bit kernel
+    bool h64 = !access(MMAP_RND_COMPAT_PATH, F_OK);
+    // supported 32-bit architecture must have 16 bits set
+    if (SetMmapRndBitsMin(16, 16, h64)) {
+        return Success();
+    }
+#elif defined(__mips__) || defined(__mips64__)
+    // TODO: add mips support b/27788820
+    return Success();
+#else
+    LOG(ERROR) << "Unknown architecture";
+#endif
+
+    LOG(ERROR) << "Unable to set adequate mmap entropy value!";
+    panic();
+    return Error();
+}
+
+#define KPTR_RESTRICT_PATH "/proc/sys/kernel/kptr_restrict"
+#define KPTR_RESTRICT_MINVALUE 2
+#define KPTR_RESTRICT_MAXVALUE 4
+
+// Set kptr_restrict to the highest available level.
+//
+// Aborts if unable to set this to an acceptable value.
+Result<Success> SetKptrRestrictAction(const std::vector<std::string>& args) {
+    std::string path = KPTR_RESTRICT_PATH;
+
+    if (!SetHighestAvailableOptionValue(path, KPTR_RESTRICT_MINVALUE, KPTR_RESTRICT_MAXVALUE)) {
+        LOG(ERROR) << "Unable to set adequate kptr_restrict value!";
+        panic();
+        return Error();
+    }
+    return Success();
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/security.h b/init/security.h
new file mode 100644
index 0000000..31e5790
--- /dev/null
+++ b/init/security.h
@@ -0,0 +1,35 @@
+/*
+ * 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_SECURITY_H
+#define _INIT_SECURITY_H
+
+#include <string>
+#include <vector>
+
+#include "result.h"
+
+namespace android {
+namespace init {
+
+Result<Success> MixHwrngIntoLinuxRngAction(const std::vector<std::string>& args);
+Result<Success> SetMmapRndBitsAction(const std::vector<std::string>& args);
+Result<Success> SetKptrRestrictAction(const std::vector<std::string>& args);
+
+}  // namespace init
+}  // namespace android
+
+#endif
diff --git a/init/selinux.cpp b/init/selinux.cpp
new file mode 100644
index 0000000..b9305ed
--- /dev/null
+++ b/init/selinux.cpp
@@ -0,0 +1,431 @@
+/*
+ * 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.
+ */
+
+// This file contains the functions that initialize SELinux during boot as well as helper functions
+// for SELinux operation for init.
+
+// When the system boots, there is no SEPolicy present and init is running in the kernel domain.
+// Init loads the SEPolicy from the file system, restores the context of /init based on this
+// SEPolicy, and finally exec()'s itself to run in the proper domain.
+
+// The SEPolicy on Android comes in two variants: monolithic and split.
+
+// The monolithic policy variant is for legacy non-treble devices that contain a single SEPolicy
+// file located at /sepolicy and is directly loaded into the kernel SELinux subsystem.
+
+// The split policy is for supporting treble devices.  It splits the SEPolicy across files on
+// /system/etc/selinux (the 'plat' portion of the policy) and /vendor/etc/selinux (the 'nonplat'
+// portion of the policy).  This is necessary to allow the system image to be updated independently
+// of the vendor image, while maintaining contributions from both partitions in the SEPolicy.  This
+// is especially important for VTS testing, where the SEPolicy on the Google System Image may not be
+// identical to the system image shipped on a vendor's device.
+
+// The split SEPolicy is loaded as described below:
+// 1) There is a precompiled SEPolicy located at /vendor/etc/selinux/precompiled_sepolicy.
+//    Stored along with this file is the sha256 hash of the parts of the SEPolicy on /system that
+//    were used to compile this precompiled policy.  The system partition contains a similar sha256
+//    of the parts of the SEPolicy that it currently contains.  If these two hashes match, then the
+//    system loads this precompiled_sepolicy directly.
+// 2) If these hashes do not match, then /system has been updated out of sync with /vendor and the
+//    init needs to compile the SEPolicy.  /system contains the SEPolicy compiler, secilc, and it
+//    is used by the LoadSplitPolicy() function below to compile the SEPolicy to a temp directory
+//    and load it.  That function contains even more documentation with the specific implementation
+//    details of how the SEPolicy is compiled if needed.
+
+#include "selinux.h"
+
+#include <fcntl.h>
+#include <paths.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <selinux/android.h>
+
+#include "log.h"
+#include "util.h"
+
+using android::base::Timer;
+using android::base::unique_fd;
+
+namespace android {
+namespace init {
+
+static struct selabel_handle* sehandle = nullptr;
+
+namespace {
+
+enum EnforcingStatus { SELINUX_PERMISSIVE, SELINUX_ENFORCING };
+
+EnforcingStatus StatusFromCmdline() {
+    EnforcingStatus status = SELINUX_ENFORCING;
+
+    import_kernel_cmdline(false,
+                          [&](const std::string& key, const std::string& value, bool in_qemu) {
+                              if (key == "androidboot.selinux" && value == "permissive") {
+                                  status = SELINUX_PERMISSIVE;
+                              }
+                          });
+
+    return status;
+}
+
+bool IsEnforcing() {
+    if (ALLOW_PERMISSIVE_SELINUX) {
+        return StatusFromCmdline() == SELINUX_ENFORCING;
+    }
+    return true;
+}
+
+// Forks, executes the provided program in the child, and waits for the completion in the parent.
+// Child's stderr is captured and logged using LOG(ERROR).
+bool ForkExecveAndWaitForCompletion(const char* filename, char* const argv[]) {
+    // Create a pipe used for redirecting child process's output.
+    // * pipe_fds[0] is the FD the parent will use for reading.
+    // * pipe_fds[1] is the FD the child will use for writing.
+    int pipe_fds[2];
+    if (pipe(pipe_fds) == -1) {
+        PLOG(ERROR) << "Failed to create pipe";
+        return false;
+    }
+
+    pid_t child_pid = fork();
+    if (child_pid == -1) {
+        PLOG(ERROR) << "Failed to fork for " << filename;
+        return false;
+    }
+
+    if (child_pid == 0) {
+        // fork succeeded -- this is executing in the child process
+
+        // Close the pipe FD not used by this process
+        TEMP_FAILURE_RETRY(close(pipe_fds[0]));
+
+        // Redirect stderr to the pipe FD provided by the parent
+        if (TEMP_FAILURE_RETRY(dup2(pipe_fds[1], STDERR_FILENO)) == -1) {
+            PLOG(ERROR) << "Failed to redirect stderr of " << filename;
+            _exit(127);
+            return false;
+        }
+        TEMP_FAILURE_RETRY(close(pipe_fds[1]));
+
+        const char* envp[] = {_PATH_DEFPATH, nullptr};
+        if (execve(filename, argv, (char**)envp) == -1) {
+            PLOG(ERROR) << "Failed to execve " << filename;
+            return false;
+        }
+        // Unreachable because execve will have succeeded and replaced this code
+        // with child process's code.
+        _exit(127);
+        return false;
+    } else {
+        // fork succeeded -- this is executing in the original/parent process
+
+        // Close the pipe FD not used by this process
+        TEMP_FAILURE_RETRY(close(pipe_fds[1]));
+
+        // Log the redirected output of the child process.
+        // It's unfortunate that there's no standard way to obtain an istream for a file descriptor.
+        // As a result, we're buffering all output and logging it in one go at the end of the
+        // invocation, instead of logging it as it comes in.
+        const int child_out_fd = pipe_fds[0];
+        std::string child_output;
+        if (!android::base::ReadFdToString(child_out_fd, &child_output)) {
+            PLOG(ERROR) << "Failed to capture full output of " << filename;
+        }
+        TEMP_FAILURE_RETRY(close(child_out_fd));
+        if (!child_output.empty()) {
+            // Log captured output, line by line, because LOG expects to be invoked for each line
+            std::istringstream in(child_output);
+            std::string line;
+            while (std::getline(in, line)) {
+                LOG(ERROR) << filename << ": " << line;
+            }
+        }
+
+        // Wait for child to terminate
+        int status;
+        if (TEMP_FAILURE_RETRY(waitpid(child_pid, &status, 0)) != child_pid) {
+            PLOG(ERROR) << "Failed to wait for " << filename;
+            return false;
+        }
+
+        if (WIFEXITED(status)) {
+            int status_code = WEXITSTATUS(status);
+            if (status_code == 0) {
+                return true;
+            } else {
+                LOG(ERROR) << filename << " exited with status " << status_code;
+            }
+        } else if (WIFSIGNALED(status)) {
+            LOG(ERROR) << filename << " killed by signal " << WTERMSIG(status);
+        } else if (WIFSTOPPED(status)) {
+            LOG(ERROR) << filename << " stopped by signal " << WSTOPSIG(status);
+        } else {
+            LOG(ERROR) << "waitpid for " << filename << " returned unexpected status: " << status;
+        }
+
+        return false;
+    }
+}
+
+bool ReadFirstLine(const char* file, std::string* line) {
+    line->clear();
+
+    std::string contents;
+    if (!android::base::ReadFileToString(file, &contents, true /* follow symlinks */)) {
+        return false;
+    }
+    std::istringstream in(contents);
+    std::getline(in, *line);
+    return true;
+}
+
+bool FindPrecompiledSplitPolicy(std::string* file) {
+    file->clear();
+
+    static constexpr const char precompiled_sepolicy[] = "/vendor/etc/selinux/precompiled_sepolicy";
+    if (access(precompiled_sepolicy, R_OK) == -1) {
+        return false;
+    }
+    std::string actual_plat_id;
+    if (!ReadFirstLine("/system/etc/selinux/plat_and_mapping_sepolicy.cil.sha256", &actual_plat_id)) {
+        PLOG(INFO) << "Failed to read "
+                      "/system/etc/selinux/plat_and_mapping_sepolicy.cil.sha256";
+        return false;
+    }
+    std::string precompiled_plat_id;
+    if (!ReadFirstLine("/vendor/etc/selinux/precompiled_sepolicy.plat_and_mapping.sha256",
+                       &precompiled_plat_id)) {
+        PLOG(INFO) << "Failed to read "
+                      "/vendor/etc/selinux/"
+                      "precompiled_sepolicy.plat_and_mapping.sha256";
+        return false;
+    }
+    if ((actual_plat_id.empty()) || (actual_plat_id != precompiled_plat_id)) {
+        return false;
+    }
+
+    *file = precompiled_sepolicy;
+    return true;
+}
+
+constexpr const char plat_policy_cil_file[] = "/system/etc/selinux/plat_sepolicy.cil";
+
+bool IsSplitPolicyDevice() {
+    return access(plat_policy_cil_file, R_OK) != -1;
+}
+
+bool LoadSplitPolicy() {
+    // IMPLEMENTATION NOTE: Split policy consists of three CIL files:
+    // * platform -- policy needed due to logic contained in the system image,
+    // * non-platform -- policy needed due to logic contained in the vendor image,
+    // * mapping -- mapping policy which helps preserve forward-compatibility of non-platform policy
+    //   with newer versions of platform policy.
+    //
+    // secilc is invoked to compile the above three policy files into a single monolithic policy
+    // file. This file is then loaded into the kernel.
+
+    // Load precompiled policy from vendor image, if a matching policy is found there. The policy
+    // must match the platform policy on the system image.
+    std::string precompiled_sepolicy_file;
+    if (FindPrecompiledSplitPolicy(&precompiled_sepolicy_file)) {
+        unique_fd fd(open(precompiled_sepolicy_file.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));
+        if (fd != -1) {
+            if (selinux_android_load_policy_from_fd(fd, precompiled_sepolicy_file.c_str()) < 0) {
+                LOG(ERROR) << "Failed to load SELinux policy from " << precompiled_sepolicy_file;
+                return false;
+            }
+            return true;
+        }
+    }
+    // No suitable precompiled policy could be loaded
+
+    LOG(INFO) << "Compiling SELinux policy";
+
+    // Determine the highest policy language version supported by the kernel
+    set_selinuxmnt("/sys/fs/selinux");
+    int max_policy_version = security_policyvers();
+    if (max_policy_version == -1) {
+        PLOG(ERROR) << "Failed to determine highest policy version supported by kernel";
+        return false;
+    }
+
+    // We store the output of the compilation on /dev because this is the most convenient tmpfs
+    // storage mount available this early in the boot sequence.
+    char compiled_sepolicy[] = "/dev/sepolicy.XXXXXX";
+    unique_fd compiled_sepolicy_fd(mkostemp(compiled_sepolicy, O_CLOEXEC));
+    if (compiled_sepolicy_fd < 0) {
+        PLOG(ERROR) << "Failed to create temporary file " << compiled_sepolicy;
+        return false;
+    }
+
+    // clang-format off
+    const char* compile_args[] = {
+        "/system/bin/secilc",
+        plat_policy_cil_file,
+        "-M", "true",
+        // Target the highest policy language version supported by the kernel
+        "-c", std::to_string(max_policy_version).c_str(),
+        "/system/etc/selinux/mapping_sepolicy.cil",
+        "/vendor/etc/selinux/nonplat_sepolicy.cil",
+        "-o", compiled_sepolicy,
+        // We don't care about file_contexts output by the compiler
+        "-f", "/sys/fs/selinux/null",  // /dev/null is not yet available
+        nullptr};
+    // clang-format on
+
+    if (!ForkExecveAndWaitForCompletion(compile_args[0], (char**)compile_args)) {
+        unlink(compiled_sepolicy);
+        return false;
+    }
+    unlink(compiled_sepolicy);
+
+    LOG(INFO) << "Loading compiled SELinux policy";
+    if (selinux_android_load_policy_from_fd(compiled_sepolicy_fd, compiled_sepolicy) < 0) {
+        LOG(ERROR) << "Failed to load SELinux policy from " << compiled_sepolicy;
+        return false;
+    }
+
+    return true;
+}
+
+bool LoadMonolithicPolicy() {
+    LOG(VERBOSE) << "Loading SELinux policy from monolithic file";
+    if (selinux_android_load_policy() < 0) {
+        PLOG(ERROR) << "Failed to load monolithic SELinux policy";
+        return false;
+    }
+    return true;
+}
+
+bool LoadPolicy() {
+    return IsSplitPolicyDevice() ? LoadSplitPolicy() : LoadMonolithicPolicy();
+}
+
+}  // namespace
+
+void SelinuxInitialize() {
+    Timer t;
+
+    LOG(INFO) << "Loading SELinux policy";
+    if (!LoadPolicy()) {
+        panic();
+    }
+
+    bool kernel_enforcing = (security_getenforce() == 1);
+    bool is_enforcing = IsEnforcing();
+    if (kernel_enforcing != is_enforcing) {
+        if (security_setenforce(is_enforcing)) {
+            PLOG(ERROR) << "security_setenforce(%s) failed" << (is_enforcing ? "true" : "false");
+            panic();
+        }
+    }
+
+    if (auto result = WriteFile("/sys/fs/selinux/checkreqprot", "0"); !result) {
+        LOG(ERROR) << "Unable to write to /sys/fs/selinux/checkreqprot: " << result.error();
+        panic();
+    }
+
+    // init's first stage can't set properties, so pass the time to the second stage.
+    setenv("INIT_SELINUX_TOOK", std::to_string(t.duration().count()).c_str(), 1);
+}
+
+// The files and directories that were created before initial sepolicy load or
+// files on ramdisk need to have their security context restored to the proper
+// value. This must happen before /dev is populated by ueventd.
+void SelinuxRestoreContext() {
+    LOG(INFO) << "Running restorecon...";
+    selinux_android_restorecon("/dev", 0);
+    selinux_android_restorecon("/dev/kmsg", 0);
+    if constexpr (WORLD_WRITABLE_KMSG) {
+        selinux_android_restorecon("/dev/kmsg_debug", 0);
+    }
+    selinux_android_restorecon("/dev/socket", 0);
+    selinux_android_restorecon("/dev/random", 0);
+    selinux_android_restorecon("/dev/urandom", 0);
+    selinux_android_restorecon("/dev/__properties__", 0);
+    selinux_android_restorecon("/plat_property_contexts", 0);
+    selinux_android_restorecon("/nonplat_property_contexts", 0);
+    selinux_android_restorecon("/dev/block", SELINUX_ANDROID_RESTORECON_RECURSE);
+    selinux_android_restorecon("/dev/device-mapper", 0);
+
+    selinux_android_restorecon("/sbin/mke2fs_static", 0);
+    selinux_android_restorecon("/sbin/e2fsdroid_static", 0);
+}
+
+// This function sets up SELinux logging to be written to kmsg, to match init's logging.
+void SelinuxSetupKernelLogging() {
+    selinux_callback cb;
+    cb.func_log = selinux_klog_callback;
+    selinux_set_callback(SELINUX_CB_LOG, cb);
+}
+
+// selinux_android_file_context_handle() takes on the order of 10+ms to run, so we want to cache
+// its value.  selinux_android_restorecon() also needs an sehandle for file context look up.  It
+// will create and store its own copy, but selinux_android_set_sehandle() can be used to provide
+// one, thus eliminating an extra call to selinux_android_file_context_handle().
+void SelabelInitialize() {
+    sehandle = selinux_android_file_context_handle();
+    selinux_android_set_sehandle(sehandle);
+}
+
+// A C++ wrapper around selabel_lookup() using the cached sehandle.
+// If sehandle is null, this returns success with an empty context.
+bool SelabelLookupFileContext(const std::string& key, int type, std::string* result) {
+    result->clear();
+
+    if (!sehandle) return true;
+
+    char* context;
+    if (selabel_lookup(sehandle, &context, key.c_str(), type) != 0) {
+        return false;
+    }
+    *result = context;
+    free(context);
+    return true;
+}
+
+// A C++ wrapper around selabel_lookup_best_match() using the cached sehandle.
+// If sehandle is null, this returns success with an empty context.
+bool SelabelLookupFileContextBestMatch(const std::string& key,
+                                       const std::vector<std::string>& aliases, int type,
+                                       std::string* result) {
+    result->clear();
+
+    if (!sehandle) return true;
+
+    std::vector<const char*> c_aliases;
+    for (const auto& alias : aliases) {
+        c_aliases.emplace_back(alias.c_str());
+    }
+    c_aliases.emplace_back(nullptr);
+
+    char* context;
+    if (selabel_lookup_best_match(sehandle, &context, key.c_str(), &c_aliases[0], type) != 0) {
+        return false;
+    }
+    *result = context;
+    free(context);
+    return true;
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/selinux.h b/init/selinux.h
new file mode 100644
index 0000000..7b880ec
--- /dev/null
+++ b/init/selinux.h
@@ -0,0 +1,40 @@
+/*
+ * 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_SELINUX_H
+#define _INIT_SELINUX_H
+
+#include <string>
+#include <vector>
+
+namespace android {
+namespace init {
+
+void SelinuxInitialize();
+void SelinuxRestoreContext();
+
+void SelinuxSetupKernelLogging();
+
+void SelabelInitialize();
+bool SelabelLookupFileContext(const std::string& key, int type, std::string* result);
+bool SelabelLookupFileContextBestMatch(const std::string& key,
+                                       const std::vector<std::string>& aliases, int type,
+                                       std::string* result);
+
+}  // namespace init
+}  // namespace android
+
+#endif
diff --git a/init/service.cpp b/init/service.cpp
index 6f756fa..1b5cc19 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -333,12 +333,12 @@
                   [] (const auto& info) { LOG(INFO) << *info; });
 }
 
-bool Service::ParseCapabilities(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseCapabilities(const std::vector<std::string>& args) {
     capabilities_ = 0;
 
     if (!CapAmbientSupported()) {
-        *err = "capabilities requested but the kernel does not support ambient capabilities";
-        return false;
+        return Error()
+               << "capabilities requested but the kernel does not support ambient capabilities";
     }
 
     unsigned int last_valid_cap = GetLastValidCap();
@@ -350,74 +350,71 @@
         const std::string& arg = args[i];
         int res = LookupCap(arg);
         if (res < 0) {
-            *err = StringPrintf("invalid capability '%s'", arg.c_str());
-            return false;
+            return Error() << StringPrintf("invalid capability '%s'", arg.c_str());
         }
         unsigned int cap = static_cast<unsigned int>(res);  // |res| is >= 0.
         if (cap > last_valid_cap) {
-            *err = StringPrintf("capability '%s' not supported by the kernel", arg.c_str());
-            return false;
+            return Error() << StringPrintf("capability '%s' not supported by the kernel",
+                                           arg.c_str());
         }
         capabilities_[cap] = true;
     }
-    return true;
+    return Success();
 }
 
-bool Service::ParseClass(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseClass(const std::vector<std::string>& args) {
     classnames_ = std::set<std::string>(args.begin() + 1, args.end());
-    return true;
+    return Success();
 }
 
-bool Service::ParseConsole(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseConsole(const std::vector<std::string>& args) {
     flags_ |= SVC_CONSOLE;
     console_ = args.size() > 1 ? "/dev/" + args[1] : "";
-    return true;
+    return Success();
 }
 
-bool Service::ParseCritical(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseCritical(const std::vector<std::string>& args) {
     flags_ |= SVC_CRITICAL;
-    return true;
+    return Success();
 }
 
-bool Service::ParseDisabled(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseDisabled(const std::vector<std::string>& args) {
     flags_ |= SVC_DISABLED;
     flags_ |= SVC_RC_DISABLED;
-    return true;
+    return Success();
 }
 
-bool Service::ParseGroup(const std::vector<std::string>& args, std::string* err) {
-    std::string decode_uid_err;
-    if (!DecodeUid(args[1], &gid_, &decode_uid_err)) {
-        *err = "Unable to find GID for '" + args[1] + "': " + decode_uid_err;
-        return false;
+Result<Success> Service::ParseGroup(const std::vector<std::string>& args) {
+    auto gid = DecodeUid(args[1]);
+    if (!gid) {
+        return Error() << "Unable to decode GID for '" << args[1] << "': " << gid.error();
     }
+    gid_ = *gid;
+
     for (std::size_t n = 2; n < args.size(); n++) {
-        gid_t gid;
-        if (!DecodeUid(args[n], &gid, &decode_uid_err)) {
-            *err = "Unable to find GID for '" + args[n] + "': " + decode_uid_err;
-            return false;
+        gid = DecodeUid(args[n]);
+        if (!gid) {
+            return Error() << "Unable to decode GID for '" << args[n] << "': " << gid.error();
         }
-        supp_gids_.emplace_back(gid);
+        supp_gids_.emplace_back(*gid);
     }
-    return true;
+    return Success();
 }
 
-bool Service::ParsePriority(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParsePriority(const std::vector<std::string>& args) {
     priority_ = 0;
     if (!ParseInt(args[1], &priority_,
                   static_cast<int>(ANDROID_PRIORITY_HIGHEST), // highest is negative
                   static_cast<int>(ANDROID_PRIORITY_LOWEST))) {
-        *err = StringPrintf("process priority value must be range %d - %d",
-                ANDROID_PRIORITY_HIGHEST, ANDROID_PRIORITY_LOWEST);
-        return false;
+        return Error() << StringPrintf("process priority value must be range %d - %d",
+                                       ANDROID_PRIORITY_HIGHEST, ANDROID_PRIORITY_LOWEST);
     }
-    return true;
+    return Success();
 }
 
-bool Service::ParseIoprio(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseIoprio(const std::vector<std::string>& args) {
     if (!ParseInt(args[2], &ioprio_pri_, 0, 7)) {
-        *err = "priority value must be range 0 - 7";
-        return false;
+        return Error() << "priority value must be range 0 - 7";
     }
 
     if (args[1] == "rt") {
@@ -427,14 +424,13 @@
     } else if (args[1] == "idle") {
         ioprio_class_ = IoSchedClass_IDLE;
     } else {
-        *err = "ioprio option usage: ioprio <rt|be|idle> <0-7>";
-        return false;
+        return Error() << "ioprio option usage: ioprio <rt|be|idle> <0-7>";
     }
 
-    return true;
+    return Success();
 }
 
-bool Service::ParseKeycodes(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseKeycodes(const std::vector<std::string>& args) {
     for (std::size_t i = 1; i < args.size(); i++) {
         int code;
         if (ParseInt(args[i], &code)) {
@@ -443,22 +439,24 @@
             LOG(WARNING) << "ignoring invalid keycode: " << args[i];
         }
     }
-    return true;
+    return Success();
 }
 
-bool Service::ParseOneshot(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseOneshot(const std::vector<std::string>& args) {
     flags_ |= SVC_ONESHOT;
-    return true;
+    return Success();
 }
 
-bool Service::ParseOnrestart(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseOnrestart(const std::vector<std::string>& args) {
     std::vector<std::string> str_args(args.begin() + 1, args.end());
     int line = onrestart_.NumCommands() + 1;
-    onrestart_.AddCommand(str_args, line, err);
-    return true;
+    if (auto result = onrestart_.AddCommand(str_args, line); !result) {
+        return Error() << "cannot add Onrestart command: " << result.error();
+    }
+    return Success();
 }
 
-bool Service::ParseNamespace(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseNamespace(const std::vector<std::string>& args) {
     for (size_t i = 1; i < args.size(); i++) {
         if (args[i] == "pid") {
             namespace_flags_ |= CLONE_NEWPID;
@@ -467,135 +465,125 @@
         } else if (args[i] == "mnt") {
             namespace_flags_ |= CLONE_NEWNS;
         } else {
-            *err = "namespace must be 'pid' or 'mnt'";
-            return false;
+            return Error() << "namespace must be 'pid' or 'mnt'";
         }
     }
-    return true;
+    return Success();
 }
 
-bool Service::ParseOomScoreAdjust(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseOomScoreAdjust(const std::vector<std::string>& args) {
     if (!ParseInt(args[1], &oom_score_adjust_, -1000, 1000)) {
-        *err = "oom_score_adjust value must be in range -1000 - +1000";
-        return false;
+        return Error() << "oom_score_adjust value must be in range -1000 - +1000";
     }
-    return true;
+    return Success();
 }
 
-bool Service::ParseMemcgSwappiness(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseMemcgSwappiness(const std::vector<std::string>& args) {
     if (!ParseInt(args[1], &swappiness_, 0)) {
-        *err = "swappiness value must be equal or greater than 0";
-        return false;
+        return Error() << "swappiness value must be equal or greater than 0";
     }
-    return true;
+    return Success();
 }
 
-bool Service::ParseMemcgLimitInBytes(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseMemcgLimitInBytes(const std::vector<std::string>& args) {
     if (!ParseInt(args[1], &limit_in_bytes_, 0)) {
-        *err = "limit_in_bytes value must be equal or greater than 0";
-        return false;
+        return Error() << "limit_in_bytes value must be equal or greater than 0";
     }
-    return true;
+    return Success();
 }
 
-bool Service::ParseMemcgSoftLimitInBytes(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseMemcgSoftLimitInBytes(const std::vector<std::string>& args) {
     if (!ParseInt(args[1], &soft_limit_in_bytes_, 0)) {
-        *err = "soft_limit_in_bytes value must be equal or greater than 0";
-        return false;
+        return Error() << "soft_limit_in_bytes value must be equal or greater than 0";
     }
-    return true;
+    return Success();
 }
 
-bool Service::ParseSeclabel(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseSeclabel(const std::vector<std::string>& args) {
     seclabel_ = args[1];
-    return true;
+    return Success();
 }
 
-bool Service::ParseSetenv(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseSetenv(const std::vector<std::string>& args) {
     envvars_.emplace_back(args[1], args[2]);
-    return true;
+    return Success();
 }
 
-bool Service::ParseShutdown(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseShutdown(const std::vector<std::string>& args) {
     if (args[1] == "critical") {
         flags_ |= SVC_SHUTDOWN_CRITICAL;
-        return true;
+        return Success();
     }
-    return false;
+    return Error() << "Invalid shutdown option";
 }
 
 template <typename T>
-bool Service::AddDescriptor(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::AddDescriptor(const std::vector<std::string>& args) {
     int perm = args.size() > 3 ? std::strtoul(args[3].c_str(), 0, 8) : -1;
-    uid_t uid = 0;
-    gid_t gid = 0;
+    Result<uid_t> uid = 0;
+    Result<gid_t> gid = 0;
     std::string context = args.size() > 6 ? args[6] : "";
 
-    std::string decode_uid_err;
     if (args.size() > 4) {
-        if (!DecodeUid(args[4], &uid, &decode_uid_err)) {
-            *err = "Unable to find UID for '" + args[4] + "': " + decode_uid_err;
-            return false;
+        uid = DecodeUid(args[4]);
+        if (!uid) {
+            return Error() << "Unable to find UID for '" << args[4] << "': " << uid.error();
         }
     }
 
     if (args.size() > 5) {
-        if (!DecodeUid(args[5], &gid, &decode_uid_err)) {
-            *err = "Unable to find GID for '" + args[5] + "': " + decode_uid_err;
-            return false;
+        gid = DecodeUid(args[5]);
+        if (!gid) {
+            return Error() << "Unable to find GID for '" << args[5] << "': " << gid.error();
         }
     }
 
-    auto descriptor = std::make_unique<T>(args[1], args[2], uid, gid, perm, context);
+    auto descriptor = std::make_unique<T>(args[1], args[2], *uid, *gid, perm, context);
 
     auto old =
         std::find_if(descriptors_.begin(), descriptors_.end(),
                      [&descriptor] (const auto& other) { return descriptor.get() == other.get(); });
 
     if (old != descriptors_.end()) {
-        *err = "duplicate descriptor " + args[1] + " " + args[2];
-        return false;
+        return Error() << "duplicate descriptor " << args[1] << " " << args[2];
     }
 
     descriptors_.emplace_back(std::move(descriptor));
-    return true;
+    return Success();
 }
 
 // name type perm [ uid gid context ]
-bool Service::ParseSocket(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseSocket(const std::vector<std::string>& args) {
     if (!StartsWith(args[2], "dgram") && !StartsWith(args[2], "stream") &&
         !StartsWith(args[2], "seqpacket")) {
-        *err = "socket type must be 'dgram', 'stream' or 'seqpacket'";
-        return false;
+        return Error() << "socket type must be 'dgram', 'stream' or 'seqpacket'";
     }
-    return AddDescriptor<SocketInfo>(args, err);
+    return AddDescriptor<SocketInfo>(args);
 }
 
 // name type perm [ uid gid context ]
-bool Service::ParseFile(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseFile(const std::vector<std::string>& args) {
     if (args[2] != "r" && args[2] != "w" && args[2] != "rw") {
-        *err = "file type must be 'r', 'w' or 'rw'";
-        return false;
+        return Error() << "file type must be 'r', 'w' or 'rw'";
     }
     if ((args[1][0] != '/') || (args[1].find("../") != std::string::npos)) {
-        *err = "file name must not be relative";
-        return false;
+        return Error() << "file name must not be relative";
     }
-    return AddDescriptor<FileInfo>(args, err);
+    return AddDescriptor<FileInfo>(args);
 }
 
-bool Service::ParseUser(const std::vector<std::string>& args, std::string* err) {
-    std::string decode_uid_err;
-    if (!DecodeUid(args[1], &uid_, &decode_uid_err)) {
-        *err = "Unable to find UID for '" + args[1] + "': " + decode_uid_err;
-        return false;
+Result<Success> Service::ParseUser(const std::vector<std::string>& args) {
+    auto uid = DecodeUid(args[1]);
+    if (!uid) {
+        return Error() << "Unable to find UID for '" << args[1] << "': " << uid.error();
     }
-    return true;
+    uid_ = *uid;
+    return Success();
 }
 
-bool Service::ParseWritepid(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseWritepid(const std::vector<std::string>& args) {
     writepid_files_.assign(args.begin() + 1, args.end());
-    return true;
+    return Success();
 }
 
 class Service::OptionParserMap : public KeywordMap<OptionParser> {
@@ -643,15 +631,13 @@
     return option_parsers;
 }
 
-bool Service::ParseLine(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseLine(const std::vector<std::string>& args) {
     static const OptionParserMap parser_map;
-    auto parser = parser_map.FindFunction(args, err);
+    auto parser = parser_map.FindFunction(args);
 
-    if (!parser) {
-        return false;
-    }
+    if (!parser) return Error() << parser.error();
 
-    return (this->*parser)(args, err);
+    return std::invoke(*parser, this, args);
 }
 
 bool Service::ExecStart() {
@@ -979,34 +965,35 @@
     if (command_arg > 2 && args[1] != "-") {
         seclabel = args[1];
     }
-    uid_t uid = 0;
+    Result<uid_t> uid = 0;
     if (command_arg > 3) {
-        std::string decode_uid_err;
-        if (!DecodeUid(args[2], &uid, &decode_uid_err)) {
-            LOG(ERROR) << "Unable to find UID for '" << args[2] << "': " << decode_uid_err;
+        uid = DecodeUid(args[2]);
+        if (!uid) {
+            LOG(ERROR) << "Unable to decode UID for '" << args[2] << "': " << uid.error();
             return nullptr;
         }
     }
-    gid_t gid = 0;
+    Result<gid_t> gid = 0;
     std::vector<gid_t> supp_gids;
     if (command_arg > 4) {
-        std::string decode_uid_err;
-        if (!DecodeUid(args[3], &gid, &decode_uid_err)) {
-            LOG(ERROR) << "Unable to find GID for '" << args[3] << "': " << decode_uid_err;
+        gid = DecodeUid(args[3]);
+        if (!gid) {
+            LOG(ERROR) << "Unable to decode GID for '" << args[3] << "': " << gid.error();
             return nullptr;
         }
         std::size_t nr_supp_gids = command_arg - 1 /* -- */ - 4 /* exec SECLABEL UID GID */;
         for (size_t i = 0; i < nr_supp_gids; ++i) {
-            gid_t supp_gid;
-            if (!DecodeUid(args[4 + i], &supp_gid, &decode_uid_err)) {
-                LOG(ERROR) << "Unable to find UID for '" << args[4 + i] << "': " << decode_uid_err;
+            auto supp_gid = DecodeUid(args[4 + i]);
+            if (!supp_gid) {
+                LOG(ERROR) << "Unable to decode GID for '" << args[4 + i]
+                           << "': " << supp_gid.error();
                 return nullptr;
             }
-            supp_gids.push_back(supp_gid);
+            supp_gids.push_back(*supp_gid);
         }
     }
 
-    return std::make_unique<Service>(name, flags, uid, gid, supp_gids, no_capabilities,
+    return std::make_unique<Service>(name, flags, *uid, *gid, supp_gids, no_capabilities,
                                      namespace_flags, seclabel, str_args);
 }
 
@@ -1039,32 +1026,29 @@
     }
 }
 
-bool ServiceParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
-                                 int line, std::string* err) {
+Result<Success> ServiceParser::ParseSection(std::vector<std::string>&& args,
+                                            const std::string& filename, int line) {
     if (args.size() < 3) {
-        *err = "services must have a name and a program";
-        return false;
+        return Error() << "services must have a name and a program";
     }
 
     const std::string& name = args[1];
     if (!IsValidName(name)) {
-        *err = StringPrintf("invalid service name '%s'", name.c_str());
-        return false;
+        return Error() << "invalid service name '" << name << "'";
     }
 
     Service* old_service = service_list_->FindService(name);
     if (old_service) {
-        *err = "ignored duplicate definition of service '" + name + "'";
-        return false;
+        return Error() << "ignored duplicate definition of service '" << name << "'";
     }
 
     std::vector<std::string> str_args(args.begin() + 2, args.end());
     service_ = std::make_unique<Service>(name, str_args);
-    return true;
+    return Success();
 }
 
-bool ServiceParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) {
-    return service_ ? service_->ParseLine(std::move(args), err) : false;
+Result<Success> ServiceParser::ParseLineSection(std::vector<std::string>&& args, int line) {
+    return service_ ? service_->ParseLine(std::move(args)) : Success();
 }
 
 void ServiceParser::EndSection() {
diff --git a/init/service.h b/init/service.h
index 6c143cb..fe85129 100644
--- a/init/service.h
+++ b/init/service.h
@@ -76,7 +76,7 @@
     static std::unique_ptr<Service> MakeTemporaryOneshotService(const std::vector<std::string>& args);
 
     bool IsRunning() { return (flags_ & SVC_RUNNING) != 0; }
-    bool ParseLine(const std::vector<std::string>& args, std::string* err);
+    Result<Success> ParseLine(const std::vector<std::string>& args);
     bool ExecStart();
     bool Start();
     bool StartIfNotDisabled();
@@ -119,8 +119,7 @@
     const std::vector<std::string>& args() const { return args_; }
 
   private:
-    using OptionParser = bool (Service::*) (const std::vector<std::string>& args,
-                                            std::string* err);
+    using OptionParser = Result<Success> (Service::*)(const std::vector<std::string>& args);
     class OptionParserMap;
 
     void NotifyStateChange(const std::string& new_state) const;
@@ -130,32 +129,32 @@
     void KillProcessGroup(int signal);
     void SetProcessAttributes();
 
-    bool ParseCapabilities(const std::vector<std::string>& args, std::string *err);
-    bool ParseClass(const std::vector<std::string>& args, std::string* err);
-    bool ParseConsole(const std::vector<std::string>& args, std::string* err);
-    bool ParseCritical(const std::vector<std::string>& args, std::string* err);
-    bool ParseDisabled(const std::vector<std::string>& args, std::string* err);
-    bool ParseGroup(const std::vector<std::string>& args, std::string* err);
-    bool ParsePriority(const std::vector<std::string>& args, std::string* err);
-    bool ParseIoprio(const std::vector<std::string>& args, std::string* err);
-    bool ParseKeycodes(const std::vector<std::string>& args, std::string* err);
-    bool ParseOneshot(const std::vector<std::string>& args, std::string* err);
-    bool ParseOnrestart(const std::vector<std::string>& args, std::string* err);
-    bool ParseOomScoreAdjust(const std::vector<std::string>& args, std::string* err);
-    bool ParseMemcgLimitInBytes(const std::vector<std::string>& args, std::string* err);
-    bool ParseMemcgSoftLimitInBytes(const std::vector<std::string>& args, std::string* err);
-    bool ParseMemcgSwappiness(const std::vector<std::string>& args, std::string* err);
-    bool ParseNamespace(const std::vector<std::string>& args, std::string* err);
-    bool ParseSeclabel(const std::vector<std::string>& args, std::string* err);
-    bool ParseSetenv(const std::vector<std::string>& args, std::string* err);
-    bool ParseShutdown(const std::vector<std::string>& args, std::string* err);
-    bool ParseSocket(const std::vector<std::string>& args, std::string* err);
-    bool ParseFile(const std::vector<std::string>& args, std::string* err);
-    bool ParseUser(const std::vector<std::string>& args, std::string* err);
-    bool ParseWritepid(const std::vector<std::string>& args, std::string* err);
+    Result<Success> ParseCapabilities(const std::vector<std::string>& args);
+    Result<Success> ParseClass(const std::vector<std::string>& args);
+    Result<Success> ParseConsole(const std::vector<std::string>& args);
+    Result<Success> ParseCritical(const std::vector<std::string>& args);
+    Result<Success> ParseDisabled(const std::vector<std::string>& args);
+    Result<Success> ParseGroup(const std::vector<std::string>& args);
+    Result<Success> ParsePriority(const std::vector<std::string>& args);
+    Result<Success> ParseIoprio(const std::vector<std::string>& args);
+    Result<Success> ParseKeycodes(const std::vector<std::string>& args);
+    Result<Success> ParseOneshot(const std::vector<std::string>& args);
+    Result<Success> ParseOnrestart(const std::vector<std::string>& args);
+    Result<Success> ParseOomScoreAdjust(const std::vector<std::string>& args);
+    Result<Success> ParseMemcgLimitInBytes(const std::vector<std::string>& args);
+    Result<Success> ParseMemcgSoftLimitInBytes(const std::vector<std::string>& args);
+    Result<Success> ParseMemcgSwappiness(const std::vector<std::string>& args);
+    Result<Success> ParseNamespace(const std::vector<std::string>& args);
+    Result<Success> ParseSeclabel(const std::vector<std::string>& args);
+    Result<Success> ParseSetenv(const std::vector<std::string>& args);
+    Result<Success> ParseShutdown(const std::vector<std::string>& args);
+    Result<Success> ParseSocket(const std::vector<std::string>& args);
+    Result<Success> ParseFile(const std::vector<std::string>& args);
+    Result<Success> ParseUser(const std::vector<std::string>& args);
+    Result<Success> ParseWritepid(const std::vector<std::string>& args);
 
     template <typename T>
-    bool AddDescriptor(const std::vector<std::string>& args, std::string* err);
+    Result<Success> AddDescriptor(const std::vector<std::string>& args);
 
     static unsigned long next_start_order_;
     static bool is_exec_service_running_;
@@ -242,9 +241,9 @@
 class ServiceParser : public SectionParser {
   public:
     ServiceParser(ServiceList* service_list) : service_list_(service_list), service_(nullptr) {}
-    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;
+    Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+                                 int line) override;
+    Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
     void EndSection() override;
 
   private:
diff --git a/init/service_test.cpp b/init/service_test.cpp
index 62e46f4..98d876f 100644
--- a/init/service_test.cpp
+++ b/init/service_test.cpp
@@ -132,29 +132,29 @@
         ASSERT_EQ("", svc->seclabel());
     }
     if (uid) {
-        uid_t decoded_uid;
-        std::string err;
-        ASSERT_TRUE(DecodeUid("log", &decoded_uid, &err));
-        ASSERT_EQ(decoded_uid, svc->uid());
+        auto decoded_uid = DecodeUid("log");
+        ASSERT_TRUE(decoded_uid);
+        ASSERT_EQ(*decoded_uid, svc->uid());
     } else {
         ASSERT_EQ(0U, svc->uid());
     }
     if (gid) {
-        uid_t decoded_uid;
-        std::string err;
-        ASSERT_TRUE(DecodeUid("shell", &decoded_uid, &err));
-        ASSERT_EQ(decoded_uid, svc->gid());
+        auto decoded_uid = DecodeUid("shell");
+        ASSERT_TRUE(decoded_uid);
+        ASSERT_EQ(*decoded_uid, svc->gid());
     } else {
         ASSERT_EQ(0U, svc->gid());
     }
     if (supplementary_gids) {
         ASSERT_EQ(2U, svc->supp_gids().size());
-        uid_t decoded_uid;
-        std::string err;
-        ASSERT_TRUE(DecodeUid("system", &decoded_uid, &err));
-        ASSERT_EQ(decoded_uid, svc->supp_gids()[0]);
-        ASSERT_TRUE(DecodeUid("adb", &decoded_uid, &err));
-        ASSERT_EQ(decoded_uid, svc->supp_gids()[1]);
+
+        auto decoded_uid = DecodeUid("system");
+        ASSERT_TRUE(decoded_uid);
+        ASSERT_EQ(*decoded_uid, svc->supp_gids()[0]);
+
+        decoded_uid = DecodeUid("adb");
+        ASSERT_TRUE(decoded_uid);
+        ASSERT_EQ(*decoded_uid, svc->supp_gids()[1]);
     } else {
         ASSERT_EQ(0U, svc->supp_gids().size());
     }
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index c0eae1e..1435d82 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -36,6 +36,7 @@
 #include "devices.h"
 #include "firmware_handler.h"
 #include "log.h"
+#include "selinux.h"
 #include "uevent_listener.h"
 #include "ueventd_parser.h"
 #include "util.h"
@@ -222,10 +223,10 @@
     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("/sys/",
+                               std::bind(ParsePermissionsLine, _1, &sysfs_permissions, nullptr));
     parser.AddSingleLineParser("/dev/",
-                               std::bind(ParsePermissionsLine, _1, _2, nullptr, &dev_permissions));
+                               std::bind(ParsePermissionsLine, _1, nullptr, &dev_permissions));
 
     parser.ParseConfig("/ueventd.rc");
     parser.ParseConfig("/vendor/ueventd.rc");
@@ -257,9 +258,8 @@
 
     LOG(INFO) << "ueventd started!";
 
-    selinux_callback cb;
-    cb.func_log = selinux_klog_callback;
-    selinux_set_callback(SELINUX_CB_LOG, cb);
+    SelinuxSetupKernelLogging();
+    SelabelInitialize();
 
     DeviceHandler device_handler = CreateDeviceHandler();
     UeventListener uevent_listener;
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
index 02e0d42..e831b8b 100644
--- a/init/ueventd_parser.cpp
+++ b/init/ueventd_parser.cpp
@@ -24,18 +24,16 @@
 namespace android {
 namespace init {
 
-bool ParsePermissionsLine(std::vector<std::string>&& args, std::string* err,
-                          std::vector<SysfsPermissions>* out_sysfs_permissions,
-                          std::vector<Permissions>* out_dev_permissions) {
+Result<Success> ParsePermissionsLine(std::vector<std::string>&& args,
+                                     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;
+        return Error() << "/sys/ lines must have 5 entries";
     }
 
     if (!is_sysfs && args.size() != 4) {
-        *err = "/dev/ lines must have 4 entries";
-        return false;
+        return Error() << "/dev/ lines must have 4 entries";
     }
 
     auto it = args.begin();
@@ -49,23 +47,20 @@
     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;
+        return Error() << "invalid mode '" << perm_string << "'";
     }
 
     std::string& uid_string = *it++;
     passwd* pwd = getpwnam(uid_string.c_str());
     if (!pwd) {
-        *err = "invalid uid '" + uid_string + "'";
-        return false;
+        return Error() << "invalid uid '" << uid_string << "'";
     }
     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;
+        return Error() << "invalid gid '" << gid_string << "'";
     }
     gid_t gid = grp->gr_gid;
 
@@ -74,53 +69,49 @@
     } else {
         out_dev_permissions->emplace_back(name, perm, uid, gid);
     }
-    return true;
+    return Success();
 }
 
-bool SubsystemParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
-                                   int line, std::string* err) {
+Result<Success> SubsystemParser::ParseSection(std::vector<std::string>&& args,
+                                              const std::string& filename, int line) {
     if (args.size() != 2) {
-        *err = "subsystems must have exactly one name";
-        return false;
+        return Error() << "subsystems must have exactly one name";
     }
 
     if (std::find(subsystems_->begin(), subsystems_->end(), args[1]) != subsystems_->end()) {
-        *err = "ignoring duplicate subsystem entry";
-        return false;
+        return Error() << "ignoring duplicate subsystem entry";
     }
 
     subsystem_.name_ = args[1];
 
-    return true;
+    return Success();
 }
 
-bool SubsystemParser::ParseDevName(std::vector<std::string>&& args, std::string* err) {
+Result<Success> SubsystemParser::ParseDevName(std::vector<std::string>&& args) {
     if (args[1] == "uevent_devname") {
         subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVNAME;
-        return true;
+        return Success();
     }
     if (args[1] == "uevent_devpath") {
         subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVPATH;
-        return true;
+        return Success();
     }
 
-    *err = "invalid devname '" + args[1] + "'";
-    return false;
+    return Error() << "invalid devname '" << args[1] << "'";
 }
 
-bool SubsystemParser::ParseDirName(std::vector<std::string>&& args, std::string* err) {
+Result<Success> SubsystemParser::ParseDirName(std::vector<std::string>&& args) {
     if (args[1].front() != '/') {
-        *err = "dirname '" + args[1] + " ' does not start with '/'";
-        return false;
+        return Error() << "dirname '" << args[1] << " ' does not start with '/'";
     }
 
     subsystem_.dir_name_ = args[1];
-    return true;
+    return Success();
 }
 
-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);
+Result<Success> SubsystemParser::ParseLineSection(std::vector<std::string>&& args, int line) {
+    using OptionParser = Result<Success> (SubsystemParser::*)(std::vector<std::string> && args);
+
     static class OptionParserMap : public KeywordMap<OptionParser> {
       private:
         const Map& map() const override {
@@ -134,13 +125,11 @@
         }
     } parser_map;
 
-    auto parser = parser_map.FindFunction(args, err);
+    auto parser = parser_map.FindFunction(args);
 
-    if (!parser) {
-        return false;
-    }
+    if (!parser) return Error() << parser.error();
 
-    return (this->*parser)(std::move(args), err);
+    return std::invoke(*parser, this, std::move(args));
 }
 
 void SubsystemParser::EndSection() {
diff --git a/init/ueventd_parser.h b/init/ueventd_parser.h
index 51d83ef..18d1027 100644
--- a/init/ueventd_parser.h
+++ b/init/ueventd_parser.h
@@ -29,22 +29,22 @@
 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;
+    Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+                                 int line) override;
+    Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) 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);
+    Result<Success> ParseDevName(std::vector<std::string>&& args);
+    Result<Success> ParseDirName(std::vector<std::string>&& args);
 
     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);
+Result<Success> ParsePermissionsLine(std::vector<std::string>&& args,
+                                     std::vector<SysfsPermissions>* out_sysfs_permissions,
+                                     std::vector<Permissions>* out_dev_permissions);
 
 }  // namespace init
 }  // namespace android
diff --git a/init/util.cpp b/init/util.cpp
index fdcb22d..fcf7ca8 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -42,6 +42,7 @@
 #include <selinux/android.h>
 
 #include "reboot.h"
+#include "selinux.h"
 
 #ifdef _INIT_INIT_H
 #error "Do not include init.h in files used by ueventd or watchdogd; it will expose init's globals"
@@ -56,30 +57,20 @@
 const std::string kDefaultAndroidDtDir("/proc/device-tree/firmware/android/");
 
 // DecodeUid() - decodes and returns the given string, which can be either the
-// numeric or name representation, into the integer uid or gid. Returns
-// UINT_MAX on error.
-bool DecodeUid(const std::string& name, uid_t* uid, std::string* err) {
-    *uid = UINT_MAX;
-    *err = "";
-
+// numeric or name representation, into the integer uid or gid.
+Result<uid_t> DecodeUid(const std::string& name) {
     if (isalpha(name[0])) {
         passwd* pwd = getpwnam(name.c_str());
-        if (!pwd) {
-            *err = "getpwnam failed: "s + strerror(errno);
-            return false;
-        }
-        *uid = pwd->pw_uid;
-        return true;
+        if (!pwd) return ErrnoError() << "getpwnam failed";
+
+        return pwd->pw_uid;
     }
 
     errno = 0;
     uid_t result = static_cast<uid_t>(strtoul(name.c_str(), 0, 0));
-    if (errno) {
-        *err = "strtoul failed: "s + strerror(errno);
-        return false;
-    }
-    *uid = result;
-    return true;
+    if (errno) return ErrnoError() << "strtoul failed";
+
+    return result;
 }
 
 /*
@@ -89,7 +80,7 @@
  * variable ANDROID_SOCKET_ENV_PREFIX<name> ("ANDROID_SOCKET_foo").
  */
 int CreateSocket(const char* name, int type, bool passcred, mode_t perm, uid_t uid, gid_t gid,
-                 const char* socketcon, selabel_handle* sehandle) {
+                 const char* socketcon) {
     if (socketcon) {
         if (setsockcreatecon(socketcon) == -1) {
             PLOG(ERROR) << "setsockcreatecon(\"" << socketcon << "\") failed";
@@ -116,11 +107,9 @@
         return -1;
     }
 
-    char *filecon = NULL;
-    if (sehandle) {
-        if (selabel_lookup(sehandle, &filecon, addr.sun_path, S_IFSOCK) == 0) {
-            setfscreatecon(filecon);
-        }
+    std::string secontext;
+    if (SelabelLookupFileContext(addr.sun_path, S_IFSOCK, &secontext) && !secontext.empty()) {
+        setfscreatecon(secontext.c_str());
     }
 
     if (passcred) {
@@ -134,8 +123,9 @@
     int ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr));
     int savederrno = errno;
 
-    setfscreatecon(NULL);
-    freecon(filecon);
+    if (!secontext.empty()) {
+        setfscreatecon(nullptr);
+    }
 
     if (ret) {
         errno = savederrno;
@@ -164,65 +154,55 @@
     return -1;
 }
 
-bool ReadFile(const std::string& path, std::string* content, std::string* err) {
-    content->clear();
-    *err = "";
-
+Result<std::string> ReadFile(const std::string& path) {
     android::base::unique_fd fd(
         TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
     if (fd == -1) {
-        *err = "Unable to open '" + path + "': " + strerror(errno);
-        return false;
+        return ErrnoError() << "open() failed";
     }
 
     // For security reasons, disallow world-writable
     // or group-writable files.
     struct stat sb;
     if (fstat(fd, &sb) == -1) {
-        *err = "fstat failed for '" + path + "': " + strerror(errno);
-        return false;
+        return ErrnoError() << "fstat failed()";
     }
     if ((sb.st_mode & (S_IWGRP | S_IWOTH)) != 0) {
-        *err = "Skipping insecure file '" + path + "'";
-        return false;
+        return Error() << "Skipping insecure file";
     }
 
-    if (!android::base::ReadFdToString(fd, content)) {
-        *err = "Unable to read '" + path + "': " + strerror(errno);
-        return false;
+    std::string content;
+    if (!android::base::ReadFdToString(fd, &content)) {
+        return ErrnoError() << "Unable to read file contents";
     }
-    return true;
+    return content;
 }
 
-bool WriteFile(const std::string& path, const std::string& content, std::string* err) {
-    *err = "";
-
+Result<Success> WriteFile(const std::string& path, const std::string& content) {
     android::base::unique_fd fd(TEMP_FAILURE_RETRY(
         open(path.c_str(), O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0600)));
     if (fd == -1) {
-        *err = "Unable to open '" + path + "': " + strerror(errno);
-        return false;
+        return ErrnoError() << "open() failed";
     }
     if (!android::base::WriteStringToFd(content, fd)) {
-        *err = "Unable to write to '" + path + "': " + strerror(errno);
-        return false;
+        return ErrnoError() << "Unable to write file contents";
     }
-    return true;
+    return Success();
 }
 
-int mkdir_recursive(const std::string& path, mode_t mode, selabel_handle* sehandle) {
+bool mkdir_recursive(const std::string& path, mode_t mode) {
     std::string::size_type slash = 0;
     while ((slash = path.find('/', slash + 1)) != std::string::npos) {
         auto directory = path.substr(0, slash);
         struct stat info;
         if (stat(directory.c_str(), &info) != 0) {
-            auto ret = make_dir(directory.c_str(), mode, sehandle);
-            if (ret && errno != EEXIST) return ret;
+            auto ret = make_dir(directory, mode);
+            if (!ret && errno != EEXIST) return false;
         }
     }
-    auto ret = make_dir(path.c_str(), mode, sehandle);
-    if (ret && errno != EEXIST) return ret;
-    return 0;
+    auto ret = make_dir(path, mode);
+    if (!ret && errno != EEXIST) return false;
+    return true;
 }
 
 int wait_for_file(const char* filename, std::chrono::nanoseconds timeout) {
@@ -249,26 +229,21 @@
     }
 }
 
-int make_dir(const char* path, mode_t mode, selabel_handle* sehandle) {
-    int rc;
-
-    char *secontext = NULL;
-
-    if (sehandle) {
-        selabel_lookup(sehandle, &secontext, path, mode);
-        setfscreatecon(secontext);
+bool make_dir(const std::string& path, mode_t mode) {
+    std::string secontext;
+    if (SelabelLookupFileContext(path, mode, &secontext) && !secontext.empty()) {
+        setfscreatecon(secontext.c_str());
     }
 
-    rc = mkdir(path, mode);
+    int rc = mkdir(path.c_str(), mode);
 
-    if (secontext) {
+    if (!secontext.empty()) {
         int save_errno = errno;
-        freecon(secontext);
-        setfscreatecon(NULL);
+        setfscreatecon(nullptr);
         errno = save_errno;
     }
 
-    return rc;
+    return rc == 0;
 }
 
 /*
diff --git a/init/util.h b/init/util.h
index 29c10cb..298aa1c 100644
--- a/init/util.h
+++ b/init/util.h
@@ -28,6 +28,8 @@
 #include <android-base/chrono_utils.h>
 #include <selinux/label.h>
 
+#include "result.h"
+
 #define COLDBOOT_DONE "/dev/.coldboot_done"
 
 using android::base::boot_clock;
@@ -37,18 +39,18 @@
 namespace init {
 
 int CreateSocket(const char* name, int type, bool passcred, mode_t perm, uid_t uid, gid_t gid,
-                 const char* socketcon, selabel_handle* sehandle);
+                 const char* socketcon);
 
-bool ReadFile(const std::string& path, std::string* content, std::string* err);
-bool WriteFile(const std::string& path, const std::string& content, std::string* err);
+Result<std::string> ReadFile(const std::string& path);
+Result<Success> WriteFile(const std::string& path, const std::string& content);
 
-bool DecodeUid(const std::string& name, uid_t* uid, std::string* err);
+Result<uid_t> DecodeUid(const std::string& name);
 
-int mkdir_recursive(const std::string& pathname, mode_t mode, selabel_handle* sehandle);
+bool mkdir_recursive(const std::string& pathname, mode_t mode);
 int wait_for_file(const char *filename, std::chrono::nanoseconds timeout);
 void import_kernel_cmdline(bool in_qemu,
                            const std::function<void(const std::string&, const std::string&, bool)>&);
-int make_dir(const char* path, mode_t mode, selabel_handle* sehandle);
+bool make_dir(const std::string& path, mode_t mode);
 std::string bytes_to_hex(const uint8_t *bytes, size_t bytes_len);
 bool is_dir(const char* pathname);
 bool expand_props(const std::string& src, std::string* dst);
diff --git a/init/util_test.cpp b/init/util_test.cpp
index c16ab74..007d10e 100644
--- a/init/util_test.cpp
+++ b/init/util_test.cpp
@@ -30,61 +30,51 @@
 namespace init {
 
 TEST(util, ReadFile_ENOENT) {
-    std::string s("hello");
-    std::string err;
     errno = 0;
-    EXPECT_FALSE(ReadFile("/proc/does-not-exist", &s, &err));
-    EXPECT_EQ("Unable to open '/proc/does-not-exist': No such file or directory", err);
+    auto file_contents = ReadFile("/proc/does-not-exist");
     EXPECT_EQ(ENOENT, errno);
-    EXPECT_EQ("", s);  // s was cleared.
+    ASSERT_FALSE(file_contents);
+    EXPECT_EQ("open() failed: No such file or directory", file_contents.error());
 }
 
 TEST(util, ReadFileGroupWriteable) {
     std::string s("hello");
     TemporaryFile tf;
-    std::string err;
     ASSERT_TRUE(tf.fd != -1);
-    EXPECT_TRUE(WriteFile(tf.path, s, &err)) << strerror(errno);
-    EXPECT_EQ("", err);
+    EXPECT_TRUE(WriteFile(tf.path, s)) << strerror(errno);
     EXPECT_NE(-1, fchmodat(AT_FDCWD, tf.path, 0620, AT_SYMLINK_NOFOLLOW)) << strerror(errno);
-    EXPECT_FALSE(ReadFile(tf.path, &s, &err)) << strerror(errno);
-    EXPECT_EQ("Skipping insecure file '"s + tf.path + "'", err);
-    EXPECT_EQ("", s);  // s was cleared.
+    auto file_contents = ReadFile(tf.path);
+    ASSERT_FALSE(file_contents) << strerror(errno);
+    EXPECT_EQ("Skipping insecure file", file_contents.error());
 }
 
 TEST(util, ReadFileWorldWiteable) {
     std::string s("hello");
     TemporaryFile tf;
-    std::string err;
     ASSERT_TRUE(tf.fd != -1);
-    EXPECT_TRUE(WriteFile(tf.path, s, &err)) << strerror(errno);
-    EXPECT_EQ("", err);
+    EXPECT_TRUE(WriteFile(tf.path, s)) << strerror(errno);
     EXPECT_NE(-1, fchmodat(AT_FDCWD, tf.path, 0602, AT_SYMLINK_NOFOLLOW)) << strerror(errno);
-    EXPECT_FALSE(ReadFile(tf.path, &s, &err)) << strerror(errno);
-    EXPECT_EQ("Skipping insecure file '"s + tf.path + "'", err);
-    EXPECT_EQ("", s);  // s was cleared.
+    auto file_contents = ReadFile(tf.path);
+    ASSERT_FALSE(file_contents) << strerror(errno);
+    EXPECT_EQ("Skipping insecure file", file_contents.error());
 }
 
 TEST(util, ReadFileSymbolicLink) {
-    std::string s("hello");
     errno = 0;
     // lrwxrwxrwx 1 root root 13 1970-01-01 00:00 charger -> /sbin/healthd
-    std::string err;
-    EXPECT_FALSE(ReadFile("/charger", &s, &err));
-    EXPECT_EQ("Unable to open '/charger': Too many symbolic links encountered", err);
+    auto file_contents = ReadFile("/charger");
     EXPECT_EQ(ELOOP, errno);
-    EXPECT_EQ("", s);  // s was cleared.
+    ASSERT_FALSE(file_contents);
+    EXPECT_EQ("open() failed: Too many symbolic links encountered", file_contents.error());
 }
 
 TEST(util, ReadFileSuccess) {
-    std::string s("hello");
-    std::string err;
-    EXPECT_TRUE(ReadFile("/proc/version", &s, &err));
-    EXPECT_EQ("", err);
-    EXPECT_GT(s.length(), 6U);
-    EXPECT_EQ('\n', s[s.length() - 1]);
-    s[5] = 0;
-    EXPECT_STREQ("Linux", s.c_str());
+    auto file_contents = ReadFile("/proc/version");
+    ASSERT_TRUE(file_contents);
+    EXPECT_GT(file_contents->length(), 6U);
+    EXPECT_EQ('\n', file_contents->at(file_contents->length() - 1));
+    (*file_contents)[5] = 0;
+    EXPECT_STREQ("Linux", file_contents->c_str());
 }
 
 TEST(util, WriteFileBinary) {
@@ -95,29 +85,23 @@
     ASSERT_EQ(10u, contents.size());
 
     TemporaryFile tf;
-    std::string err;
     ASSERT_TRUE(tf.fd != -1);
-    EXPECT_TRUE(WriteFile(tf.path, contents, &err)) << strerror(errno);
-    EXPECT_EQ("", err);
+    EXPECT_TRUE(WriteFile(tf.path, contents)) << strerror(errno);
 
-    std::string read_back_contents;
-    EXPECT_TRUE(ReadFile(tf.path, &read_back_contents, &err)) << strerror(errno);
-    EXPECT_EQ("", err);
-    EXPECT_EQ(contents, read_back_contents);
-    EXPECT_EQ(10u, read_back_contents.size());
+    auto read_back_contents = ReadFile(tf.path);
+    ASSERT_TRUE(read_back_contents) << strerror(errno);
+    EXPECT_EQ(contents, *read_back_contents);
+    EXPECT_EQ(10u, read_back_contents->size());
 }
 
 TEST(util, WriteFileNotExist) {
     std::string s("hello");
-    std::string s2("hello");
     TemporaryDir test_dir;
     std::string path = android::base::StringPrintf("%s/does-not-exist", test_dir.path);
-    std::string err;
-    EXPECT_TRUE(WriteFile(path, s, &err));
-    EXPECT_EQ("", err);
-    EXPECT_TRUE(ReadFile(path, &s2, &err));
-    EXPECT_EQ("", err);
-    EXPECT_EQ(s, s2);
+    EXPECT_TRUE(WriteFile(path, s));
+    auto file_contents = ReadFile(path);
+    ASSERT_TRUE(file_contents);
+    EXPECT_EQ(s, *file_contents);
     struct stat sb;
     int fd = open(path.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
     EXPECT_NE(-1, fd);
@@ -127,37 +111,30 @@
 }
 
 TEST(util, WriteFileExist) {
-    std::string s2("");
     TemporaryFile tf;
     ASSERT_TRUE(tf.fd != -1);
-    std::string err;
-    EXPECT_TRUE(WriteFile(tf.path, "1hello1", &err)) << strerror(errno);
-    EXPECT_EQ("", err);
-    EXPECT_TRUE(ReadFile(tf.path, &s2, &err));
-    EXPECT_EQ("", err);
-    EXPECT_STREQ("1hello1", s2.c_str());
-    EXPECT_TRUE(WriteFile(tf.path, "2ll2", &err));
-    EXPECT_EQ("", err);
-    EXPECT_TRUE(ReadFile(tf.path, &s2, &err));
-    EXPECT_EQ("", err);
-    EXPECT_STREQ("2ll2", s2.c_str());
+    EXPECT_TRUE(WriteFile(tf.path, "1hello1")) << strerror(errno);
+    auto file_contents = ReadFile(tf.path);
+    ASSERT_TRUE(file_contents);
+    EXPECT_EQ("1hello1", *file_contents);
+    EXPECT_TRUE(WriteFile(tf.path, "2ll2"));
+    file_contents = ReadFile(tf.path);
+    ASSERT_TRUE(file_contents);
+    EXPECT_EQ("2ll2", *file_contents);
 }
 
 TEST(util, DecodeUid) {
-    uid_t decoded_uid;
-    std::string err;
+    auto decoded_uid = DecodeUid("root");
+    EXPECT_TRUE(decoded_uid);
+    EXPECT_EQ(0U, *decoded_uid);
 
-    EXPECT_TRUE(DecodeUid("root", &decoded_uid, &err));
-    EXPECT_EQ("", err);
-    EXPECT_EQ(0U, decoded_uid);
+    decoded_uid = DecodeUid("toot");
+    EXPECT_FALSE(decoded_uid);
+    EXPECT_EQ("getpwnam failed: No such file or directory", decoded_uid.error());
 
-    EXPECT_FALSE(DecodeUid("toot", &decoded_uid, &err));
-    EXPECT_EQ("getpwnam failed: No such file or directory", err);
-    EXPECT_EQ(UINT_MAX, decoded_uid);
-
-    EXPECT_TRUE(DecodeUid("123", &decoded_uid, &err));
-    EXPECT_EQ("", err);
-    EXPECT_EQ(123U, decoded_uid);
+    decoded_uid = DecodeUid("123");
+    EXPECT_TRUE(decoded_uid);
+    EXPECT_EQ(123U, *decoded_uid);
 }
 
 TEST(util, is_dir) {
@@ -170,7 +147,7 @@
 TEST(util, mkdir_recursive) {
     TemporaryDir test_dir;
     std::string path = android::base::StringPrintf("%s/three/directories/deep", test_dir.path);
-    EXPECT_EQ(0, mkdir_recursive(path, 0755, nullptr));
+    EXPECT_TRUE(mkdir_recursive(path, 0755));
     std::string path1 = android::base::StringPrintf("%s/three", test_dir.path);
     EXPECT_TRUE(is_dir(path1.c_str()));
     std::string path2 = android::base::StringPrintf("%s/three/directories", test_dir.path);
@@ -182,7 +159,7 @@
 TEST(util, mkdir_recursive_extra_slashes) {
     TemporaryDir test_dir;
     std::string path = android::base::StringPrintf("%s/three////directories/deep//", test_dir.path);
-    EXPECT_EQ(0, mkdir_recursive(path, 0755, nullptr));
+    EXPECT_TRUE(mkdir_recursive(path, 0755));
     std::string path1 = android::base::StringPrintf("%s/three", test_dir.path);
     EXPECT_TRUE(is_dir(path1.c_str()));
     std::string path2 = android::base::StringPrintf("%s/three/directories", test_dir.path);
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
index 83a5bb6..e79bca3 100644
--- a/libbacktrace/UnwindStack.cpp
+++ b/libbacktrace/UnwindStack.cpp
@@ -29,6 +29,7 @@
 #endif
 
 #include <backtrace/Backtrace.h>
+#include <demangle.h>
 #include <unwindstack/Elf.h>
 #include <unwindstack/MapInfo.h>
 #include <unwindstack/Maps.h>
@@ -110,7 +111,9 @@
       frame->map.name = map_info->name;
 
       uint64_t func_offset = 0;
-      if (!elf->GetFunctionName(adjusted_rel_pc, &frame->func_name, &func_offset)) {
+      if (elf->GetFunctionName(adjusted_rel_pc, &frame->func_name, &func_offset)) {
+        frame->func_name = demangle(frame->func_name.c_str());
+      } else {
         frame->func_name = "";
       }
       frame->func_offset = func_offset;
diff --git a/trusty/keymaster/keymaster_ipc.h b/trusty/keymaster/keymaster_ipc.h
index b38eb05..d63757b 100644
--- a/trusty/keymaster/keymaster_ipc.h
+++ b/trusty/keymaster/keymaster_ipc.h
@@ -24,7 +24,8 @@
 // Commands
 enum keymaster_command : uint32_t {
     KEYMASTER_RESP_BIT              = 1,
-    KEYMASTER_REQ_SHIFT             = 1,
+    KEYMASTER_STOP_BIT              = 2,
+    KEYMASTER_REQ_SHIFT             = 2,
 
     KM_GENERATE_KEY                 = (0 << KEYMASTER_REQ_SHIFT),
     KM_BEGIN_OPERATION              = (1 << KEYMASTER_REQ_SHIFT),
diff --git a/trusty/keymaster/trusty_keymaster_device.cpp b/trusty/keymaster/trusty_keymaster_device.cpp
index cfe94cc..ff74146 100644
--- a/trusty/keymaster/trusty_keymaster_device.cpp
+++ b/trusty/keymaster/trusty_keymaster_device.cpp
@@ -36,7 +36,8 @@
 #include "trusty_keymaster_device.h"
 #include "trusty_keymaster_ipc.h"
 
-const uint32_t RECV_BUF_SIZE = PAGE_SIZE;
+// Maximum size of message from Trusty is 8K (for RSA attestation key and chain)
+const uint32_t RECV_BUF_SIZE = 2*PAGE_SIZE;
 const uint32_t SEND_BUF_SIZE = (PAGE_SIZE - sizeof(struct keymaster_message) - 16 /* tipc header */);
 
 const size_t kMaximumAttestationChallengeLength = 128;
@@ -770,6 +771,9 @@
     ALOGV("Sending %d byte request\n", (int)req.SerializedSize());
     int rc = trusty_keymaster_call(command, send_buf, req_size, recv_buf, &rsp_size);
     if (rc < 0) {
+        // Reset the connection on tipc error
+        trusty_keymaster_disconnect();
+        trusty_keymaster_connect();
         ALOGE("tipc error: %d\n", rc);
         // TODO(swillden): Distinguish permanent from transient errors and set error_ appropriately.
         return translate_error(rc);
@@ -777,8 +781,7 @@
         ALOGV("Received %d byte response\n", rsp_size);
     }
 
-    const keymaster_message* msg = (keymaster_message*)recv_buf;
-    const uint8_t* p = msg->payload;
+    const uint8_t* p = recv_buf;
     if (!rsp->Deserialize(&p, p + rsp_size)) {
         ALOGE("Error deserializing response of size %d\n", (int)rsp_size);
         return KM_ERROR_UNKNOWN_ERROR;
diff --git a/trusty/keymaster/trusty_keymaster_ipc.cpp b/trusty/keymaster/trusty_keymaster_ipc.cpp
index cdc2778..54b251e 100644
--- a/trusty/keymaster/trusty_keymaster_ipc.cpp
+++ b/trusty/keymaster/trusty_keymaster_ipc.cpp
@@ -23,6 +23,8 @@
 #include <string.h>
 #include <unistd.h>
 
+#include <algorithm>
+
 #include <log/log.h>
 #include <trusty/tipc.h>
 
@@ -31,7 +33,7 @@
 
 #define TRUSTY_DEVICE_NAME "/dev/trusty-ipc-dev0"
 
-static int handle_ = 0;
+static int handle_ = -1;
 
 int trusty_keymaster_connect() {
     int rc = tipc_connect(TRUSTY_DEVICE_NAME, KEYMASTER_PORT);
@@ -45,7 +47,7 @@
 
 int trusty_keymaster_call(uint32_t cmd, void* in, uint32_t in_size, uint8_t* out,
                           uint32_t* out_size) {
-    if (handle_ == 0) {
+    if (handle_ < 0) {
         ALOGE("not connected\n");
         return -EINVAL;
     }
@@ -62,32 +64,43 @@
         ALOGE("failed to send cmd (%d) to %s: %s\n", cmd, KEYMASTER_PORT, strerror(errno));
         return -errno;
     }
+    size_t out_max_size = *out_size;
+    *out_size = 0;
+    struct iovec iov[2];
+    struct keymaster_message header;
+    iov[0] = {.iov_base = &header, .iov_len = sizeof(struct keymaster_message)};
+    while (true) {
+        iov[1] = {
+            .iov_base = out + *out_size,
+            .iov_len = std::min<uint32_t>(KEYMASTER_MAX_BUFFER_LENGTH, out_max_size - *out_size)};
+        rc = readv(handle_, iov, 2);
+        if (rc < 0) {
+            ALOGE("failed to retrieve response for cmd (%d) to %s: %s\n", cmd, KEYMASTER_PORT,
+                  strerror(errno));
+            return -errno;
+        }
 
-    rc = read(handle_, out, *out_size);
-    if (rc < 0) {
-        ALOGE("failed to retrieve response for cmd (%d) to %s: %s\n", cmd, KEYMASTER_PORT,
-              strerror(errno));
-        return -errno;
+        if ((size_t)rc < sizeof(struct keymaster_message)) {
+            ALOGE("invalid response size (%d)\n", (int)rc);
+            return -EINVAL;
+        }
+
+        if ((cmd | KEYMASTER_RESP_BIT) != (header.cmd & ~(KEYMASTER_STOP_BIT))) {
+            ALOGE("invalid command (%d)", header.cmd);
+            return -EINVAL;
+        }
+        *out_size += ((size_t)rc - sizeof(struct keymaster_message));
+        if (header.cmd & KEYMASTER_STOP_BIT) {
+            break;
+        }
     }
 
-    if ((size_t)rc < sizeof(struct keymaster_message)) {
-        ALOGE("invalid response size (%d)\n", (int)rc);
-        return -EINVAL;
-    }
-
-    msg = (struct keymaster_message*)out;
-
-    if ((cmd | KEYMASTER_RESP_BIT) != msg->cmd) {
-        ALOGE("invalid command (%d)", msg->cmd);
-        return -EINVAL;
-    }
-
-    *out_size = ((size_t)rc) - sizeof(struct keymaster_message);
     return rc;
 }
 
 void trusty_keymaster_disconnect() {
-    if (handle_ != 0) {
+    if (handle_ >= 0) {
         tipc_close(handle_);
     }
+    handle_ = -1;
 }