Add IGsiService::suggestScratchSize()

DSU installation service could call this method to get a suggested
scratch partition size, if the DSU guest system would need overlayFS
(remount) feature. This size is just a suggestion, and is not guaranteed
to work. The caller (framework) should still check if the DSU scratch
partition is allocated successfully. The scratch partition allocation
could fail if the suggested size is larger than available free space.

Bug: 165925766
Test: TH
Change-Id: Ie6202d15a76a21f59219a80dc3a20e9405ed7f66
diff --git a/gsi_service.cpp b/gsi_service.cpp
index 14ef1d4..6d310a4 100644
--- a/gsi_service.cpp
+++ b/gsi_service.cpp
@@ -16,6 +16,7 @@
 
 #include "gsi_service.h"
 
+#include <sys/statvfs.h>
 #include <sys/vfs.h>
 #include <unistd.h>
 
@@ -42,6 +43,7 @@
 #include <libfiemap/image_manager.h>
 #include <openssl/sha.h>
 #include <private/android_filesystem_config.h>
+#include <storage_literals/storage_literals.h>
 
 #include "file_paths.h"
 #include "libgsi_private.h"
@@ -52,6 +54,7 @@
 using namespace std::literals;
 using namespace android::fs_mgr;
 using namespace android::fiemap;
+using namespace android::storage_literals;
 using android::base::ReadFileToString;
 using android::base::ReadFullyAtOffset;
 using android::base::RemoveFileIfExists;
@@ -493,6 +496,27 @@
     return binder::Status::ok();
 }
 
+binder::Status GsiService::suggestScratchSize(int64_t* _aidl_return) {
+    ENFORCE_SYSTEM;
+
+    static constexpr int64_t kMinScratchSize = 512_MiB;
+    static constexpr int64_t kMaxScratchSize = 2_GiB;
+
+    int64_t size = 0;
+    struct statvfs info;
+    if (statvfs(install_dir_.c_str(), &info)) {
+        PLOG(ERROR) << "Could not statvfs(" << install_dir_ << ")";
+    } else {
+        const int64_t available_space = static_cast<int64_t>(info.f_bavail) * info.f_frsize;
+        LOG(INFO) << "Available space of " << install_dir_ << ": " << available_space;
+        // Use up to half of free space. Don't exhaust the storage device with scratch partition.
+        size = available_space / 2;
+    }
+
+    *_aidl_return = std::clamp(size, kMinScratchSize, kMaxScratchSize);
+    return binder::Status::ok();
+}
+
 bool GsiService::CreateInstallStatusFile() {
     if (!android::base::WriteStringToFile("0", kDsuInstallStatusFile)) {
         PLOG(ERROR) << "write " << kDsuInstallStatusFile;