Merge "Update documentation for SnapshotMergeReport."
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index ea9d333..08ead7a 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -2265,3 +2265,72 @@
     }
     return LP_METADATA_DEFAULT_PARTITION_NAME;
 }
+
+bool fs_mgr_mount_overlayfs_fstab_entry(const FstabEntry& entry) {
+    auto overlayfs_valid_result = fs_mgr_overlayfs_valid();
+    if (overlayfs_valid_result == OverlayfsValidResult::kNotSupported) {
+        LERROR << __FUNCTION__ << "(): kernel does not support overlayfs";
+        return false;
+    }
+
+#if ALLOW_ADBD_DISABLE_VERITY == 0
+    // Allowlist the mount point if user build.
+    static const std::vector<const std::string> kAllowedPaths = {
+            "/odm", "/odm_dlkm", "/oem", "/product", "/system_ext", "/vendor", "/vendor_dlkm",
+    };
+    static const std::vector<const std::string> kAllowedPrefixes = {
+            "/mnt/product/",
+            "/mnt/vendor/",
+    };
+    if (std::none_of(kAllowedPaths.begin(), kAllowedPaths.end(),
+                     [&entry](const auto& path) -> bool {
+                         return entry.mount_point == path ||
+                                StartsWith(entry.mount_point, path + "/");
+                     }) &&
+        std::none_of(kAllowedPrefixes.begin(), kAllowedPrefixes.end(),
+                     [&entry](const auto& prefix) -> bool {
+                         return entry.mount_point != prefix &&
+                                StartsWith(entry.mount_point, prefix);
+                     })) {
+        LERROR << __FUNCTION__
+               << "(): mount point is forbidden on user build: " << entry.mount_point;
+        return false;
+    }
+#endif  // ALLOW_ADBD_DISABLE_VERITY == 0
+
+    // Create the mount point in case it doesn't exist.
+    mkdir(entry.mount_point.c_str(), 0755);
+
+    // Ensure that mount point exists and doesn't contain symbolic link or /../.
+    std::string mount_point;
+    if (!Realpath(entry.mount_point, &mount_point)) {
+        PERROR << __FUNCTION__ << "(): failed to realpath " << entry.mount_point;
+        return false;
+    }
+    if (entry.mount_point != mount_point) {
+        LERROR << __FUNCTION__ << "(): mount point must be a canonicalized path: realpath "
+               << entry.mount_point << " = " << mount_point;
+        return false;
+    }
+
+    auto options = "lowerdir=" + entry.lowerdir;
+    if (overlayfs_valid_result == OverlayfsValidResult::kOverrideCredsRequired) {
+        options += ",override_creds=off";
+    }
+
+    // Use "overlay-" + entry.blk_device as the mount() source, so that adb-remout-test don't
+    // confuse this with adb remount overlay, whose device name is "overlay".
+    // Overlayfs is a pseudo filesystem, so the source device is a symbolic value and isn't used to
+    // back the filesystem. However the device name would be shown in /proc/mounts.
+    auto source = "overlay-" + entry.blk_device;
+    auto report = "__mount(source=" + source + ",target=" + entry.mount_point + ",type=overlay," +
+                  options + ")=";
+    auto ret = mount(source.c_str(), entry.mount_point.c_str(), "overlay", MS_RDONLY | MS_NOATIME,
+                     options.c_str());
+    if (ret) {
+        PERROR << report << ret;
+        return false;
+    }
+    LINFO << report << ret;
+    return true;
+}
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 925d03f..cb09383 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -92,10 +92,6 @@
     return false;
 }
 
-bool fs_mgr_overlayfs_mount_fstab_entry(const android::fs_mgr::FstabEntry&) {
-    return false;
-}
-
 std::vector<std::string> fs_mgr_overlayfs_required_devices(Fstab*) {
     return {};
 }
@@ -1299,32 +1295,6 @@
     }
 }
 
-bool fs_mgr_overlayfs_mount_fstab_entry(const android::fs_mgr::FstabEntry& entry) {
-    if (fs_mgr_overlayfs_invalid()) return false;
-
-    // Create the mount point in case it doesn't exist.
-    mkdir(entry.mount_point.c_str(), 0755);
-
-    auto options = kLowerdirOption + entry.lowerdir;
-    if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kOverrideCredsRequired) {
-        options += ",override_creds=off";
-    }
-
-    // Use .blk_device as the mount() source for debugging purposes.
-    // Overlayfs is pseudo filesystem, so the source device is a symbolic value and isn't used to
-    // back the filesystem. /proc/mounts would show the source as the device name of the mount.
-    auto report = "__mount(source=" + entry.blk_device + ",target=" + entry.mount_point +
-                  ",type=overlay," + options + ")=";
-    auto ret = mount(entry.blk_device.c_str(), entry.mount_point.c_str(), "overlay",
-                     MS_RDONLY | MS_NOATIME, options.c_str());
-    if (ret) {
-        PERROR << report << ret;
-        return false;
-    }
-    LINFO << report << ret;
-    return true;
-}
-
 bool fs_mgr_overlayfs_mount_all(Fstab* fstab) {
     auto ret = false;
     if (fs_mgr_overlayfs_invalid()) return ret;
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 22c02cc..b8ebd63 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -131,3 +131,8 @@
 // Finds the dm_bow device on which this block device is stacked, or returns
 // empty string
 std::string fs_mgr_find_bow_device(const std::string& block_device);
+
+// Like fs_mgr_do_mount_one() but for overlayfs fstab entries.
+// Unlike fs_mgr_overlayfs, mount overlayfs without upperdir and workdir, so the
+// filesystem cannot be remount read-write.
+bool fs_mgr_mount_overlayfs_fstab_entry(const android::fs_mgr::FstabEntry& entry);
diff --git a/fs_mgr/include/fs_mgr_overlayfs.h b/fs_mgr/include/fs_mgr_overlayfs.h
index 22d12e7..d45e2de 100644
--- a/fs_mgr/include/fs_mgr_overlayfs.h
+++ b/fs_mgr/include/fs_mgr_overlayfs.h
@@ -27,7 +27,6 @@
 android::fs_mgr::Fstab fs_mgr_overlayfs_candidate_list(const android::fs_mgr::Fstab& fstab);
 
 bool fs_mgr_overlayfs_mount_all(android::fs_mgr::Fstab* fstab);
-bool fs_mgr_overlayfs_mount_fstab_entry(const android::fs_mgr::FstabEntry& entry);
 std::vector<std::string> fs_mgr_overlayfs_required_devices(android::fs_mgr::Fstab* fstab);
 bool fs_mgr_overlayfs_setup(const char* backing = nullptr, const char* mount_point = nullptr,
                             bool* change = nullptr, bool force = true);
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index d5b8a61..c4874b8 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -35,6 +35,10 @@
 
 #include "utility.h"
 
+#ifndef DM_DEFERRED_REMOVE
+#define DM_DEFERRED_REMOVE (1 << 17)
+#endif
+
 namespace android {
 namespace dm {
 
@@ -133,6 +137,25 @@
     return DeleteDevice(name, 0ms);
 }
 
+bool DeviceMapper::DeleteDeviceDeferred(const std::string& name) {
+    struct dm_ioctl io;
+    InitIo(&io, name);
+
+    io.flags |= DM_DEFERRED_REMOVE;
+    if (ioctl(fd_, DM_DEV_REMOVE, &io)) {
+        PLOG(ERROR) << "DM_DEV_REMOVE with DM_DEFERRED_REMOVE failed for [" << name << "]";
+        return false;
+    }
+    return true;
+}
+
+bool DeviceMapper::DeleteDeviceIfExistsDeferred(const std::string& name) {
+    if (GetState(name) == DmDeviceState::INVALID) {
+        return true;
+    }
+    return DeleteDeviceDeferred(name);
+}
+
 static std::string GenerateUuid() {
     uuid_t uuid_bytes;
     uuid_generate(uuid_bytes);
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
index 41d3145..8006db2 100644
--- a/fs_mgr/libdm/dm_test.cpp
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -35,6 +35,7 @@
 #include <libdm/dm.h>
 #include <libdm/loop_control.h>
 #include "test_util.h"
+#include "utility.h"
 
 using namespace std;
 using namespace std::chrono_literals;
@@ -617,3 +618,64 @@
     auto sub_block_device = dm.GetParentBlockDeviceByPath(dev.path());
     ASSERT_EQ(loop.device(), *sub_block_device);
 }
+
+TEST(libdm, DeleteDeviceDeferredNoReferences) {
+    unique_fd tmp(CreateTempFile("file_1", 4096));
+    ASSERT_GE(tmp, 0);
+    LoopDevice loop(tmp, 10s);
+    ASSERT_TRUE(loop.valid());
+
+    DmTable table;
+    ASSERT_TRUE(table.Emplace<DmTargetLinear>(0, 1, loop.device(), 0));
+    ASSERT_TRUE(table.valid());
+    TempDevice dev("libdm-test-dm-linear", table);
+    ASSERT_TRUE(dev.valid());
+
+    DeviceMapper& dm = DeviceMapper::Instance();
+
+    std::string path;
+    ASSERT_TRUE(dm.GetDmDevicePathByName("libdm-test-dm-linear", &path));
+    ASSERT_EQ(0, access(path.c_str(), F_OK));
+
+    ASSERT_TRUE(dm.DeleteDeviceDeferred("libdm-test-dm-linear"));
+
+    ASSERT_TRUE(WaitForFileDeleted(path, 5s));
+    ASSERT_EQ(DmDeviceState::INVALID, dm.GetState("libdm-test-dm-linear"));
+    ASSERT_NE(0, access(path.c_str(), F_OK));
+    ASSERT_EQ(ENOENT, errno);
+}
+
+TEST(libdm, DeleteDeviceDeferredWaitsForLastReference) {
+    unique_fd tmp(CreateTempFile("file_1", 4096));
+    ASSERT_GE(tmp, 0);
+    LoopDevice loop(tmp, 10s);
+    ASSERT_TRUE(loop.valid());
+
+    DmTable table;
+    ASSERT_TRUE(table.Emplace<DmTargetLinear>(0, 1, loop.device(), 0));
+    ASSERT_TRUE(table.valid());
+    TempDevice dev("libdm-test-dm-linear", table);
+    ASSERT_TRUE(dev.valid());
+
+    DeviceMapper& dm = DeviceMapper::Instance();
+
+    std::string path;
+    ASSERT_TRUE(dm.GetDmDevicePathByName("libdm-test-dm-linear", &path));
+    ASSERT_EQ(0, access(path.c_str(), F_OK));
+
+    {
+        // Open a reference to block device.
+        unique_fd fd(TEMP_FAILURE_RETRY(open(dev.path().c_str(), O_RDONLY | O_CLOEXEC)));
+        ASSERT_GE(fd.get(), 0);
+
+        ASSERT_TRUE(dm.DeleteDeviceDeferred("libdm-test-dm-linear"));
+
+        ASSERT_EQ(0, access(path.c_str(), F_OK));
+    }
+
+    // After release device will be removed.
+    ASSERT_TRUE(WaitForFileDeleted(path, 5s));
+    ASSERT_EQ(DmDeviceState::INVALID, dm.GetState("libdm-test-dm-linear"));
+    ASSERT_NE(0, access(path.c_str(), F_OK));
+    ASSERT_EQ(ENOENT, errno);
+}
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
index 5d6db46..70b14fa 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -95,6 +95,12 @@
     bool DeleteDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms);
     bool DeleteDeviceIfExists(const std::string& name, const std::chrono::milliseconds& timeout_ms);
 
+    // Enqueues a deletion of device mapper device with the given name once last reference is
+    // closed.
+    // Returns 'true' on success, false otherwise.
+    bool DeleteDeviceDeferred(const std::string& name);
+    bool DeleteDeviceIfExistsDeferred(const std::string& name);
+
     // Fetches and returns the complete state of the underlying device mapper
     // device with given name.
     std::optional<Info> GetDetailedInfo(const std::string& name) const;
diff --git a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp b/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
index 3888eb1..767cd04 100644
--- a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
@@ -103,6 +103,7 @@
     void ReadSnapshotDeviceAndValidate();
     void Shutdown();
     void MergeInterrupt();
+    void ReadDmUserBlockWithoutDaemon();
 
     std::string snapshot_dev() const { return snapshot_dev_->path(); }
 
@@ -481,6 +482,36 @@
     ASSERT_TRUE(android::fs_mgr::WaitForFile(misc_device, 10s));
 }
 
+void CowSnapuserdTest::ReadDmUserBlockWithoutDaemon() {
+    DmTable dmuser_table;
+    std::string dm_user_name = "dm-test-device";
+    unique_fd fd;
+
+    // Create a dm-user block device
+    ASSERT_TRUE(dmuser_table.AddTarget(std::make_unique<DmTargetUser>(0, 123456, dm_user_name)));
+    ASSERT_TRUE(dmuser_table.valid());
+
+    dmuser_dev_ = std::make_unique<TempDevice>(dm_user_name, dmuser_table);
+    ASSERT_TRUE(dmuser_dev_->valid());
+    ASSERT_FALSE(dmuser_dev_->path().empty());
+
+    fd.reset(open(dmuser_dev_->path().c_str(), O_RDONLY));
+    ASSERT_GE(fd, 0);
+
+    std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(1_MiB);
+
+    loff_t offset = 0;
+    // Every IO should fail as there is no daemon to process the IO
+    for (size_t j = 0; j < 10; j++) {
+        ASSERT_EQ(ReadFullyAtOffset(fd, (char*)buffer.get() + offset, BLOCK_SZ, offset), false);
+
+        offset += BLOCK_SZ;
+    }
+
+    fd = {};
+    ASSERT_TRUE(dmuser_dev_->Destroy());
+}
+
 void CowSnapuserdTest::InitDaemon() {
     bool ok = client_->AttachDmUser(system_device_ctrl_name_);
     ASSERT_TRUE(ok);
@@ -909,6 +940,11 @@
     harness.Shutdown();
 }
 
+TEST(Snapuserd_Test, ReadDmUserBlockWithoutDaemon) {
+    CowSnapuserdTest harness;
+    harness.ReadDmUserBlockWithoutDaemon();
+}
+
 }  // namespace snapshot
 }  // namespace android
 
diff --git a/fs_mgr/libsnapshot/snapshotctl.cpp b/fs_mgr/libsnapshot/snapshotctl.cpp
index 5eb2003..67189d4 100644
--- a/fs_mgr/libsnapshot/snapshotctl.cpp
+++ b/fs_mgr/libsnapshot/snapshotctl.cpp
@@ -36,7 +36,9 @@
                  "  dump\n"
                  "    Print snapshot states.\n"
                  "  merge\n"
-                 "    Deprecated.\n";
+                 "    Deprecated.\n"
+                 "  map\n"
+                 "    Map all partitions at /dev/block/mapper\n";
     return EX_USAGE;
 }
 
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index 242fa93..9542bc1 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -735,23 +735,46 @@
   fi
 }
 
+[ "USAGE: join_with <delimiter> <strings>
+
+Joins strings with delimiter" ]
+join_with() {
+  if [ "${#}" -lt 2 ]; then
+    echo
+    return
+  fi
+  local delimiter="${1}"
+  local result="${2}"
+  shift 2
+  for element in "${@}"; do
+    result+="${delimiter}${element}"
+  done
+  echo "${result}"
+}
+
 [ "USAGE: skip_administrative_mounts [data] < /proc/mounts
 
 Filters out all administrative (eg: sysfs) mounts uninteresting to the test" ]
 skip_administrative_mounts() {
+  local exclude_filesystems=(
+    "overlay" "tmpfs" "none" "sysfs" "proc" "selinuxfs" "debugfs" "bpf"
+    "binfmt_misc" "cg2_bpf" "pstore" "tracefs" "adb" "mtp" "ptp" "devpts"
+    "ramdumpfs" "binder" "securityfs" "functionfs" "rootfs"
+  )
+  local exclude_devices=(
+    "\/sys\/kernel\/debug" "\/data\/media" "\/dev\/block\/loop[0-9]*"
+    "${exclude_filesystems[@]}"
+  )
+  local exclude_mount_points=(
+    "\/cache" "\/mnt\/scratch" "\/mnt\/vendor\/persist" "\/persist"
+    "\/metadata"
+  )
   if [ "data" = "${1}" ]; then
-    grep -v " /data "
-  else
-    cat -
-  fi |
-  grep -v \
-    -e "^\(overlay\|tmpfs\|none\|sysfs\|proc\|selinuxfs\|debugfs\|bpf\) " \
-    -e "^\(binfmt_misc\|cg2_bpf\|pstore\|tracefs\|adb\|mtp\|ptp\|devpts\) " \
-    -e "^\(ramdumpfs\|binder\|/sys/kernel/debug\|securityfs\) " \
-    -e " functionfs " \
-    -e "^\(/data/media\|/dev/block/loop[0-9]*\) " \
-    -e "^rootfs / rootfs rw," \
-    -e " /\(cache\|mnt/scratch\|mnt/vendor/persist\|persist\|metadata\) "
+    exclude_mount_points+=("\/data")
+  fi
+  awk '$1 !~ /^('"$(join_with "|" "${exclude_devices[@]}")"')$/ &&
+      $2 !~ /^('"$(join_with "|" "${exclude_mount_points[@]}")"')$/ &&
+      $3 !~ /^('"$(join_with "|" "${exclude_filesystems[@]}")"')$/'
 }
 
 [ "USAGE: skip_unrelated_mounts < /proc/mounts
@@ -907,9 +930,11 @@
 
 # Acquire list of system partitions
 
+# KISS (assume system partition mount point is "/<partition name>")
 PARTITIONS=`adb_su cat /vendor/etc/fstab* </dev/null |
+              grep -v "^[#${SPACE}${TAB}]" |
               skip_administrative_mounts |
-              sed -n "s@^\([^ ${TAB}/][^ ${TAB}/]*\)[ ${TAB}].*[, ${TAB}]ro[, ${TAB}].*@\1@p" |
+              awk '$1 ~ /^[^\/]+$/ && "/"$1 == $2 && $4 ~ /(^|,)ro(,|$)/ { print $1 }' |
               sort -u |
               tr '\n' ' '`
 PARTITIONS="${PARTITIONS:-system vendor}"
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 616d285..546ea8e 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -574,7 +574,7 @@
 
     for (const auto& entry : fstab_) {
         if (entry.fs_type == "overlay") {
-            fs_mgr_overlayfs_mount_fstab_entry(entry);
+            fs_mgr_mount_overlayfs_fstab_entry(entry);
         }
     }
 
diff --git a/init/reboot.cpp b/init/reboot.cpp
index ab0e48e..0e788e4 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -478,6 +478,11 @@
     // cut the last "\n"
     backing_dev.erase(backing_dev.length() - 1);
 
+    if (android::base::StartsWith(backing_dev, "none")) {
+        LOG(INFO) << "No zram backing device configured";
+        return {};
+    }
+
     // shutdown zram handle
     Timer swap_timer;
     LOG(INFO) << "swapoff() start...";
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index 8f22d89..c471fa0 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -127,7 +127,7 @@
 #define AID_EXT_DATA_RW 1078      /* GID for app-private data directories on external storage */
 #define AID_EXT_OBB_RW 1079       /* GID for OBB directories on external storage */
 #define AID_CONTEXT_HUB 1080      /* GID for access to the Context Hub */
-#define AID_VIRTMANAGER 1081      /* VirtManager daemon */
+#define AID_VIRTUALIZATIONSERVICE 1081 /* VirtualizationService daemon */
 #define AID_ARTD 1082             /* ART Service daemon */
 #define AID_UWB 1083              /* UWB subsystem */
 /* Changes to this file must be made in AOSP, *not* in internal branches. */
diff --git a/libutils/include/utils/KeyedVector.h b/libutils/include/utils/KeyedVector.h
index 7bda99b..5cf7a11 100644
--- a/libutils/include/utils/KeyedVector.h
+++ b/libutils/include/utils/KeyedVector.h
@@ -47,7 +47,7 @@
 
     inline  void            clear()                     { mVector.clear(); }
 
-    /*! 
+    /*!
      * vector stats
      */
 
@@ -63,14 +63,14 @@
     // returns true if the arguments is known to be identical to this vector
     inline bool isIdenticalTo(const KeyedVector& rhs) const;
 
-    /*! 
+    /*!
      * accessors
      */
-            const VALUE&    valueFor(const KEY& key) const;
-            const VALUE&    valueAt(size_t index) const;
-            const KEY&      keyAt(size_t index) const;
-            ssize_t         indexOfKey(const KEY& key) const;
-            const VALUE&    operator[] (size_t index) const;
+    const VALUE& valueFor(const KEY& key) const;
+    const VALUE& valueAt(size_t index) const;
+    const KEY& keyAt(size_t index) const;
+    ssize_t indexOfKey(const KEY& key) const;
+    const VALUE& operator[](size_t index) const;
 
     /*!
      * modifying the array
@@ -79,10 +79,10 @@
             VALUE&          editValueFor(const KEY& key);
             VALUE&          editValueAt(size_t index);
 
-            /*! 
+            /*!
              * add/insert/replace items
              */
-             
+
             ssize_t         add(const KEY& key, const VALUE& item);
             ssize_t         replaceValueFor(const KEY& key, const VALUE& item);
             ssize_t         replaceValueAt(size_t index, const VALUE& item);
@@ -93,7 +93,7 @@
 
             ssize_t         removeItem(const KEY& key);
             ssize_t         removeItemsAt(size_t index, size_t count = 1);
-            
+
 private:
             SortedVector< key_value_pair_t<KEY, VALUE> >    mVector;
 };
@@ -208,7 +208,7 @@
 template<typename KEY, typename VALUE> inline
 const VALUE& DefaultKeyedVector<KEY,VALUE>::valueFor(const KEY& key) const {
     ssize_t i = this->indexOfKey(key);
-    return i >= 0 ? KeyedVector<KEY,VALUE>::valueAt(i) : mDefault;
+    return i >= 0 ? KeyedVector<KEY, VALUE>::valueAt(static_cast<size_t>(i)) : mDefault;
 }
 
 }  // namespace android
diff --git a/rootdir/etc/linker.config.json b/rootdir/etc/linker.config.json
index 6b03a1d..c58f298 100644
--- a/rootdir/etc/linker.config.json
+++ b/rootdir/etc/linker.config.json
@@ -19,7 +19,6 @@
     "libnetd_resolv.so",
     // nn
     "libneuralnetworks.so",
-    "libneuralnetworks_shim.so",
     // statsd
     "libstatspull.so",
     "libstatssocket.so",
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 6e85da5..c6b74bc 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -776,6 +776,9 @@
     mkdir /data/misc/odrefresh 0777 system system
     # directory used for on-device signing key blob
     mkdir /data/misc/odsign 0700 root root
+    # Directory for VirtualizationService temporary image files. Ensure that it is empty.
+    exec -- /bin/rm -rf /data/misc/virtualizationservice
+    mkdir /data/misc/virtualizationservice 0700 virtualizationservice virtualizationservice
 
     mkdir /data/preloads 0775 system system encryption=None
 
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index 56e774b..3101974 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -67,9 +67,9 @@
 # CDMA radio interface MUX
 /dev/ppp                  0660   radio      vpn
 
-# Virtualisation is managed by Virt Manager
-/dev/kvm                  0600   virtmanager root
-/dev/vhost-vsock          0600   virtmanager root
+# Virtualization is managed by VirtualizationService.
+/dev/kvm                  0600   virtualizationservice root
+/dev/vhost-vsock          0600   virtualizationservice root
 
 # sysfs properties
 /sys/devices/platform/trusty.*      trusty_version        0440  root   log
diff --git a/trusty/apploader/apploader.cpp b/trusty/apploader/apploader.cpp
index 4aca375..e4d9b39 100644
--- a/trusty/apploader/apploader.cpp
+++ b/trusty/apploader/apploader.cpp
@@ -220,6 +220,9 @@
         case APPLOADER_ERR_INTERNAL:
             LOG(ERROR) << "Error: internal apploader error";
             break;
+        case APPLOADER_ERR_INVALID_VERSION:
+            LOG(ERROR) << "Error: invalid application version";
+            break;
         default:
             LOG(ERROR) << "Unrecognized error: " << resp.error;
             break;
diff --git a/trusty/apploader/apploader_ipc.h b/trusty/apploader/apploader_ipc.h
index d8c915e..6cda7c1 100644
--- a/trusty/apploader/apploader_ipc.h
+++ b/trusty/apploader/apploader_ipc.h
@@ -44,6 +44,7 @@
  * @APPLOADER_ERR_ALREADY_EXISTS:       application has already been loaded
  * @APPLOADER_ERR_INTERNAL:             miscellaneous or internal apploader
  *                                      error not covered by the above
+ * @APPLOADER_ERR_INVALID_VERSION:      invalid application version
  */
 enum apploader_error : uint32_t {
     APPLOADER_NO_ERROR = 0,
@@ -54,6 +55,7 @@
     APPLOADER_ERR_LOADING_FAILED,
     APPLOADER_ERR_ALREADY_EXISTS,
     APPLOADER_ERR_INTERNAL,
+    APPLOADER_ERR_INVALID_VERSION,
 };
 
 /**