Merge "Revert "Disable XOM in init.""
diff --git a/TEST_MAPPING b/TEST_MAPPING
index c47230f..f9c3b4a 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -2,6 +2,30 @@
   "presubmit": [
     {
       "name": "adbd_test"
+    },
+    {
+      "name": "debuggerd_test"
+    },
+    {
+      "name": "init_tests"
+    },
+    {
+      "name": "libbase_test"
+    },
+    {
+      "name": "libprocinfo_test"
+    },
+    {
+      "name": "memunreachable_test"
+    },
+    {
+      "name": "memunreachable_binder_test"
+    },
+    {
+      "name": "propertyinfoserializer_tests"
+    },
+    {
+      "name": "ziparchive-tests"
     }
   ]
 }
diff --git a/adb/adb_auth.h b/adb/adb_auth.h
index 715e04f..2fc8478 100644
--- a/adb/adb_auth.h
+++ b/adb/adb_auth.h
@@ -36,6 +36,7 @@
 void adb_auth_init();
 
 int adb_auth_keygen(const char* filename);
+int adb_auth_pubkey(const char* filename);
 std::string adb_auth_get_userkey();
 std::deque<std::shared_ptr<RSA>> adb_auth_get_private_keys();
 
diff --git a/adb/client/auth.cpp b/adb/client/auth.cpp
index 71c19b8..bcb829b 100644
--- a/adb/client/auth.cpp
+++ b/adb/client/auth.cpp
@@ -43,6 +43,7 @@
 
 #include "adb.h"
 #include "adb_auth.h"
+#include "adb_io.h"
 #include "adb_utils.h"
 #include "sysdeps.h"
 #include "transport.h"
@@ -52,30 +53,7 @@
     *new std::map<std::string, std::shared_ptr<RSA>>;
 static std::map<int, std::string>& g_monitored_paths = *new std::map<int, std::string>;
 
-static std::string get_user_info() {
-    LOG(INFO) << "get_user_info...";
-
-    std::string hostname;
-    if (getenv("HOSTNAME")) hostname = getenv("HOSTNAME");
-#if !defined(_WIN32)
-    char buf[64];
-    if (hostname.empty() && gethostname(buf, sizeof(buf)) != -1) hostname = buf;
-#endif
-    if (hostname.empty()) hostname = "unknown";
-
-    std::string username;
-    if (getenv("LOGNAME")) username = getenv("LOGNAME");
-#if !defined _WIN32 && !defined ADB_HOST_ON_TARGET
-    if (username.empty() && getlogin()) username = getlogin();
-#endif
-    if (username.empty()) hostname = "unknown";
-
-    return " " + username + "@" + hostname;
-}
-
-static bool write_public_keyfile(RSA* private_key, const std::string& private_key_path) {
-    LOG(INFO) << "write_public_keyfile...";
-
+static bool calculate_public_key(std::string* out, RSA* private_key) {
     uint8_t binary_key_data[ANDROID_PUBKEY_ENCODED_SIZE];
     if (!android_pubkey_encode(private_key, binary_key_data, sizeof(binary_key_data))) {
         LOG(ERROR) << "Failed to convert to public key";
@@ -88,20 +66,10 @@
         return false;
     }
 
-    std::string content;
-    content.resize(expected_length);
-    size_t actual_length = EVP_EncodeBlock(reinterpret_cast<uint8_t*>(&content[0]), binary_key_data,
+    out->resize(expected_length);
+    size_t actual_length = EVP_EncodeBlock(reinterpret_cast<uint8_t*>(out->data()), binary_key_data,
                                            sizeof(binary_key_data));
-    content.resize(actual_length);
-
-    content += get_user_info();
-
-    std::string path(private_key_path + ".pub");
-    if (!android::base::WriteStringToFile(content, path)) {
-        PLOG(ERROR) << "Failed to write public key to '" << path << "'";
-        return false;
-    }
-
+    out->resize(actual_length);
     return true;
 }
 
@@ -140,11 +108,6 @@
         goto out;
     }
 
-    if (!write_public_keyfile(rsa, file)) {
-        D("Failed to write public key");
-        goto out;
-    }
-
     ret = 1;
 
 out:
@@ -170,36 +133,41 @@
     return result;
 }
 
-static bool read_key_file(const std::string& file) {
-    LOG(INFO) << "read_key_file '" << file << "'...";
-
+static std::shared_ptr<RSA> read_key_file(const std::string& file) {
     std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(file.c_str(), "r"), fclose);
     if (!fp) {
         PLOG(ERROR) << "Failed to open '" << file << "'";
-        return false;
+        return nullptr;
     }
 
     RSA* key = RSA_new();
     if (!PEM_read_RSAPrivateKey(fp.get(), &key, nullptr, nullptr)) {
         LOG(ERROR) << "Failed to read key";
         RSA_free(key);
+        return nullptr;
+    }
+
+    return std::shared_ptr<RSA>(key, RSA_free);
+}
+
+static bool load_key(const std::string& file) {
+    std::shared_ptr<RSA> key = read_key_file(file);
+    if (!key) {
         return false;
     }
 
     std::lock_guard<std::mutex> lock(g_keys_mutex);
-    std::string fingerprint = hash_key(key);
+    std::string fingerprint = hash_key(key.get());
     if (g_keys.find(fingerprint) != g_keys.end()) {
         LOG(INFO) << "ignoring already-loaded key: " << file;
-        RSA_free(key);
     } else {
-        g_keys[fingerprint] = std::shared_ptr<RSA>(key, RSA_free);
+        g_keys[fingerprint] = std::move(key);
     }
-
     return true;
 }
 
-static bool read_keys(const std::string& path, bool allow_dir = true) {
-    LOG(INFO) << "read_keys '" << path << "'...";
+static bool load_keys(const std::string& path, bool allow_dir = true) {
+    LOG(INFO) << "load_keys '" << path << "'...";
 
     struct stat st;
     if (stat(path.c_str(), &st) != 0) {
@@ -208,7 +176,7 @@
     }
 
     if (S_ISREG(st.st_mode)) {
-        return read_key_file(path);
+        return load_key(path);
     } else if (S_ISDIR(st.st_mode)) {
         if (!allow_dir) {
             // inotify isn't recursive. It would break expectations to load keys in nested
@@ -237,7 +205,7 @@
                 continue;
             }
 
-            result |= read_key_file((path + OS_PATH_SEPARATOR + name));
+            result |= load_key((path + OS_PATH_SEPARATOR + name));
         }
         return result;
     }
@@ -250,7 +218,7 @@
     return adb_get_android_dir_path() + OS_PATH_SEPARATOR + "adbkey";
 }
 
-static bool get_user_key() {
+static bool generate_userkey() {
     std::string path = get_user_key_path();
     if (path.empty()) {
         PLOG(ERROR) << "Error getting user key filename";
@@ -266,7 +234,7 @@
         }
     }
 
-    return read_key_file(path);
+    return load_key(path);
 }
 
 static std::set<std::string> get_vendor_keys() {
@@ -320,26 +288,42 @@
     return result;
 }
 
+static bool pubkey_from_privkey(std::string* out, const std::string& path) {
+    std::shared_ptr<RSA> privkey = read_key_file(path);
+    if (!privkey) {
+        return false;
+    }
+    return calculate_public_key(out, privkey.get());
+}
+
 std::string adb_auth_get_userkey() {
     std::string path = get_user_key_path();
     if (path.empty()) {
         PLOG(ERROR) << "Error getting user key filename";
         return "";
     }
-    path += ".pub";
 
-    std::string content;
-    if (!android::base::ReadFileToString(path, &content)) {
-        PLOG(ERROR) << "Can't load '" << path << "'";
+    std::string result;
+    if (!pubkey_from_privkey(&result, path)) {
         return "";
     }
-    return content;
+    return result;
 }
 
 int adb_auth_keygen(const char* filename) {
     return (generate_key(filename) == 0);
 }
 
+int adb_auth_pubkey(const char* filename) {
+    std::string pubkey;
+    if (!pubkey_from_privkey(&pubkey, filename)) {
+        return 1;
+    }
+    pubkey.push_back('\n');
+
+    return WriteFdExactly(STDOUT_FILENO, pubkey.data(), pubkey.size()) ? 0 : 1;
+}
+
 #if defined(__linux__)
 static void adb_auth_inotify_update(int fd, unsigned fd_event, void*) {
     LOG(INFO) << "adb_auth_inotify_update called";
@@ -380,7 +364,7 @@
                     LOG(INFO) << "ignoring new directory at '" << path << "'";
                 } else {
                     LOG(INFO) << "observed new file at '" << path << "'";
-                    read_keys(path, false);
+                    load_keys(path, false);
                 }
             } else {
                 LOG(WARNING) << "unmonitored event for " << path << ": 0x" << std::hex
@@ -420,8 +404,8 @@
 void adb_auth_init() {
     LOG(INFO) << "adb_auth_init...";
 
-    if (!get_user_key()) {
-        LOG(ERROR) << "Failed to get user key";
+    if (!generate_userkey()) {
+        LOG(ERROR) << "Failed to generate user key";
         return;
     }
 
@@ -432,7 +416,7 @@
 #endif
 
     for (const std::string& path : key_paths) {
-        read_keys(path.c_str());
+        load_keys(path.c_str());
     }
 }
 
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index b5bed28..c11052d 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -185,7 +185,6 @@
         " enable-verity            re-enable dm-verity checking on userdebug builds\n"
         " keygen FILE\n"
         "     generate adb public/private key; private key stored in FILE,\n"
-        "     public key stored in FILE.pub (existing files overwritten)\n"
         "\n"
         "scripting:\n"
         " wait-for[-TRANSPORT]-STATE\n"
@@ -1756,14 +1755,14 @@
         // Always print key generation information for keygen command.
         adb_trace_enable(AUTH);
         return adb_auth_keygen(argv[1]);
-    }
-    else if (!strcmp(argv[0], "jdwp")) {
+    } else if (!strcmp(argv[0], "pubkey")) {
+        if (argc != 2) error_exit("pubkey requires an argument");
+        return adb_auth_pubkey(argv[1]);
+    } else if (!strcmp(argv[0], "jdwp")) {
         return adb_connect_command("jdwp");
-    }
-    else if (!strcmp(argv[0], "track-jdwp")) {
+    } else if (!strcmp(argv[0], "track-jdwp")) {
         return adb_connect_command("track-jdwp");
-    }
-    else if (!strcmp(argv[0], "track-devices")) {
+    } else if (!strcmp(argv[0], "track-devices")) {
         return adb_connect_command("host:track-devices");
     } else if (!strcmp(argv[0], "raw")) {
         if (argc != 2) {
@@ -1772,13 +1771,11 @@
         return adb_connect_command(argv[1]);
     }
 
-
     /* "adb /?" is a common idiom under Windows */
     else if (!strcmp(argv[0], "--help") || !strcmp(argv[0], "help") || !strcmp(argv[0], "/?")) {
         help();
         return 0;
-    }
-    else if (!strcmp(argv[0], "--version") || !strcmp(argv[0], "version")) {
+    } else if (!strcmp(argv[0], "--version") || !strcmp(argv[0], "version")) {
         fprintf(stdout, "%s", adb_version().c_str());
         return 0;
     } else if (!strcmp(argv[0], "features")) {
diff --git a/adb/fastdeploy/OWNERS b/adb/fastdeploy/OWNERS
new file mode 100644
index 0000000..d145834
--- /dev/null
+++ b/adb/fastdeploy/OWNERS
@@ -0,0 +1 @@
+idries@google.com
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 577e336..d79d20b 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -348,8 +348,16 @@
   return vm_pid;
 }
 
+static void InstallSigPipeHandler() {
+  struct sigaction action = {};
+  action.sa_handler = SIG_IGN;
+  action.sa_flags = SA_RESTART;
+  sigaction(SIGPIPE, &action, nullptr);
+}
+
 int main(int argc, char** argv) {
   DefuseSignalHandlers();
+  InstallSigPipeHandler();
 
   atrace_begin(ATRACE_TAG, "before reparent");
   pid_t target_process = getppid();
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 0b8a936..1179263 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -78,7 +78,7 @@
   _LOG(log, logtype::HEADER, "ABI: '%s'\n", ABI_STRING);
 }
 
-static void dump_probable_cause(log_t* log, const siginfo_t* si) {
+static void dump_probable_cause(log_t* log, const siginfo_t* si, BacktraceMap* map) {
   std::string cause;
   if (si->si_signo == SIGSEGV && si->si_code == SEGV_MAPERR) {
     if (si->si_addr < reinterpret_cast<void*>(4096)) {
@@ -94,6 +94,14 @@
     } else if (si->si_addr == reinterpret_cast<void*>(0xffff0f60)) {
       cause = "call to kuser_cmpxchg64";
     }
+  } else if (si->si_signo == SIGSEGV && si->si_code == SEGV_ACCERR) {
+    for (auto it = map->begin(); it != map->end(); ++it) {
+      const backtrace_map_t* entry = *it;
+      if (si->si_addr >= reinterpret_cast<void*>(entry->start) &&
+          si->si_addr < reinterpret_cast<void*>(entry->end) && entry->flags == PROT_EXEC) {
+        cause = "execute-only (no-read) memory access error; likely due to data in .text.";
+      }
+    }
   } else if (si->si_signo == SIGSYS && si->si_code == SYS_SECCOMP) {
     cause = StringPrintf("seccomp prevented call to disallowed %s system call %d", ABI_STRING,
                          si->si_syscall);
@@ -125,8 +133,6 @@
   _LOG(log, logtype::HEADER, "signal %d (%s), code %d (%s%s), fault addr %s\n",
        thread_info.siginfo->si_signo, get_signame(thread_info.siginfo),
        thread_info.siginfo->si_code, get_sigcode(thread_info.siginfo), sender_desc, addr_desc);
-
-  dump_probable_cause(log, thread_info.siginfo);
 }
 
 static void dump_thread_info(log_t* log, const ThreadInfo& thread_info) {
@@ -426,6 +432,7 @@
 
   if (thread_info.siginfo) {
     dump_signal_info(log, thread_info, process_memory);
+    dump_probable_cause(log, thread_info.siginfo, map);
   }
 
   if (primary_thread) {
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index e066bff..0978ec1 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -162,9 +162,17 @@
         // clang-format on
 };
 
-static std::string find_item_given_name(const std::string& img_name) {
+static char* get_android_product_out() {
     char* dir = getenv("ANDROID_PRODUCT_OUT");
     if (dir == nullptr || dir[0] == '\0') {
+        return nullptr;
+    }
+    return dir;
+}
+
+static std::string find_item_given_name(const std::string& img_name) {
+    char* dir = get_android_product_out();
+    if (!dir) {
         die("ANDROID_PRODUCT_OUT not set");
     }
     return std::string(dir) + "/" + img_name;
@@ -1466,15 +1474,13 @@
             fprintf(stderr, "File system type %s not supported.\n", partition_type.c_str());
             return;
         }
-        fprintf(stderr, "Formatting is not supported for file system with type '%s'.\n",
-                partition_type.c_str());
-        return;
+        die("Formatting is not supported for file system with type '%s'.",
+            partition_type.c_str());
     }
 
     int64_t size;
     if (!android::base::ParseInt(partition_size, &size)) {
-        fprintf(stderr, "Couldn't parse partition size '%s'.\n", partition_size.c_str());
-        return;
+        die("Couldn't parse partition size '%s'.", partition_size.c_str());
     }
 
     unsigned eraseBlkSize, logicalBlkSize;
@@ -1484,17 +1490,14 @@
     if (fs_generator_generate(gen, output.path, size, initial_dir,
             eraseBlkSize, logicalBlkSize)) {
         die("Cannot generate image for %s", partition.c_str());
-        return;
     }
 
     fd.reset(open(output.path, O_RDONLY));
     if (fd == -1) {
-        fprintf(stderr, "Cannot open generated image: %s\n", strerror(errno));
-        return;
+        die("Cannot open generated image: %s", strerror(errno));
     }
     if (!load_buf_fd(fd.release(), &buf)) {
-        fprintf(stderr, "Cannot read image: %s\n", strerror(errno));
-        return;
+        die("Cannot read image: %s", strerror(errno));
     }
     flash_buf(partition, &buf);
     return;
@@ -1505,9 +1508,15 @@
         if (errMsg) fprintf(stderr, "%s", errMsg);
     }
     fprintf(stderr, "FAILED (%s)\n", fb->Error().c_str());
+    if (!skip_if_not_supported) {
+        die("Command failed");
+    }
 }
 
 static bool should_flash_in_userspace(const std::string& partition_name) {
+    if (!get_android_product_out()) {
+        return false;
+    }
     auto path = find_item_given_name("super_empty.img");
     if (path.empty()) {
         return false;
diff --git a/fastboot/fs.cpp b/fastboot/fs.cpp
index fc6a16b..8c0aa6b 100644
--- a/fastboot/fs.cpp
+++ b/fastboot/fs.cpp
@@ -172,13 +172,8 @@
     mkf2fs_args.push_back("-S");
     std::string size_str = std::to_string(partSize);
     mkf2fs_args.push_back(size_str.c_str());
-    mkf2fs_args.push_back("-f");
-    mkf2fs_args.push_back("-O");
-    mkf2fs_args.push_back("encrypt");
-    mkf2fs_args.push_back("-O");
-    mkf2fs_args.push_back("quota");
-    mkf2fs_args.push_back("-O");
-    mkf2fs_args.push_back("verity");
+    mkf2fs_args.push_back("-g");
+    mkf2fs_args.push_back("android");
     mkf2fs_args.push_back(fileName);
     mkf2fs_args.push_back(nullptr);
 
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 8e57e1e..6310238 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -1603,9 +1603,6 @@
         } else if (slot == -1) {
             suffix = fs_mgr_get_slot_suffix();
         }
-        if (suffix.empty()) {
-            LFATAL << "Super partition name can only be overridden on A/B devices.";
-        }
         return super_partition + suffix;
     }
     return LP_METADATA_DEFAULT_PARTITION_NAME;
diff --git a/fs_mgr/fs_mgr_format.cpp b/fs_mgr/fs_mgr_format.cpp
index 845cca9..0983663 100644
--- a/fs_mgr/fs_mgr_format.cpp
+++ b/fs_mgr/fs_mgr_format.cpp
@@ -117,12 +117,7 @@
     // clang-format off
     const char* const args[] = {
         "/system/bin/make_f2fs",
-        "-d1",
-        "-f",
-        "-O", "encrypt",
-        "-O", "quota",
-        "-O", "verity",
-        "-w", "4096",
+        "-g", "android",
         fs_blkdev,
         size_str.c_str(),
         nullptr
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index a1de005..bef46e1 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -432,7 +432,7 @@
         if (change) *change = true;
         if (!DestroyLogicalPartition(partition_name, 0s)) return false;
     } else {
-        PERROR << "delete partition " << overlay;
+        LERROR << "delete partition " << overlay;
         return false;
     }
     errno = save_errno;
@@ -647,49 +647,65 @@
     if (fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) return true;
     auto mnt_type = fs_mgr_overlayfs_scratch_mount_type();
     auto scratch_device = fs_mgr_overlayfs_scratch_device();
-    auto partition_exists = fs_mgr_rw_access(scratch_device);
+    auto partition_create = !fs_mgr_rw_access(scratch_device);
+    auto slot_number = fs_mgr_overlayfs_slot_number();
+    auto super_device = fs_mgr_overlayfs_super_device(slot_number);
+    if (!fs_mgr_rw_access(super_device)) return false;
+    if (!fs_mgr_overlayfs_has_logical(fstab)) return false;
+    auto builder = MetadataBuilder::New(super_device, slot_number);
+    if (!builder) {
+        LERROR << "open " << super_device << " metadata";
+        return false;
+    }
+    const auto partition_name = android::base::Basename(kScratchMountPoint);
+    auto partition = builder->FindPartition(partition_name);
+    auto partition_exists = partition != nullptr;
+    auto changed = false;
     if (!partition_exists) {
-        auto slot_number = fs_mgr_overlayfs_slot_number();
-        auto super_device = fs_mgr_overlayfs_super_device(slot_number);
-        if (!fs_mgr_rw_access(super_device)) return false;
-        if (!fs_mgr_overlayfs_has_logical(fstab)) return false;
-        auto builder = MetadataBuilder::New(super_device, slot_number);
-        if (!builder) {
-            PERROR << "open " << super_device << " metadata";
+        partition = builder->AddPartition(partition_name, LP_PARTITION_ATTR_NONE);
+        if (!partition) {
+            LERROR << "create " << partition_name;
             return false;
         }
-        const auto partition_name = android::base::Basename(kScratchMountPoint);
-        partition_exists = builder->FindPartition(partition_name) != nullptr;
-        if (!partition_exists) {
-            auto partition = builder->AddPartition(partition_name, LP_PARTITION_ATTR_NONE);
-            if (!partition) {
-                PERROR << "create " << partition_name;
-                return false;
+        changed = true;
+    }
+    // Take half of free space, minimum 512MB or free space - 256KB margin.
+    static constexpr auto kMinimumSize = uint64_t(512 * 1024 * 1024);
+    static constexpr auto kMarginSize = uint64_t(256 * 1024);
+    if (partition->size() < kMinimumSize) {
+        auto partition_size =
+                builder->AllocatableSpace() - builder->UsedSpace() + partition->size();
+        if ((partition_size > kMinimumSize) || !partition->size()) {
+            partition_size = std::max(std::min(kMinimumSize, partition_size - kMarginSize),
+                                      partition_size / 2);
+            if (partition_size > partition->size()) {
+                if (!builder->ResizePartition(partition, partition_size)) {
+                    LERROR << "resize " << partition_name;
+                    return false;
+                }
+                if (!partition_create) DestroyLogicalPartition(partition_name, 10s);
+                changed = true;
+                partition_exists = false;
             }
-            auto partition_size = builder->AllocatableSpace() - builder->UsedSpace();
-            // 512MB or half the remaining available space, whichever is greater.
-            partition_size = std::max(uint64_t(512 * 1024 * 1024), partition_size / 2);
-            if (!builder->ResizePartition(partition, partition_size)) {
-                PERROR << "resize " << partition_name;
-                return false;
-            }
-
-            auto metadata = builder->Export();
-            if (!metadata) {
-                LERROR << "generate new metadata " << partition_name;
-                return false;
-            }
-            if (!UpdatePartitionTable(super_device, *metadata.get(), slot_number)) {
-                LERROR << "update " << partition_name;
-                return false;
-            }
-
-            if (change) *change = true;
+        }
+    }
+    // land the update back on to the partition
+    if (changed) {
+        auto metadata = builder->Export();
+        if (!metadata || !UpdatePartitionTable(super_device, *metadata.get(), slot_number)) {
+            LERROR << "add partition " << partition_name;
+            return false;
         }
 
+        if (change) *change = true;
+    }
+
+    if (changed || partition_create) {
         if (!CreateLogicalPartition(super_device, slot_number, partition_name, true, 0s,
                                     &scratch_device))
             return false;
+
+        if (change) *change = true;
     }
 
     if (partition_exists) {
@@ -709,6 +725,7 @@
     } else if (mnt_type == "ext4") {
         command = kMkExt4 + " -b 4096 -t ext4 -m 0 -O has_journal -M " + kScratchMountPoint;
     } else {
+        errno = ESRCH;
         LERROR << mnt_type << " has no mkfs cookbook";
         return false;
     }
@@ -773,6 +790,8 @@
 }
 
 std::vector<std::string> fs_mgr_overlayfs_required_devices(fstab* fstab) {
+    if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) return {};
+
     if (fs_mgr_get_entry_for_mount_point(const_cast<struct fstab*>(fstab), kScratchMountPoint)) {
         return {};
     }
diff --git a/fs_mgr/fs_mgr_vendor_overlay.cpp b/fs_mgr/fs_mgr_vendor_overlay.cpp
index e1815ff..830f0dd 100644
--- a/fs_mgr/fs_mgr_vendor_overlay.cpp
+++ b/fs_mgr/fs_mgr_vendor_overlay.cpp
@@ -35,43 +35,42 @@
 
 namespace {
 
-const auto kVendorOverlaySourceDir = "/system/vendor_overlay/"s;
+// The order of the list means the priority to show the files in the directory.
+// The last one has the highest priority.
+const std::vector<const std::string> kVendorOverlaySourceDirs = {
+        "/system/vendor_overlay/",
+        "/product/vendor_overlay/",
+};
 const auto kVndkVersionPropertyName = "ro.vndk.version"s;
 const auto kVendorTopDir = "/vendor/"s;
 const auto kLowerdirOption = "lowerdir="s;
 
-std::string fs_mgr_get_vendor_overlay_top_dir() {
-    // VNDK version is provided by the /vendor/default.prop
-    // To read the property, it must be called at the second init stage after the default
-    // properties are loaded.
-    std::string vndk_version = android::base::GetProperty(kVndkVersionPropertyName, "");
-    if (vndk_version.empty()) {
-        return "";
-    }
-    return kVendorOverlaySourceDir + vndk_version;
-}
+std::vector<std::pair<std::string, std::string>> fs_mgr_get_vendor_overlay_dirs(
+        const std::string& vndk_version) {
+    std::vector<std::pair<std::string, std::string>> vendor_overlay_dirs;
+    for (const auto& vendor_overlay_source : kVendorOverlaySourceDirs) {
+        const auto overlay_top = vendor_overlay_source + vndk_version;
+        std::unique_ptr<DIR, decltype(&closedir)> vendor_overlay_top(opendir(overlay_top.c_str()),
+                                                                     closedir);
+        if (!vendor_overlay_top) continue;
 
-std::vector<std::string> fs_mgr_get_vendor_overlay_dirs(const std::string& overlay_top) {
-    std::vector<std::string> vendor_overlay_dirs;
-    std::unique_ptr<DIR, decltype(&closedir)> vendor_overlay_top(opendir(overlay_top.c_str()),
-                                                                 closedir);
-    if (!vendor_overlay_top) return vendor_overlay_dirs;
+        // Vendor overlay root for current vendor version found!
+        LINFO << "vendor overlay root: " << overlay_top;
 
-    // Vendor overlay root for current vendor version found!
-    LINFO << "vendor overlay root: " << overlay_top;
-    struct dirent* dp;
-    while ((dp = readdir(vendor_overlay_top.get())) != nullptr) {
-        if (dp->d_type != DT_DIR || dp->d_name[0] == '.') {
-            continue;
+        struct dirent* dp;
+        while ((dp = readdir(vendor_overlay_top.get())) != nullptr) {
+            if (dp->d_type != DT_DIR || dp->d_name[0] == '.') {
+                continue;
+            }
+            vendor_overlay_dirs.emplace_back(overlay_top, dp->d_name);
         }
-        vendor_overlay_dirs.push_back(dp->d_name);
     }
-
     return vendor_overlay_dirs;
 }
 
-bool fs_mgr_vendor_overlay_mount(const std::string& overlay_top, const std::string& mount_point) {
-    const auto vendor_mount_point = kVendorTopDir + mount_point;
+bool fs_mgr_vendor_overlay_mount(const std::pair<std::string, std::string>& mount_point) {
+    const auto [overlay_top, mount_dir] = mount_point;
+    const auto vendor_mount_point = kVendorTopDir + mount_dir;
     LINFO << "vendor overlay mount on " << vendor_mount_point;
 
     const auto target_context = fs_mgr_get_context(vendor_mount_point);
@@ -79,7 +78,7 @@
         PERROR << " failed: cannot find the target vendor mount point";
         return false;
     }
-    const auto source_directory = overlay_top + "/" + mount_point;
+    const auto source_directory = overlay_top + "/" + mount_dir;
     const auto source_context = fs_mgr_get_context(source_directory);
     if (target_context != source_context) {
         LERROR << " failed: source and target contexts do not match (source:" << source_context
@@ -112,22 +111,25 @@
 // To read the properties, vendor overlay must be mounted at the second stage, right
 // after "property_load_boot_defaults()" is called.
 bool fs_mgr_vendor_overlay_mount_all() {
-    const auto overlay_top = fs_mgr_get_vendor_overlay_top_dir();
-    if (overlay_top.empty()) {
+    // To read the property, it must be called at the second init stage after the default
+    // properties are loaded.
+    static const auto vndk_version = android::base::GetProperty(kVndkVersionPropertyName, "");
+    if (vndk_version.empty()) {
         LINFO << "vendor overlay: vndk version not defined";
         return false;
     }
-    const auto vendor_overlay_dirs = fs_mgr_get_vendor_overlay_dirs(overlay_top);
+
+    const auto vendor_overlay_dirs = fs_mgr_get_vendor_overlay_dirs(vndk_version);
     if (vendor_overlay_dirs.empty()) return true;
     if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) {
         LINFO << "vendor overlay: kernel does not support overlayfs";
         return false;
     }
 
-    // Mount each directory in /system/vendor_overlay/<ver> on /vendor
+    // Mount each directory in /(system|product)/vendor_overlay/<ver> on /vendor
     auto ret = true;
     for (const auto& vendor_overlay_dir : vendor_overlay_dirs) {
-        if (!fs_mgr_vendor_overlay_mount(overlay_top, vendor_overlay_dir)) {
+        if (!fs_mgr_vendor_overlay_mount(vendor_overlay_dir)) {
             ret = false;
         }
     }
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index 7a00194..d9e017c 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -63,6 +63,7 @@
 
 Returns: the logcat output" ]
 adb_logcat() {
+  echo "${RED}[     INFO ]${NORMAL} logcat ${@}" >&2 &&
   adb logcat "${@}" </dev/null |
     grep -v 'logd    : logdr: UID=' |
     sed -e '${/------- beginning of kernel/d}' -e 's/^[0-1][0-9]-[0-3][0-9] //'
@@ -106,12 +107,13 @@
 
 Returns: true if the reboot command succeeded" ]
 adb_reboot() {
-  adb reboot remount-test
+  adb reboot remount-test &&
+  sleep 2
 }
 
 [ "USAGE: adb_wait [timeout]
 
-Returns: waits until the device has returned or the optional timeout" ]
+Returns: waits until the device has returned for adb or optional timeout" ]
 adb_wait() {
   if [ -n "${1}" ]; then
     timeout --preserve-status --signal=KILL ${1} adb wait-for-device
@@ -120,14 +122,49 @@
   fi
 }
 
+[ "USAGE: fastboot_wait [timeout]
+
+Returns: waits until the device has returned for fastboot or optional timeout" ]
+fastboot_wait() {
+  # fastboot has no wait-for-device, but it does an automatic
+  # wait and requires (even a nonsensical) command to do so.
+  if [ -n "${1}" ]; then
+    timeout --preserve-status --signal=KILL ${1} fastboot wait-for-device
+  else
+    fastboot wait-for-device >/dev/null
+  fi >/dev/null 2>/dev/null ||
+    inFastboot
+}
+
 [ "USAGE: adb_root
 
 Returns: true if device in root state" ]
 adb_root() {
-  adb root >/dev/null </dev/null 2>/dev/null &&
-  sleep 1 &&
-  adb_wait &&
-  sleep 1
+  adb root >/dev/null </dev/null 2>/dev/null
+  sleep 2
+  adb_wait 2m &&
+    [ `adb_sh echo '${USER}'` = root ]
+}
+
+[ "USAGE: fastboot_getvar var expected
+
+Returns: true if var output matches expected" ]
+fastboot_getvar() {
+  O=`fastboot getvar ${1} 2>&1`
+  err=${?}
+  O="${O#< waiting for * >?}"
+  O="${O%%?Finished. Total time: *}"
+  if [ 0 -ne ${err} ]; then
+    echo ${O} >&2
+    false
+    return
+  fi
+  if [ -n "${2}" -a "${1}: ${2}" != "${O}" ]; then
+    echo "${2} != ${O}" >&2
+    false
+    return
+  fi
+  echo ${O} >&2
 }
 
 [ "USAGE: die [-t <epoch>] [message] >/dev/stderr
@@ -233,8 +270,7 @@
 adb_su ls /sys/module/overlay/parameters/override_creds </dev/null >/dev/null &&
   echo "${GREEN}[       OK ]${NORMAL} overlay module supports override_creds" >&2 ||
   die "overlay module can not be used on ANDROID"
-adb_root &&
-  adb_wait ||
+adb_root ||
   die "initial setup"
 reboot=false
 OVERLAYFS_BACKING="cache mnt/scratch"
@@ -250,8 +286,7 @@
   echo "${ORANGE}[  WARNING ]${NORMAL} rebooting before test" >&2
   adb_reboot &&
     adb_wait 2m &&
-    adb_root &&
-    adb_wait ||
+    adb_root ||
     die "reboot after wipe"
 fi
 D=`adb_sh df -k </dev/null` &&
@@ -288,7 +323,6 @@
 
 T=`adb_date`
 adb_root &&
-  adb_wait &&
   adb remount &&
   D=`adb_sh df -k </dev/null` ||
   die -t ${T} "can not collect filesystem data"
@@ -340,19 +374,31 @@
   die "re-read vendor hello after reboot w/o root"
 check_eq "cat: /vendor/hello: Permission denied" "${B}" vendor after reboot w/o root
 adb_root &&
-  adb_wait &&
   B="`adb_cat /vendor/hello`" ||
   die "re-read vendor hello after reboot"
 check_eq "${A}" "${B}" vendor after reboot
 
 adb reboot-fastboot &&
-  fastboot flash vendor &&
-  fastboot reboot ||
+  fastboot_wait 2m &&
+  fastboot flash vendor ||
   die "fastbootd flash vendor"
+# check scratch via fastboot
+fastboot_getvar partition-type:scratch raw &&
+  fastboot_getvar has-slot:scratch no &&
+  fastboot_getvar is-logical:scratch yes ||
+  die "fastboot can not see scratch parameters"
+echo "${ORANGE}[     INFO ]${NORMAL} expect fastboot erase scratch to fail" >&2
+fastboot erase scratch &&
+  die "fastbootd can erase scratch"
+echo "${ORANGE}[     INFO ]${NORMAL} expect fastboot format scratch to fail" >&2
+fastboot format scratch &&
+  die "fastbootd can format scratch"
+fastboot reboot ||
+  die "can not reboot out of fastbootd"
+echo "${ORANGE}[  WARNING ]${NORMAL} adb after fastboot ... waiting 2 minutes"
 adb_wait 2m ||
   die "did not reboot after flash"
 adb_root &&
-  adb_wait &&
   D=`adb_sh df -k </dev/null` &&
   H=`echo "${D}" | head -1` &&
   D=`echo "${D}" | grep "^overlay "` &&
@@ -365,8 +411,7 @@
 B="`adb_cat /system/hello`" ||
   die "re-read system hello after flash vendor"
 check_eq "${A}" "${B}" system after flash vendor
-adb_root &&
-  adb_wait ||
+adb_root ||
   die "adb root"
 B="`adb_cat /vendor/hello`" &&
   die "re-read vendor hello after flash vendor"
@@ -384,4 +429,27 @@
   die "re-read vendor hello after rm"
 check_eq "cat: /vendor/hello: No such file or directory" "${B}" after flash rm
 
+adb reboot-fastboot &&
+  dd if=/dev/zero of=adb-remount-test.img bs=4096 count=16 &&
+  fastboot_wait 2m ||
+  die "reboot into fastbootd"
+fastboot flash scratch adb-remount-test.img
+err=${?}
+rm adb-remount-test.img
+[ 0 -eq ${err} ] ||
+  die "fastbootd flash scratch"
+fastboot reboot ||
+  die "can not reboot out of fastbootd"
+adb_wait 2m &&
+  adb_root ||
+  die "did not reboot after flash"
+T=`adb_date`
+D=`adb disable-verity 2>&1`
+err=${?}
+echo "${D}"
+[ ${err} = 0 ] &&
+  [ X"${D}" = X"${D##*setup failed}" ] &&
+  [ X"${D}" != X"${D##*using overlayfs}" ] ||
+  die -t ${T} "setup for overlayfs"
+
 echo "${GREEN}[  PASSED  ]${NORMAL} adb remount" >&2
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 43b2ebf..a3257f5 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -449,7 +449,9 @@
     // Includes the partition names of fstab records and verity_loc_device (if any).
     // Notes that fstab_rec->blk_device has A/B suffix updated by fs_mgr when A/B is used.
     for (auto fstab_rec : mount_fstab_recs_) {
-        required_devices_partition_names_.emplace(basename(fstab_rec->blk_device));
+        if (!fs_mgr_is_logical(fstab_rec)) {
+            required_devices_partition_names_.emplace(basename(fstab_rec->blk_device));
+        }
     }
 
     if (!verity_loc_device.empty()) {
diff --git a/libmemunreachable/HeapWalker.cpp b/libmemunreachable/HeapWalker.cpp
index 2403ad0..a046dad 100644
--- a/libmemunreachable/HeapWalker.cpp
+++ b/libmemunreachable/HeapWalker.cpp
@@ -76,12 +76,14 @@
     Range range = to_do.back();
     to_do.pop_back();
 
+    walking_range_ = range;
     ForEachPtrInRange(range, [&](Range& ref_range, AllocationInfo* ref_info) {
       if (!ref_info->referenced_from_root) {
         ref_info->referenced_from_root = true;
         to_do.push_back(ref_range);
       }
     });
+    walking_range_ = Range{0, 0};
   }
 }
 
@@ -113,6 +115,10 @@
 
   RecurseRoot(vals);
 
+  if (segv_page_count_ > 0) {
+    MEM_ALOGE("%zu pages skipped due to segfaults", segv_page_count_);
+  }
+
   return true;
 }
 
@@ -168,7 +174,15 @@
     handler.reset();
     return;
   }
-  MEM_ALOGW("failed to read page at %p, signal %d", si->si_addr, signal);
+  if (!segv_logged_) {
+    MEM_ALOGW("failed to read page at %p, signal %d", si->si_addr, signal);
+    if (walking_range_.begin != 0U) {
+      MEM_ALOGW("while walking range %p-%p", reinterpret_cast<void*>(walking_range_.begin),
+                reinterpret_cast<void*>(walking_range_.end));
+    }
+    segv_logged_ = true;
+  }
+  segv_page_count_++;
   if (!MapOverPage(si->si_addr)) {
     handler.reset();
   }
diff --git a/libmemunreachable/HeapWalker.h b/libmemunreachable/HeapWalker.h
index 92a8325..b37cc62 100644
--- a/libmemunreachable/HeapWalker.h
+++ b/libmemunreachable/HeapWalker.h
@@ -53,7 +53,10 @@
         roots_(allocator),
         root_vals_(allocator),
         segv_handler_(),
-        walking_ptr_(0) {
+        walking_ptr_(0),
+        walking_range_{0, 0},
+        segv_logged_(false),
+        segv_page_count_(0) {
     valid_allocations_range_.end = 0;
     valid_allocations_range_.begin = ~valid_allocations_range_.end;
 
@@ -100,7 +103,10 @@
   allocator::vector<uintptr_t> root_vals_;
 
   ScopedSignalHandler segv_handler_;
-  uintptr_t walking_ptr_;
+  volatile uintptr_t walking_ptr_;
+  Range walking_range_;
+  bool segv_logged_;
+  size_t segv_page_count_;
 };
 
 template <class F>
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 14f82c7..a5e0dc9 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -184,6 +184,7 @@
         "tests/MapInfoGetLoadBiasTest.cpp",
         "tests/MapsTest.cpp",
         "tests/MemoryBufferTest.cpp",
+        "tests/MemoryCacheTest.cpp",
         "tests/MemoryFake.cpp",
         "tests/MemoryFileTest.cpp",
         "tests/MemoryLocalTest.cpp",
@@ -310,6 +311,28 @@
     ],
 }
 
+//-------------------------------------------------------------------------
+// Benchmarks
+//-------------------------------------------------------------------------
+cc_benchmark {
+    name: "unwind_benchmarks",
+    host_supported: true,
+    defaults: ["libunwindstack_flags"],
+
+    // Disable optimizations so that all of the calls are not optimized away.
+    cflags: [
+        "-O0",
+    ],
+
+    srcs: [
+        "benchmarks/unwind_benchmarks.cpp",
+    ],
+
+    shared_libs: [
+        "libunwindstack",
+    ],
+}
+
 // Generates the elf data for use in the tests for .gnu_debugdata frames.
 // Once these files are generated, use the xz command to compress the data.
 cc_binary_host {
diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp
index cfa8c6d..a30d65e 100644
--- a/libunwindstack/Memory.cpp
+++ b/libunwindstack/Memory.cpp
@@ -174,6 +174,13 @@
   return std::shared_ptr<Memory>(new MemoryRemote(pid));
 }
 
+std::shared_ptr<Memory> Memory::CreateProcessMemoryCached(pid_t pid) {
+  if (pid == getpid()) {
+    return std::shared_ptr<Memory>(new MemoryCache(new MemoryLocal()));
+  }
+  return std::shared_ptr<Memory>(new MemoryCache(new MemoryRemote(pid)));
+}
+
 size_t MemoryBuffer::Read(uint64_t addr, void* dst, size_t size) {
   if (addr >= raw_.size()) {
     return 0;
@@ -398,4 +405,50 @@
   return 0;
 }
 
+size_t MemoryCache::Read(uint64_t addr, void* dst, size_t size) {
+  // Only bother caching and looking at the cache if this is a small read for now.
+  if (size > 64) {
+    return impl_->Read(addr, dst, size);
+  }
+
+  uint64_t addr_page = addr >> kCacheBits;
+  auto entry = cache_.find(addr_page);
+  uint8_t* cache_dst;
+  if (entry != cache_.end()) {
+    cache_dst = entry->second;
+  } else {
+    cache_dst = cache_[addr_page];
+    if (!impl_->ReadFully(addr_page << kCacheBits, cache_dst, kCacheSize)) {
+      // Erase the entry.
+      cache_.erase(addr_page);
+      return impl_->Read(addr, dst, size);
+    }
+  }
+  size_t max_read = ((addr_page + 1) << kCacheBits) - addr;
+  if (size <= max_read) {
+    memcpy(dst, &cache_dst[addr & kCacheMask], size);
+    return size;
+  }
+
+  // The read crossed into another cached entry, since a read can only cross
+  // into one extra cached page, duplicate the code rather than looping.
+  memcpy(dst, &cache_dst[addr & kCacheMask], max_read);
+  dst = &reinterpret_cast<uint8_t*>(dst)[max_read];
+  addr_page++;
+
+  entry = cache_.find(addr_page);
+  if (entry != cache_.end()) {
+    cache_dst = entry->second;
+  } else {
+    cache_dst = cache_[addr_page];
+    if (!impl_->ReadFully(addr_page << kCacheBits, cache_dst, kCacheSize)) {
+      // Erase the entry.
+      cache_.erase(addr_page);
+      return impl_->Read(addr_page << kCacheBits, dst, size - max_read) + max_read;
+    }
+  }
+  memcpy(dst, cache_dst, size - max_read);
+  return size;
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/benchmarks/unwind_benchmarks.cpp b/libunwindstack/benchmarks/unwind_benchmarks.cpp
new file mode 100644
index 0000000..db0fb54
--- /dev/null
+++ b/libunwindstack/benchmarks/unwind_benchmarks.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <memory>
+
+#include <benchmark/benchmark.h>
+
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/RegsGetLocal.h>
+#include <unwindstack/Unwinder.h>
+
+size_t Call6(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) {
+  std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::CreateFromLocal());
+  unwindstack::RegsGetLocal(regs.get());
+  unwindstack::Unwinder unwinder(32, maps, regs.get(), process_memory);
+  unwinder.Unwind();
+  return unwinder.NumFrames();
+}
+
+size_t Call5(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) {
+  return Call6(process_memory, maps);
+}
+
+size_t Call4(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) {
+  return Call5(process_memory, maps);
+}
+
+size_t Call3(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) {
+  return Call4(process_memory, maps);
+}
+
+size_t Call2(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) {
+  return Call3(process_memory, maps);
+}
+
+size_t Call1(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) {
+  return Call2(process_memory, maps);
+}
+
+static void BM_uncached_unwind(benchmark::State& state) {
+  auto process_memory = unwindstack::Memory::CreateProcessMemory(getpid());
+  unwindstack::LocalMaps maps;
+  if (!maps.Parse()) {
+    state.SkipWithError("Failed to parse local maps.");
+  }
+
+  for (auto _ : state) {
+    benchmark::DoNotOptimize(Call1(process_memory, &maps));
+  }
+}
+BENCHMARK(BM_uncached_unwind);
+
+static void BM_cached_unwind(benchmark::State& state) {
+  auto process_memory = unwindstack::Memory::CreateProcessMemoryCached(getpid());
+  unwindstack::LocalMaps maps;
+  if (!maps.Parse()) {
+    state.SkipWithError("Failed to parse local maps.");
+  }
+
+  for (auto _ : state) {
+    benchmark::DoNotOptimize(Call1(process_memory, &maps));
+  }
+}
+BENCHMARK(BM_cached_unwind);
+
+BENCHMARK_MAIN();
diff --git a/libunwindstack/include/unwindstack/Memory.h b/libunwindstack/include/unwindstack/Memory.h
index 9c425cb..dba41d1 100644
--- a/libunwindstack/include/unwindstack/Memory.h
+++ b/libunwindstack/include/unwindstack/Memory.h
@@ -25,6 +25,7 @@
 #include <map>
 #include <memory>
 #include <string>
+#include <unordered_map>
 #include <vector>
 
 namespace unwindstack {
@@ -35,9 +36,12 @@
   virtual ~Memory() = default;
 
   static std::shared_ptr<Memory> CreateProcessMemory(pid_t pid);
+  static std::shared_ptr<Memory> CreateProcessMemoryCached(pid_t pid);
 
   virtual bool ReadString(uint64_t addr, std::string* string, uint64_t max_read = UINT64_MAX);
 
+  virtual void Clear() {}
+
   virtual size_t Read(uint64_t addr, void* dst, size_t size) = 0;
 
   bool ReadFully(uint64_t addr, void* dst, size_t size);
@@ -51,6 +55,24 @@
   }
 };
 
+class MemoryCache : public Memory {
+ public:
+  MemoryCache(Memory* memory) : impl_(memory) {}
+  virtual ~MemoryCache() = default;
+
+  size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+  void Clear() override { cache_.clear(); }
+
+ private:
+  constexpr static size_t kCacheBits = 12;
+  constexpr static size_t kCacheMask = (1 << kCacheBits) - 1;
+  constexpr static size_t kCacheSize = 1 << kCacheBits;
+  std::unordered_map<uint64_t, uint8_t[kCacheSize]> cache_;
+
+  std::unique_ptr<Memory> impl_;
+};
+
 class MemoryBuffer : public Memory {
  public:
   MemoryBuffer() = default;
diff --git a/libunwindstack/tests/MemoryCacheTest.cpp b/libunwindstack/tests/MemoryCacheTest.cpp
new file mode 100644
index 0000000..a3def20
--- /dev/null
+++ b/libunwindstack/tests/MemoryCacheTest.cpp
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/Memory.h>
+
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class MemoryCacheTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    memory_ = new MemoryFake;
+    memory_cache_.reset(new MemoryCache(memory_));
+
+    memory_->SetMemoryBlock(0x8000, 4096, 0xab);
+    memory_->SetMemoryBlock(0x9000, 4096, 0xde);
+    memory_->SetMemoryBlock(0xa000, 3000, 0x50);
+  }
+
+  MemoryFake* memory_;
+  std::unique_ptr<MemoryCache> memory_cache_;
+
+  constexpr static size_t kMaxCachedSize = 64;
+};
+
+TEST_F(MemoryCacheTest, cached_read) {
+  for (size_t i = 1; i <= kMaxCachedSize; i++) {
+    std::vector<uint8_t> buffer(i);
+    ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i))
+        << "Read failed at size " << i;
+    ASSERT_EQ(std::vector<uint8_t>(i, 0xab), buffer) << "Failed at size " << i;
+  }
+
+  // Verify the cached data is used.
+  memory_->SetMemoryBlock(0x8000, 4096, 0xff);
+  for (size_t i = 1; i <= kMaxCachedSize; i++) {
+    std::vector<uint8_t> buffer(i);
+    ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i))
+        << "Read failed at size " << i;
+    ASSERT_EQ(std::vector<uint8_t>(i, 0xab), buffer) << "Failed at size " << i;
+  }
+}
+
+TEST_F(MemoryCacheTest, no_cached_read_after_clear) {
+  for (size_t i = 1; i <= kMaxCachedSize; i++) {
+    std::vector<uint8_t> buffer(i);
+    ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i))
+        << "Read failed at size " << i;
+    ASSERT_EQ(std::vector<uint8_t>(i, 0xab), buffer) << "Failed at size " << i;
+  }
+
+  // Verify the cached data is not used after a reset.
+  memory_cache_->Clear();
+  memory_->SetMemoryBlock(0x8000, 4096, 0xff);
+  for (size_t i = 1; i <= kMaxCachedSize; i++) {
+    std::vector<uint8_t> buffer(i);
+    ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i))
+        << "Read failed at size " << i;
+    ASSERT_EQ(std::vector<uint8_t>(i, 0xff), buffer) << "Failed at size " << i;
+  }
+}
+
+TEST_F(MemoryCacheTest, cached_read_across_caches) {
+  std::vector<uint8_t> expect(16, 0xab);
+  expect.resize(32, 0xde);
+
+  std::vector<uint8_t> buffer(32);
+  ASSERT_TRUE(memory_cache_->ReadFully(0x8ff0, buffer.data(), 32));
+  ASSERT_EQ(expect, buffer);
+
+  // Verify the cached data is used.
+  memory_->SetMemoryBlock(0x8000, 4096, 0xff);
+  memory_->SetMemoryBlock(0x9000, 4096, 0xff);
+  ASSERT_TRUE(memory_cache_->ReadFully(0x8ff0, buffer.data(), 32));
+  ASSERT_EQ(expect, buffer);
+}
+
+TEST_F(MemoryCacheTest, no_cache_read) {
+  for (size_t i = kMaxCachedSize + 1; i < 2 * kMaxCachedSize; i++) {
+    std::vector<uint8_t> buffer(i);
+    ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i))
+        << "Read failed at size " << i;
+    ASSERT_EQ(std::vector<uint8_t>(i, 0xab), buffer) << "Failed at size " << i;
+  }
+
+  // Verify the cached data is not used.
+  memory_->SetMemoryBlock(0x8000, 4096, 0xff);
+  for (size_t i = kMaxCachedSize + 1; i < 2 * kMaxCachedSize; i++) {
+    std::vector<uint8_t> buffer(i);
+    ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i))
+        << "Read failed at size " << i;
+    ASSERT_EQ(std::vector<uint8_t>(i, 0xff), buffer) << "Failed at size " << i;
+  }
+}
+
+TEST_F(MemoryCacheTest, read_for_cache_fail) {
+  std::vector<uint8_t> buffer(kMaxCachedSize);
+  ASSERT_TRUE(memory_cache_->ReadFully(0xa010, buffer.data(), kMaxCachedSize));
+  ASSERT_EQ(std::vector<uint8_t>(kMaxCachedSize, 0x50), buffer);
+
+  // Verify the cached data is not used.
+  memory_->SetMemoryBlock(0xa000, 3000, 0xff);
+  ASSERT_TRUE(memory_cache_->ReadFully(0xa010, buffer.data(), kMaxCachedSize));
+  ASSERT_EQ(std::vector<uint8_t>(kMaxCachedSize, 0xff), buffer);
+}
+
+TEST_F(MemoryCacheTest, read_for_cache_fail_cross) {
+  std::vector<uint8_t> expect(16, 0xde);
+  expect.resize(32, 0x50);
+
+  std::vector<uint8_t> buffer(32);
+  ASSERT_TRUE(memory_cache_->ReadFully(0x9ff0, buffer.data(), 32));
+  ASSERT_EQ(expect, buffer);
+
+  // Verify the cached data is not used for the second half but for the first.
+  memory_->SetMemoryBlock(0xa000, 3000, 0xff);
+  ASSERT_TRUE(memory_cache_->ReadFully(0x9ff0, buffer.data(), 32));
+  expect.resize(16);
+  expect.resize(32, 0xff);
+  ASSERT_EQ(expect, buffer);
+}
+
+}  // namespace unwindstack
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index 2095189..9538bba 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -125,6 +125,7 @@
             enabled: true,
         },
     },
+    test_suites: ["device-tests"],
 }
 
 // Performance benchmarks.
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index fc8c960..18d5287 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -165,9 +165,14 @@
         bug_num->assign("");
     }
 
+    // Ensure the uid name is not null before passing it to the bug string.
     if (uid >= AID_APP_START && uid <= AID_APP_END) {
-        bug_num->append(" app=");
-        bug_num->append(android::uidToName(uid));
+        char* uidname = android::uidToName(uid);
+        if (uidname) {
+            bug_num->append(" app=");
+            bug_num->append(uidname);
+            free(uidname);
+        }
     }
 }
 
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index 2f85dec..d47506c 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -72,7 +72,7 @@
 /dev/diag_arm9            0660   radio      radio
 /dev/ttyMSM0              0600   bluetooth  bluetooth
 /dev/uhid                 0660   uhid       uhid
-/dev/uinput               0660   system     bluetooth
+/dev/uinput               0660   uhid       uhid
 /dev/alarm                0664   system     radio
 /dev/rtc0                 0640   system     system
 /dev/tty0                 0660   root       system