libsnapshot: Prepare removal of legacy snapshot

During OTA install, when vendor partition is on Android S, add a new flag in SnapshotUpdateStatus file to indicate that we need to support dm-snapshot based snapshot process. This will be used only during post OTA reboot.

The primary change here is the OTA install path. Earlier, dm-snapshot based approach was used; with this change, since both "snapuserd" and "update-engine" resides on system partition, OTA installation will use the userspace snapshot approach.

To maintain backward compatibility, the new flag "legacy_snapuserd" is used only after OTA reboot. This flag will make sure that update-engine will take the dm-snapshot based approach post reboot and for the entire duration of snapshot-merge.

Additionally, during first-stage init if the vendor is on Android S, then "snapuserd" binary will continue to work based off dm-snapshot as none of this change will impact the mount process.

Bug: 304829384
Test: OTA on Pixel
th - OTA from Android S to TOT with vendor on S
Change-Id: Idd9a60b09417cee141b2810e2d4b35e91c845a5c
Signed-off-by: Akilesh Kailash <akailash@google.com>
diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
index f2f7fc1..076a918 100644
--- a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
+++ b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
@@ -212,6 +212,9 @@
 
     // io_uring support
     bool io_uring_enabled = 10;
+
+    // legacy dm-snapshot based snapuserd
+    bool legacy_snapuserd = 11;
 }
 
 // Next: 10
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index d102863..3ccc3db 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -829,6 +829,9 @@
     // Set read-ahead size during OTA
     void SetReadAheadSize(const std::string& entry_block_device, off64_t size_kb);
 
+    // Returns true post OTA reboot if legacy snapuserd is required
+    bool IsLegacySnapuserdPostReboot();
+
     android::dm::IDeviceMapper& dm_;
     std::unique_ptr<IDeviceInfo> device_;
     std::string metadata_dir_;
@@ -839,6 +842,7 @@
     std::unique_ptr<SnapuserdClient> snapuserd_client_;
     std::unique_ptr<LpMetadata> old_partition_metadata_;
     std::optional<bool> is_snapshot_userspace_;
+    std::optional<bool> is_legacy_snapuserd_;
 };
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index e6c4de6..b4892b4 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -265,7 +265,6 @@
     auto boot_file = GetSnapshotBootIndicatorPath();
     std::string contents;
     if (!android::base::ReadFileToString(boot_file, &contents)) {
-        PLOG(WARNING) << "Cannot read " << boot_file;
         return {};
     }
     return contents;
@@ -2118,6 +2117,53 @@
     return update_status.io_uring_enabled();
 }
 
+/*
+ * Please see b/304829384 for more details.
+ *
+ * In Android S, we use dm-snapshot for mounting snapshots and snapshot-merge
+ * process. If the vendor partition continues to be on Android S, then
+ * "snapuserd" binary in first stage ramdisk will be from vendor partition.
+ * Thus, we need to maintain backward compatibility.
+ *
+ * Now, We take a two step approach to maintain the backward compatibility:
+ *
+ * 1: During OTA installation, we will continue to use "user-space" snapshots
+ * for OTA installation as both update-engine and snapuserd binary will be from system partition.
+ * However, during installation, we mark "legacy_snapuserd" in
+ * SnapshotUpdateStatus file to mark that this is a path to support backward compatibility.
+ * Thus, this function will return "false" during OTA installation.
+ *
+ * 2: Post OTA reboot, there are two key steps:
+ *    a: During first stage init, "init" and "snapuserd" could be from vendor
+ *    partition. This could be from Android S. Thus, the snapshot mount path
+ *    will be based off dm-snapshot.
+ *
+ *    b: Post selinux transition, "init" and "update-engine" will be "system"
+ *    partition. Now, since the snapshots are mounted off dm-snapshot,
+ *    update-engine interaction with "snapuserd" should work based off
+ *    dm-snapshots.
+ *
+ *    TL;DR: update-engine will use the "system" snapuserd for installing new
+ *    updates (this is safe as there is no "vendor" snapuserd running during
+ *    installation). Post reboot, update-engine will use the legacy path when
+ *    communicating with "vendor" snapuserd that was started in first-stage
+ *    init. Hence, this function checks:
+ *         i: Are we in post OTA reboot
+ *         ii: Is the Vendor from Android 12
+ *         iii: If both (i) and (ii) are true, then use the dm-snapshot based
+ *         approach.
+ *
+ */
+bool SnapshotManager::IsLegacySnapuserdPostReboot() {
+    if (is_legacy_snapuserd_.has_value() && is_legacy_snapuserd_.value() == true) {
+        auto slot = GetCurrentSlot();
+        if (slot == Slot::Target) {
+            return true;
+        }
+    }
+    return false;
+}
+
 bool SnapshotManager::UpdateUsesUserSnapshots() {
     // This and the following function is constantly
     // invoked during snapshot merge. We want to avoid
@@ -2129,7 +2175,12 @@
     // during merge phase. Hence, once we know that
     // the value is read from disk the very first time,
     // it is safe to read successive checks from memory.
+
     if (is_snapshot_userspace_.has_value()) {
+        // Check if legacy snapuserd is running post OTA reboot
+        if (IsLegacySnapuserdPostReboot()) {
+            return false;
+        }
         return is_snapshot_userspace_.value();
     }
 
@@ -2140,13 +2191,16 @@
 }
 
 bool SnapshotManager::UpdateUsesUserSnapshots(LockedFile* lock) {
-    // See UpdateUsesUserSnapshots()
-    if (is_snapshot_userspace_.has_value()) {
-        return is_snapshot_userspace_.value();
+    if (!is_snapshot_userspace_.has_value()) {
+        SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);
+        is_snapshot_userspace_ = update_status.userspace_snapshots();
+        is_legacy_snapuserd_ = update_status.legacy_snapuserd();
     }
 
-    SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);
-    is_snapshot_userspace_ = update_status.userspace_snapshots();
+    if (IsLegacySnapuserdPostReboot()) {
+        return false;
+    }
+
     return is_snapshot_userspace_.value();
 }
 
@@ -3210,6 +3264,8 @@
     // Deduce supported features.
     bool userspace_snapshots = CanUseUserspaceSnapshots();
     bool legacy_compression = GetLegacyCompressionEnabledProperty();
+    bool is_legacy_snapuserd = IsVendorFromAndroid12();
+
     if (!vabc_disable_reason.empty()) {
         if (userspace_snapshots) {
             LOG(INFO) << "Userspace snapshots disabled: " << vabc_disable_reason;
@@ -3219,6 +3275,7 @@
         }
         userspace_snapshots = false;
         legacy_compression = false;
+        is_legacy_snapuserd = false;
     }
 
     if (legacy_compression || userspace_snapshots) {
@@ -3231,6 +3288,8 @@
         }
     }
 
+    LOG(INFO) << "userspace snapshots: " << userspace_snapshots
+              << " legacy_snapuserd: " << legacy_compression;
     const bool using_snapuserd = userspace_snapshots || legacy_compression;
     if (!using_snapuserd) {
         LOG(INFO) << "Using legacy Virtual A/B (dm-snapshot)";
@@ -3328,6 +3387,10 @@
             status.set_io_uring_enabled(true);
             LOG(INFO) << "io_uring for snapshots enabled";
         }
+
+        if (is_legacy_snapuserd) {
+            status.set_legacy_snapuserd(true);
+        }
     } else if (legacy_compression) {
         LOG(INFO) << "Virtual A/B using legacy snapuserd";
     } else {
@@ -3335,6 +3398,7 @@
     }
 
     is_snapshot_userspace_.emplace(userspace_snapshots);
+    is_legacy_snapuserd_.emplace(is_legacy_snapuserd);
 
     if (!device()->IsTestDevice() && using_snapuserd) {
         // Terminate stale daemon if any
diff --git a/fs_mgr/libsnapshot/utility.cpp b/fs_mgr/libsnapshot/utility.cpp
index 1ffa89c..fe2d95c 100644
--- a/fs_mgr/libsnapshot/utility.cpp
+++ b/fs_mgr/libsnapshot/utility.cpp
@@ -230,11 +230,7 @@
     return fetcher->GetBoolProperty("ro.virtual_ab.userspace.snapshots.enabled", false);
 }
 
-bool CanUseUserspaceSnapshots() {
-    if (!GetUserspaceSnapshotsEnabledProperty()) {
-        return false;
-    }
-
+bool IsVendorFromAndroid12() {
     auto fetcher = IPropertyFetcher::GetInstance();
 
     const std::string UNKNOWN = "unknown";
@@ -243,8 +239,15 @@
 
     // No user-space snapshots if vendor partition is on Android 12
     if (vendor_release.find("12") != std::string::npos) {
-        LOG(INFO) << "Userspace snapshots disabled as vendor partition is on Android: "
-                  << vendor_release;
+        return true;
+    }
+
+    return false;
+}
+
+bool CanUseUserspaceSnapshots() {
+    if (!GetUserspaceSnapshotsEnabledProperty()) {
+        LOG(INFO) << "Virtual A/B - Userspace snapshots disabled";
         return false;
     }
 
diff --git a/fs_mgr/libsnapshot/utility.h b/fs_mgr/libsnapshot/utility.h
index 370f3c4..f956a05 100644
--- a/fs_mgr/libsnapshot/utility.h
+++ b/fs_mgr/libsnapshot/utility.h
@@ -136,6 +136,7 @@
 
 bool CanUseUserspaceSnapshots();
 bool IsDmSnapshotTestingEnabled();
+bool IsVendorFromAndroid12();
 
 // Swap the suffix of a partition name.
 std::string GetOtherPartitionName(const std::string& name);