Merge "Add API to get remaining lifetime as a percentage." into main
diff --git a/IdleMaint.cpp b/IdleMaint.cpp
index 7d3eaf4..fafa280 100644
--- a/IdleMaint.cpp
+++ b/IdleMaint.cpp
@@ -507,6 +507,30 @@
     return -1;
 }
 
+int32_t GetStorageRemainingLifetime() {
+    std::string path = getDevSysfsPath();
+    if (path.empty()) {
+        return -1;
+    }
+
+    std::string lifeTimeBasePath = path + "/health_descriptor/life_time_estimation_";
+
+    int32_t lifeTime = getLifeTime(lifeTimeBasePath + "c");
+    if (lifeTime == -1) {
+        int32_t lifeTimeA = getLifeTime(lifeTimeBasePath + "a");
+        int32_t lifeTimeB = getLifeTime(lifeTimeBasePath + "b");
+        lifeTime = std::max(lifeTimeA, lifeTimeB);
+        if (lifeTime <= 0) {
+            return -1;
+        }
+
+        // 1 = 0-10% used, 10 = 90-100% used. Subtract 1 so that a brand new
+        // device looks 0% used.
+        lifeTime = (lifeTime - 1) * 10;
+    }
+    return 100 - std::clamp(lifeTime, 0, 100);
+}
+
 void SetGCUrgentPace(int32_t neededSegments, int32_t minSegmentThreshold, float dirtyReclaimRate,
                      float reclaimWeight, int32_t gcPeriod, int32_t minGCSleepTime,
                      int32_t targetDirtyRatio) {
diff --git a/IdleMaint.h b/IdleMaint.h
index a28cde2..a1387b3 100644
--- a/IdleMaint.h
+++ b/IdleMaint.h
@@ -26,6 +26,7 @@
 int RunIdleMaint(bool needGC, const android::sp<android::os::IVoldTaskListener>& listener);
 int AbortIdleMaint(const android::sp<android::os::IVoldTaskListener>& listener);
 int32_t GetStorageLifeTime();
+int32_t GetStorageRemainingLifetime();
 void SetGCUrgentPace(int32_t neededSegments, int32_t minSegmentThreshold, float dirtyReclaimRate,
                      float reclaimWeight, int32_t gcPeriod, int32_t minGCSleepTime,
                      int32_t targetDirtyRatio);
diff --git a/VoldNativeService.cpp b/VoldNativeService.cpp
index 65731af..96f4eaf 100644
--- a/VoldNativeService.cpp
+++ b/VoldNativeService.cpp
@@ -504,6 +504,14 @@
     return Ok();
 }
 
+binder::Status VoldNativeService::getStorageRemainingLifetime(int32_t* _aidl_return) {
+    ENFORCE_SYSTEM_OR_ROOT;
+    ACQUIRE_LOCK;
+
+    *_aidl_return = GetStorageRemainingLifetime();
+    return Ok();
+}
+
 binder::Status VoldNativeService::setGCUrgentPace(int32_t neededSegments,
                                                   int32_t minSegmentThreshold,
                                                   float dirtyReclaimRate, float reclaimWeight,
diff --git a/VoldNativeService.h b/VoldNativeService.h
index d9aee57..bb00d35 100644
--- a/VoldNativeService.h
+++ b/VoldNativeService.h
@@ -89,6 +89,7 @@
                                 const android::sp<android::os::IVoldTaskListener>& listener);
     binder::Status abortIdleMaint(const android::sp<android::os::IVoldTaskListener>& listener);
     binder::Status getStorageLifeTime(int32_t* _aidl_return);
+    binder::Status getStorageRemainingLifetime(int32_t* _aidl_return);
     binder::Status setGCUrgentPace(int32_t neededSegments, int32_t minSegmentThreshold,
                                    float dirtyReclaimRate, float reclaimWeight, int32_t gcPeriod,
                                    int32_t minGCSleepTime, int32_t targetDirtyRatio);
diff --git a/binder/android/os/IVold.aidl b/binder/android/os/IVold.aidl
index 229f173..d121dee 100644
--- a/binder/android/os/IVold.aidl
+++ b/binder/android/os/IVold.aidl
@@ -66,6 +66,8 @@
     void fstrim(int fstrimFlags, IVoldTaskListener listener);
     void runIdleMaint(boolean needGC, IVoldTaskListener listener);
     void abortIdleMaint(IVoldTaskListener listener);
+    // Returns the amount of storage lifetime used, as a percentage.
+    // (eg, 10 indicates 10% of lifetime used), or -1 on failure.
     int getStorageLifeTime();
     void setGCUrgentPace(int neededSegments, int minSegmentThreshold,
                          float dirtyReclaimRate, float reclaimWeight,
@@ -135,6 +137,11 @@
 
     long getStorageSize();
 
+    // Returns the remaining storage lifetime as a percentage, rounded up as
+    // needed when the underlying hardware reports low precision. Returns -1
+    // on failure.
+    int getStorageRemainingLifetime();
+
     const int FSTRIM_FLAG_DEEP_TRIM = 1;
 
     const int MOUNT_FLAG_PRIMARY = 1;