Merge "debuggerd_handler: demote abort on exec failure to log."
diff --git a/adb/client/adb_client.cpp b/adb/client/adb_client.cpp
index 9fa827d..5a7bc8d 100644
--- a/adb/client/adb_client.cpp
+++ b/adb/client/adb_client.cpp
@@ -259,7 +259,7 @@
         if (fd >= 0) {
             std::string version_string;
             if (!ReadProtocolString(fd, &version_string, error)) {
-                return -1;
+                return false;
             }
 
             ReadOrderlyShutdown(fd);
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index bb30ae5..43a3e5e 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -295,7 +295,10 @@
                     callback->OnStderr(buffer_ptr, length);
                     break;
                 case ShellProtocol::kIdExit:
-                    exit_code = protocol->data()[0];
+                    // data() returns a char* which doesn't have defined signedness.
+                    // Cast to uint8_t to prevent 255 from being sign extended to INT_MIN,
+                    // which doesn't get truncated on Windows.
+                    exit_code = static_cast<uint8_t>(protocol->data()[0]);
                     continue;
                 default:
                     continue;
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index f4458a2..b42236e 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -115,7 +115,7 @@
 };
 
 struct IoBlock {
-    bool pending;
+    bool pending = false;
     struct iocb control;
     std::shared_ptr<Block> payload;
 
@@ -640,7 +640,10 @@
 }
 
 void usb_init() {
-    bool use_nonblocking = android::base::GetBoolProperty("persist.adb.nonblocking_ffs", true);
+    bool use_nonblocking = android::base::GetBoolProperty(
+            "persist.adb.nonblocking_ffs",
+            android::base::GetBoolProperty("ro.adb.nonblocking_ffs", true));
+
     if (use_nonblocking) {
         std::thread(usb_ffs_open_thread).detach();
     } else {
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 272190e..d26f2d5 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -307,7 +307,7 @@
         return false;
     }
 
-    if (pread(fd, sb, sizeof(*sb), 1024) != sizeof(*sb)) {
+    if (TEMP_FAILURE_RETRY(pread(fd, sb, sizeof(*sb), 1024)) != sizeof(*sb)) {
         PERROR << "Can't read '" << blk_device << "' superblock";
         return false;
     }
@@ -326,6 +326,17 @@
     return true;
 }
 
+// exported silent version of the above that just answer the question is_ext4
+bool fs_mgr_is_ext4(const std::string& blk_device) {
+    android::base::ErrnoRestorer restore;
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(blk_device.c_str(), O_RDONLY | O_CLOEXEC)));
+    if (fd < 0) return false;
+    ext4_super_block sb;
+    if (TEMP_FAILURE_RETRY(pread(fd, &sb, sizeof(sb), 1024)) != sizeof(sb)) return false;
+    if (!is_ext4_superblock_valid(&sb)) return false;
+    return true;
+}
+
 // Some system images do not have tune2fs for licensing reasons.
 // Detect these and skip running it.
 static bool tune2fs_available(void) {
@@ -494,11 +505,12 @@
         return false;
     }
 
-    if (pread(fd, &sb1, sizeof(sb1), F2FS_SUPER_OFFSET) != sizeof(sb1)) {
+    if (TEMP_FAILURE_RETRY(pread(fd, &sb1, sizeof(sb1), F2FS_SUPER_OFFSET)) != sizeof(sb1)) {
         PERROR << "Can't read '" << blk_device << "' superblock1";
         return false;
     }
-    if (pread(fd, &sb2, sizeof(sb2), F2FS_BLKSIZE + F2FS_SUPER_OFFSET) != sizeof(sb2)) {
+    if (TEMP_FAILURE_RETRY(pread(fd, &sb2, sizeof(sb2), F2FS_BLKSIZE + F2FS_SUPER_OFFSET)) !=
+        sizeof(sb2)) {
         PERROR << "Can't read '" << blk_device << "' superblock2";
         return false;
     }
@@ -511,6 +523,23 @@
     return true;
 }
 
+// exported silent version of the above that just answer the question is_f2fs
+bool fs_mgr_is_f2fs(const std::string& blk_device) {
+    android::base::ErrnoRestorer restore;
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(blk_device.c_str(), O_RDONLY | O_CLOEXEC)));
+    if (fd < 0) return false;
+    __le32 sb;
+    if (TEMP_FAILURE_RETRY(pread(fd, &sb, sizeof(sb), F2FS_SUPER_OFFSET)) != sizeof(sb)) {
+        return false;
+    }
+    if (sb == cpu_to_le32(F2FS_SUPER_MAGIC)) return true;
+    if (TEMP_FAILURE_RETRY(pread(fd, &sb, sizeof(sb), F2FS_BLKSIZE + F2FS_SUPER_OFFSET)) !=
+        sizeof(sb)) {
+        return false;
+    }
+    return sb == cpu_to_le32(F2FS_SUPER_MAGIC);
+}
+
 //
 // Prepare the filesystem on the given block device to be mounted.
 //
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index e61f588..dea4844 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -543,6 +543,10 @@
         if (!fs_mgr_rw_access(device_path)) return false;
     }
 
+    auto f2fs = fs_mgr_is_f2fs(device_path);
+    auto ext4 = fs_mgr_is_ext4(device_path);
+    if (!f2fs && !ext4) return false;
+
     if (setfscreatecon(kOverlayfsFileContext)) {
         PERROR << "setfscreatecon " << kOverlayfsFileContext;
     }
@@ -554,6 +558,8 @@
     entry.blk_device = device_path;
     entry.mount_point = kScratchMountPoint;
     entry.fs_type = mnt_type;
+    if ((mnt_type == "f2fs") && !f2fs) entry.fs_type = "ext4";
+    if ((mnt_type == "ext4") && !ext4) entry.fs_type = "f2fs";
     entry.flags = MS_RELATIME;
     if (readonly) {
         entry.flags |= MS_RDONLY;
@@ -563,12 +569,13 @@
     auto save_errno = errno;
     auto mounted = fs_mgr_do_mount_one(entry) == 0;
     if (!mounted) {
-        if (mnt_type == "f2fs") {
+        if ((entry.fs_type == "f2fs") && ext4) {
             entry.fs_type = "ext4";
-        } else {
+            mounted = fs_mgr_do_mount_one(entry) == 0;
+        } else if ((entry.fs_type == "ext4") && f2fs) {
             entry.fs_type = "f2fs";
+            mounted = fs_mgr_do_mount_one(entry) == 0;
         }
-        mounted = fs_mgr_do_mount_one(entry) == 0;
         if (!mounted) save_errno = errno;
     }
     setfscreatecon(nullptr);
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 166c32b..11602ea 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -100,3 +100,6 @@
 const std::string& get_android_dt_dir();
 bool is_dt_compatible();
 int load_verity_state(const android::fs_mgr::FstabEntry& entry, int* mode);
+
+bool fs_mgr_is_ext4(const std::string& blk_device);
+bool fs_mgr_is_f2fs(const std::string& blk_device);
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index ebd6997..27222af 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -591,11 +591,25 @@
         free_regions = PrioritizeSecondHalfOfSuper(free_regions);
     }
 
-    // Find gaps that we can use for new extents. Note we store new extents in a
-    // temporary vector, and only commit them if we are guaranteed enough free
-    // space.
+    // Note we store new extents in a temporary vector, and only commit them
+    // if we are guaranteed enough free space.
     std::vector<std::unique_ptr<LinearExtent>> new_extents;
+
+    // If the last extent in the partition has a size < alignment, then the
+    // difference is unallocatable due to being misaligned. We peek for that
+    // case here to avoid wasting space.
+    if (auto extent = ExtendFinalExtent(partition, free_regions, sectors_needed)) {
+        sectors_needed -= extent->num_sectors();
+        new_extents.emplace_back(std::move(extent));
+    }
+
     for (auto& region : free_regions) {
+        // Note: this comes first, since we may enter the loop not needing any
+        // more sectors.
+        if (!sectors_needed) {
+            break;
+        }
+
         if (region.length() % sectors_per_block != 0) {
             // This should never happen, because it would imply that we
             // once allocated an extent that was not a multiple of the
@@ -613,22 +627,10 @@
         }
 
         uint64_t sectors = std::min(sectors_needed, region.length());
-        if (sectors < region.length()) {
-            const auto& block_device = block_devices_[region.device_index];
-            if (block_device.alignment) {
-                const uint64_t alignment = block_device.alignment / LP_SECTOR_SIZE;
-                sectors = AlignTo(sectors, alignment);
-                sectors = std::min(sectors, region.length());
-            }
-        }
         CHECK(sectors % sectors_per_block == 0);
 
         auto extent = std::make_unique<LinearExtent>(sectors, region.device_index, region.start);
         new_extents.push_back(std::move(extent));
-        if (sectors >= sectors_needed) {
-            sectors_needed = 0;
-            break;
-        }
         sectors_needed -= sectors;
     }
     if (sectors_needed) {
@@ -677,6 +679,64 @@
     return second_half;
 }
 
+std::unique_ptr<LinearExtent> MetadataBuilder::ExtendFinalExtent(
+        Partition* partition, const std::vector<Interval>& free_list,
+        uint64_t sectors_needed) const {
+    if (partition->extents().empty()) {
+        return nullptr;
+    }
+    LinearExtent* extent = partition->extents().back()->AsLinearExtent();
+    if (!extent) {
+        return nullptr;
+    }
+
+    // If the sector ends where the next aligned chunk begins, then there's
+    // no missing gap to try and allocate.
+    const auto& block_device = block_devices_[extent->device_index()];
+    uint64_t next_aligned_sector = AlignSector(block_device, extent->end_sector());
+    if (extent->end_sector() == next_aligned_sector) {
+        return nullptr;
+    }
+
+    uint64_t num_sectors = std::min(next_aligned_sector - extent->end_sector(), sectors_needed);
+    auto new_extent = std::make_unique<LinearExtent>(num_sectors, extent->device_index(),
+                                                     extent->end_sector());
+    if (IsAnyRegionAllocated(*new_extent.get()) ||
+        IsAnyRegionCovered(free_list, *new_extent.get())) {
+        LERROR << "Misaligned region " << new_extent->physical_sector() << ".."
+               << new_extent->end_sector() << " was allocated or marked allocatable.";
+        return nullptr;
+    }
+    return new_extent;
+}
+
+bool MetadataBuilder::IsAnyRegionCovered(const std::vector<Interval>& regions,
+                                         const LinearExtent& candidate) const {
+    for (const auto& region : regions) {
+        if (region.device_index == candidate.device_index() &&
+            (candidate.OwnsSector(region.start) || candidate.OwnsSector(region.end))) {
+            return true;
+        }
+    }
+    return false;
+}
+
+bool MetadataBuilder::IsAnyRegionAllocated(const LinearExtent& candidate) const {
+    for (const auto& partition : partitions_) {
+        for (const auto& extent : partition->extents()) {
+            LinearExtent* linear = extent->AsLinearExtent();
+            if (!linear || linear->device_index() != candidate.device_index()) {
+                continue;
+            }
+            if (linear->OwnsSector(candidate.physical_sector()) ||
+                linear->OwnsSector(candidate.end_sector() - 1)) {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
 void MetadataBuilder::ShrinkPartition(Partition* partition, uint64_t aligned_size) {
     partition->ShrinkTo(aligned_size);
 }
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index 81305b3..46bfe92 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -209,8 +209,8 @@
         ASSERT_TRUE(builder->ResizePartition(a, a->size() + 4096));
         ASSERT_TRUE(builder->ResizePartition(b, b->size() + 4096));
     }
-    EXPECT_EQ(a->size(), 7864320);
-    EXPECT_EQ(b->size(), 7864320);
+    EXPECT_EQ(a->size(), 40960);
+    EXPECT_EQ(b->size(), 40960);
 
     unique_ptr<LpMetadata> exported = builder->Export();
     ASSERT_NE(exported, nullptr);
@@ -218,7 +218,7 @@
     // Check that each starting sector is aligned.
     for (const auto& extent : exported->extents) {
         ASSERT_EQ(extent.target_type, LP_TARGET_TYPE_LINEAR);
-        EXPECT_EQ(extent.num_sectors, 1536);
+        EXPECT_EQ(extent.num_sectors, 80);
 
         uint64_t lba = extent.target_data * LP_SECTOR_SIZE;
         uint64_t aligned_lba = AlignTo(lba, device_info.alignment, device_info.alignment_offset);
@@ -226,7 +226,7 @@
     }
 
     // Sanity check one extent.
-    EXPECT_EQ(exported->extents.back().target_data, 30656);
+    EXPECT_EQ(exported->extents.back().target_data, 3008);
 }
 
 TEST_F(BuilderTest, UseAllDiskSpace) {
@@ -698,7 +698,7 @@
     EXPECT_EQ(metadata->extents[1].target_type, LP_TARGET_TYPE_LINEAR);
     EXPECT_EQ(metadata->extents[1].target_data, 1472);
     EXPECT_EQ(metadata->extents[1].target_source, 1);
-    EXPECT_EQ(metadata->extents[2].num_sectors, 129600);
+    EXPECT_EQ(metadata->extents[2].num_sectors, 129088);
     EXPECT_EQ(metadata->extents[2].target_type, LP_TARGET_TYPE_LINEAR);
     EXPECT_EQ(metadata->extents[2].target_data, 1472);
     EXPECT_EQ(metadata->extents[2].target_source, 2);
@@ -805,7 +805,7 @@
     EXPECT_EQ(exported->extents[0].target_data, 10487808);
     EXPECT_EQ(exported->extents[0].num_sectors, 10483712);
     EXPECT_EQ(exported->extents[1].target_data, 6292992);
-    EXPECT_EQ(exported->extents[1].num_sectors, 2099712);
+    EXPECT_EQ(exported->extents[1].num_sectors, 2099200);
     EXPECT_EQ(exported->extents[2].target_data, 1536);
     EXPECT_EQ(exported->extents[2].num_sectors, 6291456);
 }
@@ -821,7 +821,7 @@
     ASSERT_NE(vendor, nullptr);
     ASSERT_TRUE(builder->ResizePartition(system, device_info.alignment + 4096));
     ASSERT_TRUE(builder->ResizePartition(vendor, device_info.alignment));
-    ASSERT_EQ(system->size(), device_info.alignment * 2);
+    ASSERT_EQ(system->size(), device_info.alignment + 4096);
     ASSERT_EQ(vendor->size(), device_info.alignment);
 
     ASSERT_TRUE(builder->ResizePartition(system, device_info.alignment * 2));
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index 486a71f..c706f2a 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -66,6 +66,10 @@
     uint64_t end_sector() const { return physical_sector_ + num_sectors_; }
     uint32_t device_index() const { return device_index_; }
 
+    bool OwnsSector(uint64_t sector) const {
+        return sector >= physical_sector_ && sector < end_sector();
+    }
+
   private:
     uint32_t device_index_;
     uint64_t physical_sector_;
@@ -322,9 +326,15 @@
         }
     };
     std::vector<Interval> GetFreeRegions() const;
+    bool IsAnyRegionCovered(const std::vector<Interval>& regions,
+                            const LinearExtent& candidate) const;
+    bool IsAnyRegionAllocated(const LinearExtent& candidate) const;
     void ExtentsToFreeList(const std::vector<Interval>& extents,
                            std::vector<Interval>* free_regions) const;
     std::vector<Interval> PrioritizeSecondHalfOfSuper(const std::vector<Interval>& free_list);
+    std::unique_ptr<LinearExtent> ExtendFinalExtent(Partition* partition,
+                                                    const std::vector<Interval>& free_list,
+                                                    uint64_t sectors_needed) const;
 
     static bool sABOverrideValue;
     static bool sABOverrideSet;
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index bd5a4fe..ec6eb52 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -951,7 +951,7 @@
 
 adb_reboot &&
   adb_wait 2m ||
-  die "reboot after override content added failed"
+  die "reboot after override content added failed `usb_status`"
 
 if ${overlayfs_needed}; then
   D=`adb_su df -k </dev/null` &&
diff --git a/init/Android.bp b/init/Android.bp
index 9aeb837..8a0bb55 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -110,6 +110,7 @@
         "init.cpp",
         "keychords.cpp",
         "modalias_handler.cpp",
+        "mount_handler.cpp",
         "mount_namespace.cpp",
         "parser.cpp",
         "persistent_properties.cpp",
diff --git a/init/README.md b/init/README.md
index f0e5d55..b2039b4 100644
--- a/init/README.md
+++ b/init/README.md
@@ -660,12 +660,19 @@
 
 Properties
 ----------
-Init provides information about the services that it is responsible
-for via the below properties.
+Init provides state information with the following properties.
 
 `init.svc.<name>`
 > State of a named service ("stopped", "stopping", "running", "restarting")
 
+`dev.mnt.blk.<mount_point>`
+> Block device base name associated with a *mount_point*.
+  The *mount_point* has / replaced by . and if referencing the root mount point
+  "/", it will use "/root", specifically `dev.mnt.blk.root`.
+  Meant for references to `/sys/device/block/${dev.mnt.blk.<mount_point>}/` and
+  `/sys/fs/ext4/${dev.mnt.blk.<mount_point>}/` to tune the block device
+  characteristics in a device agnostic manner.
+
 
 Boot timing
 -----------
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 17cd470..4b0f05d 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -580,7 +580,14 @@
             required_devices_partition_names_.emplace(basename(device.c_str()));
             auto uevent_callback = [this](const Uevent& uevent) { return UeventCallback(uevent); };
             uevent_listener_.RegenerateUevents(uevent_callback);
-            uevent_listener_.Poll(uevent_callback, 10s);
+            if (!required_devices_partition_names_.empty()) {
+                uevent_listener_.Poll(uevent_callback, 10s);
+                if (!required_devices_partition_names_.empty()) {
+                    LOG(ERROR) << __PRETTY_FUNCTION__
+                               << ": partition(s) not found after polling timeout: "
+                               << android::base::Join(required_devices_partition_names_, ", ");
+                }
+            }
         } else {
             InitMappedDevice(device);
         }
diff --git a/init/init.cpp b/init/init.cpp
index 7182fda..a5f4549 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -57,6 +57,7 @@
 #include "first_stage_mount.h"
 #include "import_parser.h"
 #include "keychords.h"
+#include "mount_handler.h"
 #include "mount_namespace.h"
 #include "property_service.h"
 #include "reboot.h"
@@ -686,6 +687,7 @@
     fs_mgr_vendor_overlay_mount_all();
     export_oem_lock_status();
     StartPropertyService(&epoll);
+    MountHandler mount_handler(&epoll);
     set_usb_controller();
 
     const BuiltinFunctionMap function_map;
diff --git a/init/mount_handler.cpp b/init/mount_handler.cpp
new file mode 100644
index 0000000..12dfc6d
--- /dev/null
+++ b/init/mount_handler.cpp
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "mount_handler.h"
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <string>
+#include <utility>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <fs_mgr.h>
+#include <fstab/fstab.h>
+
+#include "epoll.h"
+#include "property_service.h"
+
+namespace android {
+namespace init {
+
+namespace {
+
+MountHandlerEntry ParseMount(const std::string& line) {
+    auto fields = android::base::Split(line, " ");
+    while (fields.size() < 3) fields.emplace_back("");
+    if (fields[0] == "/dev/root") {
+        if (android::fs_mgr::Fstab fstab; android::fs_mgr::ReadDefaultFstab(&fstab)) {
+            if (auto entry = GetEntryForMountPoint(&fstab, "/")) {
+                fields[0] = entry->blk_device;
+            }
+        }
+    }
+    if (android::base::StartsWith(fields[0], "/dev/")) {
+        if (std::string link; android::base::Readlink(fields[0], &link)) {
+            fields[0] = link;
+        }
+    }
+    return MountHandlerEntry(fields[0], fields[1], fields[2]);
+}
+
+void SetMountProperty(const MountHandlerEntry& entry, bool add) {
+    static constexpr char devblock[] = "/dev/block/";
+    if (!android::base::StartsWith(entry.blk_device, devblock)) return;
+    std::string value;
+    if (add) {
+        value = entry.blk_device.substr(strlen(devblock));
+        if (android::base::StartsWith(value, "sd")) {
+            // All sd partitions inherit their queue characteristics
+            // from the whole device reference.  Strip partition number.
+            auto it = std::find_if(value.begin(), value.end(), [](char c) { return isdigit(c); });
+            if (it != value.end()) value.erase(it, value.end());
+        }
+        auto queue = "/sys/block/" + value + "/queue";
+        struct stat sb;
+        if (stat(queue.c_str(), &sb) || !S_ISDIR(sb.st_mode)) value = "";
+        if (stat(entry.mount_point.c_str(), &sb) || !S_ISDIR(sb.st_mode)) value = "";
+        // Skip the noise associated with APEX until there is a need
+        if (android::base::StartsWith(value, "loop")) value = "";
+    }
+    std::string property =
+            "dev.mnt.blk" + ((entry.mount_point == "/") ? "/root" : entry.mount_point);
+    std::replace(property.begin(), property.end(), '/', '.');
+    if (value.empty() && android::base::GetProperty(property, "").empty()) return;
+    property_set(property, value);
+}
+
+}  // namespace
+
+MountHandlerEntry::MountHandlerEntry(const std::string& blk_device, const std::string& mount_point,
+                                     const std::string& fs_type)
+    : blk_device(blk_device), mount_point(mount_point), fs_type(fs_type) {}
+
+bool MountHandlerEntry::operator<(const MountHandlerEntry& r) const {
+    if (blk_device < r.blk_device) return true;
+    if (blk_device > r.blk_device) return false;
+    if (mount_point < r.mount_point) return true;
+    if (mount_point > r.mount_point) return false;
+    return fs_type < r.fs_type;
+}
+
+MountHandler::MountHandler(Epoll* epoll) : epoll_(epoll), fp_(fopen("/proc/mounts", "re"), fclose) {
+    if (!fp_) PLOG(FATAL) << "Could not open /proc/mounts";
+    auto result = epoll->RegisterHandler(
+            fileno(fp_.get()), [this]() { this->MountHandlerFunction(); }, EPOLLERR | EPOLLPRI);
+    if (!result) LOG(FATAL) << result.error();
+}
+
+MountHandler::~MountHandler() {
+    if (fp_) epoll_->UnregisterHandler(fileno(fp_.get())).IgnoreError();
+}
+
+void MountHandler::MountHandlerFunction() {
+    rewind(fp_.get());
+    char* buf = nullptr;
+    size_t len = 0;
+    auto untouched = mounts_;
+    while (getline(&buf, &len, fp_.get()) != -1) {
+        auto entry = ParseMount(std::string(buf, len));
+        auto match = untouched.find(entry);
+        if (match == untouched.end()) {
+            SetMountProperty(entry, true);
+            mounts_.emplace(std::move(entry));
+        } else {
+            untouched.erase(match);
+        }
+    }
+    free(buf);
+    for (auto entry : untouched) {
+        auto match = mounts_.find(entry);
+        if (match == mounts_.end()) continue;
+        mounts_.erase(match);
+        SetMountProperty(entry, false);
+    }
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/mount_handler.h b/init/mount_handler.h
new file mode 100644
index 0000000..e524a74
--- /dev/null
+++ b/init/mount_handler.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdio.h>
+
+#include <memory>
+#include <set>
+#include <string>
+
+#include "epoll.h"
+
+namespace android {
+namespace init {
+
+struct MountHandlerEntry {
+    MountHandlerEntry(const std::string& blk_device, const std::string& mount_point,
+                      const std::string& fs_type);
+
+    bool operator<(const MountHandlerEntry& r) const;
+
+    const std::string blk_device;
+    const std::string mount_point;
+    const std::string fs_type;
+};
+
+class MountHandler {
+  public:
+    explicit MountHandler(Epoll* epoll);
+    MountHandler(const MountHandler&) = delete;
+    MountHandler(MountHandler&&) = delete;
+    MountHandler& operator=(const MountHandler&) = delete;
+    MountHandler& operator=(MountHandler&&) = delete;
+    ~MountHandler();
+
+  private:
+    void MountHandlerFunction();
+
+    Epoll* epoll_;
+    std::unique_ptr<FILE, decltype(&fclose)> fp_;
+    std::set<MountHandlerEntry> mounts_;
+};
+
+}  // namespace init
+}  // namespace android
diff --git a/libkeyutils/mini_keyctl.cpp b/libkeyutils/mini_keyctl.cpp
index 4fe4c3c..e09c864 100644
--- a/libkeyutils/mini_keyctl.cpp
+++ b/libkeyutils/mini_keyctl.cpp
@@ -20,8 +20,12 @@
 
 #include "mini_keyctl_utils.h"
 
+#include <error.h>
+#include <stdio.h>
 #include <unistd.h>
 
+#include <android-base/parseint.h>
+
 static void Usage(int exit_code) {
   fprintf(stderr, "usage: mini-keyctl <action> [args,]\n");
   fprintf(stderr, "       mini-keyctl add <type> <desc> <data> <keyring>\n");
@@ -29,9 +33,18 @@
   fprintf(stderr, "       mini-keyctl dadd <type> <desc_prefix> <cert_dir> <keyring>\n");
   fprintf(stderr, "       mini-keyctl unlink <key> <keyring>\n");
   fprintf(stderr, "       mini-keyctl restrict_keyring <keyring>\n");
+  fprintf(stderr, "       mini-keyctl security <key>\n");
   _exit(exit_code);
 }
 
+static key_serial_t parseKeyOrDie(const char* str) {
+  key_serial_t key;
+  if (!android::base::ParseInt(str, &key)) {
+    error(1 /* exit code */, 0 /* errno */, "Unparsable key: '%s'\n", str);
+  }
+  return key;
+}
+
 int main(int argc, const char** argv) {
   if (argc < 2) Usage(1);
   const std::string action = argv[1];
@@ -63,10 +76,22 @@
     return RestrictKeyring(keyring);
   } else if (action == "unlink") {
     if (argc != 4) Usage(1);
-    key_serial_t key = std::stoi(argv[2], nullptr, 16);
+    key_serial_t key = parseKeyOrDie(argv[2]);
     const std::string keyring = argv[3];
     return Unlink(key, keyring);
+  } else if (action == "security") {
+    if (argc != 3) Usage(1);
+    const char* key_str = argv[2];
+    key_serial_t key = parseKeyOrDie(key_str);
+    std::string context = RetrieveSecurityContext(key);
+    if (context.empty()) {
+      perror(key_str);
+      return 1;
+    }
+    fprintf(stderr, "%s\n", context.c_str());
+    return 0;
   } else {
+    fprintf(stderr, "Unrecognized action: %s\n", action.c_str());
     Usage(1);
   }
 
diff --git a/libkeyutils/mini_keyctl_utils.cpp b/libkeyutils/mini_keyctl_utils.cpp
index c4fc96c..9fe2dfe 100644
--- a/libkeyutils/mini_keyctl_utils.cpp
+++ b/libkeyutils/mini_keyctl_utils.cpp
@@ -30,18 +30,62 @@
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/parseint.h>
 #include <android-base/properties.h>
 #include <android-base/strings.h>
 #include <keyutils.h>
 
 static constexpr int kMaxCertSize = 4096;
 
-std::vector<std::string> SplitBySpace(const std::string& s) {
+static std::vector<std::string> SplitBySpace(const std::string& s) {
   std::istringstream iss(s);
   return std::vector<std::string>{std::istream_iterator<std::string>{iss},
                                   std::istream_iterator<std::string>{}};
 }
 
+// Find the keyring id. Because request_key(2) syscall is not available or the key is
+// kernel keyring, the id is looked up from /proc/keys. The keyring description may contain other
+// information in the descritption section depending on the key type, only the first word in the
+// keyring description is used for searching.
+static bool GetKeyringId(const std::string& keyring_desc, key_serial_t* keyring_id) {
+  if (!keyring_id) {
+    LOG(ERROR) << "keyring_id is null";
+    return false;
+  }
+
+  // If the keyring id is already a hex number, directly convert it to keyring id
+  if (android::base::ParseInt(keyring_desc.c_str(), keyring_id)) {
+    return true;
+  }
+
+  // Only keys allowed by SELinux rules will be shown here.
+  std::ifstream proc_keys_file("/proc/keys");
+  if (!proc_keys_file.is_open()) {
+    PLOG(ERROR) << "Failed to open /proc/keys";
+    return false;
+  }
+
+  std::string line;
+  while (getline(proc_keys_file, line)) {
+    std::vector<std::string> tokens = SplitBySpace(line);
+    if (tokens.size() < 9) {
+      continue;
+    }
+    std::string key_id = tokens[0];
+    std::string key_type = tokens[7];
+    // The key description may contain space.
+    std::string key_desc_prefix = tokens[8];
+    // The prefix has a ":" at the end
+    std::string key_desc_pattern = keyring_desc + ":";
+    if (key_type != "keyring" || key_desc_prefix != key_desc_pattern) {
+      continue;
+    }
+    *keyring_id = std::stoi(key_id, nullptr, 16);
+    return true;
+  }
+  return false;
+}
+
 int AddCertsFromDir(const std::string& type, const std::string& desc_prefix,
                     const std::string& cert_dir, const std::string& keyring) {
   key_serial_t keyring_id;
@@ -89,49 +133,6 @@
   return 0;
 }
 
-bool GetKeyringId(const std::string& keyring_desc, key_serial_t* keyring_id) {
-  if (!keyring_id) {
-    LOG(ERROR) << "keyring_id is null";
-    return false;
-  }
-
-  // If the keyring id is already a hex number, directly convert it to keyring id
-  try {
-    key_serial_t id = std::stoi(keyring_desc, nullptr, 16);
-    *keyring_id = id;
-    return true;
-  } catch (const std::exception& e) {
-    LOG(INFO) << "search /proc/keys for keyring id";
-  }
-
-  // Only keys allowed by SELinux rules will be shown here.
-  std::ifstream proc_keys_file("/proc/keys");
-  if (!proc_keys_file.is_open()) {
-    PLOG(ERROR) << "Failed to open /proc/keys";
-    return false;
-  }
-
-  std::string line;
-  while (getline(proc_keys_file, line)) {
-    std::vector<std::string> tokens = SplitBySpace(line);
-    if (tokens.size() < 9) {
-      continue;
-    }
-    std::string key_id = tokens[0];
-    std::string key_type = tokens[7];
-    // The key description may contain space.
-    std::string key_desc_prefix = tokens[8];
-    // The prefix has a ":" at the end
-    std::string key_desc_pattern = keyring_desc + ":";
-    if (key_type != "keyring" || key_desc_prefix != key_desc_pattern) {
-      continue;
-    }
-    *keyring_id = std::stoi(key_id, nullptr, 16);
-    return true;
-  }
-  return false;
-}
-
 int Unlink(key_serial_t key, const std::string& keyring) {
   key_serial_t keyring_id;
   if (!GetKeyringId(keyring, &keyring_id)) {
@@ -210,3 +211,21 @@
   }
   return 0;
 }
+
+std::string RetrieveSecurityContext(key_serial_t key) {
+  // Simply assume this size is enough in practice.
+  const int kMaxSupportedSize = 256;
+  std::string context;
+  context.resize(kMaxSupportedSize);
+  long retval = keyctl_get_security(key, context.data(), kMaxSupportedSize);
+  if (retval < 0) {
+    PLOG(ERROR) << "Cannot get security context of key 0x" << std::hex << key;
+    return std::string();
+  }
+  if (retval > kMaxSupportedSize) {
+    LOG(ERROR) << "The key has unexpectedly long security context than " << kMaxSupportedSize;
+    return std::string();
+  }
+  context.resize(retval);
+  return context;
+}
diff --git a/libkeyutils/mini_keyctl_utils.h b/libkeyutils/mini_keyctl_utils.h
index 3c69611..804a357 100644
--- a/libkeyutils/mini_keyctl_utils.h
+++ b/libkeyutils/mini_keyctl_utils.h
@@ -23,11 +23,6 @@
 int AddCertsFromDir(const std::string& type, const std::string& desc_prefix,
                     const std::string& cert_dir, const std::string& keyring);
 
-// Add all the certs from directory path to keyring with keyring_id. Returns the number of keys
-// added. Returns non-zero if any error happens.
-int AddKeys(const std::string& path, const key_serial_t keyring_id, const std::string& type,
-            const std::string& desc, int start_index);
-
 // Add key to a keyring. Returns non-zero if error happens.
 int Add(const std::string& type, const std::string& desc, const std::string& data,
         const std::string& keyring);
@@ -41,8 +36,5 @@
 // Apply key-linking to a keyring. Return non-zero if error happens.
 int RestrictKeyring(const std::string& keyring);
 
-// Find the keyring id. Because request_key(2) syscall is not available or the key is
-// kernel keyring, the id is looked up from /proc/keys. The keyring description may contain other
-// information in the descritption section depending on the key type, only the first word in the
-// keyring description is used for searching.
-bool GetKeyringId(const std::string& keyring_desc, key_serial_t* keyring_id);
+// Retrieves a key's security context. Return the context string, or empty string on error.
+std::string RetrieveSecurityContext(key_serial_t key);
diff --git a/liblog/logger_write.cpp b/liblog/logger_write.cpp
index 908fe7f..7fa3f43 100644
--- a/liblog/logger_write.cpp
+++ b/liblog/logger_write.cpp
@@ -413,6 +413,8 @@
   if (!tag) tag = "";
 
   /* XXX: This needs to go! */
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wstring-plus-int"
   if (bufID != LOG_ID_RADIO) {
     switch (tag[0]) {
       case 'H':
@@ -454,6 +456,7 @@
         break;
     }
   }
+#pragma clang diagnostic pop
 
 #if __BIONIC__
   if (prio == ANDROID_LOG_FATAL) {
diff --git a/libprocessgroup/profiles/Android.bp b/libprocessgroup/profiles/Android.bp
index 15d0172..e05a690 100644
--- a/libprocessgroup/profiles/Android.bp
+++ b/libprocessgroup/profiles/Android.bp
@@ -29,9 +29,21 @@
     src: "task_profiles.json",
 }
 
+cc_defaults {
+    name: "libprocessgroup_test_defaults",
+    cflags: [
+        "-Wall",
+        "-Werror",
+
+        // Needed for headers from libprotobuf.
+        "-Wno-unused-parameter",
+    ],
+}
+
 cc_library_static {
     name: "libprocessgroup_proto",
     host_supported: true,
+    defaults: ["libprocessgroup_test_defaults"],
     srcs: [
         "cgroups.proto",
         "task_profiles.proto",
@@ -40,15 +52,11 @@
         type: "full",
         export_proto_headers: true,
     },
-    cflags: [
-        "-Wall",
-        "-Werror",
-        "-Wno-unused-parameter",
-    ],
 }
 
 cc_test_host {
     name: "libprocessgroup_proto_test",
+    defaults: ["libprocessgroup_test_defaults"],
     srcs: [
         "test.cpp",
     ],
@@ -64,11 +72,6 @@
     shared_libs: [
         "libprotobuf-cpp-full",
     ],
-    cflags: [
-        "-Wall",
-        "-Werror",
-        "-Wno-unused-parameter",
-    ],
     data: [
         "cgroups.json",
         "cgroups.recovery.json",
@@ -78,3 +81,28 @@
         "general-tests",
     ],
 }
+
+cc_test {
+    name: "vts_processgroup_validate_test",
+    defaults: ["libprocessgroup_test_defaults"],
+    srcs: [
+        "test_vendor.cpp",
+    ],
+    static_libs: [
+        "libgmock",
+        "libjsonpbverify",
+        "libjsonpbparse",
+        "libprocessgroup_proto",
+    ],
+    shared_libs: [
+        "libbase",
+        "liblog",
+        "libjsoncpp",
+        "libprotobuf-cpp-full",
+    ],
+    target: {
+        android: {
+            test_config: "vts_processgroup_validate_test.xml",
+        },
+    },
+}
diff --git a/libprocessgroup/profiles/Android.mk b/libprocessgroup/profiles/Android.mk
new file mode 100644
index 0000000..eab96d4
--- /dev/null
+++ b/libprocessgroup/profiles/Android.mk
@@ -0,0 +1,21 @@
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := VtsProcessgroupValidateTest
+-include test/vts/tools/build/Android.host_config.mk
diff --git a/libprocessgroup/profiles/cgroups_test.h b/libprocessgroup/profiles/cgroups_test.h
new file mode 100644
index 0000000..1309957
--- /dev/null
+++ b/libprocessgroup/profiles/cgroups_test.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <gmock/gmock.h>
+#include <jsonpb/json_schema_test.h>
+
+#include "cgroups.pb.h"
+
+using ::testing::MatchesRegex;
+
+namespace android {
+namespace profiles {
+
+class CgroupsTest : public jsonpb::JsonSchemaTest {
+  public:
+    void SetUp() override {
+        JsonSchemaTest::SetUp();
+        cgroups_ = static_cast<Cgroups*>(message());
+    }
+    Cgroups* cgroups_;
+};
+
+TEST_P(CgroupsTest, CgroupRequiredFields) {
+    for (int i = 0; i < cgroups_->cgroups_size(); ++i) {
+        auto&& cgroup = cgroups_->cgroups(i);
+        EXPECT_FALSE(cgroup.controller().empty())
+                << "No controller name for cgroup #" << i << " in " << file_path_;
+        EXPECT_FALSE(cgroup.path().empty()) << "No path for cgroup #" << i << " in " << file_path_;
+    }
+}
+
+TEST_P(CgroupsTest, Cgroup2RequiredFields) {
+    if (cgroups_->has_cgroups2()) {
+        EXPECT_FALSE(cgroups_->cgroups2().path().empty())
+                << "No path for cgroup2 in " << file_path_;
+    }
+}
+
+// "Mode" field must be in the format of "0xxx".
+static inline constexpr const char* REGEX_MODE = "(0[0-7]{3})?";
+TEST_P(CgroupsTest, CgroupMode) {
+    for (int i = 0; i < cgroups_->cgroups_size(); ++i) {
+        EXPECT_THAT(cgroups_->cgroups(i).mode(), MatchesRegex(REGEX_MODE))
+                << "For cgroup controller #" << i << " in " << file_path_;
+    }
+}
+
+TEST_P(CgroupsTest, Cgroup2Mode) {
+    EXPECT_THAT(cgroups_->cgroups2().mode(), MatchesRegex(REGEX_MODE))
+            << "For cgroups2 in " << file_path_;
+}
+
+}  // namespace profiles
+}  // namespace android
diff --git a/libprocessgroup/profiles/task_profiles_test.h b/libprocessgroup/profiles/task_profiles_test.h
new file mode 100644
index 0000000..32f122d
--- /dev/null
+++ b/libprocessgroup/profiles/task_profiles_test.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <gmock/gmock.h>
+#include <jsonpb/json_schema_test.h>
+
+#include "task_profiles.pb.h"
+
+namespace android {
+namespace profiles {
+
+class TaskProfilesTest : public jsonpb::JsonSchemaTest {
+  public:
+    void SetUp() override {
+        JsonSchemaTest::SetUp();
+        task_profiles_ = static_cast<TaskProfiles*>(message());
+    }
+    TaskProfiles* task_profiles_;
+};
+
+TEST_P(TaskProfilesTest, AttributeRequiredFields) {
+    for (int i = 0; i < task_profiles_->attributes_size(); ++i) {
+        auto&& attribute = task_profiles_->attributes(i);
+        EXPECT_FALSE(attribute.name().empty())
+                << "No name for attribute #" << i << " in " << file_path_;
+        EXPECT_FALSE(attribute.controller().empty())
+                << "No controller for attribute #" << i << " in " << file_path_;
+        EXPECT_FALSE(attribute.file().empty())
+                << "No file for attribute #" << i << " in " << file_path_;
+    }
+}
+
+TEST_P(TaskProfilesTest, ProfileRequiredFields) {
+    for (int profile_idx = 0; profile_idx < task_profiles_->profiles_size(); ++profile_idx) {
+        auto&& profile = task_profiles_->profiles(profile_idx);
+        EXPECT_FALSE(profile.name().empty())
+                << "No name for profile #" << profile_idx << " in " << file_path_;
+        for (int action_idx = 0; action_idx < profile.actions_size(); ++action_idx) {
+            auto&& action = profile.actions(action_idx);
+            EXPECT_FALSE(action.name().empty())
+                    << "No name for profiles[" << profile_idx << "].actions[" << action_idx
+                    << "] in " << file_path_;
+        }
+    }
+}
+
+}  // namespace profiles
+}  // namespace android
diff --git a/libprocessgroup/profiles/test.cpp b/libprocessgroup/profiles/test.cpp
index 8ba14d6..bc9aade 100644
--- a/libprocessgroup/profiles/test.cpp
+++ b/libprocessgroup/profiles/test.cpp
@@ -14,18 +14,15 @@
  * limitations under the License.
  */
 
-#include <string>
-
 #include <android-base/file.h>
-#include <gmock/gmock.h>
+#include <gtest/gtest.h>
 #include <jsonpb/json_schema_test.h>
 
-#include "cgroups.pb.h"
-#include "task_profiles.pb.h"
+#include "cgroups_test.h"
+#include "task_profiles_test.h"
 
 using namespace ::android::jsonpb;
 using ::android::base::GetExecutableDirectory;
-using ::testing::MatchesRegex;
 
 namespace android {
 namespace profiles {
@@ -35,87 +32,7 @@
     return jsonpb::MakeTestParam<T>(GetExecutableDirectory() + path);
 }
 
-TEST(LibProcessgroupProto, EmptyMode) {
-    EXPECT_EQ(0, strtoul("", nullptr, 8))
-            << "Empty mode string cannot be silently converted to 0; this should not happen";
-}
-
-class CgroupsTest : public JsonSchemaTest {
-  public:
-    void SetUp() override {
-        JsonSchemaTest::SetUp();
-        cgroups_ = static_cast<Cgroups*>(message());
-    }
-    Cgroups* cgroups_;
-};
-
-TEST_P(CgroupsTest, CgroupRequiredFields) {
-    for (int i = 0; i < cgroups_->cgroups_size(); ++i) {
-        auto&& cgroup = cgroups_->cgroups(i);
-        EXPECT_FALSE(cgroup.controller().empty())
-                << "No controller name for cgroup #" << i << " in " << file_path_;
-        EXPECT_FALSE(cgroup.path().empty()) << "No path for cgroup #" << i << " in " << file_path_;
-    }
-}
-
-TEST_P(CgroupsTest, Cgroup2RequiredFields) {
-    if (cgroups_->has_cgroups2()) {
-        EXPECT_FALSE(cgroups_->cgroups2().path().empty())
-                << "No path for cgroup2 in " << file_path_;
-    }
-}
-
-// "Mode" field must be in the format of "0xxx".
-static constexpr const char* REGEX_MODE = "(0[0-7]{3})?";
-TEST_P(CgroupsTest, CgroupMode) {
-    for (int i = 0; i < cgroups_->cgroups_size(); ++i) {
-        EXPECT_THAT(cgroups_->cgroups(i).mode(), MatchesRegex(REGEX_MODE))
-                << "For cgroup controller #" << i << " in " << file_path_;
-    }
-}
-
-TEST_P(CgroupsTest, Cgroup2Mode) {
-    EXPECT_THAT(cgroups_->cgroups2().mode(), MatchesRegex(REGEX_MODE))
-            << "For cgroups2 in " << file_path_;
-}
-
-class TaskProfilesTest : public JsonSchemaTest {
-  public:
-    void SetUp() override {
-        JsonSchemaTest::SetUp();
-        task_profiles_ = static_cast<TaskProfiles*>(message());
-    }
-    TaskProfiles* task_profiles_;
-};
-
-TEST_P(TaskProfilesTest, AttributeRequiredFields) {
-    for (int i = 0; i < task_profiles_->attributes_size(); ++i) {
-        auto&& attribute = task_profiles_->attributes(i);
-        EXPECT_FALSE(attribute.name().empty())
-                << "No name for attribute #" << i << " in " << file_path_;
-        EXPECT_FALSE(attribute.controller().empty())
-                << "No controller for attribute #" << i << " in " << file_path_;
-        EXPECT_FALSE(attribute.file().empty())
-                << "No file for attribute #" << i << " in " << file_path_;
-    }
-}
-
-TEST_P(TaskProfilesTest, ProfileRequiredFields) {
-    for (int profile_idx = 0; profile_idx < task_profiles_->profiles_size(); ++profile_idx) {
-        auto&& profile = task_profiles_->profiles(profile_idx);
-        EXPECT_FALSE(profile.name().empty())
-                << "No name for profile #" << profile_idx << " in " << file_path_;
-        for (int action_idx = 0; action_idx < profile.actions_size(); ++action_idx) {
-            auto&& action = profile.actions(action_idx);
-            EXPECT_FALSE(action.name().empty())
-                    << "No name for profiles[" << profile_idx << "].actions[" << action_idx
-                    << "] in " << file_path_;
-        }
-    }
-}
-
 // Test suite instantiations
-
 INSTANTIATE_TEST_SUITE_P(, JsonSchemaTest,
                          ::testing::Values(MakeTestParam<Cgroups>("/cgroups.json"),
                                            MakeTestParam<Cgroups>("/cgroups.recovery.json"),
diff --git a/libprocessgroup/profiles/test_vendor.cpp b/libprocessgroup/profiles/test_vendor.cpp
new file mode 100644
index 0000000..3ec7fcf
--- /dev/null
+++ b/libprocessgroup/profiles/test_vendor.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/file.h>
+#include <gtest/gtest.h>
+#include <jsonpb/json_schema_test.h>
+
+#include "cgroups_test.h"
+#include "task_profiles_test.h"
+
+using ::android::base::GetExecutableDirectory;
+using namespace ::android::jsonpb;
+
+namespace android {
+namespace profiles {
+
+static constexpr const char* kVendorCgroups = "/vendor/etc/cgroups.json";
+static constexpr const char* kVendorTaskProfiles = "/vendor/etc/task_profiles.json";
+
+template <typename T>
+class TestConfig : public JsonSchemaTestConfig {
+  public:
+    TestConfig(const std::string& path) : file_path_(path){};
+    std::unique_ptr<google::protobuf::Message> CreateMessage() const override {
+        return std::make_unique<T>();
+    }
+    std::string file_path() const override { return file_path_; }
+    bool optional() const override {
+        // Ignore when vendor JSON files are missing.
+        return true;
+    }
+
+  private:
+    std::string file_path_;
+};
+
+template <typename T>
+JsonSchemaTestConfigFactory MakeTestParam(const std::string& path) {
+    return [path]() { return std::make_unique<TestConfig<T>>(path); };
+}
+
+INSTANTIATE_TEST_SUITE_P(VendorCgroups, JsonSchemaTest,
+                         ::testing::Values(MakeTestParam<Cgroups>(kVendorCgroups)));
+INSTANTIATE_TEST_SUITE_P(VendorCgroups, CgroupsTest,
+                         ::testing::Values(MakeTestParam<Cgroups>(kVendorCgroups)));
+
+INSTANTIATE_TEST_SUITE_P(VendorTaskProfiles, JsonSchemaTest,
+                         ::testing::Values(MakeTestParam<TaskProfiles>(kVendorTaskProfiles)));
+INSTANTIATE_TEST_SUITE_P(VendorTaskProfiles, TaskProfilesTest,
+                         ::testing::Values(MakeTestParam<TaskProfiles>(kVendorTaskProfiles)));
+
+}  // namespace profiles
+}  // namespace android
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
diff --git a/libprocessgroup/profiles/vts_processgroup_validate_test.xml b/libprocessgroup/profiles/vts_processgroup_validate_test.xml
new file mode 100644
index 0000000..21d29cd
--- /dev/null
+++ b/libprocessgroup/profiles/vts_processgroup_validate_test.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+          http://www.apache.org/licenses/LICENSE-2.0
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Config for VtsProcessgroupValidateTest">
+    <option name="config-descriptor:metadata" key="plan" value="vts-treble" />
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.VtsFilePusher">
+        <option name="abort-on-push-failure" value="false"/>
+        <option name="push-group" value="HostDrivenTest.push"/>
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.VtsMultiDeviceTest">
+        <option name="test-module-name" value="VtsProcessgroupValidateTest"/>
+        <option name="binary-test-working-directory" value="_32bit::/data/nativetest/" />
+        <option name="binary-test-working-directory" value="_64bit::/data/nativetest64/" />
+        <option name="binary-test-source" value="_32bit::DATA/nativetest/vts_processgroup_validate_test/vts_processgroup_validate_test" />
+        <option name="binary-test-source" value="_64bit::DATA/nativetest64/vts_processgroup_validate_test/vts_processgroup_validate_test" />
+        <option name="binary-test-type" value="gtest"/>
+        <option name="binary-test-disable-framework" value="false"/>
+        <option name="test-timeout" value="30s"/>
+    </test>
+</configuration>
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index e0b291d..54b019e 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -220,10 +220,10 @@
 # A symlink can't overwrite a directory and the /system/usr/icu directory once
 # existed so the required structure must be created whatever we find.
 LOCAL_POST_INSTALL_CMD = mkdir -p $(TARGET_OUT)/usr && rm -rf $(TARGET_OUT)/usr/icu
-LOCAL_POST_INSTALL_CMD += ; ln -sf /apex/com.android.runtime/etc/icu $(TARGET_OUT)/usr/icu
+LOCAL_POST_INSTALL_CMD += && ln -sf /apex/com.android.runtime/etc/icu $(TARGET_OUT)/usr/icu
 
 # TODO(b/124106384): Clean up compat symlinks for ART binaries.
-ART_BINARIES= \
+ART_BINARIES := \
   dalvikvm \
   dalvikvm32 \
   dalvikvm64 \
@@ -235,10 +235,10 @@
   oatdump \
   profman \
 
-LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_OUT)/bin
+LOCAL_POST_INSTALL_CMD += && mkdir -p $(TARGET_OUT)/bin
 $(foreach b,$(ART_BINARIES), \
   $(eval LOCAL_POST_INSTALL_CMD += \
-    ; ln -sf /apex/com.android.runtime/bin/$(b) $(TARGET_OUT)/bin/$(b)) \
+    && ln -sf /apex/com.android.runtime/bin/$(b) $(TARGET_OUT)/bin/$(b)) \
 )
 
 # End of runtime APEX compatibilty.
diff --git a/rootdir/etc/ld.config.legacy.txt b/rootdir/etc/ld.config.legacy.txt
index d4d5c28..fa46334 100644
--- a/rootdir/etc/ld.config.legacy.txt
+++ b/rootdir/etc/ld.config.legacy.txt
@@ -11,6 +11,11 @@
 dir.legacy = /odm
 dir.legacy = /sbin
 
+dir.legacy = /data/nativetest
+dir.legacy = /data/nativetest64
+dir.legacy = /data/benchmarktest
+dir.legacy = /data/benchmarktest64
+
 # Except for /postinstall, where only /system and /product are searched
 dir.postinstall = /postinstall
 
@@ -94,7 +99,21 @@
 namespace.media.permitted.paths = /apex/com.android.media/${LIB}/extractors
 
 namespace.media.links = default
-namespace.media.link.default.allow_all_shared_libs = true
+namespace.media.link.default.shared_libs  = libandroid.so
+namespace.media.link.default.shared_libs += libbinder_ndk.so
+namespace.media.link.default.shared_libs += libc.so
+namespace.media.link.default.shared_libs += libdl.so
+namespace.media.link.default.shared_libs += liblog.so
+namespace.media.link.default.shared_libs += libmediametrics.so
+namespace.media.link.default.shared_libs += libmediandk.so
+namespace.media.link.default.shared_libs += libm.so
+namespace.media.link.default.shared_libs += libvndksupport.so
+
+namespace.media.link.default.shared_libs += libclang_rt.asan-aarch64-android.so
+namespace.media.link.default.shared_libs += libclang_rt.asan-arm-android.so
+namespace.media.link.default.shared_libs += libclang_rt.asan-i686-android.so
+namespace.media.link.default.shared_libs += libclang_rt.asan-x86_64-android.so
+namespace.media.link.default.shared_libs += libclang_rt.hwasan-aarch64-android.so
 
 ###############################################################################
 # "conscrypt" APEX namespace
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 4c52596..8e63a81 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -418,6 +418,15 @@
     mkdir /data/bootchart 0755 shell shell
     bootchart start
 
+    # Load fsverity keys. This needs to happen before apexd, as post-install of
+    # APEXes may rely on keys.
+    exec -- /system/bin/mini-keyctl dadd asymmetric product_cert /product/etc/security/cacerts_fsverity .fs-verity
+    exec -- /system/bin/mini-keyctl dadd asymmetric vendor_cert /vendor/etc/security/cacerts_fsverity .fs-verity
+    # Prevent future key links to fsverity keyring
+    exec -- /system/bin/mini-keyctl restrict_keyring .fs-verity
+    # Enforce fsverity signature checking
+    write /proc/sys/fs/verity/require_signatures 1
+
     # Make sure that apexd is started in the default namespace
     enter_default_mount_ns
 
@@ -585,12 +594,6 @@
     # Set SELinux security contexts on upgrade or policy update.
     restorecon --recursive --skip-ce /data
 
-    # load fsverity keys
-    exec -- /system/bin/mini-keyctl dadd asymmetric product_cert /product/etc/security/cacerts_fsverity .fs-verity
-    exec -- /system/bin/mini-keyctl dadd asymmetric vendor_cert /vendor/etc/security/cacerts_fsverity .fs-verity
-    # Prevent future key links to fsverity keyring
-    exec -- /system/bin/mini-keyctl restrict_keyring .fs-verity
-
     # Check any timezone data in /data is newer than the copy in the runtime module, delete if not.
     exec - system system -- /system/bin/tzdatacheck /apex/com.android.runtime/etc/tz /data/misc/zoneinfo