Merge "Implement force-all-apps-standly in job scheduler."
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index dd4825e..0fce7a4 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -527,8 +527,7 @@
ServiceType.SOUND,
ServiceType.BATTERY_STATS,
ServiceType.DATA_SAVER,
- ServiceType.FORCE_ALL_APPS_STANDBY_JOBS,
- ServiceType.FORCE_ALL_APPS_STANDBY_ALARMS,
+ ServiceType.FORCE_ALL_APPS_STANDBY,
ServiceType.OPTIONAL_SENSORS,
})
public @interface ServiceType {
@@ -545,14 +544,9 @@
int DATA_SAVER = 10;
/**
- * Whether the job scheduler should force app standby on all apps on battery saver or not.
+ * Whether to enable force-app-standby on all apps or not.
*/
- int FORCE_ALL_APPS_STANDBY_JOBS = 11;
-
- /**
- * Whether the alarm manager should force app standby on all apps on battery saver or not.
- */
- int FORCE_ALL_APPS_STANDBY_ALARMS = 12;
+ int FORCE_ALL_APPS_STANDBY = 11;
/**
* Whether to disable non-essential sensors. (e.g. edge sensors.)
diff --git a/services/core/java/com/android/server/ForceAppStandbyTracker.java b/services/core/java/com/android/server/ForceAppStandbyTracker.java
new file mode 100644
index 0000000..5dd3ee0
--- /dev/null
+++ b/services/core/java/com/android/server/ForceAppStandbyTracker.java
@@ -0,0 +1,386 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.AppOpsManager;
+import android.app.AppOpsManager.PackageOps;
+import android.app.IUidObserver;
+import android.content.Context;
+import android.os.Handler;
+import android.os.PowerManager.ServiceType;
+import android.os.PowerManagerInternal;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.util.ArraySet;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.SparseBooleanArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.app.IAppOpsCallback;
+import com.android.internal.app.IAppOpsService;
+import com.android.internal.util.Preconditions;
+
+import java.util.List;
+
+/**
+ * Class to track OP_RUN_ANY_IN_BACKGROUND, UID foreground state and "force all app standby".
+ *
+ * TODO Clean up cache when a user is deleted.
+ * TODO Add unit tests. b/68769804.
+ */
+public class ForceAppStandbyTracker {
+ private static final String TAG = "ForceAppStandbyTracker";
+
+ @GuardedBy("ForceAppStandbyTracker.class")
+ private static ForceAppStandbyTracker sInstance;
+
+ private final Object mLock = new Object();
+ private final Context mContext;
+
+ AppOpsManager mAppOpsManager;
+ IAppOpsService mAppOpsService;
+ PowerManagerInternal mPowerManagerInternal;
+
+ private final Handler mCallbackHandler;
+
+ /**
+ * Pair of (uid (not user-id), packageName) with OP_RUN_ANY_IN_BACKGROUND *not* allowed.
+ */
+ @GuardedBy("mLock")
+ final ArraySet<Pair<Integer, String>> mForcedAppStandbyUidPackages = new ArraySet<>();
+
+ @GuardedBy("mLock")
+ final SparseBooleanArray mForegroundUids = new SparseBooleanArray();
+
+ @GuardedBy("mLock")
+ final ArraySet<Listener> mListeners = new ArraySet<>();
+
+ @GuardedBy("mLock")
+ boolean mStarted;
+
+ @GuardedBy("mLock")
+ boolean mForceAllAppsStandby;
+
+ public static abstract class Listener {
+ public void onRestrictionChanged(int uid, @Nullable String packageName) {
+ }
+
+ public void onGlobalRestrictionChanged() {
+ }
+ }
+
+ private ForceAppStandbyTracker(Context context) {
+ mContext = context;
+ mCallbackHandler = FgThread.getHandler();
+ }
+
+ /**
+ * Get the singleton instance.
+ */
+ public static synchronized ForceAppStandbyTracker getInstance(Context context) {
+ if (sInstance == null) {
+ sInstance = new ForceAppStandbyTracker(context);
+ }
+ return sInstance;
+ }
+
+ /**
+ * Call it when the system is ready.
+ */
+ public void start() {
+ synchronized (mLock) {
+ if (mStarted) {
+ return;
+ }
+ mStarted = true;
+
+ mAppOpsManager = Preconditions.checkNotNull(
+ mContext.getSystemService(AppOpsManager.class));
+ mAppOpsService = Preconditions.checkNotNull(
+ IAppOpsService.Stub.asInterface(
+ ServiceManager.getService(Context.APP_OPS_SERVICE)));
+ mPowerManagerInternal = Preconditions.checkNotNull(
+ LocalServices.getService(PowerManagerInternal.class));
+
+ try {
+ ActivityManager.getService().registerUidObserver(new UidObserver(),
+ ActivityManager.UID_OBSERVER_GONE | ActivityManager.UID_OBSERVER_IDLE
+ | ActivityManager.UID_OBSERVER_ACTIVE,
+ ActivityManager.PROCESS_STATE_UNKNOWN, null);
+ mAppOpsService.startWatchingMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, null,
+ new AppOpsWatcher());
+ } catch (RemoteException e) {
+ // shouldn't happen.
+ }
+
+ mPowerManagerInternal.registerLowPowerModeObserver(
+ ServiceType.FORCE_ALL_APPS_STANDBY,
+ state -> updateForceAllAppsStandby(state.batterySaverEnabled));
+
+ updateForceAllAppsStandby(
+ mPowerManagerInternal.getLowPowerState(ServiceType.FORCE_ALL_APPS_STANDBY)
+ .batterySaverEnabled);
+
+ refreshForcedAppStandbyUidPackagesLocked();
+ }
+ }
+
+ /**
+ * Update {@link #mForcedAppStandbyUidPackages} with the current app ops state.
+ */
+ private void refreshForcedAppStandbyUidPackagesLocked() {
+ final int op = AppOpsManager.OP_RUN_ANY_IN_BACKGROUND;
+
+ mForcedAppStandbyUidPackages.clear();
+ final List<PackageOps> ops = mAppOpsManager.getPackagesForOps(new int[] {op});
+
+ if (ops == null) {
+ return;
+ }
+ final int size = ops.size();
+ for (int i = 0; i < size; i++) {
+ final AppOpsManager.PackageOps pkg = ops.get(i);
+ final List<AppOpsManager.OpEntry> entries = ops.get(i).getOps();
+
+ for (int j = 0; j < entries.size(); j++) {
+ AppOpsManager.OpEntry ent = entries.get(j);
+ if (ent.getOp() != op) {
+ continue;
+ }
+ if (ent.getMode() != AppOpsManager.MODE_ALLOWED) {
+ mForcedAppStandbyUidPackages.add(Pair.create(
+ pkg.getUid(), pkg.getPackageName()));
+ }
+ }
+ }
+ }
+
+ boolean isRunAnyInBackgroundAppOpRestricted(int uid, @NonNull String packageName) {
+ try {
+ return mAppOpsService.checkOperation(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND,
+ uid, packageName) != AppOpsManager.MODE_ALLOWED;
+ } catch (RemoteException e) {
+ return false; // shouldn't happen.
+ }
+ }
+
+ private int findForcedAppStandbyUidPackageIndexLocked(int uid, @NonNull String packageName) {
+ // TODO Maybe we should switch to indexOf(Pair.create()) if the array size is too big.
+ final int size = mForcedAppStandbyUidPackages.size();
+ for (int i = 0; i < size; i++) {
+ final Pair<Integer, String> pair = mForcedAppStandbyUidPackages.valueAt(i);
+
+ if ((pair.first == uid) && packageName.equals(pair.second)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * @return whether a uid package-name pair is in mForcedAppStandbyUidPackages.
+ */
+ boolean isUidPackageRestrictedLocked(int uid, @NonNull String packageName) {
+ return findForcedAppStandbyUidPackageIndexLocked(uid, packageName) >= 0;
+ }
+
+ boolean updateRestrictedUidPackageLocked(int uid, @NonNull String packageName,
+ boolean restricted) {
+ final int index = findForcedAppStandbyUidPackageIndexLocked(uid, packageName);
+ final boolean wasRestricted = index >= 0;
+ if (wasRestricted == restricted) {
+ return false;
+ }
+ if (restricted) {
+ mForcedAppStandbyUidPackages.add(Pair.create(uid, packageName));
+ } else {
+ mForcedAppStandbyUidPackages.removeAt(index);
+ }
+ return true;
+ }
+
+ void uidToForeground(int uid) {
+ synchronized (mLock) {
+ if (!UserHandle.isApp(uid)) {
+ return;
+ }
+ // TODO This can be optimized by calling indexOfKey and sharing the index for get and
+ // put.
+ if (mForegroundUids.get(uid)) {
+ return;
+ }
+ mForegroundUids.put(uid, true);
+ notifyForUidPackage(uid, null);
+ }
+ }
+
+ void uidToBackground(int uid, boolean remove) {
+ synchronized (mLock) {
+ if (!UserHandle.isApp(uid)) {
+ return;
+ }
+ // TODO This can be optimized by calling indexOfKey and sharing the index for get and
+ // put.
+ if (!mForegroundUids.get(uid)) {
+ return;
+ }
+ if (remove) {
+ mForegroundUids.delete(uid);
+ } else {
+ mForegroundUids.put(uid, false);
+ }
+ notifyForUidPackage(uid, null);
+ }
+ }
+
+ // Event handlers
+
+ final class UidObserver extends IUidObserver.Stub {
+ @Override public void onUidStateChanged(int uid, int procState, long procStateSeq) {
+ }
+
+ @Override public void onUidGone(int uid, boolean disabled) {
+ uidToBackground(uid, /*remove=*/ true);
+ }
+
+ @Override public void onUidActive(int uid) {
+ uidToForeground(uid);
+ }
+
+ @Override public void onUidIdle(int uid, boolean disabled) {
+ // Just to avoid excessive memcpy, don't remove from the array in this case.
+ uidToBackground(uid, /*remove=*/ false);
+ }
+
+ @Override public void onUidCachedChanged(int uid, boolean cached) {
+ }
+ };
+
+ private final class AppOpsWatcher extends IAppOpsCallback.Stub {
+ @Override
+ public void opChanged(int op, int uid, String packageName) throws RemoteException {
+ synchronized (mLock) {
+ final boolean restricted = isRunAnyInBackgroundAppOpRestricted(uid, packageName);
+
+ if (updateRestrictedUidPackageLocked(uid, packageName, restricted)) {
+ notifyForUidPackage(uid, packageName);
+ }
+ }
+ }
+ }
+
+ private Listener[] cloneListeners() {
+ synchronized (mLock) {
+ return mListeners.toArray(new Listener[mListeners.size()]);
+ }
+ }
+
+ void notifyForUidPackage(int uid, String packageName) {
+ mCallbackHandler.post(() -> {
+ for (Listener l : cloneListeners()) {
+ l.onRestrictionChanged(uid, packageName);
+ }
+ });
+ }
+
+ void notifyGlobal() {
+ mCallbackHandler.post(() -> {
+ for (Listener l : cloneListeners()) {
+ l.onGlobalRestrictionChanged();
+ }
+ });
+ }
+
+ void updateForceAllAppsStandby(boolean forceAllAppsStandby) {
+ synchronized (mLock) {
+ if (mForceAllAppsStandby == forceAllAppsStandby) {
+ return;
+ }
+ mForceAllAppsStandby = forceAllAppsStandby;
+ Slog.i(TAG, "Force all app standby: " + mForceAllAppsStandby);
+ notifyGlobal();
+ }
+ }
+
+ // Public interface.
+
+ /**
+ * Register a new listener.
+ */
+ public void addListener(@NonNull Listener listener) {
+ synchronized (mLock) {
+ mListeners.add(listener);
+ }
+ }
+
+ /**
+ * Whether force-app-standby is effective for a UID package-name.
+ */
+ public boolean isRestricted(int uid, @NonNull String packageName) {
+ if (isInForeground(uid)) {
+ return false;
+ }
+ synchronized (mLock) {
+ if (mForceAllAppsStandby) {
+ return true;
+ }
+ return isUidPackageRestrictedLocked(uid, packageName);
+ }
+ }
+
+ /** For dumpsys -- otherwise the callers don't need to know it. */
+ public boolean isInForeground(int uid) {
+ if (!UserHandle.isApp(uid)) {
+ return true;
+ }
+ synchronized (mLock) {
+ return mForegroundUids.get(uid);
+ }
+ }
+
+ /** For dumpsys -- otherwise the callers don't need to know it. */
+ public boolean isForceAllAppsStandbyEnabled() {
+ synchronized (mLock) {
+ return mForceAllAppsStandby;
+ }
+ }
+
+ /** For dumpsys -- otherwise the callers don't need to know it. */
+ public boolean isRunAnyInBackgroundAppOpsAllowed(int uid, @NonNull String packageName) {
+ synchronized (mLock) {
+ return !isUidPackageRestrictedLocked(uid, packageName);
+ }
+ }
+
+ /** For dumpsys -- otherwise the callers don't need to know it. */
+ public SparseBooleanArray getForegroudUids() {
+ synchronized (mLock) {
+ return mForegroundUids.clone();
+ }
+ }
+
+ /** For dumpsys -- otherwise the callers don't need to know it. */
+ public ArraySet<Pair<Integer, String>> getRestrictedUidPackages() {
+ synchronized (mLock) {
+ return new ArraySet(mForcedAppStandbyUidPackages);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index b549768..b9777ec 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -622,24 +622,15 @@
if (disabled) {
cancelJobsForUid(uid, "uid gone");
}
- synchronized (mLock) {
- mBackgroundJobsController.setUidActiveLocked(uid, false);
- }
}
@Override public void onUidActive(int uid) throws RemoteException {
- synchronized (mLock) {
- mBackgroundJobsController.setUidActiveLocked(uid, true);
- }
}
@Override public void onUidIdle(int uid, boolean disabled) {
if (disabled) {
cancelJobsForUid(uid, "app uid idle");
}
- synchronized (mLock) {
- mBackgroundJobsController.setUidActiveLocked(uid, false);
- }
}
@Override public void onUidCachedChanged(int uid, boolean cached) {
diff --git a/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java b/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java
index 78b4160..f67bd04 100644
--- a/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java
+++ b/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java
@@ -16,7 +16,6 @@
package com.android.server.job.controllers;
-import android.app.AppOpsManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -25,19 +24,20 @@
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.SystemClock;
import android.os.UserHandle;
-import android.util.ArraySet;
+import android.util.Pair;
import android.util.Slog;
-import android.util.SparseArray;
import android.util.SparseBooleanArray;
-import com.android.internal.app.IAppOpsCallback;
-import com.android.internal.app.IAppOpsService;
import com.android.internal.util.ArrayUtils;
+import com.android.server.ForceAppStandbyTracker;
+import com.android.server.ForceAppStandbyTracker.Listener;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.JobStore;
import java.io.PrintWriter;
+import java.util.function.Predicate;
public final class BackgroundJobsController extends StateController {
@@ -49,18 +49,13 @@
private static volatile BackgroundJobsController sController;
private final JobSchedulerService mJobSchedulerService;
- private final IAppOpsService mAppOpsService;
private final IDeviceIdleController mDeviceIdleController;
- private final SparseBooleanArray mForegroundUids;
private int[] mPowerWhitelistedUserAppIds;
private int[] mTempWhitelistedAppIds;
- /**
- * Only tracks jobs for which source package app op RUN_ANY_IN_BACKGROUND is not ALLOWED.
- * Maps jobs to the sourceUid unlike the global {@link JobSchedulerService#mJobs JobStore}
- * which uses callingUid.
- */
- private SparseArray<ArraySet<JobStatus>> mTrackedJobs;
+
+ private final ForceAppStandbyTracker mForceAppStandbyTracker;
+
public static BackgroundJobsController get(JobSchedulerService service) {
synchronized (sCreationLock) {
@@ -89,9 +84,7 @@
} catch (RemoteException rexc) {
Slog.e(LOG_TAG, "Device idle controller not reachable");
}
- if (checkAllTrackedJobsLocked()) {
- mStateChangedListener.onControllerStateChanged();
- }
+ updateAllJobRestrictionsLocked();
}
}
};
@@ -99,16 +92,12 @@
private BackgroundJobsController(JobSchedulerService service, Context context, Object lock) {
super(service, context, lock);
mJobSchedulerService = service;
- mAppOpsService = IAppOpsService.Stub.asInterface(
- ServiceManager.getService(Context.APP_OPS_SERVICE));
mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
- mForegroundUids = new SparseBooleanArray();
- mTrackedJobs = new SparseArray<>();
+ mForceAppStandbyTracker = ForceAppStandbyTracker.getInstance(context);
+
try {
- mAppOpsService.startWatchingMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, null,
- new AppOpsWatcher());
mPowerWhitelistedUserAppIds = mDeviceIdleController.getAppIdUserWhitelist();
mTempWhitelistedAppIds = mDeviceIdleController.getAppIdTempWhitelist();
} catch (RemoteException rexc) {
@@ -120,184 +109,165 @@
powerWhitelistFilter.addAction(PowerManager.ACTION_POWER_SAVE_TEMP_WHITELIST_CHANGED);
context.registerReceiverAsUser(mDozeWhitelistReceiver, UserHandle.ALL, powerWhitelistFilter,
null, null);
+
+ mForceAppStandbyTracker.addListener(mForceAppStandbyListener);
+ mForceAppStandbyTracker.start();
}
@Override
public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
- final int uid = jobStatus.getSourceUid();
- final String packageName = jobStatus.getSourcePackageName();
- try {
- final int mode = mAppOpsService.checkOperation(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND,
- uid, packageName);
- if (mode == AppOpsManager.MODE_ALLOWED) {
- jobStatus.setBackgroundNotRestrictedConstraintSatisfied(true);
- return;
- }
- } catch (RemoteException rexc) {
- Slog.e(LOG_TAG, "Cannot reach app ops service", rexc);
- }
- jobStatus.setBackgroundNotRestrictedConstraintSatisfied(canRunJobLocked(uid));
- startTrackingJobLocked(jobStatus);
+ updateSingleJobRestrictionLocked(jobStatus);
}
@Override
public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
boolean forUpdate) {
- stopTrackingJobLocked(jobStatus);
- }
-
- /* Called by JobSchedulerService to report uid state changes between active and idle */
- public void setUidActiveLocked(int uid, boolean active) {
- final boolean changed = (active != mForegroundUids.get(uid));
- if (!changed) {
- return;
- }
- if (DEBUG) {
- Slog.d(LOG_TAG, "uid " + uid + " going to " + (active ? "fg" : "bg"));
- }
- if (active) {
- mForegroundUids.put(uid, true);
- } else {
- mForegroundUids.delete(uid);
- }
- if (checkTrackedJobsForUidLocked(uid)) {
- mStateChangedListener.onControllerStateChanged();
- }
}
@Override
public void dumpControllerStateLocked(final PrintWriter pw, final int filterUid) {
pw.println("BackgroundJobsController");
+
+ pw.print("Force all apps standby: ");
+ pw.println(mForceAppStandbyTracker.isForceAllAppsStandbyEnabled());
+
pw.print("Foreground uids: [");
- for (int i = 0; i < mForegroundUids.size(); i++) {
- if (mForegroundUids.valueAt(i)) pw.print(mForegroundUids.keyAt(i) + " ");
+ final SparseBooleanArray foregroundUids = mForceAppStandbyTracker.getForegroudUids();
+
+ String sep = "";
+ for (int i = 0; i < foregroundUids.size(); i++) {
+ if (foregroundUids.valueAt(i)) {
+ pw.print(sep);
+ pw.print(UserHandle.formatUid(foregroundUids.keyAt(i)));
+ sep = " ";
+ }
}
pw.println("]");
- mJobSchedulerService.getJobStore().forEachJob(new JobStore.JobStatusFunctor() {
- @Override
- public void process(JobStatus jobStatus) {
- if (!jobStatus.shouldDump(filterUid)) {
- return;
- }
- final int uid = jobStatus.getSourceUid();
- pw.print(" #");
- jobStatus.printUniqueId(pw);
- pw.print(" from ");
- UserHandle.formatUid(pw, uid);
- pw.print(mForegroundUids.get(uid) ? " foreground" : " background");
- if (isWhitelistedLocked(uid)) {
- pw.print(", whitelisted");
- }
- pw.print(": ");
- pw.print(jobStatus.getSourcePackageName());
- pw.print(" [background restrictions");
- final ArraySet<JobStatus> jobsForUid = mTrackedJobs.get(uid);
- pw.print(jobsForUid != null && jobsForUid.contains(jobStatus) ? " on]" : " off]");
- if ((jobStatus.satisfiedConstraints
- & JobStatus.CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0) {
- pw.println(" RUNNABLE");
- } else {
- pw.println(" WAITING");
- }
+
+ pw.println("Restricted packages:");
+ for (Pair<Integer, String> uidAndPackage
+ : mForceAppStandbyTracker.getRestrictedUidPackages()) {
+ pw.print(" ");
+ pw.print(UserHandle.formatUid(uidAndPackage.first));
+ pw.print(" ");
+ pw.print(uidAndPackage.second);
+ pw.println();
+ }
+
+ pw.println("Job state:");
+ mJobSchedulerService.getJobStore().forEachJob((jobStatus) -> {
+ if (!jobStatus.shouldDump(filterUid)) {
+ return;
+ }
+ final int uid = jobStatus.getSourceUid();
+ pw.print(" #");
+ jobStatus.printUniqueId(pw);
+ pw.print(" from ");
+ UserHandle.formatUid(pw, uid);
+ pw.print(mForceAppStandbyTracker.isInForeground(uid) ? " foreground" : " background");
+ if (isWhitelistedLocked(uid)) {
+ pw.print(", whitelisted");
+ }
+ pw.print(": ");
+ pw.print(jobStatus.getSourcePackageName());
+
+ pw.print(" [background restrictions ");
+ pw.print(mForceAppStandbyTracker.isRunAnyInBackgroundAppOpsAllowed(
+ jobStatus.getSourceUid(), jobStatus.getSourcePackageName()) ? "off]" : "on]");
+
+ if ((jobStatus.satisfiedConstraints
+ & JobStatus.CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0) {
+ pw.println(" RUNNABLE");
+ } else {
+ pw.println(" WAITING");
}
});
}
- void startTrackingJobLocked(JobStatus jobStatus) {
+ private void updateAllJobRestrictionsLocked() {
+ updateJobRestrictionsLocked(/*filterUid=*/ -1);
+ }
+
+ private void updateJobRestrictionsForUidLocked(int uid) {
+
+ // TODO Use forEachJobForSourceUid() once we have it.
+
+ updateJobRestrictionsLocked(/*filterUid=*/ uid);
+ }
+
+ private void updateJobRestrictionsLocked(int filterUid) {
+ final UpdateJobFunctor updateTrackedJobs =
+ new UpdateJobFunctor(filterUid);
+
+ final long start = DEBUG ? SystemClock.elapsedRealtimeNanos() : 0;
+
+ mJobSchedulerService.getJobStore().forEachJob(updateTrackedJobs);
+
+ final long time = DEBUG ? (SystemClock.elapsedRealtimeNanos() - start) : 0;
+ if (DEBUG) {
+ Slog.d(LOG_TAG, String.format(
+ "Job status updated: %d/%d checked/total jobs, %d us",
+ updateTrackedJobs.mCheckedCount,
+ updateTrackedJobs.mTotalCount,
+ (time / 1000)
+ ));
+ }
+
+ if (updateTrackedJobs.mChanged) {
+ mStateChangedListener.onControllerStateChanged();
+ }
+ }
+
+ private boolean isWhitelistedLocked(int uid) {
+ final int appId = UserHandle.getAppId(uid);
+ return ArrayUtils.contains(mTempWhitelistedAppIds, appId)
+ || ArrayUtils.contains(mPowerWhitelistedUserAppIds, appId);
+ }
+
+ boolean updateSingleJobRestrictionLocked(JobStatus jobStatus) {
+
final int uid = jobStatus.getSourceUid();
- ArraySet<JobStatus> jobsForUid = mTrackedJobs.get(uid);
- if (jobsForUid == null) {
- jobsForUid = new ArraySet<>();
- mTrackedJobs.put(uid, jobsForUid);
- }
- jobsForUid.add(jobStatus);
+ final String packageName = jobStatus.getSourcePackageName();
+
+ final boolean canRun = isWhitelistedLocked(uid)
+ || !mForceAppStandbyTracker.isRestricted(uid, packageName);
+
+ return jobStatus.setBackgroundNotRestrictedConstraintSatisfied(canRun);
}
- void stopTrackingJobLocked(JobStatus jobStatus) {
- final int uid = jobStatus.getSourceUid();
- ArraySet<JobStatus> jobsForUid = mTrackedJobs.get(uid);
- if (jobsForUid != null) {
- jobsForUid.remove(jobStatus);
- }
- }
+ private final class UpdateJobFunctor implements JobStore.JobStatusFunctor {
+ private final int mFilterUid;
- boolean checkAllTrackedJobsLocked() {
- boolean changed = false;
- for (int i = 0; i < mTrackedJobs.size(); i++) {
- changed |= checkTrackedJobsForUidLocked(mTrackedJobs.keyAt(i));
- }
- return changed;
- }
+ boolean mChanged = false;
+ int mTotalCount = 0;
+ int mCheckedCount = 0;
- private boolean checkTrackedJobsForUidLocked(int uid) {
- final ArraySet<JobStatus> jobsForUid = mTrackedJobs.get(uid);
- boolean changed = false;
- if (jobsForUid != null) {
- for (int i = 0; i < jobsForUid.size(); i++) {
- JobStatus jobStatus = jobsForUid.valueAt(i);
- changed |= jobStatus.setBackgroundNotRestrictedConstraintSatisfied(
- canRunJobLocked(uid));
- }
- }
- return changed;
- }
-
- boolean isWhitelistedLocked(int uid) {
- return ArrayUtils.contains(mTempWhitelistedAppIds, UserHandle.getAppId(uid))
- || ArrayUtils.contains(mPowerWhitelistedUserAppIds, UserHandle.getAppId(uid));
- }
-
- boolean canRunJobLocked(int uid) {
- return mForegroundUids.get(uid) || isWhitelistedLocked(uid);
- }
-
- private final class AppOpsWatcher extends IAppOpsCallback.Stub {
- @Override
- public void opChanged(int op, int uid, String packageName) throws RemoteException {
- synchronized (mLock) {
- final int mode = mAppOpsService.checkOperation(op, uid, packageName);
- if (DEBUG) {
- Slog.d(LOG_TAG,
- "Appop changed for " + uid + ", " + packageName + " to " + mode);
- }
- final boolean shouldTrack = (mode != AppOpsManager.MODE_ALLOWED);
- UpdateTrackedJobsFunc updateTrackedJobs = new UpdateTrackedJobsFunc(uid,
- packageName, shouldTrack);
- mJobSchedulerService.getJobStore().forEachJob(updateTrackedJobs);
- if (updateTrackedJobs.mChanged) {
- mStateChangedListener.onControllerStateChanged();
- }
- }
- }
- }
-
- private final class UpdateTrackedJobsFunc implements JobStore.JobStatusFunctor {
- private final String mPackageName;
- private final int mUid;
- private final boolean mShouldTrack;
- private boolean mChanged = false;
-
- UpdateTrackedJobsFunc(int uid, String packageName, boolean shouldTrack) {
- mUid = uid;
- mPackageName = packageName;
- mShouldTrack = shouldTrack;
+ UpdateJobFunctor(int filterUid) {
+ mFilterUid = filterUid;
}
@Override
public void process(JobStatus jobStatus) {
- final String packageName = jobStatus.getSourcePackageName();
- final int uid = jobStatus.getSourceUid();
- if (mUid != uid || !mPackageName.equals(packageName)) {
+ mTotalCount++;
+ if ((mFilterUid > 0) && (mFilterUid != jobStatus.getSourceUid())) {
return;
}
- if (mShouldTrack) {
- mChanged |= jobStatus.setBackgroundNotRestrictedConstraintSatisfied(
- canRunJobLocked(uid));
- startTrackingJobLocked(jobStatus);
- } else {
- mChanged |= jobStatus.setBackgroundNotRestrictedConstraintSatisfied(true);
- stopTrackingJobLocked(jobStatus);
+ mCheckedCount++;
+ if (updateSingleJobRestrictionLocked(jobStatus)) {
+ mChanged = true;
}
}
}
+
+ private final Listener mForceAppStandbyListener = new Listener() {
+ @Override
+ public void onRestrictionChanged(int uid, String packageName) {
+ updateJobRestrictionsForUidLocked(uid);
+ }
+
+ @Override
+ public void onGlobalRestrictionChanged() {
+ updateAllJobRestrictionsLocked();
+ }
+ };
}
diff --git a/services/core/java/com/android/server/power/BatterySaverPolicy.java b/services/core/java/com/android/server/power/BatterySaverPolicy.java
index 15121b8..2fceae4 100644
--- a/services/core/java/com/android/server/power/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/BatterySaverPolicy.java
@@ -29,9 +29,9 @@
import android.util.KeyValueListParser;
import android.util.Slog;
+import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.R;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -62,8 +62,7 @@
private static final String KEY_ADJUST_BRIGHTNESS_FACTOR = "adjust_brightness_factor";
private static final String KEY_FULLBACKUP_DEFERRED = "fullbackup_deferred";
private static final String KEY_KEYVALUE_DEFERRED = "keyvaluebackup_deferred";
- private static final String KEY_FORCE_ALL_APPS_STANDBY_JOBS = "force_all_apps_standby_jobs";
- private static final String KEY_FORCE_ALL_APPS_STANDBY_ALARMS = "force_all_apps_standby_alarms";
+ private static final String KEY_FORCE_ALL_APPS_STANDBY = "force_all_apps_standby";
private static final String KEY_OPTIONAL_SENSORS_DISABLED = "optional_sensors_disabled";
private static final String KEY_SCREEN_ON_FILE_PREFIX = "file-on:";
@@ -156,14 +155,9 @@
private float mAdjustBrightnessFactor;
/**
- * Whether to put all apps in the stand-by mode or not for job scheduler.
+ * Whether to put all apps in the stand-by mode.
*/
- private boolean mForceAllAppsStandbyJobs;
-
- /**
- * Whether to put all apps in the stand-by mode or not for alarms.
- */
- private boolean mForceAllAppsStandbyAlarms;
+ private boolean mForceAllAppsStandby;
/**
* Weather to show non-essential sensors (e.g. edge sensors) or not.
@@ -297,9 +291,7 @@
mAdjustBrightnessDisabled = parser.getBoolean(KEY_ADJUST_BRIGHTNESS_DISABLED, false);
mAdjustBrightnessFactor = parser.getFloat(KEY_ADJUST_BRIGHTNESS_FACTOR, 0.5f);
mDataSaverDisabled = parser.getBoolean(KEY_DATASAVER_DISABLED, true);
- mForceAllAppsStandbyJobs = parser.getBoolean(KEY_FORCE_ALL_APPS_STANDBY_JOBS, true);
- mForceAllAppsStandbyAlarms =
- parser.getBoolean(KEY_FORCE_ALL_APPS_STANDBY_ALARMS, true);
+ mForceAllAppsStandby = parser.getBoolean(KEY_FORCE_ALL_APPS_STANDBY, true);
mOptionalSensorsDisabled = parser.getBoolean(KEY_OPTIONAL_SENSORS_DISABLED, true);
// Get default value from Settings.Secure
@@ -387,12 +379,6 @@
case ServiceType.VIBRATION:
return builder.setBatterySaverEnabled(mVibrationDisabled)
.build();
- case ServiceType.FORCE_ALL_APPS_STANDBY_JOBS:
- return builder.setBatterySaverEnabled(mForceAllAppsStandbyJobs)
- .build();
- case ServiceType.FORCE_ALL_APPS_STANDBY_ALARMS:
- return builder.setBatterySaverEnabled(mForceAllAppsStandbyAlarms)
- .build();
case ServiceType.OPTIONAL_SENSORS:
return builder.setBatterySaverEnabled(mOptionalSensorsDisabled)
.build();
@@ -428,8 +414,7 @@
pw.println(" " + KEY_ADJUST_BRIGHTNESS_DISABLED + "=" + mAdjustBrightnessDisabled);
pw.println(" " + KEY_ADJUST_BRIGHTNESS_FACTOR + "=" + mAdjustBrightnessFactor);
pw.println(" " + KEY_GPS_MODE + "=" + mGpsMode);
- pw.println(" " + KEY_FORCE_ALL_APPS_STANDBY_JOBS + "=" + mForceAllAppsStandbyJobs);
- pw.println(" " + KEY_FORCE_ALL_APPS_STANDBY_ALARMS + "=" + mForceAllAppsStandbyAlarms);
+ pw.println(" " + KEY_FORCE_ALL_APPS_STANDBY + "=" + mForceAllAppsStandby);
pw.println(" " + KEY_OPTIONAL_SENSORS_DISABLED + "=" + mOptionalSensorsDisabled);
pw.println();
diff --git a/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java b/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java
index afe432b..467b47a 100644
--- a/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java
@@ -37,7 +37,6 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
-import android.provider.Settings;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.LargeTest;
import android.support.test.runner.AndroidJUnit4;
@@ -57,10 +56,10 @@
* To run this test from root of checkout:
* <pre>
* mmm -j32 frameworks/base/services/tests/servicestests/
- * adb install out/target/product/marlin/data/app/JobTestApp/JobTestApp.apk
- * adb install out/target/product/marlin/data/app/FrameworksServicesTests/FrameworksServicesTests.apk
+ * adb install -r $OUT/data/app/JobTestApp/JobTestApp.apk
+ * adb install -r $OUT/data/app/FrameworksServicesTests/FrameworksServicesTests.apk
* adb shell am instrument -e class 'com.android.server.job.BackgroundRestrictionsTest' -w \
- * 'com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner'
+ com.android.frameworks.servicestests
* </pre>
*/
@RunWith(AndroidJUnit4.class)