Loading apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +1 −1 Original line number Diff line number Diff line Loading @@ -1556,7 +1556,7 @@ public class JobSchedulerService extends com.android.server.SystemService Slog.d(TAG, "UID " + uid + " bias changed from " + prevBias + " to " + newBias); } for (int c = 0; c < mControllers.size(); ++c) { mControllers.get(c).onUidBiasChangedLocked(uid, newBias); mControllers.get(c).onUidBiasChangedLocked(uid, prevBias, newBias); } } } Loading apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java +154 −8 Original line number Diff line number Diff line Loading @@ -18,6 +18,14 @@ package com.android.server.job.controllers; 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 @@ -27,6 +35,7 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; import com.android.server.JobSchedulerBackgroundThread; import com.android.server.LocalServices; import com.android.server.job.JobSchedulerService; import com.android.server.job.StateControllerProto; Loading @@ -42,10 +51,26 @@ public final class BatteryController extends RestrictingController { private static final boolean DEBUG = JobSchedulerService.DEBUG || Log.isLoggable(TAG, Log.DEBUG); @GuardedBy("mLock") private final ArraySet<JobStatus> mTrackedTasks = new ArraySet<>(); /** * List of jobs that started while the UID was in the TOP state. */ @GuardedBy("mLock") private final ArraySet<JobStatus> mTopStartedJobs = new ArraySet<>(); private final PowerTracker mPowerTracker; /** * Helper set to avoid too much GC churn from frequent calls to * {@link #maybeReportNewChargingStateLocked()}. */ private final ArraySet<JobStatus> mChangedJobs = new ArraySet<>(); public BatteryController(JobSchedulerService service) { super(service); mPowerTracker = new PowerTracker(); mPowerTracker.startTracking(); } @Override Loading @@ -54,8 +79,15 @@ public final class BatteryController extends RestrictingController { final long nowElapsed = sElapsedRealtimeClock.millis(); mTrackedTasks.add(taskStatus); taskStatus.setTrackingController(JobStatus.TRACKING_BATTERY); if (taskStatus.hasChargingConstraint()) { if (hasTopExemptionLocked(taskStatus)) { taskStatus.setChargingConstraintSatisfied(nowElapsed, mPowerTracker.isPowerConnected()); } else { taskStatus.setChargingConstraintSatisfied(nowElapsed, mService.isBatteryCharging() && mService.isBatteryNotLow()); } } taskStatus.setBatteryNotLowConstraintSatisfied(nowElapsed, mService.isBatteryNotLow()); } } Loading @@ -65,10 +97,33 @@ public final class BatteryController extends RestrictingController { maybeStartTrackingJobLocked(jobStatus, null); } @Override @GuardedBy("mLock") public void prepareForExecutionLocked(JobStatus jobStatus) { if (DEBUG) { Slog.d(TAG, "Prepping for " + jobStatus.toShortString()); } final int uid = jobStatus.getSourceUid(); if (mService.getUidBias(uid) == JobInfo.BIAS_TOP_APP) { if (DEBUG) { Slog.d(TAG, jobStatus.toShortString() + " is top started job"); } mTopStartedJobs.add(jobStatus); } } @Override @GuardedBy("mLock") public void unprepareFromExecutionLocked(JobStatus jobStatus) { mTopStartedJobs.remove(jobStatus); } @Override public void maybeStopTrackingJobLocked(JobStatus taskStatus, JobStatus incomingJob, boolean forUpdate) { if (taskStatus.clearTrackingController(JobStatus.TRACKING_BATTERY)) { mTrackedTasks.remove(taskStatus); mTopStartedJobs.remove(taskStatus); } } Loading @@ -90,33 +145,124 @@ public final class BatteryController extends RestrictingController { }); } @Override @GuardedBy("mLock") public void onUidBiasChangedLocked(int uid, int prevBias, int newBias) { if (prevBias == JobInfo.BIAS_TOP_APP || newBias == JobInfo.BIAS_TOP_APP) { maybeReportNewChargingStateLocked(); } } @GuardedBy("mLock") private boolean hasTopExemptionLocked(@NonNull JobStatus taskStatus) { return mService.getUidBias(taskStatus.getSourceUid()) == JobInfo.BIAS_TOP_APP || mTopStartedJobs.contains(taskStatus); } @GuardedBy("mLock") private void maybeReportNewChargingStateLocked() { final boolean powerConnected = mPowerTracker.isPowerConnected(); final boolean stablePower = mService.isBatteryCharging() && mService.isBatteryNotLow(); final boolean batteryNotLow = mService.isBatteryNotLow(); if (DEBUG) { Slog.d(TAG, "maybeReportNewChargingStateLocked: " + stablePower); Slog.d(TAG, "maybeReportNewChargingStateLocked: " + powerConnected + "/" + stablePower + "/" + batteryNotLow); } final long nowElapsed = sElapsedRealtimeClock.millis(); boolean reportChange = false; for (int i = mTrackedTasks.size() - 1; i >= 0; i--) { final JobStatus ts = mTrackedTasks.valueAt(i); reportChange |= ts.setChargingConstraintSatisfied(nowElapsed, stablePower); reportChange |= ts.setBatteryNotLowConstraintSatisfied(nowElapsed, batteryNotLow); if (ts.hasChargingConstraint()) { if (hasTopExemptionLocked(ts) && ts.getEffectivePriority() >= JobInfo.PRIORITY_DEFAULT) { // If the job started while the app was on top or the app is currently on top, // let the job run as long as there's power connected, even if the device isn't // officially charging. // For user requested/initiated jobs, users may be confused when the task stops // running even though the device is plugged in. // Low priority jobs don't need to be exempted. if (ts.setChargingConstraintSatisfied(nowElapsed, powerConnected)) { mChangedJobs.add(ts); } } else if (ts.setChargingConstraintSatisfied(nowElapsed, stablePower)) { mChangedJobs.add(ts); } } if (ts.hasBatteryNotLowConstraint() && ts.setBatteryNotLowConstraintSatisfied(nowElapsed, batteryNotLow)) { mChangedJobs.add(ts); } } if (stablePower || batteryNotLow) { // If one of our conditions has been satisfied, always schedule any newly ready jobs. mStateChangedListener.onRunJobNow(null); } else if (reportChange) { } else if (mChangedJobs.size() > 0) { // Otherwise, just let the job scheduler know the state has changed and take care of it // as it thinks is best. mStateChangedListener.onControllerStateChanged(mTrackedTasks); mStateChangedListener.onControllerStateChanged(mChangedJobs); } 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(); } } } @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 apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java +1 −1 Original line number Diff line number Diff line Loading @@ -517,7 +517,7 @@ public final class ConnectivityController extends RestrictingController implemen @GuardedBy("mLock") @Override public void onUidBiasChangedLocked(int uid, int newBias) { public void onUidBiasChangedLocked(int uid, int prevBias, int newBias) { UidStats uidStats = mUidStats.get(uid); if (uidStats != null && uidStats.baseBias != newBias) { uidStats.baseBias = newBias; Loading apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java +4 −13 Original line number Diff line number Diff line Loading @@ -40,7 +40,6 @@ import android.util.IndentingPrintWriter; import android.util.Log; import android.util.Slog; import android.util.SparseArrayMap; import android.util.SparseBooleanArray; import android.util.TimeUtils; import com.android.internal.annotations.GuardedBy; Loading Loading @@ -81,9 +80,6 @@ public class PrefetchController extends StateController { */ @GuardedBy("mLock") private final SparseArrayMap<String, Long> mEstimatedLaunchTimes = new SparseArrayMap<>(); /** Cached list of UIDs in the TOP state. */ @GuardedBy("mLock") private final SparseBooleanArray mTopUids = new SparseBooleanArray(); private final ThresholdAlarmListener mThresholdAlarmListener; /** Loading Loading @@ -186,15 +182,9 @@ public class PrefetchController extends StateController { @GuardedBy("mLock") @Override public void onUidBiasChangedLocked(int uid, int newBias) { public void onUidBiasChangedLocked(int uid, int prevBias, int newBias) { final boolean isNowTop = newBias == JobInfo.BIAS_TOP_APP; final boolean wasTop = mTopUids.get(uid); if (isNowTop) { mTopUids.put(uid, true); } else { // Delete entries of non-top apps so the set doesn't get too large. mTopUids.delete(uid); } final boolean wasTop = prevBias == JobInfo.BIAS_TOP_APP; if (isNowTop != wasTop) { mHandler.obtainMessage(MSG_PROCESS_TOP_STATE_CHANGE, uid, 0).sendToTarget(); } Loading Loading @@ -314,7 +304,8 @@ public class PrefetchController extends StateController { // 3. The app is not open but has an active widget (we can't tell if a widget displays // status/data, so this assumes the prefetch job is to update the data displayed on // the widget). final boolean appIsOpen = mTopUids.get(jobStatus.getSourceUid()); final boolean appIsOpen = mService.getUidBias(jobStatus.getSourceUid()) == JobInfo.BIAS_TOP_APP; final boolean satisfied; if (!appIsOpen) { final int userId = jobStatus.getSourceUserId(); Loading apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java +1 −1 Original line number Diff line number Diff line Loading @@ -144,7 +144,7 @@ public abstract class StateController { * important the UID is. */ @GuardedBy("mLock") public void onUidBiasChangedLocked(int uid, int newBias) { public void onUidBiasChangedLocked(int uid, int prevBias, int newBias) { } protected boolean wouldBeReadyWithConstraintLocked(JobStatus jobStatus, int constraint) { Loading Loading
apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +1 −1 Original line number Diff line number Diff line Loading @@ -1556,7 +1556,7 @@ public class JobSchedulerService extends com.android.server.SystemService Slog.d(TAG, "UID " + uid + " bias changed from " + prevBias + " to " + newBias); } for (int c = 0; c < mControllers.size(); ++c) { mControllers.get(c).onUidBiasChangedLocked(uid, newBias); mControllers.get(c).onUidBiasChangedLocked(uid, prevBias, newBias); } } } Loading
apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java +154 −8 Original line number Diff line number Diff line Loading @@ -18,6 +18,14 @@ package com.android.server.job.controllers; 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 @@ -27,6 +35,7 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; import com.android.server.JobSchedulerBackgroundThread; import com.android.server.LocalServices; import com.android.server.job.JobSchedulerService; import com.android.server.job.StateControllerProto; Loading @@ -42,10 +51,26 @@ public final class BatteryController extends RestrictingController { private static final boolean DEBUG = JobSchedulerService.DEBUG || Log.isLoggable(TAG, Log.DEBUG); @GuardedBy("mLock") private final ArraySet<JobStatus> mTrackedTasks = new ArraySet<>(); /** * List of jobs that started while the UID was in the TOP state. */ @GuardedBy("mLock") private final ArraySet<JobStatus> mTopStartedJobs = new ArraySet<>(); private final PowerTracker mPowerTracker; /** * Helper set to avoid too much GC churn from frequent calls to * {@link #maybeReportNewChargingStateLocked()}. */ private final ArraySet<JobStatus> mChangedJobs = new ArraySet<>(); public BatteryController(JobSchedulerService service) { super(service); mPowerTracker = new PowerTracker(); mPowerTracker.startTracking(); } @Override Loading @@ -54,8 +79,15 @@ public final class BatteryController extends RestrictingController { final long nowElapsed = sElapsedRealtimeClock.millis(); mTrackedTasks.add(taskStatus); taskStatus.setTrackingController(JobStatus.TRACKING_BATTERY); if (taskStatus.hasChargingConstraint()) { if (hasTopExemptionLocked(taskStatus)) { taskStatus.setChargingConstraintSatisfied(nowElapsed, mPowerTracker.isPowerConnected()); } else { taskStatus.setChargingConstraintSatisfied(nowElapsed, mService.isBatteryCharging() && mService.isBatteryNotLow()); } } taskStatus.setBatteryNotLowConstraintSatisfied(nowElapsed, mService.isBatteryNotLow()); } } Loading @@ -65,10 +97,33 @@ public final class BatteryController extends RestrictingController { maybeStartTrackingJobLocked(jobStatus, null); } @Override @GuardedBy("mLock") public void prepareForExecutionLocked(JobStatus jobStatus) { if (DEBUG) { Slog.d(TAG, "Prepping for " + jobStatus.toShortString()); } final int uid = jobStatus.getSourceUid(); if (mService.getUidBias(uid) == JobInfo.BIAS_TOP_APP) { if (DEBUG) { Slog.d(TAG, jobStatus.toShortString() + " is top started job"); } mTopStartedJobs.add(jobStatus); } } @Override @GuardedBy("mLock") public void unprepareFromExecutionLocked(JobStatus jobStatus) { mTopStartedJobs.remove(jobStatus); } @Override public void maybeStopTrackingJobLocked(JobStatus taskStatus, JobStatus incomingJob, boolean forUpdate) { if (taskStatus.clearTrackingController(JobStatus.TRACKING_BATTERY)) { mTrackedTasks.remove(taskStatus); mTopStartedJobs.remove(taskStatus); } } Loading @@ -90,33 +145,124 @@ public final class BatteryController extends RestrictingController { }); } @Override @GuardedBy("mLock") public void onUidBiasChangedLocked(int uid, int prevBias, int newBias) { if (prevBias == JobInfo.BIAS_TOP_APP || newBias == JobInfo.BIAS_TOP_APP) { maybeReportNewChargingStateLocked(); } } @GuardedBy("mLock") private boolean hasTopExemptionLocked(@NonNull JobStatus taskStatus) { return mService.getUidBias(taskStatus.getSourceUid()) == JobInfo.BIAS_TOP_APP || mTopStartedJobs.contains(taskStatus); } @GuardedBy("mLock") private void maybeReportNewChargingStateLocked() { final boolean powerConnected = mPowerTracker.isPowerConnected(); final boolean stablePower = mService.isBatteryCharging() && mService.isBatteryNotLow(); final boolean batteryNotLow = mService.isBatteryNotLow(); if (DEBUG) { Slog.d(TAG, "maybeReportNewChargingStateLocked: " + stablePower); Slog.d(TAG, "maybeReportNewChargingStateLocked: " + powerConnected + "/" + stablePower + "/" + batteryNotLow); } final long nowElapsed = sElapsedRealtimeClock.millis(); boolean reportChange = false; for (int i = mTrackedTasks.size() - 1; i >= 0; i--) { final JobStatus ts = mTrackedTasks.valueAt(i); reportChange |= ts.setChargingConstraintSatisfied(nowElapsed, stablePower); reportChange |= ts.setBatteryNotLowConstraintSatisfied(nowElapsed, batteryNotLow); if (ts.hasChargingConstraint()) { if (hasTopExemptionLocked(ts) && ts.getEffectivePriority() >= JobInfo.PRIORITY_DEFAULT) { // If the job started while the app was on top or the app is currently on top, // let the job run as long as there's power connected, even if the device isn't // officially charging. // For user requested/initiated jobs, users may be confused when the task stops // running even though the device is plugged in. // Low priority jobs don't need to be exempted. if (ts.setChargingConstraintSatisfied(nowElapsed, powerConnected)) { mChangedJobs.add(ts); } } else if (ts.setChargingConstraintSatisfied(nowElapsed, stablePower)) { mChangedJobs.add(ts); } } if (ts.hasBatteryNotLowConstraint() && ts.setBatteryNotLowConstraintSatisfied(nowElapsed, batteryNotLow)) { mChangedJobs.add(ts); } } if (stablePower || batteryNotLow) { // If one of our conditions has been satisfied, always schedule any newly ready jobs. mStateChangedListener.onRunJobNow(null); } else if (reportChange) { } else if (mChangedJobs.size() > 0) { // Otherwise, just let the job scheduler know the state has changed and take care of it // as it thinks is best. mStateChangedListener.onControllerStateChanged(mTrackedTasks); mStateChangedListener.onControllerStateChanged(mChangedJobs); } 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(); } } } @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
apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java +1 −1 Original line number Diff line number Diff line Loading @@ -517,7 +517,7 @@ public final class ConnectivityController extends RestrictingController implemen @GuardedBy("mLock") @Override public void onUidBiasChangedLocked(int uid, int newBias) { public void onUidBiasChangedLocked(int uid, int prevBias, int newBias) { UidStats uidStats = mUidStats.get(uid); if (uidStats != null && uidStats.baseBias != newBias) { uidStats.baseBias = newBias; Loading
apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java +4 −13 Original line number Diff line number Diff line Loading @@ -40,7 +40,6 @@ import android.util.IndentingPrintWriter; import android.util.Log; import android.util.Slog; import android.util.SparseArrayMap; import android.util.SparseBooleanArray; import android.util.TimeUtils; import com.android.internal.annotations.GuardedBy; Loading Loading @@ -81,9 +80,6 @@ public class PrefetchController extends StateController { */ @GuardedBy("mLock") private final SparseArrayMap<String, Long> mEstimatedLaunchTimes = new SparseArrayMap<>(); /** Cached list of UIDs in the TOP state. */ @GuardedBy("mLock") private final SparseBooleanArray mTopUids = new SparseBooleanArray(); private final ThresholdAlarmListener mThresholdAlarmListener; /** Loading Loading @@ -186,15 +182,9 @@ public class PrefetchController extends StateController { @GuardedBy("mLock") @Override public void onUidBiasChangedLocked(int uid, int newBias) { public void onUidBiasChangedLocked(int uid, int prevBias, int newBias) { final boolean isNowTop = newBias == JobInfo.BIAS_TOP_APP; final boolean wasTop = mTopUids.get(uid); if (isNowTop) { mTopUids.put(uid, true); } else { // Delete entries of non-top apps so the set doesn't get too large. mTopUids.delete(uid); } final boolean wasTop = prevBias == JobInfo.BIAS_TOP_APP; if (isNowTop != wasTop) { mHandler.obtainMessage(MSG_PROCESS_TOP_STATE_CHANGE, uid, 0).sendToTarget(); } Loading Loading @@ -314,7 +304,8 @@ public class PrefetchController extends StateController { // 3. The app is not open but has an active widget (we can't tell if a widget displays // status/data, so this assumes the prefetch job is to update the data displayed on // the widget). final boolean appIsOpen = mTopUids.get(jobStatus.getSourceUid()); final boolean appIsOpen = mService.getUidBias(jobStatus.getSourceUid()) == JobInfo.BIAS_TOP_APP; final boolean satisfied; if (!appIsOpen) { final int userId = jobStatus.getSourceUserId(); Loading
apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java +1 −1 Original line number Diff line number Diff line Loading @@ -144,7 +144,7 @@ public abstract class StateController { * important the UID is. */ @GuardedBy("mLock") public void onUidBiasChangedLocked(int uid, int newBias) { public void onUidBiasChangedLocked(int uid, int prevBias, int newBias) { } protected boolean wouldBeReadyWithConstraintLocked(JobStatus jobStatus, int constraint) { Loading