While-idle alarm timeout & EBS
Use the long whileidle timeout for apps in the background.
Test: Manual test with a test app (I9d0c0ed4b6a0)
Test: atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
Test: atest $ANDROID_BUILD_TOP/cts/tests/tests/batterysaving/src/android/os/cts/batterysaving/BatterySaverAlarmTest.java
Bug: 72124522
Change-Id: Ibd0a46e573604f7f2d1ec51a60ea87c163233003
diff --git a/core/proto/android/server/alarmmanagerservice.proto b/core/proto/android/server/alarmmanagerservice.proto
index aa2663f..0342c9c 100644
--- a/core/proto/android/server/alarmmanagerservice.proto
+++ b/core/proto/android/server/alarmmanagerservice.proto
@@ -27,6 +27,7 @@
option java_multiple_files = true;
+// next ID: 43
message AlarmManagerServiceProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
@@ -114,7 +115,14 @@
optional int32 uid = 1;
// In the 'elapsed' timebase.
optional int64 time_ms = 2;
+
+ // Time when the next while-idle is allowed, in the 'elapsed' timebase.
+ optional int64 next_allowed_ms = 3;
}
+
+ // Whether the short or long while-idle timeout should be used for each UID.
+ repeated int32 use_allow_while_idle_short_time = 42;
+
// For each uid, this is the last time we dispatched an "allow while idle"
// alarm, used to determine the earliest we can dispatch the next such alarm.
repeated LastAllowWhileIdleDispatch last_allow_while_idle_dispatch_times = 36;
diff --git a/core/proto/android/server/forceappstandbytracker.proto b/core/proto/android/server/forceappstandbytracker.proto
index 43c869c..5296e47 100644
--- a/core/proto/android/server/forceappstandbytracker.proto
+++ b/core/proto/android/server/forceappstandbytracker.proto
@@ -24,14 +24,19 @@
option java_multiple_files = true;
// Dump from com.android.server.ForceAppStandbyTracker.
+//
+// Next ID: 12
message ForceAppStandbyTrackerProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
// Whether all apps are forced standby or not.
optional bool force_all_apps_standby = 1;
+ // UIDs currently active.
+ repeated int32 active_uids = 2;
+
// UIDs currently in the foreground.
- repeated int32 foreground_uids = 2;
+ repeated int32 foreground_uids = 11;
// App ids that are in power-save whitelist.
repeated int32 power_save_whitelist_app_ids = 3;
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 30dfee8..f49cd67 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -1840,14 +1840,6 @@
if (!blocked) {
pw.println(" none");
}
- pw.print(" mUseAllowWhileIdleShortTime: [");
- for (int i = 0; i < mUseAllowWhileIdleShortTime.size(); i++) {
- if (mUseAllowWhileIdleShortTime.valueAt(i)) {
- UserHandle.formatUid(pw, mUseAllowWhileIdleShortTime.keyAt(i));
- pw.print(" ");
- }
- }
- pw.println("]");
pw.println(" mLastAlarmDeliveredForPackage:");
for (int i = 0; i < mLastAlarmDeliveredForPackage.size(); i++) {
@@ -1913,14 +1905,32 @@
if (mLastAllowWhileIdleDispatch.size() > 0) {
pw.println(" Last allow while idle dispatch times:");
for (int i=0; i<mLastAllowWhileIdleDispatch.size(); i++) {
- pw.print(" UID ");
- UserHandle.formatUid(pw, mLastAllowWhileIdleDispatch.keyAt(i));
+ pw.print(" UID ");
+ final int uid = mLastAllowWhileIdleDispatch.keyAt(i);
+ UserHandle.formatUid(pw, uid);
pw.print(": ");
- TimeUtils.formatDuration(mLastAllowWhileIdleDispatch.valueAt(i),
- nowELAPSED, pw);
+ final long lastTime = mLastAllowWhileIdleDispatch.valueAt(i);
+ TimeUtils.formatDuration(lastTime, nowELAPSED, pw);
+
+ final long minInterval = getWhileIdleMinIntervalLocked(uid);
+ pw.print(" Next allowed:");
+ TimeUtils.formatDuration(lastTime + minInterval, nowELAPSED, pw);
+ pw.print(" (");
+ TimeUtils.formatDuration(minInterval, 0, pw);
+ pw.print(")");
+
pw.println();
}
}
+
+ pw.print(" mUseAllowWhileIdleShortTime: [");
+ for (int i = 0; i < mUseAllowWhileIdleShortTime.size(); i++) {
+ if (mUseAllowWhileIdleShortTime.valueAt(i)) {
+ UserHandle.formatUid(pw, mUseAllowWhileIdleShortTime.keyAt(i));
+ pw.print(" ");
+ }
+ }
+ pw.println("]");
pw.println();
if (mLog.dump(pw, " Recent problems", " ")) {
@@ -2181,13 +2191,23 @@
for (int i = 0; i < mLastAllowWhileIdleDispatch.size(); ++i) {
final long token = proto.start(
AlarmManagerServiceProto.LAST_ALLOW_WHILE_IDLE_DISPATCH_TIMES);
- proto.write(AlarmManagerServiceProto.LastAllowWhileIdleDispatch.UID,
- mLastAllowWhileIdleDispatch.keyAt(i));
- proto.write(AlarmManagerServiceProto.LastAllowWhileIdleDispatch.TIME_MS,
- mLastAllowWhileIdleDispatch.valueAt(i));
+ final int uid = mLastAllowWhileIdleDispatch.keyAt(i);
+ final long lastTime = mLastAllowWhileIdleDispatch.valueAt(i);
+
+ proto.write(AlarmManagerServiceProto.LastAllowWhileIdleDispatch.UID, uid);
+ proto.write(AlarmManagerServiceProto.LastAllowWhileIdleDispatch.TIME_MS, lastTime);
+ proto.write(AlarmManagerServiceProto.LastAllowWhileIdleDispatch.NEXT_ALLOWED_MS,
+ lastTime + getWhileIdleMinIntervalLocked(uid));
proto.end(token);
}
+ for (int i = 0; i < mUseAllowWhileIdleShortTime.size(); i++) {
+ if (mUseAllowWhileIdleShortTime.valueAt(i)) {
+ proto.write(AlarmManagerServiceProto.USE_ALLOW_WHILE_IDLE_SHORT_TIME,
+ mUseAllowWhileIdleShortTime.keyAt(i));
+ }
+ }
+
mLog.writeToProto(proto, AlarmManagerServiceProto.RECENT_PROBLEMS);
final FilterStats[] topFilters = new FilterStats[10];
@@ -2866,6 +2886,23 @@
private native int setKernelTime(long nativeData, long millis);
private native int setKernelTimezone(long nativeData, int minuteswest);
+ private long getWhileIdleMinIntervalLocked(int uid) {
+ final boolean dozing = mPendingIdleUntil != null;
+ final boolean ebs = mForceAppStandbyTracker.isForceAllAppsStandbyEnabled();
+ if (!dozing && !ebs) {
+ return mConstants.ALLOW_WHILE_IDLE_SHORT_TIME;
+ }
+ if (dozing) {
+ return mConstants.ALLOW_WHILE_IDLE_LONG_TIME;
+ }
+ if (mUseAllowWhileIdleShortTime.get(uid)) {
+ // if the last allow-while-idle went off while uid was fg, or the uid
+ // recently came into fg, don't block the alarm for long.
+ return mConstants.ALLOW_WHILE_IDLE_SHORT_TIME;
+ }
+ return mConstants.ALLOW_WHILE_IDLE_LONG_TIME;
+ }
+
boolean triggerAlarmsLocked(ArrayList<Alarm> triggerList, final long nowELAPSED,
final long nowRTC) {
boolean hasWakeup = false;
@@ -2891,20 +2928,7 @@
// If this is an ALLOW_WHILE_IDLE alarm, we constrain how frequently the app can
// schedule such alarms.
final long lastTime = mLastAllowWhileIdleDispatch.get(alarm.creatorUid, 0);
- final boolean dozing = mPendingIdleUntil != null;
- final boolean ebs = mForceAppStandbyTracker.isForceAllAppsStandbyEnabled();
- final long minTime;
- if (!dozing && !ebs) {
- minTime = lastTime + mConstants.ALLOW_WHILE_IDLE_SHORT_TIME;
- } else if (dozing) {
- minTime = lastTime + mConstants.ALLOW_WHILE_IDLE_LONG_TIME;
- } else if (mUseAllowWhileIdleShortTime.get(alarm.creatorUid)) {
- // if the last allow-while-idle went off while uid was fg, or the uid
- // recently came into fg, don't block the alarm for long.
- minTime = lastTime + mConstants.ALLOW_WHILE_IDLE_SHORT_TIME;
- } else {
- minTime = lastTime + mConstants.ALLOW_WHILE_IDLE_LONG_TIME;
- }
+ final long minTime = lastTime + getWhileIdleMinIntervalLocked(alarm.creatorUid);
if (nowELAPSED < minTime) {
// Whoops, it hasn't been long enough since the last ALLOW_WHILE_IDLE
// alarm went off for this app. Reschedule the alarm to be in the
@@ -3641,6 +3665,7 @@
} else if (Intent.ACTION_UID_REMOVED.equals(action)) {
if (uid >= 0) {
mLastAllowWhileIdleDispatch.delete(uid);
+ mUseAllowWhileIdleShortTime.delete(uid);
}
} else {
if (Intent.ACTION_PACKAGE_REMOVED.equals(action)
@@ -3693,7 +3718,6 @@
@Override public void onUidGone(int uid, boolean disabled) {
synchronized (mLock) {
- mUseAllowWhileIdleShortTime.delete(uid);
if (disabled) {
removeForStoppedLocked(uid);
}
@@ -3701,9 +3725,6 @@
}
@Override public void onUidActive(int uid) {
- synchronized (mLock) {
- mUseAllowWhileIdleShortTime.put(uid, true);
- }
}
@Override public void onUidIdle(int uid, boolean disabled) {
@@ -3766,6 +3787,18 @@
sendPendingBackgroundAlarmsLocked(uid, packageName);
}
}
+
+ @Override
+ public void onUidForeground(int uid, boolean foreground) {
+ synchronized (mLock) {
+ if (foreground) {
+ mUseAllowWhileIdleShortTime.put(uid, true);
+
+ // Note we don't have to drain the pending while-idle alarms here, because
+ // this event should coincide with unblockAlarmsForUid().
+ }
+ }
+ }
};
private final BroadcastStats getStatsLocked(PendingIntent pi) {
@@ -4026,7 +4059,7 @@
if (allowWhileIdle) {
// Record the last time this uid handled an ALLOW_WHILE_IDLE alarm.
mLastAllowWhileIdleDispatch.put(alarm.creatorUid, nowELAPSED);
- if (mForceAppStandbyTracker.isInForeground(alarm.creatorUid)) {
+ if (mForceAppStandbyTracker.isUidInForeground(alarm.creatorUid)) {
mUseAllowWhileIdleShortTime.put(alarm.creatorUid, true);
} else {
mUseAllowWhileIdleShortTime.put(alarm.creatorUid, false);
diff --git a/services/core/java/com/android/server/ForceAppStandbyTracker.java b/services/core/java/com/android/server/ForceAppStandbyTracker.java
index 7604044..339101f 100644
--- a/services/core/java/com/android/server/ForceAppStandbyTracker.java
+++ b/services/core/java/com/android/server/ForceAppStandbyTracker.java
@@ -54,6 +54,7 @@
import com.android.internal.app.IAppOpsService;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
+import com.android.server.DeviceIdleController.LocalService;
import com.android.server.ForceAppStandbyTrackerProto.ExemptedPackage;
import com.android.server.ForceAppStandbyTrackerProto.RunAnyInBackgroundRestrictedPackages;
@@ -64,18 +65,15 @@
/**
* Class to keep track of the information related to "force app standby", which includes:
* - OP_RUN_ANY_IN_BACKGROUND for each package
- * - UID foreground state
+ * - UID foreground/active state
* - User+system power save whitelist
* - Temporary power save whitelist
* - Global "force all apps standby" mode enforced by battery saver.
*
- * TODO: In general, we can reduce the number of callbacks by checking all signals before sending
- * each callback. For example, even when an UID comes into the foreground, if it wasn't
- * originally restricted, then there's no need to send an event.
- * Doing this would be error-prone, so we punt it for now, but we should revisit it later.
+ * TODO: Make it a LocalService.
*
* Test:
- * atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
+ atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
*/
public class ForceAppStandbyTracker {
private static final String TAG = "ForceAppStandbyTracker";
@@ -108,6 +106,11 @@
@GuardedBy("mLock")
final ArraySet<Pair<Integer, String>> mRunAnyRestrictedPackages = new ArraySet<>();
+ /** UIDs that are active. */
+ @GuardedBy("mLock")
+ final SparseBooleanArray mActiveUids = new SparseBooleanArray();
+
+ /** UIDs that are in the foreground. */
@GuardedBy("mLock")
final SparseBooleanArray mForegroundUids = new SparseBooleanArray();
@@ -160,18 +163,20 @@
boolean mForcedAppStandbyEnabled;
interface Stats {
- int UID_STATE_CHANGED = 0;
- int RUN_ANY_CHANGED = 1;
- int ALL_UNWHITELISTED = 2;
- int ALL_WHITELIST_CHANGED = 3;
- int TEMP_WHITELIST_CHANGED = 4;
- int EXEMPT_CHANGED = 5;
- int FORCE_ALL_CHANGED = 6;
- int FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED = 7;
+ int UID_FG_STATE_CHANGED = 0;
+ int UID_ACTIVE_STATE_CHANGED = 1;
+ int RUN_ANY_CHANGED = 2;
+ int ALL_UNWHITELISTED = 3;
+ int ALL_WHITELIST_CHANGED = 4;
+ int TEMP_WHITELIST_CHANGED = 5;
+ int EXEMPT_CHANGED = 6;
+ int FORCE_ALL_CHANGED = 7;
+ int FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED = 8;
}
private final StatLogger mStatLogger = new StatLogger(new String[] {
- "UID_STATE_CHANGED",
+ "UID_FG_STATE_CHANGED",
+ "UID_ACTIVE_STATE_CHANGED",
"RUN_ANY_CHANGED",
"ALL_UNWHITELISTED",
"ALL_WHITELIST_CHANGED",
@@ -260,9 +265,16 @@
* This is called when the foreground state changed for a UID.
*/
private void onUidForegroundStateChanged(ForceAppStandbyTracker sender, int uid) {
+ onUidForeground(uid, sender.isUidInForeground(uid));
+ }
+
+ /**
+ * This is called when the active/idle state changed for a UID.
+ */
+ private void onUidActiveStateChanged(ForceAppStandbyTracker sender, int uid) {
updateJobsForUid(uid);
- if (sender.isInForeground(uid)) {
+ if (sender.isUidActive(uid)) {
unblockAlarmsForUid(uid);
}
}
@@ -355,6 +367,14 @@
*/
public void unblockAlarmsForUidPackage(int uid, String packageName) {
}
+
+ /**
+ * Called when a UID comes into the foreground or the background.
+ *
+ * @see #isUidInForeground(int)
+ */
+ public void onUidForeground(int uid, boolean foreground) {
+ }
}
@VisibleForTesting
@@ -404,8 +424,10 @@
try {
mIActivityManager.registerUidObserver(new UidObserver(),
- ActivityManager.UID_OBSERVER_GONE | ActivityManager.UID_OBSERVER_IDLE
- | ActivityManager.UID_OBSERVER_ACTIVE,
+ ActivityManager.UID_OBSERVER_GONE
+ | ActivityManager.UID_OBSERVER_IDLE
+ | ActivityManager.UID_OBSERVER_ACTIVE
+ | ActivityManager.UID_OBSERVER_PROCSTATE,
ActivityManager.PROCESS_STATE_UNKNOWN, null);
mAppOpsService.startWatchingMode(TARGET_OP, null,
new AppOpsWatcher());
@@ -563,65 +585,77 @@
return true;
}
- /**
- * Puts a UID to {@link #mForegroundUids}.
- */
- void uidToForeground(int uid) {
- synchronized (mLock) {
- if (UserHandle.isCore(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);
- mHandler.notifyUidForegroundStateChanged(uid);
+ private static boolean addUidToArray(SparseBooleanArray array, int uid) {
+ if (UserHandle.isCore(uid)) {
+ return false;
}
+ if (array.get(uid)) {
+ return false;
+ }
+ array.put(uid, true);
+ return true;
}
- /**
- * Sets false for a UID {@link #mForegroundUids}, or remove it when {@code remove} is true.
- */
- void uidToBackground(int uid, boolean remove) {
- synchronized (mLock) {
- if (UserHandle.isCore(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);
- }
- mHandler.notifyUidForegroundStateChanged(uid);
+ private static boolean removeUidFromArray(SparseBooleanArray array, int uid, boolean remove) {
+ if (UserHandle.isCore(uid)) {
+ return false;
}
+ if (!array.get(uid)) {
+ return false;
+ }
+ if (remove) {
+ array.delete(uid);
+ } else {
+ array.put(uid, false);
+ }
+ return true;
}
private final class UidObserver extends IUidObserver.Stub {
@Override
public void onUidStateChanged(int uid, int procState, long procStateSeq) {
+ synchronized (mLock) {
+ if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
+ if (removeUidFromArray(mForegroundUids, uid, false)) {
+ mHandler.notifyUidForegroundStateChanged(uid);
+ }
+ } else {
+ if (addUidToArray(mForegroundUids, uid)) {
+ mHandler.notifyUidForegroundStateChanged(uid);
+ }
+ }
+ }
}
@Override
public void onUidGone(int uid, boolean disabled) {
- uidToBackground(uid, /*remove=*/ true);
+ removeUid(uid, true);
}
@Override
public void onUidActive(int uid) {
- uidToForeground(uid);
+ synchronized (mLock) {
+ if (addUidToArray(mActiveUids, uid)) {
+ mHandler.notifyUidActiveStateChanged(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);
+ removeUid(uid, false);
+ }
+
+ private void removeUid(int uid, boolean remove) {
+ synchronized (mLock) {
+ if (removeUidFromArray(mActiveUids, uid, remove)) {
+ mHandler.notifyUidActiveStateChanged(uid);
+ }
+ if (removeUidFromArray(mForegroundUids, uid, remove)) {
+ mHandler.notifyUidForegroundStateChanged(uid);
+ }
+ }
}
@Override
@@ -695,22 +729,27 @@
}
private class MyHandler extends Handler {
- private static final int MSG_UID_STATE_CHANGED = 1;
- private static final int MSG_RUN_ANY_CHANGED = 2;
- private static final int MSG_ALL_UNWHITELISTED = 3;
- private static final int MSG_ALL_WHITELIST_CHANGED = 4;
- private static final int MSG_TEMP_WHITELIST_CHANGED = 5;
- private static final int MSG_FORCE_ALL_CHANGED = 6;
- private static final int MSG_USER_REMOVED = 7;
- private static final int MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED = 8;
- private static final int MSG_EXEMPT_CHANGED = 9;
+ private static final int MSG_UID_ACTIVE_STATE_CHANGED = 0;
+ private static final int MSG_UID_FG_STATE_CHANGED = 1;
+ private static final int MSG_RUN_ANY_CHANGED = 3;
+ private static final int MSG_ALL_UNWHITELISTED = 4;
+ private static final int MSG_ALL_WHITELIST_CHANGED = 5;
+ private static final int MSG_TEMP_WHITELIST_CHANGED = 6;
+ private static final int MSG_FORCE_ALL_CHANGED = 7;
+ private static final int MSG_USER_REMOVED = 8;
+ private static final int MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED = 9;
+ private static final int MSG_EXEMPT_CHANGED = 10;
public MyHandler(Looper looper) {
super(looper);
}
+ public void notifyUidActiveStateChanged(int uid) {
+ obtainMessage(MSG_UID_ACTIVE_STATE_CHANGED, uid, 0).sendToTarget();
+ }
+
public void notifyUidForegroundStateChanged(int uid) {
- obtainMessage(MSG_UID_STATE_CHANGED, uid, 0).sendToTarget();
+ obtainMessage(MSG_UID_FG_STATE_CHANGED, uid, 0).sendToTarget();
}
public void notifyRunAnyAppOpsChanged(int uid, @NonNull String packageName) {
@@ -718,26 +757,32 @@
}
public void notifyAllUnwhitelisted() {
+ removeMessages(MSG_ALL_UNWHITELISTED);
obtainMessage(MSG_ALL_UNWHITELISTED).sendToTarget();
}
public void notifyAllWhitelistChanged() {
+ removeMessages(MSG_ALL_WHITELIST_CHANGED);
obtainMessage(MSG_ALL_WHITELIST_CHANGED).sendToTarget();
}
public void notifyTempWhitelistChanged() {
+ removeMessages(MSG_TEMP_WHITELIST_CHANGED);
obtainMessage(MSG_TEMP_WHITELIST_CHANGED).sendToTarget();
}
public void notifyForceAllAppsStandbyChanged() {
+ removeMessages(MSG_FORCE_ALL_CHANGED);
obtainMessage(MSG_FORCE_ALL_CHANGED).sendToTarget();
}
public void notifyForcedAppStandbyFeatureFlagChanged() {
+ removeMessages(MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED);
obtainMessage(MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED).sendToTarget();
}
public void notifyExemptChanged() {
+ removeMessages(MSG_EXEMPT_CHANGED);
obtainMessage(MSG_EXEMPT_CHANGED).sendToTarget();
}
@@ -763,11 +808,18 @@
long start = mStatLogger.getTime();
switch (msg.what) {
- case MSG_UID_STATE_CHANGED:
+ case MSG_UID_ACTIVE_STATE_CHANGED:
+ for (Listener l : cloneListeners()) {
+ l.onUidActiveStateChanged(sender, msg.arg1);
+ }
+ mStatLogger.logDurationStat(Stats.UID_ACTIVE_STATE_CHANGED, start);
+ return;
+
+ case MSG_UID_FG_STATE_CHANGED:
for (Listener l : cloneListeners()) {
l.onUidForegroundStateChanged(sender, msg.arg1);
}
- mStatLogger.logDurationStat(Stats.UID_STATE_CHANGED, start);
+ mStatLogger.logDurationStat(Stats.UID_FG_STATE_CHANGED, start);
return;
case MSG_RUN_ANY_CHANGED:
@@ -846,18 +898,23 @@
mRunAnyRestrictedPackages.removeAt(i);
}
}
- for (int i = mForegroundUids.size() - 1; i >= 0; i--) {
- final int uid = mForegroundUids.keyAt(i);
- final int userId = UserHandle.getUserId(uid);
-
- if (userId == removedUserId) {
- mForegroundUids.removeAt(i);
- }
- }
+ cleanUpArrayForUser(mActiveUids, removedUserId);
+ cleanUpArrayForUser(mForegroundUids, removedUserId);
mExemptedPackages.remove(removedUserId);
}
}
+ private void cleanUpArrayForUser(SparseBooleanArray array, int removedUserId) {
+ for (int i = array.size() - 1; i >= 0; i--) {
+ final int uid = array.keyAt(i);
+ final int userId = UserHandle.getUserId(uid);
+
+ if (userId == removedUserId) {
+ array.removeAt(i);
+ }
+ }
+ }
+
/**
* Called by device idle controller to update the power save whitelists.
*/
@@ -954,7 +1011,7 @@
*/
private boolean isRestricted(int uid, @NonNull String packageName,
boolean useTempWhitelistToo, boolean exemptOnBatterySaver) {
- if (isInForeground(uid)) {
+ if (isUidActive(uid)) {
return false;
}
synchronized (mLock) {
@@ -982,13 +1039,29 @@
}
/**
+ * @return whether a UID is in active or not.
+ *
+ * Note this information is based on the UID proc state callback, meaning it's updated
+ * asynchronously and may subtly be stale. If the fresh data is needed, use
+ * {@link ActivityManagerInternal#getUidProcessState} instead.
+ */
+ public boolean isUidActive(int uid) {
+ if (UserHandle.isCore(uid)) {
+ return true;
+ }
+ synchronized (mLock) {
+ return mActiveUids.get(uid);
+ }
+ }
+
+ /**
* @return whether a UID is in the foreground or not.
*
* Note this information is based on the UID proc state callback, meaning it's updated
* asynchronously and may subtly be stale. If the fresh data is needed, use
* {@link ActivityManagerInternal#getUidProcessState} instead.
*/
- public boolean isInForeground(int uid) {
+ public boolean isUidInForeground(int uid) {
if (UserHandle.isCore(uid)) {
return true;
}
@@ -1062,17 +1135,12 @@
pw.println(mIsPluggedIn);
pw.print(indent);
- pw.print("Foreground uids: [");
+ pw.print("Active uids: ");
+ dumpUids(pw, mActiveUids);
- String sep = "";
- for (int i = 0; i < mForegroundUids.size(); i++) {
- if (mForegroundUids.valueAt(i)) {
- pw.print(sep);
- pw.print(UserHandle.formatUid(mForegroundUids.keyAt(i)));
- sep = " ";
- }
- }
- pw.println("]");
+ pw.print(indent);
+ pw.print("Foreground uids: ");
+ dumpUids(pw, mForegroundUids);
pw.print(indent);
pw.print("Whitelist appids: ");
@@ -1114,6 +1182,20 @@
}
}
+ private void dumpUids(PrintWriter pw, SparseBooleanArray array) {
+ pw.print("[");
+
+ String sep = "";
+ for (int i = 0; i < array.size(); i++) {
+ if (array.valueAt(i)) {
+ pw.print(sep);
+ pw.print(UserHandle.formatUid(array.keyAt(i)));
+ sep = " ";
+ }
+ }
+ pw.println("]");
+ }
+
public void dumpProto(ProtoOutputStream proto, long fieldId) {
synchronized (mLock) {
final long token = proto.start(fieldId);
@@ -1125,6 +1207,13 @@
mForceAllAppStandbyForSmallBattery);
proto.write(ForceAppStandbyTrackerProto.IS_PLUGGED_IN, mIsPluggedIn);
+ for (int i = 0; i < mActiveUids.size(); i++) {
+ if (mActiveUids.valueAt(i)) {
+ proto.write(ForceAppStandbyTrackerProto.ACTIVE_UIDS,
+ mActiveUids.keyAt(i));
+ }
+ }
+
for (int i = 0; i < mForegroundUids.size(); i++) {
if (mForegroundUids.valueAt(i)) {
proto.write(ForceAppStandbyTrackerProto.FOREGROUND_UIDS,
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 2e4567a..5eb7700 100644
--- a/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java
+++ b/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java
@@ -92,7 +92,7 @@
jobStatus.printUniqueId(pw);
pw.print(" from ");
UserHandle.formatUid(pw, uid);
- pw.print(mForceAppStandbyTracker.isInForeground(uid) ? " foreground" : " background");
+ pw.print(mForceAppStandbyTracker.isUidActive(uid) ? " active" : " idle");
if (mForceAppStandbyTracker.isUidPowerSaveWhitelisted(uid) ||
mForceAppStandbyTracker.isUidTempPowerSaveWhitelisted(uid)) {
pw.print(", whitelisted");
@@ -136,7 +136,7 @@
proto.write(TrackedJob.SOURCE_PACKAGE_NAME, sourcePkg);
proto.write(TrackedJob.IS_IN_FOREGROUND,
- mForceAppStandbyTracker.isInForeground(sourceUid));
+ mForceAppStandbyTracker.isUidActive(sourceUid));
proto.write(TrackedJob.IS_WHITELISTED,
mForceAppStandbyTracker.isUidPowerSaveWhitelisted(sourceUid) ||
mForceAppStandbyTracker.isUidTempPowerSaveWhitelisted(sourceUid));
diff --git a/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java b/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
index a16f118..2c50f22 100644
--- a/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
@@ -54,7 +54,6 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.provider.Settings;
import android.provider.Settings.Global;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -64,7 +63,6 @@
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsService;
-import com.android.internal.util.test.FakeSettingsProvider;
import com.android.server.ForceAppStandbyTracker.Listener;
import org.junit.Before;
@@ -83,6 +81,12 @@
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
+/**
+ * Tests for {@link ForceAppStandbyTracker}
+ *
+ * Run with:
+ atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
+ */
@SmallTest
@RunWith(AndroidJUnit4.class)
public class ForceAppStandbyTrackerTest {
@@ -236,7 +240,8 @@
verify(mMockIActivityManager).registerUidObserver(
uidObserverArgumentCaptor.capture(),
eq(ActivityManager.UID_OBSERVER_GONE | ActivityManager.UID_OBSERVER_IDLE
- | ActivityManager.UID_OBSERVER_ACTIVE),
+ | ActivityManager.UID_OBSERVER_ACTIVE
+ | ActivityManager.UID_OBSERVER_PROCSTATE),
eq(ActivityManager.PROCESS_STATE_UNKNOWN),
isNull());
verify(mMockIAppOpsService).startWatchingMode(
@@ -333,23 +338,23 @@
mPowerSaveMode = true;
mPowerSaveObserver.accept(getPowerSaveState());
- assertFalse(instance.isInForeground(UID_1));
- assertFalse(instance.isInForeground(UID_2));
- assertTrue(instance.isInForeground(Process.SYSTEM_UID));
+ assertFalse(instance.isUidActive(UID_1));
+ assertFalse(instance.isUidActive(UID_2));
+ assertTrue(instance.isUidActive(Process.SYSTEM_UID));
mIUidObserver.onUidActive(UID_1);
areRestricted(instance, UID_1, PACKAGE_1, NONE);
areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS);
areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE);
- assertTrue(instance.isInForeground(UID_1));
- assertFalse(instance.isInForeground(UID_2));
+ assertTrue(instance.isUidActive(UID_1));
+ assertFalse(instance.isUidActive(UID_2));
mIUidObserver.onUidGone(UID_1, /*disable=*/ false);
areRestricted(instance, UID_1, PACKAGE_1, JOBS_AND_ALARMS);
areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS);
areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE);
- assertFalse(instance.isInForeground(UID_1));
- assertFalse(instance.isInForeground(UID_2));
+ assertFalse(instance.isUidActive(UID_1));
+ assertFalse(instance.isUidActive(UID_2));
mIUidObserver.onUidActive(UID_1);
areRestricted(instance, UID_1, PACKAGE_1, NONE);
@@ -360,8 +365,8 @@
areRestricted(instance, UID_1, PACKAGE_1, JOBS_AND_ALARMS);
areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS);
areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE);
- assertFalse(instance.isInForeground(UID_1));
- assertFalse(instance.isInForeground(UID_2));
+ assertFalse(instance.isUidActive(UID_1));
+ assertFalse(instance.isUidActive(UID_2));
// Toggle the app ops.
mPowerSaveMode = false;
@@ -456,6 +461,88 @@
}
@Test
+ public void testUidStateForeground() throws Exception {
+ final ForceAppStandbyTrackerTestable instance = newInstance();
+ callStart(instance);
+
+ mIUidObserver.onUidActive(UID_1);
+
+ assertTrue(instance.isUidActive(UID_1));
+ assertFalse(instance.isUidActive(UID_2));
+ assertTrue(instance.isUidActive(Process.SYSTEM_UID));
+
+ assertFalse(instance.isUidInForeground(UID_1));
+ assertFalse(instance.isUidInForeground(UID_2));
+ assertTrue(instance.isUidInForeground(Process.SYSTEM_UID));
+
+
+ mIUidObserver.onUidStateChanged(UID_2,
+ ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE, 0);
+
+ assertTrue(instance.isUidActive(UID_1));
+ assertFalse(instance.isUidActive(UID_2));
+ assertTrue(instance.isUidActive(Process.SYSTEM_UID));
+
+ assertFalse(instance.isUidInForeground(UID_1));
+ assertTrue(instance.isUidInForeground(UID_2));
+ assertTrue(instance.isUidInForeground(Process.SYSTEM_UID));
+
+
+ mIUidObserver.onUidStateChanged(UID_1,
+ ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0);
+
+ assertTrue(instance.isUidActive(UID_1));
+ assertFalse(instance.isUidActive(UID_2));
+ assertTrue(instance.isUidActive(Process.SYSTEM_UID));
+
+ assertTrue(instance.isUidInForeground(UID_1));
+ assertTrue(instance.isUidInForeground(UID_2));
+ assertTrue(instance.isUidInForeground(Process.SYSTEM_UID));
+
+ mIUidObserver.onUidGone(UID_1, true);
+
+ assertFalse(instance.isUidActive(UID_1));
+ assertFalse(instance.isUidActive(UID_2));
+ assertTrue(instance.isUidActive(Process.SYSTEM_UID));
+
+ assertFalse(instance.isUidInForeground(UID_1));
+ assertTrue(instance.isUidInForeground(UID_2));
+ assertTrue(instance.isUidInForeground(Process.SYSTEM_UID));
+
+ mIUidObserver.onUidIdle(UID_2, true);
+
+ assertFalse(instance.isUidActive(UID_1));
+ assertFalse(instance.isUidActive(UID_2));
+ assertTrue(instance.isUidActive(Process.SYSTEM_UID));
+
+ assertFalse(instance.isUidInForeground(UID_1));
+ assertFalse(instance.isUidInForeground(UID_2));
+ assertTrue(instance.isUidInForeground(Process.SYSTEM_UID));
+
+ mIUidObserver.onUidStateChanged(UID_1,
+ ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND, 0);
+
+ assertFalse(instance.isUidActive(UID_1));
+ assertFalse(instance.isUidActive(UID_2));
+ assertTrue(instance.isUidActive(Process.SYSTEM_UID));
+
+ assertTrue(instance.isUidInForeground(UID_1));
+ assertFalse(instance.isUidInForeground(UID_2));
+ assertTrue(instance.isUidInForeground(Process.SYSTEM_UID));
+
+ mIUidObserver.onUidStateChanged(UID_1,
+ ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0);
+
+ assertFalse(instance.isUidActive(UID_1));
+ assertFalse(instance.isUidActive(UID_2));
+ assertTrue(instance.isUidActive(Process.SYSTEM_UID));
+
+ assertFalse(instance.isUidInForeground(UID_1));
+ assertFalse(instance.isUidInForeground(UID_2));
+ assertTrue(instance.isUidInForeground(Process.SYSTEM_UID));
+ }
+
+ @Test
public void testExempt() throws Exception {
final ForceAppStandbyTrackerTestable instance = newInstance();
callStart(instance);
@@ -953,8 +1040,8 @@
setAppOps(UID_2, PACKAGE_2, true);
setAppOps(UID_10_2, PACKAGE_2, true);
- assertTrue(instance.isInForeground(UID_1));
- assertTrue(instance.isInForeground(UID_10_1));
+ assertTrue(instance.isUidActive(UID_1));
+ assertTrue(instance.isUidActive(UID_10_1));
assertFalse(instance.isRunAnyInBackgroundAppOpsAllowed(UID_2, PACKAGE_2));
assertFalse(instance.isRunAnyInBackgroundAppOpsAllowed(UID_10_2, PACKAGE_2));
@@ -965,8 +1052,8 @@
waitUntilMainHandlerDrain();
- assertTrue(instance.isInForeground(UID_1));
- assertFalse(instance.isInForeground(UID_10_1));
+ assertTrue(instance.isUidActive(UID_1));
+ assertFalse(instance.isUidActive(UID_10_1));
assertFalse(instance.isRunAnyInBackgroundAppOpsAllowed(UID_2, PACKAGE_2));
assertTrue(instance.isRunAnyInBackgroundAppOpsAllowed(UID_10_2, PACKAGE_2));