Merge "gsid: Add a command-line option for verifying image maps."
diff --git a/OWNERS b/OWNERS
index 8a0a10e..09f4d71 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,3 +1,4 @@
+# Bug component: 30545
 dvander@google.com
 sspatil@google.com
 elsk@google.com
diff --git a/gsi_service.cpp b/gsi_service.cpp
index 98963ef..c7b3a28 100644
--- a/gsi_service.cpp
+++ b/gsi_service.cpp
@@ -155,13 +155,34 @@
 binder::Status GsiService::closeInstall(int* _aidl_return) {
     ENFORCE_SYSTEM;
     std::lock_guard<std::mutex> guard(lock_);
+
+    installer_ = {};
+
     auto dsu_slot = GetDsuSlot(install_dir_);
     std::string file = GetCompleteIndication(dsu_slot);
     if (!WriteStringToFile("OK", file)) {
         PLOG(ERROR) << "write failed: " << file;
-        *_aidl_return = INSTALL_ERROR_GENERIC;
+        *_aidl_return = IGsiService::INSTALL_ERROR_GENERIC;
+        return binder::Status::ok();
     }
-    *_aidl_return = INSTALL_OK;
+
+    // Create installation complete marker files, but set disabled immediately.
+    if (!WriteStringToFile(dsu_slot, kDsuActiveFile)) {
+        PLOG(ERROR) << "cannot write active DSU slot (" << dsu_slot << "): " << kDsuActiveFile;
+        *_aidl_return = IGsiService::INSTALL_ERROR_GENERIC;
+        return binder::Status::ok();
+    }
+    RestoreconMetadataFiles();
+
+    // DisableGsi() creates the DSU install status file and mark it as "disabled".
+    if (!DisableGsi()) {
+        PLOG(ERROR) << "cannot write DSU status file: " << kDsuInstallStatusFile;
+        *_aidl_return = IGsiService::INSTALL_ERROR_GENERIC;
+        return binder::Status::ok();
+    }
+
+    SetProperty(kGsiInstalledProp, "1");
+    *_aidl_return = IGsiService::INSTALL_OK;
     return binder::Status::ok();
 }
 
@@ -217,7 +238,8 @@
         return binder::Status::ok();
     }
     // It is important to not reset |installer_| here because other methods such
-    // as enableGsi() relies on the state of |installer_|.
+    // as isGsiInstallInProgress() relies on the state of |installer_|.
+    // TODO: Maybe don't do this, use a dedicated |in_progress_| flag?
     *_aidl_return = installer_->FinishInstall();
     return binder::Status::ok();
 }
@@ -303,6 +325,7 @@
 }
 
 binder::Status GsiService::enableGsi(bool one_shot, const std::string& dsuSlot, int* _aidl_return) {
+    ENFORCE_SYSTEM_OR_SHELL;
     std::lock_guard<std::mutex> guard(lock_);
 
     if (!WriteStringToFile(dsuSlot, kDsuActiveFile)) {
@@ -311,22 +334,14 @@
         return binder::Status::ok();
     }
     RestoreconMetadataFiles();
+
     if (installer_) {
-        ENFORCE_SYSTEM;
-        installer_ = {};
-        // Note: create the install status file last, since this is the actual boot
-        // indicator.
-        if (!SetBootMode(one_shot) || !CreateInstallStatusFile()) {
-            *_aidl_return = IGsiService::INSTALL_ERROR_GENERIC;
-        } else {
-            *_aidl_return = INSTALL_OK;
-        }
-    } else {
-        ENFORCE_SYSTEM_OR_SHELL;
-        *_aidl_return = ReenableGsi(one_shot);
+        LOG(ERROR) << "cannot enable an ongoing installation, was closeInstall() called?";
+        *_aidl_return = IGsiService::INSTALL_ERROR_GENERIC;
+        return binder::Status::ok();
     }
 
-    installer_ = nullptr;
+    *_aidl_return = ReenableGsi(one_shot);
     return binder::Status::ok();
 }
 
@@ -516,12 +531,12 @@
     if (statvfs(install_dir_.c_str(), &info)) {
         PLOG(ERROR) << "Could not statvfs(" << install_dir_ << ")";
     } else {
-        // Keep the storage device at least 40% free, plus 1% for jitter.
-        constexpr int jitter = 1;
-        const uint64_t reserved_blocks =
-                static_cast<uint64_t>(info.f_blocks) * (kMinimumFreeSpaceThreshold + jitter) / 100;
-        if (info.f_bavail > reserved_blocks) {
-            size = (info.f_bavail - reserved_blocks) * info.f_frsize;
+        uint64_t free_space = static_cast<uint64_t>(info.f_bavail) * info.f_frsize;
+        const auto free_space_threshold =
+                PartitionInstaller::GetMinimumFreeSpaceThreshold(install_dir_);
+        if (free_space_threshold.has_value() && free_space > *free_space_threshold) {
+            // Round down to multiples of filesystem block size.
+            size = (free_space - *free_space_threshold) / info.f_frsize * info.f_frsize;
         }
     }
 
@@ -530,7 +545,7 @@
     return binder::Status::ok();
 }
 
-bool GsiService::CreateInstallStatusFile() {
+bool GsiService::ResetBootAttemptCounter() {
     if (!android::base::WriteStringToFile("0", kDsuInstallStatusFile)) {
         PLOG(ERROR) << "write " << kDsuInstallStatusFile;
         return false;
@@ -936,13 +951,7 @@
         LOG(ERROR) << "GSI is not currently disabled";
         return INSTALL_ERROR_GENERIC;
     }
-    if (IsGsiRunning()) {
-        if (!SetBootMode(one_shot) || !CreateInstallStatusFile()) {
-            return IGsiService::INSTALL_ERROR_GENERIC;
-        }
-        return IGsiService::INSTALL_OK;
-    }
-    if (!SetBootMode(one_shot) || !CreateInstallStatusFile()) {
+    if (!SetBootMode(one_shot) || !ResetBootAttemptCounter()) {
         return IGsiService::INSTALL_ERROR_GENERIC;
     }
     return IGsiService::INSTALL_OK;
diff --git a/gsi_service.h b/gsi_service.h
index a6b3389..0ec7620 100644
--- a/gsi_service.h
+++ b/gsi_service.h
@@ -101,7 +101,11 @@
 
     enum class AccessLevel { System, SystemOrShell };
     binder::Status CheckUid(AccessLevel level = AccessLevel::System);
-    bool CreateInstallStatusFile();
+
+    // Mark install completion, and reset boot attempt counter.
+    // Next boot will try to boot into DSU.
+    bool ResetBootAttemptCounter();
+
     bool SetBootMode(bool one_shot);
 
     static android::wp<GsiService> sInstance;
diff --git a/libgsi_private.h b/libgsi_private.h
index 82814a9..51c7915 100644
--- a/libgsi_private.h
+++ b/libgsi_private.h
@@ -28,8 +28,5 @@
 static constexpr char kInstallStatusWipe[] = "wipe";
 static constexpr char kInstallStatusDisabled[] = "disabled";
 
-// We are looking for /data to have at least 40% free space.
-static constexpr uint32_t kMinimumFreeSpaceThreshold = 40;
-
 }  // namespace gsi
 }  // namespace android
diff --git a/partition_installer.cpp b/partition_installer.cpp
index 79af71a..5450169 100644
--- a/partition_installer.cpp
+++ b/partition_installer.cpp
@@ -22,9 +22,11 @@
 #include <android-base/logging.h>
 #include <android-base/unique_fd.h>
 #include <ext4_utils/ext4_utils.h>
+#include <fs_mgr.h>
 #include <fs_mgr_dm_linear.h>
 #include <libdm/dm.h>
 #include <libgsi/libgsi.h>
+#include <liblp/partition_opener.h>
 
 #include "file_paths.h"
 #include "gsi_service.h"
@@ -127,17 +129,18 @@
     // This is the same as android::vold::GetFreebytes() but we also
     // need the total file system size so we open code it here.
     uint64_t free_space = static_cast<uint64_t>(sb.f_bavail) * sb.f_frsize;
-    uint64_t fs_size = static_cast<uint64_t>(sb.f_blocks) * sb.f_frsize;
     if (free_space <= (size_)) {
         LOG(ERROR) << "not enough free space (only " << free_space << " bytes available)";
         return IGsiService::INSTALL_ERROR_NO_SPACE;
     }
-    // We are asking for 40% of the /data to be empty.
-    // TODO: may be not hard code it like this
-    double free_space_percent = ((1.0 * free_space) / fs_size) * 100;
-    if (free_space_percent < kMinimumFreeSpaceThreshold) {
-        LOG(ERROR) << "free space " << static_cast<uint64_t>(free_space_percent)
-                   << "% is below the minimum threshold of " << kMinimumFreeSpaceThreshold << "%";
+
+    const auto free_space_threshold = GetMinimumFreeSpaceThreshold(install_dir_);
+    if (!free_space_threshold.has_value()) {
+        return IGsiService::INSTALL_ERROR_GENERIC;
+    }
+    if (free_space < size_ + *free_space_threshold) {
+        LOG(ERROR) << "post-installation free space (" << free_space << " - " << size_
+                   << ") would be below the minimum threshold of " << *free_space_threshold;
         return IGsiService::INSTALL_ERROR_FILE_SYSTEM_CLUTTERED;
     }
     return IGsiService::INSTALL_OK;
@@ -345,5 +348,23 @@
     return IGsiService::INSTALL_OK;
 }
 
+std::optional<uint64_t> PartitionInstaller::GetMinimumFreeSpaceThreshold(
+        const std::string& install_dir) {
+    // No need to retain any space if we were not installing to the internal storage.
+    if (!android::base::StartsWith(install_dir, "/data"s)) {
+        return 0;
+    }
+    // Dynamic Partitions device must have a "super" block device.
+    BlockDeviceInfo info;
+    PartitionOpener opener;
+    if (!opener.GetInfo(fs_mgr_get_super_partition_name(), &info)) {
+        // We shouldn't reach here, but handle it just in case.
+        LOG(ERROR) << "could not get block device info of super";
+        return std::nullopt;
+    }
+    // Reserve |super partition| of storage space so we don't disable VAB.
+    return info.size;
+}
+
 }  // namespace gsi
 }  // namespace android
diff --git a/partition_installer.h b/partition_installer.h
index 920af47..1cb9535 100644
--- a/partition_installer.h
+++ b/partition_installer.h
@@ -53,6 +53,9 @@
     static int WipeWritable(const std::string& active_dsu, const std::string& install_dir,
                             const std::string& name);
 
+    // Returns the minimum free space to reserve for /data.
+    static std::optional<uint64_t> GetMinimumFreeSpaceThreshold(const std::string& install_dir);
+
     // Finish a partition installation and release resources.
     // If the installation is incomplete or corrupted, the backing image would
     // be cleaned up and an error code is returned.