Add isZramMaintenanceSupported to IMmd

system_server needs to know whether the device supports zram
maintenance to stop calling doZramMaintenanceAsync() periodically on no
zram devices which is no-op and useless.

Since isZramMaintenanceSupported() is synchronous API, we rename
doZramMaintenance() as doZramMaintenanceAsync() to make it clear whether
API is synchronous or asynchronous.

isZramMaintenanceSupported() returns not based on system properties but
based on the zram status (i.e. zram writeback/recompression) in
ZramContext because system properties can be updated during runtime.
System properties should be examined on every doZramMaintenanceAsync()
timing. Also even if some zram feature (e.g. writeback/recompression) in
the system properties is enabled after zram setup, zram maintenance for
the feature is still no-op because enabling the feature status of zram
in kernel cannot be changed once zram starts working.

The result of isZramMaintenanceSupported() is consistent because the
status is loaded before mmd exposes the Binder API and the feature
contexts in ZramContext are not cleared or re-initialized after setup.

Bug: 375432472
Test: atest mmd_unit_tests

Change-Id: I284dada186c557be4bab260a13b68c472024f21c
diff --git a/aidl/android/os/IMmd.aidl b/aidl/android/os/IMmd.aidl
index 251f02e..569c6da 100644
--- a/aidl/android/os/IMmd.aidl
+++ b/aidl/android/os/IMmd.aidl
@@ -22,13 +22,24 @@
  * IMmd is oneway asynchronous API. mmd uses any information passed from outside (e.g.
  * system_server) as hints. Hint producers don't need to wait until mmd consumes the hists.
  */
-oneway interface IMmd {
+interface IMmd {
     /**
      * mmd starts zram maintenance operation (e.g. zram writeback, zram recompression) if
      * applicable.
      *
      * mmd expects this Binder is called on a good timing to execute the maintenance (e.g. while the
      * system is idle).
+     *
+     * This is oneway asynchronous API. mmd uses any information passed from outside (e.g.
+     * system_server) as hints. Hint producers don't need to wait until mmd consumes the hists.
      */
-    void doZramMaintenance();
+    oneway void doZramMaintenanceAsync();
+
+    /**
+     * Whether mmd supports doZramMaintenance() call on the device.
+     *
+     * System, which don't utilize zram, should not call doZramMaintenance() because it is no-op and
+     * useless.
+     */
+    boolean isZramMaintenanceSupported();
 }
diff --git a/src/service.rs b/src/service.rs
index 53733ba..b35e688 100644
--- a/src/service.rs
+++ b/src/service.rs
@@ -48,7 +48,7 @@
 impl Interface for MmdService {}
 
 impl IMmd for MmdService {
-    fn doZramMaintenance(&self) -> BinderResult<()> {
+    fn doZramMaintenanceAsync(&self) -> BinderResult<()> {
         let mut ctx = self.ctx.lock().expect("mmd aborts on panics");
         let ZramContext { zram_writeback, zram_recompression, suspend_history, .. } =
             ctx.deref_mut();
@@ -94,6 +94,11 @@
 
         Ok(())
     }
+
+    fn isZramMaintenanceSupported(&self) -> BinderResult<bool> {
+        let ctx = self.ctx.lock().expect("mmd aborts on panics");
+        Ok(ctx.zram_writeback.is_some() || ctx.zram_recompression.is_some())
+    }
 }
 
 fn load_zram_writeback_params() -> mmd::zram::writeback::Params {
@@ -132,3 +137,44 @@
     params.threshold_bytes = U64Prop::ZramRecompressionThresholdBytes.get(params.threshold_bytes);
     params
 }
+
+#[cfg(test)]
+mod tests {
+    use mmd::suspend_history::SuspendHistory;
+    use mmd::zram::recompression::ZramRecompression;
+    use mmd::zram::writeback::ZramWriteback;
+
+    use super::*;
+
+    #[test]
+    fn test_is_zram_maintenance_supported() {
+        assert!(!MmdService::new(Arc::new(Mutex::new(ZramContext {
+            zram_writeback: None,
+            zram_recompression: None,
+            suspend_history: SuspendHistory::new(),
+        })))
+        .isZramMaintenanceSupported()
+        .unwrap());
+        assert!(MmdService::new(Arc::new(Mutex::new(ZramContext {
+            zram_writeback: Some(ZramWriteback::new(1024 * 1024, 1024 * 1024)),
+            zram_recompression: None,
+            suspend_history: SuspendHistory::new(),
+        })))
+        .isZramMaintenanceSupported()
+        .unwrap());
+        assert!(MmdService::new(Arc::new(Mutex::new(ZramContext {
+            zram_writeback: None,
+            zram_recompression: Some(ZramRecompression::new()),
+            suspend_history: SuspendHistory::new(),
+        })))
+        .isZramMaintenanceSupported()
+        .unwrap());
+        assert!(MmdService::new(Arc::new(Mutex::new(ZramContext {
+            zram_writeback: Some(ZramWriteback::new(1024 * 1024, 1024 * 1024)),
+            zram_recompression: Some(ZramRecompression::new()),
+            suspend_history: SuspendHistory::new(),
+        })))
+        .isZramMaintenanceSupported()
+        .unwrap());
+    }
+}