Merge "Be more careful about symlinking a file that actually exists on the host."
diff --git a/base/Android.bp b/base/Android.bp
index aeb8864..8351461 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -149,6 +149,7 @@
         "logging_test.cpp",
         "macros_test.cpp",
         "mapped_file_test.cpp",
+        "no_destructor_test.cpp",
         "parsedouble_test.cpp",
         "parsebool_test.cpp",
         "parseint_test.cpp",
diff --git a/base/include/android-base/no_destructor.h b/base/include/android-base/no_destructor.h
new file mode 100644
index 0000000..ce0dc9f
--- /dev/null
+++ b/base/include/android-base/no_destructor.h
@@ -0,0 +1,94 @@
+#pragma once
+
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <utility>
+
+#include "android-base/macros.h"
+
+namespace android {
+namespace base {
+
+// A wrapper that makes it easy to create an object of type T with static
+// storage duration that:
+// - is only constructed on first access
+// - never invokes the destructor
+// in order to satisfy the styleguide ban on global constructors and
+// destructors.
+//
+// Runtime constant example:
+// const std::string& GetLineSeparator() {
+//  // Forwards to std::string(size_t, char, const Allocator&) constructor.
+//   static const base::NoDestructor<std::string> s(5, '-');
+//   return *s;
+// }
+//
+// More complex initialization with a lambda:
+// const std::string& GetSessionNonce() {
+//   static const base::NoDestructor<std::string> nonce([] {
+//     std::string s(16);
+//     crypto::RandString(s.data(), s.size());
+//     return s;
+//   }());
+//   return *nonce;
+// }
+//
+// NoDestructor<T> stores the object inline, so it also avoids a pointer
+// indirection and a malloc. Also note that since C++11 static local variable
+// initialization is thread-safe and so is this pattern. Code should prefer to
+// use NoDestructor<T> over:
+// - A function scoped static T* or T& that is dynamically initialized.
+// - A global base::LazyInstance<T>.
+//
+// Note that since the destructor is never run, this *will* leak memory if used
+// as a stack or member variable. Furthermore, a NoDestructor<T> should never
+// have global scope as that may require a static initializer.
+template <typename T>
+class NoDestructor {
+ public:
+  // Not constexpr; just write static constexpr T x = ...; if the value should
+  // be a constexpr.
+  template <typename... Args>
+  explicit NoDestructor(Args&&... args) {
+    new (storage_) T(std::forward<Args>(args)...);
+  }
+
+  // Allows copy and move construction of the contained type, to allow
+  // construction from an initializer list, e.g. for std::vector.
+  explicit NoDestructor(const T& x) { new (storage_) T(x); }
+  explicit NoDestructor(T&& x) { new (storage_) T(std::move(x)); }
+
+  NoDestructor(const NoDestructor&) = delete;
+  NoDestructor& operator=(const NoDestructor&) = delete;
+
+  ~NoDestructor() = default;
+
+  const T& operator*() const { return *get(); }
+  T& operator*() { return *get(); }
+
+  const T* operator->() const { return get(); }
+  T* operator->() { return get(); }
+
+  const T* get() const { return reinterpret_cast<const T*>(storage_); }
+  T* get() { return reinterpret_cast<T*>(storage_); }
+
+ private:
+  alignas(T) char storage_[sizeof(T)];
+};
+
+}  // namespace base
+}  // namespace android
diff --git a/base/no_destructor_test.cpp b/base/no_destructor_test.cpp
new file mode 100644
index 0000000..f19468a
--- /dev/null
+++ b/base/no_destructor_test.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/no_destructor.h"
+
+#include <gtest/gtest.h>
+
+struct __attribute__((packed)) Bomb {
+  Bomb() : magic_(123) {}
+
+  ~Bomb() { exit(42); }
+
+  int get() const { return magic_; }
+
+ private:
+  [[maybe_unused]] char padding_;
+  int magic_;
+};
+
+TEST(no_destructor, bomb) {
+  ASSERT_EXIT(({
+                {
+                  Bomb b;
+                  if (b.get() != 123) exit(1);
+                }
+
+                exit(0);
+              }),
+              ::testing::ExitedWithCode(42), "");
+}
+
+TEST(no_destructor, defused) {
+  ASSERT_EXIT(({
+                {
+                  android::base::NoDestructor<Bomb> b;
+                  if (b->get() != 123) exit(1);
+                }
+
+                exit(0);
+              }),
+              ::testing::ExitedWithCode(0), "");
+}
+
+TEST(no_destructor, operators) {
+  android::base::NoDestructor<Bomb> b;
+  const android::base::NoDestructor<Bomb>& c = b;
+  ASSERT_EQ(123, b.get()->get());
+  ASSERT_EQ(123, b->get());
+  ASSERT_EQ(123, (*b).get());
+  ASSERT_EQ(123, c.get()->get());
+  ASSERT_EQ(123, c->get());
+  ASSERT_EQ(123, (*c).get());
+}
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index e7a3ff2..809318c 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -16,11 +16,14 @@
 
 #include "libdm/dm.h"
 
+#include <linux/dm-ioctl.h>
 #include <sys/ioctl.h>
 #include <sys/sysmacros.h>
 #include <sys/types.h>
 
+#include <chrono>
 #include <functional>
+#include <string_view>
 #include <thread>
 
 #include <android-base/file.h>
@@ -79,14 +82,24 @@
     return true;
 }
 
-bool DeviceMapper::DeleteDeviceIfExists(const std::string& name) {
+bool DeviceMapper::DeleteDeviceIfExists(const std::string& name,
+                                        const std::chrono::milliseconds& timeout_ms) {
     if (GetState(name) == DmDeviceState::INVALID) {
         return true;
     }
-    return DeleteDevice(name);
+    return DeleteDevice(name, timeout_ms);
 }
 
-bool DeviceMapper::DeleteDevice(const std::string& name) {
+bool DeviceMapper::DeleteDeviceIfExists(const std::string& name) {
+    return DeleteDeviceIfExists(name, 0ms);
+}
+
+bool DeviceMapper::DeleteDevice(const std::string& name,
+                                const std::chrono::milliseconds& timeout_ms) {
+    std::string unique_path;
+    if (!GetDeviceUniquePath(name, &unique_path)) {
+        LOG(ERROR) << "Failed to get unique path for device " << name;
+    }
     struct dm_ioctl io;
     InitIo(&io, name);
 
@@ -100,9 +113,23 @@
     CHECK(io.flags & DM_UEVENT_GENERATED_FLAG)
             << "Didn't generate uevent for [" << name << "] removal";
 
+    if (timeout_ms <= std::chrono::milliseconds::zero()) {
+        return true;
+    }
+    if (unique_path.empty()) {
+        return false;
+    }
+    if (!WaitForFileDeleted(unique_path, timeout_ms)) {
+        LOG(ERROR) << "Timeout out waiting for " << unique_path << " to be deleted";
+        return false;
+    }
     return true;
 }
 
+bool DeviceMapper::DeleteDevice(const std::string& name) {
+    return DeleteDevice(name, 0ms);
+}
+
 static std::string GenerateUuid() {
     uuid_t uuid_bytes;
     uuid_generate(uuid_bytes);
@@ -479,5 +506,74 @@
     return std::string{spec.target_type, sizeof(spec.target_type)};
 }
 
+static bool ExtractBlockDeviceName(const std::string& path, std::string* name) {
+    static constexpr std::string_view kDevBlockPrefix("/dev/block/");
+    if (android::base::StartsWith(path, kDevBlockPrefix)) {
+        *name = path.substr(kDevBlockPrefix.length());
+        return true;
+    }
+    return false;
+}
+
+bool DeviceMapper::IsDmBlockDevice(const std::string& path) {
+    std::string name;
+    if (!ExtractBlockDeviceName(path, &name)) {
+        return false;
+    }
+    return android::base::StartsWith(name, "dm-");
+}
+
+std::optional<std::string> DeviceMapper::GetDmDeviceNameByPath(const std::string& path) {
+    std::string name;
+    if (!ExtractBlockDeviceName(path, &name)) {
+        LOG(WARNING) << path << " is not a block device";
+        return std::nullopt;
+    }
+    if (!android::base::StartsWith(name, "dm-")) {
+        LOG(WARNING) << path << " is not a dm device";
+        return std::nullopt;
+    }
+    std::string dm_name_file = "/sys/block/" + name + "/dm/name";
+    std::string dm_name;
+    if (!android::base::ReadFileToString(dm_name_file, &dm_name)) {
+        PLOG(ERROR) << "Failed to read file " << dm_name_file;
+        return std::nullopt;
+    }
+    dm_name = android::base::Trim(dm_name);
+    return dm_name;
+}
+
+std::optional<std::string> DeviceMapper::GetParentBlockDeviceByPath(const std::string& path) {
+    std::string name;
+    if (!ExtractBlockDeviceName(path, &name)) {
+        LOG(WARNING) << path << " is not a block device";
+        return std::nullopt;
+    }
+    if (!android::base::StartsWith(name, "dm-")) {
+        // Reached bottom of the device mapper stack.
+        return std::nullopt;
+    }
+    auto slaves_dir = "/sys/block/" + name + "/slaves";
+    auto dir = std::unique_ptr<DIR, decltype(&closedir)>(opendir(slaves_dir.c_str()), closedir);
+    if (dir == nullptr) {
+        PLOG(ERROR) << "Failed to open: " << slaves_dir;
+        return std::nullopt;
+    }
+    std::string sub_device_name = "";
+    for (auto entry = readdir(dir.get()); entry; entry = readdir(dir.get())) {
+        if (entry->d_type != DT_LNK) continue;
+        if (!sub_device_name.empty()) {
+            LOG(ERROR) << "Too many slaves in " << slaves_dir;
+            return std::nullopt;
+        }
+        sub_device_name = entry->d_name;
+    }
+    if (sub_device_name.empty()) {
+        LOG(ERROR) << "No slaves in " << slaves_dir;
+        return std::nullopt;
+    }
+    return "/dev/block/" + sub_device_name;
+}
+
 }  // namespace dm
 }  // namespace android
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
index ed2fa83..b7f31bc 100644
--- a/fs_mgr/libdm/dm_test.cpp
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -29,6 +29,7 @@
 #include <thread>
 
 #include <android-base/file.h>
+#include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <gtest/gtest.h>
 #include <libdm/dm.h>
@@ -520,3 +521,87 @@
     ASSERT_TRUE(target.Valid());
     ASSERT_EQ(target.GetParameterString(), "AES-256-XTS abcdef0123456789 /dev/loop0 0");
 }
+
+TEST(libdm, DeleteDeviceWithTimeout) {
+    unique_fd tmp(CreateTempFile("file_1", 4096));
+    ASSERT_GE(tmp, 0);
+    LoopDevice loop(tmp, 10s);
+    ASSERT_TRUE(loop.valid());
+
+    DmTable table;
+    ASSERT_TRUE(table.Emplace<DmTargetLinear>(0, 1, loop.device(), 0));
+    ASSERT_TRUE(table.valid());
+    TempDevice dev("libdm-test-dm-linear", table);
+    ASSERT_TRUE(dev.valid());
+
+    DeviceMapper& dm = DeviceMapper::Instance();
+
+    std::string path;
+    ASSERT_TRUE(dm.GetDmDevicePathByName("libdm-test-dm-linear", &path));
+    ASSERT_EQ(0, access(path.c_str(), F_OK));
+
+    ASSERT_TRUE(dm.DeleteDevice("libdm-test-dm-linear", 5s));
+    ASSERT_EQ(DmDeviceState::INVALID, dm.GetState("libdm-test-dm-linear"));
+    ASSERT_NE(0, access(path.c_str(), F_OK));
+    ASSERT_EQ(ENOENT, errno);
+}
+
+TEST(libdm, IsDmBlockDevice) {
+    unique_fd tmp(CreateTempFile("file_1", 4096));
+    ASSERT_GE(tmp, 0);
+    LoopDevice loop(tmp, 10s);
+    ASSERT_TRUE(loop.valid());
+    ASSERT_TRUE(android::base::StartsWith(loop.device(), "/dev/block"));
+
+    DmTable table;
+    ASSERT_TRUE(table.Emplace<DmTargetLinear>(0, 1, loop.device(), 0));
+    ASSERT_TRUE(table.valid());
+
+    TempDevice dev("libdm-test-dm-linear", table);
+    ASSERT_TRUE(dev.valid());
+
+    DeviceMapper& dm = DeviceMapper::Instance();
+    ASSERT_TRUE(dm.IsDmBlockDevice(dev.path()));
+    ASSERT_FALSE(dm.IsDmBlockDevice(loop.device()));
+}
+
+TEST(libdm, GetDmDeviceNameByPath) {
+    unique_fd tmp(CreateTempFile("file_1", 4096));
+    ASSERT_GE(tmp, 0);
+    LoopDevice loop(tmp, 10s);
+    ASSERT_TRUE(loop.valid());
+    ASSERT_TRUE(android::base::StartsWith(loop.device(), "/dev/block"));
+
+    DmTable table;
+    ASSERT_TRUE(table.Emplace<DmTargetLinear>(0, 1, loop.device(), 0));
+    ASSERT_TRUE(table.valid());
+
+    TempDevice dev("libdm-test-dm-linear", table);
+    ASSERT_TRUE(dev.valid());
+
+    DeviceMapper& dm = DeviceMapper::Instance();
+    // Not a dm device, GetDmDeviceNameByPath will return std::nullopt.
+    ASSERT_FALSE(dm.GetDmDeviceNameByPath(loop.device()));
+    auto name = dm.GetDmDeviceNameByPath(dev.path());
+    ASSERT_EQ("libdm-test-dm-linear", *name);
+}
+
+TEST(libdm, GetParentBlockDeviceByPath) {
+    unique_fd tmp(CreateTempFile("file_1", 4096));
+    ASSERT_GE(tmp, 0);
+    LoopDevice loop(tmp, 10s);
+    ASSERT_TRUE(loop.valid());
+    ASSERT_TRUE(android::base::StartsWith(loop.device(), "/dev/block"));
+
+    DmTable table;
+    ASSERT_TRUE(table.Emplace<DmTargetLinear>(0, 1, loop.device(), 0));
+    ASSERT_TRUE(table.valid());
+
+    TempDevice dev("libdm-test-dm-linear", table);
+    ASSERT_TRUE(dev.valid());
+
+    DeviceMapper& dm = DeviceMapper::Instance();
+    ASSERT_FALSE(dm.GetParentBlockDeviceByPath(loop.device()));
+    auto sub_block_device = dm.GetParentBlockDeviceByPath(dev.path());
+    ASSERT_EQ(loop.device(), *sub_block_device);
+}
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
index e25ce7f..418210c 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -90,6 +90,10 @@
     // Returns 'true' on success, false otherwise.
     bool DeleteDevice(const std::string& name);
     bool DeleteDeviceIfExists(const std::string& name);
+    // Removes a device mapper device with the given name and waits for |timeout_ms| milliseconds
+    // for the corresponding block device to be deleted.
+    bool DeleteDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms);
+    bool DeleteDeviceIfExists(const std::string& name, const std::chrono::milliseconds& timeout_ms);
 
     // Fetches and returns the complete state of the underlying device mapper
     // device with given name.
@@ -210,6 +214,19 @@
 
     static std::string GetTargetType(const struct dm_target_spec& spec);
 
+    // Returns true if given path is a path to a dm block device.
+    bool IsDmBlockDevice(const std::string& path);
+
+    // Returns name of a dm-device with the given path, or std::nulloptr if given path is not a
+    // dm-device.
+    std::optional<std::string> GetDmDeviceNameByPath(const std::string& path);
+
+    // Returns a parent block device of a dm device with the given path, or std::nullopt if:
+    //  * Given path doesn't correspond to a dm device.
+    //  * A dm device is based on top of more than one block devices.
+    //  * A failure occurred.
+    std::optional<std::string> GetParentBlockDeviceByPath(const std::string& path);
+
   private:
     // Maximum possible device mapper targets registered in the kernel.
     // This is only used to read the list of targets from kernel so we allocate
diff --git a/fs_mgr/libdm/utility.cpp b/fs_mgr/libdm/utility.cpp
index eccf2fb..f252565 100644
--- a/fs_mgr/libdm/utility.cpp
+++ b/fs_mgr/libdm/utility.cpp
@@ -52,5 +52,15 @@
     return WaitForCondition(condition, timeout_ms);
 }
 
+bool WaitForFileDeleted(const std::string& path, const std::chrono::milliseconds& timeout_ms) {
+    auto condition = [&]() -> WaitResult {
+        if (access(path.c_str(), F_OK) == 0 || errno != ENOENT) {
+            return WaitResult::Wait;
+        }
+        return WaitResult::Done;
+    };
+    return WaitForCondition(condition, timeout_ms);
+}
+
 }  // namespace dm
 }  // namespace android
diff --git a/fs_mgr/libdm/utility.h b/fs_mgr/libdm/utility.h
index f1dce9e..58fa96b 100644
--- a/fs_mgr/libdm/utility.h
+++ b/fs_mgr/libdm/utility.h
@@ -23,6 +23,7 @@
 enum class WaitResult { Wait, Done, Fail };
 
 bool WaitForFile(const std::string& path, const std::chrono::milliseconds& timeout_ms);
+bool WaitForFileDeleted(const std::string& path, const std::chrono::milliseconds& timeout_ms);
 bool WaitForCondition(const std::function<WaitResult()>& condition,
                       const std::chrono::milliseconds& timeout_ms);
 
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 1d72c70..30d01a6 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -121,6 +121,34 @@
     ],
 }
 
+cc_library_static {
+    name: "libsnapshot_test_helpers",
+    defaults: ["libsnapshot_defaults"],
+    export_include_dirs: [
+        "include_test",
+    ],
+    srcs: [
+        "test_helpers.cpp",
+    ],
+    shared_libs: [
+        "android.hardware.boot@1.1",
+        "libcrypto",
+    ],
+    export_shared_lib_headers: [
+        "android.hardware.boot@1.1",
+    ],
+    header_libs: [
+        "libstorage_literals_headers",
+    ],
+    export_header_lib_headers: [
+        "libstorage_literals_headers",
+    ],
+    static_libs: [
+        "libgtest",
+        "libgmock",
+    ],
+}
+
 cc_test {
     name: "libsnapshot_test",
     defaults: ["libsnapshot_defaults"],
@@ -144,6 +172,7 @@
         "libgmock",
         "liblp",
         "libsnapshot",
+        "libsnapshot_test_helpers",
         "libsparse",
         "libz",
     ],
diff --git a/fs_mgr/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
similarity index 100%
rename from fs_mgr/libsnapshot/test_helpers.h
rename to fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
diff --git a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
index eae6c35..9da3f05 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
+++ b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
@@ -18,9 +18,10 @@
 #include <liblp/builder.h>
 #include <liblp/property_fetcher.h>
 
+#include <libsnapshot/test_helpers.h>
+
 #include "dm_snapshot_internals.h"
 #include "partition_cow_creator.h"
-#include "test_helpers.h"
 #include "utility.h"
 
 using namespace android::fs_mgr;
diff --git a/fs_mgr/libsnapshot/snapshot_metadata_updater_test.cpp b/fs_mgr/libsnapshot/snapshot_metadata_updater_test.cpp
index 4fd8759..337be4f 100644
--- a/fs_mgr/libsnapshot/snapshot_metadata_updater_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_metadata_updater_test.cpp
@@ -24,7 +24,7 @@
 #include <liblp/builder.h>
 #include <storage_literals/storage_literals.h>
 
-#include "test_helpers.h"
+#include <libsnapshot/test_helpers.h>
 
 using namespace android::storage_literals;
 using android::fs_mgr::LpMetadata;
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 9e5fef3..ff943f2 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -38,7 +38,7 @@
 #include <storage_literals/storage_literals.h>
 
 #include <android/snapshot/snapshot.pb.h>
-#include "test_helpers.h"
+#include <libsnapshot/test_helpers.h>
 #include "utility.h"
 
 namespace android {
diff --git a/fs_mgr/libsnapshot/test_helpers.cpp b/fs_mgr/libsnapshot/test_helpers.cpp
index 2d62347..f7f25af 100644
--- a/fs_mgr/libsnapshot/test_helpers.cpp
+++ b/fs_mgr/libsnapshot/test_helpers.cpp
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "test_helpers.h"
+#include <libsnapshot/test_helpers.h>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
diff --git a/init/Android.mk b/init/Android.mk
index 997b2bc..ade4fb5 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -73,7 +73,7 @@
 LOCAL_REQUIRED_MODULES := \
    adb_debug.prop \
 
-# Set up the same mount points on the ramdisk that system-as-root contains.
+# Set up the directories that first stage init mounts on.
 LOCAL_POST_INSTALL_CMD := mkdir -p \
     $(TARGET_RAMDISK_OUT)/apex \
     $(TARGET_RAMDISK_OUT)/debug_ramdisk \
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 1028330..62a19ab 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -360,57 +360,61 @@
     return {};
 }
 
-// mkdir <path> [mode] [owner] [group] [<option> ...]
-static Result<void> do_mkdir(const BuiltinArguments& args) {
-    auto options = ParseMkdir(args.args);
-    if (!options) return options.error();
+static Result<void> make_dir_with_options(const MkdirOptions& options) {
     std::string ref_basename;
-    if (options->ref_option == "ref") {
+    if (options.ref_option == "ref") {
         ref_basename = fscrypt_key_ref;
-    } else if (options->ref_option == "per_boot_ref") {
+    } else if (options.ref_option == "per_boot_ref") {
         ref_basename = fscrypt_key_per_boot_ref;
     } else {
-        return Error() << "Unknown key option: '" << options->ref_option << "'";
+        return Error() << "Unknown key option: '" << options.ref_option << "'";
     }
 
     struct stat mstat;
-    if (lstat(options->target.c_str(), &mstat) != 0) {
+    if (lstat(options.target.c_str(), &mstat) != 0) {
         if (errno != ENOENT) {
-            return ErrnoError() << "lstat() failed on " << options->target;
+            return ErrnoError() << "lstat() failed on " << options.target;
         }
-        if (!make_dir(options->target, options->mode)) {
-            return ErrnoErrorIgnoreEnoent() << "mkdir() failed on " << options->target;
+        if (!make_dir(options.target, options.mode)) {
+            return ErrnoErrorIgnoreEnoent() << "mkdir() failed on " << options.target;
         }
-        if (lstat(options->target.c_str(), &mstat) != 0) {
-            return ErrnoError() << "lstat() failed on new " << options->target;
+        if (lstat(options.target.c_str(), &mstat) != 0) {
+            return ErrnoError() << "lstat() failed on new " << options.target;
         }
     }
     if (!S_ISDIR(mstat.st_mode)) {
-        return Error() << "Not a directory on " << options->target;
+        return Error() << "Not a directory on " << options.target;
     }
-    bool needs_chmod = (mstat.st_mode & ~S_IFMT) != options->mode;
-    if ((options->uid != static_cast<uid_t>(-1) && options->uid != mstat.st_uid) ||
-        (options->gid != static_cast<gid_t>(-1) && options->gid != mstat.st_gid)) {
-        if (lchown(options->target.c_str(), options->uid, options->gid) == -1) {
-            return ErrnoError() << "lchown failed on " << options->target;
+    bool needs_chmod = (mstat.st_mode & ~S_IFMT) != options.mode;
+    if ((options.uid != static_cast<uid_t>(-1) && options.uid != mstat.st_uid) ||
+        (options.gid != static_cast<gid_t>(-1) && options.gid != mstat.st_gid)) {
+        if (lchown(options.target.c_str(), options.uid, options.gid) == -1) {
+            return ErrnoError() << "lchown failed on " << options.target;
         }
         // chown may have cleared S_ISUID and S_ISGID, chmod again
         needs_chmod = true;
     }
     if (needs_chmod) {
-        if (fchmodat(AT_FDCWD, options->target.c_str(), options->mode, AT_SYMLINK_NOFOLLOW) == -1) {
-            return ErrnoError() << "fchmodat() failed on " << options->target;
+        if (fchmodat(AT_FDCWD, options.target.c_str(), options.mode, AT_SYMLINK_NOFOLLOW) == -1) {
+            return ErrnoError() << "fchmodat() failed on " << options.target;
         }
     }
     if (fscrypt_is_native()) {
-        if (!FscryptSetDirectoryPolicy(ref_basename, options->fscrypt_action, options->target)) {
+        if (!FscryptSetDirectoryPolicy(ref_basename, options.fscrypt_action, options.target)) {
             return reboot_into_recovery(
-                    {"--prompt_and_wipe_data", "--reason=set_policy_failed:"s + options->target});
+                    {"--prompt_and_wipe_data", "--reason=set_policy_failed:"s + options.target});
         }
     }
     return {};
 }
 
+// mkdir <path> [mode] [owner] [group] [<option> ...]
+static Result<void> do_mkdir(const BuiltinArguments& args) {
+    auto options = ParseMkdir(args.args);
+    if (!options) return options.error();
+    return make_dir_with_options(*options);
+}
+
 /* umount <path> */
 static Result<void> do_umount(const BuiltinArguments& args) {
     if (umount(args[1].c_str()) < 0) {
@@ -1172,7 +1176,7 @@
     return {};
 }
 
-static Result<void> do_parse_apex_configs(const BuiltinArguments& args) {
+static Result<void> parse_apex_configs() {
     glob_t glob_result;
     static constexpr char glob_pattern[] = "/apex/*/etc/*.rc";
     const int ret = glob(glob_pattern, GLOB_MARK, nullptr, &glob_result);
@@ -1181,7 +1185,7 @@
         return Error() << "glob pattern '" << glob_pattern << "' failed";
     }
     std::vector<std::string> configs;
-    Parser parser = CreateServiceOnlyParser(ServiceList::GetInstance());
+    Parser parser = CreateServiceOnlyParser(ServiceList::GetInstance(), true);
     for (size_t i = 0; i < glob_result.gl_pathc; i++) {
         std::string path = glob_result.gl_pathv[i];
         // Filter-out /apex/<name>@<ver> paths. The paths are bind-mounted to
@@ -1211,6 +1215,45 @@
     }
 }
 
+/*
+ * Creates a directory under /data/misc/apexdata/ for each APEX.
+ */
+static Result<void> create_apex_data_dirs() {
+    auto dirp = std::unique_ptr<DIR, int (*)(DIR*)>(opendir("/apex"), closedir);
+    if (!dirp) {
+        return ErrnoError() << "Unable to open apex directory";
+    }
+    struct dirent* entry;
+    while ((entry = readdir(dirp.get())) != nullptr) {
+        if (entry->d_type != DT_DIR) continue;
+
+        const char* name = entry->d_name;
+        // skip any starting with "."
+        if (name[0] == '.') continue;
+
+        if (strchr(name, '@') != nullptr) continue;
+
+        auto path = "/data/misc/apexdata/" + std::string(name);
+        auto system_uid = DecodeUid("system");
+        auto options =
+                MkdirOptions{path, 0700, *system_uid, *system_uid, FscryptAction::kNone, "ref"};
+        make_dir_with_options(options);
+    }
+    return {};
+}
+
+static Result<void> do_perform_apex_config(const BuiltinArguments& args) {
+    auto create_dirs = create_apex_data_dirs();
+    if (!create_dirs) {
+        return create_dirs.error();
+    }
+    auto parse_configs = parse_apex_configs();
+    if (!parse_configs) {
+        return parse_configs.error();
+    }
+    return {};
+}
+
 static Result<void> do_enter_default_mount_ns(const BuiltinArguments& args) {
     if (SwitchToDefaultMountNamespace()) {
         return {};
@@ -1271,7 +1314,7 @@
         // mount and umount are run in the same context as mount_all for symmetry.
         {"mount_all",               {1,     kMax, {false,  do_mount_all}}},
         {"mount",                   {3,     kMax, {false,  do_mount}}},
-        {"parse_apex_configs",      {0,     0,    {false,  do_parse_apex_configs}}},
+        {"perform_apex_config",     {0,     0,    {false,  do_perform_apex_config}}},
         {"umount",                  {1,     1,    {false,  do_umount}}},
         {"umount_all",              {1,     1,    {false,  do_umount_all}}},
         {"readahead",               {1,     2,    {true,   do_readahead}}},
diff --git a/init/init.cpp b/init/init.cpp
index c457de6..6ba64ee 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -121,11 +121,12 @@
 }
 
 // parser that only accepts new services
-Parser CreateServiceOnlyParser(ServiceList& service_list) {
+Parser CreateServiceOnlyParser(ServiceList& service_list, bool from_apex) {
     Parser parser;
 
-    parser.AddSectionParser("service", std::make_unique<ServiceParser>(
-                                               &service_list, subcontext.get(), std::nullopt));
+    parser.AddSectionParser("service",
+                            std::make_unique<ServiceParser>(&service_list, subcontext.get(),
+                                                            std::nullopt, from_apex));
     return parser;
 }
 
diff --git a/init/init.h b/init/init.h
index 0805940..4bbca6f 100644
--- a/init/init.h
+++ b/init/init.h
@@ -29,7 +29,7 @@
 namespace init {
 
 Parser CreateParser(ActionManager& action_manager, ServiceList& service_list);
-Parser CreateServiceOnlyParser(ServiceList& service_list);
+Parser CreateServiceOnlyParser(ServiceList& service_list, bool from_apex);
 
 bool start_waiting_for_property(const char *name, const char *value);
 
diff --git a/init/persistent_properties.cpp b/init/persistent_properties.cpp
index baa9ad4..1758cfa 100644
--- a/init/persistent_properties.cpp
+++ b/init/persistent_properties.cpp
@@ -31,10 +31,11 @@
 
 #include "util.h"
 
+using android::base::Dirname;
 using android::base::ReadFdToString;
 using android::base::StartsWith;
-using android::base::WriteStringToFd;
 using android::base::unique_fd;
+using android::base::WriteStringToFd;
 
 namespace android {
 namespace init {
@@ -191,6 +192,18 @@
         unlink(temp_filename.c_str());
         return Error(saved_errno) << "Unable to rename persistent property file";
     }
+
+    // rename() is atomic with regards to the kernel's filesystem buffers, but the parent
+    // directories must be fsync()'ed otherwise, the rename is not necessarily written to storage.
+    // Note in this case, that the source and destination directories are the same, so only one
+    // fsync() is required.
+    auto dir = Dirname(persistent_property_filename);
+    auto dir_fd = unique_fd{open(dir.c_str(), O_DIRECTORY | O_RDONLY | O_CLOEXEC)};
+    if (dir_fd < 0) {
+        return ErrnoError() << "Unable to open persistent properties directory for fsync()";
+    }
+    fsync(dir_fd);
+
     return {};
 }
 
diff --git a/init/reboot.cpp b/init/reboot.cpp
index e9d918e..0e61234 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -797,6 +797,14 @@
     if (!SwitchToBootstrapMountNamespaceIfNeeded()) {
         return Error() << "Failed to switch to bootstrap namespace";
     }
+    // Remove services that were defined in an APEX.
+    ServiceList::GetInstance().RemoveServiceIf([](const std::unique_ptr<Service>& s) -> bool {
+        if (s->is_from_apex()) {
+            LOG(INFO) << "Removing service '" << s->name() << "' because it's defined in an APEX";
+            return true;
+        }
+        return false;
+    });
     // Re-enable services
     for (const auto& s : were_enabled) {
         LOG(INFO) << "Re-enabling service '" << s->name() << "'";
diff --git a/init/service.cpp b/init/service.cpp
index cc97d94..be46585 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -131,13 +131,13 @@
 bool Service::is_exec_service_running_ = false;
 
 Service::Service(const std::string& name, Subcontext* subcontext_for_restart_commands,
-                 const std::vector<std::string>& args)
-    : Service(name, 0, 0, 0, {}, 0, "", subcontext_for_restart_commands, args) {}
+                 const std::vector<std::string>& args, bool from_apex)
+    : Service(name, 0, 0, 0, {}, 0, "", subcontext_for_restart_commands, args, from_apex) {}
 
 Service::Service(const std::string& name, unsigned flags, uid_t uid, gid_t gid,
                  const std::vector<gid_t>& supp_gids, int namespace_flags,
                  const std::string& seclabel, Subcontext* subcontext_for_restart_commands,
-                 const std::vector<std::string>& args)
+                 const std::vector<std::string>& args, bool from_apex)
     : name_(name),
       classnames_({"default"}),
       flags_(flags),
@@ -155,7 +155,8 @@
                  "onrestart", {}),
       oom_score_adjust_(DEFAULT_OOM_SCORE_ADJUST),
       start_order_(0),
-      args_(args) {}
+      args_(args),
+      from_apex_(from_apex) {}
 
 void Service::NotifyStateChange(const std::string& new_state) const {
     if ((flags_ & SVC_TEMPORARY) != 0) {
@@ -763,7 +764,7 @@
     }
 
     return std::make_unique<Service>(name, flags, *uid, *gid, supp_gids, namespace_flags, seclabel,
-                                     nullptr, str_args);
+                                     nullptr, str_args, false);
 }
 
 }  // namespace init
diff --git a/init/service.h b/init/service.h
index f842b3c..cf3f0c2 100644
--- a/init/service.h
+++ b/init/service.h
@@ -65,11 +65,12 @@
 
   public:
     Service(const std::string& name, Subcontext* subcontext_for_restart_commands,
-            const std::vector<std::string>& args);
+            const std::vector<std::string>& args, bool from_apex = false);
 
     Service(const std::string& name, unsigned flags, uid_t uid, gid_t gid,
             const std::vector<gid_t>& supp_gids, int namespace_flags, const std::string& seclabel,
-            Subcontext* subcontext_for_restart_commands, const std::vector<std::string>& args);
+            Subcontext* subcontext_for_restart_commands, const std::vector<std::string>& args,
+            bool from_apex = false);
 
     static Result<std::unique_ptr<Service>> MakeTemporaryOneshotService(
             const std::vector<std::string>& args);
@@ -128,6 +129,7 @@
     const std::vector<std::string>& args() const { return args_; }
     bool is_updatable() const { return updatable_; }
     bool is_post_data() const { return post_data_; }
+    bool is_from_apex() const { return from_apex_; }
 
   private:
     void NotifyStateChange(const std::string& new_state) const;
@@ -199,6 +201,8 @@
     bool running_at_post_data_reset_ = false;
 
     std::optional<std::string> on_failure_reboot_target_;
+
+    bool from_apex_ = false;
 };
 
 }  // namespace init
diff --git a/init/service_list.h b/init/service_list.h
index ee2c702..1838624 100644
--- a/init/service_list.h
+++ b/init/service_list.h
@@ -34,6 +34,11 @@
 
     void AddService(std::unique_ptr<Service> service);
     void RemoveService(const Service& svc);
+    template <class UnaryPredicate>
+    void RemoveServiceIf(UnaryPredicate predicate) {
+        services_.erase(std::remove_if(services_.begin(), services_.end(), predicate),
+                        services_.end());
+    }
 
     template <typename T, typename F = decltype(&Service::name)>
     Service* FindService(T value, F function = &Service::name) const {
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index 154d1dd..1d431e3 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -569,7 +569,7 @@
         }
     }
 
-    service_ = std::make_unique<Service>(name, restart_action_subcontext, str_args);
+    service_ = std::make_unique<Service>(name, restart_action_subcontext, str_args, from_apex_);
     return {};
 }
 
diff --git a/init/service_parser.h b/init/service_parser.h
index b1281f5..7bb0cc0 100644
--- a/init/service_parser.h
+++ b/init/service_parser.h
@@ -31,11 +31,13 @@
   public:
     ServiceParser(
             ServiceList* service_list, Subcontext* subcontext,
-            const std::optional<InterfaceInheritanceHierarchyMap>& interface_inheritance_hierarchy)
+            const std::optional<InterfaceInheritanceHierarchyMap>& interface_inheritance_hierarchy,
+            bool from_apex = false)
         : service_list_(service_list),
           subcontext_(subcontext),
           interface_inheritance_hierarchy_(interface_inheritance_hierarchy),
-          service_(nullptr) {}
+          service_(nullptr),
+          from_apex_(from_apex) {}
     Result<void> ParseSection(std::vector<std::string>&& args, const std::string& filename,
                               int line) override;
     Result<void> ParseLineSection(std::vector<std::string>&& args, int line) override;
@@ -89,6 +91,7 @@
     std::optional<InterfaceInheritanceHierarchyMap> interface_inheritance_hierarchy_;
     std::unique_ptr<Service> service_;
     std::string filename_;
+    bool from_apex_ = false;
 };
 
 }  // namespace init
diff --git a/liblog/include/log/log_read.h b/liblog/include/log/log_read.h
index 6601072..18c1c33 100644
--- a/liblog/include/log/log_read.h
+++ b/liblog/include/log/log_read.h
@@ -139,8 +139,7 @@
                                       char* buf, size_t len);
 ssize_t android_logger_get_prune_list(struct logger_list* logger_list,
                                       char* buf, size_t len);
-int android_logger_set_prune_list(struct logger_list* logger_list, char* buf,
-                                  size_t len);
+int android_logger_set_prune_list(struct logger_list* logger_list, const char* buf, size_t len);
 
 #define ANDROID_LOG_RDONLY O_RDONLY
 #define ANDROID_LOG_WRONLY O_WRONLY
diff --git a/liblog/logd_reader.cpp b/liblog/logd_reader.cpp
index 96e7a61..6865c14 100644
--- a/liblog/logd_reader.cpp
+++ b/liblog/logd_reader.cpp
@@ -33,6 +33,8 @@
 #include <time.h>
 #include <unistd.h>
 
+#include <string>
+
 #include <cutils/sockets.h>
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
@@ -249,22 +251,14 @@
   return SendLogdControlMessage(buf, len);
 }
 
-int android_logger_set_prune_list(struct logger_list* logger_list, char* buf, size_t len) {
+int android_logger_set_prune_list(struct logger_list* logger_list, const char* buf, size_t len) {
   if (logger_list->mode & ANDROID_LOG_PSTORE) {
     return -EINVAL;
   }
 
-  const char cmd[] = "setPruneList ";
-  const size_t cmdlen = sizeof(cmd) - 1;
+  std::string cmd = "setPruneList " + std::string{buf, len};
 
-  if (strlen(buf) > (len - cmdlen)) {
-    return -ENOMEM; /* KISS */
-  }
-  memmove(buf + cmdlen, buf, len - cmdlen);
-  buf[len - 1] = '\0';
-  memcpy(buf, cmd, cmdlen);
-
-  return check_log_success(buf, SendLogdControlMessage(buf, len));
+  return check_log_success(cmd.data(), SendLogdControlMessage(cmd.data(), cmd.size()));
 }
 
 static int logdOpen(struct logger_list* logger_list) {
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
index 250e600..0ab68db 100644
--- a/libunwindstack/Maps.cpp
+++ b/libunwindstack/Maps.cpp
@@ -139,6 +139,9 @@
       if (start == info->start && end == info->end && flags == info->flags && *name == info->name) {
         // No need to check
         search_map_idx = old_map_idx + 1;
+        if (new_map_idx + 1 < maps_.size()) {
+          maps_[new_map_idx + 1]->prev_map = info.get();
+        }
         maps_[new_map_idx] = nullptr;
         total_entries--;
         break;
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index c0e11d3..7b18438 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -17,6 +17,7 @@
 #include <ctype.h>
 #include <dirent.h>
 #include <errno.h>
+#include <error.h>
 #include <fcntl.h>
 #include <getopt.h>
 #include <math.h>
@@ -103,15 +104,6 @@
     bool debug_ = false;
 };
 
-// logd prefixes records with a length field
-#define RECORD_LENGTH_FIELD_SIZE_BYTES sizeof(uint32_t)
-
-enum helpType { HELP_FALSE, HELP_TRUE, HELP_FORMAT };
-
-// if show_help is set, newline required in fmt statement to transition to usage
-static void LogcatPanic(enum helpType showHelp, const char* fmt, ...) __printflike(2, 3)
-        __attribute__((__noreturn__));
-
 #ifndef F2FS_IOC_SET_PIN_FILE
 #define F2FS_IOCTL_MAGIC       0xf5
 #define F2FS_IOC_SET_PIN_FILE _IOW(F2FS_IOCTL_MAGIC, 13, __u32)
@@ -170,7 +162,7 @@
     output_fd_.reset(openLogFile(output_file_name_, log_rotate_size_kb_));
 
     if (!output_fd_.ok()) {
-        LogcatPanic(HELP_FALSE, "couldn't open output file");
+        error(EXIT_FAILURE, errno, "Couldn't open output file");
     }
 
     out_byte_count_ = 0;
@@ -209,7 +201,7 @@
             bytesWritten = android_log_printLogLine(logformat_.get(), output_fd_.get(), &entry);
 
             if (bytesWritten < 0) {
-                LogcatPanic(HELP_FALSE, "output error");
+                error(EXIT_FAILURE, 0, "Output error.");
             }
         }
     }
@@ -229,7 +221,7 @@
         if (dprintf(output_fd_.get(), "--------- %s %s\n",
                     printed_start_[log_id] ? "switch to" : "beginning of",
                     android_log_id_to_name(log_id)) < 0) {
-            LogcatPanic(HELP_FALSE, "output error");
+            error(EXIT_FAILURE, errno, "Output error");
         }
     }
     last_printed_id_ = log_id;
@@ -259,18 +251,16 @@
     output_fd_.reset(openLogFile(output_file_name_, log_rotate_size_kb_));
 
     if (!output_fd_.ok()) {
-        LogcatPanic(HELP_FALSE, "couldn't open output file");
+        error(EXIT_FAILURE, errno, "Couldn't open output file");
     }
 
     struct stat statbuf;
     if (fstat(output_fd_.get(), &statbuf) == -1) {
-        output_fd_.reset();
-        LogcatPanic(HELP_FALSE, "couldn't get output file stat\n");
+        error(EXIT_FAILURE, errno, "Couldn't get output file stat");
     }
 
     if ((size_t)statbuf.st_size > SIZE_MAX || statbuf.st_size < 0) {
-        output_fd_.reset();
-        LogcatPanic(HELP_FALSE, "invalid output file stat\n");
+        error(EXIT_FAILURE, 0, "Invalid output file stat.");
     }
 
     out_byte_count_ = statbuf.st_size;
@@ -427,27 +417,6 @@
     return std::make_pair(value, multipliers[i]);
 }
 
-static void LogcatPanic(enum helpType showHelp, const char* fmt, ...) {
-    va_list args;
-    va_start(args, fmt);
-    vfprintf(stderr, fmt, args);
-    va_end(args);
-
-    switch (showHelp) {
-        case HELP_TRUE:
-            show_help();
-            break;
-        case HELP_FORMAT:
-            show_format_help();
-            break;
-        case HELP_FALSE:
-        default:
-            break;
-    }
-
-    exit(EXIT_FAILURE);
-}
-
 static char* parseTime(log_time& t, const char* cp) {
     char* ep = t.strptime(cp, "%m-%d %H:%M:%S.%q");
     if (ep) return ep;
@@ -612,13 +581,12 @@
                 // only long options
                 if (long_options[option_index].name == pid_str) {
                     if (pid != 0) {
-                        LogcatPanic(HELP_TRUE, "Only supports one PID argument.\n");
+                        error(EXIT_FAILURE, 0, "Only one --pid argument can be provided.");
                     }
 
-                    // ToDo: determine runtime PID_MAX?
                     if (!ParseUint(optarg, &pid) || pid < 1) {
-                        LogcatPanic(HELP_TRUE, "%s %s out of range\n",
-                                    long_options[option_index].name, optarg);
+                        error(EXIT_FAILURE, 0, "%s %s out of range.",
+                              long_options[option_index].name, optarg);
                     }
                     break;
                 }
@@ -628,8 +596,8 @@
                     // ToDo: implement API that supports setting a wrap timeout
                     size_t dummy = ANDROID_LOG_WRAP_DEFAULT_TIMEOUT;
                     if (optarg && (!ParseUint(optarg, &dummy) || dummy < 1)) {
-                        LogcatPanic(HELP_TRUE, "%s %s out of range\n",
-                                    long_options[option_index].name, optarg);
+                        error(EXIT_FAILURE, 0, "%s %s out of range.",
+                              long_options[option_index].name, optarg);
                     }
                     if (dummy != ANDROID_LOG_WRAP_DEFAULT_TIMEOUT) {
                         fprintf(stderr, "WARNING: %s %u seconds, ignoring %zu\n",
@@ -678,13 +646,13 @@
                 if (strspn(optarg, "0123456789") != strlen(optarg)) {
                     char* cp = parseTime(tail_time, optarg);
                     if (!cp) {
-                        LogcatPanic(HELP_FALSE, "-%c \"%s\" not in time format\n", c, optarg);
+                        error(EXIT_FAILURE, 0, "-%c '%s' not in time format.", c, optarg);
                     }
                     if (*cp) {
                         char ch = *cp;
                         *cp = '\0';
-                        fprintf(stderr, "WARNING: -%c \"%s\"\"%c%s\" time truncated\n", c, optarg,
-                                ch, cp + 1);
+                        fprintf(stderr, "WARNING: -%c '%s' '%c%s' time truncated\n", c, optarg, ch,
+                                cp + 1);
                         *cp = ch;
                     }
                 } else {
@@ -705,8 +673,8 @@
 
             case 'm': {
                 if (!ParseUint(optarg, &max_count_) || max_count_ < 1) {
-                    LogcatPanic(HELP_FALSE, "-%c \"%s\" isn't an integer greater than zero\n", c,
-                                optarg);
+                    error(EXIT_FAILURE, 0, "-%c '%s' isn't an integer greater than zero.", c,
+                          optarg);
                 }
             } break;
 
@@ -719,7 +687,7 @@
 
             case 'G': {
                 if (!ParseByteCount(optarg, &setLogSize) || setLogSize < 1) {
-                    LogcatPanic(HELP_FALSE, "ERROR: -G <num><multiplier>\n");
+                    error(EXIT_FAILURE, 0, "-G must be specified as <num><multiplier>.");
                 }
             } break;
 
@@ -743,7 +711,8 @@
                     } else {
                         log_id_t log_id = android_name_to_log_id(buffer.c_str());
                         if (log_id >= LOG_ID_MAX) {
-                            LogcatPanic(HELP_TRUE, "unknown buffer %s\n", buffer.c_str());
+                            error(EXIT_FAILURE, 0, "Unknown buffer '%s' listed for -b.",
+                                  buffer.c_str());
                         }
                         if (log_id == LOG_ID_SECURITY) {
                             security_buffer_selected = true;
@@ -767,13 +736,13 @@
 
             case 'r':
                 if (!ParseUint(optarg, &log_rotate_size_kb_) || log_rotate_size_kb_ < 1) {
-                    LogcatPanic(HELP_TRUE, "Invalid parameter \"%s\" to -r\n", optarg);
+                    error(EXIT_FAILURE, 0, "Invalid parameter '%s' to -r.", optarg);
                 }
                 break;
 
             case 'n':
                 if (!ParseUint(optarg, &max_rotated_logs_) || max_rotated_logs_ < 1) {
-                    LogcatPanic(HELP_TRUE, "Invalid parameter \"%s\" to -n\n", optarg);
+                    error(EXIT_FAILURE, 0, "Invalid parameter '%s' to -n.", optarg);
                 }
                 break;
 
@@ -785,7 +754,7 @@
                 for (const auto& arg : Split(optarg, delimiters)) {
                     int err = SetLogFormat(arg.c_str());
                     if (err < 0) {
-                        LogcatPanic(HELP_FORMAT, "Invalid parameter \"%s\" to -v\n", arg.c_str());
+                        error(EXIT_FAILURE, 0, "Invalid parameter '%s' to -v.", arg.c_str());
                     }
                     if (err) hasSetLogFormat = true;
                 }
@@ -882,20 +851,25 @@
                 break;
 
             case ':':
-                LogcatPanic(HELP_TRUE, "Option -%c needs an argument\n", optopt);
+                error(EXIT_FAILURE, 0, "Option '%s' needs an argument.", argv[optind - 1]);
+                break;
 
             case 'h':
                 show_help();
                 show_format_help();
                 return EXIT_SUCCESS;
 
+            case '?':
+                error(EXIT_FAILURE, 0, "Unknown option '%s'.", argv[optind - 1]);
+                break;
+
             default:
-                LogcatPanic(HELP_TRUE, "Unrecognized Option %c\n", optopt);
+                error(EXIT_FAILURE, 0, "Unknown getopt_long() result '%c'.", c);
         }
     }
 
     if (max_count_ && got_t) {
-        LogcatPanic(HELP_TRUE, "Cannot use -m (--max-count) and -t together\n");
+        error(EXIT_FAILURE, 0, "Cannot use -m (--max-count) and -t together.");
     }
     if (print_it_anyways_ && (!regex_ || !max_count_)) {
         // One day it would be nice if --print -v color and --regex <expr>
@@ -915,12 +889,12 @@
     }
 
     if (log_rotate_size_kb_ != 0 && !output_file_name_) {
-        LogcatPanic(HELP_TRUE, "-r requires -f as well\n");
+        error(EXIT_FAILURE, 0, "-r requires -f as well.");
     }
 
     if (setId != 0) {
         if (!output_file_name_) {
-            LogcatPanic(HELP_TRUE, "--id='%s' requires -f as well\n", setId);
+            error(EXIT_FAILURE, 0, "--id='%s' requires -f as well.", setId);
         }
 
         std::string file_name = StringPrintf("%s.id", output_file_name_);
@@ -952,7 +926,7 @@
     if (forceFilters.size()) {
         int err = android_log_addFilterString(logformat_.get(), forceFilters.c_str());
         if (err < 0) {
-            LogcatPanic(HELP_FALSE, "Invalid filter expression in logcat args\n");
+            error(EXIT_FAILURE, 0, "Invalid filter expression in logcat args.");
         }
     } else if (argc == optind) {
         // Add from environment variable
@@ -962,7 +936,7 @@
             int err = android_log_addFilterString(logformat_.get(), env_tags_orig);
 
             if (err < 0) {
-                LogcatPanic(HELP_TRUE, "Invalid filter expression in ANDROID_LOG_TAGS\n");
+                error(EXIT_FAILURE, 0, "Invalid filter expression in ANDROID_LOG_TAGS.");
             }
         }
     } else {
@@ -970,18 +944,53 @@
         for (int i = optind ; i < argc ; i++) {
             int err = android_log_addFilterString(logformat_.get(), argv[i]);
             if (err < 0) {
-                LogcatPanic(HELP_TRUE, "Invalid filter expression '%s'\n", argv[i]);
+                error(EXIT_FAILURE, 0, "Invalid filter expression '%s'.", argv[i]);
             }
         }
     }
 
     if (mode & ANDROID_LOG_PSTORE) {
+        if (output_file_name_) {
+            error(EXIT_FAILURE, 0, "-c is ambiguous with both -f and -L specified.");
+        }
+        if (setLogSize || getLogSize || printStatistics || getPruneList || setPruneList) {
+            error(EXIT_FAILURE, 0, "-L is incompatible with -g/-G, -S, and -p/-P.");
+        }
         if (clearLog) {
             unlink("/sys/fs/pstore/pmsg-ramoops-0");
             return EXIT_SUCCESS;
         }
+    }
+
+    if (output_file_name_) {
         if (setLogSize || getLogSize || printStatistics || getPruneList || setPruneList) {
-            LogcatPanic(HELP_TRUE, "-L is incompatible with -g/-G, -S, and -p/-P");
+            error(EXIT_FAILURE, 0, "-f is incompatible with -g/-G, -S, and -p/-P.");
+        }
+
+        if (clearLog || setId) {
+            int max_rotation_count_digits =
+                    max_rotated_logs_ > 0 ? (int)(floor(log10(max_rotated_logs_) + 1)) : 0;
+
+            for (int i = max_rotated_logs_; i >= 0; --i) {
+                std::string file;
+
+                if (!i) {
+                    file = output_file_name_;
+                } else {
+                    file = StringPrintf("%s.%.*d", output_file_name_, max_rotation_count_digits, i);
+                }
+
+                int err = unlink(file.c_str());
+
+                if (err < 0 && errno != ENOENT) {
+                    fprintf(stderr, "failed to delete log file '%s': %s\n", file.c_str(),
+                            strerror(errno));
+                }
+            }
+        }
+
+        if (clearLog) {
+            return EXIT_SUCCESS;
         }
     }
 
@@ -1009,35 +1018,8 @@
             continue;
         }
 
-        if (clearLog || setId) {
-            if (output_file_name_) {
-                int max_rotation_count_digits =
-                        max_rotated_logs_ > 0 ? (int)(floor(log10(max_rotated_logs_) + 1)) : 0;
-
-                for (int i = max_rotated_logs_; i >= 0; --i) {
-                    std::string file;
-
-                    if (!i) {
-                        file = output_file_name_;
-                    } else {
-                        file = StringPrintf("%s.%.*d", output_file_name_, max_rotation_count_digits,
-                                            i);
-                    }
-
-                    if (!file.length()) {
-                        perror("while clearing log files");
-                        ReportErrorName(buffer_name, security_buffer_selected, &clear_failures);
-                        break;
-                    }
-
-                    int err = unlink(file.c_str());
-
-                    if (err < 0 && errno != ENOENT) {
-                        perror("while clearing log files");
-                        ReportErrorName(buffer_name, security_buffer_selected, &clear_failures);
-                    }
-                }
-            } else if (android_logger_clear(logger)) {
+        if (clearLog) {
+            if (android_logger_clear(logger)) {
                 ReportErrorName(buffer_name, security_buffer_selected, &clear_failures);
             }
         }
@@ -1070,85 +1052,70 @@
 
     // report any errors in the above loop and exit
     if (!open_device_failures.empty()) {
-        LogcatPanic(HELP_FALSE, "Unable to open log device%s '%s'\n",
-                    open_device_failures.size() > 1 ? "s" : "",
-                    Join(open_device_failures, ",").c_str());
+        error(EXIT_FAILURE, 0, "Unable to open log device%s '%s'.",
+              open_device_failures.size() > 1 ? "s" : "", Join(open_device_failures, ",").c_str());
     }
     if (!clear_failures.empty()) {
-        LogcatPanic(HELP_FALSE, "failed to clear the '%s' log%s\n",
-                    Join(clear_failures, ",").c_str(), clear_failures.size() > 1 ? "s" : "");
+        error(EXIT_FAILURE, 0, "failed to clear the '%s' log%s.", Join(clear_failures, ",").c_str(),
+              clear_failures.size() > 1 ? "s" : "");
     }
     if (!set_size_failures.empty()) {
-        LogcatPanic(HELP_FALSE, "failed to set the '%s' log size%s\n",
-                    Join(set_size_failures, ",").c_str(), set_size_failures.size() > 1 ? "s" : "");
+        error(EXIT_FAILURE, 0, "failed to set the '%s' log size%s.",
+              Join(set_size_failures, ",").c_str(), set_size_failures.size() > 1 ? "s" : "");
     }
     if (!get_size_failures.empty()) {
-        LogcatPanic(HELP_FALSE, "failed to get the readable '%s' log size%s\n",
-                    Join(get_size_failures, ",").c_str(), get_size_failures.size() > 1 ? "s" : "");
+        error(EXIT_FAILURE, 0, "failed to get the readable '%s' log size%s.",
+              Join(get_size_failures, ",").c_str(), get_size_failures.size() > 1 ? "s" : "");
     }
 
     if (setPruneList) {
         size_t len = strlen(setPruneList);
-        // extra 32 bytes are needed by android_logger_set_prune_list
-        size_t bLen = len + 32;
-        char* buf = nullptr;
-        if (asprintf(&buf, "%-*s", (int)(bLen - 1), setPruneList) > 0) {
-            buf[len] = '\0';
-            if (android_logger_set_prune_list(logger_list.get(), buf, bLen)) {
-                LogcatPanic(HELP_FALSE, "failed to set the prune list");
-            }
-            free(buf);
-        } else {
-            LogcatPanic(HELP_FALSE, "failed to set the prune list (alloc)");
+        if (android_logger_set_prune_list(logger_list.get(), setPruneList, len)) {
+            error(EXIT_FAILURE, 0, "Failed to set the prune list.");
         }
         return EXIT_SUCCESS;
     }
 
     if (printStatistics || getPruneList) {
-        size_t len = 8192;
-        char* buf;
+        std::string buf(8192, '\0');
+        size_t ret_length = 0;
+        int retry = 32;
 
-        for (int retry = 32; (retry >= 0) && ((buf = new char[len]));
-             delete[] buf, buf = nullptr, --retry) {
+        for (; retry >= 0; --retry) {
             if (getPruneList) {
-                android_logger_get_prune_list(logger_list.get(), buf, len);
+                android_logger_get_prune_list(logger_list.get(), buf.data(), buf.size());
             } else {
-                android_logger_get_statistics(logger_list.get(), buf, len);
+                android_logger_get_statistics(logger_list.get(), buf.data(), buf.size());
             }
-            buf[len - 1] = '\0';
-            if (atol(buf) < 3) {
-                delete[] buf;
-                buf = nullptr;
+
+            ret_length = atol(buf.c_str());
+            if (ret_length < 3) {
+                error(EXIT_FAILURE, 0, "Failed to read data.");
+            }
+
+            if (ret_length < buf.size()) {
                 break;
             }
-            size_t ret = atol(buf) + 1;
-            if (ret <= len) {
-                len = ret;
-                break;
-            }
-            len = ret;
+
+            buf.resize(ret_length + 1);
         }
 
-        if (!buf) {
-            LogcatPanic(HELP_FALSE, "failed to read data");
+        if (retry < 0) {
+            error(EXIT_FAILURE, 0, "Failed to read data.");
         }
 
-        // remove trailing FF
-        char* cp = buf + len - 1;
-        *cp = '\0';
-        bool truncated = *--cp != '\f';
-        if (!truncated) *cp = '\0';
-
-        // squash out the byte count
-        cp = buf;
-        if (!truncated) {
-            while (isdigit(*cp)) ++cp;
-            if (*cp == '\n') ++cp;
+        buf.resize(ret_length);
+        if (buf.back() == '\f') {
+            buf.pop_back();
         }
 
-        len = strlen(cp);
+        // Remove the byte count prefix
+        const char* cp = buf.c_str();
+        while (isdigit(*cp)) ++cp;
+        if (*cp == '\n') ++cp;
+
+        size_t len = strlen(cp);
         TEMP_FAILURE_RETRY(write(output_fd_.get(), cp, len));
-        delete[] buf;
         return EXIT_SUCCESS;
     }
 
@@ -1160,30 +1127,29 @@
         struct log_msg log_msg;
         int ret = android_logger_list_read(logger_list.get(), &log_msg);
         if (!ret) {
-            LogcatPanic(HELP_FALSE, R"init(read: unexpected EOF!
+            error(EXIT_FAILURE, 0, R"init(Unexpected EOF!
 
 This means that either logd crashed, or more likely, this instance of logcat was unable to read log
 messages as quickly as they were being produced.
 
-If you have enabled significant logging, look into using the -G option to increase log buffer sizes.
-)init");
+If you have enabled significant logging, look into using the -G option to increase log buffer sizes.)init");
         }
 
         if (ret < 0) {
             if (ret == -EAGAIN) break;
 
             if (ret == -EIO) {
-                LogcatPanic(HELP_FALSE, "read: unexpected EOF!\n");
+                error(EXIT_FAILURE, 0, "Unexpected EOF!");
             }
             if (ret == -EINVAL) {
-                LogcatPanic(HELP_FALSE, "read: unexpected length.\n");
+                error(EXIT_FAILURE, 0, "Unexpected length.");
             }
-            LogcatPanic(HELP_FALSE, "logcat read failure\n");
+            error(EXIT_FAILURE, errno, "Logcat read failure");
         }
 
         if (log_msg.id() > LOG_ID_MAX) {
-            LogcatPanic(HELP_FALSE, "read: unexpected log id (%d) over LOG_ID_MAX (%d)",
-                        log_msg.id(), LOG_ID_MAX);
+            error(EXIT_FAILURE, 0, "Unexpected log id (%d) over LOG_ID_MAX (%d).", log_msg.id(),
+                  LOG_ID_MAX);
         }
 
         PrintDividers(log_msg.id(), printDividers);
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp
index 7a843d8..694b5fa 100644
--- a/logd/CommandListener.cpp
+++ b/logd/CommandListener.cpp
@@ -19,6 +19,7 @@
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <math.h>
 #include <netinet/in.h>
 #include <stdlib.h>
 #include <string.h>
@@ -186,14 +187,26 @@
     : LogCommand("getStatistics"), mBuf(*buf) {
 }
 
-static std::string package_string(const std::string& str) {
-    // Calculate total buffer size prefix, count is the string length w/o nul
-    char fmt[32];
-    for (size_t l = str.length(), y = 0, x = 6; y != x;
-         y = x, x = strlen(fmt) - 2) {
-        snprintf(fmt, sizeof(fmt), "%zu\n%%s\n\f", l + x);
+// This returns a string with a length prefix with the format <length>\n<data>\n\f.  The length
+// prefix includes the length of the prefix itself.
+static std::string PackageString(const std::string& str) {
+    size_t overhead_length = 3;  // \n \n \f.
+
+    // Number of digits needed to represent length(str + overhead_length).
+    size_t str_size_digits = 1 + static_cast<size_t>(log10(str.size() + overhead_length));
+    // Number of digits needed to represent the total size.
+    size_t total_size_digits =
+            1 + static_cast<size_t>(log10(str.size() + overhead_length + str_size_digits));
+
+    // If adding the size prefix causes a new digit to be required to represent the new total
+    // size, add it to the 'overhead_length'.  This can only happen once, since each new digit
+    // allows for 10x the previous size to be recorded.
+    if (total_size_digits != str_size_digits) {
+        overhead_length++;
     }
-    return android::base::StringPrintf(fmt, str.c_str());
+
+    size_t total_size = str.size() + overhead_length + str_size_digits;
+    return android::base::StringPrintf("%zu\n%s\n\f", total_size, str.c_str());
 }
 
 int CommandListener::GetStatisticsCmd::runCommand(SocketClient* cli, int argc,
@@ -228,8 +241,7 @@
         }
     }
 
-    cli->sendMsg(
-        package_string(mBuf.formatStatistics(uid, pid, logMask)).c_str());
+    cli->sendMsg(PackageString(mBuf.formatStatistics(uid, pid, logMask)).c_str());
     return 0;
 }
 
@@ -240,7 +252,7 @@
 int CommandListener::GetPruneListCmd::runCommand(SocketClient* cli,
                                                  int /*argc*/, char** /*argv*/) {
     setname();
-    cli->sendMsg(package_string(mBuf.formatPrune()).c_str());
+    cli->sendMsg(PackageString(mBuf.formatPrune()).c_str());
     return 0;
 }
 
@@ -316,12 +328,11 @@
             cli->sendMsg("can not mix id= with either format= or name=");
             return 0;
         }
-        cli->sendMsg(package_string(mBuf.formatEntry(atoi(id), uid)).c_str());
+        cli->sendMsg(PackageString(mBuf.formatEntry(atoi(id), uid)).c_str());
         return 0;
     }
 
-    cli->sendMsg(
-        package_string(mBuf.formatGetEventTag(uid, name, format)).c_str());
+    cli->sendMsg(PackageString(mBuf.formatGetEventTag(uid, name, format)).c_str());
 
     return 0;
 }
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 782fb92..88b6da4 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -579,6 +579,8 @@
     mkdir /data/misc/profman 0770 system shell
     mkdir /data/misc/gcov 0770 root root
     mkdir /data/misc/installd 0700 root root
+    mkdir /data/misc/apexdata 0700 root root
+    mkdir /data/misc/apexrollback 0700 root root
 
     mkdir /data/preloads 0775 system system encryption=None
 
@@ -668,7 +670,9 @@
 
     # Wait for apexd to finish activating APEXes before starting more processes.
     wait_for_prop apexd.status ready
-    parse_apex_configs
+    perform_apex_config
+
+    exec_start derive_sdk
 
     init_user0
 
@@ -769,6 +773,11 @@
     write /sys/fs/f2fs/${dev.mnt.blk.data}/cp_interval 200
     write /sys/fs/f2fs/${dev.mnt.blk.data}/gc_urgent_sleep_time 50
 
+    # limit discard size to 128MB in order to avoid long IO latency
+    # for filesystem tuning first (dm or sda)
+    # Note that, if dm-<num> is used, sda/mmcblk0 should be tuned in vendor/init.rc
+    write /sys/devices/virtual/block/${dev.mnt.blk.data}/queue/discard_max_bytes 134217728
+
     # Permissions for System Server and daemons.
     chown system system /sys/power/autosleep
 
diff --git a/trusty/storage/proxy/proxy.c b/trusty/storage/proxy/proxy.c
index c61f7d0..5f56408 100644
--- a/trusty/storage/proxy/proxy.c
+++ b/trusty/storage/proxy/proxy.c
@@ -46,6 +46,8 @@
         return MMC_RPMB;
     } else if (!strcmp(dev_type_name, "virt")) {
         return VIRT_RPMB;
+    } else if (!strcmp(dev_type_name, "sock")) {
+        return SOCK_RPMB;
     } else {
         return UNKNOWN_RPMB;
     }
diff --git a/trusty/storage/proxy/rpmb.c b/trusty/storage/proxy/rpmb.c
index 29827e2..0bd9e68 100644
--- a/trusty/storage/proxy/rpmb.c
+++ b/trusty/storage/proxy/rpmb.c
@@ -21,6 +21,8 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
 #include <unistd.h>
 
 #include <linux/major.h>
@@ -192,7 +194,7 @@
             msg->result = STORAGE_ERR_GENERIC;
             goto err_response;
         }
-    } else if (dev_type == VIRT_RPMB) {
+    } else if ((dev_type == VIRT_RPMB) || (dev_type == SOCK_RPMB)) {
         size_t payload_size = req->reliable_write_size + req->write_size;
         rc = send_virt_rpmb_req(rpmb_fd, read_buf, req->read_size, req->payload, payload_size);
         if (rc < 0) {
@@ -234,12 +236,33 @@
     int rc;
     dev_type = open_dev_type;
 
-    rc = open(rpmb_devname, O_RDWR, 0);
-    if (rc < 0) {
-        ALOGE("unable (%d) to open rpmb device '%s': %s\n", errno, rpmb_devname, strerror(errno));
-        return rc;
+    if (dev_type != SOCK_RPMB) {
+        rc = open(rpmb_devname, O_RDWR, 0);
+        if (rc < 0) {
+            ALOGE("unable (%d) to open rpmb device '%s': %s\n", errno, rpmb_devname, strerror(errno));
+            return rc;
+        }
+        rpmb_fd = rc;
+    } else {
+        struct sockaddr_un unaddr;
+        struct sockaddr *addr = (struct sockaddr *)&unaddr;
+        rc = socket(AF_UNIX, SOCK_STREAM, 0);
+        if (rc < 0) {
+            ALOGE("unable (%d) to create socket: %s\n", errno, strerror(errno));
+            return rc;
+        }
+        rpmb_fd = rc;
+
+        memset(&unaddr, 0, sizeof(unaddr));
+        unaddr.sun_family = AF_UNIX;
+        // TODO if it overflowed, bail rather than connecting?
+        strncpy(unaddr.sun_path, rpmb_devname, sizeof(unaddr.sun_path)-1);
+        rc = connect(rpmb_fd, addr, sizeof(unaddr));
+        if (rc < 0) {
+            ALOGE("unable (%d) to connect to rpmb socket '%s': %s\n", errno, rpmb_devname, strerror(errno));
+            return rc;
+        }
     }
-    rpmb_fd = rc;
     return 0;
 }
 
diff --git a/trusty/storage/proxy/rpmb.h b/trusty/storage/proxy/rpmb.h
index 4c330c9..09af3c5 100644
--- a/trusty/storage/proxy/rpmb.h
+++ b/trusty/storage/proxy/rpmb.h
@@ -18,7 +18,7 @@
 #include <stdint.h>
 #include <trusty/interface/storage.h>
 
-enum dev_type { UNKNOWN_RPMB, MMC_RPMB, VIRT_RPMB };
+enum dev_type { UNKNOWN_RPMB, MMC_RPMB, VIRT_RPMB, SOCK_RPMB };
 
 int rpmb_open(const char* rpmb_devname, enum dev_type dev_type);
 int rpmb_send(struct storage_msg* msg, const void* r, size_t req_len);
diff --git a/trusty/utils/rpmb_dev/Android.bp b/trusty/utils/rpmb_dev/Android.bp
new file mode 100644
index 0000000..e923e82
--- /dev/null
+++ b/trusty/utils/rpmb_dev/Android.bp
@@ -0,0 +1,33 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at //
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_binary {
+    name: "rpmb_dev",
+    vendor: true,
+
+    srcs: [
+        "rpmb_dev.c",
+    ],
+    shared_libs: [
+        "libc",
+        "liblog",
+        "libcrypto",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    init_rc: [
+        "rpmb_dev.rc",
+    ],
+}
diff --git a/trusty/utils/rpmb_dev/rpmb.h b/trusty/utils/rpmb_dev/rpmb.h
new file mode 100644
index 0000000..ab7e8d8
--- /dev/null
+++ b/trusty/utils/rpmb_dev/rpmb.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2015 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 __RPMB_H__
+#define __RPMB_H__
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+struct rpmb_key {
+    uint8_t byte[32];
+};
+
+struct rpmb_state;
+
+#define RPMB_BUF_SIZE 256
+
+/* provides */
+int rpmb_init(struct rpmb_state** statep,
+              void* mmc_handle,
+              const struct rpmb_key* key);
+void rpmb_uninit(struct rpmb_state* statep);
+int rpmb_read(struct rpmb_state* state,
+              void* buf,
+              uint16_t addr,
+              uint16_t count);
+/* count must be 1 or 2, addr must be aligned */
+int rpmb_write(struct rpmb_state* state,
+               const void* buf,
+               uint16_t addr,
+               uint16_t count,
+               bool sync);
+
+/* needs */
+int rpmb_send(void* mmc_handle,
+              void* reliable_write_buf,
+              size_t reliable_write_size,
+              void* write_buf,
+              size_t write_buf_size,
+              void* read_buf,
+              size_t read_buf_size,
+              bool sync);
+
+#endif
diff --git a/trusty/utils/rpmb_dev/rpmb_dev.c b/trusty/utils/rpmb_dev/rpmb_dev.c
new file mode 100644
index 0000000..af97eba
--- /dev/null
+++ b/trusty/utils/rpmb_dev/rpmb_dev.c
@@ -0,0 +1,657 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#define LOG_TAG "rpmb_mock"
+
+#include "rpmb_protocol.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <log/log.h>
+#include <openssl/hmac.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+/* verbose is an int for getopt */
+static int verbose = false;
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+
+HMAC_CTX* HMAC_CTX_new(void) {
+    HMAC_CTX* ctx = malloc(sizeof(*ctx));
+    if (ctx != NULL) {
+        HMAC_CTX_init(ctx);
+    }
+    return ctx;
+}
+
+void HMAC_CTX_free(HMAC_CTX* ctx) {
+    if (ctx != NULL) {
+        HMAC_CTX_cleanup(ctx);
+        free(ctx);
+    }
+}
+
+#endif
+
+#define MAX_WRITE_COUNTER (0xffffffff)
+
+struct rpmb_data_header {
+    uint32_t write_counter;
+    uint16_t max_block;
+    uint8_t pad1;
+    uint8_t key_programmed;
+    struct rpmb_key key;
+    uint8_t pad[512 - 4 - 2 - 1 - 1 - sizeof(struct rpmb_key)];
+};
+
+#define MAX_PACKET_COUNT (8)
+
+struct rpmb_dev_state {
+    struct rpmb_data_header header;
+    struct rpmb_packet cmd[MAX_PACKET_COUNT];
+    struct rpmb_packet res[MAX_PACKET_COUNT];
+    uint16_t cmd_count;
+    uint16_t res_count;
+    int data_fd;
+};
+
+/* TODO: move to common location */
+static int rpmb_mac(struct rpmb_key key, struct rpmb_packet* packet, size_t packet_count,
+                    struct rpmb_key* mac) {
+    size_t i;
+    int hmac_ret;
+    unsigned int md_len;
+    HMAC_CTX* hmac_ctx;
+
+    hmac_ctx = HMAC_CTX_new();
+    hmac_ret = HMAC_Init_ex(hmac_ctx, &key, sizeof(key), EVP_sha256(), NULL);
+    if (!hmac_ret) {
+        ALOGE("HMAC_Init_ex failed\n");
+        goto err;
+    }
+    for (i = 0; i < packet_count; i++) {
+        hmac_ret = HMAC_Update(hmac_ctx, packet[i].data, 284);
+        if (!hmac_ret) {
+            ALOGE("HMAC_Update failed\n");
+            goto err;
+        }
+    }
+    hmac_ret = HMAC_Final(hmac_ctx, mac->byte, &md_len);
+    if (md_len != sizeof(mac->byte)) {
+        ALOGE("bad md_len %d != %zd\n", md_len, sizeof(mac->byte));
+        exit(1);
+    }
+    if (!hmac_ret) {
+        ALOGE("HMAC_Final failed\n");
+        goto err;
+    }
+
+err:
+    HMAC_CTX_free(hmac_ctx);
+    return hmac_ret ? 0 : -1;
+}
+
+static int rpmb_file_seek(struct rpmb_dev_state* s, uint16_t addr) {
+    int ret;
+    int pos = addr * RPMB_PACKET_DATA_SIZE + sizeof(s->header);
+    ret = lseek(s->data_fd, pos, SEEK_SET);
+    if (ret != pos) {
+        ALOGE("rpmb_dev: seek to %d failed, got %d\n", pos, ret);
+        return -1;
+    }
+    return 0;
+}
+
+static uint16_t rpmb_dev_program_key(struct rpmb_dev_state* s) {
+    int ret;
+
+    if (s->header.key_programmed) {
+        return RPMB_RES_WRITE_FAILURE;
+    }
+
+    s->header.key = s->cmd[0].key_mac;
+    s->header.key_programmed = 1;
+
+    ret = lseek(s->data_fd, 0, SEEK_SET);
+    if (ret) {
+        ALOGE("rpmb_dev: Failed to seek rpmb data file\n");
+        return RPMB_RES_WRITE_FAILURE;
+    }
+
+    ret = write(s->data_fd, &s->header, sizeof(s->header));
+    if (ret != sizeof(s->header)) {
+        ALOGE("rpmb_dev: Failed to write rpmb key: %d, %s\n", ret, strerror(errno));
+
+        return RPMB_RES_WRITE_FAILURE;
+    }
+
+    return RPMB_RES_OK;
+}
+
+static uint16_t rpmb_dev_get_counter(struct rpmb_dev_state* s) {
+    s->res[0].write_counter = rpmb_u32(s->header.write_counter);
+
+    return RPMB_RES_OK;
+}
+
+static uint16_t rpmb_dev_data_write(struct rpmb_dev_state* s) {
+    uint16_t addr = rpmb_get_u16(s->cmd[0].address);
+    uint16_t block_count = s->cmd_count;
+    uint32_t write_counter;
+    int ret;
+
+    if (s->header.write_counter == MAX_WRITE_COUNTER) {
+        if (verbose) {
+            ALOGE("rpmb_dev: Write counter expired\n");
+        }
+        return RPMB_RES_WRITE_FAILURE;
+    }
+
+    write_counter = rpmb_get_u32(s->cmd[0].write_counter);
+    if (s->header.write_counter != write_counter) {
+        if (verbose) {
+            ALOGE("rpmb_dev: Invalid write counter %u. Expected: %u\n", write_counter,
+                  s->header.write_counter);
+        }
+        return RPMB_RES_COUNT_FAILURE;
+    }
+
+    ret = rpmb_file_seek(s, addr);
+    if (ret) {
+        ALOGE("rpmb_dev: Failed to seek rpmb data file\n");
+        return RPMB_RES_WRITE_FAILURE;
+    }
+
+    for (int i = 0; i < block_count; i++) {
+        ret = write(s->data_fd, s->cmd[i].data, RPMB_PACKET_DATA_SIZE);
+        if (ret != RPMB_PACKET_DATA_SIZE) {
+            ALOGE("rpmb_dev: Failed to write rpmb data file: %d, %s\n", ret, strerror(errno));
+            return RPMB_RES_WRITE_FAILURE;
+        }
+    }
+
+    s->header.write_counter++;
+
+    ret = lseek(s->data_fd, 0, SEEK_SET);
+    if (ret) {
+        ALOGE("rpmb_dev: Failed to seek rpmb data file\n");
+        return RPMB_RES_WRITE_FAILURE;
+    }
+
+    ret = write(s->data_fd, &s->header.write_counter, sizeof(s->header.write_counter));
+    if (ret != sizeof(s->header.write_counter)) {
+        ALOGE("rpmb_dev: Failed to write rpmb write counter: %d, %s\n", ret, strerror(errno));
+
+        return RPMB_RES_WRITE_FAILURE;
+    }
+
+    s->res[0].write_counter = rpmb_u32(s->header.write_counter);
+    return RPMB_RES_OK;
+}
+
+static uint16_t rpmb_dev_data_read(struct rpmb_dev_state* s) {
+    uint16_t addr;
+    uint16_t block_count;
+    int ret;
+
+    addr = rpmb_get_u16(s->cmd[0].address);
+    block_count = s->res_count;
+
+    rpmb_file_seek(s, addr);
+
+    for (int i = 0; i < block_count; i++) {
+        ret = read(s->data_fd, s->res[i].data, RPMB_PACKET_DATA_SIZE);
+        if (ret != 0 && ret != RPMB_PACKET_DATA_SIZE) {
+            ALOGE("rpmb_dev: Failed to read rpmb data file: %d, %s\n", ret, strerror(errno));
+            return RPMB_RES_READ_FAILURE;
+        }
+    }
+
+    return RPMB_RES_OK;
+}
+
+struct rpmb_dev_cmd {
+    uint16_t (*func)(struct rpmb_dev_state* s);
+    uint16_t resp;
+    bool key_mac_is_key;
+    bool check_mac;
+    bool check_result_read;
+    bool check_key_programmed;
+    bool check_addr;
+    bool multi_packet_cmd;
+    bool multi_packet_res;
+    bool res_mac;
+};
+
+static struct rpmb_dev_cmd rpmb_dev_cmd_table[] = {
+        [RPMB_REQ_PROGRAM_KEY] =
+                {
+                        .func = rpmb_dev_program_key,
+                        .resp = RPMB_RESP_PROGRAM_KEY,
+                        .key_mac_is_key = true,
+                        .check_result_read = true,
+                },
+        [RPMB_REQ_GET_COUNTER] =
+                {
+                        .func = rpmb_dev_get_counter,
+                        .resp = RPMB_RESP_GET_COUNTER,
+                        .check_key_programmed = true,
+                        .res_mac = true,
+                },
+        [RPMB_REQ_DATA_WRITE] =
+                {
+                        .func = rpmb_dev_data_write,
+                        .resp = RPMB_RESP_DATA_WRITE,
+                        .check_mac = true,
+                        .check_result_read = true,
+                        .check_key_programmed = true,
+                        .check_addr = true,
+                        .multi_packet_cmd = true,
+                        .res_mac = true,
+                },
+        [RPMB_REQ_DATA_READ] =
+                {
+                        .func = rpmb_dev_data_read,
+                        .resp = RPMB_RESP_DATA_READ,
+                        .check_addr = true,
+                        .multi_packet_res = true,
+                        .res_mac = true,
+                },
+};
+
+#define countof(arr) (sizeof(arr) / sizeof(arr[0]))
+
+static void rpmb_dev_process_cmd(struct rpmb_dev_state* s) {
+    assert(s->cmd_count > 0);
+    assert(s->res_count > 0);
+    uint16_t req_resp = rpmb_get_u16(s->cmd[0].req_resp);
+    uint16_t addr = rpmb_get_u16(s->cmd[0].address);
+    uint16_t sub_req;
+    uint16_t cmd_index = req_resp < countof(rpmb_dev_cmd_table) ? req_resp : 0;
+    struct rpmb_dev_cmd* cmd = &rpmb_dev_cmd_table[cmd_index];
+    uint16_t result = RPMB_RES_GENERAL_FAILURE;
+    struct rpmb_key mac;
+    uint16_t block_count = 0;
+
+    if (cmd->check_result_read) {
+        sub_req = rpmb_get_u16(s->cmd[s->cmd_count - 1].req_resp);
+        if (sub_req != RPMB_REQ_RESULT_READ) {
+            if (verbose) {
+                ALOGE("rpmb_dev: Request %d, missing result read request, got %d, cmd_count %d\n",
+                      req_resp, sub_req, s->cmd_count);
+            }
+            goto err;
+        }
+        assert(s->cmd_count > 1);
+        s->cmd_count--;
+    }
+
+    if (cmd->check_mac) {
+        if (rpmb_mac(s->header.key, s->cmd, s->cmd_count, &mac) != 0) {
+            ALOGE("rpmb_dev: failed to caclulate mac\n");
+            goto err;
+        }
+    } else if (cmd->key_mac_is_key) {
+        mac = s->cmd[s->cmd_count - 1].key_mac;
+    } else {
+        memset(mac.byte, 0, sizeof(mac.byte));
+    }
+
+    if (memcmp(&mac, s->cmd[s->cmd_count - 1].key_mac.byte, sizeof(mac))) {
+        if (verbose) {
+            ALOGE("rpmb_dev: Request %d, invalid MAC, cmd_count %d\n", req_resp, s->cmd_count);
+        }
+        if (cmd->check_mac) {
+            result = RPMB_RES_AUTH_FAILURE;
+        }
+        goto err;
+    }
+
+    if (cmd->multi_packet_cmd) {
+        block_count = s->cmd_count;
+    }
+    if (cmd->multi_packet_res) {
+        block_count = s->res_count;
+    }
+
+    if (cmd->check_addr && (addr + block_count > s->header.max_block + 1)) {
+        if (verbose) {
+            ALOGE("rpmb_dev: Request %d, invalid addr: 0x%x count 0x%x, Out of bounds. Max addr "
+                  "0x%x\n",
+                  req_resp, addr, block_count, s->header.max_block + 1);
+        }
+        result = RPMB_RES_ADDR_FAILURE;
+        goto err;
+    }
+    if (!cmd->check_addr && addr) {
+        if (verbose) {
+            ALOGE("rpmb_dev: Request %d, invalid addr: 0x%x != 0\n", req_resp, addr);
+        }
+        goto err;
+    }
+
+    for (int i = 1; i < s->cmd_count; i++) {
+        sub_req = rpmb_get_u16(s->cmd[i].req_resp);
+        if (sub_req != req_resp) {
+            if (verbose) {
+                ALOGE("rpmb_dev: Request %d, sub-request mismatch, %d, at %d\n", req_resp, i,
+                      sub_req);
+            }
+            goto err;
+        }
+    }
+    if (!cmd->multi_packet_cmd && s->cmd_count != 1) {
+        if (verbose) {
+            ALOGE("rpmb_dev: Request %d, bad cmd count %d, expected 1\n", req_resp, s->cmd_count);
+        }
+        goto err;
+    }
+    if (!cmd->multi_packet_res && s->res_count != 1) {
+        if (verbose) {
+            ALOGE("rpmb_dev: Request %d, bad res count %d, expected 1\n", req_resp, s->res_count);
+        }
+        goto err;
+    }
+
+    if (cmd->check_key_programmed && !s->header.key_programmed) {
+        if (verbose) {
+            ALOGE("rpmb_dev: Request %d, key is not programmed\n", req_resp);
+        }
+        s->res[0].result = rpmb_u16(RPMB_RES_NO_AUTH_KEY);
+        return;
+    }
+
+    if (!cmd->func) {
+        if (verbose) {
+            ALOGE("rpmb_dev: Unsupported request: %d\n", req_resp);
+        }
+        goto err;
+    }
+
+    result = cmd->func(s);
+
+err:
+    if (s->header.write_counter == MAX_WRITE_COUNTER) {
+        result |= RPMB_RES_WRITE_COUNTER_EXPIRED;
+    }
+
+    for (int i = 0; i < s->res_count; i++) {
+        s->res[i].nonce = s->cmd[0].nonce;
+        s->res[i].address = rpmb_u16(addr);
+        s->res[i].block_count = rpmb_u16(block_count);
+        s->res[i].result = rpmb_u16(result);
+        s->res[i].req_resp = rpmb_u16(cmd->resp);
+    }
+    if (cmd->res_mac) {
+        rpmb_mac(s->header.key, s->res, s->res_count, &s->res[s->res_count - 1].key_mac);
+    }
+}
+
+/*
+ * Receives data until one of the following is true:
+ * - The buffer is full (return will be len)
+ * - The connection closed (return > 0, < len)
+ * - An error occurred (return will be the negative error code from recv)
+ */
+ssize_t recv_until(int sock, void* dest_in, size_t len) {
+    size_t bytes_recvd = 0;
+    char* dest = dest_in;
+    while (bytes_recvd < len) {
+        ssize_t ret = recv(sock, dest, len - bytes_recvd, 0);
+        if (ret < 0) {
+            return ret;
+        }
+        dest += ret;
+        bytes_recvd += ret;
+        if (ret == 0) {
+            break;
+        }
+    }
+    return bytes_recvd;
+}
+
+/*
+ * Handles an incoming connection to the rpmb daemon.
+ * Returns 0 if the client disconnects without violating the protocol.
+ * Returns a negative value if we terminated the connection abnormally.
+ *
+ * Arguments:
+ *   conn_sock - an fd to send/recv on
+ *   s - an initialized rpmb device
+ */
+int handle_conn(struct rpmb_dev_state* s, int conn_sock) {
+    int ret;
+
+    while (true) {
+        memset(s->res, 0, sizeof(s->res));
+        ret = recv_until(conn_sock, &s->res_count, sizeof(s->res_count));
+
+        /*
+         * Disconnected while not in the middle of anything.
+         */
+        if (ret <= 0) {
+            return 0;
+        }
+
+        if (s->res_count > MAX_PACKET_COUNT) {
+            ALOGE("rpmb_dev: Receive count too large: %d\n", s->res_count);
+            return -1;
+        }
+        if (s->res_count <= 0) {
+            ALOGE("rpmb_dev: Receive count too small: %d\n", s->res_count);
+            return -1;
+        }
+
+        ret = recv_until(conn_sock, &s->cmd_count, sizeof(s->cmd_count));
+        if (ret != sizeof(s->cmd_count)) {
+            ALOGE("rpmb_dev: Failed to read cmd_count");
+            return -1;
+        }
+
+        if (s->cmd_count == 0) {
+            ALOGE("rpmb_dev: Must contain at least one command\n");
+            return -1;
+        }
+
+        if (s->cmd_count > MAX_PACKET_COUNT) {
+            ALOGE("rpmb_dev: Command count is too large\n");
+            return -1;
+        }
+
+        size_t cmd_size = s->cmd_count * sizeof(s->cmd[0]);
+        ret = recv_until(conn_sock, s->cmd, cmd_size);
+        if (ret != (int)cmd_size) {
+            ALOGE("rpmb_dev: Failed to read command: "
+                  "cmd_size: %zu ret: %d, %s\n",
+                  cmd_size, ret, strerror(errno));
+            return -1;
+        }
+
+        rpmb_dev_process_cmd(s);
+
+        size_t resp_size = sizeof(s->res[0]) * s->res_count;
+        ret = send(conn_sock, s->res, resp_size, 0);
+        if (ret != (int)resp_size) {
+            ALOGE("rpmb_dev: Failed to send response: %d, %s\n", ret, strerror(errno));
+            return -1;
+        }
+    }
+}
+
+void usage(const char* argv0) {
+    fprintf(stderr, "Usage: %s [-d|--dev] <datafile> [--sock] <socket_path>\n", argv0);
+    fprintf(stderr, "or:    %s [-d|--dev] <datafile> [--size <size>] [--key key]\n", argv0);
+}
+
+int main(int argc, char** argv) {
+    struct rpmb_dev_state s;
+    int ret;
+    int cmdres_sock;
+    struct sockaddr_un cmdres_sockaddr;
+    const char* data_file_name = NULL;
+    const char* socket_path = NULL;
+    int open_flags;
+    int init = false;
+
+    struct option long_options[] = {{"size", required_argument, 0, 0},
+                                    {"key", required_argument, 0, 0},
+                                    {"sock", required_argument, 0, 0},
+                                    {"dev", required_argument, 0, 'd'},
+                                    {"init", no_argument, &init, true},
+                                    {"verbose", no_argument, &verbose, true},
+                                    {0, 0, 0, 0}};
+
+    memset(&s.header, 0, sizeof(s.header));
+
+    while (1) {
+        int c;
+        int option_index = 0;
+        c = getopt_long(argc, argv, "d:", long_options, &option_index);
+        if (c == -1) {
+            break;
+        }
+
+        switch (c) {
+            /* long args */
+            case 0:
+                switch (option_index) {
+                    /* size */
+                    case 0:
+                        s.header.max_block = atoi(optarg) - 1;
+                        break;
+                    /* key */
+                    case 1:
+                        for (size_t i = 0; i < sizeof(s.header.key.byte); i++) {
+                            if (!optarg) {
+                                break;
+                            }
+                            s.header.key.byte[i] = strtol(optarg, &optarg, 16);
+                            s.header.key_programmed = 1;
+                        }
+                        break;
+                    /* sock */
+                    case 2:
+                        socket_path = optarg;
+                        break;
+                }
+                break;
+            /* dev */
+            case 'd':
+                data_file_name = optarg;
+                break;
+            default:
+                usage(argv[0]);
+                return EXIT_FAILURE;
+        }
+    }
+
+    /*
+     * We always need a data file, and at exactly one of --init or --sock
+     * must be specified.
+     */
+    if (!data_file_name || (!init == !socket_path)) {
+        usage(argv[0]);
+        return EXIT_FAILURE;
+    }
+
+    /*
+     * If the file is already initialized, exit early.
+     */
+    if (init && !access(data_file_name, F_OK)) {
+        return EXIT_SUCCESS;
+    }
+
+    open_flags = O_RDWR;
+    if (init) {
+        open_flags |= O_CREAT | O_TRUNC;
+    }
+    s.data_fd = open(data_file_name, open_flags, S_IWUSR | S_IRUSR);
+    if (s.data_fd < 0) {
+        ALOGE("rpmb_dev: Failed to open rpmb data file, %s: %s\n", data_file_name, strerror(errno));
+        return EXIT_FAILURE;
+    }
+
+    if (init) {
+        /* Create new rpmb data file */
+        if (s.header.max_block == 0) {
+            s.header.max_block = 512 - 1;
+        }
+        ret = write(s.data_fd, &s.header, sizeof(s.header));
+        if (ret != sizeof(s.header)) {
+            ALOGE("rpmb_dev: Failed to write rpmb data file: %d, %s\n", ret, strerror(errno));
+            return EXIT_FAILURE;
+        }
+        return EXIT_SUCCESS;
+    }
+
+    ret = read(s.data_fd, &s.header, sizeof(s.header));
+    if (ret != sizeof(s.header)) {
+        ALOGE("rpmb_dev: Failed to read rpmb data file: %d, %s\n", ret, strerror(errno));
+        return EXIT_FAILURE;
+    }
+
+    cmdres_sock = socket(AF_UNIX, SOCK_STREAM, 0);
+    if (cmdres_sock < 0) {
+        ALOGE("rpmb_dev: Failed to create command/response socket: %s\n", strerror(errno));
+        return EXIT_FAILURE;
+    }
+
+    cmdres_sockaddr.sun_family = AF_UNIX;
+    strncpy(cmdres_sockaddr.sun_path, socket_path, sizeof(cmdres_sockaddr.sun_path));
+
+    ret = bind(cmdres_sock, (struct sockaddr*)&cmdres_sockaddr, sizeof(struct sockaddr_un));
+    if (ret < 0) {
+        ALOGE("rpmb_dev: Failed to bind command/response socket: %s: %s\n", socket_path,
+              strerror(errno));
+        return EXIT_FAILURE;
+    }
+
+    ret = listen(cmdres_sock, 1);
+    if (ret < 0) {
+        ALOGE("rpmb_dev: Failed to listen on command/response socket: %s\n", strerror(errno));
+        return EXIT_FAILURE;
+    }
+
+    while (true) {
+        int conn_sock = accept(cmdres_sock, NULL, NULL);
+        if (conn_sock < 0) {
+            ALOGE("rpmb_dev: Could not accept connection: %s\n", strerror(errno));
+            return EXIT_FAILURE;
+        }
+        ret = handle_conn(&s, conn_sock);
+        close(conn_sock);
+        if (ret) {
+            ALOGE("rpmb_dev: Connection terminated: %d", ret);
+        }
+    }
+}
diff --git a/trusty/utils/rpmb_dev/rpmb_dev.rc b/trusty/utils/rpmb_dev/rpmb_dev.rc
new file mode 100644
index 0000000..9f60e81
--- /dev/null
+++ b/trusty/utils/rpmb_dev/rpmb_dev.rc
@@ -0,0 +1,29 @@
+# RPMB Mock
+on post-fs-data
+    mkdir /data/vendor/ss
+    chown root system /data/vendor/ss
+    chmod 0770 /data/vendor/ss
+    rm /data/vendor/ss/rpmb_sock
+    start rpmb_mock_init
+    start rpmb_mock
+
+    # Storage proxy
+    start storageproxyd
+
+service storageproxyd /vendor/bin/storageproxyd -d /dev/trusty-ipc-dev0 \
+        -r /data/vendor/ss/rpmb_sock -p /data/vendor/ss -t sock
+    class main
+    disabled
+    user root
+
+service rpmb_mock_init /vendor/bin/rpmb_dev --dev /data/vendor/ss/RPMB_DATA --init --key "ea df 64 44 ea 65 5d 1c 87 27 d4 20 71 0d 53 42 dd 73 a3 38 63 e1 d7 94 c3 72 a6 ea e0 64 64 e6" --size 2048
+    disabled
+    user system
+    group system
+    oneshot
+
+service rpmb_mock /vendor/bin/rpmb_dev --dev /data/vendor/ss/RPMB_DATA --sock /data/vendor/ss/rpmb_sock
+    class main
+    disabled
+    user system
+    group system
diff --git a/trusty/utils/rpmb_dev/rpmb_protocol.h b/trusty/utils/rpmb_dev/rpmb_protocol.h
new file mode 100644
index 0000000..bfcb806
--- /dev/null
+++ b/trusty/utils/rpmb_dev/rpmb_protocol.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#include "rpmb.h" /* For struct rpmb_key */
+
+#define MMC_READ_MULTIPLE_BLOCK 18
+#define MMC_WRITE_MULTIPLE_BLOCK 25
+#define MMC_RELIABLE_WRITE_FLAG (1 << 31)
+
+#define MMC_RSP_PRESENT (1 << 0)
+#define MMC_RSP_CRC (1 << 2)
+#define MMC_RSP_OPCODE (1 << 4)
+#define MMC_CMD_ADTC (1 << 5)
+#define MMC_RSP_SPI_S1 (1 << 7)
+#define MMC_RSP_R1 (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE)
+#define MMC_RSP_SPI_R1 (MMC_RSP_SPI_S1)
+
+struct rpmb_nonce {
+    uint8_t byte[16];
+};
+
+struct rpmb_u16 {
+    uint8_t byte[2];
+};
+
+struct rpmb_u32 {
+    uint8_t byte[4];
+};
+
+#define RPMB_PACKET_DATA_SIZE (256)
+
+struct rpmb_packet {
+    uint8_t pad[196];
+    struct rpmb_key key_mac;
+    uint8_t data[RPMB_PACKET_DATA_SIZE];
+    struct rpmb_nonce nonce;
+    struct rpmb_u32 write_counter;
+    struct rpmb_u16 address;
+    struct rpmb_u16 block_count;
+    struct rpmb_u16 result;
+    struct rpmb_u16 req_resp;
+};
+
+enum rpmb_request {
+    RPMB_REQ_PROGRAM_KEY = 0x0001,
+    RPMB_REQ_GET_COUNTER = 0x0002,
+    RPMB_REQ_DATA_WRITE = 0x0003,
+    RPMB_REQ_DATA_READ = 0x0004,
+    RPMB_REQ_RESULT_READ = 0x0005,
+};
+
+enum rpmb_response {
+    RPMB_RESP_PROGRAM_KEY = 0x0100,
+    RPMB_RESP_GET_COUNTER = 0x0200,
+    RPMB_RESP_DATA_WRITE = 0x0300,
+    RPMB_RESP_DATA_READ = 0x0400,
+};
+
+enum rpmb_result {
+    RPMB_RES_OK = 0x0000,
+    RPMB_RES_GENERAL_FAILURE = 0x0001,
+    RPMB_RES_AUTH_FAILURE = 0x0002,
+    RPMB_RES_COUNT_FAILURE = 0x0003,
+    RPMB_RES_ADDR_FAILURE = 0x0004,
+    RPMB_RES_WRITE_FAILURE = 0x0005,
+    RPMB_RES_READ_FAILURE = 0x0006,
+    RPMB_RES_NO_AUTH_KEY = 0x0007,
+
+    RPMB_RES_WRITE_COUNTER_EXPIRED = 0x0080,
+};
+
+static inline struct rpmb_u16 rpmb_u16(uint16_t val) {
+    struct rpmb_u16 ret = {{
+            (uint8_t)(val >> 8),
+            (uint8_t)(val >> 0),
+    }};
+    return ret;
+}
+
+static inline struct rpmb_u32 rpmb_u32(uint32_t val) {
+    struct rpmb_u32 ret = {{
+            (uint8_t)(val >> 24),
+            (uint8_t)(val >> 16),
+            (uint8_t)(val >> 8),
+            (uint8_t)(val >> 0),
+    }};
+    return ret;
+}
+
+static inline uint16_t rpmb_get_u16(struct rpmb_u16 u16) {
+    size_t i;
+    uint16_t val;
+
+    val = 0;
+    for (i = 0; i < sizeof(u16.byte); i++)
+        val = val << 8 | u16.byte[i];
+
+    return val;
+}
+
+static inline uint32_t rpmb_get_u32(struct rpmb_u32 u32) {
+    size_t i;
+    uint32_t val;
+
+    val = 0;
+    for (i = 0; i < sizeof(u32.byte); i++)
+        val = val << 8 | u32.byte[i];
+
+    return val;
+}