Merge "Snap for 8140651 from ed54a2f2bef1c7007b908ebedc7f43e458b7dcbb to sdk-release" into sdk-release
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 2cf5b18..0737612 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -88,7 +88,7 @@
     struct sigaction old_sigaction;                                \
     struct sigaction new_sigaction = {};                           \
     new_sigaction.sa_handler = [](int) {};                         \
-    if (sigaction(SIGALRM, &new_sigaction, &new_sigaction) != 0) { \
+    if (sigaction(SIGALRM, &new_sigaction, &old_sigaction) != 0) { \
       err(1, "sigaction failed");                                  \
     }                                                              \
     alarm(seconds);                                                \
diff --git a/fs_mgr/libfiemap/image_manager.cpp b/fs_mgr/libfiemap/image_manager.cpp
index 2c14c8a..e3f5716 100644
--- a/fs_mgr/libfiemap/image_manager.cpp
+++ b/fs_mgr/libfiemap/image_manager.cpp
@@ -744,6 +744,116 @@
     return CreateLogicalPartitions(*metadata.get(), data_partition_name);
 }
 
+std::ostream& operator<<(std::ostream& os, android::fs_mgr::Extent* extent) {
+    if (auto e = extent->AsLinearExtent()) {
+        return os << "<begin:" << e->physical_sector() << ", end:" << e->end_sector()
+                  << ", device:" << e->device_index() << ">";
+    }
+    return os << "<unknown>";
+}
+
+static bool CompareExtent(android::fs_mgr::Extent* a, android::fs_mgr::Extent* b) {
+    if (auto linear_a = a->AsLinearExtent()) {
+        auto linear_b = b->AsLinearExtent();
+        if (!linear_b) {
+            return false;
+        }
+        return linear_a->physical_sector() == linear_b->physical_sector() &&
+               linear_a->num_sectors() == linear_b->num_sectors() &&
+               linear_a->device_index() == linear_b->device_index();
+    }
+    return false;
+}
+
+static bool CompareExtents(android::fs_mgr::Partition* oldp, android::fs_mgr::Partition* newp) {
+    const auto& old_extents = oldp->extents();
+    const auto& new_extents = newp->extents();
+
+    auto old_iter = old_extents.begin();
+    auto new_iter = new_extents.begin();
+    while (true) {
+        if (old_iter == old_extents.end()) {
+            if (new_iter == new_extents.end()) {
+                break;
+            }
+            LOG(ERROR) << "Unexpected extent added: " << (*new_iter);
+            return false;
+        }
+        if (new_iter == new_extents.end()) {
+            LOG(ERROR) << "Unexpected extent removed: " << (*old_iter);
+            return false;
+        }
+
+        if (!CompareExtent(old_iter->get(), new_iter->get())) {
+            LOG(ERROR) << "Extents do not match: " << old_iter->get() << ", " << new_iter->get();
+            return false;
+        }
+
+        old_iter++;
+        new_iter++;
+    }
+    return true;
+}
+
+bool ImageManager::ValidateImageMaps() {
+    if (!MetadataExists(metadata_dir_)) {
+        LOG(INFO) << "ImageManager skipping verification; no images for " << metadata_dir_;
+        return true;
+    }
+
+    auto metadata = OpenMetadata(metadata_dir_);
+    if (!metadata) {
+        LOG(ERROR) << "ImageManager skipping verification; failed to open " << metadata_dir_;
+        return true;
+    }
+
+    for (const auto& partition : metadata->partitions) {
+        auto name = GetPartitionName(partition);
+        auto image_path = GetImageHeaderPath(name);
+        auto fiemap = SplitFiemap::Open(image_path);
+        if (fiemap == nullptr) {
+            LOG(ERROR) << "SplitFiemap::Open(\"" << image_path << "\") failed";
+            return false;
+        }
+        if (!fiemap->HasPinnedExtents()) {
+            LOG(ERROR) << "Image doesn't have pinned extents: " << image_path;
+            return false;
+        }
+
+        android::fs_mgr::PartitionOpener opener;
+        auto builder = android::fs_mgr::MetadataBuilder::New(*metadata.get(), &opener);
+        if (!builder) {
+            LOG(ERROR) << "Could not create metadata builder: " << image_path;
+            return false;
+        }
+
+        auto new_p = builder->AddPartition("_temp_for_verify", 0);
+        if (!new_p) {
+            LOG(ERROR) << "Could not add temporary partition: " << image_path;
+            return false;
+        }
+
+        auto partition_size = android::fs_mgr::GetPartitionSize(*metadata.get(), partition);
+        if (!FillPartitionExtents(builder.get(), new_p, fiemap.get(), partition_size)) {
+            LOG(ERROR) << "Could not fill partition extents: " << image_path;
+            return false;
+        }
+
+        auto old_p = builder->FindPartition(name);
+        if (!old_p) {
+            LOG(ERROR) << "Could not find metadata for " << image_path;
+            return false;
+        }
+
+        if (!CompareExtents(old_p, new_p)) {
+            LOG(ERROR) << "Metadata for " << image_path << " does not match fiemap";
+            return false;
+        }
+    }
+
+    return true;
+}
+
 std::unique_ptr<MappedDevice> MappedDevice::Open(IImageManager* manager,
                                                  const std::chrono::milliseconds& timeout_ms,
                                                  const std::string& name) {
diff --git a/fs_mgr/libfiemap/include/libfiemap/image_manager.h b/fs_mgr/libfiemap/include/libfiemap/image_manager.h
index 3c87000..b23a7f7 100644
--- a/fs_mgr/libfiemap/include/libfiemap/image_manager.h
+++ b/fs_mgr/libfiemap/include/libfiemap/image_manager.h
@@ -174,6 +174,9 @@
     // Writes |bytes| zeros at the beginning of the passed image
     FiemapStatus ZeroFillNewImage(const std::string& name, uint64_t bytes);
 
+    // Validate that all images still have the same block map.
+    bool ValidateImageMaps();
+
   private:
     ImageManager(const std::string& metadata_dir, const std::string& data_dir,
                  const DeviceInfo& device_info);
diff --git a/fs_mgr/libfiemap/metadata.h b/fs_mgr/libfiemap/metadata.h
index 4eb3ad5..30b2c61 100644
--- a/fs_mgr/libfiemap/metadata.h
+++ b/fs_mgr/libfiemap/metadata.h
@@ -20,6 +20,7 @@
 #include <string>
 
 #include <libfiemap/split_fiemap_writer.h>
+#include <liblp/builder.h>
 #include <liblp/liblp.h>
 
 namespace android {
@@ -34,5 +35,9 @@
 bool RemoveImageMetadata(const std::string& metadata_dir, const std::string& partition_name);
 bool RemoveAllMetadata(const std::string& dir);
 
+bool FillPartitionExtents(android::fs_mgr::MetadataBuilder* builder,
+                          android::fs_mgr::Partition* partition, android::fiemap::SplitFiemap* file,
+                          uint64_t partition_size);
+
 }  // namespace fiemap
 }  // namespace android
diff --git a/healthd/healthd_draw.cpp b/healthd/healthd_draw.cpp
index 0e6fd27..3e73fcd 100644
--- a/healthd/healthd_draw.cpp
+++ b/healthd/healthd_draw.cpp
@@ -99,6 +99,15 @@
     gr_fb_blank(blank, drm);
 }
 
+/* support screen rotation for foldable phone */
+void HealthdDraw::rotate_screen(int drm) {
+    if (!graphics_available) return;
+    if (drm == 0)
+        gr_rotate(GRRotation::RIGHT /* landscape mode */);
+    else
+        gr_rotate(GRRotation::NONE /* Portrait mode */);
+}
+
 void HealthdDraw::clear_screen(void) {
     if (!graphics_available) return;
     gr_color(0, 0, 0, 255);
diff --git a/healthd/healthd_draw.h b/healthd/healthd_draw.h
index 0d7ac7a..3d4abbd 100644
--- a/healthd/healthd_draw.h
+++ b/healthd/healthd_draw.h
@@ -35,6 +35,9 @@
   // Blanks screen if true, unblanks if false.
   virtual void blank_screen(bool blank, int drm);
 
+  // Rotate screen.
+  virtual void rotate_screen(int drm);
+
   static std::unique_ptr<HealthdDraw> Create(animation *anim);
 
  protected:
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index 012e33b..9fe85d4 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -352,6 +352,7 @@
     if (screen_switch_ == SCREEN_SWITCH_ENABLE) {
         healthd_draw_->blank_screen(true, 0 /* drm */);
         healthd_draw_->blank_screen(true, 1 /* drm */);
+        healthd_draw_->rotate_screen(static_cast<int>(drm_));
         screen_blanked_ = true;
         screen_switch_ = SCREEN_SWITCH_DISABLE;
     }
diff --git a/init/Android.bp b/init/Android.bp
index c39d163..66427dc 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -162,15 +162,12 @@
         "libavb",
         "libc++fs",
         "libcgrouprc_format",
-        "libfsverity_init",
         "liblmkd_utils",
-        "libmini_keyctl_static",
         "libmodprobe",
         "libprocinfo",
         "libprotobuf-cpp-lite",
         "libpropertyinfoserializer",
         "libpropertyinfoparser",
-        "libsigningutils",
         "libsnapshot_cow",
         "libsnapshot_init",
         "libxml2",
@@ -181,7 +178,6 @@
         "libbacktrace",
         "libbase",
         "libbootloader_message",
-        "libcrypto",
         "libcutils",
         "libdl",
         "libext4_utils",
@@ -196,7 +192,6 @@
         "libprocessgroup_setup",
         "libselinux",
         "libutils",
-        "libziparchive",
     ],
     bootstrap: true,
     visibility: [":__subpackages__"],
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index 3cd0252..c7b7b0c 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -254,9 +254,6 @@
     // stage init
     CHECKCALL(mount("tmpfs", kSecondStageRes, "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
                     "mode=0755,uid=0,gid=0"))
-
-    // First stage init stores Mainline sepolicy here.
-    CHECKCALL(mkdir("/dev/selinux", 0744));
 #undef CHECKCALL
 
     SetStdioToDevNull(argv);
diff --git a/init/property_service.cpp b/init/property_service.cpp
index bdd5677..70e26ec 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -1171,9 +1171,6 @@
         // Don't check for failure here, since we don't always have all of these partitions.
         // E.g. In case of recovery, the vendor partition will not have mounted and we
         // still need the system / platform properties to function.
-        if (access("/dev/selinux/apex_property_contexts", R_OK) != -1) {
-            LoadPropertyInfoFromFile("/dev/selinux/apex_property_contexts", &property_infos);
-        }
         if (access("/system_ext/etc/selinux/system_ext_property_contexts", R_OK) != -1) {
             LoadPropertyInfoFromFile("/system_ext/etc/selinux/system_ext_property_contexts",
                                      &property_infos);
@@ -1197,7 +1194,6 @@
         LoadPropertyInfoFromFile("/vendor_property_contexts", &property_infos);
         LoadPropertyInfoFromFile("/product_property_contexts", &property_infos);
         LoadPropertyInfoFromFile("/odm_property_contexts", &property_infos);
-        LoadPropertyInfoFromFile("/dev/selinux/apex_property_contexts", &property_infos);
     }
 
     auto serialized_contexts = std::string();
diff --git a/init/selinux.cpp b/init/selinux.cpp
index c89c5ab..28cd012 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -26,29 +26,26 @@
 // The monolithic policy variant is for legacy non-treble devices that contain a single SEPolicy
 // file located at /sepolicy and is directly loaded into the kernel SELinux subsystem.
 
-// The split policy is for supporting treble devices and updateable apexes.  It splits the SEPolicy
-// across files on /system/etc/selinux (the 'plat' portion of the policy), /vendor/etc/selinux
-// (the 'vendor' portion of the policy), /system_ext/etc/selinux, /product/etc/selinux,
-// /odm/etc/selinux, and /dev/selinux (the apex portion of policy).  This is necessary to allow
-// images to be updated independently of the vendor image, while maintaining contributions from
-// multiple partitions in the SEPolicy.  This is especially important for VTS testing, where the
-// SEPolicy on the Google System Image may not be identical to the system image shipped on a
-// vendor's device.
+// The split policy is for supporting treble devices.  It splits the SEPolicy across files on
+// /system/etc/selinux (the 'plat' portion of the policy) and /vendor/etc/selinux (the 'vendor'
+// portion of the policy).  This is necessary to allow the system image to be updated independently
+// of the vendor image, while maintaining contributions from both partitions in the SEPolicy.  This
+// is especially important for VTS testing, where the SEPolicy on the Google System Image may not be
+// identical to the system image shipped on a vendor's device.
 
 // The split SEPolicy is loaded as described below:
 // 1) There is a precompiled SEPolicy located at either /vendor/etc/selinux/precompiled_sepolicy or
 //    /odm/etc/selinux/precompiled_sepolicy if odm parition is present.  Stored along with this file
-//    are the sha256 hashes of the parts of the SEPolicy on /system, /system_ext, /product, and apex
-//    that were used to compile this precompiled policy.  The system partition contains a similar
-//    sha256 of the parts of the SEPolicy that it currently contains. Symmetrically, system_ext,
-//    product, and apex contain sha256 hashes of their SEPolicy. Init loads this
+//    are the sha256 hashes of the parts of the SEPolicy on /system, /system_ext and /product that
+//    were used to compile this precompiled policy.  The system partition contains a similar sha256
+//    of the parts of the SEPolicy that it currently contains.  Symmetrically, system_ext and
+//    product paritition contain sha256 hashes of their SEPolicy.  The init loads this
 //    precompiled_sepolicy directly if and only if the hashes along with the precompiled SEPolicy on
-//    /vendor or /odm match the hashes for system, system_ext, product, and apex SEPolicy,
-//    respectively.
-// 2) If these hashes do not match, then either /system or /system_ext /product, or apex (or some of
-//    them) have been updated out of sync with /vendor (or /odm if it is present) and the init needs
-//    to compile the SEPolicy.  /system contains the SEPolicy compiler, secilc, and it is used by
-//    the OpenSplitPolicy() function below to compile the SEPolicy to a temp directory and load it.
+//    /vendor or /odm match the hashes for system, system_ext and product SEPolicy, respectively.
+// 2) If these hashes do not match, then either /system or /system_ext or /product (or some of them)
+//    have been updated out of sync with /vendor (or /odm if it is present) and the init needs to
+//    compile the SEPolicy.  /system contains the SEPolicy compiler, secilc, and it is used by the
+//    OpenSplitPolicy() function below to compile the SEPolicy to a temp directory and load it.
 //    That function contains even more documentation with the specific implementation details of how
 //    the SEPolicy is compiled if needed.
 
@@ -61,25 +58,19 @@
 #include <stdlib.h>
 #include <sys/wait.h>
 #include <unistd.h>
-#include <fstream>
 
-#include <CertUtils.h>
 #include <android-base/chrono_utils.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
 #include <android-base/result.h>
-#include <android-base/scopeguard.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <fs_avb/fs_avb.h>
 #include <fs_mgr.h>
-#include <fsverity_init.h>
 #include <libgsi/libgsi.h>
 #include <libsnapshot/snapshot.h>
-#include <mini_keyctl_utils.h>
 #include <selinux/android.h>
-#include <ziparchive/zip_archive.h>
 
 #include "block_dev_initializer.h"
 #include "debug_ramdisk.h"
@@ -256,7 +247,6 @@
              precompiled_sepolicy + ".system_ext_sepolicy_and_mapping.sha256"},
             {"/product/etc/selinux/product_sepolicy_and_mapping.sha256",
              precompiled_sepolicy + ".product_sepolicy_and_mapping.sha256"},
-            {"/dev/selinux/apex_sepolicy.sha256", precompiled_sepolicy + ".apex_sepolicy.sha256"},
     };
 
     for (const auto& [actual_id_path, precompiled_id_path] : sepolicy_hashes) {
@@ -335,7 +325,7 @@
     // * vendor -- policy needed due to logic contained in the vendor image,
     // * mapping -- mapping policy which helps preserve forward-compatibility of non-platform policy
     //   with newer versions of platform policy.
-    // * (optional) policy needed due to logic on product, system_ext, odm, or apex.
+    // * (optional) policy needed due to logic on product, system_ext, or odm images.
     // secilc is invoked to compile the above three policy files into a single monolithic policy
     // file. This file is then loaded into the kernel.
 
@@ -431,12 +421,6 @@
     if (access(odm_policy_cil_file.c_str(), F_OK) == -1) {
         odm_policy_cil_file.clear();
     }
-
-    // apex_sepolicy.cil is default but optional.
-    std::string apex_policy_cil_file("/dev/selinux/apex_sepolicy.cil");
-    if (access(apex_policy_cil_file.c_str(), F_OK) == -1) {
-        apex_policy_cil_file.clear();
-    }
     const std::string version_as_string = std::to_string(SEPOLICY_VERSION);
 
     // clang-format off
@@ -479,9 +463,6 @@
     if (!odm_policy_cil_file.empty()) {
         compile_args.push_back(odm_policy_cil_file.c_str());
     }
-    if (!apex_policy_cil_file.empty()) {
-        compile_args.push_back(apex_policy_cil_file.c_str());
-    }
     compile_args.push_back(nullptr);
 
     if (!ForkExecveAndWaitForCompletion(compile_args[0], (char**)compile_args.data())) {
@@ -508,197 +489,6 @@
     return true;
 }
 
-constexpr const char* kSigningCertRelease =
-        "/system/etc/selinux/com.android.sepolicy.cert-release.der";
-constexpr const char* kFsVerityProcPath = "/proc/sys/fs/verity";
-const std::string kSepolicyApexMetadataDir = "/metadata/sepolicy/";
-const std::string kSepolicyApexSystemDir = "/system/etc/selinux/apex/";
-const std::string kSepolicyZip = "SEPolicy.zip";
-const std::string kSepolicySignature = "SEPolicy.zip.sig";
-
-const std::string kTmpfsDir = "/dev/selinux/";
-
-// Files that are deleted after policy is compiled/loaded.
-const std::vector<std::string> kApexSepolicyTmp{"apex_sepolicy.cil", "apex_sepolicy.sha256"};
-// Files that need to persist because they are used by userspace processes.
-const std::vector<std::string> kApexSepolicy{"apex_file_contexts", "apex_property_contexts",
-                                             "apex_service_contexts", "apex_seapp_contexts",
-                                             "apex_test"};
-
-Result<void> PutFileInTmpfs(ZipArchiveHandle archive, const std::string& fileName) {
-    ZipEntry entry;
-    std::string dstPath = kTmpfsDir + fileName;
-
-    int ret = FindEntry(archive, fileName, &entry);
-    if (ret != 0) {
-        // All files are optional. If a file doesn't exist, return without error.
-        return {};
-    }
-
-    unique_fd fd(TEMP_FAILURE_RETRY(
-            open(dstPath.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, S_IRUSR | S_IWUSR)));
-    if (fd == -1) {
-        return Error() << "Failed to open " << dstPath;
-    }
-
-    ret = ExtractEntryToFile(archive, &entry, fd);
-    if (ret != 0) {
-        return Error() << "Failed to extract entry \"" << fileName << "\" ("
-                       << entry.uncompressed_length << " bytes) to \"" << dstPath
-                       << "\": " << ErrorCodeString(ret);
-    }
-
-    return {};
-}
-
-Result<void> GetPolicyFromApex(const std::string& dir) {
-    LOG(INFO) << "Loading APEX Sepolicy from " << dir + kSepolicyZip;
-    unique_fd fd(open((dir + kSepolicyZip).c_str(), O_RDONLY | O_BINARY | O_CLOEXEC));
-    if (fd < 0) {
-        return ErrnoError() << "Failed to open package " << dir + kSepolicyZip;
-    }
-
-    ZipArchiveHandle handle;
-    int ret = OpenArchiveFd(fd.get(), (dir + kSepolicyZip).c_str(), &handle,
-                            /*assume_ownership=*/false);
-    if (ret < 0) {
-        return Error() << "Failed to open package " << dir + kSepolicyZip << ": "
-                       << ErrorCodeString(ret);
-    }
-
-    auto handle_guard = android::base::make_scope_guard([&handle] { CloseArchive(handle); });
-
-    for (const auto& file : kApexSepolicy) {
-        auto extract = PutFileInTmpfs(handle, file);
-        if (!extract.ok()) {
-            return extract.error();
-        }
-    }
-    for (const auto& file : kApexSepolicyTmp) {
-        auto extract = PutFileInTmpfs(handle, file);
-        if (!extract.ok()) {
-            return extract.error();
-        }
-    }
-    return {};
-}
-
-Result<void> LoadSepolicyApexCerts() {
-    key_serial_t keyring_id = android::GetKeyringId(".fs-verity");
-    if (keyring_id < 0) {
-        return Error() << "Failed to find .fs-verity keyring id";
-    }
-
-    // TODO(b/199914227) the release key should always exist. Once it's checked in, start
-    // throwing an error here if it doesn't exist.
-    if (access(kSigningCertRelease, F_OK) == 0) {
-        LoadKeyFromFile(keyring_id, "fsv_sepolicy_apex_release", kSigningCertRelease);
-    }
-    return {};
-}
-
-Result<void> SepolicyFsVerityCheck() {
-    return Error() << "TODO implementent support for fsverity SEPolicy.";
-}
-
-Result<void> SepolicyCheckSignature(const std::string& dir) {
-    std::string signature;
-    if (!android::base::ReadFileToString(dir + kSepolicySignature, &signature)) {
-        return ErrnoError() << "Failed to read " << kSepolicySignature;
-    }
-
-    std::fstream sepolicyZip(dir + kSepolicyZip, std::ios::in | std::ios::binary);
-    if (!sepolicyZip) {
-        return Error() << "Failed to open " << kSepolicyZip;
-    }
-    sepolicyZip.seekg(0);
-    std::string sepolicyStr((std::istreambuf_iterator<char>(sepolicyZip)),
-                            std::istreambuf_iterator<char>());
-
-    auto releaseKey = extractPublicKeyFromX509(kSigningCertRelease);
-    if (!releaseKey.ok()) {
-        return releaseKey.error();
-    }
-
-    return verifySignature(sepolicyStr, signature, *releaseKey);
-}
-
-Result<void> SepolicyVerify(const std::string& dir, bool supportsFsVerity) {
-    if (supportsFsVerity) {
-        auto fsVerityCheck = SepolicyFsVerityCheck();
-        if (fsVerityCheck.ok()) {
-            return fsVerityCheck;
-        }
-        // TODO(b/199914227) If the device supports fsverity, but we fail here, we should fail to
-        // boot and not carry on. For now, fallback to a signature checkuntil the fsverity
-        // logic is implemented.
-        LOG(INFO) << "Falling back to standard signature check. " << fsVerityCheck.error();
-    }
-
-    auto sepolicySignature = SepolicyCheckSignature(dir);
-    if (!sepolicySignature.ok()) {
-        return Error() << "Apex SEPolicy failed signature check";
-    }
-    return {};
-}
-
-void CleanupApexSepolicy() {
-    for (const auto& file : kApexSepolicyTmp) {
-        std::string path = kTmpfsDir + file;
-        unlink(path.c_str());
-    }
-}
-
-// Updatable sepolicy is shipped within an zip within an APEX. Because
-// it needs to be available before Apexes are mounted, apexd copies
-// the zip from the APEX and stores it in /metadata/sepolicy. If there is
-// no updatable sepolicy in /metadata/sepolicy, then the updatable policy is
-// loaded from /system/etc/selinux/apex. Init performs the following
-// steps on boot:
-//
-// 1. Validates the zip by checking its signature against a public key that is
-// stored in /system/etc/selinux.
-// 2. Extracts files from zip and stores them in /dev/selinux.
-// 3. Checks if the apex_sepolicy.sha256 matches the sha256 of precompiled_sepolicy.
-// if so, the precompiled sepolicy is used. Otherwise, an on-device compile of the policy
-// is used. This is the same flow as on-device compilation of policy for Treble.
-// 4. Cleans up files in /dev/selinux which are no longer needed.
-// 5. Restorecons the remaining files in /dev/selinux.
-// 6. Sets selinux into enforcing mode and continues normal booting.
-//
-void PrepareApexSepolicy() {
-    bool supportsFsVerity = access(kFsVerityProcPath, F_OK) == 0;
-    if (supportsFsVerity) {
-        auto loadSepolicyApexCerts = LoadSepolicyApexCerts();
-        if (!loadSepolicyApexCerts.ok()) {
-            // TODO(b/199914227) If the device supports fsverity, but we fail here, we should fail
-            // to boot and not carry on. For now, fallback to a signature checkuntil the fsverity
-            // logic is implemented.
-            LOG(INFO) << loadSepolicyApexCerts.error();
-        }
-    }
-    // If apex sepolicy zip exists in /metadata/sepolicy, use that, otherwise use version on
-    // /system.
-    auto dir = (access((kSepolicyApexMetadataDir + kSepolicyZip).c_str(), F_OK) == 0)
-                       ? kSepolicyApexMetadataDir
-                       : kSepolicyApexSystemDir;
-
-    auto sepolicyVerify = SepolicyVerify(dir, supportsFsVerity);
-    if (!sepolicyVerify.ok()) {
-        LOG(INFO) << "Error: " << sepolicyVerify.error();
-        // If signature verification fails, fall back to version on /system.
-        // This file doesn't need to be verified because it lives on the system partition which
-        // is signed and protected by verified boot.
-        dir = kSepolicyApexSystemDir;
-    }
-
-    auto apex = GetPolicyFromApex(dir);
-    if (!apex.ok()) {
-        // TODO(b/199914227) Make failure fatal. For now continue booting with non-apex sepolicy.
-        LOG(ERROR) << apex.error();
-    }
-}
-
 void ReadPolicy(std::string* policy) {
     PolicyFile policy_file;
 
@@ -950,12 +740,9 @@
 
     LOG(INFO) << "Opening SELinux policy";
 
-    PrepareApexSepolicy();
-
     // Read the policy before potentially killing snapuserd.
     std::string policy;
     ReadPolicy(&policy);
-    CleanupApexSepolicy();
 
     auto snapuserd_helper = SnapuserdSelinuxHelper::CreateIfNeeded();
     if (snapuserd_helper) {
@@ -973,13 +760,6 @@
         snapuserd_helper = nullptr;
     }
 
-    // This restorecon is intentionally done before SelinuxSetEnforcement because the permissions
-    // needed to transition files from tmpfs to *_contexts_file context should not be granted to
-    // any process after selinux is set into enforcing mode.
-    if (selinux_android_restorecon("/dev/selinux/", SELINUX_ANDROID_RESTORECON_RECURSE) == -1) {
-        PLOG(FATAL) << "restorecon failed of /dev/selinux failed";
-    }
-
     SelinuxSetEnforcement();
 
     // We're in the kernel domain and want to transition to the init domain.  File systems that
diff --git a/init/service_utils.cpp b/init/service_utils.cpp
index f2383d7..263cb73 100644
--- a/init/service_utils.cpp
+++ b/init/service_utils.cpp
@@ -190,8 +190,6 @@
     // Fixup as we set O_NONBLOCK for open, the intent for fd is to block reads.
     fcntl(fd, F_SETFL, flags);
 
-    LOG(INFO) << "Opened file '" << name << "', flags " << flags;
-
     return Descriptor(ANDROID_FILE_ENV_PREFIX + name, std::move(fd));
 }
 
diff --git a/libsparse/img2simg.cpp b/libsparse/img2simg.cpp
index 4c2c6ca..3e24cc0 100644
--- a/libsparse/img2simg.cpp
+++ b/libsparse/img2simg.cpp
@@ -93,7 +93,7 @@
   }
 
   sparse_file_verbose(s);
-  ret = sparse_file_read(s, in, false, false);
+  ret = sparse_file_read(s, in, SPARSE_READ_MODE_NORMAL, false);
   if (ret) {
     fprintf(stderr, "Failed to read file\n");
     exit(-1);
diff --git a/libsparse/include/sparse/sparse.h b/libsparse/include/sparse/sparse.h
index 9f91269..7c52c3f 100644
--- a/libsparse/include/sparse/sparse.h
+++ b/libsparse/include/sparse/sparse.h
@@ -225,23 +225,42 @@
 	int (*write)(void *priv, const void *data, size_t len, unsigned int block,
 		     unsigned int nr_blocks),
 	void *priv);
+
+/**
+ * enum sparse_read_mode - The method to use when reading in files
+ * @SPARSE_READ_MODE_NORMAL: The input is a regular file. Constant chunks of
+ *                           data (including holes) will be be converted to
+ *                           fill chunks.
+ * @SPARSE_READ_MODE_SPARSE: The input is an Android sparse file.
+ * @SPARSE_READ_MODE_HOLE: The input is a regular file. Holes will be converted
+ *                         to "don't care" chunks. Other constant chunks will
+ *                         be converted to fill chunks.
+ */
+enum sparse_read_mode {
+	SPARSE_READ_MODE_NORMAL = false,
+	SPARSE_READ_MODE_SPARSE = true,
+	SPARSE_READ_MODE_HOLE,
+};
+
 /**
  * sparse_file_read - read a file into a sparse file cookie
  *
  * @s - sparse file cookie
  * @fd - file descriptor to read from
- * @sparse - read a file in the Android sparse file format
+ * @mode - mode to use when reading the input file
  * @crc - verify the crc of a file in the Android sparse file format
  *
- * Reads a file into a sparse file cookie.  If sparse is true, the file is
- * assumed to be in the Android sparse file format.  If sparse is false, the
- * file will be sparsed by looking for block aligned chunks of all zeros or
- * another 32 bit value.  If crc is true, the crc of the sparse file will be
- * verified.
+ * Reads a file into a sparse file cookie. If @mode is
+ * %SPARSE_READ_MODE_SPARSE, the file is assumed to be in the Android sparse
+ * file format. If @mode is %SPARSE_READ_MODE_NORMAL, the file will be sparsed
+ * by looking for block aligned chunks of all zeros or another 32 bit value. If
+ * @mode is %SPARSE_READ_MODE_HOLE, the file will be sparsed like
+ * %SPARSE_READ_MODE_NORMAL, but holes in the file will be converted to "don't
+ * care" chunks. If crc is true, the crc of the sparse file will be verified.
  *
  * Returns 0 on success, negative errno on error.
  */
-int sparse_file_read(struct sparse_file *s, int fd, bool sparse, bool crc);
+int sparse_file_read(struct sparse_file *s, int fd, enum sparse_read_mode mode, bool crc);
 
 /**
  * sparse_file_import - import an existing sparse file
diff --git a/libsparse/sparse_read.cpp b/libsparse/sparse_read.cpp
index 0f39172..028b6be 100644
--- a/libsparse/sparse_read.cpp
+++ b/libsparse/sparse_read.cpp
@@ -457,12 +457,10 @@
   return 0;
 }
 
-static int sparse_file_read_normal(struct sparse_file* s, int fd) {
+static int do_sparse_file_read_normal(struct sparse_file* s, int fd, uint32_t* buf, int64_t offset,
+                                      int64_t remain) {
   int ret;
-  uint32_t* buf = (uint32_t*)malloc(s->block_size);
-  unsigned int block = 0;
-  int64_t remain = s->len;
-  int64_t offset = 0;
+  unsigned int block = offset / s->block_size;
   unsigned int to_read;
   unsigned int i;
   bool sparse_block;
@@ -476,7 +474,6 @@
     ret = read_all(fd, buf, to_read);
     if (ret < 0) {
       error("failed to read sparse file");
-      free(buf);
       return ret;
     }
 
@@ -504,20 +501,93 @@
     block++;
   }
 
-  free(buf);
   return 0;
 }
 
-int sparse_file_read(struct sparse_file* s, int fd, bool sparse, bool crc) {
-  if (crc && !sparse) {
+static int sparse_file_read_normal(struct sparse_file* s, int fd) {
+  int ret;
+  uint32_t* buf = (uint32_t*)malloc(s->block_size);
+
+  if (!buf)
+    return -ENOMEM;
+
+  ret = do_sparse_file_read_normal(s, fd, buf, 0, s->len);
+  free(buf);
+  return ret;
+}
+
+#ifdef __linux__
+static int sparse_file_read_hole(struct sparse_file* s, int fd) {
+  int ret;
+  uint32_t* buf = (uint32_t*)malloc(s->block_size);
+  int64_t end = 0;
+  int64_t start = 0;
+
+  if (!buf) {
+    return -ENOMEM;
+  }
+
+  do {
+    start = lseek(fd, end, SEEK_DATA);
+    if (start < 0) {
+      if (errno == ENXIO)
+        /* The rest of the file is a hole */
+        break;
+
+      error("could not seek to data");
+      free(buf);
+      return -errno;
+    } else if (start > s->len) {
+      break;
+    }
+
+    end = lseek(fd, start, SEEK_HOLE);
+    if (end < 0) {
+      error("could not seek to end");
+      free(buf);
+      return -errno;
+    }
+    end = std::min(end, s->len);
+
+    start = ALIGN_DOWN(start, s->block_size);
+    end = ALIGN(end, s->block_size);
+    if (lseek(fd, start, SEEK_SET) < 0) {
+      free(buf);
+      return -errno;
+    }
+
+    ret = do_sparse_file_read_normal(s, fd, buf, start, end - start);
+    if (ret) {
+      free(buf);
+      return ret;
+    }
+  } while (end < s->len);
+
+  free(buf);
+  return 0;
+}
+#else
+static int sparse_file_read_hole(struct sparse_file* s __unused, int fd __unused) {
+  return -ENOTSUP;
+}
+#endif
+
+int sparse_file_read(struct sparse_file* s, int fd, enum sparse_read_mode mode, bool crc) {
+  if (crc && mode != SPARSE_READ_MODE_SPARSE) {
     return -EINVAL;
   }
 
-  if (sparse) {
-    SparseFileFdSource source(fd);
-    return sparse_file_read_sparse(s, &source, crc);
-  } else {
-    return sparse_file_read_normal(s, fd);
+  switch (mode) {
+    case SPARSE_READ_MODE_SPARSE: {
+      SparseFileFdSource source(fd);
+      return sparse_file_read_sparse(s, &source, crc);
+    }
+    case SPARSE_READ_MODE_NORMAL:
+      return sparse_file_read_normal(s, fd);
+    case SPARSE_READ_MODE_HOLE:
+      return sparse_file_read_hole(s, fd);
+    default:
+      return -EINVAL;
   }
 }
 
diff --git a/rootdir/etc/linker.config.json b/rootdir/etc/linker.config.json
index d62c41d..780ace5 100644
--- a/rootdir/etc/linker.config.json
+++ b/rootdir/etc/linker.config.json
@@ -20,6 +20,8 @@
     "libicuuc.so",
     // resolv
     "libnetd_resolv.so",
+    // netd
+    "libnetd_updatable.so",
     // nn
     "libneuralnetworks.so",
     // statsd
diff --git a/trusty/storage/proxy/storage.c b/trusty/storage/proxy/storage.c
index d74a708..c00c399 100644
--- a/trusty/storage/proxy/storage.c
+++ b/trusty/storage/proxy/storage.c
@@ -41,11 +41,9 @@
     SS_DIRTY =  1,
 };
 
-static int ssdir_fd = -1;
 static const char *ssdir_name;
 
 static enum sync_state fs_state;
-static enum sync_state dir_state;
 static enum sync_state fd_state[FD_TBL_SIZE];
 
 static bool alternate_mode;
@@ -59,10 +57,6 @@
 {
     uint32_t handle = fd;
 
-    if (open_flags & O_CREAT) {
-        dir_state = SS_DIRTY;
-    }
-
     if (handle < FD_TBL_SIZE) {
             fd_state[fd] = SS_CLEAN; /* fd clean */
             if (open_flags & O_TRUNC) {
@@ -193,7 +187,6 @@
         goto err_response;
     }
 
-    dir_state = SS_DIRTY;
     rc = unlink(path);
     if (rc < 0) {
         rc = errno;
@@ -217,12 +210,21 @@
     return ipc_respond(msg, NULL, 0);
 }
 
+static void sync_parent(const char* path) {
+    int parent_fd;
+    char* parent_path = dirname(path);
+    parent_fd = TEMP_FAILURE_RETRY(open(parent_path, O_RDONLY));
+    if (parent_fd >= 0) {
+        fsync(parent_fd);
+        close(parent_fd);
+    } else {
+        ALOGE("%s: failed to open parent directory \"%s\" for sync: %s\n", __func__, parent_path,
+              strerror(errno));
+    }
+}
 
-int storage_file_open(struct storage_msg *msg,
-                      const void *r, size_t req_len)
-{
-    char *path = NULL;
-    char* parent_path;
+int storage_file_open(struct storage_msg* msg, const void* r, size_t req_len) {
+    char* path = NULL;
     const struct storage_file_open_req *req = r;
     struct storage_file_open_resp resp = {0};
 
@@ -271,7 +273,6 @@
     if (req->flags & STORAGE_FILE_OPEN_TRUNCATE)
         open_flags |= O_TRUNC;
 
-    parent_path = dirname(path);
     if (req->flags & STORAGE_FILE_OPEN_CREATE) {
         /*
          * Create the alternate parent dir if needed & allowed.
@@ -281,8 +282,11 @@
          * it has access to the necessary bit of information.
          */
         if (strstr(req->name, ALTERNATE_DATA_DIR) == req->name) {
+            char* parent_path = dirname(path);
             rc = mkdir(parent_path, S_IRWXU);
-            if (rc && errno != EEXIST) {
+            if (rc == 0) {
+                sync_parent(parent_path);
+            } else if (errno != EEXIST) {
                 ALOGE("%s: Could not create parent directory \"%s\": %s\n", __func__, parent_path,
                       strerror(errno));
             }
@@ -320,6 +324,10 @@
         msg->result = translate_errno(rc);
         goto err_response;
     }
+
+    if (open_flags & O_CREAT) {
+        sync_parent(path);
+    }
     free(path);
 
     /* at this point rc contains storage file fd */
@@ -512,16 +520,10 @@
     alternate_mode = is_gsi_running();
 
     fs_state = SS_CLEAN;
-    dir_state = SS_CLEAN;
     for (uint i = 0; i < FD_TBL_SIZE; i++) {
         fd_state[i] = SS_UNUSED;  /* uninstalled */
     }
 
-    ssdir_fd = open(dirname, O_RDONLY);
-    if (ssdir_fd < 0) {
-        ALOGE("failed to open ss root dir \"%s\": %s\n",
-               dirname, strerror(errno));
-    }
     ssdir_name = dirname;
     return 0;
 }
@@ -545,25 +547,16 @@
          }
     }
 
-    /* check if we need to sync the directory */
-    if (dir_state == SS_DIRTY) {
-        if (fs_state == SS_CLEAN) {
-            rc = fsync(ssdir_fd);
-            if (rc < 0) {
-                ALOGE("fsync for ssdir failed: %s\n", strerror(errno));
-                return rc;
-            }
-        }
-        dir_state = SS_CLEAN;  /* set to clean */
-    }
-
-    /* check if we need to sync the whole fs */
+    /* check if we need to sync all filesystems */
     if (fs_state == SS_DIRTY) {
-        rc = syscall(SYS_syncfs, ssdir_fd);
-        if (rc < 0) {
-            ALOGE("syncfs failed: %s\n", strerror(errno));
-            return rc;
-        }
+        /*
+         * We sync all filesystems here because we don't know what filesystem
+         * needs syncing if there happen to be other filesystems symlinked under
+         * the root data directory. This should not happen in the normal case
+         * because our fd table is large enough to handle the few open files we
+         * use.
+         */
+        sync();
         fs_state = SS_CLEAN;
     }