Merge "adb: switch version back to 40."
diff --git a/adb/daemon/remount_service.cpp b/adb/daemon/remount_service.cpp
index ae02525..b72ed16 100644
--- a/adb/daemon/remount_service.cpp
+++ b/adb/daemon/remount_service.cpp
@@ -78,7 +78,13 @@
     std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
                                                                fs_mgr_free_fstab);
     struct fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab.get(), dir);
-    return rec ? rec->blk_device : "";
+    if (!rec) {
+        return "";
+    }
+    if (fs_mgr_is_logical(rec)) {
+        fs_mgr_update_logical_partition(rec);
+    }
+    return rec->blk_device;
 }
 
 // The proc entry for / is full of lies, so check fstab instead.
@@ -87,12 +93,16 @@
     if (is_root) {
         return find_fstab_mount(dir);
     } else {
-       return find_proc_mount(dir);
+        return find_proc_mount(dir);
     }
 }
 
+bool dev_is_overlayfs(const std::string& dev) {
+    return (dev == "overlay") || (dev == "overlayfs");
+}
+
 bool make_block_device_writable(const std::string& dev) {
-    if ((dev == "overlay") || (dev == "overlayfs")) return true;
+    if (dev_is_overlayfs(dev)) return true;
     int fd = unix_open(dev.c_str(), O_RDONLY | O_CLOEXEC);
     if (fd == -1) {
         return false;
@@ -155,11 +165,16 @@
         return true;
     }
     bool is_root = strcmp(dir, "/") == 0;
-    if (is_root && !find_mount("/system", false).empty()) {
+    if (is_root && dev_is_overlayfs(find_mount("/system", false))) {
         dir = "/system";
         is_root = false;
     }
     std::string dev = find_mount(dir, is_root);
+    if (is_root && dev.empty()) {
+        // The fstab entry will be /system if the device switched roots during
+        // first-stage init.
+        dev = find_mount("/system", true);
+    }
     // Even if the device for the root is not found, we still try to remount it
     // as rw. This typically only happens when running Android in a container:
     // the root will almost always be in a loop device, which is dynamic, so
diff --git a/base/include/android-base/unique_fd.h b/base/include/android-base/unique_fd.h
index cd2dc04..4e3879b 100644
--- a/base/include/android-base/unique_fd.h
+++ b/base/include/android-base/unique_fd.h
@@ -19,6 +19,7 @@
 #include <fcntl.h>
 
 #if !defined(_WIN32)
+#include <dirent.h>
 #include <sys/socket.h>
 #endif
 
@@ -211,6 +212,17 @@
   return file;
 }
 
+// Using fdopendir with unique_fd correctly is more annoying than it should be,
+// because fdopen doesn't close the file descriptor received upon failure.
+inline DIR* Fdopendir(unique_fd&& ufd) {
+  int fd = ufd.release();
+  DIR* dir = fdopendir(fd);
+  if (dir == nullptr) {
+    close(fd);
+  }
+  return dir;
+}
+
 #endif  // !defined(_WIN32)
 
 }  // namespace base
diff --git a/fastboot/constants.h b/fastboot/constants.h
index 705da33..81f0560 100644
--- a/fastboot/constants.h
+++ b/fastboot/constants.h
@@ -64,3 +64,4 @@
 #define FB_VAR_OFF_MODE_CHARGE_STATE "off-mode-charge"
 #define FB_VAR_BATTERY_VOLTAGE "battery-voltage"
 #define FB_VAR_BATTERY_SOC_OK "battery-soc-ok"
+#define FB_VAR_SUPER_PARTITION_NAME "super-partition-name"
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index 6e45133..08744ec 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -99,7 +99,8 @@
             {FB_VAR_OFF_MODE_CHARGE_STATE, {GetOffModeChargeState, nullptr}},
             {FB_VAR_BATTERY_VOLTAGE, {GetBatteryVoltage, nullptr}},
             {FB_VAR_BATTERY_SOC_OK, {GetBatterySoCOk, nullptr}},
-            {FB_VAR_HW_REVISION, {GetHardwareRevision, nullptr}}};
+            {FB_VAR_HW_REVISION, {GetHardwareRevision, nullptr}},
+            {FB_VAR_SUPER_PARTITION_NAME, {GetSuperPartitionName, nullptr}}};
 
     if (args.size() < 2) {
         return device->WriteFail("Missing argument");
@@ -329,7 +330,6 @@
 
   private:
     std::string super_device_;
-    uint32_t slot_number_;
     std::unique_ptr<MetadataBuilder> builder_;
 };
 
@@ -341,8 +341,8 @@
     super_device_ = *super_device;
 
     std::string slot = device->GetCurrentSlot();
-    slot_number_ = SlotNumberForSlotSuffix(slot);
-    builder_ = MetadataBuilder::New(super_device_, slot_number_);
+    uint32_t slot_number = SlotNumberForSlotSuffix(slot);
+    builder_ = MetadataBuilder::New(super_device_, slot_number);
 }
 
 bool PartitionBuilder::Write() {
@@ -350,7 +350,11 @@
     if (!metadata) {
         return false;
     }
-    return UpdatePartitionTable(super_device_, *metadata.get(), slot_number_);
+    bool ok = true;
+    for (uint32_t i = 0; i < metadata->geometry.metadata_slot_count; i++) {
+        ok &= UpdatePartitionTable(super_device_, *metadata.get(), i);
+    }
+    return ok;
 }
 
 bool CreatePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args) {
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
index 66b90bf..7c9e1d0 100644
--- a/fastboot/device/flashing.cpp
+++ b/fastboot/device/flashing.cpp
@@ -148,13 +148,40 @@
     // image.
     std::string slot_suffix = device->GetCurrentSlot();
     uint32_t slot_number = SlotNumberForSlotSuffix(slot_suffix);
-    if (wipe || !ReadMetadata(super_name, slot_number)) {
+    std::unique_ptr<LpMetadata> old_metadata = ReadMetadata(super_name, slot_number);
+    if (wipe || !old_metadata) {
         if (!FlashPartitionTable(super_name, *new_metadata.get())) {
             return device->WriteFail("Unable to flash new partition table");
         }
         return device->WriteOkay("Successfully flashed partition table");
     }
 
+    std::set<std::string> partitions_to_keep;
+    for (const auto& partition : old_metadata->partitions) {
+        // Preserve partitions in the other slot, but not the current slot.
+        std::string partition_name = GetPartitionName(partition);
+        if (!slot_suffix.empty() && GetPartitionSlotSuffix(partition_name) == slot_suffix) {
+            continue;
+        }
+        partitions_to_keep.emplace(partition_name);
+    }
+
+    // Do not preserve the scratch partition.
+    partitions_to_keep.erase("scratch");
+
+    if (!partitions_to_keep.empty()) {
+        std::unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(*new_metadata.get());
+        if (!builder->ImportPartitions(*old_metadata.get(), partitions_to_keep)) {
+            return device->WriteFail(
+                    "Old partitions are not compatible with the new super layout; wipe needed");
+        }
+
+        new_metadata = builder->Export();
+        if (!new_metadata) {
+            return device->WriteFail("Unable to build new partition table; wipe needed");
+        }
+    }
+
     // Write the new table to every metadata slot.
     bool ok = true;
     for (size_t i = 0; i < new_metadata->geometry.metadata_slot_count; i++) {
diff --git a/fastboot/device/variables.cpp b/fastboot/device/variables.cpp
index cbd2856..601af34 100644
--- a/fastboot/device/variables.cpp
+++ b/fastboot/device/variables.cpp
@@ -24,7 +24,9 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <ext4_utils/ext4_utils.h>
+#include <fs_mgr.h>
 #include <healthhalutils/HealthHalUtils.h>
+#include <liblp/liblp.h>
 
 #include "fastboot_device.h"
 #include "flashing.h"
@@ -35,6 +37,7 @@
 using ::android::hardware::fastboot::V1_0::FileSystemType;
 using ::android::hardware::fastboot::V1_0::Result;
 using ::android::hardware::fastboot::V1_0::Status;
+using namespace android::fs_mgr;
 
 constexpr char kFastbootProtocolVersion[] = "0.4";
 
@@ -417,3 +420,10 @@
     *message = android::base::GetProperty("ro.revision", "");
     return true;
 }
+
+bool GetSuperPartitionName(FastbootDevice* device, const std::vector<std::string>& /* args */,
+                           std::string* message) {
+    uint32_t slot_number = SlotNumberForSlotSuffix(device->GetCurrentSlot());
+    *message = fs_mgr_get_super_partition_name(slot_number);
+    return true;
+}
diff --git a/fastboot/device/variables.h b/fastboot/device/variables.h
index 59b71e8..015a4c5 100644
--- a/fastboot/device/variables.h
+++ b/fastboot/device/variables.h
@@ -59,6 +59,8 @@
                        std::string* message);
 bool GetBatterySoCOk(FastbootDevice* device, const std::vector<std::string>& args,
                      std::string* message);
+bool GetSuperPartitionName(FastbootDevice* device, const std::vector<std::string>& args,
+                           std::string* message);
 
 // Helpers for getvar all.
 std::vector<std::vector<std::string>> GetAllPartitionArgsWithSlot(FastbootDevice* device);
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 3c6b1b7..625e047 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -1129,25 +1129,6 @@
     return fb->GetVar("is-userspace", &value) == fastboot::SUCCESS && value == "yes";
 }
 
-static bool if_partition_exists(const std::string& partition, const std::string& slot) {
-    std::string has_slot;
-    std::string partition_name = partition;
-
-    if (fb->GetVar("has-slot:" + partition, &has_slot) == fastboot::SUCCESS && has_slot == "yes") {
-        if (slot == "") {
-            std::string current_slot = get_current_slot();
-            if (current_slot == "") {
-                die("Failed to identify current slot");
-            }
-            partition_name += "_" + current_slot;
-        } else {
-            partition_name += "_" + slot;
-        }
-    }
-    std::string partition_size;
-    return fb->GetVar("partition-size:" + partition_name, &partition_size) == fastboot::SUCCESS;
-}
-
 static void reboot_to_userspace_fastboot() {
     fb->RebootTo("fastboot");
 
@@ -1200,6 +1181,15 @@
 void FlashAllTool::Flash() {
     DumpInfo();
     CheckRequirements();
+
+    // Change the slot first, so we boot into the correct recovery image when
+    // using fastbootd.
+    if (slot_override_ == "all") {
+        set_active("a");
+    } else {
+        set_active(slot_override_);
+    }
+
     DetermineSecondarySlot();
     CollectImages();
 
@@ -1223,12 +1213,6 @@
 
     // Flash OS images, resizing logical partitions as needed.
     FlashImages(os_images_);
-
-    if (slot_override_ == "all") {
-        set_active("a");
-    } else {
-        set_active(slot_override_);
-    }
 }
 
 void FlashAllTool::CheckRequirements() {
@@ -1243,7 +1227,7 @@
     if (skip_secondary_) {
         return;
     }
-    if (slot_override_ != "") {
+    if (slot_override_ != "" && slot_override_ != "all") {
         secondary_slot_ = get_other_slot(slot_override_);
     } else {
         secondary_slot_ = get_other_slot();
@@ -1304,10 +1288,6 @@
 }
 
 void FlashAllTool::UpdateSuperPartition() {
-    if (!if_partition_exists("super", "")) {
-        return;
-    }
-
     int fd = source_.OpenFile("super_empty.img");
     if (fd < 0) {
         return;
@@ -1318,9 +1298,14 @@
     if (!is_userspace_fastboot()) {
         die("Failed to boot into userspace; one or more components might be unbootable.");
     }
-    fb->Download("super", fd, get_file_size(fd));
 
-    std::string command = "update-super:super";
+    std::string super_name;
+    if (fb->GetVar("super-partition-name", &super_name) != fastboot::RetCode::SUCCESS) {
+        super_name = "super";
+    }
+    fb->Download(super_name, fd, get_file_size(fd));
+
+    std::string command = "update-super:" + super_name;
     if (wipe_) {
         command += ":wipe";
     }
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index ae2e2fe..c321fe3 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -1588,6 +1588,25 @@
     return true;
 }
 
-std::string fs_mgr_get_super_partition_name(int /* slot */) {
+std::string fs_mgr_get_super_partition_name(int slot) {
+    // Devices upgrading to dynamic partitions are allowed to specify a super
+    // partition name, assumed to be A/B (non-A/B retrofit is not supported).
+    // For devices launching with dynamic partition support, the partition
+    // name must be "super".
+    std::string super_partition;
+    if (fs_mgr_get_boot_config_from_kernel_cmdline("super_partition", &super_partition)) {
+        std::string suffix;
+        if (slot == 0) {
+            suffix = "_a";
+        } else if (slot == 1) {
+            suffix = "_b";
+        } 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_dm_linear.cpp b/fs_mgr/fs_mgr_dm_linear.cpp
index 4dacebf..fe0e039 100644
--- a/fs_mgr/fs_mgr_dm_linear.cpp
+++ b/fs_mgr/fs_mgr_dm_linear.cpp
@@ -137,13 +137,22 @@
         LOG(ERROR) << "Could not read partition table.";
         return true;
     }
-    for (const auto& partition : metadata->partitions) {
+    return CreateLogicalPartitions(*metadata.get());
+}
+
+std::unique_ptr<LpMetadata> ReadCurrentMetadata(const std::string& block_device) {
+    uint32_t slot = SlotNumberForSlotSuffix(fs_mgr_get_slot_suffix());
+    return ReadMetadata(block_device.c_str(), slot);
+}
+
+bool CreateLogicalPartitions(const LpMetadata& metadata) {
+    for (const auto& partition : metadata.partitions) {
         if (!partition.num_extents) {
             LINFO << "Skipping zero-length logical partition: " << GetPartitionName(partition);
             continue;
         }
         std::string path;
-        if (!CreateLogicalPartition(*metadata.get(), partition, false, {}, &path)) {
+        if (!CreateLogicalPartition(metadata, partition, false, {}, &path)) {
             LERROR << "Could not create logical partition: " << GetPartitionName(partition);
             return false;
         }
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index fc3a05c..e89c91c 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -403,10 +403,13 @@
 static bool is_dt_fstab_compatible() {
     std::string dt_value;
     std::string file_name = get_android_dt_dir() + "/fstab/compatible";
-    if (read_dt_file(file_name, &dt_value)) {
-        if (dt_value == "android,fstab") {
-            return true;
-        }
+
+    if (read_dt_file(file_name, &dt_value) && dt_value == "android,fstab") {
+        // If there's no status property or its set to "ok" or "okay", then we use the DT fstab.
+        std::string status_value;
+        std::string status_file_name = get_android_dt_dir() + "/fstab/status";
+        return !read_dt_file(status_file_name, &status_value) || status_value == "ok" ||
+               status_value == "okay";
     }
 
     return false;
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index f06b819..a1de005 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -20,12 +20,14 @@
 #include <linux/fs.h>
 #include <selinux/selinux.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <sys/mount.h>
 #include <sys/param.h>
 #include <sys/stat.h>
 #include <sys/statvfs.h>
 #include <sys/types.h>
+#include <sys/utsname.h>
 #include <sys/vfs.h>
 #include <unistd.h>
 
@@ -55,13 +57,17 @@
 using namespace android::dm;
 using namespace android::fs_mgr;
 
-static bool fs_mgr_access(const std::string& path) {
+namespace {
+
+bool fs_mgr_access(const std::string& path) {
     auto save_errno = errno;
     auto ret = access(path.c_str(), F_OK) == 0;
     errno = save_errno;
     return ret;
 }
 
+}  // namespace
+
 #if ALLOW_ADBD_DISABLE_VERITY == 0  // If we are a user build, provide stubs
 
 bool fs_mgr_overlayfs_mount_all(fstab*) {
@@ -217,9 +223,12 @@
 std::string fs_mgr_get_overlayfs_options(const std::string& mount_point) {
     auto candidate = fs_mgr_get_overlayfs_candidate(mount_point);
     if (candidate.empty()) return "";
-
-    return "override_creds=off," + kLowerdirOption + mount_point + "," + kUpperdirOption +
-           candidate + kUpperName + ",workdir=" + candidate + kWorkName;
+    auto ret = kLowerdirOption + mount_point + "," + kUpperdirOption + candidate + kUpperName +
+               ",workdir=" + candidate + kWorkName;
+    if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kOverrideCredsRequired) {
+        ret += ",override_creds=off";
+    }
+    return ret;
 }
 
 const char* fs_mgr_mount_point(const char* mount_point) {
@@ -692,13 +701,21 @@
         errno = 0;
     }
 
-    auto ret = system((mnt_type == "f2fs")
-                              ? ((kMkF2fs + " -d1 " + scratch_device).c_str())
-                              : ((kMkExt4 + " -b 4096 -t ext4 -m 0 -M " + kScratchMountPoint +
-                                  " -O has_journal " + scratch_device)
-                                         .c_str()));
+    // Force mkfs by design for overlay support of adb remount, simplify and
+    // thus do not rely on fsck to correct problems that could creep in.
+    auto command = ""s;
+    if (mnt_type == "f2fs") {
+        command = kMkF2fs + " -w 4096 -f -d1 -l" + android::base::Basename(kScratchMountPoint);
+    } else if (mnt_type == "ext4") {
+        command = kMkExt4 + " -b 4096 -t ext4 -m 0 -O has_journal -M " + kScratchMountPoint;
+    } else {
+        LERROR << mnt_type << " has no mkfs cookbook";
+        return false;
+    }
+    command += " " + scratch_device;
+    auto ret = system(command.c_str());
     if (ret) {
-        LERROR << "make " << mnt_type << " filesystem on " << scratch_device << " error=" << ret;
+        LERROR << "make " << mnt_type << " filesystem on " << scratch_device << " return=" << ret;
         return false;
     }
 
@@ -724,7 +741,7 @@
 bool fs_mgr_overlayfs_mount_all(fstab* fstab) {
     auto ret = false;
 
-    if (!fs_mgr_overlayfs_supports_override_creds()) return ret;
+    if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) return ret;
 
     if (!fstab) return ret;
 
@@ -782,7 +799,7 @@
 bool fs_mgr_overlayfs_setup(const char* backing, const char* mount_point, bool* change) {
     if (change) *change = false;
     auto ret = false;
-    if (!fs_mgr_overlayfs_supports_override_creds()) return ret;
+    if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) return ret;
     if (!fs_mgr_boot_completed()) {
         errno = EBUSY;
         PERROR << "setup";
@@ -845,7 +862,7 @@
     for (const auto& overlay_mount_point : kOverlayMountPoints) {
         ret &= fs_mgr_overlayfs_teardown_one(overlay_mount_point, mount_point ?: "", change);
     }
-    if (!fs_mgr_overlayfs_supports_override_creds()) {
+    if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) {
         // After obligatory teardown to make sure everything is clean, but if
         // we didn't want overlayfs in the the first place, we do not want to
         // waste time on a reboot (or reboot request message).
@@ -896,7 +913,30 @@
     return context;
 }
 
-bool fs_mgr_overlayfs_supports_override_creds() {
+OverlayfsValidResult fs_mgr_overlayfs_valid() {
     // Overlayfs available in the kernel, and patched for override_creds?
-    return fs_mgr_access("/sys/module/overlay/parameters/override_creds");
+    if (fs_mgr_access("/sys/module/overlay/parameters/override_creds")) {
+        return OverlayfsValidResult::kOverrideCredsRequired;
+    }
+    if (!fs_mgr_access("/sys/module/overlay")) {
+        return OverlayfsValidResult::kNotSupported;
+    }
+    struct utsname uts;
+    if (uname(&uts) == -1) {
+        return OverlayfsValidResult::kNotSupported;
+    }
+    int major, minor;
+    if (sscanf(uts.release, "%d.%d", &major, &minor) != 2) {
+        return OverlayfsValidResult::kNotSupported;
+    }
+    if (major < 4) {
+        return OverlayfsValidResult::kOk;
+    }
+    if (major > 4) {
+        return OverlayfsValidResult::kNotSupported;
+    }
+    if (minor > 6) {
+        return OverlayfsValidResult::kNotSupported;
+    }
+    return OverlayfsValidResult::kOk;
 }
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 5e83cfb..23a92d3 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -39,11 +39,13 @@
 #define LINFO    LOG(INFO) << FS_MGR_TAG
 #define LWARNING LOG(WARNING) << FS_MGR_TAG
 #define LERROR   LOG(ERROR) << FS_MGR_TAG
+#define LFATAL LOG(FATAL) << FS_MGR_TAG
 
 // Logs a message with strerror(errno) at the end
 #define PINFO    PLOG(INFO) << FS_MGR_TAG
 #define PWARNING PLOG(WARNING) << FS_MGR_TAG
 #define PERROR   PLOG(ERROR) << FS_MGR_TAG
+#define PFATAL PLOG(FATAL) << FS_MGR_TAG
 
 #define CRYPTO_TMPFS_OPTIONS "size=512m,mode=0771,uid=1000,gid=1000"
 
diff --git a/fs_mgr/fs_mgr_vendor_overlay.cpp b/fs_mgr/fs_mgr_vendor_overlay.cpp
index 360a117..e1815ff 100644
--- a/fs_mgr/fs_mgr_vendor_overlay.cpp
+++ b/fs_mgr/fs_mgr_vendor_overlay.cpp
@@ -74,16 +74,23 @@
     const auto vendor_mount_point = kVendorTopDir + mount_point;
     LINFO << "vendor overlay mount on " << vendor_mount_point;
 
-    auto context = fs_mgr_get_context(vendor_mount_point);
-    if (!context.empty()) {
-        context = ",rootcontext="s + context;
-    } else {
-        PERROR << " result: cannot find the mount point";
+    const auto target_context = fs_mgr_get_context(vendor_mount_point);
+    if (target_context.empty()) {
+        PERROR << " failed: cannot find the target vendor mount point";
+        return false;
+    }
+    const auto source_directory = overlay_top + "/" + mount_point;
+    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
+               << ", target:" << target_context << ")";
         return false;
     }
 
-    auto options = "override_creds=off,"s + kLowerdirOption + overlay_top + "/" + mount_point +
-                   ":" + vendor_mount_point + context;
+    auto options = kLowerdirOption + source_directory + ":" + vendor_mount_point;
+    if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kOverrideCredsRequired) {
+        options += ",override_creds=off";
+    }
     auto report = "__mount(source=overlay,target="s + vendor_mount_point + ",type=overlay," +
                   options + ")=";
     auto ret = mount("overlay", vendor_mount_point.c_str(), "overlay", MS_RDONLY | MS_RELATIME,
@@ -112,7 +119,7 @@
     }
     const auto vendor_overlay_dirs = fs_mgr_get_vendor_overlay_dirs(overlay_top);
     if (vendor_overlay_dirs.empty()) return true;
-    if (!fs_mgr_overlayfs_supports_override_creds()) {
+    if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) {
         LINFO << "vendor overlay: kernel does not support overlayfs";
         return false;
     }
diff --git a/fs_mgr/include/fs_mgr_dm_linear.h b/fs_mgr/include/fs_mgr_dm_linear.h
index 08f4554..66abfca 100644
--- a/fs_mgr/include/fs_mgr_dm_linear.h
+++ b/fs_mgr/include/fs_mgr_dm_linear.h
@@ -33,11 +33,20 @@
 #include <vector>
 
 #include <libdm/dm.h>
-#include <liblp/metadata_format.h>
+#include <liblp/liblp.h>
 
 namespace android {
 namespace fs_mgr {
 
+// Read metadata from the current slot.
+std::unique_ptr<LpMetadata> ReadCurrentMetadata(const std::string& block_device);
+
+// Create block devices for all logical partitions in the given metadata. The
+// metadata must have been read from the current slot.
+bool CreateLogicalPartitions(const LpMetadata& metadata);
+
+// Create block devices for all logical partitions. This is a convenience
+// method for ReadMetadata and CreateLogicalPartitions.
 bool CreateLogicalPartitions(const std::string& block_device);
 
 // Create a block device for a single logical partition, given metadata and
diff --git a/fs_mgr/include/fs_mgr_overlayfs.h b/fs_mgr/include/fs_mgr_overlayfs.h
index 72202ab..deaf4cb 100644
--- a/fs_mgr/include/fs_mgr_overlayfs.h
+++ b/fs_mgr/include/fs_mgr_overlayfs.h
@@ -31,4 +31,10 @@
 bool fs_mgr_overlayfs_teardown(const char* mount_point = nullptr, bool* change = nullptr);
 bool fs_mgr_has_shared_blocks(const std::string& mount_point, const std::string& dev);
 std::string fs_mgr_get_context(const std::string& mount_point);
-bool fs_mgr_overlayfs_supports_override_creds();
+
+enum class OverlayfsValidResult {
+    kNotSupported = 0,
+    kOk,
+    kOverrideCredsRequired,
+};
+OverlayfsValidResult fs_mgr_overlayfs_valid();
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 3cd33b1..4007ad9 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -184,22 +184,26 @@
         if (!builder) {
             return false;
         }
-
-        for (size_t i = 0; i < partition.num_extents; i++) {
-            const LpMetadataExtent& extent = metadata.extents[partition.first_extent_index + i];
-            if (extent.target_type == LP_TARGET_TYPE_LINEAR) {
-                auto copy = std::make_unique<LinearExtent>(extent.num_sectors, extent.target_source,
-                                                           extent.target_data);
-                builder->AddExtent(std::move(copy));
-            } else if (extent.target_type == LP_TARGET_TYPE_ZERO) {
-                auto copy = std::make_unique<ZeroExtent>(extent.num_sectors);
-                builder->AddExtent(std::move(copy));
-            }
-        }
+        ImportExtents(builder, metadata, partition);
     }
     return true;
 }
 
+void MetadataBuilder::ImportExtents(Partition* dest, const LpMetadata& metadata,
+                                    const LpMetadataPartition& source) {
+    for (size_t i = 0; i < source.num_extents; i++) {
+        const LpMetadataExtent& extent = metadata.extents[source.first_extent_index + i];
+        if (extent.target_type == LP_TARGET_TYPE_LINEAR) {
+            auto copy = std::make_unique<LinearExtent>(extent.num_sectors, extent.target_source,
+                                                       extent.target_data);
+            dest->AddExtent(std::move(copy));
+        } else if (extent.target_type == LP_TARGET_TYPE_ZERO) {
+            auto copy = std::make_unique<ZeroExtent>(extent.num_sectors);
+            dest->AddExtent(std::move(copy));
+        }
+    }
+}
+
 static bool VerifyDeviceProperties(const BlockDeviceInfo& device_info) {
     if (device_info.logical_block_size % LP_SECTOR_SIZE != 0) {
         LERROR << "Block device " << device_info.partition_name
@@ -471,13 +475,18 @@
     return free_regions;
 }
 
-bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t aligned_size) {
+bool MetadataBuilder::ValidatePartitionSizeChange(Partition* partition, uint64_t old_size,
+                                                  uint64_t new_size) {
     PartitionGroup* group = FindGroup(partition->group_name());
     CHECK(group);
 
+    if (new_size <= old_size) {
+        return true;
+    }
+
     // Figure out how much we need to allocate, and whether our group has
     // enough space remaining.
-    uint64_t space_needed = aligned_size - partition->size();
+    uint64_t space_needed = new_size - old_size;
     if (group->maximum_size() > 0) {
         uint64_t group_size = TotalSizeOfGroup(group);
         if (group_size >= group->maximum_size() ||
@@ -488,7 +497,11 @@
             return false;
         }
     }
+    return true;
+}
 
+bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t aligned_size) {
+    uint64_t space_needed = aligned_size - partition->size();
     uint64_t sectors_needed = space_needed / LP_SECTOR_SIZE;
     DCHECK(sectors_needed * LP_SECTOR_SIZE == space_needed);
 
@@ -704,6 +717,10 @@
     uint64_t aligned_size = AlignTo(requested_size, geometry_.logical_block_size);
     uint64_t old_size = partition->size();
 
+    if (!ValidatePartitionSizeChange(partition, old_size, aligned_size)) {
+        return false;
+    }
+
     if (aligned_size > old_size) {
         if (!GrowPartition(partition, aligned_size)) {
             return false;
@@ -750,5 +767,74 @@
     }
 }
 
+static bool CompareBlockDevices(const LpMetadataBlockDevice& first,
+                                const LpMetadataBlockDevice& second) {
+    // Note: we don't compare alignment, since it's a performance thing and
+    // won't affect whether old extents continue to work.
+    return first.first_logical_sector == second.first_logical_sector && first.size == second.size &&
+           GetBlockDevicePartitionName(first) == GetBlockDevicePartitionName(second);
+}
+
+bool MetadataBuilder::ImportPartitions(const LpMetadata& metadata,
+                                       const std::set<std::string>& partition_names) {
+    // The block device list must be identical. We do not try to be clever and
+    // allow ordering changes or changes that don't affect partitions. This
+    // process is designed to allow the most common flashing scenarios and more
+    // complex ones should require a wipe.
+    if (metadata.block_devices.size() != block_devices_.size()) {
+        LINFO << "Block device tables does not match.";
+        return false;
+    }
+    for (size_t i = 0; i < metadata.block_devices.size(); i++) {
+        const LpMetadataBlockDevice& old_device = metadata.block_devices[i];
+        const LpMetadataBlockDevice& new_device = block_devices_[i];
+        if (!CompareBlockDevices(old_device, new_device)) {
+            LINFO << "Block device tables do not match";
+            return false;
+        }
+    }
+
+    // Import named partitions. Note that we do not attempt to merge group
+    // information here. If the device changed its group names, the old
+    // partitions will fail to merge. The same could happen if the group
+    // allocation sizes change.
+    for (const auto& partition : metadata.partitions) {
+        std::string partition_name = GetPartitionName(partition);
+        if (partition_names.find(partition_name) == partition_names.end()) {
+            continue;
+        }
+        if (!ImportPartition(metadata, partition)) {
+            return false;
+        }
+    }
+    return true;
+}
+
+bool MetadataBuilder::ImportPartition(const LpMetadata& metadata,
+                                      const LpMetadataPartition& source) {
+    std::string partition_name = GetPartitionName(source);
+    Partition* partition = FindPartition(partition_name);
+    if (!partition) {
+        std::string group_name = GetPartitionGroupName(metadata.groups[source.group_index]);
+        partition = AddPartition(partition_name, group_name, source.attributes);
+        if (!partition) {
+            return false;
+        }
+    }
+    if (partition->size() > 0) {
+        LINFO << "Importing partition table would overwrite non-empty partition: "
+              << partition_name;
+        return false;
+    }
+
+    ImportExtents(partition, metadata, source);
+
+    if (!ValidatePartitionSizeChange(partition, 0, partition->size())) {
+        partition->RemoveExtents();
+        return false;
+    }
+    return true;
+}
+
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index c27e300..35cab38 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -632,3 +632,64 @@
     EXPECT_EQ(metadata->extents[2].target_data, 1472);
     EXPECT_EQ(metadata->extents[2].target_source, 2);
 }
+
+TEST(liblp, ImportPartitionsOk) {
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+    ASSERT_NE(builder, nullptr);
+
+    Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
+    Partition* vendor = builder->AddPartition("vendor", LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(system, nullptr);
+    ASSERT_NE(vendor, nullptr);
+    EXPECT_EQ(builder->ResizePartition(system, 65536), true);
+    EXPECT_EQ(builder->ResizePartition(vendor, 32768), true);
+    EXPECT_EQ(builder->ResizePartition(system, 98304), true);
+
+    unique_ptr<LpMetadata> exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+
+    builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+    ASSERT_NE(builder, nullptr);
+
+    ASSERT_TRUE(builder->ImportPartitions(*exported.get(), {"vendor"}));
+    EXPECT_NE(builder->FindPartition("vendor"), nullptr);
+    EXPECT_EQ(builder->FindPartition("system"), nullptr);
+
+    unique_ptr<LpMetadata> new_metadata = builder->Export();
+    ASSERT_NE(new_metadata, nullptr);
+
+    ASSERT_EQ(exported->partitions.size(), static_cast<size_t>(2));
+    ASSERT_EQ(GetPartitionName(exported->partitions[1]), "vendor");
+    ASSERT_EQ(new_metadata->partitions.size(), static_cast<size_t>(1));
+    ASSERT_EQ(GetPartitionName(new_metadata->partitions[0]), "vendor");
+
+    const LpMetadataExtent& extent_a =
+            exported->extents[exported->partitions[1].first_extent_index];
+    const LpMetadataExtent& extent_b =
+            new_metadata->extents[new_metadata->partitions[0].first_extent_index];
+    EXPECT_EQ(extent_a.num_sectors, extent_b.num_sectors);
+    EXPECT_EQ(extent_a.target_type, extent_b.target_type);
+    EXPECT_EQ(extent_a.target_data, extent_b.target_data);
+    EXPECT_EQ(extent_a.target_source, extent_b.target_source);
+}
+
+TEST(liblp, ImportPartitionsFail) {
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+    ASSERT_NE(builder, nullptr);
+
+    Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
+    Partition* vendor = builder->AddPartition("vendor", LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(system, nullptr);
+    ASSERT_NE(vendor, nullptr);
+    EXPECT_EQ(builder->ResizePartition(system, 65536), true);
+    EXPECT_EQ(builder->ResizePartition(vendor, 32768), true);
+    EXPECT_EQ(builder->ResizePartition(system, 98304), true);
+
+    unique_ptr<LpMetadata> exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+
+    // Different device size.
+    builder = MetadataBuilder::New(1024 * 2048, 1024, 2);
+    ASSERT_NE(builder, nullptr);
+    EXPECT_FALSE(builder->ImportPartitions(*exported.get(), {"system"}));
+}
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index f9de106..d5e3fed 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -22,6 +22,7 @@
 
 #include <map>
 #include <memory>
+#include <set>
 
 #include "liblp.h"
 #include "partition_opener.h"
@@ -225,6 +226,11 @@
     bool GetBlockDeviceInfo(const std::string& partition_name, BlockDeviceInfo* info) const;
     bool UpdateBlockDeviceInfo(const std::string& partition_name, const BlockDeviceInfo& info);
 
+    // Attempt to preserve the named partitions from an older metadata. If this
+    // is not possible (for example, the block device list has changed) then
+    // false is returned.
+    bool ImportPartitions(const LpMetadata& metadata, const std::set<std::string>& partition_names);
+
   private:
     MetadataBuilder();
     MetadataBuilder(const MetadataBuilder&) = delete;
@@ -240,6 +246,10 @@
     uint64_t TotalSizeOfGroup(PartitionGroup* group) const;
     bool UpdateBlockDeviceInfo(size_t index, const BlockDeviceInfo& info);
     bool FindBlockDeviceByName(const std::string& partition_name, uint32_t* index) const;
+    bool ValidatePartitionSizeChange(Partition* partition, uint64_t old_size, uint64_t new_size);
+    void ImportExtents(Partition* dest, const LpMetadata& metadata,
+                       const LpMetadataPartition& source);
+    bool ImportPartition(const LpMetadata& metadata, const LpMetadataPartition& source);
 
     struct Interval {
         uint32_t device_index;
diff --git a/fs_mgr/liblp/include/liblp/liblp.h b/fs_mgr/liblp/include/liblp/liblp.h
index 4669cea..8723a7f 100644
--- a/fs_mgr/liblp/include/liblp/liblp.h
+++ b/fs_mgr/liblp/include/liblp/liblp.h
@@ -90,8 +90,12 @@
 // Return the total size of all partitions comprising the super partition.
 uint64_t GetTotalSuperPartitionSize(const LpMetadata& metadata);
 
-// Helper to return a slot number for a slot suffix.
+// Get the list of block device names required by the given metadata.
+std::vector<std::string> GetBlockDevicePartitionNames(const LpMetadata& metadata);
+
+// Slot suffix helpers.
 uint32_t SlotNumberForSlotSuffix(const std::string& suffix);
+std::string GetPartitionSlotSuffix(const std::string& partition_name);
 
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp
index 742ad82..cce90a3 100644
--- a/fs_mgr/liblp/utility.cpp
+++ b/fs_mgr/liblp/utility.cpp
@@ -97,15 +97,15 @@
 }
 
 uint32_t SlotNumberForSlotSuffix(const std::string& suffix) {
-    if (suffix.empty()) {
+    if (suffix.empty() || suffix == "a" || suffix == "_a") {
         return 0;
-    }
-    if (suffix.size() != 2 || suffix[0] != '_' || suffix[1] < 'a') {
+    } else if (suffix == "b" || suffix == "_b") {
+        return 1;
+    } else {
         LERROR << __PRETTY_FUNCTION__ << "slot '" << suffix
                << "' does not have a recognized format.";
         return 0;
     }
-    return suffix[1] - 'a';
 }
 
 uint64_t GetTotalSuperPartitionSize(const LpMetadata& metadata) {
@@ -116,5 +116,21 @@
     return size;
 }
 
+std::vector<std::string> GetBlockDevicePartitionNames(const LpMetadata& metadata) {
+    std::vector<std::string> list;
+    for (const auto& block_device : metadata.block_devices) {
+        list.emplace_back(GetBlockDevicePartitionName(block_device));
+    }
+    return list;
+}
+
+std::string GetPartitionSlotSuffix(const std::string& partition_name) {
+    if (partition_name.size() <= 2) {
+        return "";
+    }
+    std::string suffix = partition_name.substr(partition_name.size() - 2);
+    return (suffix == "_a" || suffix == "_b") ? suffix : "";
+}
+
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/liblp/utility_test.cpp b/fs_mgr/liblp/utility_test.cpp
index bdf6dfd..0fa4590 100644
--- a/fs_mgr/liblp/utility_test.cpp
+++ b/fs_mgr/liblp/utility_test.cpp
@@ -24,10 +24,12 @@
 
 TEST(liblp, SlotNumberForSlotSuffix) {
     EXPECT_EQ(SlotNumberForSlotSuffix(""), 0);
+    EXPECT_EQ(SlotNumberForSlotSuffix("a"), 0);
     EXPECT_EQ(SlotNumberForSlotSuffix("_a"), 0);
+    EXPECT_EQ(SlotNumberForSlotSuffix("b"), 1);
     EXPECT_EQ(SlotNumberForSlotSuffix("_b"), 1);
-    EXPECT_EQ(SlotNumberForSlotSuffix("_c"), 2);
-    EXPECT_EQ(SlotNumberForSlotSuffix("_d"), 3);
+    EXPECT_EQ(SlotNumberForSlotSuffix("_c"), 0);
+    EXPECT_EQ(SlotNumberForSlotSuffix("_d"), 0);
 }
 
 TEST(liblp, GetMetadataOffset) {
@@ -60,3 +62,11 @@
     EXPECT_EQ(AlignTo(32, 32, 30), 62);
     EXPECT_EQ(AlignTo(17, 32, 30), 30);
 }
+
+TEST(liblp, GetPartitionSlotSuffix) {
+    EXPECT_EQ(GetPartitionSlotSuffix("system"), "");
+    EXPECT_EQ(GetPartitionSlotSuffix("_"), "");
+    EXPECT_EQ(GetPartitionSlotSuffix("_a"), "");
+    EXPECT_EQ(GetPartitionSlotSuffix("system_a"), "_a");
+    EXPECT_EQ(GetPartitionSlotSuffix("system_b"), "_b");
+}
diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp
index c740bd4..f4c9b99 100644
--- a/fs_mgr/liblp/writer.cpp
+++ b/fs_mgr/liblp/writer.cpp
@@ -82,12 +82,8 @@
 
 // Perform sanity checks so we don't accidentally overwrite valid metadata
 // with potentially invalid metadata, or random partition data with metadata.
-static bool ValidateAndSerializeMetadata(int fd, const LpMetadata& metadata, std::string* blob) {
-    uint64_t blockdevice_size;
-    if (!GetDescriptorSize(fd, &blockdevice_size)) {
-        return false;
-    }
-
+static bool ValidateAndSerializeMetadata(const IPartitionOpener& opener, const LpMetadata& metadata,
+                                         std::string* blob) {
     const LpMetadataHeader& header = metadata.header;
     const LpMetadataGeometry& geometry = metadata.geometry;
 
@@ -104,7 +100,7 @@
     // metadata.
     uint64_t reserved_size = LP_METADATA_GEOMETRY_SIZE +
                              uint64_t(geometry.metadata_max_size) * geometry.metadata_slot_count;
-    uint64_t total_reserved = reserved_size * 2;
+    uint64_t total_reserved = LP_PARTITION_RESERVED_BYTES + reserved_size * 2;
 
     const LpMetadataBlockDevice* super_device = GetMetadataSuperBlockDevice(metadata);
     if (!super_device) {
@@ -112,15 +108,27 @@
         return false;
     }
 
-    if (total_reserved > blockdevice_size ||
-        total_reserved > super_device->first_logical_sector * LP_SECTOR_SIZE) {
+    if (total_reserved > super_device->first_logical_sector * LP_SECTOR_SIZE) {
         LERROR << "Not enough space to store all logical partition metadata slots.";
         return false;
     }
-    if (blockdevice_size != super_device->size) {
-        LERROR << "Block device size " << blockdevice_size
-               << " does not match metadata requested size " << super_device->size;
-        return false;
+    for (const auto& block_device : metadata.block_devices) {
+        std::string partition_name = GetBlockDevicePartitionName(block_device);
+        if ((block_device.first_logical_sector + 1) * LP_SECTOR_SIZE > block_device.size) {
+            LERROR << "Block device " << partition_name << " has invalid first sector "
+                   << block_device.first_logical_sector << " for size " << block_device.size;
+            return false;
+        }
+        BlockDeviceInfo info;
+        if (!opener.GetInfo(partition_name, &info)) {
+            PERROR << partition_name << ": ioctl";
+            return false;
+        }
+        if (info.size != block_device.size) {
+            LERROR << "Block device " << partition_name << " size mismatch (expected"
+                   << block_device.size << ", got " << info.size << ")";
+            return false;
+        }
     }
 
     // Make sure all partition entries reference valid extents.
@@ -230,7 +238,7 @@
     // basic checks that the geometry and tables are coherent, and will fit
     // on the given block device.
     std::string metadata_blob;
-    if (!ValidateAndSerializeMetadata(fd, metadata, &metadata_blob)) {
+    if (!ValidateAndSerializeMetadata(opener, metadata, &metadata_blob)) {
         return false;
     }
 
@@ -295,7 +303,7 @@
     // basic checks that the geometry and tables are coherent, and will fit
     // on the given block device.
     std::string blob;
-    if (!ValidateAndSerializeMetadata(fd, metadata, &blob)) {
+    if (!ValidateAndSerializeMetadata(opener, metadata, &blob)) {
         return false;
     }
 
@@ -327,7 +335,7 @@
         // synchronize the backup copy. This guarantees that a partial write
         // still leaves one copy intact.
         std::string old_blob;
-        if (!ValidateAndSerializeMetadata(fd, *primary.get(), &old_blob)) {
+        if (!ValidateAndSerializeMetadata(opener, *primary.get(), &old_blob)) {
             LERROR << "Error serializing primary metadata to repair corrupted backup";
             return false;
         }
@@ -339,7 +347,7 @@
         // The backup copy is coherent, and the primary is not. Sync it for
         // safety.
         std::string old_blob;
-        if (!ValidateAndSerializeMetadata(fd, *backup.get(), &old_blob)) {
+        if (!ValidateAndSerializeMetadata(opener, *backup.get(), &old_blob)) {
             LERROR << "Error serializing primary metadata to repair corrupted backup";
             return false;
         }
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index b6a8eef..98c0879 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -198,44 +198,84 @@
 
 # Do something
 adb_wait || die "wait for device failed"
-adb_sh ls -d /sys/module/overlay </dev/null || die "overlay module not present"
-adb_su ls /sys/module/overlay/parameters/override_creds </dev/null ||
+adb_sh ls -d /sys/module/overlay </dev/null >/dev/null &&
+  echo "${GREEN}[       OK ]${NORMAL} overlay module present" >&2 ||
+  die "overlay module not present"
+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 &&
-  D=`adb disable-verity 2>&1` ||
-    die "setup for overlay"
+  adb_wait ||
+  die "initial setup"
+reboot=false
+OVERLAYFS_BACKING="cache mnt/scratch"
+for d in ${OVERLAYFS_BACKING}; do
+  if adb_sh ls -d /${d}/overlay </dev/null >/dev/null 2>&1; then
+    echo "${ORANGE}[  WARNING ]${NORMAL} /${d}/overlay is setup, wiping" >&2
+    adb_sh rm -rf /${d}/overlay </dev/null ||
+      die "/${d}/overlay wipe"
+    reboot=true
+  fi
+done
+if ${reboot}; then
+  echo "${ORANGE}[  WARNING ]${NORMAL} rebooting before test" >&2
+  adb_reboot &&
+    adb_wait 2m &&
+    adb_root &&
+    adb_wait ||
+    die "reboot after wipe"
+fi
+D=`adb_sh df -k </dev/null` &&
+  H=`echo "${D}" | head -1` &&
+  D=`echo "${D}" | grep "^overlay "` &&
+  echo "${H}" &&
+  echo "${D}" &&
+  echo "${ORANGE}[  WARNING ]${NORMAL} overlays present before setup" >&2 ||
+  echo "${GREEN}[       OK ]${NORMAL} no overlay present before setup" >&2
+
+D=`adb disable-verity 2>&1` ||
+  die "setup for overlay ${D}"
 echo "${D}"
 if [ X"${D}" != X"${D##*using overlayfs}" ]; then
   echo "${GREEN}[       OK ]${NORMAL} using overlayfs" >&2
 fi
-if adb_sh ls -d /data/overlay </dev/null >/dev/null 2>&1; then
-  echo "/data/overlay setup, clearing out" >&2
-  adb_sh rm -rf /data/overlay </dev/null ||
-    die "/data/overlay removal"
-fi
-adb_sh ls -d /cache/overlay </dev/null >/dev/null 2>&1 ||
-  adb_sh ls -d /mnt/scratch/overlay </dev/null >/dev/null 2>&1 ||
-  die "overlay directory setup"
 adb_reboot &&
   adb_wait &&
-  adb_sh df -k </dev/null | head -1 &&
-  adb_sh df -k </dev/null | grep "^overlay " ||
+  D=`adb_sh df -k </dev/null` &&
+  H=`echo "${D}" | head -1` &&
+  D=`echo "${D}" | grep "^overlay "` &&
+  echo "${H}" &&
+  echo "${D}" ||
   die "overlay takeover failed"
-adb_sh df -k </dev/null | grep "^overlay .* /system\$" >/dev/null ||
+echo "${D}" | grep "^overlay .* /system\$" >/dev/null ||
   echo "${ORANGE}[  WARNING ]${NORMAL} overlay takeover before remount not complete" >&2
 
 adb_root &&
   adb_wait &&
   adb remount &&
-  adb_sh df -k </dev/null | head -1 &&
-  adb_sh df -k </dev/null | grep "^overlay " &&
-  adb_sh df -k </dev/null | grep "^overlay .* /system\$" >/dev/null ||
+  D=`adb_sh df -k </dev/null` ||
+  die "can not collect filesystem data"
+if echo "${D}" | grep " /mnt/scratch" >/dev/null; then
+  echo "${ORANGE}[     INFO ]${NORMAL} using scratch dynamic partition for overrides" >&2
+  H=`adb_sh cat /proc/mounts | sed -n 's@\([^ ]*\) /mnt/scratch \([^ ]*\) .*@\2 on \1@p'`
+  [ -n "${H}" ] &&
+    echo "${ORANGE}[     INFO ]${NORMAL} scratch filesystem ${H}"
+fi
+for d in ${OVERLAYFS_BACKING}; do
+  if adb_sh ls -d /${d}/overlay/system/upper </dev/null >/dev/null 2>&1; then
+    echo "${ORANGE}[     INFO ]${NORMAL} /${d}/overlay is setup" >&2
+  fi
+done
+
+H=`echo "${D}" | head -1` &&
+  D=`echo "${D}" | grep "^overlay "` &&
+  echo "${H}" &&
+  echo "${D}" &&
+  echo "${D}" | grep "^overlay .* /system\$" >/dev/null ||
   die  "overlay takeover after remount"
 !(adb_sh grep "^overlay " /proc/mounts </dev/null | grep " overlay ro,") &&
-  !(adb_sh grep " rw," /proc/mounts </dev/null |
-  skip_administrative_mounts) ||
-    die "remount overlayfs missed a spot"
+  !(adb_sh grep " rw," /proc/mounts </dev/null | skip_administrative_mounts) ||
+  die "remount overlayfs missed a spot (ro)"
 
 adb_su "sed -n '1,/overlay \\/system/p' /proc/mounts" </dev/null |
   skip_administrative_mounts |
@@ -275,11 +315,14 @@
 adb_wait &&
   adb_root &&
   adb_wait &&
-  adb_sh df -k </dev/null | head -1 &&
-  adb_sh df -k </dev/null | grep "^overlay " &&
-  adb_sh df -k </dev/null | grep "^overlay .* /system\$" >/dev/null ||
+  D=`adb_sh df -k </dev/null` &&
+  H=`echo "${D}" | head -1` &&
+  D=`echo "${D}" | grep "^overlay "` &&
+  echo "${H}" &&
+  echo "${D}" &&
+  echo "${D}" | grep "^overlay .* /system\$" >/dev/null ||
   die  "overlay system takeover after flash vendor"
-adb_sh df -k </dev/null | grep "^overlay .* /vendor\$" >/dev/null &&
+echo "${D}" | grep "^overlay .* /vendor\$" >/dev/null &&
   die  "overlay minus vendor takeover after flash vendor"
 B="`adb_cat /system/hello`" ||
   die "re-read system hello after flash vendor"
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index 2a5667c..a1519da 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -240,10 +240,9 @@
     if (readFromFile(mHealthdConfig->batteryTechnologyPath, &buf) > 0)
         props.batteryTechnology = String8(buf.c_str());
 
-    unsigned int i;
     double MaxPower = 0;
 
-    for (i = 0; i < mChargerNames.size(); i++) {
+    for (size_t i = 0; i < mChargerNames.size(); i++) {
         String8 path;
         path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH,
                           mChargerNames[i].string());
diff --git a/init/Android.mk b/init/Android.mk
index c85727c..dc46d21 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -90,6 +90,8 @@
     libcap \
 
 LOCAL_SANITIZE := signed-integer-overflow
+# First stage init is weird: it may start without stdout/stderr, and no /proc.
+LOCAL_NOSANITIZE := hwaddress
 include $(BUILD_EXECUTABLE)
 
 include $(CLEAR_VARS)
diff --git a/init/README.md b/init/README.md
index 6c51b37..2c531df 100644
--- a/init/README.md
+++ b/init/README.md
@@ -235,9 +235,16 @@
   to "123,124,125". Since keycodes are handled very early in init,
   only PRODUCT_DEFAULT_PROPERTY_OVERRIDES properties can be used.
 
-`memcg.limit_in_bytes <value>`
-> Sets the child's memory.limit_in_bytes to the specified value (only if memcg is mounted),
-  which must be equal or greater than 0.
+`memcg.limit_in_bytes <value>` and `memcg.limit_percent <value>`
+> Sets the child's memory.limit_in_bytes to the minimum of `limit_in_bytes`
+  bytes and `limit_percent` which is interpreted as a percentage of the size
+  of the device's physical memory (only if memcg is mounted).
+  Values must be equal or greater than 0.
+
+`memcg.limit_property <value>`
+> Sets the child's memory.limit_in_bytes to the value of the specified property
+  (only if memcg is mounted). This property will override the values specified
+  via `memcg.limit_in_bytes` and `memcg.limit_percent`.
 
 `memcg.soft_limit_in_bytes <value>`
 > Sets the child's memory.soft_limit_in_bytes to the specified value (only if memcg is mounted),
diff --git a/init/devices.cpp b/init/devices.cpp
index 58c8b2e..45b17a2 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -419,7 +419,7 @@
 }
 
 void DeviceHandler::ColdbootDone() {
-    skip_restorecon_ = true;
+    skip_restorecon_ = false;
 }
 
 DeviceHandler::DeviceHandler(std::vector<Permissions> dev_permissions,
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index eb86eb0..13a9d08 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -33,7 +33,7 @@
 #include <fs_mgr_avb.h>
 #include <fs_mgr_dm_linear.h>
 #include <fs_mgr_overlayfs.h>
-#include <liblp/metadata_format.h>
+#include <liblp/liblp.h>
 
 #include "devices.h"
 #include "switch_root.h"
@@ -69,7 +69,8 @@
     bool MountPartition(fstab_rec* fstab_rec);
     bool MountPartitions();
     bool IsDmLinearEnabled();
-    bool GetBackingDmLinearDevices();
+    bool GetDmLinearMetadataDevice();
+    bool InitDmLinearBackingDevices(const android::fs_mgr::LpMetadata& metadata);
 
     virtual ListenerAction UeventCallback(const Uevent& uevent);
 
@@ -183,7 +184,7 @@
 }
 
 bool FirstStageMount::InitDevices() {
-    return GetBackingDmLinearDevices() && GetDmVerityDevices() && InitRequiredDevices();
+    return GetDmLinearMetadataDevice() && GetDmVerityDevices() && InitRequiredDevices();
 }
 
 bool FirstStageMount::IsDmLinearEnabled() {
@@ -193,7 +194,7 @@
     return false;
 }
 
-bool FirstStageMount::GetBackingDmLinearDevices() {
+bool FirstStageMount::GetDmLinearMetadataDevice() {
     // Add any additional devices required for dm-linear mappings.
     if (!IsDmLinearEnabled()) {
         return true;
@@ -258,17 +259,48 @@
     return true;
 }
 
+bool FirstStageMount::InitDmLinearBackingDevices(const android::fs_mgr::LpMetadata& metadata) {
+    auto partition_names = GetBlockDevicePartitionNames(metadata);
+    for (const auto& partition_name : partition_names) {
+        if (partition_name == lp_metadata_partition_) {
+            continue;
+        }
+        required_devices_partition_names_.emplace(partition_name);
+    }
+    if (required_devices_partition_names_.empty()) {
+        return true;
+    }
+
+    auto uevent_callback = [this](const Uevent& uevent) { return UeventCallback(uevent); };
+    uevent_listener_.RegenerateUevents(uevent_callback);
+
+    if (!required_devices_partition_names_.empty()) {
+        LOG(ERROR) << __PRETTY_FUNCTION__ << ": partition(s) not found after polling timeout: "
+                   << android::base::Join(required_devices_partition_names_, ", ");
+        return false;
+    }
+    return true;
+}
+
 bool FirstStageMount::CreateLogicalPartitions() {
     if (!IsDmLinearEnabled()) {
         return true;
     }
-
     if (lp_metadata_partition_.empty()) {
         LOG(ERROR) << "Could not locate logical partition tables in partition "
                    << super_partition_name_;
         return false;
     }
-    return android::fs_mgr::CreateLogicalPartitions(lp_metadata_partition_);
+
+    auto metadata = android::fs_mgr::ReadCurrentMetadata(lp_metadata_partition_);
+    if (!metadata) {
+        LOG(ERROR) << "Could not read logical partition metadata from " << lp_metadata_partition_;
+        return false;
+    }
+    if (!InitDmLinearBackingDevices(*metadata.get())) {
+        return false;
+    }
+    return android::fs_mgr::CreateLogicalPartitions(*metadata.get());
 }
 
 ListenerAction FirstStageMount::HandleBlockDevice(const std::string& name, const Uevent& uevent) {
diff --git a/init/grab-bootchart.sh b/init/grab-bootchart.sh
index c4ff6df..2c56698 100755
--- a/init/grab-bootchart.sh
+++ b/init/grab-bootchart.sh
@@ -17,6 +17,6 @@
     adb "${@}" pull $LOGROOT/$f $TMPDIR/$f 2>&1 > /dev/null
 done
 (cd $TMPDIR && tar -czf $TARBALL $FILES)
-bootchart ${TMPDIR}/${TARBALL}
-gnome-open ${TARBALL%.tgz}.png
+pybootchartgui ${TMPDIR}/${TARBALL}
+xdg-open ${TARBALL%.tgz}.png
 echo "Clean up ${TMPDIR}/ and ./${TARBALL%.tgz}.png when done"
diff --git a/init/service.cpp b/init/service.cpp
index 7f49423..1bda7ec 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -235,9 +235,6 @@
       ioprio_pri_(0),
       priority_(0),
       oom_score_adjust_(-1000),
-      swappiness_(-1),
-      soft_limit_in_bytes_(-1),
-      limit_in_bytes_(-1),
       start_order_(0),
       args_(args) {}
 
@@ -630,6 +627,18 @@
     return Success();
 }
 
+Result<Success> Service::ParseMemcgLimitPercent(std::vector<std::string>&& args) {
+    if (!ParseInt(args[1], &limit_percent_, 0)) {
+        return Error() << "limit_percent value must be equal or greater than 0";
+    }
+    return Success();
+}
+
+Result<Success> Service::ParseMemcgLimitProperty(std::vector<std::string>&& args) {
+    limit_property_ = std::move(args[1]);
+    return Success();
+}
+
 Result<Success> Service::ParseMemcgSoftLimitInBytes(std::vector<std::string>&& args) {
     if (!ParseInt(args[1], &soft_limit_in_bytes_, 0)) {
         return Error() << "soft_limit_in_bytes value must be equal or greater than 0";
@@ -783,6 +792,10 @@
         {"keycodes",    {1,     kMax, &Service::ParseKeycodes}},
         {"memcg.limit_in_bytes",
                         {1,     1,    &Service::ParseMemcgLimitInBytes}},
+        {"memcg.limit_percent",
+                        {1,     1,    &Service::ParseMemcgLimitPercent}},
+        {"memcg.limit_property",
+                        {1,     1,    &Service::ParseMemcgLimitProperty}},
         {"memcg.soft_limit_in_bytes",
                         {1,     1,    &Service::ParseMemcgSoftLimitInBytes}},
         {"memcg.swappiness",
@@ -1001,11 +1014,13 @@
     start_order_ = next_start_order_++;
     process_cgroup_empty_ = false;
 
-    errno = -createProcessGroup(uid_, pid_);
+    bool use_memcg = swappiness_ != -1 || soft_limit_in_bytes_ != -1 || limit_in_bytes_ != -1 ||
+                      limit_percent_ != -1 || !limit_property_.empty();
+    errno = -createProcessGroup(uid_, pid_, use_memcg);
     if (errno != 0) {
         PLOG(ERROR) << "createProcessGroup(" << uid_ << ", " << pid_ << ") failed for service '"
                     << name_ << "'";
-    } else {
+    } else if (use_memcg) {
         if (swappiness_ != -1) {
             if (!setProcessGroupSwappiness(uid_, pid_, swappiness_)) {
                 PLOG(ERROR) << "setProcessGroupSwappiness failed";
@@ -1018,8 +1033,29 @@
             }
         }
 
-        if (limit_in_bytes_ != -1) {
-            if (!setProcessGroupLimit(uid_, pid_, limit_in_bytes_)) {
+        size_t computed_limit_in_bytes = limit_in_bytes_;
+        if (limit_percent_ != -1) {
+            long page_size = sysconf(_SC_PAGESIZE);
+            long num_pages = sysconf(_SC_PHYS_PAGES);
+            if (page_size > 0 && num_pages > 0) {
+                size_t max_mem = SIZE_MAX;
+                if (size_t(num_pages) < SIZE_MAX / size_t(page_size)) {
+                    max_mem = size_t(num_pages) * size_t(page_size);
+                }
+                computed_limit_in_bytes =
+                        std::min(computed_limit_in_bytes, max_mem / 100 * limit_percent_);
+            }
+        }
+
+        if (!limit_property_.empty()) {
+            // This ends up overwriting computed_limit_in_bytes but only if the
+            // property is defined.
+            computed_limit_in_bytes = android::base::GetUintProperty(
+                    limit_property_, computed_limit_in_bytes, SIZE_MAX);
+        }
+
+        if (computed_limit_in_bytes != size_t(-1)) {
+            if (!setProcessGroupLimit(uid_, pid_, computed_limit_in_bytes)) {
                 PLOG(ERROR) << "setProcessGroupLimit failed";
             }
         }
diff --git a/init/service.h b/init/service.h
index c7beee9..49b09ce 100644
--- a/init/service.h
+++ b/init/service.h
@@ -154,6 +154,8 @@
     Result<Success> ParseOomScoreAdjust(std::vector<std::string>&& args);
     Result<Success> ParseOverride(std::vector<std::string>&& args);
     Result<Success> ParseMemcgLimitInBytes(std::vector<std::string>&& args);
+    Result<Success> ParseMemcgLimitPercent(std::vector<std::string>&& args);
+    Result<Success> ParseMemcgLimitProperty(std::vector<std::string>&& args);
     Result<Success> ParseMemcgSoftLimitInBytes(std::vector<std::string>&& args);
     Result<Success> ParseMemcgSwappiness(std::vector<std::string>&& args);
     Result<Success> ParseNamespace(std::vector<std::string>&& args);
@@ -213,9 +215,12 @@
 
     int oom_score_adjust_;
 
-    int swappiness_;
-    int soft_limit_in_bytes_;
-    int limit_in_bytes_;
+    int swappiness_ = -1;
+    int soft_limit_in_bytes_ = -1;
+
+    int limit_in_bytes_ = -1;
+    int limit_percent_ = -1;
+    std::string limit_property_;
 
     bool process_cgroup_empty_ = false;
 
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index 845c586..68bf898 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -132,6 +132,7 @@
 #define AID_LMKD 1069            /* low memory killer daemon */
 #define AID_LLKD 1070            /* live lock daemon */
 #define AID_IORAPD 1071          /* input/output readahead and pin daemon */
+#define AID_GPU_SERVICE 1072     /* GPU service daemon */
 /* Changes to this file must be made in AOSP, *not* in internal branches. */
 
 #define AID_SHELL 2000 /* adb and debug shell user */
diff --git a/liblog/include/android/log.h b/liblog/include/android/log.h
index ee9220d..b2f0ed9 100644
--- a/liblog/include/android/log.h
+++ b/liblog/include/android/log.h
@@ -14,25 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _ANDROID_LOG_H
-#define _ANDROID_LOG_H
-
-/******************************************************************
- *
- * IMPORTANT NOTICE:
- *
- *   This file is part of Android's set of stable system headers
- *   exposed by the Android NDK (Native Development Kit) since
- *   platform release 1.5
- *
- *   Third-party source AND binary code relies on the definitions
- *   here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES.
- *
- *   - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES)
- *   - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS
- *   - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY
- *   - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES
- */
+#pragma once
 
 /**
  * @addtogroup Logging
@@ -154,27 +136,51 @@
 
 #ifndef log_id_t_defined
 #define log_id_t_defined
+/**
+ * Identifies a specific log buffer for __android_log_buf_write()
+ * and __android_log_buf_print().
+ */
 typedef enum log_id {
   LOG_ID_MIN = 0,
 
+  /** The main log buffer. This is the only log buffer available to apps. */
   LOG_ID_MAIN = 0,
+  /** The radio log buffer. */
   LOG_ID_RADIO = 1,
+  /** The event log buffer. */
   LOG_ID_EVENTS = 2,
+  /** The system log buffer. */
   LOG_ID_SYSTEM = 3,
+  /** The crash log buffer. */
   LOG_ID_CRASH = 4,
+  /** The statistics log buffer. */
   LOG_ID_STATS = 5,
+  /** The security log buffer. */
   LOG_ID_SECURITY = 6,
-  LOG_ID_KERNEL = 7, /* place last, third-parties can not use it */
+  /** The kernel log buffer. */
+  LOG_ID_KERNEL = 7,
 
   LOG_ID_MAX
 } log_id_t;
 #endif
 
-/*
- * Send a simple string to the log.
+/**
+ * Writes the constant string `text` to the log buffer `id`,
+ * with priority `prio` and tag `tag`.
+ *
+ * Apps should use __android_log_write() instead.
  */
 int __android_log_buf_write(int bufID, int prio, const char* tag,
                             const char* text);
+
+/**
+ * Writes a formatted string to log buffer `id`,
+ * with priority `prio` and tag `tag`.
+ * The details of formatting are the same as for
+ * [printf(3)](http://man7.org/linux/man-pages/man3/printf.3.html).
+ *
+ * Apps should use __android_log_print() instead.
+ */
 int __android_log_buf_print(int bufID, int prio, const char* tag,
                             const char* fmt, ...)
 #if defined(__GNUC__)
@@ -187,5 +193,3 @@
 #endif
 
 /** @} */
-
-#endif /* _ANDROID_LOG_H */
diff --git a/libmeminfo/.clang-format b/libmeminfo/.clang-format
new file mode 120000
index 0000000..1af4f51
--- /dev/null
+++ b/libmeminfo/.clang-format
@@ -0,0 +1 @@
+../.clang-format-4
\ No newline at end of file
diff --git a/libmeminfo/Android.bp b/libmeminfo/Android.bp
new file mode 100644
index 0000000..aab3743
--- /dev/null
+++ b/libmeminfo/Android.bp
@@ -0,0 +1,70 @@
+//
+// 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.
+//
+
+cc_defaults {
+    name: "libmeminfo_defaults",
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "liblog",
+        "libprocinfo",
+    ],
+}
+
+cc_library {
+    name: "libmeminfo",
+    defaults: ["libmeminfo_defaults"],
+    export_include_dirs: ["include"],
+    export_shared_lib_headers: ["libbase"],
+    srcs: [
+        "pageacct.cpp",
+        "procmeminfo.cpp",
+        "sysmeminfo.cpp",
+    ],
+}
+
+cc_test {
+    name: "libmeminfo_test",
+    defaults: ["libmeminfo_defaults"],
+
+    static_libs: [
+        "libmeminfo",
+        "libpagemap",
+        "libbase",
+        "liblog",
+    ],
+
+    srcs: [
+        "libmeminfo_test.cpp"
+    ],
+}
+
+cc_benchmark {
+    name: "libmeminfo_benchmark",
+    srcs: [
+        "libmeminfo_benchmark.cpp",
+    ],
+    static_libs : [
+        "libbase",
+        "liblog",
+        "libmeminfo",
+        "libprocinfo",
+    ],
+}
diff --git a/libmeminfo/include/meminfo/meminfo.h b/libmeminfo/include/meminfo/meminfo.h
new file mode 100644
index 0000000..c328648
--- /dev/null
+++ b/libmeminfo/include/meminfo/meminfo.h
@@ -0,0 +1,75 @@
+/*
+ * 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 <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+namespace android {
+namespace meminfo {
+
+struct MemUsage {
+    uint64_t vss;
+    uint64_t rss;
+    uint64_t pss;
+    uint64_t uss;
+
+    uint64_t private_clean;
+    uint64_t private_dirty;
+    uint64_t shared_clean;
+    uint64_t shared_dirty;
+
+    MemUsage()
+        : vss(0),
+          rss(0),
+          pss(0),
+          uss(0),
+          private_clean(0),
+          private_dirty(0),
+          shared_clean(0),
+          shared_dirty(0) {}
+
+    ~MemUsage() = default;
+
+    void clear() {
+        vss = rss = pss = uss = 0;
+        private_clean = private_dirty = shared_clean = shared_dirty = 0;
+    }
+};
+
+struct Vma {
+    uint64_t start;
+    uint64_t end;
+    uint64_t offset;
+    uint16_t flags;
+    std::string name;
+
+    Vma(uint64_t s, uint64_t e, uint64_t off, uint16_t f, const char* n)
+        : start(s), end(e), offset(off), flags(f), name(n) {}
+    ~Vma() = default;
+
+    // Memory usage of this mapping.
+    MemUsage usage;
+    // Working set within this mapping.
+    MemUsage wss;
+};
+
+}  // namespace meminfo
+}  // namespace android
diff --git a/libmeminfo/include/meminfo/pageacct.h b/libmeminfo/include/meminfo/pageacct.h
new file mode 100644
index 0000000..8ddaef2
--- /dev/null
+++ b/libmeminfo/include/meminfo/pageacct.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace meminfo {
+
+class PageAcct final {
+    // Class for per-page accounting by using kernel provided interfaces like
+    // kpagecount, kpageflags etc.
+  public:
+    static bool KernelHasPageIdle() {
+        return (access("/sys/kernel/mm/page_idle/bitmap", R_OK | W_OK) == 0);
+    }
+
+    bool InitPageAcct(bool pageidle_enable = false);
+    bool PageFlags(uint64_t pfn, uint64_t* flags);
+    bool PageMapCount(uint64_t pfn, uint64_t* mapcount);
+
+    int IsPageIdle(uint64_t pfn);
+
+    // The only way to create PageAcct object
+    static PageAcct& Instance() {
+        static PageAcct instance;
+        return instance;
+    }
+
+    ~PageAcct() = default;
+
+  private:
+    PageAcct() : kpagecount_fd_(-1), kpageflags_fd_(-1), pageidle_fd_(-1) {}
+    int MarkPageIdle(uint64_t pfn) const;
+    int GetPageIdle(uint64_t pfn) const;
+
+    // Non-copyable & Non-movable
+    PageAcct(const PageAcct&) = delete;
+    PageAcct& operator=(const PageAcct&) = delete;
+    PageAcct& operator=(PageAcct&&) = delete;
+    PageAcct(PageAcct&&) = delete;
+
+    ::android::base::unique_fd kpagecount_fd_;
+    ::android::base::unique_fd kpageflags_fd_;
+    ::android::base::unique_fd pageidle_fd_;
+};
+
+}  // namespace meminfo
+}  // namespace android
diff --git a/libmeminfo/include/meminfo/procmeminfo.h b/libmeminfo/include/meminfo/procmeminfo.h
new file mode 100644
index 0000000..b37c56b
--- /dev/null
+++ b/libmeminfo/include/meminfo/procmeminfo.h
@@ -0,0 +1,55 @@
+/*
+ * 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 <sys/types.h>
+
+#include <string>
+#include <vector>
+
+#include "meminfo.h"
+
+namespace android {
+namespace meminfo {
+
+class ProcMemInfo final {
+    // Per-process memory accounting
+  public:
+    ProcMemInfo(pid_t pid, bool get_wss = false);
+
+    const std::vector<Vma>& Maps();
+    const MemUsage& Usage();
+    const MemUsage& Wss();
+
+    bool WssReset();
+    ~ProcMemInfo() = default;
+
+  private:
+    bool ReadMaps(bool get_wss);
+    bool ReadVmaStats(int pagemap_fd, Vma& vma, bool get_wss);
+
+    pid_t pid_;
+    bool get_wss_;
+
+    std::vector<Vma> maps_;
+
+    MemUsage usage_;
+    MemUsage wss_;
+};
+
+}  // namespace meminfo
+}  // namespace android
diff --git a/libmeminfo/include/meminfo/sysmeminfo.h b/libmeminfo/include/meminfo/sysmeminfo.h
new file mode 100644
index 0000000..f5e05bd
--- /dev/null
+++ b/libmeminfo/include/meminfo/sysmeminfo.h
@@ -0,0 +1,62 @@
+/*
+ * 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 <sys/types.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+namespace android {
+namespace meminfo {
+
+class SysMemInfo final {
+    // System or Global memory accounting
+  public:
+    static const std::vector<std::string> kDefaultSysMemInfoTags;
+
+    SysMemInfo() = default;
+
+    // Parse /proc/meminfo and read values that are needed
+    bool ReadMemInfo(const std::string& path = "/proc/meminfo");
+    bool ReadMemInfo(const std::vector<std::string>& tags,
+                     const std::string& path = "/proc/meminfo");
+
+    // getters
+    uint64_t mem_total_kb() { return mem_in_kb_["MemTotal:"]; }
+    uint64_t mem_free_kb() { return mem_in_kb_["MemFree:"]; }
+    uint64_t mem_buffers_kb() { return mem_in_kb_["Buffers:"]; }
+    uint64_t mem_cached_kb() { return mem_in_kb_["Cached:"]; }
+    uint64_t mem_shmem_kb() { return mem_in_kb_["Shmem:"]; }
+    uint64_t mem_slab_kb() { return mem_in_kb_["Slab:"]; }
+    uint64_t mem_slab_reclailmable_kb() { return mem_in_kb_["SReclaimable:"]; }
+    uint64_t mem_slab_unreclaimable_kb() { return mem_in_kb_["SUnreclaim:"]; }
+    uint64_t mem_swap_kb() { return mem_in_kb_["SwapTotal:"]; }
+    uint64_t mem_free_swap_kb() { return mem_in_kb_["SwapFree:"]; }
+    uint64_t mem_zram_kb() { return mem_in_kb_["Zram:"]; }
+    uint64_t mem_mapped_kb() { return mem_in_kb_["Mapped:"]; }
+    uint64_t mem_vmalloc_used_kb() { return mem_in_kb_["VmallocUsed:"]; }
+    uint64_t mem_page_tables_kb() { return mem_in_kb_["PageTables:"]; }
+    uint64_t mem_kernel_stack_kb() { return mem_in_kb_["KernelStack:"]; }
+
+  private:
+    std::map<std::string, uint64_t> mem_in_kb_;
+};
+
+}  // namespace meminfo
+}  // namespace android
diff --git a/libmeminfo/libmeminfo_benchmark.cpp b/libmeminfo/libmeminfo_benchmark.cpp
new file mode 100644
index 0000000..3820776b
--- /dev/null
+++ b/libmeminfo/libmeminfo_benchmark.cpp
@@ -0,0 +1,218 @@
+/*
+ * 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 <meminfo/sysmeminfo.h>
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/test_utils.h>
+
+#include <benchmark/benchmark.h>
+
+enum {
+    MEMINFO_TOTAL,
+    MEMINFO_FREE,
+    MEMINFO_BUFFERS,
+    MEMINFO_CACHED,
+    MEMINFO_SHMEM,
+    MEMINFO_SLAB,
+    MEMINFO_SLAB_RECLAIMABLE,
+    MEMINFO_SLAB_UNRECLAIMABLE,
+    MEMINFO_SWAP_TOTAL,
+    MEMINFO_SWAP_FREE,
+    MEMINFO_ZRAM_TOTAL,
+    MEMINFO_MAPPED,
+    MEMINFO_VMALLOC_USED,
+    MEMINFO_PAGE_TABLES,
+    MEMINFO_KERNEL_STACK,
+    MEMINFO_COUNT
+};
+
+void get_mem_info(uint64_t mem[], const char* file) {
+    char buffer[4096];
+    unsigned int numFound = 0;
+
+    int fd = open(file, O_RDONLY);
+
+    if (fd < 0) {
+        printf("Unable to open %s: %s\n", file, strerror(errno));
+        return;
+    }
+
+    const int len = read(fd, buffer, sizeof(buffer) - 1);
+    close(fd);
+
+    if (len < 0) {
+        printf("Empty %s\n", file);
+        return;
+    }
+    buffer[len] = 0;
+
+    static const char* const tags[] = {
+            "MemTotal:",     "MemFree:",    "Buffers:",     "Cached:",       "Shmem:", "Slab:",
+            "SReclaimable:", "SUnreclaim:", "SwapTotal:",   "SwapFree:",     "ZRam:",  "Mapped:",
+            "VmallocUsed:",  "PageTables:", "KernelStack:", NULL};
+
+    static const int tagsLen[] = {9, 8, 8, 7, 6, 5, 13, 11, 10, 9, 5, 7, 12, 11, 12, 0};
+
+    memset(mem, 0, sizeof(uint64_t) * 15);
+    char* p = buffer;
+    while (*p && (numFound < (sizeof(tagsLen) / sizeof(tagsLen[0])))) {
+        int i = 0;
+        while (tags[i]) {
+            //std::cout << "tag =" << tags[i] << " p = " << std::string(p, tagsLen[i]) << std::endl;
+            if (strncmp(p, tags[i], tagsLen[i]) == 0) {
+                p += tagsLen[i];
+                while (*p == ' ') p++;
+                char* num = p;
+                while (*p >= '0' && *p <= '9') p++;
+                if (*p != 0) {
+                    *p = 0;
+                    p++;
+                }
+                mem[i] = atoll(num);
+                numFound++;
+                break;
+            }
+            i++;
+        }
+        while (*p && *p != '\n') {
+            p++;
+        }
+        if (*p) p++;
+    }
+}
+
+static void BM_ParseSysMemInfo(benchmark::State& state) {
+    std::string meminfo = R"meminfo(MemTotal:        3019740 kB
+MemFree:         1809728 kB
+MemAvailable:    2546560 kB
+Buffers:           54736 kB
+Cached:           776052 kB
+SwapCached:            0 kB
+Active:           445856 kB
+Inactive:         459092 kB
+Active(anon):      78492 kB
+Inactive(anon):     2240 kB
+Active(file):     367364 kB
+Inactive(file):   456852 kB
+Unevictable:        3096 kB
+Mlocked:            3096 kB
+SwapTotal:             0 kB
+SwapFree:              0 kB
+Dirty:                32 kB
+Writeback:             0 kB
+AnonPages:         74988 kB
+Mapped:            62624 kB
+Shmem:              4020 kB
+Slab:              86464 kB
+SReclaimable:      44432 kB
+SUnreclaim:        42032 kB
+KernelStack:        4880 kB
+PageTables:         2900 kB
+NFS_Unstable:          0 kB
+Bounce:                0 kB
+WritebackTmp:          0 kB
+CommitLimit:     1509868 kB
+Committed_AS:      80296 kB
+VmallocTotal:   263061440 kB
+VmallocUsed:           0 kB
+VmallocChunk:          0 kB
+AnonHugePages:      6144 kB
+ShmemHugePages:        0 kB
+ShmemPmdMapped:        0 kB
+CmaTotal:         131072 kB
+CmaFree:          130380 kB
+HugePages_Total:       0
+HugePages_Free:        0
+HugePages_Rsvd:        0
+HugePages_Surp:        0
+Hugepagesize:       2048 kB)meminfo";
+
+    TemporaryFile tf;
+    ::android::base::WriteStringToFd(meminfo, tf.fd);
+
+    uint64_t mem[MEMINFO_COUNT];
+    for (auto _ : state) {
+        get_mem_info(mem, tf.path);
+    }
+}
+BENCHMARK(BM_ParseSysMemInfo);
+
+static void BM_ReadMemInfo(benchmark::State& state) {
+    std::string meminfo = R"meminfo(MemTotal:        3019740 kB
+MemFree:         1809728 kB
+MemAvailable:    2546560 kB
+Buffers:           54736 kB
+Cached:           776052 kB
+SwapCached:            0 kB
+Active:           445856 kB
+Inactive:         459092 kB
+Active(anon):      78492 kB
+Inactive(anon):     2240 kB
+Active(file):     367364 kB
+Inactive(file):   456852 kB
+Unevictable:        3096 kB
+Mlocked:            3096 kB
+SwapTotal:             0 kB
+SwapFree:              0 kB
+Dirty:                32 kB
+Writeback:             0 kB
+AnonPages:         74988 kB
+Mapped:            62624 kB
+Shmem:              4020 kB
+Slab:              86464 kB
+SReclaimable:      44432 kB
+SUnreclaim:        42032 kB
+KernelStack:        4880 kB
+PageTables:         2900 kB
+NFS_Unstable:          0 kB
+Bounce:                0 kB
+WritebackTmp:          0 kB
+CommitLimit:     1509868 kB
+Committed_AS:      80296 kB
+VmallocTotal:   263061440 kB
+VmallocUsed:           0 kB
+VmallocChunk:          0 kB
+AnonHugePages:      6144 kB
+ShmemHugePages:        0 kB
+ShmemPmdMapped:        0 kB
+CmaTotal:         131072 kB
+CmaFree:          130380 kB
+HugePages_Total:       0
+HugePages_Free:        0
+HugePages_Rsvd:        0
+HugePages_Surp:        0
+Hugepagesize:       2048 kB)meminfo";
+
+    TemporaryFile tf;
+    android::base::WriteStringToFd(meminfo, tf.fd);
+
+    std::string file = std::string(tf.path);
+    ::android::meminfo::SysMemInfo mi;
+    for (auto _ : state) {
+        mi.ReadMemInfo(file);
+    }
+}
+BENCHMARK(BM_ReadMemInfo);
+
+BENCHMARK_MAIN();
diff --git a/libmeminfo/libmeminfo_test.cpp b/libmeminfo/libmeminfo_test.cpp
new file mode 100644
index 0000000..22f3585
--- /dev/null
+++ b/libmeminfo/libmeminfo_test.cpp
@@ -0,0 +1,301 @@
+/*
+ * 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 <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <gtest/gtest.h>
+
+#include <string>
+#include <vector>
+
+#include <meminfo/pageacct.h>
+#include <meminfo/procmeminfo.h>
+#include <meminfo/sysmeminfo.h>
+#include <pagemap/pagemap.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/test_utils.h>
+
+using namespace std;
+using namespace android::meminfo;
+
+pid_t pid = -1;
+
+class ValidateProcMemInfo : public ::testing::Test {
+  protected:
+    void SetUp() override {
+        ASSERT_EQ(0, pm_kernel_create(&ker));
+        ASSERT_EQ(0, pm_process_create(ker, pid, &proc));
+        proc_mem = new ProcMemInfo(pid);
+        ASSERT_NE(proc_mem, nullptr);
+    }
+
+    void TearDown() override {
+        delete proc_mem;
+        pm_process_destroy(proc);
+        pm_kernel_destroy(ker);
+    }
+
+    pm_kernel_t* ker;
+    pm_process_t* proc;
+    ProcMemInfo* proc_mem;
+};
+
+TEST_F(ValidateProcMemInfo, TestMapsSize) {
+    const std::vector<Vma>& maps = proc_mem->Maps();
+    ASSERT_FALSE(maps.empty()) << "Process " << getpid() << " maps are empty";
+}
+
+TEST_F(ValidateProcMemInfo, TestMapsEquality) {
+    const std::vector<Vma>& maps = proc_mem->Maps();
+    ASSERT_EQ(proc->num_maps, maps.size());
+
+    for (size_t i = 0; i < maps.size(); ++i) {
+        EXPECT_EQ(proc->maps[i]->start, maps[i].start);
+        EXPECT_EQ(proc->maps[i]->end, maps[i].end);
+        EXPECT_EQ(proc->maps[i]->offset, maps[i].offset);
+        EXPECT_EQ(std::string(proc->maps[i]->name), maps[i].name);
+    }
+}
+
+TEST_F(ValidateProcMemInfo, TestMapsUsage) {
+    const std::vector<Vma>& maps = proc_mem->Maps();
+    ASSERT_FALSE(maps.empty());
+    ASSERT_EQ(proc->num_maps, maps.size());
+
+    pm_memusage_t map_usage, proc_usage;
+    pm_memusage_zero(&map_usage);
+    pm_memusage_zero(&proc_usage);
+    for (size_t i = 0; i < maps.size(); i++) {
+        ASSERT_EQ(0, pm_map_usage(proc->maps[i], &map_usage));
+        EXPECT_EQ(map_usage.vss, maps[i].usage.vss) << "VSS mismatch for map: " << maps[i].name;
+        EXPECT_EQ(map_usage.rss, maps[i].usage.rss) << "RSS mismatch for map: " << maps[i].name;
+        EXPECT_EQ(map_usage.pss, maps[i].usage.pss) << "PSS mismatch for map: " << maps[i].name;
+        EXPECT_EQ(map_usage.uss, maps[i].usage.uss) << "USS mismatch for map: " << maps[i].name;
+        pm_memusage_add(&proc_usage, &map_usage);
+    }
+
+    EXPECT_EQ(proc_usage.vss, proc_mem->Usage().vss);
+    EXPECT_EQ(proc_usage.rss, proc_mem->Usage().rss);
+    EXPECT_EQ(proc_usage.pss, proc_mem->Usage().pss);
+    EXPECT_EQ(proc_usage.uss, proc_mem->Usage().uss);
+}
+
+class ValidateProcMemInfoWss : public ::testing::Test {
+  protected:
+    void SetUp() override {
+        ASSERT_EQ(0, pm_kernel_create(&ker));
+        ASSERT_EQ(0, pm_process_create(ker, pid, &proc));
+        proc_mem = new ProcMemInfo(pid, true);
+        ASSERT_NE(proc_mem, nullptr);
+    }
+
+    void TearDown() override {
+        delete proc_mem;
+        pm_process_destroy(proc);
+        pm_kernel_destroy(ker);
+    }
+
+    pm_kernel_t* ker;
+    pm_process_t* proc;
+    ProcMemInfo* proc_mem;
+};
+
+TEST_F(ValidateProcMemInfoWss, TestWorkingTestReset) {
+    // Expect reset to succeed
+    EXPECT_TRUE(proc_mem->WssReset());
+}
+
+TEST_F(ValidateProcMemInfoWss, TestWssEquality) {
+    // Read wss using libpagemap
+    pm_memusage_t wss_pagemap;
+    EXPECT_EQ(0, pm_process_workingset(proc, &wss_pagemap, 0));
+
+    // Read wss using libmeminfo
+    MemUsage wss = proc_mem->Wss();
+
+    // compare
+    EXPECT_EQ(wss_pagemap.rss, wss.rss);
+    EXPECT_EQ(wss_pagemap.pss, wss.pss);
+    EXPECT_EQ(wss_pagemap.uss, wss.uss);
+}
+
+class ValidatePageAcct : public ::testing::Test {
+  protected:
+    void SetUp() override {
+        ASSERT_EQ(0, pm_kernel_create(&ker));
+        ASSERT_EQ(0, pm_process_create(ker, pid, &proc));
+    }
+
+    void TearDown() override {
+        pm_process_destroy(proc);
+        pm_kernel_destroy(ker);
+    }
+
+    pm_kernel_t* ker;
+    pm_process_t* proc;
+};
+
+TEST_F(ValidatePageAcct, TestPageFlags) {
+    PageAcct& pi = PageAcct::Instance();
+    pi.InitPageAcct(false);
+
+    uint64_t* pagemap;
+    size_t num_pages;
+    for (size_t i = 0; i < proc->num_maps; i++) {
+        ASSERT_EQ(0, pm_map_pagemap(proc->maps[i], &pagemap, &num_pages));
+        for (size_t j = 0; j < num_pages; j++) {
+            if (!PM_PAGEMAP_PRESENT(pagemap[j])) continue;
+
+            uint64_t pfn = PM_PAGEMAP_PFN(pagemap[j]);
+            uint64_t page_flags_pagemap, page_flags_meminfo;
+
+            ASSERT_EQ(0, pm_kernel_flags(ker, pfn, &page_flags_pagemap));
+            ASSERT_TRUE(pi.PageFlags(pfn, &page_flags_meminfo));
+            // check if page flags equal
+            EXPECT_EQ(page_flags_pagemap, page_flags_meminfo);
+        }
+        free(pagemap);
+    }
+}
+
+TEST_F(ValidatePageAcct, TestPageCounts) {
+    PageAcct& pi = PageAcct::Instance();
+    pi.InitPageAcct(false);
+
+    uint64_t* pagemap;
+    size_t num_pages;
+    for (size_t i = 0; i < proc->num_maps; i++) {
+        ASSERT_EQ(0, pm_map_pagemap(proc->maps[i], &pagemap, &num_pages));
+        for (size_t j = 0; j < num_pages; j++) {
+            uint64_t pfn = PM_PAGEMAP_PFN(pagemap[j]);
+            uint64_t map_count_pagemap, map_count_meminfo;
+
+            ASSERT_EQ(0, pm_kernel_count(ker, pfn, &map_count_pagemap));
+            ASSERT_TRUE(pi.PageMapCount(pfn, &map_count_meminfo));
+            // check if map counts are equal
+            EXPECT_EQ(map_count_pagemap, map_count_meminfo);
+        }
+        free(pagemap);
+    }
+}
+
+TEST_F(ValidatePageAcct, TestPageIdle) {
+    // skip the test if idle page tracking isn't enabled
+    if (pm_kernel_init_page_idle(ker) != 0) {
+        return;
+    }
+
+    PageAcct& pi = PageAcct::Instance();
+    ASSERT_TRUE(pi.InitPageAcct(true));
+
+    uint64_t* pagemap;
+    size_t num_pages;
+    for (size_t i = 0; i < proc->num_maps; i++) {
+        ASSERT_EQ(0, pm_map_pagemap(proc->maps[i], &pagemap, &num_pages));
+        for (size_t j = 0; j < num_pages; j++) {
+            if (!PM_PAGEMAP_PRESENT(pagemap[j])) continue;
+            uint64_t pfn = PM_PAGEMAP_PFN(pagemap[j]);
+
+            ASSERT_EQ(0, pm_kernel_mark_page_idle(ker, &pfn, 1));
+            int idle_status_pagemap = pm_kernel_get_page_idle(ker, pfn);
+            int idle_status_meminfo = pi.IsPageIdle(pfn);
+            EXPECT_EQ(idle_status_pagemap, idle_status_meminfo);
+        }
+        free(pagemap);
+    }
+}
+
+TEST(SysMemInfoParser, TestSysMemInfoFile) {
+    std::string meminfo = R"meminfo(MemTotal:        3019740 kB
+MemFree:         1809728 kB
+MemAvailable:    2546560 kB
+Buffers:           54736 kB
+Cached:           776052 kB
+SwapCached:            0 kB
+Active:           445856 kB
+Inactive:         459092 kB
+Active(anon):      78492 kB
+Inactive(anon):     2240 kB
+Active(file):     367364 kB
+Inactive(file):   456852 kB
+Unevictable:        3096 kB
+Mlocked:            3096 kB
+SwapTotal:             0 kB
+SwapFree:              0 kB
+Dirty:                32 kB
+Writeback:             0 kB
+AnonPages:         74988 kB
+Mapped:            62624 kB
+Shmem:              4020 kB
+Slab:              86464 kB
+SReclaimable:      44432 kB
+SUnreclaim:        42032 kB
+KernelStack:        4880 kB
+PageTables:         2900 kB
+NFS_Unstable:          0 kB
+Bounce:                0 kB
+WritebackTmp:          0 kB
+CommitLimit:     1509868 kB
+Committed_AS:      80296 kB
+VmallocTotal:   263061440 kB
+VmallocUsed:           0 kB
+VmallocChunk:          0 kB
+AnonHugePages:      6144 kB
+ShmemHugePages:        0 kB
+ShmemPmdMapped:        0 kB
+CmaTotal:         131072 kB
+CmaFree:          130380 kB
+HugePages_Total:       0
+HugePages_Free:        0
+HugePages_Rsvd:        0
+HugePages_Surp:        0
+Hugepagesize:       2048 kB)meminfo";
+
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    ASSERT_TRUE(::android::base::WriteStringToFd(meminfo, tf.fd));
+
+    SysMemInfo mi;
+    ASSERT_TRUE(mi.ReadMemInfo(tf.path));
+    EXPECT_EQ(mi.mem_total_kb(), 3019740);
+    EXPECT_EQ(mi.mem_page_tables_kb(), 2900);
+}
+
+TEST(SysMemInfoParser, TestEmptyFile) {
+    TemporaryFile tf;
+    std::string empty_string = "";
+    ASSERT_TRUE(tf.fd != -1);
+    ASSERT_TRUE(::android::base::WriteStringToFd(empty_string, tf.fd));
+
+    SysMemInfo mi;
+    EXPECT_TRUE(mi.ReadMemInfo(tf.path));
+    EXPECT_EQ(mi.mem_total_kb(), 0);
+}
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    if (argc <= 1) {
+        cerr << "Pid of a permanently sleeping process must be provided." << endl;
+        exit(EXIT_FAILURE);
+    }
+    ::android::base::InitLogging(argv, android::base::StderrLogger);
+    pid = std::stoi(std::string(argv[1]));
+    return RUN_ALL_TESTS();
+}
diff --git a/libmeminfo/meminfo_private.h b/libmeminfo/meminfo_private.h
new file mode 100644
index 0000000..df5699c
--- /dev/null
+++ b/libmeminfo/meminfo_private.h
@@ -0,0 +1,34 @@
+/*
+ * 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 <sys/types.h>
+#include <unistd.h>
+
+#include <meminfo/meminfo.h>
+#include <meminfo/pageacct.h>
+#include <meminfo/procmeminfo.h>
+#include <meminfo/sysmeminfo.h>
+
+// Macros to do per-page flag manipulation
+#define _BITS(x, offset, bits) (((x) >> (offset)) & ((1LL << (bits)) - 1))
+#define PAGE_PRESENT(x) (_BITS(x, 63, 1))
+#define PAGE_SWAPPED(x) (_BITS(x, 62, 1))
+#define PAGE_SHIFT(x) (_BITS(x, 55, 6))
+#define PAGE_PFN(x) (_BITS(x, 0, 55))
+#define PAGE_SWAP_OFFSET(x) (_BITS(x, 5, 50))
+#define PAGE_SWAP_TYPE(x) (_BITS(x, 0, 5))
diff --git a/libmeminfo/pageacct.cpp b/libmeminfo/pageacct.cpp
new file mode 100644
index 0000000..887a74d
--- /dev/null
+++ b/libmeminfo/pageacct.cpp
@@ -0,0 +1,142 @@
+/*
+ * 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 <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+
+#include "meminfo_private.h"
+
+using unique_fd = ::android::base::unique_fd;
+
+namespace android {
+namespace meminfo {
+
+static inline off64_t pfn_to_idle_bitmap_offset(uint64_t pfn) {
+    return static_cast<off64_t>((pfn >> 6) << 3);
+}
+
+uint64_t pagesize(void) {
+    static uint64_t pagesize = sysconf(_SC_PAGE_SIZE);
+    return pagesize;
+}
+
+bool PageAcct::InitPageAcct(bool pageidle_enable) {
+    if (pageidle_enable && !PageAcct::KernelHasPageIdle()) {
+        LOG(ERROR) << "Idle page tracking is not supported by the kernel";
+        return false;
+    }
+
+    if (kpagecount_fd_ < 0) {
+        unique_fd count_fd(TEMP_FAILURE_RETRY(open("/proc/kpagecount", O_RDONLY | O_CLOEXEC)));
+        if (count_fd < 0) {
+            PLOG(ERROR) << "Failed to open /proc/kpagecount";
+            return false;
+        }
+        kpagecount_fd_ = std::move(count_fd);
+    }
+
+    if (kpageflags_fd_ < 0) {
+        unique_fd flags_fd(TEMP_FAILURE_RETRY(open("/proc/kpageflags", O_RDONLY | O_CLOEXEC)));
+        if (flags_fd < 0) {
+            PLOG(ERROR) << "Failed to open /proc/kpageflags";
+            return false;
+        }
+        kpageflags_fd_ = std::move(flags_fd);
+    }
+
+    if (pageidle_enable && pageidle_fd_ < 0) {
+        unique_fd idle_fd(
+                TEMP_FAILURE_RETRY(open("/sys/kernel/mm/page_idle/bitmap", O_RDWR | O_CLOEXEC)));
+        if (idle_fd < 0) {
+            PLOG(ERROR) << "Failed to open page idle bitmap";
+            return false;
+        }
+        pageidle_fd_ = std::move(idle_fd);
+    }
+
+    return true;
+}
+
+bool PageAcct::PageFlags(uint64_t pfn, uint64_t* flags) {
+    if (!flags) return false;
+
+    if (kpageflags_fd_ < 0) {
+        if (!InitPageAcct()) return false;
+    }
+
+    if (pread64(kpageflags_fd_, flags, sizeof(uint64_t), pfn * sizeof(uint64_t)) < 0) {
+        PLOG(ERROR) << "Failed to read page flags for page " << pfn;
+        return false;
+    }
+    return true;
+}
+
+bool PageAcct::PageMapCount(uint64_t pfn, uint64_t* mapcount) {
+    if (!mapcount) return false;
+
+    if (kpagecount_fd_ < 0) {
+        if (!InitPageAcct()) return false;
+    }
+
+    if (pread64(kpagecount_fd_, mapcount, sizeof(uint64_t), pfn * sizeof(uint64_t)) < 0) {
+        PLOG(ERROR) << "Failed to read map count for page " << pfn;
+        return false;
+    }
+    return true;
+}
+
+int PageAcct::IsPageIdle(uint64_t pfn) {
+    if (pageidle_fd_ < 0) {
+        if (!InitPageAcct(true)) return -EOPNOTSUPP;
+    }
+
+    int idle_status = MarkPageIdle(pfn);
+    if (idle_status) return idle_status;
+
+    return GetPageIdle(pfn);
+}
+
+int PageAcct::MarkPageIdle(uint64_t pfn) const {
+    off64_t offset = pfn_to_idle_bitmap_offset(pfn);
+    // set the bit corresponding to page frame
+    uint64_t idle_bits = 1ULL << (pfn % 64);
+
+    if (pwrite64(pageidle_fd_, &idle_bits, sizeof(uint64_t), offset) < 0) {
+        PLOG(ERROR) << "Failed to write page idle bitmap for page " << pfn;
+        return -errno;
+    }
+
+    return 0;
+}
+
+int PageAcct::GetPageIdle(uint64_t pfn) const {
+    off64_t offset = pfn_to_idle_bitmap_offset(pfn);
+    uint64_t idle_bits;
+
+    if (pread64(pageidle_fd_, &idle_bits, sizeof(uint64_t), offset) < 0) {
+        PLOG(ERROR) << "Failed to read page idle bitmap for page " << pfn;
+        return -errno;
+    }
+
+    return !!(idle_bits & (1ULL << (pfn % 64)));
+}
+
+}  // namespace meminfo
+}  // namespace android
diff --git a/libmeminfo/procmeminfo.cpp b/libmeminfo/procmeminfo.cpp
new file mode 100644
index 0000000..fe91d25
--- /dev/null
+++ b/libmeminfo/procmeminfo.cpp
@@ -0,0 +1,218 @@
+/*
+ * 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 <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <linux/kernel-page-flags.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <fstream>
+#include <iostream>
+#include <memory>
+#include <string>
+#include <utility>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <procinfo/process_map.h>
+
+#include "meminfo_private.h"
+
+namespace android {
+namespace meminfo {
+
+static void add_mem_usage(MemUsage* to, const MemUsage& from) {
+    to->vss += from.vss;
+    to->rss += from.rss;
+    to->pss += from.pss;
+    to->uss += from.uss;
+
+    to->private_clean += from.private_clean;
+    to->private_dirty += from.private_dirty;
+
+    to->shared_clean += from.shared_clean;
+    to->shared_dirty += from.shared_dirty;
+}
+
+ProcMemInfo::ProcMemInfo(pid_t pid, bool get_wss) : pid_(pid), get_wss_(get_wss) {
+    if (!ReadMaps(get_wss_)) {
+        LOG(ERROR) << "Failed to read maps for Process " << pid_;
+    }
+}
+
+const std::vector<Vma>& ProcMemInfo::Maps() {
+    return maps_;
+}
+
+const MemUsage& ProcMemInfo::Usage() {
+    if (get_wss_) {
+        LOG(WARNING) << "Trying to read memory usage from working set object";
+    }
+    return usage_;
+}
+
+const MemUsage& ProcMemInfo::Wss() {
+    if (!get_wss_) {
+        LOG(WARNING) << "Trying to read working set when there is none";
+    }
+
+    return wss_;
+}
+
+bool ProcMemInfo::WssReset() {
+    if (!get_wss_) {
+        LOG(ERROR) << "Trying to reset working set from a memory usage counting object";
+        return false;
+    }
+
+    std::string clear_refs_path = ::android::base::StringPrintf("/proc/%d/clear_refs", pid_);
+    if (!::android::base::WriteStringToFile("1\n", clear_refs_path)) {
+        PLOG(ERROR) << "Failed to write to " << clear_refs_path;
+        return false;
+    }
+
+    wss_.clear();
+    return true;
+}
+
+bool ProcMemInfo::ReadMaps(bool get_wss) {
+    // parse and read /proc/<pid>/maps
+    std::string maps_file = ::android::base::StringPrintf("/proc/%d/maps", pid_);
+    if (!::android::procinfo::ReadMapFile(
+                maps_file, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff,
+                               const char* name) {
+                    maps_.emplace_back(Vma(start, end, pgoff, flags, name));
+                })) {
+        LOG(ERROR) << "Failed to parse " << maps_file;
+        maps_.clear();
+        return false;
+    }
+
+    std::string pagemap_file = ::android::base::StringPrintf("/proc/%d/pagemap", pid_);
+    ::android::base::unique_fd pagemap_fd(
+            TEMP_FAILURE_RETRY(open(pagemap_file.c_str(), O_RDONLY | O_CLOEXEC)));
+    if (pagemap_fd < 0) {
+        PLOG(ERROR) << "Failed to open " << pagemap_file;
+        return false;
+    }
+
+    for (auto& vma : maps_) {
+        if (!ReadVmaStats(pagemap_fd.get(), vma, get_wss)) {
+            LOG(ERROR) << "Failed to read page map for vma " << vma.name << "[" << vma.start
+                       << "-" << vma.end << "]";
+            maps_.clear();
+            return false;
+        }
+        if (get_wss) {
+            add_mem_usage(&wss_, vma.wss);
+        } else {
+            add_mem_usage(&usage_, vma.usage);
+        }
+    }
+
+    return true;
+}
+
+bool ProcMemInfo::ReadVmaStats(int pagemap_fd, Vma& vma, bool get_wss) {
+    PageAcct& pinfo = PageAcct::Instance();
+    uint64_t pagesz = getpagesize();
+    uint64_t num_pages = (vma.end - vma.start) / pagesz;
+
+    std::unique_ptr<uint64_t[]> pg_frames(new uint64_t[num_pages]);
+    uint64_t first = vma.start / pagesz;
+    if (pread64(pagemap_fd, pg_frames.get(), num_pages * sizeof(uint64_t),
+                first * sizeof(uint64_t)) < 0) {
+        PLOG(ERROR) << "Failed to read page frames from page map for pid: " << pid_;
+        return false;
+    }
+
+    std::unique_ptr<uint64_t[]> pg_flags(new uint64_t[num_pages]);
+    std::unique_ptr<uint64_t[]> pg_counts(new uint64_t[num_pages]);
+    for (uint64_t i = 0; i < num_pages; ++i) {
+        if (!get_wss) {
+            vma.usage.vss += pagesz;
+        }
+        uint64_t p = pg_frames[i];
+        if (!PAGE_PRESENT(p) && !PAGE_SWAPPED(p)) continue;
+
+        if (PAGE_SWAPPED(p)) {
+            // TODO: do what's needed for swapped pages
+            continue;
+        }
+
+        uint64_t page_frame = PAGE_PFN(p);
+        if (!pinfo.PageFlags(page_frame, &pg_flags[i])) {
+            LOG(ERROR) << "Failed to get page flags for " << page_frame << " in process " << pid_;
+            return false;
+        }
+
+        if (!pinfo.PageMapCount(page_frame, &pg_counts[i])) {
+            LOG(ERROR) << "Failed to get page count for " << page_frame << " in process " << pid_;
+            return false;
+        }
+
+        // Page was unmapped between the presence check at the beginning of the loop and here.
+        if (pg_counts[i] == 0) {
+            pg_frames[i] = 0;
+            pg_flags[i] = 0;
+            continue;
+        }
+
+        bool is_dirty = !!(pg_flags[i] & (1 << KPF_DIRTY));
+        bool is_private = (pg_counts[i] == 1);
+        // Working set
+        if (get_wss) {
+            bool is_referenced = !!(pg_flags[i] & (1 << KPF_REFERENCED));
+            if (!is_referenced) {
+                continue;
+            }
+            // This effectively makes vss = rss for the working set is requested.
+            // The libpagemap implementation returns vss > rss for
+            // working set, which doesn't make sense.
+            vma.wss.vss += pagesz;
+            vma.wss.rss += pagesz;
+            vma.wss.uss += is_private ? pagesz : 0;
+            vma.wss.pss += pagesz / pg_counts[i];
+            if (is_private) {
+                vma.wss.private_dirty += is_dirty ? pagesz : 0;
+                vma.wss.private_clean += is_dirty ? 0 : pagesz;
+            } else {
+                vma.wss.shared_dirty += is_dirty ? pagesz : 0;
+                vma.wss.shared_clean += is_dirty ? 0 : pagesz;
+            }
+        } else {
+            vma.usage.rss += pagesz;
+            vma.usage.uss += is_private ? pagesz : 0;
+            vma.usage.pss += pagesz / pg_counts[i];
+            if (is_private) {
+                vma.usage.private_dirty += is_dirty ? pagesz : 0;
+                vma.usage.private_clean += is_dirty ? 0 : pagesz;
+            } else {
+                vma.usage.shared_dirty += is_dirty ? pagesz : 0;
+                vma.usage.shared_clean += is_dirty ? 0 : pagesz;
+            }
+        }
+    }
+
+    return true;
+}
+
+}  // namespace meminfo
+}  // namespace android
diff --git a/libmeminfo/sysmeminfo.cpp b/libmeminfo/sysmeminfo.cpp
new file mode 100644
index 0000000..50fa213
--- /dev/null
+++ b/libmeminfo/sysmeminfo.cpp
@@ -0,0 +1,129 @@
+/*
+ * 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 <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <cctype>
+#include <fstream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+
+#include "meminfo_private.h"
+
+namespace android {
+namespace meminfo {
+
+const std::vector<std::string> SysMemInfo::kDefaultSysMemInfoTags = {
+    "MemTotal:", "MemFree:",      "Buffers:",     "Cached:",     "Shmem:",
+    "Slab:",     "SReclaimable:", "SUnreclaim:",  "SwapTotal:",  "SwapFree:",
+    "ZRam:",     "Mapped:",       "VmallocUsed:", "PageTables:", "KernelStack:",
+};
+
+bool SysMemInfo::ReadMemInfo(const std::string& path) {
+    return ReadMemInfo(SysMemInfo::kDefaultSysMemInfoTags, path);
+}
+
+// TODO: Delete this function if it can't match up with the c-like implementation below.
+// Currently, this added about 50 % extra overhead on hikey.
+#if 0
+bool SysMemInfo::ReadMemInfo(const std::vector<std::string>& tags, const std::string& path) {
+    std::string buffer;
+    if (!::android::base::ReadFileToString(path, &buffer)) {
+        PLOG(ERROR) << "Failed to read : " << path;
+        return false;
+    }
+
+    uint32_t total_found = 0;
+    for (auto s = buffer.begin(); s < buffer.end() && total_found < tags.size();) {
+        for (auto& tag : tags) {
+            if (tag == std::string(s, s + tag.size())) {
+                s += tag.size();
+                while (isspace(*s)) s++;
+                auto num_start = s;
+                while (std::isdigit(*s)) s++;
+
+                std::string number(num_start, num_start + (s - num_start));
+                if (!::android::base::ParseUint(number, &mem_in_kb_[tag])) {
+                    LOG(ERROR) << "Failed to parse uint";
+                    return false;
+                }
+                total_found++;
+                break;
+            }
+        }
+        while (s < buffer.end() && *s != '\n') s++;
+        if (s < buffer.end()) s++;
+    }
+
+    return true;
+}
+
+#else
+bool SysMemInfo::ReadMemInfo(const std::vector<std::string>& tags, const std::string& path) {
+    char buffer[4096];
+    int fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
+    if (fd < 0) {
+        PLOG(ERROR) << "Failed to open file :" << path;
+        return false;
+    }
+
+    const int len = read(fd, buffer, sizeof(buffer) - 1);
+    close(fd);
+    if (len < 0) {
+        return false;
+    }
+
+    buffer[len] = '\0';
+    char* p = buffer;
+    uint32_t found = 0;
+    while (*p && found < tags.size()) {
+        for (auto& tag : tags) {
+            if (strncmp(p, tag.c_str(), tag.size()) == 0) {
+                p += tag.size();
+                while (*p == ' ') p++;
+                char* endptr = nullptr;
+                mem_in_kb_[tag] = strtoull(p, &endptr, 10);
+                if (p == endptr) {
+                    PLOG(ERROR) << "Failed to parse line in file: " << path;
+                    return false;
+                }
+                p = endptr;
+                found++;
+                break;
+            }
+        }
+        while (*p && *p != '\n') {
+            p++;
+        }
+        if (*p) p++;
+    }
+
+    return true;
+}
+#endif
+
+}  // namespace meminfo
+}  // namespace android
diff --git a/libmeminfo/tools/Android.bp b/libmeminfo/tools/Android.bp
new file mode 100644
index 0000000..0870130
--- /dev/null
+++ b/libmeminfo/tools/Android.bp
@@ -0,0 +1,27 @@
+// 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.
+
+cc_binary {
+    name: "procmem2",
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-unused-parameter",
+    ],
+
+    srcs: ["procmem.cpp"],
+    shared_libs: [
+        "libmeminfo",
+    ],
+}
diff --git a/libmeminfo/tools/procmem.cpp b/libmeminfo/tools/procmem.cpp
new file mode 100644
index 0000000..3571e41
--- /dev/null
+++ b/libmeminfo/tools/procmem.cpp
@@ -0,0 +1,171 @@
+/*
+ * 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 <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <meminfo/procmeminfo.h>
+
+using ProcMemInfo = ::android::meminfo::ProcMemInfo;
+using MemUsage = ::android::meminfo::MemUsage;
+
+static void usage(const char* cmd) {
+    fprintf(stderr,
+            "Usage: %s [-i] [ -w | -W ] [ -p | -m ] [ -h ] pid\n"
+            "    -i  Uses idle page tracking for working set statistics.\n"
+            "    -w  Displays statistics for the working set only.\n"
+            "    -W  Resets the working set of the process.\n"
+            "    -p  Sort by PSS.\n"
+            "    -u  Sort by USS.\n"
+            "    -m  Sort by mapping order (as read from /proc).\n"
+            "    -h  Hide maps with no RSS.\n",
+            cmd);
+}
+
+static void show_footer(uint32_t nelem, const std::string& padding) {
+    std::string elem(7, '-');
+
+    for (uint32_t i = 0; i < nelem; ++i) {
+        std::cout << std::setw(7) << elem << padding;
+    }
+    std::cout << std::endl;
+}
+
+static void show_header(const std::vector<std::string>& header, const std::string& padding) {
+    if (header.empty()) return;
+
+    for (size_t i = 0; i < header.size() - 1; ++i) {
+        std::cout << std::setw(7) << header[i] << padding;
+    }
+    std::cout << header.back() << std::endl;
+    show_footer(header.size() - 1, padding);
+}
+
+static void scan_usage(std::stringstream& ss, const MemUsage& usage, const std::string& padding,
+                       bool show_wss) {
+    // clear string stream first.
+    ss.str("");
+    // TODO: use ::android::base::StringPrintf instead of <iomanip> here.
+    if (!show_wss)
+        ss << std::setw(6) << usage.vss/1024 << padding;
+    ss << std::setw(6) << usage.rss/1024 << padding << std::setw(6)
+       << usage.pss/1024 << padding << std::setw(6) << usage.uss/1024 << padding
+       << std::setw(6) << usage.shared_clean/1024 << padding << std::setw(6)
+       << usage.shared_dirty/1024 << padding << std::setw(6)
+       << usage.private_clean/1024 << padding << std::setw(6)
+       << usage.private_dirty/1024 << padding;
+}
+
+static int show(ProcMemInfo& proc, bool hide_zeroes, bool show_wss) {
+    const std::vector<std::string> main_header = {"Vss",  "Rss",  "Pss",  "Uss", "ShCl",
+                                                  "ShDi", "PrCl", "PrDi", "Name"};
+    const std::vector<std::string> wss_header = {"WRss",  "WPss",  "WUss",  "WShCl",
+                                                 "WShDi", "WPrCl", "WPrDi", "Name"};
+    const std::vector<std::string>& header = show_wss ? wss_header : main_header;
+
+    // Read process memory stats
+    const MemUsage& stats = show_wss ? proc.Wss() : proc.Usage();
+    const std::vector<::android::meminfo::Vma>& maps = proc.Maps();
+
+    // following retains 'procmem' output so as to not break any scripts
+    // that rely on it.
+    std::string spaces = "  ";
+    show_header(header, spaces);
+    const std::string padding = "K  ";
+    std::stringstream ss;
+    for (auto& vma : maps) {
+        const MemUsage& vma_stats = show_wss ? vma.wss : vma.usage;
+        if (hide_zeroes && vma_stats.rss == 0) {
+            continue;
+        }
+        scan_usage(ss, vma_stats, padding, show_wss);
+        ss << vma.name << std::endl;
+        std::cout << ss.str();
+    }
+    show_footer(header.size() - 1, spaces);
+    scan_usage(ss, stats, padding, show_wss);
+    ss << "TOTAL" << std::endl;
+    std::cout << ss.str();
+
+    return 0;
+}
+
+int main(int argc, char* argv[]) {
+    bool use_pageidle = false;
+    bool hide_zeroes = false;
+    bool wss_reset = false;
+    bool show_wss = false;
+    int opt;
+
+    while ((opt = getopt(argc, argv, "himpuWw")) != -1) {
+        switch (opt) {
+            case 'h':
+                hide_zeroes = true;
+                break;
+            case 'i':
+                use_pageidle = true;
+                break;
+            case 'm':
+                break;
+            case 'p':
+                break;
+            case 'u':
+                break;
+            case 'W':
+                wss_reset = true;
+                break;
+            case 'w':
+                show_wss = true;
+                break;
+            case '?':
+                usage(argv[0]);
+                return 0;
+            default:
+                abort();
+        }
+    }
+
+    if (optind != (argc - 1)) {
+        fprintf(stderr, "Need exactly one pid at the end\n");
+        usage(argv[0]);
+        exit(EXIT_FAILURE);
+    }
+
+    pid_t pid = atoi(argv[optind]);
+    if (pid == 0) {
+        std::cerr << "Invalid process id" << std::endl;
+        exit(EXIT_FAILURE);
+    }
+
+    bool need_wss = wss_reset || show_wss;
+    ProcMemInfo proc(pid, need_wss);
+    if (wss_reset) {
+        if (!proc.WssReset()) {
+            std::cerr << "Failed to reset working set of pid : " << pid << std::endl;
+            exit(EXIT_FAILURE);
+        }
+        return 0;
+    }
+
+    return show(proc, hide_zeroes, show_wss);
+}
diff --git a/libmemunreachable/Android.bp b/libmemunreachable/Android.bp
index 248a9d2..b78a4c4 100644
--- a/libmemunreachable/Android.bp
+++ b/libmemunreachable/Android.bp
@@ -46,12 +46,6 @@
             static_libs: ["libunwind_llvm"],
         },
     },
-
-    // TODO(b/78118944), clang lld link flags do not work with special link
-    // rules for libunwind_llvm yet. Linked aosp_arm-eng image failed to
-    // boot up in the emulator.
-    use_clang_lld: false,
-
     export_include_dirs: ["include"],
     local_include_dirs: ["include"],
 }
diff --git a/libmetricslogger/Android.bp b/libmetricslogger/Android.bp
index 2d327ee..7d7554b 100644
--- a/libmetricslogger/Android.bp
+++ b/libmetricslogger/Android.bp
@@ -34,15 +34,6 @@
     export_shared_lib_headers: ["libstatssocket"],
 }
 
-// static version of libmetricslogger, needed by a few art static binaries
-// TODO(b/117829226): Remove once dependencies are cleaned up.
-cc_library_static {
-    name: "libmetricslogger_static",
-    srcs: metricslogger_lib_src_files,
-    defaults: ["metricslogger_defaults"],
-    export_shared_lib_headers: ["libstatssocket"],
-}
-
 // metricslogger shared library, debug
 // -----------------------------------------------------------------------------
 cc_library_shared {
diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h
index 9fa4154..2412f3c 100644
--- a/libprocessgroup/include/processgroup/processgroup.h
+++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -31,8 +31,10 @@
 // that it only returns 0 in the case that the cgroup exists and it contains no processes.
 int killProcessGroupOnce(uid_t uid, int initialPid, int signal);
 
-int createProcessGroup(uid_t uid, int initialPid);
+int createProcessGroup(uid_t uid, int initialPid, bool memControl = false);
 
+// Set various properties of a process group. For these functions to work, the process group must
+// have been created by passing memControl=true to createProcessGroup.
 bool setProcessGroupSwappiness(uid_t uid, int initialPid, int swappiness);
 bool setProcessGroupSoftLimit(uid_t uid, int initialPid, int64_t softLimitInBytes);
 bool setProcessGroupLimit(uid_t uid, int initialPid, int64_t limitInBytes);
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 0a42053..9df8dd9 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -53,49 +53,31 @@
 
 using namespace std::chrono_literals;
 
-#define MEM_CGROUP_PATH "/dev/memcg/apps"
-#define MEM_CGROUP_TASKS "/dev/memcg/apps/tasks"
-#define ACCT_CGROUP_PATH "/acct"
+static const char kCpuacctCgroup[] = "/acct";
+static const char kMemoryCgroup[] = "/dev/memcg/apps";
 
 #define PROCESSGROUP_CGROUP_PROCS_FILE "/cgroup.procs"
 
-std::once_flag init_path_flag;
-
-static const std::string& GetCgroupRootPath() {
-    static std::string cgroup_root_path;
-    std::call_once(init_path_flag, [&]() {
-        // low-ram devices use per-app memcg by default, unlike high-end ones
-        bool low_ram_device = GetBoolProperty("ro.config.low_ram", false);
-        bool per_app_memcg =
-            GetBoolProperty("ro.config.per_app_memcg", low_ram_device);
-        if (per_app_memcg) {
-            // Check if mem cgroup is mounted, only then check for
-            // write-access to avoid SELinux denials
-            cgroup_root_path =
-                (access(MEM_CGROUP_TASKS, F_OK) || access(MEM_CGROUP_PATH, W_OK) ?
-                ACCT_CGROUP_PATH : MEM_CGROUP_PATH);
-        } else {
-            cgroup_root_path = ACCT_CGROUP_PATH;
-        }
-    });
-    return cgroup_root_path;
+static bool isMemoryCgroupSupported() {
+    static bool memcg_supported = !access("/dev/memcg/memory.limit_in_bytes", F_OK);
+    return memcg_supported;
 }
 
-static std::string ConvertUidToPath(uid_t uid) {
-    return StringPrintf("%s/uid_%d", GetCgroupRootPath().c_str(), uid);
+static std::string ConvertUidToPath(const char* cgroup, uid_t uid) {
+    return StringPrintf("%s/uid_%d", cgroup, uid);
 }
 
-static std::string ConvertUidPidToPath(uid_t uid, int pid) {
-    return StringPrintf("%s/uid_%d/pid_%d", GetCgroupRootPath().c_str(), uid, pid);
+static std::string ConvertUidPidToPath(const char* cgroup, uid_t uid, int pid) {
+    return StringPrintf("%s/uid_%d/pid_%d", cgroup, uid, pid);
 }
 
-static int RemoveProcessGroup(uid_t uid, int pid) {
+static int RemoveProcessGroup(const char* cgroup, uid_t uid, int pid) {
     int ret;
 
-    auto uid_pid_path = ConvertUidPidToPath(uid, pid);
+    auto uid_pid_path = ConvertUidPidToPath(cgroup, uid, pid);
     ret = rmdir(uid_pid_path.c_str());
 
-    auto uid_path = ConvertUidToPath(uid);
+    auto uid_path = ConvertUidToPath(cgroup, uid);
     rmdir(uid_path.c_str());
 
     return ret;
@@ -124,25 +106,26 @@
 void removeAllProcessGroups()
 {
     LOG(VERBOSE) << "removeAllProcessGroups()";
-    const auto& cgroup_root_path = GetCgroupRootPath();
-    std::unique_ptr<DIR, decltype(&closedir)> root(opendir(cgroup_root_path.c_str()), closedir);
-    if (root == NULL) {
-        PLOG(ERROR) << "Failed to open " << cgroup_root_path;
-    } else {
-        dirent* dir;
-        while ((dir = readdir(root.get())) != nullptr) {
-            if (dir->d_type != DT_DIR) {
-                continue;
-            }
+    for (const char* cgroup_root_path : {kCpuacctCgroup, kMemoryCgroup}) {
+        std::unique_ptr<DIR, decltype(&closedir)> root(opendir(cgroup_root_path), closedir);
+        if (root == NULL) {
+            PLOG(ERROR) << "Failed to open " << cgroup_root_path;
+        } else {
+            dirent* dir;
+            while ((dir = readdir(root.get())) != nullptr) {
+                if (dir->d_type != DT_DIR) {
+                    continue;
+                }
 
-            if (!StartsWith(dir->d_name, "uid_")) {
-                continue;
-            }
+                if (!StartsWith(dir->d_name, "uid_")) {
+                    continue;
+                }
 
-            auto path = StringPrintf("%s/%s", cgroup_root_path.c_str(), dir->d_name);
-            RemoveUidProcessGroups(path);
-            LOG(VERBOSE) << "Removing " << path;
-            if (rmdir(path.c_str()) == -1) PLOG(WARNING) << "Failed to remove " << path;
+                auto path = StringPrintf("%s/%s", cgroup_root_path, dir->d_name);
+                RemoveUidProcessGroups(path);
+                LOG(VERBOSE) << "Removing " << path;
+                if (rmdir(path.c_str()) == -1) PLOG(WARNING) << "Failed to remove " << path;
+            }
         }
     }
 }
@@ -150,8 +133,8 @@
 // Returns number of processes killed on success
 // Returns 0 if there are no processes in the process cgroup left to kill
 // Returns -1 on error
-static int DoKillProcessGroupOnce(uid_t uid, int initialPid, int signal) {
-    auto path = ConvertUidPidToPath(uid, initialPid) + PROCESSGROUP_CGROUP_PROCS_FILE;
+static int DoKillProcessGroupOnce(const char* cgroup, uid_t uid, int initialPid, int signal) {
+    auto path = ConvertUidPidToPath(cgroup, uid, initialPid) + PROCESSGROUP_CGROUP_PROCS_FILE;
     std::unique_ptr<FILE, decltype(&fclose)> fd(fopen(path.c_str(), "re"), fclose);
     if (!fd) {
         PLOG(WARNING) << "Failed to open process cgroup uid " << uid << " pid " << initialPid;
@@ -217,11 +200,16 @@
 }
 
 static int KillProcessGroup(uid_t uid, int initialPid, int signal, int retries) {
+    const char* cgroup =
+            (!access(ConvertUidPidToPath(kCpuacctCgroup, uid, initialPid).c_str(), F_OK))
+                    ? kCpuacctCgroup
+                    : kMemoryCgroup;
+
     std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
 
     int retry = retries;
     int processes;
-    while ((processes = DoKillProcessGroupOnce(uid, initialPid, signal)) > 0) {
+    while ((processes = DoKillProcessGroupOnce(cgroup, uid, initialPid, signal)) > 0) {
         LOG(VERBOSE) << "Killed " << processes << " processes for processgroup " << initialPid;
         if (retry > 0) {
             std::this_thread::sleep_for(5ms);
@@ -251,7 +239,7 @@
             LOG(INFO) << "Successfully killed process cgroup uid " << uid << " pid " << initialPid
                       << " in " << static_cast<int>(ms) << "ms";
         }
-        return RemoveProcessGroup(uid, initialPid);
+        return RemoveProcessGroup(cgroup, uid, initialPid);
     } else {
         if (retries > 0) {
             LOG(ERROR) << "Failed to kill process cgroup uid " << uid << " pid " << initialPid
@@ -285,16 +273,29 @@
     return true;
 }
 
-int createProcessGroup(uid_t uid, int initialPid)
+static bool isPerAppMemcgEnabled() {
+    static bool per_app_memcg =
+            GetBoolProperty("ro.config.per_app_memcg", GetBoolProperty("ro.config.low_ram", false));
+    return per_app_memcg;
+}
+
+int createProcessGroup(uid_t uid, int initialPid, bool memControl)
 {
-    auto uid_path = ConvertUidToPath(uid);
+    const char* cgroup;
+    if (isMemoryCgroupSupported() && (memControl || isPerAppMemcgEnabled())) {
+        cgroup = kMemoryCgroup;
+    } else {
+        cgroup = kCpuacctCgroup;
+    }
+
+    auto uid_path = ConvertUidToPath(cgroup, uid);
 
     if (!MkdirAndChown(uid_path, 0750, AID_SYSTEM, AID_SYSTEM)) {
         PLOG(ERROR) << "Failed to make and chown " << uid_path;
         return -errno;
     }
 
-    auto uid_pid_path = ConvertUidPidToPath(uid, initialPid);
+    auto uid_pid_path = ConvertUidPidToPath(cgroup, uid, initialPid);
 
     if (!MkdirAndChown(uid_pid_path, 0750, AID_SYSTEM, AID_SYSTEM)) {
         PLOG(ERROR) << "Failed to make and chown " << uid_pid_path;
@@ -313,12 +314,12 @@
 }
 
 static bool SetProcessGroupValue(uid_t uid, int pid, const std::string& file_name, int64_t value) {
-    if (GetCgroupRootPath() != MEM_CGROUP_PATH) {
+    if (!isMemoryCgroupSupported()) {
         PLOG(ERROR) << "Memcg is not mounted.";
         return false;
     }
 
-    auto path = ConvertUidPidToPath(uid, pid) + file_name;
+    auto path = ConvertUidPidToPath(kMemoryCgroup, uid, pid) + file_name;
 
     if (!WriteStringToFile(std::to_string(value), path)) {
         PLOG(ERROR) << "Failed to write '" << value << "' to " << path;
diff --git a/libutils/include/utils/Thread.h b/libutils/include/utils/Thread.h
index 3525138..fc67656 100644
--- a/libutils/include/utils/Thread.h
+++ b/libutils/include/utils/Thread.h
@@ -47,6 +47,7 @@
     virtual             ~Thread();
 
     // Start the thread in threadLoop() which needs to be implemented.
+    // NOLINTNEXTLINE(google-default-arguments)
     virtual status_t    run(    const char* name,
                                 int32_t priority = PRIORITY_DEFAULT,
                                 size_t stack = 0);
diff --git a/llkd/README.md b/llkd/README.md
index e5be850..3da7a2f 100644
--- a/llkd/README.md
+++ b/llkd/README.md
@@ -44,7 +44,8 @@
 ABA detection since forward scheduling progress is allowed, thus the condition
 for the symbols are:
 
-- Check is looking for " " + __symbol__+ "0x" in /proc/<pid>/stack.
+- Check is looking for " __symbol__+0x" or " __symbol__.cfi+0x" in
+  /proc/__pid__/stack.
 - The __symbol__ should be rare and short lived enough that on a typical
   system the function is seen at most only once in a sample over the timeout
   period of ro.llk.stack.timeout_ms, samples occur every ro.llk.check_ms. This
@@ -88,7 +89,14 @@
 Android Properties llkd respond to (*prop*_ms parms are in milliseconds):
 
 #### ro.config.low_ram
-default false, if true do not sysrq t (dump all threads).
+device is configured with limited memory.
+
+#### ro.debuggable
+device is configured for userdebug or eng build.
+
+#### ro.llk.sysrq_t
+default not ro.config.low_ram, or ro.debuggable if property is "eng".
+if true do sysrq t (dump all threads).
 
 #### ro.llk.enable
 default false, allow live-lock daemon to be enabled.
@@ -121,14 +129,14 @@
 #### ro.llk.stack.timeout_ms
 default ro.llk.timeout_ms,
 checking for persistent stack symbols maximum timelimit.
-Only active on userdebug and eng builds.
+Only active on userdebug or eng builds.
 
 #### ro.llk.check_ms
 default 2 minutes samples of threads for D or Z.
 
 #### ro.llk.stack
-default cma_alloc,__get_user_pages, comma separated list of kernel symbols.
-The string "*false*" is the equivalent to an *empty* list.
+default cma_alloc,__get_user_pages,bit_wait_io comma separated list of kernel
+symbols.  The string "*false*" is the equivalent to an *empty* list.
 Look for kernel stack symbols that if ever persistently present can
 indicate a subsystem is locked up.
 Beware, check does not on purpose do forward scheduling ABA except by polling
@@ -136,11 +144,14 @@
 should be exceptionally rare and fleeting.
 One must be convinced that it is virtually *impossible* for symbol to show up
 persistently in all samples of the stack.
-Only active on userdebug and eng builds.
+Again, looks for a match for either " **symbol**+0x" or " **symbol**.cfi+0x"
+in stack expansion.
+Only available on userdebug or eng builds, limited privileges due to security
+concerns on user builds prevents this checking.
 
 #### ro.llk.blacklist.process
 default 0,1,2 (kernel, init and [kthreadd]) plus process names
-init,[kthreadd],[khungtaskd],lmkd,lmkd.llkd,llkd,watchdogd,
+init,[kthreadd],[khungtaskd],lmkd,llkd,watchdogd,
 [watchdogd],[watchdogd/0],...,[watchdogd/***get_nprocs**-1*].
 The string "*false*" is the equivalent to an *empty* list.
 Do not watch these processes.  A process can be comm, cmdline or pid reference.
@@ -160,7 +171,7 @@
 Do not watch processes that match this uid.
 
 #### ro.llk.blacklist.process.stack
-default process names init,lmkd,lmkd.llkd,llkd,keystore,logd.
+default process names init,lmkd.llkd,llkd,keystore,ueventd,apexd,logd.
 The string "*false*" is the equivalent to an *empty* list.
 This subset of processes are not monitored for live lock stack signatures.
 Also prevents the sepolicy violation associated with processes that block
diff --git a/llkd/include/llkd.h b/llkd/include/llkd.h
index 2c62fca..b16b1d8 100644
--- a/llkd/include/llkd.h
+++ b/llkd/include/llkd.h
@@ -35,6 +35,8 @@
 #define LLK_ENABLE_DEFAULT             false /* "eng" and userdebug true */
 #define KHT_ENABLE_WRITEABLE_PROPERTY  "khungtask.enable"
 #define KHT_ENABLE_PROPERTY            "ro." KHT_ENABLE_WRITEABLE_PROPERTY
+#define LLK_ENABLE_SYSRQ_T_PROPERTY    "ro.llk.sysrq_t"
+#define LLK_ENABLE_SYSRQ_T_DEFAULT     true
 #define LLK_MLOCKALL_PROPERTY          "ro.llk.mlockall"
 #define LLK_MLOCKALL_DEFAULT           true
 #define LLK_KILLTEST_PROPERTY          "ro.llk.killtest"
@@ -48,7 +50,7 @@
 /* LLK_CHECK_MS_DEFAULT = actual timeout_ms / LLK_CHECKS_PER_TIMEOUT_DEFAULT */
 #define LLK_CHECKS_PER_TIMEOUT_DEFAULT 5
 #define LLK_CHECK_STACK_PROPERTY       "ro.llk.stack"
-#define LLK_CHECK_STACK_DEFAULT        "cma_alloc,__get_user_pages"
+#define LLK_CHECK_STACK_DEFAULT        "cma_alloc,__get_user_pages,bit_wait_io"
 #define LLK_BLACKLIST_PROCESS_PROPERTY "ro.llk.blacklist.process"
 #define LLK_BLACKLIST_PROCESS_DEFAULT  \
     "0,1,2,init,[kthreadd],[khungtaskd],lmkd,llkd,watchdogd,[watchdogd],[watchdogd/0]"
diff --git a/llkd/libllkd.cpp b/llkd/libllkd.cpp
index 2727aab..0827470 100644
--- a/llkd/libllkd.cpp
+++ b/llkd/libllkd.cpp
@@ -85,6 +85,7 @@
 milliseconds llkCheckMs;                             // checking interval to inspect any
                                                      // persistent live-locked states
 bool llkLowRam;                                      // ro.config.low_ram
+bool llkEnableSysrqT = LLK_ENABLE_SYSRQ_T_DEFAULT;   // sysrq stack trace dump
 bool khtEnable = LLK_ENABLE_DEFAULT;                 // [khungtaskd] panic
 // [khungtaskd] should have a timeout beyond the granularity of llkTimeoutMs.
 // Provides a wide angle of margin b/c khtTimeout is also its granularity.
@@ -509,8 +510,10 @@
     return android::base::Trim(content) == string;
 }
 
-void llkPanicKernel(bool dump, pid_t tid, const char* state) __noreturn;
-void llkPanicKernel(bool dump, pid_t tid, const char* state) {
+void llkPanicKernel(bool dump, pid_t tid, const char* state,
+                    const std::string& message = "") __noreturn;
+void llkPanicKernel(bool dump, pid_t tid, const char* state, const std::string& message) {
+    if (!message.empty()) LOG(ERROR) << message;
     auto sysrqTriggerFd = llkFileToWriteFd("/proc/sysrq-trigger");
     if (sysrqTriggerFd < 0) {
         // DYB
@@ -523,14 +526,24 @@
     if (dump) {
         // Show all locks that are held
         android::base::WriteStringToFd("d", sysrqTriggerFd);
+        // Show all waiting tasks
+        android::base::WriteStringToFd("w", sysrqTriggerFd);
         // This can trigger hardware watchdog, that is somewhat _ok_.
         // But useless if pstore configured for <256KB, low ram devices ...
-        if (!llkLowRam) {
+        if (llkEnableSysrqT) {
             android::base::WriteStringToFd("t", sysrqTriggerFd);
+            // Show all locks that are held (in case 't' overflows ramoops)
+            android::base::WriteStringToFd("d", sysrqTriggerFd);
+            // Show all waiting tasks (in case 't' overflows ramoops)
+            android::base::WriteStringToFd("w", sysrqTriggerFd);
         }
         ::usleep(200000);  // let everything settle
     }
-    llkWriteStringToFile("SysRq : Trigger a crash : 'livelock,"s + state + "'\n", "/dev/kmsg");
+    // SysRq message matches kernel format, and propagates through bootstat
+    // ultimately to the boot reason into panic,livelock,<state>.
+    llkWriteStringToFile(message + (message.empty() ? "" : "\n") +
+                                 "SysRq : Trigger a crash : 'livelock,"s + state + "'\n",
+                         "/dev/kmsg");
     android::base::WriteStringToFd("c", sysrqTriggerFd);
     // NOTREACHED
     // DYB
@@ -726,7 +739,8 @@
     char match = -1;
     for (const auto& stack : llkCheckStackSymbols) {
         if (++idx < 0) break;
-        if (kernel_stack.find(" "s + stack + "+0x") != std::string::npos) {
+        if ((kernel_stack.find(" "s + stack + "+0x") != std::string::npos) ||
+            (kernel_stack.find(" "s + stack + ".cfi+0x") != std::string::npos)) {
             match = idx;
             break;
         }
@@ -798,6 +812,7 @@
 
 void llkLogConfig(void) {
     LOG(INFO) << "ro.config.low_ram=" << llkFormat(llkLowRam) << "\n"
+              << LLK_ENABLE_SYSRQ_T_PROPERTY "=" << llkFormat(llkEnableSysrqT) << "\n"
               << LLK_ENABLE_PROPERTY "=" << llkFormat(llkEnable) << "\n"
               << KHT_ENABLE_PROPERTY "=" << llkFormat(khtEnable) << "\n"
               << LLK_MLOCKALL_PROPERTY "=" << llkFormat(llkMlockall) << "\n"
@@ -1089,10 +1104,12 @@
                 }
             }
             // We are here because we have confirmed kernel live-lock
-            LOG(ERROR) << state << ' ' << llkFormat(procp->count) << ' ' << ppid << "->" << pid
-                       << "->" << tid << ' ' << procp->getComm() << " [panic]";
+            const auto message = state + " "s + llkFormat(procp->count) + " " +
+                                 std::to_string(ppid) + "->" + std::to_string(pid) + "->" +
+                                 std::to_string(tid) + " " + procp->getComm() + " [panic]";
             llkPanicKernel(true, tid,
-                           (state == 'Z') ? "zombie" : (state == 'D') ? "driver" : "sleeping");
+                           (state == 'Z') ? "zombie" : (state == 'D') ? "driver" : "sleeping",
+                           message);
         }
         LOG(VERBOSE) << "+closedir()";
     }
@@ -1149,13 +1166,22 @@
     return duration_cast<milliseconds>(llkCheck()).count();
 }
 
+bool llkCheckEng(const std::string& property) {
+    return android::base::GetProperty(property, "eng") == "eng";
+}
+
 bool llkInit(const char* threadname) {
     auto debuggable = android::base::GetBoolProperty("ro.debuggable", false);
     llkLowRam = android::base::GetBoolProperty("ro.config.low_ram", false);
-    if (!LLK_ENABLE_DEFAULT && debuggable) {
-        llkEnable = android::base::GetProperty(LLK_ENABLE_PROPERTY, "eng") == "eng";
-        khtEnable = android::base::GetProperty(KHT_ENABLE_PROPERTY, "eng") == "eng";
+    llkEnableSysrqT &= !llkLowRam;
+    if (debuggable) {
+        llkEnableSysrqT |= llkCheckEng(LLK_ENABLE_SYSRQ_T_PROPERTY);
+        if (!LLK_ENABLE_DEFAULT) {  // NB: default is currently true ...
+            llkEnable |= llkCheckEng(LLK_ENABLE_PROPERTY);
+            khtEnable |= llkCheckEng(KHT_ENABLE_PROPERTY);
+        }
     }
+    llkEnableSysrqT = android::base::GetBoolProperty(LLK_ENABLE_SYSRQ_T_PROPERTY, llkEnableSysrqT);
     llkEnable = android::base::GetBoolProperty(LLK_ENABLE_PROPERTY, llkEnable);
     if (llkEnable && !llkTopDirectory.reset(procdir)) {
         // Most likely reason we could be here is llkd was started
diff --git a/run-as/.clang-format b/run-as/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/run-as/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/run-as/run-as.cpp b/run-as/run-as.cpp
index d005ecf..f49bdf7 100644
--- a/run-as/run-as.cpp
+++ b/run-as/run-as.cpp
@@ -25,6 +25,8 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <string>
+
 #include <libminijail.h>
 #include <scoped_minijail.h>
 
@@ -144,7 +146,7 @@
   // Some devices can disable running run-as, such as Chrome OS when running in
   // non-developer mode.
   if (android::base::GetBoolProperty("ro.boot.disable_runas", false)) {
-      error(1, 0, "run-as is disabled from the kernel commandline");
+    error(1, 0, "run-as is disabled from the kernel commandline");
   }
 
   char* pkgname = argv[1];
@@ -167,6 +169,15 @@
   if (!packagelist_parse(packagelist_parse_callback, &info)) {
     error(1, errno, "packagelist_parse failed");
   }
+
+  // Handle a multi-user data path
+  if (userId > 0) {
+    free(info.data_dir);
+    if (asprintf(&info.data_dir, "/data/user/%d/%s", userId, pkgname) == -1) {
+      error(1, errno, "asprintf failed");
+    }
+  }
+
   if (info.uid == 0) {
     error(1, 0, "unknown package: %s", pkgname);
   }
@@ -205,7 +216,8 @@
   minijail_keep_supplementary_gids(j.get());
   minijail_enter(j.get());
 
-  if (selinux_android_setcontext(uid, 0, info.seinfo, pkgname) < 0) {
+  std::string seinfo = std::string(info.seinfo) + ":fromRunAs";
+  if (selinux_android_setcontext(uid, 0, seinfo.c_str(), pkgname) < 0) {
     error(1, errno, "couldn't set SELinux security context");
   }