Skip to content

Commit

Permalink
SystemUIGoogle: Implement adaptive charging based on Google battery HAL
Browse files Browse the repository at this point in the history
[spezi77] Adapted for pixel framework

Change-Id: I01b211e5e121dd6856aa321a7ef9dd937754914d
Signed-off-by: spezi77 <spezi7713@gmx.net>
Signed-off-by: aswin7469 <aswinas@pixysos.com>
  • Loading branch information
anayw2001 authored and basamaryan committed Jul 30, 2023
1 parent 42974b7 commit c27a93d
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 163 deletions.
1 change: 1 addition & 0 deletions SystemUIGoogle/Android.bp
Expand Up @@ -29,6 +29,7 @@ android_library {
"nga-lib",
"elmyra-lib",
"columbus-lib",
"vendor.google.google_battery-V1-java",
],
manifest: "AndroidManifest.xml",

Expand Down
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2022 The PixelExperience Project
* Copyright (C) 2022 StatixOS
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -17,9 +18,12 @@
package com.google.android.systemui.googlebattery;

import android.content.Context;
import android.os.IHwBinder;
import android.os.Binder;
import android.os.IBinder;
import android.os.LocaleList;
import android.os.ParcelFormatException;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.text.format.DateFormat;
Expand All @@ -28,141 +32,138 @@
import java.util.Locale;
import java.util.NoSuchElementException;

import vendor.google.google_battery.V1_1.IGoogleBattery;
import vendor.google.google_battery.ChargingStage;
import vendor.google.google_battery.IGoogleBattery;

public class AdaptiveChargingManager {

private static final boolean DEBUG = Log.isLoggable("AdaptiveChargingManager", 3);
Context mContext;
private static final String TAG = "AdaptiveChargingManager";

private Context mContext;

public AdaptiveChargingManager(Context context) {
mContext = context;
}

private static IGoogleBattery initHalInterface(IHwBinder.DeathRecipient deathRecipient) {
if (DEBUG) {
Log.d("AdaptiveChargingManager", "initHalInterface");
}
try {
IGoogleBattery service = IGoogleBattery.getService();
if (service != null && deathRecipient != null) {
service.linkToDeath(deathRecipient, 0L);
}
return service;
} catch (RemoteException | NoSuchElementException e) {
Log.e("AdaptiveChargingManager", "failed to get Google Battery HAL: ", e);
return null;
}
public interface AdaptiveChargingStatusReceiver {
void onDestroyInterface();

void onReceiveStatus(int seconds, String stage);
}

public static boolean isStageActive(String str) {
return "Active".equals(str);
private Locale getLocale() {
LocaleList locales = mContext.getResources().getConfiguration().getLocales();
return (locales == null || locales.isEmpty()) ? Locale.getDefault() : locales.get(0);
}

public static boolean isStageEnabled(String str) {
return "Enabled".equals(str);
public String formatTimeToFull(long j) {
return DateFormat.format(DateFormat.getBestDateTimePattern(getLocale(), DateFormat.is24HourFormat(mContext) ? "Hm" : "hma"), j).toString();
}

public static boolean isStageActiveOrEnabled(String str) {
return isStageActive(str) || isStageEnabled(str);
public boolean hasAdaptiveChargingFeature() {
return mContext.getPackageManager().hasSystemFeature("com.google.android.feature.ADAPTIVE_CHARGING");
}

public static boolean isActive(String str, int i) {
return isStageActiveOrEnabled(str) && i > 0;
public boolean isAvailable() {
return hasAdaptiveChargingFeature() && shouldShowNotification();
}

private void destroyHalInterface(IGoogleBattery iGoogleBattery, IHwBinder.DeathRecipient deathRecipient) {
if (DEBUG) {
Log.d("AdaptiveChargingManager", "destroyHalInterface");
}
if (deathRecipient != null) {
try {
iGoogleBattery.unlinkToDeath(deathRecipient);
} catch (RemoteException e) {
Log.e("AdaptiveChargingManager", "unlinkToDeath failed: ", e);
}
}
public boolean shouldShowNotification() {
return DeviceConfig.getBoolean("adaptive_charging", "adaptive_charging_notification", true);
}

boolean hasAdaptiveChargingFeature() {
return mContext.getPackageManager().hasSystemFeature("com.google.android.feature.ADAPTIVE_CHARGING");
public boolean getEnabled() {
return Settings.Secure.getInt(mContext.getContentResolver(), "adaptive_charging", 1) == 1;
}

public boolean isAvailable() {
return hasAdaptiveChargingFeature() && DeviceConfig.getBoolean("adaptive_charging", "adaptive_charging_enabled", true);
public void setEnabled(boolean on) {
Settings.Secure.putInt(mContext.getContentResolver(), "adaptive_charging", on ? 1 : 0);
}

public boolean shouldShowNotification() {
return DeviceConfig.getBoolean("adaptive_charging", "adaptive_charging_notification", false);
public static boolean isStageActive(String stage) {
return "Active".equals(stage);
}

public boolean isEnabled() {
return Settings.Secure.getInt(mContext.getContentResolver(), "adaptive_charging_enabled", 1) == 1;
public static boolean isStageEnabled(String stage) {
return "Enabled".equals(stage);
}

private Locale getLocale() {
LocaleList locales = mContext.getResources().getConfiguration().getLocales();
return (locales == null || locales.isEmpty()) ? Locale.getDefault() : locales.get(0);
public static boolean isStageActiveOrEnabled(String stage) {
return isStageActive(stage) || isStageEnabled(stage);
}

public String formatTimeToFull(long j) {
return DateFormat.format(DateFormat.getBestDateTimePattern(getLocale(), DateFormat.is24HourFormat(mContext) ? "Hm" : "hma"), j).toString();
public static boolean isActive(String state, int seconds) {
return isStageActiveOrEnabled(state) && seconds > 0;
}

public boolean setAdaptiveChargingDeadline(int i) {
IGoogleBattery initHalInterface = initHalInterface(null);
boolean z = false;
if (initHalInterface == null) {
public boolean setAdaptiveChargingDeadline(int secondsFromNow) {
IGoogleBattery googBatteryInterface = initHalInterface(null);
if (googBatteryInterface == null) {
return false;
}
boolean result = false;
try {
if (initHalInterface.setChargingDeadline(i) == 0) {
z = true;
}
googBatteryInterface.setChargingDeadline(secondsFromNow);
result = true;
} catch (RemoteException e) {
Log.e("AdaptiveChargingManager", "setChargingDeadline failed: ", e);
Log.e(TAG, "setChargingDeadline() failed");
}
destroyHalInterface(initHalInterface, null);
return z;
destroyHalInterface(googBatteryInterface, null);
return result;
}

public void queryStatus(final AdaptiveChargingStatusReceiver adaptiveChargingStatusReceiver) {
final IHwBinder.DeathRecipient deathRecipient = new IHwBinder.DeathRecipient() {
public void serviceDied(long j) {
if (AdaptiveChargingManager.DEBUG) {
Log.d("AdaptiveChargingManager", "serviceDied");
}
adaptiveChargingStatusReceiver.onDestroyInterface();
}
};
final IGoogleBattery initHalInterface = initHalInterface(deathRecipient);
if (initHalInterface == null) {
adaptiveChargingStatusReceiver.onDestroyInterface();
return;
}
try {
initHalInterface.getChargingStageAndDeadline(new IGoogleBattery.getChargingStageAndDeadlineCallback() {
IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
@Override
public void onValues(byte b, String str, int i) {
if (AdaptiveChargingManager.DEBUG) {
Log.d("AdaptiveChargingManager", "getChargingStageDeadlineCallback result: " + ((int) b) + ", stage: \"" + str + "\", seconds: " + i);
public final void binderDied() {
if (DEBUG) {
Log.d("AdaptiveChargingManager", "serviceDied");
}
if (b == 0) {
adaptiveChargingStatusReceiver.onReceiveStatus(str, i);
}
destroyHalInterface(initHalInterface, deathRecipient);
adaptiveChargingStatusReceiver.onDestroyInterface();
}
});
} catch (RemoteException e) {
Log.e("AdaptiveChargingManager", "Failed to get Adaptive Chaging status: ", e);
destroyHalInterface(initHalInterface, deathRecipient);
};
IGoogleBattery googBatteryIntf = initHalInterface(deathRecipient);
if (googBatteryIntf == null) {
adaptiveChargingStatusReceiver.onDestroyInterface();
return;
}
try {
ChargingStage stage = googBatteryIntf.getChargingStageAndDeadline();
adaptiveChargingStatusReceiver.onReceiveStatus(stage.deadlineSecs, stage.stage);
} catch (RemoteException | ParcelFormatException e) {
Log.e("AdaptiveChargingManager", "Failed to get Adaptive Charging status: ", e);
}
destroyHalInterface(googBatteryIntf, deathRecipient);
adaptiveChargingStatusReceiver.onDestroyInterface();
}

public interface AdaptiveChargingStatusReceiver {
void onDestroyInterface();
private static void destroyHalInterface(IGoogleBattery iGoogleBattery, IBinder.DeathRecipient deathRecipient) {
if (DEBUG) {
Log.d("AdaptiveChargingManager", "destroyHalInterface");
}
if (deathRecipient != null && iGoogleBattery != null) {
iGoogleBattery.asBinder().unlinkToDeath(deathRecipient, 0);
}
}

void onReceiveStatus(String str, int i);
private static IGoogleBattery initHalInterface(IBinder.DeathRecipient deathReceiver) {
if (DEBUG) {
Log.d("AdaptiveChargingManager", "initHalInterface");
}
try {
IBinder binder = Binder.allowBlocking(ServiceManager.waitForDeclaredService("vendor.google.google_battery.IGoogleBattery/default"));
IGoogleBattery batteryInterface = null;
if (binder != null) {
batteryInterface = IGoogleBattery.Stub.asInterface(binder);
if (batteryInterface != null && deathReceiver != null) {
binder.linkToDeath(deathReceiver, 0);
}
}
return batteryInterface;
} catch (RemoteException | NoSuchElementException | SecurityException e) {
Log.e("AdaptiveChargingManager", "failed to get Google Battery HAL: ", e);
return null;
}
}
}
Expand Up @@ -117,7 +117,7 @@ public void onDestroyInterface() {
}

@Override
public void onReceiveStatus(final String str, final int i) {
public void onReceiveStatus(final int i, final String str) {
mHandler.post(() -> handleOnReceiveStatus(str, i, forceUpdate));
}
}
Expand Down

0 comments on commit c27a93d

Please sign in to comment.