Loading apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +126 −16 Original line number Diff line number Diff line Loading @@ -318,7 +318,8 @@ public class JobSchedulerService extends com.android.server.SystemService private final List<JobRestriction> mJobRestrictions; @GuardedBy("mLock") private final BatteryStateTracker mBatteryStateTracker; @VisibleForTesting final BatteryStateTracker mBatteryStateTracker; @GuardedBy("mLock") private final SparseArray<String> mCloudMediaProviderPackages = new SparseArray<>(); Loading Loading @@ -4261,20 +4262,34 @@ public class JobSchedulerService extends com.android.server.SystemService .sendToTarget(); } private final class BatteryStateTracker extends BroadcastReceiver { @VisibleForTesting final class BatteryStateTracker extends BroadcastReceiver implements BatteryManagerInternal.ChargingPolicyChangeListener { private final BatteryManagerInternal mBatteryManagerInternal; /** Last reported battery level. */ private int mBatteryLevel; /** Keep track of whether the battery is charged enough that we want to do work. */ private boolean mBatteryNotLow; /** * Track whether we're "charging", where charging means that we're ready to commit to * doing work. * Charging status based on {@link BatteryManager#ACTION_CHARGING} and * {@link BatteryManager#ACTION_DISCHARGING}. */ private boolean mCharging; /** Keep track of whether the battery is charged enough that we want to do work. */ private boolean mBatteryNotLow; /** * The most recently acquired value of * {@link BatteryManager#BATTERY_PROPERTY_CHARGING_POLICY}. */ private int mChargingPolicy; /** Track whether there is power connected. It doesn't mean the device is charging. */ private boolean mPowerConnected; /** Sequence number of last broadcast. */ private int mLastBatterySeq = -1; private BroadcastReceiver mMonitor; BatteryStateTracker() { mBatteryManagerInternal = LocalServices.getService(BatteryManagerInternal.class); } public void startTracking() { Loading @@ -4286,13 +4301,18 @@ public class JobSchedulerService extends com.android.server.SystemService // Charging/not charging. filter.addAction(BatteryManager.ACTION_CHARGING); filter.addAction(BatteryManager.ACTION_DISCHARGING); filter.addAction(Intent.ACTION_BATTERY_LEVEL_CHANGED); filter.addAction(Intent.ACTION_POWER_CONNECTED); filter.addAction(Intent.ACTION_POWER_DISCONNECTED); getTestableContext().registerReceiver(this, filter); mBatteryManagerInternal.registerChargingPolicyChangeListener(this); // Initialise tracker state. BatteryManagerInternal batteryManagerInternal = LocalServices.getService(BatteryManagerInternal.class); mBatteryNotLow = !batteryManagerInternal.getBatteryLevelLow(); mCharging = batteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY); mBatteryLevel = mBatteryManagerInternal.getBatteryLevel(); mBatteryNotLow = !mBatteryManagerInternal.getBatteryLevelLow(); mCharging = mBatteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY); mChargingPolicy = mBatteryManagerInternal.getChargingPolicy(); } public void setMonitorBatteryLocked(boolean enabled) { Loading @@ -4315,7 +4335,7 @@ public class JobSchedulerService extends com.android.server.SystemService } public boolean isCharging() { return mCharging; return isConsideredCharging(); } public boolean isBatteryNotLow() { Loading @@ -4326,17 +4346,42 @@ public class JobSchedulerService extends com.android.server.SystemService return mMonitor != null; } public boolean isPowerConnected() { return mPowerConnected; } public int getSeq() { return mLastBatterySeq; } @Override public void onChargingPolicyChanged(int newPolicy) { synchronized (mLock) { if (mChargingPolicy == newPolicy) { return; } if (DEBUG) { Slog.i(TAG, "Charging policy changed from " + mChargingPolicy + " to " + newPolicy); } final boolean wasConsideredCharging = isConsideredCharging(); mChargingPolicy = newPolicy; if (isConsideredCharging() != wasConsideredCharging) { for (int c = mControllers.size() - 1; c >= 0; --c) { mControllers.get(c).onBatteryStateChangedLocked(); } } } } @Override public void onReceive(Context context, Intent intent) { onReceiveInternal(intent); } @VisibleForTesting public void onReceiveInternal(Intent intent) { private void onReceiveInternal(Intent intent) { synchronized (mLock) { final String action = intent.getAction(); boolean changed = false; Loading @@ -4356,21 +4401,49 @@ public class JobSchedulerService extends com.android.server.SystemService mBatteryNotLow = true; changed = true; } } else if (Intent.ACTION_BATTERY_LEVEL_CHANGED.equals(action)) { if (DEBUG) { Slog.d(TAG, "Battery level changed @ " + sElapsedRealtimeClock.millis()); } final boolean wasConsideredCharging = isConsideredCharging(); mBatteryLevel = mBatteryManagerInternal.getBatteryLevel(); changed = isConsideredCharging() != wasConsideredCharging; } else if (Intent.ACTION_POWER_CONNECTED.equals(action)) { if (DEBUG) { Slog.d(TAG, "Power connected @ " + sElapsedRealtimeClock.millis()); } if (mPowerConnected) { return; } mPowerConnected = true; changed = true; } else if (Intent.ACTION_POWER_DISCONNECTED.equals(action)) { if (DEBUG) { Slog.d(TAG, "Power disconnected @ " + sElapsedRealtimeClock.millis()); } if (!mPowerConnected) { return; } mPowerConnected = false; changed = true; } else if (BatteryManager.ACTION_CHARGING.equals(action)) { if (DEBUG) { Slog.d(TAG, "Battery charging @ " + sElapsedRealtimeClock.millis()); } if (!mCharging) { final boolean wasConsideredCharging = isConsideredCharging(); mCharging = true; changed = true; changed = isConsideredCharging() != wasConsideredCharging; } } else if (BatteryManager.ACTION_DISCHARGING.equals(action)) { if (DEBUG) { Slog.d(TAG, "Battery discharging @ " + sElapsedRealtimeClock.millis()); } if (mCharging) { final boolean wasConsideredCharging = isConsideredCharging(); mCharging = false; changed = true; changed = isConsideredCharging() != wasConsideredCharging; } } mLastBatterySeq = Loading @@ -4382,6 +4455,30 @@ public class JobSchedulerService extends com.android.server.SystemService } } } private boolean isConsideredCharging() { if (mCharging) { return true; } // BatteryService (or Health HAL or whatever central location makes sense) // should ideally hold this logic so that everyone has a consistent // idea of when the device is charging (or an otherwise stable charging/plugged state). // TODO(304512874): move this determination to BatteryService if (!mPowerConnected) { return false; } if (mChargingPolicy == Integer.MIN_VALUE) { // Property not supported on this device. return false; } // Adaptive charging policies don't expose their target battery level, but 80% is a // commonly used threshold for battery health, so assume that's what's being used by // the policies and use 70%+ as the threshold here for charging in case some // implementations choose to discharge the device slightly before recharging back up // to the target level. return mBatteryLevel >= 70 && BatteryManager.isAdaptiveChargingPolicy(mChargingPolicy); } } final class LocalService implements JobSchedulerInternal { Loading Loading @@ -5450,6 +5547,13 @@ public class JobSchedulerService extends com.android.server.SystemService } } /** Return {@code true} if the device is connected to power. */ public boolean isPowerConnected() { synchronized (mLock) { return mBatteryStateTracker.isPowerConnected(); } } int getStorageSeq() { synchronized (mLock) { return mStorageController.getTracker().getSeq(); Loading Loading @@ -5778,8 +5882,14 @@ public class JobSchedulerService extends com.android.server.SystemService mQuotaTracker.dump(pw); pw.println(); pw.print("Power connected: "); pw.println(mBatteryStateTracker.isPowerConnected()); pw.print("Battery charging: "); pw.println(mBatteryStateTracker.isCharging()); pw.println(mBatteryStateTracker.mCharging); pw.print("Considered charging: "); pw.println(mBatteryStateTracker.isConsideredCharging()); pw.print("Battery level: "); pw.println(mBatteryStateTracker.mBatteryLevel); pw.print("Battery not low: "); pw.println(mBatteryStateTracker.isBatteryNotLow()); if (mBatteryStateTracker.isMonitoring()) { Loading apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java +2 −74 Original line number Diff line number Diff line Loading @@ -20,12 +20,6 @@ import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import android.annotation.NonNull; import android.app.job.JobInfo; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.BatteryManager; import android.os.BatteryManagerInternal; import android.os.UserHandle; import android.util.ArraySet; import android.util.IndentingPrintWriter; Loading @@ -36,7 +30,6 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.server.AppSchedulingModuleThread; import com.android.server.LocalServices; import com.android.server.job.JobSchedulerService; import com.android.server.job.StateControllerProto; Loading @@ -60,8 +53,6 @@ public final class BatteryController extends RestrictingController { @GuardedBy("mLock") private final ArraySet<JobStatus> mTopStartedJobs = new ArraySet<>(); private final PowerTracker mPowerTracker; private final FlexibilityController mFlexibilityController; /** * Helper set to avoid too much GC churn from frequent calls to Loading @@ -77,15 +68,9 @@ public final class BatteryController extends RestrictingController { public BatteryController(JobSchedulerService service, FlexibilityController flexibilityController) { super(service); mPowerTracker = new PowerTracker(); mFlexibilityController = flexibilityController; } @Override public void startTrackingLocked() { mPowerTracker.startTracking(); } @Override public void maybeStartTrackingJobLocked(JobStatus taskStatus, JobStatus lastJob) { if (taskStatus.hasPowerConstraint()) { Loading @@ -95,7 +80,7 @@ public final class BatteryController extends RestrictingController { if (taskStatus.hasChargingConstraint()) { if (hasTopExemptionLocked(taskStatus)) { taskStatus.setChargingConstraintSatisfied(nowElapsed, mPowerTracker.isPowerConnected()); mService.isPowerConnected()); } else { taskStatus.setChargingConstraintSatisfied(nowElapsed, mService.isBatteryCharging() && mService.isBatteryNotLow()); Loading Loading @@ -178,7 +163,7 @@ public final class BatteryController extends RestrictingController { @GuardedBy("mLock") private void maybeReportNewChargingStateLocked() { final boolean powerConnected = mPowerTracker.isPowerConnected(); final boolean powerConnected = mService.isPowerConnected(); final boolean stablePower = mService.isBatteryCharging() && mService.isBatteryNotLow(); final boolean batteryNotLow = mService.isBatteryNotLow(); if (DEBUG) { Loading Loading @@ -239,62 +224,6 @@ public final class BatteryController extends RestrictingController { mChangedJobs.clear(); } private final class PowerTracker extends BroadcastReceiver { /** * Track whether there is power connected. It doesn't mean the device is charging. * Use {@link JobSchedulerService#isBatteryCharging()} to determine if the device is * charging. */ private boolean mPowerConnected; PowerTracker() { } void startTracking() { IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_POWER_CONNECTED); filter.addAction(Intent.ACTION_POWER_DISCONNECTED); mContext.registerReceiver(this, filter); // Initialize tracker state. BatteryManagerInternal batteryManagerInternal = LocalServices.getService(BatteryManagerInternal.class); mPowerConnected = batteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY); } boolean isPowerConnected() { return mPowerConnected; } @Override public void onReceive(Context context, Intent intent) { synchronized (mLock) { final String action = intent.getAction(); if (Intent.ACTION_POWER_CONNECTED.equals(action)) { if (DEBUG) { Slog.d(TAG, "Power connected @ " + sElapsedRealtimeClock.millis()); } if (mPowerConnected) { return; } mPowerConnected = true; } else if (Intent.ACTION_POWER_DISCONNECTED.equals(action)) { if (DEBUG) { Slog.d(TAG, "Power disconnected @ " + sElapsedRealtimeClock.millis()); } if (!mPowerConnected) { return; } mPowerConnected = false; } maybeReportNewChargingStateLocked(); } } } @VisibleForTesting ArraySet<JobStatus> getTrackedJobs() { return mTrackedTasks; Loading @@ -308,7 +237,6 @@ public final class BatteryController extends RestrictingController { @Override public void dumpControllerStateLocked(IndentingPrintWriter pw, Predicate<JobStatus> predicate) { pw.println("Power connected: " + mPowerTracker.isPowerConnected()); pw.println("Stable power: " + (mService.isBatteryCharging() && mService.isBatteryNotLow())); pw.println("Not low: " + mService.isBatteryNotLow()); Loading core/java/android/os/BatteryManager.java +10 −0 Original line number Diff line number Diff line Loading @@ -238,6 +238,16 @@ public class BatteryManager { public static final int CHARGING_POLICY_ADAPTIVE_LONGLIFE = OsProtoEnums.CHARGING_POLICY_ADAPTIVE_LONGLIFE; // = 4 /** * Returns true if the policy is some type of adaptive charging policy. * @hide */ public static boolean isAdaptiveChargingPolicy(int policy) { return policy == CHARGING_POLICY_ADAPTIVE_AC || policy == CHARGING_POLICY_ADAPTIVE_AON || policy == CHARGING_POLICY_ADAPTIVE_LONGLIFE; } // values for "battery part status" property /** * Battery part status is not supported. Loading core/java/android/os/BatteryManagerInternal.java +22 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package android.os; import android.annotation.NonNull; /** * Battery manager local system service interface. * Loading Loading @@ -84,6 +86,26 @@ public abstract class BatteryManagerInternal { */ public abstract boolean getBatteryLevelLow(); public interface ChargingPolicyChangeListener { void onChargingPolicyChanged(int newPolicy); } /** * Register a listener for changes to {@link BatteryManager#BATTERY_PROPERTY_CHARGING_POLICY}. * The charging policy can't be added to the BATTERY_CHANGED intent because it requires * the BATTERY_STATS permission. */ public abstract void registerChargingPolicyChangeListener( @NonNull ChargingPolicyChangeListener chargingPolicyChangeListener); /** * Returns the value of {@link BatteryManager#BATTERY_PROPERTY_CHARGING_POLICY}. * This will return {@link Integer#MIN_VALUE} if the device does not support the property. * * @see BatteryManager#getIntProperty(int) */ public abstract int getChargingPolicy(); /** * Returns a non-zero value if an unsupported charger is attached. * Loading services/core/java/com/android/server/BatteryService.java +42 −1 Original line number Diff line number Diff line Loading @@ -16,8 +16,8 @@ package com.android.server; import static android.os.Flags.stateOfHealthPublic; import static android.os.Flags.batteryServiceSupportCurrentAdbCommand; import static android.os.Flags.stateOfHealthPublic; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent; import static com.android.server.health.Utils.copyV1Battery; Loading Loading @@ -81,6 +81,7 @@ import java.io.PrintWriter; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.NoSuchElementException; import java.util.concurrent.CopyOnWriteArraySet; /** * <p>BatteryService monitors the charging status, and charge level of the device Loading Loading @@ -157,6 +158,12 @@ public final class BatteryService extends SystemService { private int mLastChargeCounter; private int mLastBatteryCycleCount; private int mLastCharingState; /** * The last seen charging policy. This requires the * {@link android.Manifest.permission#BATTERY_STATS} permission and should therefore not be * included in the ACTION_BATTERY_CHANGED intent extras. */ private int mLastChargingPolicy; private int mSequence = 1; Loading Loading @@ -197,6 +204,9 @@ public final class BatteryService extends SystemService { private ArrayDeque<Bundle> mBatteryLevelsEventQueue; private long mLastBatteryLevelChangedSentMs; private final CopyOnWriteArraySet<BatteryManagerInternal.ChargingPolicyChangeListener> mChargingPolicyChangeListeners = new CopyOnWriteArraySet<>(); private Bundle mBatteryChangedOptions = BroadcastOptions.makeBasic() .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT) .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE) Loading Loading @@ -527,6 +537,11 @@ public final class BatteryService extends SystemService { shutdownIfNoPowerLocked(); shutdownIfOverTempLocked(); if (force || mHealthInfo.chargingPolicy != mLastChargingPolicy) { mLastChargingPolicy = mHealthInfo.chargingPolicy; mHandler.post(this::notifyChargingPolicyChanged); } if (force || (mHealthInfo.batteryStatus != mLastBatteryStatus || mHealthInfo.batteryHealth != mLastBatteryHealth Loading Loading @@ -827,6 +842,17 @@ public final class BatteryService extends SystemService { mLastBatteryLevelChangedSentMs = SystemClock.elapsedRealtime(); } private void notifyChargingPolicyChanged() { final int newPolicy; synchronized (mLock) { newPolicy = mLastChargingPolicy; } for (BatteryManagerInternal.ChargingPolicyChangeListener listener : mChargingPolicyChangeListeners) { listener.onChargingPolicyChanged(newPolicy); } } // TODO: Current code doesn't work since "--unplugged" flag in BSS was purposefully removed. private void logBatteryStatsLocked() { IBinder batteryInfoService = ServiceManager.getService(BatteryStats.SERVICE_NAME); Loading Loading @@ -1220,6 +1246,8 @@ public final class BatteryService extends SystemService { pw.println(" voltage: " + mHealthInfo.batteryVoltageMillivolts); pw.println(" temperature: " + mHealthInfo.batteryTemperatureTenthsCelsius); pw.println(" technology: " + mHealthInfo.batteryTechnology); pw.println(" Charging state: " + mHealthInfo.chargingState); pw.println(" Charging policy: " + mHealthInfo.chargingPolicy); } else { Shell shell = new Shell(); shell.exec(mBinderService, null, fd, null, args, null, new ResultReceiver(null)); Loading Loading @@ -1451,6 +1479,19 @@ public final class BatteryService extends SystemService { } } @Override public void registerChargingPolicyChangeListener( BatteryManagerInternal.ChargingPolicyChangeListener listener) { mChargingPolicyChangeListeners.add(listener); } @Override public int getChargingPolicy() { synchronized (mLock) { return mLastChargingPolicy; } } @Override public int getInvalidCharger() { synchronized (mLock) { Loading Loading
apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +126 −16 Original line number Diff line number Diff line Loading @@ -318,7 +318,8 @@ public class JobSchedulerService extends com.android.server.SystemService private final List<JobRestriction> mJobRestrictions; @GuardedBy("mLock") private final BatteryStateTracker mBatteryStateTracker; @VisibleForTesting final BatteryStateTracker mBatteryStateTracker; @GuardedBy("mLock") private final SparseArray<String> mCloudMediaProviderPackages = new SparseArray<>(); Loading Loading @@ -4261,20 +4262,34 @@ public class JobSchedulerService extends com.android.server.SystemService .sendToTarget(); } private final class BatteryStateTracker extends BroadcastReceiver { @VisibleForTesting final class BatteryStateTracker extends BroadcastReceiver implements BatteryManagerInternal.ChargingPolicyChangeListener { private final BatteryManagerInternal mBatteryManagerInternal; /** Last reported battery level. */ private int mBatteryLevel; /** Keep track of whether the battery is charged enough that we want to do work. */ private boolean mBatteryNotLow; /** * Track whether we're "charging", where charging means that we're ready to commit to * doing work. * Charging status based on {@link BatteryManager#ACTION_CHARGING} and * {@link BatteryManager#ACTION_DISCHARGING}. */ private boolean mCharging; /** Keep track of whether the battery is charged enough that we want to do work. */ private boolean mBatteryNotLow; /** * The most recently acquired value of * {@link BatteryManager#BATTERY_PROPERTY_CHARGING_POLICY}. */ private int mChargingPolicy; /** Track whether there is power connected. It doesn't mean the device is charging. */ private boolean mPowerConnected; /** Sequence number of last broadcast. */ private int mLastBatterySeq = -1; private BroadcastReceiver mMonitor; BatteryStateTracker() { mBatteryManagerInternal = LocalServices.getService(BatteryManagerInternal.class); } public void startTracking() { Loading @@ -4286,13 +4301,18 @@ public class JobSchedulerService extends com.android.server.SystemService // Charging/not charging. filter.addAction(BatteryManager.ACTION_CHARGING); filter.addAction(BatteryManager.ACTION_DISCHARGING); filter.addAction(Intent.ACTION_BATTERY_LEVEL_CHANGED); filter.addAction(Intent.ACTION_POWER_CONNECTED); filter.addAction(Intent.ACTION_POWER_DISCONNECTED); getTestableContext().registerReceiver(this, filter); mBatteryManagerInternal.registerChargingPolicyChangeListener(this); // Initialise tracker state. BatteryManagerInternal batteryManagerInternal = LocalServices.getService(BatteryManagerInternal.class); mBatteryNotLow = !batteryManagerInternal.getBatteryLevelLow(); mCharging = batteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY); mBatteryLevel = mBatteryManagerInternal.getBatteryLevel(); mBatteryNotLow = !mBatteryManagerInternal.getBatteryLevelLow(); mCharging = mBatteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY); mChargingPolicy = mBatteryManagerInternal.getChargingPolicy(); } public void setMonitorBatteryLocked(boolean enabled) { Loading @@ -4315,7 +4335,7 @@ public class JobSchedulerService extends com.android.server.SystemService } public boolean isCharging() { return mCharging; return isConsideredCharging(); } public boolean isBatteryNotLow() { Loading @@ -4326,17 +4346,42 @@ public class JobSchedulerService extends com.android.server.SystemService return mMonitor != null; } public boolean isPowerConnected() { return mPowerConnected; } public int getSeq() { return mLastBatterySeq; } @Override public void onChargingPolicyChanged(int newPolicy) { synchronized (mLock) { if (mChargingPolicy == newPolicy) { return; } if (DEBUG) { Slog.i(TAG, "Charging policy changed from " + mChargingPolicy + " to " + newPolicy); } final boolean wasConsideredCharging = isConsideredCharging(); mChargingPolicy = newPolicy; if (isConsideredCharging() != wasConsideredCharging) { for (int c = mControllers.size() - 1; c >= 0; --c) { mControllers.get(c).onBatteryStateChangedLocked(); } } } } @Override public void onReceive(Context context, Intent intent) { onReceiveInternal(intent); } @VisibleForTesting public void onReceiveInternal(Intent intent) { private void onReceiveInternal(Intent intent) { synchronized (mLock) { final String action = intent.getAction(); boolean changed = false; Loading @@ -4356,21 +4401,49 @@ public class JobSchedulerService extends com.android.server.SystemService mBatteryNotLow = true; changed = true; } } else if (Intent.ACTION_BATTERY_LEVEL_CHANGED.equals(action)) { if (DEBUG) { Slog.d(TAG, "Battery level changed @ " + sElapsedRealtimeClock.millis()); } final boolean wasConsideredCharging = isConsideredCharging(); mBatteryLevel = mBatteryManagerInternal.getBatteryLevel(); changed = isConsideredCharging() != wasConsideredCharging; } else if (Intent.ACTION_POWER_CONNECTED.equals(action)) { if (DEBUG) { Slog.d(TAG, "Power connected @ " + sElapsedRealtimeClock.millis()); } if (mPowerConnected) { return; } mPowerConnected = true; changed = true; } else if (Intent.ACTION_POWER_DISCONNECTED.equals(action)) { if (DEBUG) { Slog.d(TAG, "Power disconnected @ " + sElapsedRealtimeClock.millis()); } if (!mPowerConnected) { return; } mPowerConnected = false; changed = true; } else if (BatteryManager.ACTION_CHARGING.equals(action)) { if (DEBUG) { Slog.d(TAG, "Battery charging @ " + sElapsedRealtimeClock.millis()); } if (!mCharging) { final boolean wasConsideredCharging = isConsideredCharging(); mCharging = true; changed = true; changed = isConsideredCharging() != wasConsideredCharging; } } else if (BatteryManager.ACTION_DISCHARGING.equals(action)) { if (DEBUG) { Slog.d(TAG, "Battery discharging @ " + sElapsedRealtimeClock.millis()); } if (mCharging) { final boolean wasConsideredCharging = isConsideredCharging(); mCharging = false; changed = true; changed = isConsideredCharging() != wasConsideredCharging; } } mLastBatterySeq = Loading @@ -4382,6 +4455,30 @@ public class JobSchedulerService extends com.android.server.SystemService } } } private boolean isConsideredCharging() { if (mCharging) { return true; } // BatteryService (or Health HAL or whatever central location makes sense) // should ideally hold this logic so that everyone has a consistent // idea of when the device is charging (or an otherwise stable charging/plugged state). // TODO(304512874): move this determination to BatteryService if (!mPowerConnected) { return false; } if (mChargingPolicy == Integer.MIN_VALUE) { // Property not supported on this device. return false; } // Adaptive charging policies don't expose their target battery level, but 80% is a // commonly used threshold for battery health, so assume that's what's being used by // the policies and use 70%+ as the threshold here for charging in case some // implementations choose to discharge the device slightly before recharging back up // to the target level. return mBatteryLevel >= 70 && BatteryManager.isAdaptiveChargingPolicy(mChargingPolicy); } } final class LocalService implements JobSchedulerInternal { Loading Loading @@ -5450,6 +5547,13 @@ public class JobSchedulerService extends com.android.server.SystemService } } /** Return {@code true} if the device is connected to power. */ public boolean isPowerConnected() { synchronized (mLock) { return mBatteryStateTracker.isPowerConnected(); } } int getStorageSeq() { synchronized (mLock) { return mStorageController.getTracker().getSeq(); Loading Loading @@ -5778,8 +5882,14 @@ public class JobSchedulerService extends com.android.server.SystemService mQuotaTracker.dump(pw); pw.println(); pw.print("Power connected: "); pw.println(mBatteryStateTracker.isPowerConnected()); pw.print("Battery charging: "); pw.println(mBatteryStateTracker.isCharging()); pw.println(mBatteryStateTracker.mCharging); pw.print("Considered charging: "); pw.println(mBatteryStateTracker.isConsideredCharging()); pw.print("Battery level: "); pw.println(mBatteryStateTracker.mBatteryLevel); pw.print("Battery not low: "); pw.println(mBatteryStateTracker.isBatteryNotLow()); if (mBatteryStateTracker.isMonitoring()) { Loading
apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java +2 −74 Original line number Diff line number Diff line Loading @@ -20,12 +20,6 @@ import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import android.annotation.NonNull; import android.app.job.JobInfo; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.BatteryManager; import android.os.BatteryManagerInternal; import android.os.UserHandle; import android.util.ArraySet; import android.util.IndentingPrintWriter; Loading @@ -36,7 +30,6 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.server.AppSchedulingModuleThread; import com.android.server.LocalServices; import com.android.server.job.JobSchedulerService; import com.android.server.job.StateControllerProto; Loading @@ -60,8 +53,6 @@ public final class BatteryController extends RestrictingController { @GuardedBy("mLock") private final ArraySet<JobStatus> mTopStartedJobs = new ArraySet<>(); private final PowerTracker mPowerTracker; private final FlexibilityController mFlexibilityController; /** * Helper set to avoid too much GC churn from frequent calls to Loading @@ -77,15 +68,9 @@ public final class BatteryController extends RestrictingController { public BatteryController(JobSchedulerService service, FlexibilityController flexibilityController) { super(service); mPowerTracker = new PowerTracker(); mFlexibilityController = flexibilityController; } @Override public void startTrackingLocked() { mPowerTracker.startTracking(); } @Override public void maybeStartTrackingJobLocked(JobStatus taskStatus, JobStatus lastJob) { if (taskStatus.hasPowerConstraint()) { Loading @@ -95,7 +80,7 @@ public final class BatteryController extends RestrictingController { if (taskStatus.hasChargingConstraint()) { if (hasTopExemptionLocked(taskStatus)) { taskStatus.setChargingConstraintSatisfied(nowElapsed, mPowerTracker.isPowerConnected()); mService.isPowerConnected()); } else { taskStatus.setChargingConstraintSatisfied(nowElapsed, mService.isBatteryCharging() && mService.isBatteryNotLow()); Loading Loading @@ -178,7 +163,7 @@ public final class BatteryController extends RestrictingController { @GuardedBy("mLock") private void maybeReportNewChargingStateLocked() { final boolean powerConnected = mPowerTracker.isPowerConnected(); final boolean powerConnected = mService.isPowerConnected(); final boolean stablePower = mService.isBatteryCharging() && mService.isBatteryNotLow(); final boolean batteryNotLow = mService.isBatteryNotLow(); if (DEBUG) { Loading Loading @@ -239,62 +224,6 @@ public final class BatteryController extends RestrictingController { mChangedJobs.clear(); } private final class PowerTracker extends BroadcastReceiver { /** * Track whether there is power connected. It doesn't mean the device is charging. * Use {@link JobSchedulerService#isBatteryCharging()} to determine if the device is * charging. */ private boolean mPowerConnected; PowerTracker() { } void startTracking() { IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_POWER_CONNECTED); filter.addAction(Intent.ACTION_POWER_DISCONNECTED); mContext.registerReceiver(this, filter); // Initialize tracker state. BatteryManagerInternal batteryManagerInternal = LocalServices.getService(BatteryManagerInternal.class); mPowerConnected = batteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY); } boolean isPowerConnected() { return mPowerConnected; } @Override public void onReceive(Context context, Intent intent) { synchronized (mLock) { final String action = intent.getAction(); if (Intent.ACTION_POWER_CONNECTED.equals(action)) { if (DEBUG) { Slog.d(TAG, "Power connected @ " + sElapsedRealtimeClock.millis()); } if (mPowerConnected) { return; } mPowerConnected = true; } else if (Intent.ACTION_POWER_DISCONNECTED.equals(action)) { if (DEBUG) { Slog.d(TAG, "Power disconnected @ " + sElapsedRealtimeClock.millis()); } if (!mPowerConnected) { return; } mPowerConnected = false; } maybeReportNewChargingStateLocked(); } } } @VisibleForTesting ArraySet<JobStatus> getTrackedJobs() { return mTrackedTasks; Loading @@ -308,7 +237,6 @@ public final class BatteryController extends RestrictingController { @Override public void dumpControllerStateLocked(IndentingPrintWriter pw, Predicate<JobStatus> predicate) { pw.println("Power connected: " + mPowerTracker.isPowerConnected()); pw.println("Stable power: " + (mService.isBatteryCharging() && mService.isBatteryNotLow())); pw.println("Not low: " + mService.isBatteryNotLow()); Loading
core/java/android/os/BatteryManager.java +10 −0 Original line number Diff line number Diff line Loading @@ -238,6 +238,16 @@ public class BatteryManager { public static final int CHARGING_POLICY_ADAPTIVE_LONGLIFE = OsProtoEnums.CHARGING_POLICY_ADAPTIVE_LONGLIFE; // = 4 /** * Returns true if the policy is some type of adaptive charging policy. * @hide */ public static boolean isAdaptiveChargingPolicy(int policy) { return policy == CHARGING_POLICY_ADAPTIVE_AC || policy == CHARGING_POLICY_ADAPTIVE_AON || policy == CHARGING_POLICY_ADAPTIVE_LONGLIFE; } // values for "battery part status" property /** * Battery part status is not supported. Loading
core/java/android/os/BatteryManagerInternal.java +22 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package android.os; import android.annotation.NonNull; /** * Battery manager local system service interface. * Loading Loading @@ -84,6 +86,26 @@ public abstract class BatteryManagerInternal { */ public abstract boolean getBatteryLevelLow(); public interface ChargingPolicyChangeListener { void onChargingPolicyChanged(int newPolicy); } /** * Register a listener for changes to {@link BatteryManager#BATTERY_PROPERTY_CHARGING_POLICY}. * The charging policy can't be added to the BATTERY_CHANGED intent because it requires * the BATTERY_STATS permission. */ public abstract void registerChargingPolicyChangeListener( @NonNull ChargingPolicyChangeListener chargingPolicyChangeListener); /** * Returns the value of {@link BatteryManager#BATTERY_PROPERTY_CHARGING_POLICY}. * This will return {@link Integer#MIN_VALUE} if the device does not support the property. * * @see BatteryManager#getIntProperty(int) */ public abstract int getChargingPolicy(); /** * Returns a non-zero value if an unsupported charger is attached. * Loading
services/core/java/com/android/server/BatteryService.java +42 −1 Original line number Diff line number Diff line Loading @@ -16,8 +16,8 @@ package com.android.server; import static android.os.Flags.stateOfHealthPublic; import static android.os.Flags.batteryServiceSupportCurrentAdbCommand; import static android.os.Flags.stateOfHealthPublic; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent; import static com.android.server.health.Utils.copyV1Battery; Loading Loading @@ -81,6 +81,7 @@ import java.io.PrintWriter; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.NoSuchElementException; import java.util.concurrent.CopyOnWriteArraySet; /** * <p>BatteryService monitors the charging status, and charge level of the device Loading Loading @@ -157,6 +158,12 @@ public final class BatteryService extends SystemService { private int mLastChargeCounter; private int mLastBatteryCycleCount; private int mLastCharingState; /** * The last seen charging policy. This requires the * {@link android.Manifest.permission#BATTERY_STATS} permission and should therefore not be * included in the ACTION_BATTERY_CHANGED intent extras. */ private int mLastChargingPolicy; private int mSequence = 1; Loading Loading @@ -197,6 +204,9 @@ public final class BatteryService extends SystemService { private ArrayDeque<Bundle> mBatteryLevelsEventQueue; private long mLastBatteryLevelChangedSentMs; private final CopyOnWriteArraySet<BatteryManagerInternal.ChargingPolicyChangeListener> mChargingPolicyChangeListeners = new CopyOnWriteArraySet<>(); private Bundle mBatteryChangedOptions = BroadcastOptions.makeBasic() .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT) .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE) Loading Loading @@ -527,6 +537,11 @@ public final class BatteryService extends SystemService { shutdownIfNoPowerLocked(); shutdownIfOverTempLocked(); if (force || mHealthInfo.chargingPolicy != mLastChargingPolicy) { mLastChargingPolicy = mHealthInfo.chargingPolicy; mHandler.post(this::notifyChargingPolicyChanged); } if (force || (mHealthInfo.batteryStatus != mLastBatteryStatus || mHealthInfo.batteryHealth != mLastBatteryHealth Loading Loading @@ -827,6 +842,17 @@ public final class BatteryService extends SystemService { mLastBatteryLevelChangedSentMs = SystemClock.elapsedRealtime(); } private void notifyChargingPolicyChanged() { final int newPolicy; synchronized (mLock) { newPolicy = mLastChargingPolicy; } for (BatteryManagerInternal.ChargingPolicyChangeListener listener : mChargingPolicyChangeListeners) { listener.onChargingPolicyChanged(newPolicy); } } // TODO: Current code doesn't work since "--unplugged" flag in BSS was purposefully removed. private void logBatteryStatsLocked() { IBinder batteryInfoService = ServiceManager.getService(BatteryStats.SERVICE_NAME); Loading Loading @@ -1220,6 +1246,8 @@ public final class BatteryService extends SystemService { pw.println(" voltage: " + mHealthInfo.batteryVoltageMillivolts); pw.println(" temperature: " + mHealthInfo.batteryTemperatureTenthsCelsius); pw.println(" technology: " + mHealthInfo.batteryTechnology); pw.println(" Charging state: " + mHealthInfo.chargingState); pw.println(" Charging policy: " + mHealthInfo.chargingPolicy); } else { Shell shell = new Shell(); shell.exec(mBinderService, null, fd, null, args, null, new ResultReceiver(null)); Loading Loading @@ -1451,6 +1479,19 @@ public final class BatteryService extends SystemService { } } @Override public void registerChargingPolicyChangeListener( BatteryManagerInternal.ChargingPolicyChangeListener listener) { mChargingPolicyChangeListeners.add(listener); } @Override public int getChargingPolicy() { synchronized (mLock) { return mLastChargingPolicy; } } @Override public int getInvalidCharger() { synchronized (mLock) { Loading