OmniLib: health: Implement charge limit mode

Change-Id: I6d4f03a62555bd07ec70d23745506c7759b715ab
Signed-off-by: micky387 <mickaelsaibi@free.fr>
diff --git a/Android.bp b/Android.bp
index 4c25331..77d1aa5 100644
--- a/Android.bp
+++ b/Android.bp
@@ -51,7 +51,7 @@
 // ============================================================
 
 omnirom_sdk_LOCAL_STATIC_JAVA_LIBRARIES = [
-    "vendor.lineage.health-V1-java",
+    "vendor.lineage.health-V2-java",
 ]
 
 // Used by services
diff --git a/src/org/omnirom/omnilib/internal/health/ChargingControlController.java b/src/org/omnirom/omnilib/internal/health/ChargingControlController.java
index 84dbb5f..e5f0735 100644
--- a/src/org/omnirom/omnilib/internal/health/ChargingControlController.java
+++ b/src/org/omnirom/omnilib/internal/health/ChargingControlController.java
@@ -27,6 +27,7 @@
 import org.omnirom.omnilib.R;
 import org.omnirom.omnilib.internal.health.ccprovider.ChargingControlProvider;
 import org.omnirom.omnilib.internal.health.ccprovider.Deadline;
+import org.omnirom.omnilib.internal.health.ccprovider.Limit;
 import org.omnirom.omnilib.internal.health.ccprovider.Toggle;
 
 import java.io.PrintWriter;
@@ -72,6 +73,9 @@
 
     // Current selected provider
     private ChargingControlProvider mCurrentProvider;
+    private Deadline mDeadline;
+    private Limit mLimit;
+    private Toggle mToggle;
 
     public ChargingControlController(Context context, Handler handler) {
         super(context, handler);
@@ -100,17 +104,16 @@
                 R.integer.config_defaultChargingControlLimit);
 
         // Set up charging control providers
-        mCurrentProvider = new Toggle(mChargingControl, mContext);
-        if (!mCurrentProvider.isSupported()) {
-            mCurrentProvider = null;
-        }
-        if (mCurrentProvider == null) {
-            mCurrentProvider = new Deadline(mChargingControl, mContext);
-            if (!mCurrentProvider.isSupported()) {
-                mCurrentProvider = null;
-            }
-        }
-        if (mCurrentProvider == null) {
+        mDeadline = new Deadline(mChargingControl, mContext);
+        mLimit = new Limit(mChargingControl, mContext);
+        mToggle = new Toggle(mChargingControl, mContext);
+        if (mLimit.isSupported()) {
+            mCurrentProvider = mLimit;
+        } else if (mToggle.isSupported()) {
+            mCurrentProvider = mToggle;
+        } else if (mDeadline.isSupported()) {
+            mCurrentProvider = mDeadline;
+        } else {
             Log.wtf(TAG, "No charging control provider is supported");
         }
     }
@@ -141,6 +144,23 @@
             return false;
         }
 
+        mCurrentProvider = null;
+        if (mode == MODE_LIMIT) {
+            if (mLimit.isSupported()) {
+                mCurrentProvider = mLimit;
+            } else if (mToggle.isSupported()) {
+                mCurrentProvider = mToggle;
+            }
+        } else if (mode == MODE_AUTO || mode == MODE_MANUAL) {
+            if (mDeadline.isSupported()) {
+                mCurrentProvider = mDeadline;
+            }
+        }
+
+        if (mCurrentProvider == null) {
+            return false;
+        }
+
         putInt(OmniSettings.OMNI_CHARGING_CONTROL_MODE, mode);
         return true;
     }
diff --git a/src/org/omnirom/omnilib/internal/health/HealthInterfaceService.java b/src/org/omnirom/omnilib/internal/health/HealthInterfaceService.java
index 2c8bbc4..373f132 100644
--- a/src/org/omnirom/omnilib/internal/health/HealthInterfaceService.java
+++ b/src/org/omnirom/omnilib/internal/health/HealthInterfaceService.java
@@ -162,8 +162,9 @@
 
         @Override
         public boolean allowFineGrainedSettings() {
-            // We allow fine-grained settings if allow toggle and bypass
-            return mCCC.isChargingModeSupported(ChargingControlSupportedMode.TOGGLE);
+            // We allow fine-grained settings if bypass and toggle or limit modes are supported
+            return mCCC.isChargingModeSupported(ChargingControlSupportedMode.TOGGLE)
+                    || mCCC.isChargingModeSupported(ChargingControlSupportedMode.LIMIT);
         }
 
         @Override
diff --git a/src/org/omnirom/omnilib/internal/health/ccprovider/Limit.java b/src/org/omnirom/omnilib/internal/health/ccprovider/Limit.java
new file mode 100644
index 0000000..ec7e108
--- /dev/null
+++ b/src/org/omnirom/omnilib/internal/health/ccprovider/Limit.java
@@ -0,0 +1,98 @@
+/*
+ * SPDX-FileCopyrightText: 2024-2025 The LineageOS Project
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.omnirom.omnilib.internal.health.ccprovider;
+
+import static omnirom.health.HealthInterface.MODE_AUTO;
+import static omnirom.health.HealthInterface.MODE_LIMIT;
+import static omnirom.health.HealthInterface.MODE_MANUAL;
+
+import android.content.Context;
+import android.util.Log;
+
+import org.omnirom.omnilib.R;
+
+import vendor.lineage.health.ChargingControlSupportedMode;
+import vendor.lineage.health.ChargingLimitInfo;
+import vendor.lineage.health.IChargingControl;
+
+import java.io.PrintWriter;
+
+public class Limit extends ChargingControlProvider {
+    protected final int mChargingLimitMargin;
+
+    public Limit(IChargingControl chargingControl, Context context) {
+        super(context, chargingControl);
+
+        boolean isBypassSupported = isHALModeSupported(ChargingControlSupportedMode.BYPASS);
+        if (!isBypassSupported) {
+            mChargingLimitMargin = mContext.getResources().getInteger(
+                    R.integer.config_chargingControlBatteryRechargeMargin);
+        } else {
+            mChargingLimitMargin = 1;
+        }
+        Log.i(TAG, "isBypassSupported: " + isBypassSupported);
+    }
+
+    @Override
+    protected boolean onBatteryChanged(float currentPct, int targetPct) {
+        Log.i(TAG, "Current battery level: " + currentPct + ", target: " + targetPct);
+        return setChargingLimit(targetPct);
+    }
+
+    @Override
+    protected void onEnabled() {
+        onReset();
+    }
+
+    @Override
+    protected void onDisable() {
+        onReset();
+    }
+
+    @Override
+    protected void onReset() {
+        setChargingLimit(100);
+    }
+
+    private boolean setChargingLimit(int targetPct) {
+        try {
+            if (mChargingControl.getChargingLimit().max != targetPct) {
+                ChargingLimitInfo limit = new ChargingLimitInfo();
+                if (targetPct == 100) {
+                    limit.min = 0;
+                } else {
+                    limit.min = targetPct - mChargingLimitMargin;
+                }
+                limit.max = targetPct;
+                mChargingControl.setChargingLimit(limit);
+            }
+            return true;
+        } catch (Exception e) {
+            Log.e(TAG, "Failed to set charging limit", e);
+            return false;
+        }
+    }
+
+    @Override
+    public boolean isSupported() {
+        return isHALModeSupported(ChargingControlSupportedMode.LIMIT);
+    }
+
+    @Override
+    public boolean requiresBatteryLevelMonitoring() {
+        return !isHALModeSupported(ChargingControlSupportedMode.BYPASS);
+    }
+
+    @Override
+    public boolean isChargingControlModeSupported(int mode) {
+        return mode == MODE_AUTO || mode == MODE_MANUAL || mode == MODE_LIMIT;
+    }
+
+    @Override
+    public void dump(PrintWriter pw) {
+        pw.println("Provider: " + getClass().getName());
+    }
+}