Merge "fs_mgr: Add VTS tests for EROFS."
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/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
index a79e3e1..eb64704 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
@@ -599,8 +599,13 @@
         return false;
     }
 
-    // We must re-initialize property service access, since we launched before
-    // second-stage init.
+    // This initialization of system property is important. When daemon is
+    // launched post selinux transition (before init second stage),
+    // bionic libc initializes system property as part of __libc_init_common();
+    // however that initialization fails silently given that fact that we don't
+    // have /dev/__properties__ setup which is created at init second stage.
+    //
+    // At this point, we have the handlers setup and is safe to setup property.
     __system_properties_init();
 
     if (!android::base::WaitForProperty("snapuserd.proxy_ready", "true")) {
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index 377acb7..5890f9a 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -92,6 +92,7 @@
       mBatteryDevicePresent(false),
       mBatteryFixedCapacity(0),
       mBatteryFixedTemperature(0),
+      mChargerDockOnline(false),
       mHealthInfo(std::make_unique<HealthInfo_2_1>()) {
     initHealthInfo(mHealthInfo.get());
 }
@@ -196,6 +197,7 @@
             {"USB_PD", ANDROID_POWER_SUPPLY_TYPE_AC},
             {"USB_PD_DRP", ANDROID_POWER_SUPPLY_TYPE_USB},
             {"Wireless", ANDROID_POWER_SUPPLY_TYPE_WIRELESS},
+            {"Dock", ANDROID_POWER_SUPPLY_TYPE_DOCK},
             {NULL, 0},
     };
     std::string buf;
@@ -319,9 +321,21 @@
             case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:
                 props.chargerWirelessOnline = true;
                 break;
+            case ANDROID_POWER_SUPPLY_TYPE_DOCK:
+                mChargerDockOnline = true;
+                break;
             default:
-                KLOG_WARNING(LOG_TAG, "%s: Unknown power supply type\n",
-                             mChargerNames[i].string());
+                path.clear();
+                path.appendFormat("%s/%s/is_dock", POWER_SUPPLY_SYSFS_PATH,
+                                  mChargerNames[i].string());
+                if (access(path.string(), R_OK) == 0) {
+                    mChargerDockOnline = true;
+                    KLOG_INFO(LOG_TAG, "%s: online\n",
+                              mChargerNames[i].string());
+                } else {
+                    KLOG_WARNING(LOG_TAG, "%s: Unknown power supply type\n",
+                                 mChargerNames[i].string());
+                }
             }
             path.clear();
             path.appendFormat("%s/%s/current_max", POWER_SUPPLY_SYSFS_PATH,
@@ -391,8 +405,8 @@
 
 bool BatteryMonitor::isChargerOnline() {
     const HealthInfo_1_0& props = mHealthInfo->legacy.legacy;
-    return props.chargerAcOnline | props.chargerUsbOnline |
-            props.chargerWirelessOnline;
+    return props.chargerAcOnline | props.chargerUsbOnline | props.chargerWirelessOnline |
+           mChargerDockOnline;
 }
 
 int BatteryMonitor::getChargeStatus() {
@@ -477,10 +491,10 @@
     char vs[128];
     const HealthInfo_1_0& props = mHealthInfo->legacy.legacy;
 
-    snprintf(vs, sizeof(vs), "ac: %d usb: %d wireless: %d current_max: %d voltage_max: %d\n",
-             props.chargerAcOnline, props.chargerUsbOnline,
-             props.chargerWirelessOnline, props.maxChargingCurrent,
-             props.maxChargingVoltage);
+    snprintf(vs, sizeof(vs),
+             "ac: %d usb: %d wireless: %d dock: %d current_max: %d voltage_max: %d\n",
+             props.chargerAcOnline, props.chargerUsbOnline, props.chargerWirelessOnline,
+             mChargerDockOnline, props.maxChargingCurrent, props.maxChargingVoltage);
     write(fd, vs, strlen(vs));
     snprintf(vs, sizeof(vs), "status: %d health: %d present: %d\n",
              props.batteryStatus, props.batteryHealth, props.batteryPresent);
@@ -554,6 +568,7 @@
             case ANDROID_POWER_SUPPLY_TYPE_AC:
             case ANDROID_POWER_SUPPLY_TYPE_USB:
             case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:
+            case ANDROID_POWER_SUPPLY_TYPE_DOCK:
                 path.clear();
                 path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, name);
                 if (access(path.string(), R_OK) == 0)
@@ -691,6 +706,17 @@
             case ANDROID_POWER_SUPPLY_TYPE_UNKNOWN:
                 break;
             }
+
+            // Look for "is_dock" file
+            path.clear();
+            path.appendFormat("%s/%s/is_dock", POWER_SUPPLY_SYSFS_PATH, name);
+            if (access(path.string(), R_OK) == 0) {
+                path.clear();
+                path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, name);
+                if (access(path.string(), R_OK) == 0)
+                    mChargerNames.add(String8(name));
+
+            }
         }
     }
 
diff --git a/healthd/healthd_draw.cpp b/healthd/healthd_draw.cpp
index 4484fa6..3e73fcd 100644
--- a/healthd/healthd_draw.cpp
+++ b/healthd/healthd_draw.cpp
@@ -94,9 +94,18 @@
     gr_flip();
 }
 
-void HealthdDraw::blank_screen(bool blank) {
+void HealthdDraw::blank_screen(bool blank, int drm) {
     if (!graphics_available) return;
-    gr_fb_blank(blank);
+    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) {
@@ -139,6 +148,8 @@
 void HealthdDraw::determine_xy(const animation::text_field& field,
                                const int length, int* x, int* y) {
   *x = field.pos_x;
+  screen_width_ = gr_fb_width() / (kSplitScreen ? 2 : 1);
+  screen_height_ = gr_fb_height();
 
   int str_len_px = length * field.font->char_width;
   if (field.pos_x == CENTER_VAL) {
diff --git a/healthd/healthd_draw.h b/healthd/healthd_draw.h
index 0b48ce8..3d4abbd 100644
--- a/healthd/healthd_draw.h
+++ b/healthd/healthd_draw.h
@@ -31,8 +31,12 @@
   // Redraws screen.
   void redraw_screen(const animation* batt_anim, GRSurface* surf_unknown);
 
+  // According to the index of Direct Rendering Manager,
   // Blanks screen if true, unblanks if false.
-  virtual void blank_screen(bool blank);
+  virtual void blank_screen(bool blank, int drm);
+
+  // Rotate screen.
+  virtual void rotate_screen(int drm);
 
   static std::unique_ptr<HealthdDraw> Create(animation *anim);
 
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index 0f9779c..9fe85d4 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -327,7 +327,7 @@
 
 #if !defined(__ANDROID_VNDK__)
         if (android::sysprop::ChargerProperties::disable_init_blank().value_or(false)) {
-            healthd_draw_->blank_screen(true);
+            healthd_draw_->blank_screen(true, static_cast<int>(drm_));
             screen_blanked_ = true;
         }
 #endif
@@ -337,7 +337,7 @@
     if (batt_anim_.num_cycles > 0 && batt_anim_.cur_cycle == batt_anim_.num_cycles) {
         reset_animation(&batt_anim_);
         next_screen_transition_ = -1;
-        healthd_draw_->blank_screen(true);
+        healthd_draw_->blank_screen(true, static_cast<int>(drm_));
         screen_blanked_ = true;
         LOGV("[%" PRId64 "] animation done\n", now);
         if (configuration_->ChargerIsOnline()) {
@@ -348,8 +348,17 @@
 
     disp_time = batt_anim_.frames[batt_anim_.cur_frame].disp_time;
 
+    /* turn off all screen */
+    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;
+    }
+
     if (screen_blanked_) {
-        healthd_draw_->blank_screen(false);
+        healthd_draw_->blank_screen(false, static_cast<int>(drm_));
         screen_blanked_ = false;
     }
 
@@ -452,7 +461,26 @@
     return 0;
 }
 
+int Charger::SetSwCallback(int code, int value) {
+    if (code > SW_MAX) return -1;
+    if (code == SW_LID) {
+        if ((screen_switch_ == SCREEN_SWITCH_DEFAULT) || ((value != 0) && (drm_ == DRM_INNER)) ||
+            ((value == 0) && (drm_ == DRM_OUTER))) {
+            screen_switch_ = SCREEN_SWITCH_ENABLE;
+            drm_ = (value != 0) ? DRM_OUTER : DRM_INNER;
+            keys_[code].pending = true;
+        }
+    }
+
+    return 0;
+}
+
 void Charger::UpdateInputState(input_event* ev) {
+    if (ev->type == EV_SW && ev->code == SW_LID) {
+        SetSwCallback(ev->code, ev->value);
+        return;
+    }
+
     if (ev->type != EV_KEY) return;
     SetKeyCallback(ev->code, ev->value);
 }
@@ -511,10 +539,26 @@
     key->pending = false;
 }
 
+void Charger::ProcessHallSensor(int code) {
+    key_state* key = &keys_[code];
+
+    if (code == SW_LID) {
+        if (key->pending) {
+            reset_animation(&batt_anim_);
+            kick_animation(&batt_anim_);
+            RequestDisableSuspend();
+        }
+    }
+
+    key->pending = false;
+}
+
 void Charger::HandleInputState(int64_t now) {
     ProcessKey(KEY_POWER, now);
 
     if (next_key_check_ != -1 && now > next_key_check_) next_key_check_ = -1;
+
+    ProcessHallSensor(SW_LID);
 }
 
 void Charger::HandlePowerSupplyState(int64_t now) {
@@ -743,9 +787,14 @@
             batt_anim_.frames[i].surface = scale_frames[i];
         }
     }
+    drm_ = DRM_INNER;
+    screen_switch_ = SCREEN_SWITCH_DEFAULT;
     ev_sync_key_state(std::bind(&Charger::SetKeyCallback, this, std::placeholders::_1,
                                 std::placeholders::_2));
 
+    (void)ev_sync_sw_state(
+            std::bind(&Charger::SetSwCallback, this, std::placeholders::_1, std::placeholders::_2));
+
     next_screen_transition_ = -1;
     next_key_check_ = -1;
     next_pwr_check_ = -1;
diff --git a/healthd/include/healthd/BatteryMonitor.h b/healthd/include/healthd/BatteryMonitor.h
index 3cda727..89c2e25 100644
--- a/healthd/include/healthd/BatteryMonitor.h
+++ b/healthd/include/healthd/BatteryMonitor.h
@@ -48,7 +48,8 @@
         ANDROID_POWER_SUPPLY_TYPE_AC,
         ANDROID_POWER_SUPPLY_TYPE_USB,
         ANDROID_POWER_SUPPLY_TYPE_WIRELESS,
-        ANDROID_POWER_SUPPLY_TYPE_BATTERY
+        ANDROID_POWER_SUPPLY_TYPE_BATTERY,
+        ANDROID_POWER_SUPPLY_TYPE_DOCK
     };
 
     BatteryMonitor();
@@ -75,6 +76,8 @@
     bool mBatteryDevicePresent;
     int mBatteryFixedCapacity;
     int mBatteryFixedTemperature;
+    // TODO(b/214126090): to migrate to AIDL HealthInfo
+    bool mChargerDockOnline;
     std::unique_ptr<android::hardware::health::V2_1::HealthInfo> mHealthInfo;
 
     int readFromFile(const String8& path, std::string* buf);
diff --git a/healthd/include_charger/charger/healthd_mode_charger.h b/healthd/include_charger/charger/healthd_mode_charger.h
index 216e5ad..28e1fb5 100644
--- a/healthd/include_charger/charger/healthd_mode_charger.h
+++ b/healthd/include_charger/charger/healthd_mode_charger.h
@@ -44,6 +44,17 @@
     aidl::android::hardware::health::BatteryStatus battery_status;
 };
 
+enum DirectRenderManager {
+    DRM_INNER,
+    DRM_OUTER,
+};
+
+enum SrceenSwitch {
+    SCREEN_SWITCH_DEFAULT,
+    SCREEN_SWITCH_DISABLE,
+    SCREEN_SWITCH_ENABLE,
+};
+
 // Configuration interface for charger. This includes:
 // - HalHealthLoop APIs that interests charger.
 // - configuration values that used to be provided by sysprops
@@ -85,9 +96,11 @@
     void InitDefaultAnimationFrames();
     void UpdateScreenState(int64_t now);
     int SetKeyCallback(int code, int value);
+    int SetSwCallback(int code, int value);
     void UpdateInputState(input_event* ev);
     void SetNextKeyCheck(key_state* key, int64_t timeout);
     void ProcessKey(int code, int64_t now);
+    void ProcessHallSensor(int code);
     void HandleInputState(int64_t now);
     void HandlePowerSupplyState(int64_t now);
     int InputCallback(int fd, unsigned int epevents);
@@ -102,6 +115,9 @@
     int64_t next_pwr_check_ = 0;
     int64_t wait_batt_level_timestamp_ = 0;
 
+    DirectRenderManager drm_;
+    SrceenSwitch screen_switch_;
+
     key_state keys_[KEY_MAX + 1] = {};
 
     animation batt_anim_;
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 70e26ec..f2b2cda 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -1107,6 +1107,7 @@
     LoadPropertiesFromSecondStageRes(&properties);
     load_properties_from_file("/system/build.prop", nullptr, &properties);
     load_properties_from_partition("system_ext", /* support_legacy_path_until */ 30);
+    load_properties_from_file("/system_dlkm/etc/build.prop", nullptr, &properties);
     // TODO(b/117892318): uncomment the following condition when vendor.imgs for aosp_* targets are
     // all updated.
     // if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_R__) {
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/init/snapuserd_transition.cpp b/init/snapuserd_transition.cpp
index e11510e..5deaf31 100644
--- a/init/snapuserd_transition.cpp
+++ b/init/snapuserd_transition.cpp
@@ -32,6 +32,7 @@
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <cutils/sockets.h>
+#include <fs_avb/fs_avb.h>
 #include <libsnapshot/snapshot.h>
 #include <private/android_filesystem_config.h>
 #include <procinfo/process_map.h>
@@ -247,6 +248,56 @@
     }
 }
 
+/*
+ * Before starting init second stage, we will wait
+ * for snapuserd daemon to be up and running; bionic libc
+ * may read /system/etc/selinux/plat_property_contexts file
+ * before invoking main() function. This will happen if
+ * init initializes property during second stage. Any access
+ * to /system without snapuserd daemon will lead to a deadlock.
+ *
+ * Thus, we do a simple probe by reading system partition. This
+ * read will eventually be serviced by daemon confirming that
+ * daemon is up and running. Furthermore, we are still in the kernel
+ * domain and sepolicy has not been enforced yet. Thus, access
+ * to these device mapper block devices are ok even though
+ * we may see audit logs.
+ */
+bool SnapuserdSelinuxHelper::TestSnapuserdIsReady() {
+    std::string dev = "/dev/block/mapper/system"s + fs_mgr_get_slot_suffix();
+    android::base::unique_fd fd(open(dev.c_str(), O_RDONLY | O_DIRECT));
+    if (fd < 0) {
+        PLOG(ERROR) << "open " << dev << " failed";
+        return false;
+    }
+
+    void* addr;
+    ssize_t page_size = getpagesize();
+    if (posix_memalign(&addr, page_size, page_size) < 0) {
+        PLOG(ERROR) << "posix_memalign with page size " << page_size;
+        return false;
+    }
+
+    std::unique_ptr<void, decltype(&::free)> buffer(addr, ::free);
+
+    int iter = 0;
+    while (iter < 10) {
+        ssize_t n = TEMP_FAILURE_RETRY(pread(fd.get(), buffer.get(), page_size, 0));
+        if (n < 0) {
+            // Wait for sometime before retry
+            std::this_thread::sleep_for(100ms);
+        } else if (n == page_size) {
+            return true;
+        } else {
+            LOG(ERROR) << "pread returned: " << n << " from: " << dev << " expected: " << page_size;
+        }
+
+        iter += 1;
+    }
+
+    return false;
+}
+
 void SnapuserdSelinuxHelper::RelaunchFirstStageSnapuserd() {
     auto fd = GetRamdiskSnapuserdFd();
     if (!fd) {
@@ -268,6 +319,13 @@
         setenv(kSnapuserdFirstStagePidVar, std::to_string(pid).c_str(), 1);
 
         LOG(INFO) << "Relaunched snapuserd with pid: " << pid;
+
+        if (!TestSnapuserdIsReady()) {
+            PLOG(FATAL) << "snapuserd daemon failed to launch";
+        } else {
+            LOG(INFO) << "snapuserd daemon is up and running";
+        }
+
         return;
     }
 
diff --git a/init/snapuserd_transition.h b/init/snapuserd_transition.h
index be22afd..557d105 100644
--- a/init/snapuserd_transition.h
+++ b/init/snapuserd_transition.h
@@ -56,6 +56,7 @@
   private:
     void RelaunchFirstStageSnapuserd();
     void ExecSnapuserd();
+    bool TestSnapuserdIsReady();
 
     std::unique_ptr<SnapshotManager> sm_;
     BlockDevInitializer block_dev_init_;
diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h
index be34f95..c5badc9 100644
--- a/libprocessgroup/include/processgroup/processgroup.h
+++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -35,6 +35,8 @@
 
 #ifndef __ANDROID_VNDK__
 
+bool SetProcessProfilesCached(uid_t uid, pid_t pid, const std::vector<std::string>& profiles);
+
 static constexpr const char* CGROUPS_RC_PATH = "/dev/cgroup_info/cgroup.rc";
 
 bool UsePerAppMemcg();
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 0320b02..cb2fe0a 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -126,11 +126,16 @@
 }
 
 void DropTaskProfilesResourceCaching() {
-    TaskProfiles::GetInstance().DropResourceCaching();
+    TaskProfiles::GetInstance().DropResourceCaching(ProfileAction::RCT_TASK);
+    TaskProfiles::GetInstance().DropResourceCaching(ProfileAction::RCT_PROCESS);
 }
 
 bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles) {
-    return TaskProfiles::GetInstance().SetProcessProfiles(uid, pid, profiles);
+    return TaskProfiles::GetInstance().SetProcessProfiles(uid, pid, profiles, false);
+}
+
+bool SetProcessProfilesCached(uid_t uid, pid_t pid, const std::vector<std::string>& profiles) {
+    return TaskProfiles::GetInstance().SetProcessProfiles(uid, pid, profiles, true);
 }
 
 bool SetTaskProfiles(int tid, const std::vector<std::string>& profiles, bool use_fd_cache) {
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index 3834f91..74ba7f6 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -51,6 +51,67 @@
 static constexpr const char* TEMPLATE_TASK_PROFILE_API_FILE =
         "/etc/task_profiles/task_profiles_%u.json";
 
+class FdCacheHelper {
+  public:
+    enum FdState {
+        FDS_INACCESSIBLE = -1,
+        FDS_APP_DEPENDENT = -2,
+        FDS_NOT_CACHED = -3,
+    };
+
+    static void Cache(const std::string& path, android::base::unique_fd& fd);
+    static void Drop(android::base::unique_fd& fd);
+    static void Init(const std::string& path, android::base::unique_fd& fd);
+    static bool IsCached(const android::base::unique_fd& fd) { return fd > FDS_INACCESSIBLE; }
+
+  private:
+    static bool IsAppDependentPath(const std::string& path);
+};
+
+void FdCacheHelper::Init(const std::string& path, android::base::unique_fd& fd) {
+    // file descriptors for app-dependent paths can't be cached
+    if (IsAppDependentPath(path)) {
+        // file descriptor is not cached
+        fd.reset(FDS_APP_DEPENDENT);
+        return;
+    }
+    // file descriptor can be cached later on request
+    fd.reset(FDS_NOT_CACHED);
+}
+
+void FdCacheHelper::Cache(const std::string& path, android::base::unique_fd& fd) {
+    if (fd != FDS_NOT_CACHED) {
+        return;
+    }
+
+    if (access(path.c_str(), W_OK) != 0) {
+        // file is not accessible
+        fd.reset(FDS_INACCESSIBLE);
+        return;
+    }
+
+    unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_WRONLY | O_CLOEXEC)));
+    if (tmp_fd < 0) {
+        PLOG(ERROR) << "Failed to cache fd '" << path << "'";
+        fd.reset(FDS_INACCESSIBLE);
+        return;
+    }
+
+    fd = std::move(tmp_fd);
+}
+
+void FdCacheHelper::Drop(android::base::unique_fd& fd) {
+    if (fd == FDS_NOT_CACHED) {
+        return;
+    }
+
+    fd.reset(FDS_NOT_CACHED);
+}
+
+bool FdCacheHelper::IsAppDependentPath(const std::string& path) {
+    return path.find("<uid>", 0) != std::string::npos || path.find("<pid>", 0) != std::string::npos;
+}
+
 void ProfileAttribute::Reset(const CgroupController& controller, const std::string& file_name) {
     controller_ = controller;
     file_name_ = file_name;
@@ -144,57 +205,11 @@
     return true;
 }
 
-void CachedFdProfileAction::EnableResourceCaching() {
-    std::lock_guard<std::mutex> lock(fd_mutex_);
-    if (fd_ != FDS_NOT_CACHED) {
-        return;
-    }
-
-    std::string tasks_path = GetPath();
-
-    if (access(tasks_path.c_str(), W_OK) != 0) {
-        // file is not accessible
-        fd_.reset(FDS_INACCESSIBLE);
-        return;
-    }
-
-    unique_fd fd(TEMP_FAILURE_RETRY(open(tasks_path.c_str(), O_WRONLY | O_CLOEXEC)));
-    if (fd < 0) {
-        PLOG(ERROR) << "Failed to cache fd '" << tasks_path << "'";
-        fd_.reset(FDS_INACCESSIBLE);
-        return;
-    }
-
-    fd_ = std::move(fd);
-}
-
-void CachedFdProfileAction::DropResourceCaching() {
-    std::lock_guard<std::mutex> lock(fd_mutex_);
-    if (fd_ == FDS_NOT_CACHED) {
-        return;
-    }
-
-    fd_.reset(FDS_NOT_CACHED);
-}
-
-bool CachedFdProfileAction::IsAppDependentPath(const std::string& path) {
-    return path.find("<uid>", 0) != std::string::npos || path.find("<pid>", 0) != std::string::npos;
-}
-
-void CachedFdProfileAction::InitFd(const std::string& path) {
-    // file descriptors for app-dependent paths can't be cached
-    if (IsAppDependentPath(path)) {
-        // file descriptor is not cached
-        fd_.reset(FDS_APP_DEPENDENT);
-        return;
-    }
-    // file descriptor can be cached later on request
-    fd_.reset(FDS_NOT_CACHED);
-}
-
 SetCgroupAction::SetCgroupAction(const CgroupController& c, const std::string& p)
     : controller_(c), path_(p) {
-    InitFd(controller_.GetTasksFilePath(path_));
+    FdCacheHelper::Init(controller_.GetTasksFilePath(path_), fd_[ProfileAction::RCT_TASK]);
+    // uid and pid don't matter because IsAppDependentPath ensures the path doesn't use them
+    FdCacheHelper::Init(controller_.GetProcsFilePath(path_, 0, 0), fd_[ProfileAction::RCT_PROCESS]);
 }
 
 bool SetCgroupAction::AddTidToCgroup(int tid, int fd, const char* controller_name) {
@@ -232,7 +247,40 @@
     return false;
 }
 
+ProfileAction::CacheUseResult SetCgroupAction::UseCachedFd(ResourceCacheType cache_type,
+                                                           int id) const {
+    std::lock_guard<std::mutex> lock(fd_mutex_);
+    if (FdCacheHelper::IsCached(fd_[cache_type])) {
+        // fd is cached, reuse it
+        if (!AddTidToCgroup(id, fd_[cache_type], controller()->name())) {
+            LOG(ERROR) << "Failed to add task into cgroup";
+            return ProfileAction::FAIL;
+        }
+        return ProfileAction::SUCCESS;
+    }
+
+    if (fd_[cache_type] == FdCacheHelper::FDS_INACCESSIBLE) {
+        // no permissions to access the file, ignore
+        return ProfileAction::SUCCESS;
+    }
+
+    if (cache_type == ResourceCacheType::RCT_TASK &&
+        fd_[cache_type] == FdCacheHelper::FDS_APP_DEPENDENT) {
+        // application-dependent path can't be used with tid
+        PLOG(ERROR) << "Application profile can't be applied to a thread";
+        return ProfileAction::FAIL;
+    }
+
+    return ProfileAction::UNUSED;
+}
+
 bool SetCgroupAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
+    CacheUseResult result = UseCachedFd(ProfileAction::RCT_PROCESS, pid);
+    if (result != ProfileAction::UNUSED) {
+        return result == ProfileAction::SUCCESS;
+    }
+
+    // fd was not cached or cached fd can't be used
     std::string procs_path = controller()->GetProcsFilePath(path_, uid, pid);
     unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(procs_path.c_str(), O_WRONLY | O_CLOEXEC)));
     if (tmp_fd < 0) {
@@ -248,28 +296,12 @@
 }
 
 bool SetCgroupAction::ExecuteForTask(int tid) const {
-    std::lock_guard<std::mutex> lock(fd_mutex_);
-    if (IsFdValid()) {
-        // fd is cached, reuse it
-        if (!AddTidToCgroup(tid, fd_, controller()->name())) {
-            LOG(ERROR) << "Failed to add task into cgroup";
-            return false;
-        }
-        return true;
+    CacheUseResult result = UseCachedFd(ProfileAction::RCT_TASK, tid);
+    if (result != ProfileAction::UNUSED) {
+        return result == ProfileAction::SUCCESS;
     }
 
-    if (fd_ == FDS_INACCESSIBLE) {
-        // no permissions to access the file, ignore
-        return true;
-    }
-
-    if (fd_ == FDS_APP_DEPENDENT) {
-        // application-dependent path can't be used with tid
-        PLOG(ERROR) << "Application profile can't be applied to a thread";
-        return false;
-    }
-
-    // fd was not cached because cached fd can't be used
+    // fd was not cached or cached fd can't be used
     std::string tasks_path = controller()->GetTasksFilePath(path_);
     unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(tasks_path.c_str(), O_WRONLY | O_CLOEXEC)));
     if (tmp_fd < 0) {
@@ -284,10 +316,36 @@
     return true;
 }
 
+void SetCgroupAction::EnableResourceCaching(ResourceCacheType cache_type) {
+    std::lock_guard<std::mutex> lock(fd_mutex_);
+    // Return early to prevent unnecessary calls to controller_.Get{Tasks|Procs}FilePath() which
+    // include regex evaluations
+    if (fd_[cache_type] != FdCacheHelper::FDS_NOT_CACHED) {
+        return;
+    }
+    switch (cache_type) {
+        case (ProfileAction::RCT_TASK):
+            FdCacheHelper::Cache(controller_.GetTasksFilePath(path_), fd_[cache_type]);
+            break;
+        case (ProfileAction::RCT_PROCESS):
+            // uid and pid don't matter because IsAppDependentPath ensures the path doesn't use them
+            FdCacheHelper::Cache(controller_.GetProcsFilePath(path_, 0, 0), fd_[cache_type]);
+            break;
+        default:
+            LOG(ERROR) << "Invalid cache type is specified!";
+            break;
+    }
+}
+
+void SetCgroupAction::DropResourceCaching(ResourceCacheType cache_type) {
+    std::lock_guard<std::mutex> lock(fd_mutex_);
+    FdCacheHelper::Drop(fd_[cache_type]);
+}
+
 WriteFileAction::WriteFileAction(const std::string& path, const std::string& value,
                                  bool logfailures)
     : path_(path), value_(value), logfailures_(logfailures) {
-    InitFd(path_);
+    FdCacheHelper::Init(path_, fd_);
 }
 
 bool WriteFileAction::WriteValueToFile(const std::string& value, const std::string& path,
@@ -309,13 +367,43 @@
     return true;
 }
 
-bool WriteFileAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
+ProfileAction::CacheUseResult WriteFileAction::UseCachedFd(ResourceCacheType cache_type,
+                                                           const std::string& value) const {
     std::lock_guard<std::mutex> lock(fd_mutex_);
+    if (FdCacheHelper::IsCached(fd_)) {
+        // fd is cached, reuse it
+        if (!WriteStringToFd(value, fd_)) {
+            if (logfailures_) PLOG(ERROR) << "Failed to write '" << value << "' to " << path_;
+            return ProfileAction::FAIL;
+        }
+        return ProfileAction::SUCCESS;
+    }
+
+    if (fd_ == FdCacheHelper::FDS_INACCESSIBLE) {
+        // no permissions to access the file, ignore
+        return ProfileAction::SUCCESS;
+    }
+
+    if (cache_type == ResourceCacheType::RCT_TASK && fd_ == FdCacheHelper::FDS_APP_DEPENDENT) {
+        // application-dependent path can't be used with tid
+        PLOG(ERROR) << "Application profile can't be applied to a thread";
+        return ProfileAction::FAIL;
+    }
+    return ProfileAction::UNUSED;
+}
+
+bool WriteFileAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
     std::string value(value_);
-    std::string path(path_);
 
     value = StringReplace(value, "<uid>", std::to_string(uid), true);
     value = StringReplace(value, "<pid>", std::to_string(pid), true);
+
+    CacheUseResult result = UseCachedFd(ProfileAction::RCT_PROCESS, value);
+    if (result != ProfileAction::UNUSED) {
+        return result == ProfileAction::SUCCESS;
+    }
+
+    std::string path(path_);
     path = StringReplace(path, "<uid>", std::to_string(uid), true);
     path = StringReplace(path, "<pid>", std::to_string(pid), true);
 
@@ -323,41 +411,33 @@
 }
 
 bool WriteFileAction::ExecuteForTask(int tid) const {
-    std::lock_guard<std::mutex> lock(fd_mutex_);
     std::string value(value_);
     int uid = getuid();
 
     value = StringReplace(value, "<uid>", std::to_string(uid), true);
     value = StringReplace(value, "<pid>", std::to_string(tid), true);
 
-    if (IsFdValid()) {
-        // fd is cached, reuse it
-        if (!WriteStringToFd(value, fd_)) {
-            if (logfailures_) PLOG(ERROR) << "Failed to write '" << value << "' to " << path_;
-            return false;
-        }
-        return true;
-    }
-
-    if (fd_ == FDS_INACCESSIBLE) {
-        // no permissions to access the file, ignore
-        return true;
-    }
-
-    if (fd_ == FDS_APP_DEPENDENT) {
-        // application-dependent path can't be used with tid
-        PLOG(ERROR) << "Application profile can't be applied to a thread";
-        return false;
+    CacheUseResult result = UseCachedFd(ProfileAction::RCT_TASK, value);
+    if (result != ProfileAction::UNUSED) {
+        return result == ProfileAction::SUCCESS;
     }
 
     return WriteValueToFile(value, path_, logfailures_);
 }
 
+void WriteFileAction::EnableResourceCaching(ResourceCacheType) {
+    std::lock_guard<std::mutex> lock(fd_mutex_);
+    FdCacheHelper::Cache(path_, fd_);
+}
+
+void WriteFileAction::DropResourceCaching(ResourceCacheType) {
+    std::lock_guard<std::mutex> lock(fd_mutex_);
+    FdCacheHelper::Drop(fd_);
+}
+
 bool ApplyProfileAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
     for (const auto& profile : profiles_) {
-        if (!profile->ExecuteForProcess(uid, pid)) {
-            PLOG(WARNING) << "ExecuteForProcess failed for aggregate profile";
-        }
+        profile->ExecuteForProcess(uid, pid);
     }
     return true;
 }
@@ -369,15 +449,15 @@
     return true;
 }
 
-void ApplyProfileAction::EnableResourceCaching() {
+void ApplyProfileAction::EnableResourceCaching(ResourceCacheType cache_type) {
     for (const auto& profile : profiles_) {
-        profile->EnableResourceCaching();
+        profile->EnableResourceCaching(cache_type);
     }
 }
 
-void ApplyProfileAction::DropResourceCaching() {
+void ApplyProfileAction::DropResourceCaching(ResourceCacheType cache_type) {
     for (const auto& profile : profiles_) {
-        profile->DropResourceCaching();
+        profile->DropResourceCaching(cache_type);
     }
 }
 
@@ -407,33 +487,33 @@
     return true;
 }
 
-void TaskProfile::EnableResourceCaching() {
+void TaskProfile::EnableResourceCaching(ProfileAction::ResourceCacheType cache_type) {
     if (res_cached_) {
         return;
     }
 
     for (auto& element : elements_) {
-        element->EnableResourceCaching();
+        element->EnableResourceCaching(cache_type);
     }
 
     res_cached_ = true;
 }
 
-void TaskProfile::DropResourceCaching() {
+void TaskProfile::DropResourceCaching(ProfileAction::ResourceCacheType cache_type) {
     if (!res_cached_) {
         return;
     }
 
     for (auto& element : elements_) {
-        element->DropResourceCaching();
+        element->DropResourceCaching(cache_type);
     }
 
     res_cached_ = false;
 }
 
-void TaskProfiles::DropResourceCaching() const {
+void TaskProfiles::DropResourceCaching(ProfileAction::ResourceCacheType cache_type) const {
     for (auto& iter : profiles_) {
-        iter.second->DropResourceCaching();
+        iter.second->DropResourceCaching(cache_type);
     }
 }
 
@@ -457,8 +537,7 @@
                 android::base::StringPrintf(TEMPLATE_TASK_PROFILE_API_FILE, api_level);
         if (!access(api_profiles_path.c_str(), F_OK) || errno != ENOENT) {
             if (!Load(CgroupMap::GetInstance(), api_profiles_path)) {
-                LOG(ERROR) << "Loading " << api_profiles_path << " for [" << getpid()
-                           << "] failed";
+                LOG(ERROR) << "Loading " << api_profiles_path << " for [" << getpid() << "] failed";
             }
         }
     }
@@ -651,10 +730,13 @@
 }
 
 bool TaskProfiles::SetProcessProfiles(uid_t uid, pid_t pid,
-                                      const std::vector<std::string>& profiles) {
+                                      const std::vector<std::string>& profiles, bool use_fd_cache) {
     for (const auto& name : profiles) {
         TaskProfile* profile = GetProfile(name);
         if (profile != nullptr) {
+            if (use_fd_cache) {
+                profile->EnableResourceCaching(ProfileAction::RCT_PROCESS);
+            }
             if (!profile->ExecuteForProcess(uid, pid)) {
                 PLOG(WARNING) << "Failed to apply " << name << " process profile";
             }
@@ -671,7 +753,7 @@
         TaskProfile* profile = GetProfile(name);
         if (profile != nullptr) {
             if (use_fd_cache) {
-                profile->EnableResourceCaching();
+                profile->EnableResourceCaching(ProfileAction::RCT_TASK);
             }
             if (!profile->ExecuteForTask(tid)) {
                 PLOG(WARNING) << "Failed to apply " << name << " task profile";
diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h
index 278892d..1aaa196 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -45,14 +45,19 @@
 // Abstract profile element
 class ProfileAction {
   public:
+    enum ResourceCacheType { RCT_TASK = 0, RCT_PROCESS, RCT_COUNT };
+
     virtual ~ProfileAction() {}
 
     // Default implementations will fail
     virtual bool ExecuteForProcess(uid_t, pid_t) const { return false; };
     virtual bool ExecuteForTask(int) const { return false; };
 
-    virtual void EnableResourceCaching() {}
-    virtual void DropResourceCaching() {}
+    virtual void EnableResourceCaching(ResourceCacheType) {}
+    virtual void DropResourceCaching(ResourceCacheType) {}
+
+  protected:
+    enum CacheUseResult { SUCCESS, FAIL, UNUSED };
 };
 
 // Profile actions
@@ -108,67 +113,47 @@
     std::string value_;
 };
 
-// Abstract profile element for cached fd
-class CachedFdProfileAction : public ProfileAction {
-  public:
-    virtual void EnableResourceCaching();
-    virtual void DropResourceCaching();
-
-  protected:
-    enum FdState {
-        FDS_INACCESSIBLE = -1,
-        FDS_APP_DEPENDENT = -2,
-        FDS_NOT_CACHED = -3,
-    };
-
-    android::base::unique_fd fd_;
-    mutable std::mutex fd_mutex_;
-
-    static bool IsAppDependentPath(const std::string& path);
-
-    void InitFd(const std::string& path);
-    bool IsFdValid() const { return fd_ > FDS_INACCESSIBLE; }
-
-    virtual const std::string GetPath() const = 0;
-};
-
 // Set cgroup profile element
-class SetCgroupAction : public CachedFdProfileAction {
+class SetCgroupAction : public ProfileAction {
   public:
     SetCgroupAction(const CgroupController& c, const std::string& p);
 
     virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
     virtual bool ExecuteForTask(int tid) const;
+    virtual void EnableResourceCaching(ResourceCacheType cache_type);
+    virtual void DropResourceCaching(ResourceCacheType cache_type);
 
     const CgroupController* controller() const { return &controller_; }
 
-  protected:
-    const std::string GetPath() const override { return controller_.GetTasksFilePath(path_); }
-
   private:
     CgroupController controller_;
     std::string path_;
+    android::base::unique_fd fd_[ProfileAction::RCT_COUNT];
+    mutable std::mutex fd_mutex_;
 
     static bool AddTidToCgroup(int tid, int fd, const char* controller_name);
+    CacheUseResult UseCachedFd(ResourceCacheType cache_type, int id) const;
 };
 
 // Write to file action
-class WriteFileAction : public CachedFdProfileAction {
+class WriteFileAction : public ProfileAction {
   public:
     WriteFileAction(const std::string& path, const std::string& value, bool logfailures);
 
     virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
     virtual bool ExecuteForTask(int tid) const;
-
-  protected:
-    const std::string GetPath() const override { return path_; }
+    virtual void EnableResourceCaching(ResourceCacheType cache_type);
+    virtual void DropResourceCaching(ResourceCacheType cache_type);
 
   private:
     std::string path_, value_;
     bool logfailures_;
+    android::base::unique_fd fd_;
+    mutable std::mutex fd_mutex_;
 
     static bool WriteValueToFile(const std::string& value, const std::string& path,
                                  bool logfailures);
+    CacheUseResult UseCachedFd(ResourceCacheType cache_type, const std::string& value) const;
 };
 
 class TaskProfile {
@@ -180,8 +165,8 @@
 
     bool ExecuteForProcess(uid_t uid, pid_t pid) const;
     bool ExecuteForTask(int tid) const;
-    void EnableResourceCaching();
-    void DropResourceCaching();
+    void EnableResourceCaching(ProfileAction::ResourceCacheType cache_type);
+    void DropResourceCaching(ProfileAction::ResourceCacheType cache_type);
 
   private:
     bool res_cached_;
@@ -196,8 +181,8 @@
 
     virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
     virtual bool ExecuteForTask(int tid) const;
-    virtual void EnableResourceCaching();
-    virtual void DropResourceCaching();
+    virtual void EnableResourceCaching(ProfileAction::ResourceCacheType cache_type);
+    virtual void DropResourceCaching(ProfileAction::ResourceCacheType cache_type);
 
   private:
     std::vector<std::shared_ptr<TaskProfile>> profiles_;
@@ -210,8 +195,9 @@
 
     TaskProfile* GetProfile(const std::string& name) const;
     const ProfileAttribute* GetAttribute(const std::string& name) const;
-    void DropResourceCaching() const;
-    bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles);
+    void DropResourceCaching(ProfileAction::ResourceCacheType cache_type) const;
+    bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles,
+                            bool use_fd_cache);
     bool SetTaskProfiles(int tid, const std::vector<std::string>& profiles, bool use_fd_cache);
 
   private:
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/libusbhost/Android.bp b/libusbhost/Android.bp
index 3883317..9ae73d0 100644
--- a/libusbhost/Android.bp
+++ b/libusbhost/Android.bp
@@ -30,11 +30,9 @@
     export_include_dirs: ["include"],
     target: {
         android: {
-            cflags: [
-                "-g",
-                "-DUSE_LIBLOG",
-            ],
+            header_libs: ["jni_headers"],
             shared_libs: ["liblog"],
+            srcs: ["usbhost_jni.cpp"],
         },
         darwin: {
             enabled: false,
diff --git a/libusbhost/include/usbhost/usbhost.h b/libusbhost/include/usbhost/usbhost.h
index 7e62542..01cd68b 100644
--- a/libusbhost/include/usbhost/usbhost.h
+++ b/libusbhost/include/usbhost/usbhost.h
@@ -21,6 +21,7 @@
 extern "C" {
 #endif
 
+#include <stddef.h>
 #include <stdint.h>
 
 #include <linux/version.h>
diff --git a/libusbhost/include/usbhost/usbhost_jni.h b/libusbhost/include/usbhost/usbhost_jni.h
new file mode 100644
index 0000000..4885d45
--- /dev/null
+++ b/libusbhost/include/usbhost/usbhost_jni.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2022 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 <jni.h>
+
+/**
+ * Reads USB descriptors from `fd`.
+ *
+ * Returns a byte[] on success,
+ * or returns NULL and logs an appropriate error on failure.
+ */
+jbyteArray usb_jni_read_descriptors(JNIEnv* env, int fd);
diff --git a/libusbhost/usbhost.c b/libusbhost/usbhost.c
index 3bed0e3..d8f15cd 100644
--- a/libusbhost/usbhost.c
+++ b/libusbhost/usbhost.c
@@ -18,20 +18,9 @@
 #define _GNU_SOURCE
 #endif
 
-// #define DEBUG 1
-#if DEBUG
+#include <usbhost/usbhost.h>
 
-#ifdef USE_LIBLOG
-#define LOG_TAG "usbhost"
-#include "log/log.h"
-#define D ALOGD
-#else
-#define D printf
-#endif
-
-#else
-#define D(...)
-#endif
+#include "usbhost_private.h"
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -48,12 +37,19 @@
 #include <errno.h>
 #include <ctype.h>
 #include <poll.h>
-#include <pthread.h>
 
 #include <linux/usbdevice_fs.h>
-#include <asm/byteorder.h>
 
-#include "usbhost/usbhost.h"
+// #define DEBUG 1
+#if defined(DEBUG)
+#if defined(__BIONIC__)
+#define D ALOGD
+#else
+#define D printf
+#endif
+#else
+#define D(...)
+#endif
 
 #define DEV_DIR             "/dev"
 #define DEV_BUS_DIR         DEV_DIR "/bus"
@@ -76,8 +72,6 @@
     int                         wddbus;
 };
 
-#define MAX_DESCRIPTORS_LENGTH 4096
-
 struct usb_device {
     char dev_name[64];
     unsigned char desc[MAX_DESCRIPTORS_LENGTH];
diff --git a/libusbhost/usbhost_jni.cpp b/libusbhost/usbhost_jni.cpp
new file mode 100644
index 0000000..0da83dc
--- /dev/null
+++ b/libusbhost/usbhost_jni.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 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 <usbhost/usbhost_jni.h>
+
+#include "usbhost_private.h"
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+jbyteArray usb_jni_read_descriptors(JNIEnv* env, int fd) {
+    if (TEMP_FAILURE_RETRY(lseek(fd, 0, SEEK_SET)) == -1) {
+        ALOGE("usb_jni_read_descriptors(%d): lseek() failed: %s", fd, strerror(errno));
+        return NULL;
+    }
+
+    jbyte buf[MAX_DESCRIPTORS_LENGTH];
+    ssize_t n = TEMP_FAILURE_RETRY(read(fd, buf, sizeof(buf)));
+    if (n == -1) {
+        ALOGE("usb_jni_read_descriptors: read failed: %s", strerror(errno));
+        return NULL;
+    }
+
+    jbyteArray result = env->NewByteArray(n);
+    if (result) env->SetByteArrayRegion(result, 0, n, buf);
+    return result;
+}
diff --git a/libusbhost/usbhost_private.h b/libusbhost/usbhost_private.h
new file mode 100644
index 0000000..72d7938
--- /dev/null
+++ b/libusbhost/usbhost_private.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2022 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
+
+#define LOG_TAG "usbhost"
+#include <log/log.h>
+
+// Somewhat arbitrary: Sony has reported needing more than 4KiB (but less
+// than 8KiB), and some frameworks code had 16KiB without any explanation,
+// so we went with the largest of those.
+#define MAX_DESCRIPTORS_LENGTH (16 * 1024)
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index ce2ec0e..d592366 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -110,9 +110,6 @@
 ifdef BOARD_USES_METADATA_PARTITION
   LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/metadata
 endif
-ifdef BOARD_USES_SYSTEM_DLKM_PARTITION
-  LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/system_dlkm
-endif
 
 # For /odm partition.
 LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/odm
@@ -152,6 +149,9 @@
 # via /odm/lib/modules directly.
 LOCAL_POST_INSTALL_CMD += ; ln -sf /odm/odm_dlkm/etc $(TARGET_ROOT_OUT)/odm_dlkm/etc
 
+# For /system_dlkm partition
+LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/system_dlkm
+
 ifdef BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE
   LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/cache
 else
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/rootdir/init.rc b/rootdir/init.rc
index cd73498..c4c9eca 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -142,11 +142,21 @@
     chown system system /dev/stune/background/tasks
     chown system system /dev/stune/top-app/tasks
     chown system system /dev/stune/rt/tasks
+    chown system system /dev/stune/cgroup.procs
+    chown system system /dev/stune/foreground/cgroup.procs
+    chown system system /dev/stune/background/cgroup.procs
+    chown system system /dev/stune/top-app/cgroup.procs
+    chown system system /dev/stune/rt/cgroup.procs
     chmod 0664 /dev/stune/tasks
     chmod 0664 /dev/stune/foreground/tasks
     chmod 0664 /dev/stune/background/tasks
     chmod 0664 /dev/stune/top-app/tasks
     chmod 0664 /dev/stune/rt/tasks
+    chmod 0664 /dev/stune/cgroup.procs
+    chmod 0664 /dev/stune/foreground/cgroup.procs
+    chmod 0664 /dev/stune/background/cgroup.procs
+    chmod 0664 /dev/stune/top-app/cgroup.procs
+    chmod 0664 /dev/stune/rt/cgroup.procs
 
     # cpuctl hierarchy for devices using utilclamp
     mkdir /dev/cpuctl/foreground
@@ -172,6 +182,14 @@
     chown system system /dev/cpuctl/system/tasks
     chown system system /dev/cpuctl/system-background/tasks
     chown system system /dev/cpuctl/dex2oat/tasks
+    chown system system /dev/cpuctl/cgroup.procs
+    chown system system /dev/cpuctl/foreground/cgroup.procs
+    chown system system /dev/cpuctl/background/cgroup.procs
+    chown system system /dev/cpuctl/top-app/cgroup.procs
+    chown system system /dev/cpuctl/rt/cgroup.procs
+    chown system system /dev/cpuctl/system/cgroup.procs
+    chown system system /dev/cpuctl/system-background/cgroup.procs
+    chown system system /dev/cpuctl/dex2oat/cgroup.procs
     chmod 0664 /dev/cpuctl/tasks
     chmod 0664 /dev/cpuctl/foreground/tasks
     chmod 0664 /dev/cpuctl/background/tasks
@@ -180,12 +198,22 @@
     chmod 0664 /dev/cpuctl/system/tasks
     chmod 0664 /dev/cpuctl/system-background/tasks
     chmod 0664 /dev/cpuctl/dex2oat/tasks
+    chmod 0664 /dev/cpuctl/cgroup.procs
+    chmod 0664 /dev/cpuctl/foreground/cgroup.procs
+    chmod 0664 /dev/cpuctl/background/cgroup.procs
+    chmod 0664 /dev/cpuctl/top-app/cgroup.procs
+    chmod 0664 /dev/cpuctl/rt/cgroup.procs
+    chmod 0664 /dev/cpuctl/system/cgroup.procs
+    chmod 0664 /dev/cpuctl/system-background/cgroup.procs
+    chmod 0664 /dev/cpuctl/dex2oat/cgroup.procs
 
     # Create a cpu group for NNAPI HAL processes
     mkdir /dev/cpuctl/nnapi-hal
     chown system system /dev/cpuctl/nnapi-hal
     chown system system /dev/cpuctl/nnapi-hal/tasks
+    chown system system /dev/cpuctl/nnapi-hal/cgroup.procs
     chmod 0664 /dev/cpuctl/nnapi-hal/tasks
+    chmod 0664 /dev/cpuctl/nnapi-hal/cgroup.procs
     write /dev/cpuctl/nnapi-hal/cpu.uclamp.min 1
     write /dev/cpuctl/nnapi-hal/cpu.uclamp.latency_sensitive 1
 
@@ -193,19 +221,25 @@
     mkdir /dev/cpuctl/camera-daemon
     chown system system /dev/cpuctl/camera-daemon
     chown system system /dev/cpuctl/camera-daemon/tasks
+    chown system system /dev/cpuctl/camera-daemon/cgroup.procs
     chmod 0664 /dev/cpuctl/camera-daemon/tasks
+    chmod 0664 /dev/cpuctl/camera-daemon/cgroup.procs
 
     # Create an stune group for camera-specific processes
     mkdir /dev/stune/camera-daemon
     chown system system /dev/stune/camera-daemon
     chown system system /dev/stune/camera-daemon/tasks
+    chown system system /dev/stune/camera-daemon/cgroup.procs
     chmod 0664 /dev/stune/camera-daemon/tasks
+    chmod 0664 /dev/stune/camera-daemon/cgroup.procs
 
     # Create an stune group for NNAPI HAL processes
     mkdir /dev/stune/nnapi-hal
     chown system system /dev/stune/nnapi-hal
     chown system system /dev/stune/nnapi-hal/tasks
+    chown system system /dev/stune/nnapi-hal/cgroup.procs
     chmod 0664 /dev/stune/nnapi-hal/tasks
+    chmod 0664 /dev/stune/nnapi-hal/cgroup.procs
     write /dev/stune/nnapi-hal/schedtune.boost 1
     write /dev/stune/nnapi-hal/schedtune.prefer_idle 1
 
@@ -217,8 +251,12 @@
     chown system system /dev/blkio/background
     chown system system /dev/blkio/tasks
     chown system system /dev/blkio/background/tasks
+    chown system system /dev/blkio/cgroup.procs
+    chown system system /dev/blkio/background/cgroup.procs
     chmod 0664 /dev/blkio/tasks
     chmod 0664 /dev/blkio/background/tasks
+    chmod 0664 /dev/blkio/cgroup.procs
+    chmod 0664 /dev/blkio/background/cgroup.procs
     write /dev/blkio/blkio.weight 1000
     write /dev/blkio/background/blkio.weight 200
     write /dev/blkio/background/blkio.bfq.weight 10
@@ -367,6 +405,13 @@
     chown system system /dev/cpuset/top-app/tasks
     chown system system /dev/cpuset/restricted/tasks
     chown system system /dev/cpuset/camera-daemon/tasks
+    chown system system /dev/cpuset/cgroup.procs
+    chown system system /dev/cpuset/foreground/cgroup.procs
+    chown system system /dev/cpuset/background/cgroup.procs
+    chown system system /dev/cpuset/system-background/cgroup.procs
+    chown system system /dev/cpuset/top-app/cgroup.procs
+    chown system system /dev/cpuset/restricted/cgroup.procs
+    chown system system /dev/cpuset/camera-daemon/cgroup.procs
 
     # set system-background to 0775 so SurfaceFlinger can touch it
     chmod 0775 /dev/cpuset/system-background
@@ -378,6 +423,13 @@
     chmod 0664 /dev/cpuset/restricted/tasks
     chmod 0664 /dev/cpuset/tasks
     chmod 0664 /dev/cpuset/camera-daemon/tasks
+    chmod 0664 /dev/cpuset/foreground/cgroup.procs
+    chmod 0664 /dev/cpuset/background/cgroup.procs
+    chmod 0664 /dev/cpuset/system-background/cgroup.procs
+    chmod 0664 /dev/cpuset/top-app/cgroup.procs
+    chmod 0664 /dev/cpuset/restricted/cgroup.procs
+    chmod 0664 /dev/cpuset/cgroup.procs
+    chmod 0664 /dev/cpuset/camera-daemon/cgroup.procs
 
     # make the PSI monitor accessible to others
     chown system system /proc/pressure/memory
diff --git a/trusty/storage/proxy/Android.bp b/trusty/storage/proxy/Android.bp
index 38d8685..94f26d8 100644
--- a/trusty/storage/proxy/Android.bp
+++ b/trusty/storage/proxy/Android.bp
@@ -35,7 +35,10 @@
         "liblog",
         "libhardware_legacy",
     ],
-    header_libs: ["libcutils_headers"],
+    header_libs: [
+        "libcutils_headers",
+        "libgsi_headers",
+    ],
 
     static_libs: [
         "libfstab",
diff --git a/trusty/storage/proxy/checkpoint_handling.cpp b/trusty/storage/proxy/checkpoint_handling.cpp
index 6c2fd36..3305d8d 100644
--- a/trusty/storage/proxy/checkpoint_handling.cpp
+++ b/trusty/storage/proxy/checkpoint_handling.cpp
@@ -18,9 +18,12 @@
 #include "log.h"
 
 #include <fstab/fstab.h>
+#include <unistd.h>
 #include <cstring>
 #include <string>
 
+#include <libgsi/libgsi.h>
+
 namespace {
 
 bool checkpointingDoneForever = false;
@@ -75,3 +78,15 @@
 
     return 0;
 }
+
+/**
+ * is_gsi_running() - Check if a GSI image is running via DSU.
+ *
+ * This function is equivalent to android::gsi::IsGsiRunning(), but this API is
+ * not yet vendor-accessible although the underlying metadata file is.
+ *
+ */
+bool is_gsi_running() {
+    /* TODO(b/210501710): Expose GSI image running state to vendor storageproxyd */
+    return !access(android::gsi::kGsiBootedIndicatorFile, F_OK);
+}
diff --git a/trusty/storage/proxy/checkpoint_handling.h b/trusty/storage/proxy/checkpoint_handling.h
index f1bf27c..dfe2947 100644
--- a/trusty/storage/proxy/checkpoint_handling.h
+++ b/trusty/storage/proxy/checkpoint_handling.h
@@ -32,6 +32,8 @@
  */
 int is_data_checkpoint_active(bool* active);
 
+bool is_gsi_running();
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/trusty/storage/proxy/proxy.c b/trusty/storage/proxy/proxy.c
index c690a28..2620034 100644
--- a/trusty/storage/proxy/proxy.c
+++ b/trusty/storage/proxy/proxy.c
@@ -104,8 +104,11 @@
         return -1;
     }
 
-    /* no-execute for user, no access for group and other */
-    umask(S_IXUSR | S_IRWXG | S_IRWXO);
+    /*
+     * No access for group and other. We need execute access for user to create
+     * an accessible directory.
+     */
+    umask(S_IRWXG | S_IRWXO);
 
     return 0;
 }
diff --git a/trusty/storage/proxy/storage.c b/trusty/storage/proxy/storage.c
index 2fde30f..c00c399 100644
--- a/trusty/storage/proxy/storage.c
+++ b/trusty/storage/proxy/storage.c
@@ -16,6 +16,7 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
+#include <libgen.h>
 #include <stdbool.h>
 #include <stdlib.h>
 #include <string.h>
@@ -24,26 +25,29 @@
 #include <sys/types.h>
 #include <unistd.h>
 
-#include "log.h"
+#include "checkpoint_handling.h"
 #include "ipc.h"
+#include "log.h"
 #include "storage.h"
 
 #define FD_TBL_SIZE 64
 #define MAX_READ_SIZE 4096
 
+#define ALTERNATE_DATA_DIR "alternate/"
+
 enum sync_state {
     SS_UNUSED = -1,
     SS_CLEAN =  0,
     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;
+
 static struct {
    struct storage_file_read_resp hdr;
    uint8_t data[MAX_READ_SIZE];
@@ -53,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) {
@@ -187,7 +187,6 @@
         goto err_response;
     }
 
-    dir_state = SS_DIRTY;
     rc = unlink(path);
     if (rc < 0) {
         rc = errno;
@@ -211,11 +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;
+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};
 
@@ -234,6 +243,24 @@
         goto err_response;
     }
 
+    /*
+     * TODO(b/210501710): Expose GSI image running state to vendor
+     * storageproxyd. We want to control data file paths in vendor_init, but we
+     * don't have access to the necessary property there yet. When we have
+     * access to that property we can set the root data path read-only and only
+     * allow creation of files in alternate/. Checking paths here temporarily
+     * until that is fixed.
+     *
+     * We are just checking for "/" instead of "alternate/" because we still
+     * want to still allow access to "persist/" in alternate mode (for now, this
+     * may change in the future).
+     */
+    if (alternate_mode && !strchr(req->name, '/')) {
+        ALOGE("%s: Cannot open root data file \"%s\" in alternate mode\n", __func__, req->name);
+        msg->result = STORAGE_ERR_ACCESS;
+        goto err_response;
+    }
+
     int rc = asprintf(&path, "%s/%s", ssdir_name, req->name);
     if (rc < 0) {
         ALOGE("%s: asprintf failed\n", __func__);
@@ -247,6 +274,24 @@
         open_flags |= O_TRUNC;
 
     if (req->flags & STORAGE_FILE_OPEN_CREATE) {
+        /*
+         * Create the alternate parent dir if needed & allowed.
+         *
+         * TODO(b/210501710): Expose GSI image running state to vendor
+         * storageproxyd. This directory should be created by vendor_init, once
+         * 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 == 0) {
+                sync_parent(parent_path);
+            } else if (errno != EEXIST) {
+                ALOGE("%s: Could not create parent directory \"%s\": %s\n", __func__, parent_path,
+                      strerror(errno));
+            }
+        }
+
         /* open or create */
         if (req->flags & STORAGE_FILE_OPEN_CREATE_EXCLUSIVE) {
             /* create exclusive */
@@ -279,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 */
@@ -467,17 +516,14 @@
 
 int storage_init(const char *dirname)
 {
+    /* If there is an active DSU image, use the alternate fs mode. */
+    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;
 }
@@ -501,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;
     }