summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java167
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java4
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java175
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java70
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java91
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java7
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java5
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java37
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java23
9 files changed, 238 insertions, 341 deletions
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 86d7a5a5a62e..3fb1fadba062 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -54,6 +54,8 @@ import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
import android.content.pm.ServiceInfo;
import android.net.Uri;
+import android.os.BatteryManager;
+import android.os.BatteryManagerInternal;
import android.os.BatteryStats;
import android.os.BatteryStatsInternal;
import android.os.Binder;
@@ -250,8 +252,6 @@ public class JobSchedulerService extends com.android.server.SystemService
*/
private final List<RestrictingController> mRestrictiveControllers;
/** Need direct access to this for testing. */
- private final BatteryController mBatteryController;
- /** Need direct access to this for testing. */
private final StorageController mStorageController;
/** Need directly for sending uid state changes */
private final DeviceIdleJobsController mDeviceIdleJobsController;
@@ -268,6 +268,9 @@ public class JobSchedulerService extends com.android.server.SystemService
*/
private final List<JobRestriction> mJobRestrictions;
+ @GuardedBy("mLock")
+ private final BatteryStateTracker mBatteryStateTracker;
+
@NonNull
private final String mSystemGalleryPackage;
@@ -1697,6 +1700,9 @@ public class JobSchedulerService extends com.android.server.SystemService
// Initialize the job store and set up any persisted jobs
mJobs = JobStore.initAndGet(this);
+ mBatteryStateTracker = new BatteryStateTracker();
+ mBatteryStateTracker.startTracking();
+
// Create the controllers.
mControllers = new ArrayList<StateController>();
final ConnectivityController connectivityController = new ConnectivityController(this);
@@ -1704,8 +1710,8 @@ public class JobSchedulerService extends com.android.server.SystemService
mControllers.add(new TimeController(this));
final IdleController idleController = new IdleController(this);
mControllers.add(idleController);
- mBatteryController = new BatteryController(this);
- mControllers.add(mBatteryController);
+ final BatteryController batteryController = new BatteryController(this);
+ mControllers.add(batteryController);
mStorageController = new StorageController(this);
mControllers.add(mStorageController);
final BackgroundJobsController backgroundJobsController =
@@ -1725,7 +1731,7 @@ public class JobSchedulerService extends com.android.server.SystemService
mControllers.add(mTareController);
mRestrictiveControllers = new ArrayList<>();
- mRestrictiveControllers.add(mBatteryController);
+ mRestrictiveControllers.add(batteryController);
mRestrictiveControllers.add(connectivityController);
mRestrictiveControllers.add(idleController);
@@ -2818,6 +2824,129 @@ public class JobSchedulerService extends com.android.server.SystemService
return adjustJobBias(bias, job);
}
+ private final class BatteryStateTracker extends BroadcastReceiver {
+ /**
+ * Track whether we're "charging", where charging means that we're ready to commit to
+ * doing work.
+ */
+ private boolean mCharging;
+ /** Keep track of whether the battery is charged enough that we want to do work. */
+ private boolean mBatteryNotLow;
+ /** Sequence number of last broadcast. */
+ private int mLastBatterySeq = -1;
+
+ private BroadcastReceiver mMonitor;
+
+ BatteryStateTracker() {
+ }
+
+ public void startTracking() {
+ IntentFilter filter = new IntentFilter();
+
+ // Battery health.
+ filter.addAction(Intent.ACTION_BATTERY_LOW);
+ filter.addAction(Intent.ACTION_BATTERY_OKAY);
+ // Charging/not charging.
+ filter.addAction(BatteryManager.ACTION_CHARGING);
+ filter.addAction(BatteryManager.ACTION_DISCHARGING);
+ getTestableContext().registerReceiver(this, filter);
+
+ // Initialise tracker state.
+ BatteryManagerInternal batteryManagerInternal =
+ LocalServices.getService(BatteryManagerInternal.class);
+ mBatteryNotLow = !batteryManagerInternal.getBatteryLevelLow();
+ mCharging = batteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
+ }
+
+ public void setMonitorBatteryLocked(boolean enabled) {
+ if (enabled) {
+ if (mMonitor == null) {
+ mMonitor = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ onReceiveInternal(intent);
+ }
+ };
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_BATTERY_CHANGED);
+ getTestableContext().registerReceiver(mMonitor, filter);
+ }
+ } else if (mMonitor != null) {
+ getTestableContext().unregisterReceiver(mMonitor);
+ mMonitor = null;
+ }
+ }
+
+ public boolean isCharging() {
+ return mCharging;
+ }
+
+ public boolean isBatteryNotLow() {
+ return mBatteryNotLow;
+ }
+
+ public boolean isMonitoring() {
+ return mMonitor != null;
+ }
+
+ public int getSeq() {
+ return mLastBatterySeq;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ onReceiveInternal(intent);
+ }
+
+ @VisibleForTesting
+ public void onReceiveInternal(Intent intent) {
+ synchronized (mLock) {
+ final String action = intent.getAction();
+ boolean changed = false;
+ if (Intent.ACTION_BATTERY_LOW.equals(action)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Battery life too low @ " + sElapsedRealtimeClock.millis());
+ }
+ if (mBatteryNotLow) {
+ mBatteryNotLow = false;
+ changed = true;
+ }
+ } else if (Intent.ACTION_BATTERY_OKAY.equals(action)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Battery high enough @ " + sElapsedRealtimeClock.millis());
+ }
+ if (!mBatteryNotLow) {
+ mBatteryNotLow = true;
+ changed = true;
+ }
+ } else if (BatteryManager.ACTION_CHARGING.equals(action)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Battery charging @ " + sElapsedRealtimeClock.millis());
+ }
+ if (!mCharging) {
+ mCharging = true;
+ changed = true;
+ }
+ } else if (BatteryManager.ACTION_DISCHARGING.equals(action)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Disconnected from power @ " + sElapsedRealtimeClock.millis());
+ }
+ if (mCharging) {
+ mCharging = false;
+ changed = true;
+ }
+ }
+ mLastBatterySeq =
+ intent.getIntExtra(BatteryManager.EXTRA_SEQUENCE, mLastBatterySeq);
+ if (changed) {
+ for (int c = mControllers.size() - 1; c >= 0; --c) {
+ mControllers.get(c).onBatteryStateChangedLocked();
+ }
+ }
+ }
+ }
+ }
+
final class LocalService implements JobSchedulerInternal {
/**
@@ -3417,29 +3546,27 @@ public class JobSchedulerService extends com.android.server.SystemService
void setMonitorBattery(boolean enabled) {
synchronized (mLock) {
- if (mBatteryController != null) {
- mBatteryController.getTracker().setMonitorBatteryLocked(enabled);
- }
+ mBatteryStateTracker.setMonitorBatteryLocked(enabled);
}
}
int getBatterySeq() {
synchronized (mLock) {
- return mBatteryController != null ? mBatteryController.getTracker().getSeq() : -1;
+ return mBatteryStateTracker.getSeq();
}
}
- boolean getBatteryCharging() {
+ /** Return {@code true} if the device is currently charging. */
+ public boolean isBatteryCharging() {
synchronized (mLock) {
- return mBatteryController != null
- ? mBatteryController.getTracker().isOnStablePower() : false;
+ return mBatteryStateTracker.isCharging();
}
}
- boolean getBatteryNotLow() {
+ /** Return {@code true} if the battery is not low. */
+ public boolean isBatteryNotLow() {
synchronized (mLock) {
- return mBatteryController != null
- ? mBatteryController.getTracker().isBatteryNotLow() : false;
+ return mBatteryStateTracker.isBatteryNotLow();
}
}
@@ -3614,6 +3741,16 @@ public class JobSchedulerService extends com.android.server.SystemService
mQuotaTracker.dump(pw);
pw.println();
+ pw.print("Battery charging: ");
+ pw.println(mBatteryStateTracker.isCharging());
+ pw.print("Battery not low: ");
+ pw.println(mBatteryStateTracker.isBatteryNotLow());
+ if (mBatteryStateTracker.isMonitoring()) {
+ pw.print("MONITORING: seq=");
+ pw.println(mBatteryStateTracker.getSeq());
+ }
+ pw.println();
+
pw.println("Started users: " + Arrays.toString(mStartedUsers));
pw.print("Registered ");
pw.print(mJobs.size());
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
index cc202130ab07..27268d267001 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
@@ -293,13 +293,13 @@ public final class JobSchedulerShellCommand extends BasicShellCommandHandler {
}
private int getBatteryCharging(PrintWriter pw) {
- boolean val = mInternal.getBatteryCharging();
+ boolean val = mInternal.isBatteryCharging();
pw.println(val);
return 0;
}
private int getBatteryNotLow(PrintWriter pw) {
- boolean val = mInternal.getBatteryNotLow();
+ boolean val = mInternal.isBatteryNotLow();
pw.println(val);
return 0;
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java
index 657f4700bd5c..f7338494384d 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java
@@ -18,12 +18,6 @@ package com.android.server.job.controllers;
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
-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;
@@ -31,8 +25,8 @@ import android.util.Log;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.LocalServices;
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.JobSchedulerBackgroundThread;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.StateControllerProto;
@@ -49,17 +43,9 @@ public final class BatteryController extends RestrictingController {
|| Log.isLoggable(TAG, Log.DEBUG);
private final ArraySet<JobStatus> mTrackedTasks = new ArraySet<>();
- private ChargingTracker mChargeTracker;
-
- @VisibleForTesting
- public ChargingTracker getTracker() {
- return mChargeTracker;
- }
public BatteryController(JobSchedulerService service) {
super(service);
- mChargeTracker = new ChargingTracker();
- mChargeTracker.startTracking();
}
@Override
@@ -68,9 +54,9 @@ public final class BatteryController extends RestrictingController {
final long nowElapsed = sElapsedRealtimeClock.millis();
mTrackedTasks.add(taskStatus);
taskStatus.setTrackingController(JobStatus.TRACKING_BATTERY);
- taskStatus.setChargingConstraintSatisfied(nowElapsed, mChargeTracker.isOnStablePower());
- taskStatus.setBatteryNotLowConstraintSatisfied(
- nowElapsed, mChargeTracker.isBatteryNotLow());
+ taskStatus.setChargingConstraintSatisfied(nowElapsed,
+ mService.isBatteryCharging() && mService.isBatteryNotLow());
+ taskStatus.setBatteryNotLowConstraintSatisfied(nowElapsed, mService.isBatteryNotLow());
}
}
@@ -93,9 +79,21 @@ public final class BatteryController extends RestrictingController {
}
}
+ @Override
+ @GuardedBy("mLock")
+ public void onBatteryStateChangedLocked() {
+ // Update job bookkeeping out of band.
+ JobSchedulerBackgroundThread.getHandler().post(() -> {
+ synchronized (mLock) {
+ maybeReportNewChargingStateLocked();
+ }
+ });
+ }
+
+ @GuardedBy("mLock")
private void maybeReportNewChargingStateLocked() {
- final boolean stablePower = mChargeTracker.isOnStablePower();
- final boolean batteryNotLow = mChargeTracker.isBatteryNotLow();
+ final boolean stablePower = mService.isBatteryCharging() && mService.isBatteryNotLow();
+ final boolean batteryNotLow = mService.isBatteryNotLow();
if (DEBUG) {
Slog.d(TAG, "maybeReportNewChargingStateLocked: " + stablePower);
}
@@ -116,133 +114,11 @@ public final class BatteryController extends RestrictingController {
}
}
- public final class ChargingTracker extends BroadcastReceiver {
- /**
- * Track whether we're "charging", where charging means that we're ready to commit to
- * doing work.
- */
- private boolean mCharging;
- /** Keep track of whether the battery is charged enough that we want to do work. */
- private boolean mBatteryHealthy;
- /** Sequence number of last broadcast. */
- private int mLastBatterySeq = -1;
-
- private BroadcastReceiver mMonitor;
-
- public ChargingTracker() {
- }
-
- public void startTracking() {
- IntentFilter filter = new IntentFilter();
-
- // Battery health.
- filter.addAction(Intent.ACTION_BATTERY_LOW);
- filter.addAction(Intent.ACTION_BATTERY_OKAY);
- // Charging/not charging.
- filter.addAction(BatteryManager.ACTION_CHARGING);
- filter.addAction(BatteryManager.ACTION_DISCHARGING);
- mContext.registerReceiver(this, filter);
-
- // Initialise tracker state.
- BatteryManagerInternal batteryManagerInternal =
- LocalServices.getService(BatteryManagerInternal.class);
- mBatteryHealthy = !batteryManagerInternal.getBatteryLevelLow();
- mCharging = batteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
- }
-
- public void setMonitorBatteryLocked(boolean enabled) {
- if (enabled) {
- if (mMonitor == null) {
- mMonitor = new BroadcastReceiver() {
- @Override public void onReceive(Context context, Intent intent) {
- ChargingTracker.this.onReceive(context, intent);
- }
- };
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_BATTERY_CHANGED);
- mContext.registerReceiver(mMonitor, filter);
- }
- } else {
- if (mMonitor != null) {
- mContext.unregisterReceiver(mMonitor);
- mMonitor = null;
- }
- }
- }
-
- public boolean isOnStablePower() {
- return mCharging && mBatteryHealthy;
- }
-
- public boolean isBatteryNotLow() {
- return mBatteryHealthy;
- }
-
- public boolean isMonitoring() {
- return mMonitor != null;
- }
-
- public int getSeq() {
- return mLastBatterySeq;
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- onReceiveInternal(intent);
- }
-
- @VisibleForTesting
- public void onReceiveInternal(Intent intent) {
- synchronized (mLock) {
- final String action = intent.getAction();
- if (Intent.ACTION_BATTERY_LOW.equals(action)) {
- if (DEBUG) {
- Slog.d(TAG, "Battery life too low to do work. @ "
- + sElapsedRealtimeClock.millis());
- }
- // If we get this action, the battery is discharging => it isn't plugged in so
- // there's no work to cancel. We track this variable for the case where it is
- // charging, but hasn't been for long enough to be healthy.
- mBatteryHealthy = false;
- maybeReportNewChargingStateLocked();
- } else if (Intent.ACTION_BATTERY_OKAY.equals(action)) {
- if (DEBUG) {
- Slog.d(TAG, "Battery life healthy enough to do work. @ "
- + sElapsedRealtimeClock.millis());
- }
- mBatteryHealthy = true;
- maybeReportNewChargingStateLocked();
- } else if (BatteryManager.ACTION_CHARGING.equals(action)) {
- if (DEBUG) {
- Slog.d(TAG, "Received charging intent, fired @ "
- + sElapsedRealtimeClock.millis());
- }
- mCharging = true;
- maybeReportNewChargingStateLocked();
- } else if (BatteryManager.ACTION_DISCHARGING.equals(action)) {
- if (DEBUG) {
- Slog.d(TAG, "Disconnected from power.");
- }
- mCharging = false;
- maybeReportNewChargingStateLocked();
- }
- mLastBatterySeq = intent.getIntExtra(BatteryManager.EXTRA_SEQUENCE,
- mLastBatterySeq);
- }
- }
- }
-
@Override
public void dumpControllerStateLocked(IndentingPrintWriter pw,
Predicate<JobStatus> predicate) {
- pw.println("Stable power: " + mChargeTracker.isOnStablePower());
- pw.println("Not low: " + mChargeTracker.isBatteryNotLow());
-
- if (mChargeTracker.isMonitoring()) {
- pw.print("MONITORING: seq=");
- pw.println(mChargeTracker.getSeq());
- }
- pw.println();
+ pw.println("Stable power: " + (mService.isBatteryCharging() && mService.isBatteryNotLow()));
+ pw.println("Not low: " + mService.isBatteryNotLow());
for (int i = 0; i < mTrackedTasks.size(); i++) {
final JobStatus js = mTrackedTasks.valueAt(i);
@@ -264,14 +140,9 @@ public final class BatteryController extends RestrictingController {
final long mToken = proto.start(StateControllerProto.BATTERY);
proto.write(StateControllerProto.BatteryController.IS_ON_STABLE_POWER,
- mChargeTracker.isOnStablePower());
+ mService.isBatteryCharging() && mService.isBatteryNotLow());
proto.write(StateControllerProto.BatteryController.IS_BATTERY_NOT_LOW,
- mChargeTracker.isBatteryNotLow());
-
- proto.write(StateControllerProto.BatteryController.IS_MONITORING,
- mChargeTracker.isMonitoring());
- proto.write(StateControllerProto.BatteryController.LAST_BROADCAST_SEQUENCE_NUMBER,
- mChargeTracker.getSeq());
+ mService.isBatteryNotLow());
for (int i = 0; i < mTrackedTasks.size(); i++) {
final JobStatus js = mTrackedTasks.valueAt(i);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
index 524d68fb72e7..9fb7ab594607 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
@@ -25,18 +25,12 @@ import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.job.JobInfo;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkPolicyManager;
import android.net.NetworkRequest;
-import android.os.BatteryManager;
-import android.os.BatteryManagerInternal;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -55,6 +49,7 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.JobSchedulerBackgroundThread;
import com.android.server.LocalServices;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.JobSchedulerService.Constants;
@@ -107,8 +102,6 @@ public final class ConnectivityController extends RestrictingController implemen
private final ConnectivityManager mConnManager;
private final NetworkPolicyManagerInternal mNetPolicyManagerInternal;
- private final ChargingTracker mChargingTracker;
-
/** List of tracked jobs keyed by source UID. */
@GuardedBy("mLock")
private final SparseArray<ArraySet<JobStatus>> mTrackedJobs = new SparseArray<>();
@@ -237,9 +230,6 @@ public final class ConnectivityController extends RestrictingController implemen
// network changes against the active network for each UID with jobs.
final NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build();
mConnManager.registerNetworkCallback(request, mNetworkCallback);
-
- mChargingTracker = new ChargingTracker();
- mChargingTracker.startTracking();
}
@GuardedBy("mLock")
@@ -535,6 +525,17 @@ public final class ConnectivityController extends RestrictingController implemen
}
}
+ @Override
+ @GuardedBy("mLock")
+ public void onBatteryStateChangedLocked() {
+ // Update job bookkeeping out of band to avoid blocking broadcast progress.
+ JobSchedulerBackgroundThread.getHandler().post(() -> {
+ synchronized (mLock) {
+ updateTrackedJobsLocked(-1, null);
+ }
+ });
+ }
+
private boolean isUsable(NetworkCapabilities capabilities) {
return capabilities != null
&& capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED);
@@ -591,7 +592,7 @@ public final class ConnectivityController extends RestrictingController implemen
// Minimum chunk size isn't defined. Check using the estimated upload/download sizes.
if (capabilities.hasCapability(NET_CAPABILITY_NOT_METERED)
- && mChargingTracker.isCharging()) {
+ && mService.isBatteryCharging()) {
// We're charging and on an unmetered network. We don't have to be as conservative about
// making sure the job will run within its max execution time. Let's just hope the app
// supports interruptible work.
@@ -1072,51 +1073,6 @@ public final class ConnectivityController extends RestrictingController implemen
}
}
- private final class ChargingTracker extends BroadcastReceiver {
- /**
- * Track whether we're "charging", where charging means that we're ready to commit to
- * doing work.
- */
- private boolean mCharging;
-
- ChargingTracker() {}
-
- public void startTracking() {
- IntentFilter filter = new IntentFilter();
- filter.addAction(BatteryManager.ACTION_CHARGING);
- filter.addAction(BatteryManager.ACTION_DISCHARGING);
- mContext.registerReceiver(this, filter);
-
- // Initialise tracker state.
- final BatteryManagerInternal batteryManagerInternal =
- LocalServices.getService(BatteryManagerInternal.class);
- mCharging = batteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
- }
-
- public boolean isCharging() {
- return mCharging;
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- synchronized (mLock) {
- final String action = intent.getAction();
- if (BatteryManager.ACTION_CHARGING.equals(action)) {
- if (mCharging) {
- return;
- }
- mCharging = true;
- } else if (BatteryManager.ACTION_DISCHARGING.equals(action)) {
- if (!mCharging) {
- return;
- }
- mCharging = false;
- }
- updateTrackedJobsLocked(-1, null);
- }
- }
- }
-
private final NetworkCallback mNetworkCallback = new NetworkCallback() {
@Override
public void onAvailable(Network network) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index c147ef83dcf0..65e1d49d1510 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -39,15 +39,11 @@ import android.app.IUidObserver;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManagerInternal;
import android.app.usage.UsageStatsManagerInternal.UsageEventListener;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.BatteryManager;
-import android.os.BatteryManagerInternal;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -319,7 +315,6 @@ public final class QuotaController extends StateController {
private final SparseLongArray mTopAppGraceCache = new SparseLongArray();
private final AlarmManager mAlarmManager;
- private final ChargingTracker mChargeTracker;
private final QcHandler mHandler;
private final QcConstants mQcConstants;
@@ -542,8 +537,6 @@ public final class QuotaController extends StateController {
@NonNull ConnectivityController connectivityController) {
super(service);
mHandler = new QcHandler(mContext.getMainLooper());
- mChargeTracker = new ChargingTracker();
- mChargeTracker.startTracking();
mAlarmManager = mContext.getSystemService(AlarmManager.class);
mQcConstants = new QcConstants();
mBackgroundJobsController = backgroundJobsController;
@@ -705,6 +698,11 @@ public final class QuotaController extends StateController {
mTopAppTrackers.delete(userId);
}
+ @Override
+ public void onBatteryStateChangedLocked() {
+ handleNewChargingStateLocked();
+ }
+
/** Drop all historical stats and stop tracking any active sessions for the specified app. */
public void clearAppStatsLocked(int userId, @NonNull String packageName) {
mTrackedJobs.delete(userId, packageName);
@@ -766,7 +764,7 @@ public final class QuotaController extends StateController {
if (!jobStatus.shouldTreatAsExpeditedJob()) {
// If quota is currently "free", then the job can run for the full amount of time,
// regardless of bucket (hence using charging instead of isQuotaFreeLocked()).
- if (mChargeTracker.isChargingLocked()
+ if (mService.isBatteryCharging()
|| mTopAppCache.get(jobStatus.getSourceUid())
|| isTopStartedJobLocked(jobStatus)
|| isUidInForeground(jobStatus.getSourceUid())) {
@@ -777,7 +775,7 @@ public final class QuotaController extends StateController {
}
// Expedited job.
- if (mChargeTracker.isChargingLocked()) {
+ if (mService.isBatteryCharging()) {
return mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS;
}
if (mTopAppCache.get(jobStatus.getSourceUid()) || isTopStartedJobLocked(jobStatus)) {
@@ -864,7 +862,7 @@ public final class QuotaController extends StateController {
@GuardedBy("mLock")
private boolean isQuotaFreeLocked(final int standbyBucket) {
// Quota constraint is not enforced while charging.
- if (mChargeTracker.isChargingLocked()) {
+ if (mService.isBatteryCharging()) {
// Restricted jobs require additional constraints when charging, so don't immediately
// mark quota as free when charging.
return standbyBucket != RESTRICTED_INDEX;
@@ -1538,15 +1536,19 @@ public final class QuotaController extends StateController {
private void handleNewChargingStateLocked() {
mTimerChargingUpdateFunctor.setStatus(sElapsedRealtimeClock.millis(),
- mChargeTracker.isChargingLocked());
+ mService.isBatteryCharging());
if (DEBUG) {
- Slog.d(TAG, "handleNewChargingStateLocked: " + mChargeTracker.isChargingLocked());
+ Slog.d(TAG, "handleNewChargingStateLocked: " + mService.isBatteryCharging());
}
// Deal with Timers first.
mEJPkgTimers.forEach(mTimerChargingUpdateFunctor);
mPkgTimers.forEach(mTimerChargingUpdateFunctor);
- // Now update jobs.
- maybeUpdateAllConstraintsLocked();
+ // Now update jobs out of band so broadcast processing can proceed.
+ JobSchedulerBackgroundThread.getHandler().post(() -> {
+ synchronized (mLock) {
+ maybeUpdateAllConstraintsLocked();
+ }
+ });
}
private void maybeUpdateAllConstraintsLocked() {
@@ -1811,58 +1813,6 @@ public final class QuotaController extends StateController {
return false;
}
- private final class ChargingTracker extends BroadcastReceiver {
- /**
- * Track whether we're charging. This has a slightly different definition than that of
- * BatteryController.
- */
- @GuardedBy("mLock")
- private boolean mCharging;
-
- ChargingTracker() {
- }
-
- public void startTracking() {
- IntentFilter filter = new IntentFilter();
-
- // Charging/not charging.
- filter.addAction(BatteryManager.ACTION_CHARGING);
- filter.addAction(BatteryManager.ACTION_DISCHARGING);
- mContext.registerReceiver(this, filter);
-
- // Initialise tracker state.
- BatteryManagerInternal batteryManagerInternal =
- LocalServices.getService(BatteryManagerInternal.class);
- mCharging = batteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
- }
-
- @GuardedBy("mLock")
- public boolean isChargingLocked() {
- return mCharging;
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- synchronized (mLock) {
- final String action = intent.getAction();
- if (BatteryManager.ACTION_CHARGING.equals(action)) {
- if (DEBUG) {
- Slog.d(TAG, "Received charging intent, fired @ "
- + sElapsedRealtimeClock.millis());
- }
- mCharging = true;
- handleNewChargingStateLocked();
- } else if (BatteryManager.ACTION_DISCHARGING.equals(action)) {
- if (DEBUG) {
- Slog.d(TAG, "Disconnected from power.");
- }
- mCharging = false;
- handleNewChargingStateLocked();
- }
- }
- }
- }
-
@VisibleForTesting
static final class TimingSession {
// Start timestamp in elapsed realtime timebase.
@@ -2127,6 +2077,12 @@ public final class QuotaController extends StateController {
final long topAppGracePeriodEndElapsed = mTopAppGraceCache.get(mUid);
final boolean hasTopAppExemption = !mRegularJobTimer
&& (mTopAppCache.get(mUid) || nowElapsed < topAppGracePeriodEndElapsed);
+ if (DEBUG) {
+ Slog.d(TAG, "quotaFree=" + isQuotaFreeLocked(standbyBucket)
+ + " isFG=" + mForegroundUids.get(mUid)
+ + " tempEx=" + hasTempAllowlistExemption
+ + " topEx=" + hasTopAppExemption);
+ }
return !isQuotaFreeLocked(standbyBucket)
&& !mForegroundUids.get(mUid) && !hasTempAllowlistExemption
&& !hasTopAppExemption;
@@ -3938,7 +3894,6 @@ public final class QuotaController extends StateController {
public void dumpControllerStateLocked(final IndentingPrintWriter pw,
final Predicate<JobStatus> predicate) {
pw.println("Is enabled: " + mIsEnabled);
- pw.println("Is charging: " + mChargeTracker.isChargingLocked());
pw.println("Current elapsed time: " + sElapsedRealtimeClock.millis());
pw.println();
@@ -4116,7 +4071,7 @@ public final class QuotaController extends StateController {
final long mToken = proto.start(StateControllerProto.QUOTA);
proto.write(StateControllerProto.QuotaController.IS_CHARGING,
- mChargeTracker.isChargingLocked());
+ mService.isBatteryCharging());
proto.write(StateControllerProto.QuotaController.ELAPSED_REALTIME,
sElapsedRealtimeClock.millis());
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java
index 5e73f42ff1cb..509fb6963a3c 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java
@@ -133,6 +133,13 @@ public abstract class StateController {
}
/**
+ * Called when the battery status changes.
+ */
+ @GuardedBy("mLock")
+ public void onBatteryStateChangedLocked() {
+ }
+
+ /**
* Called when a UID's base bias has changed. The more positive the bias, the more
* important the UID is.
*/
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
index 0c3e472b46a8..bdeb2b4fd839 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -122,15 +122,14 @@ public class JobSchedulerServiceTest {
.when(() -> LocalServices.getService(ActivityManagerInternal.class));
doReturn(mock(AppStandbyInternal.class))
.when(() -> LocalServices.getService(AppStandbyInternal.class));
+ doReturn(mock(BatteryManagerInternal.class))
+ .when(() -> LocalServices.getService(BatteryManagerInternal.class));
doReturn(mock(UsageStatsManagerInternal.class))
.when(() -> LocalServices.getService(UsageStatsManagerInternal.class));
when(mContext.getString(anyInt())).thenReturn("some_test_string");
// Called in BackgroundJobsController constructor.
doReturn(mock(AppStateTrackerImpl.class))
.when(() -> LocalServices.getService(AppStateTracker.class));
- // Called in BatteryController constructor.
- doReturn(mock(BatteryManagerInternal.class))
- .when(() -> LocalServices.getService(BatteryManagerInternal.class));
// Called in ConnectivityController constructor.
when(mContext.getSystemService(ConnectivityManager.class))
.thenReturn(mock(ConnectivityManager.class));
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
index a9853bf94eeb..f61d6ca750cd 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
@@ -48,18 +48,14 @@ import static org.mockito.Mockito.verify;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.job.JobInfo;
-import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
import android.content.pm.PackageManagerInternal;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkPolicyManager;
-import android.os.BatteryManager;
-import android.os.BatteryManagerInternal;
import android.os.Build;
import android.os.Looper;
import android.os.SystemClock;
@@ -74,7 +70,6 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
-import org.mockito.ArgumentMatchers;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
@@ -88,8 +83,6 @@ public class ConnectivityControllerTest {
@Mock
private Context mContext;
@Mock
- private BatteryManagerInternal mBatteryManagerInternal;
- @Mock
private ConnectivityManager mConnManager;
@Mock
private NetworkPolicyManager mNetPolicyManager;
@@ -115,9 +108,6 @@ public class ConnectivityControllerTest {
LocalServices.removeServiceForTest(NetworkPolicyManagerInternal.class);
LocalServices.addService(NetworkPolicyManagerInternal.class, mNetPolicyManagerInternal);
- LocalServices.removeServiceForTest(BatteryManagerInternal.class);
- LocalServices.addService(BatteryManagerInternal.class, mBatteryManagerInternal);
-
when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
// Freeze the clocks at this moment in time
@@ -158,18 +148,10 @@ public class ConnectivityControllerTest {
.setMinimumNetworkChunkBytes(DataUnit.KIBIBYTES.toBytes(100))
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
- final ArgumentCaptor<BroadcastReceiver> chargingCaptor =
- ArgumentCaptor.forClass(BroadcastReceiver.class);
- when(mBatteryManagerInternal.isPowered(eq(BatteryManager.BATTERY_PLUGGED_ANY)))
- .thenReturn(false);
+ when(mService.isBatteryCharging()).thenReturn(false);
final ConnectivityController controller = new ConnectivityController(mService);
- verify(mContext).registerReceiver(chargingCaptor.capture(),
- ArgumentMatchers.argThat(filter ->
- filter.hasAction(BatteryManager.ACTION_CHARGING)
- && filter.hasAction(BatteryManager.ACTION_DISCHARGING)));
when(mService.getMaxJobExecutionTimeMs(any())).thenReturn(10 * 60_000L);
- final BroadcastReceiver chargingReceiver = chargingCaptor.getValue();
- chargingReceiver.onReceive(mContext, new Intent(BatteryManager.ACTION_DISCHARGING));
+ controller.onBatteryStateChangedLocked();
// Slow network is too slow
assertFalse(controller.isSatisfied(createJobStatus(job), net,
@@ -225,17 +207,15 @@ public class ConnectivityControllerTest {
createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(130)
.setLinkDownstreamBandwidthKbps(130).build(), mConstants));
// Slow network is too slow, but device is charging and network is unmetered.
- when(mBatteryManagerInternal.isPowered(eq(BatteryManager.BATTERY_PLUGGED_ANY)))
- .thenReturn(true);
- chargingReceiver.onReceive(mContext, new Intent(BatteryManager.ACTION_CHARGING));
+ when(mService.isBatteryCharging()).thenReturn(true);
+ controller.onBatteryStateChangedLocked();
assertTrue(controller.isSatisfied(createJobStatus(job), net,
createCapabilitiesBuilder().addCapability(NET_CAPABILITY_NOT_METERED)
.setLinkUpstreamBandwidthKbps(1).setLinkDownstreamBandwidthKbps(1).build(),
mConstants));
- when(mBatteryManagerInternal.isPowered(eq(BatteryManager.BATTERY_PLUGGED_ANY)))
- .thenReturn(false);
- chargingReceiver.onReceive(mContext, new Intent(BatteryManager.ACTION_DISCHARGING));
+ when(mService.isBatteryCharging()).thenReturn(false);
+ controller.onBatteryStateChangedLocked();
when(mService.getMaxJobExecutionTimeMs(any())).thenReturn(60_000L);
// Slow network is too slow
@@ -259,9 +239,8 @@ public class ConnectivityControllerTest {
createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(130)
.setLinkDownstreamBandwidthKbps(130).build(), mConstants));
// Slow network is too slow, but device is charging and network is unmetered.
- when(mBatteryManagerInternal.isPowered(eq(BatteryManager.BATTERY_PLUGGED_ANY)))
- .thenReturn(true);
- chargingReceiver.onReceive(mContext, new Intent(BatteryManager.ACTION_CHARGING));
+ when(mService.isBatteryCharging()).thenReturn(true);
+ controller.onBatteryStateChangedLocked();
assertTrue(controller.isSatisfied(createJobStatus(job), net,
createCapabilitiesBuilder().addCapability(NET_CAPABILITY_NOT_METERED)
.setLinkUpstreamBandwidthKbps(1).setLinkDownstreamBandwidthKbps(1).build(),
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index 300f93feed28..cfae9a3d586a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -63,16 +63,13 @@ import android.app.job.JobInfo;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManager;
import android.app.usage.UsageStatsManagerInternal;
-import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ServiceInfo;
-import android.os.BatteryManager;
import android.os.BatteryManagerInternal;
import android.os.Handler;
import android.os.Looper;
@@ -126,7 +123,6 @@ public class QuotaControllerTest {
private static final String SOURCE_PACKAGE = "com.android.frameworks.mockingservicestests";
private static final int SOURCE_USER_ID = 0;
- private BroadcastReceiver mChargingReceiver;
private QuotaController mQuotaController;
private QuotaController.QcConstants mQcConstants;
private JobSchedulerService.Constants mConstants = new JobSchedulerService.Constants();
@@ -225,8 +221,6 @@ public class QuotaControllerTest {
// Initialize real objects.
// Capture the listeners.
- ArgumentCaptor<BroadcastReceiver> receiverCaptor =
- ArgumentCaptor.forClass(BroadcastReceiver.class);
ArgumentCaptor<IUidObserver> uidObserverCaptor =
ArgumentCaptor.forClass(IUidObserver.class);
ArgumentCaptor<PowerAllowlistInternal.TempAllowlistChangeListener> taChangeCaptor =
@@ -236,11 +230,6 @@ public class QuotaControllerTest {
mQuotaController = new QuotaController(mJobSchedulerService,
mock(BackgroundJobsController.class), mock(ConnectivityController.class));
- verify(mContext).registerReceiver(receiverCaptor.capture(),
- ArgumentMatchers.argThat(filter ->
- filter.hasAction(BatteryManager.ACTION_CHARGING)
- && filter.hasAction(BatteryManager.ACTION_DISCHARGING)));
- mChargingReceiver = receiverCaptor.getValue();
verify(mPowerAllowlistInternal)
.registerTempAllowlistChangeListener(taChangeCaptor.capture());
mTempAllowlistListener = taChangeCaptor.getValue();
@@ -280,13 +269,17 @@ public class QuotaControllerTest {
}
private void setCharging() {
- Intent intent = new Intent(BatteryManager.ACTION_CHARGING);
- mChargingReceiver.onReceive(mContext, intent);
+ doReturn(true).when(mJobSchedulerService).isBatteryCharging();
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.onBatteryStateChangedLocked();
+ }
}
private void setDischarging() {
- Intent intent = new Intent(BatteryManager.ACTION_DISCHARGING);
- mChargingReceiver.onReceive(mContext, intent);
+ doReturn(false).when(mJobSchedulerService).isBatteryCharging();
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.onBatteryStateChangedLocked();
+ }
}
private void setProcessState(int procState) {