Do not throttle EXEMPT apps on battery saver
- Also keep track of # of callback calls and their duration and print them in
dumpsys. Example:
Stats:
UID_STATE_CHANGED: count=141, total=31.9ms, avg=0.226ms
RUN_ANY_CHANGED: count=0, total=0.0ms, avg=0.000ms
ALL_UNWHITELISTED: count=0, total=0.0ms, avg=0.000ms
ALL_WHITELIST_CHANGED: count=0, total=0.0ms, avg=0.000ms
TEMP_WHITELIST_CHANGED: count=28, total=14.9ms, avg=0.532ms
EXEMPT_CHANGED: count=2, total=4.7ms, avg=2.370ms
FORCE_ALL_CHANGED: count=6, total=1.1ms, avg=0.178ms
FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED: count=0, total=0.0ms, avg=0.000ms
Bug: 70565111
Test: atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
Test: atest CtsBatterySavingTestCases
Test: atest CtsJobSchedulerTestCases
Test: atest CtsAlarmManagerTestCases
Change-Id: Ie4983456dd60f7115a15ee25a8d1bf5c078dac74
diff --git a/services/core/java/com/android/server/ForceAppStandbyTracker.java b/services/core/java/com/android/server/ForceAppStandbyTracker.java
index 257845e..7604044 100644
--- a/services/core/java/com/android/server/ForceAppStandbyTracker.java
+++ b/services/core/java/com/android/server/ForceAppStandbyTracker.java
@@ -22,6 +22,9 @@
import android.app.AppOpsManager.PackageOps;
import android.app.IActivityManager;
import android.app.IUidObserver;
+import android.app.usage.UsageStatsManager;
+import android.app.usage.UsageStatsManagerInternal;
+import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -42,6 +45,7 @@
import android.util.Pair;
import android.util.Slog;
import android.util.SparseBooleanArray;
+import android.util.SparseSetArray;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
@@ -50,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.ForceAppStandbyTrackerProto.ExemptedPackage;
import com.android.server.ForceAppStandbyTrackerProto.RunAnyInBackgroundRestrictedPackages;
import java.io.PrintWriter;
@@ -74,7 +79,7 @@
*/
public class ForceAppStandbyTracker {
private static final String TAG = "ForceAppStandbyTracker";
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = true;
@GuardedBy("ForceAppStandbyTracker.class")
private static ForceAppStandbyTracker sInstance;
@@ -89,6 +94,8 @@
AppOpsManager mAppOpsManager;
IAppOpsService mAppOpsService;
PowerManagerInternal mPowerManagerInternal;
+ StandbyTracker mStandbyTracker;
+ UsageStatsManagerInternal mUsageStatsManagerInternal;
private final MyHandler mHandler;
@@ -113,6 +120,12 @@
@GuardedBy("mLock")
private int[] mTempWhitelistedAppIds = mPowerWhitelistedAllAppIds;
+ /**
+ * Per-user packages that are in the EXEMPT bucket.
+ */
+ @GuardedBy("mLock")
+ private final SparseSetArray<String> mExemptedPackages = new SparseSetArray<>();
+
@GuardedBy("mLock")
final ArraySet<Listener> mListeners = new ArraySet<>();
@@ -146,6 +159,28 @@
@GuardedBy("mLock")
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;
+ }
+
+ private final StatLogger mStatLogger = new StatLogger(new String[] {
+ "UID_STATE_CHANGED",
+ "RUN_ANY_CHANGED",
+ "ALL_UNWHITELISTED",
+ "ALL_WHITELIST_CHANGED",
+ "TEMP_WHITELIST_CHANGED",
+ "EXEMPT_CHANGED",
+ "FORCE_ALL_CHANGED",
+ "FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED",
+ });
+
@VisibleForTesting
class FeatureFlagsObserver extends ContentObserver {
FeatureFlagsObserver() {
@@ -162,12 +197,11 @@
}
boolean isForcedAppStandbyEnabled() {
- return Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.FORCED_APP_STANDBY_ENABLED, 1) == 1;
+ return injectGetGlobalSettingInt(Settings.Global.FORCED_APP_STANDBY_ENABLED, 1) == 1;
}
boolean isForcedAppStandbyForSmallBatteryEnabled() {
- return Settings.Global.getInt(mContext.getContentResolver(),
+ return injectGetGlobalSettingInt(
Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED, 0) == 1;
}
@@ -258,6 +292,17 @@
// only for affected app-ids.
updateAllJobs();
+
+ // Note when an app is just put in the temp whitelist, we do *not* drain pending alarms.
+ }
+
+ /**
+ * This is called when the EXEMPT bucket is updated.
+ */
+ private void onExemptChanged(ForceAppStandbyTracker sender) {
+ // This doesn't happen very often, so just re-evaluate all jobs / alarms.
+ updateAllJobs();
+ unblockAllUnrestrictedAlarms();
}
/**
@@ -346,11 +391,16 @@
mAppOpsManager = Preconditions.checkNotNull(injectAppOpsManager());
mAppOpsService = Preconditions.checkNotNull(injectIAppOpsService());
mPowerManagerInternal = Preconditions.checkNotNull(injectPowerManagerInternal());
+ mUsageStatsManagerInternal = Preconditions.checkNotNull(
+ injectUsageStatsManagerInternal());
+
mFlagsObserver = new FeatureFlagsObserver();
mFlagsObserver.register();
mForcedAppStandbyEnabled = mFlagsObserver.isForcedAppStandbyEnabled();
mForceAllAppStandbyForSmallBattery =
mFlagsObserver.isForcedAppStandbyForSmallBatteryEnabled();
+ mStandbyTracker = new StandbyTracker();
+ mUsageStatsManagerInternal.addAppIdleStateChangeListener(mStandbyTracker);
try {
mIActivityManager.registerUidObserver(new UidObserver(),
@@ -408,10 +458,20 @@
}
@VisibleForTesting
+ UsageStatsManagerInternal injectUsageStatsManagerInternal() {
+ return LocalServices.getService(UsageStatsManagerInternal.class);
+ }
+
+ @VisibleForTesting
boolean isSmallBatteryDevice() {
return ActivityManager.isSmallBatteryDevice();
}
+ @VisibleForTesting
+ int injectGetGlobalSettingInt(String key, int def) {
+ return Settings.Global.getInt(mContext.getContentResolver(), key, def);
+ }
+
/**
* Update {@link #mRunAnyRestrictedPackages} with the current app ops state.
*/
@@ -604,6 +664,30 @@
}
}
+ final class StandbyTracker extends AppIdleStateChangeListener {
+ @Override
+ public void onAppIdleStateChanged(String packageName, int userId, boolean idle,
+ int bucket) {
+ if (DEBUG) {
+ Slog.d(TAG,"onAppIdleStateChanged: " + packageName + " u" + userId
+ + (idle ? " idle" : " active") + " " + bucket);
+ }
+ final boolean changed;
+ if (bucket == UsageStatsManager.STANDBY_BUCKET_EXEMPTED) {
+ changed = mExemptedPackages.add(userId, packageName);
+ } else {
+ changed = mExemptedPackages.remove(userId, packageName);
+ }
+ if (changed) {
+ mHandler.notifyExemptChanged();
+ }
+ }
+
+ @Override
+ public void onParoleStateChanged(boolean isParoleOn) {
+ }
+ }
+
private Listener[] cloneListeners() {
synchronized (mLock) {
return mListeners.toArray(new Listener[mListeners.size()]);
@@ -619,6 +703,7 @@
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;
public MyHandler(Looper looper) {
super(looper);
@@ -652,6 +737,10 @@
obtainMessage(MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED).sendToTarget();
}
+ public void notifyExemptChanged() {
+ obtainMessage(MSG_EXEMPT_CHANGED).sendToTarget();
+ }
+
public void doUserRemoved(int userId) {
obtainMessage(MSG_USER_REMOVED, userId, 0).sendToTarget();
}
@@ -672,37 +761,57 @@
}
final ForceAppStandbyTracker sender = ForceAppStandbyTracker.this;
+ long start = mStatLogger.getTime();
switch (msg.what) {
case MSG_UID_STATE_CHANGED:
for (Listener l : cloneListeners()) {
l.onUidForegroundStateChanged(sender, msg.arg1);
}
+ mStatLogger.logDurationStat(Stats.UID_STATE_CHANGED, start);
return;
+
case MSG_RUN_ANY_CHANGED:
for (Listener l : cloneListeners()) {
l.onRunAnyAppOpsChanged(sender, msg.arg1, (String) msg.obj);
}
+ mStatLogger.logDurationStat(Stats.RUN_ANY_CHANGED, start);
return;
+
case MSG_ALL_UNWHITELISTED:
for (Listener l : cloneListeners()) {
l.onPowerSaveUnwhitelisted(sender);
}
+ mStatLogger.logDurationStat(Stats.ALL_UNWHITELISTED, start);
return;
+
case MSG_ALL_WHITELIST_CHANGED:
for (Listener l : cloneListeners()) {
l.onPowerSaveWhitelistedChanged(sender);
}
+ mStatLogger.logDurationStat(Stats.ALL_WHITELIST_CHANGED, start);
return;
+
case MSG_TEMP_WHITELIST_CHANGED:
for (Listener l : cloneListeners()) {
l.onTempPowerSaveWhitelistChanged(sender);
}
+ mStatLogger.logDurationStat(Stats.TEMP_WHITELIST_CHANGED, start);
return;
+
+ case MSG_EXEMPT_CHANGED:
+ for (Listener l : cloneListeners()) {
+ l.onExemptChanged(sender);
+ }
+ mStatLogger.logDurationStat(Stats.EXEMPT_CHANGED, start);
+ return;
+
case MSG_FORCE_ALL_CHANGED:
for (Listener l : cloneListeners()) {
l.onForceAllAppsStandbyChanged(sender);
}
+ mStatLogger.logDurationStat(Stats.FORCE_ALL_CHANGED, start);
return;
+
case MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED:
// Feature flag for forced app standby changed.
final boolean unblockAlarms;
@@ -715,7 +824,10 @@
l.unblockAllUnrestrictedAlarms();
}
}
+ mStatLogger.logDurationStat(
+ Stats.FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED, start);
return;
+
case MSG_USER_REMOVED:
handleUserRemoved(msg.arg1);
return;
@@ -742,6 +854,7 @@
mForegroundUids.removeAt(i);
}
}
+ mExemptedPackages.remove(removedUserId);
}
}
@@ -860,6 +973,10 @@
if (exemptOnBatterySaver) {
return false;
}
+ final int userId = UserHandle.getUserId(uid);
+ if (mExemptedPackages.contains(userId, packageName)) {
+ return false;
+ }
return mForceAllAppsStandby;
}
}
@@ -966,6 +1083,23 @@
pw.println(Arrays.toString(mTempWhitelistedAppIds));
pw.print(indent);
+ pw.println("Exempted packages:");
+ for (int i = 0; i < mExemptedPackages.size(); i++) {
+ pw.print(indent);
+ pw.print(" User ");
+ pw.print(mExemptedPackages.keyAt(i));
+ pw.println();
+
+ for (int j = 0; j < mExemptedPackages.sizeAt(i); j++) {
+ pw.print(indent);
+ pw.print(" ");
+ pw.print(mExemptedPackages.valueAt(i, j));
+ pw.println();
+ }
+ }
+ pw.println();
+
+ pw.print(indent);
pw.println("Restricted packages:");
for (Pair<Integer, String> uidAndPackage : mRunAnyRestrictedPackages) {
pw.print(indent);
@@ -975,6 +1109,8 @@
pw.print(uidAndPackage.second);
pw.println();
}
+
+ mStatLogger.dump(pw, indent);
}
}
@@ -987,7 +1123,7 @@
isSmallBatteryDevice());
proto.write(ForceAppStandbyTrackerProto.FORCE_ALL_APPS_STANDBY_FOR_SMALL_BATTERY,
mForceAllAppStandbyForSmallBattery);
- proto.write(ForceAppStandbyTrackerProto.IS_CHARGING, mIsPluggedIn);
+ proto.write(ForceAppStandbyTrackerProto.IS_PLUGGED_IN, mIsPluggedIn);
for (int i = 0; i < mForegroundUids.size(); i++) {
if (mForegroundUids.valueAt(i)) {
@@ -1004,6 +1140,18 @@
proto.write(ForceAppStandbyTrackerProto.TEMP_POWER_SAVE_WHITELIST_APP_IDS, appId);
}
+ for (int i = 0; i < mExemptedPackages.size(); i++) {
+ for (int j = 0; j < mExemptedPackages.sizeAt(i); j++) {
+ final long token2 = proto.start(
+ ForceAppStandbyTrackerProto.EXEMPTED_PACKAGES);
+
+ proto.write(ExemptedPackage.USER_ID, mExemptedPackages.keyAt(i));
+ proto.write(ExemptedPackage.PACKAGE_NAME, mExemptedPackages.valueAt(i, j));
+
+ proto.end(token2);
+ }
+ }
+
for (Pair<Integer, String> uidAndPackage : mRunAnyRestrictedPackages) {
final long token2 = proto.start(
ForceAppStandbyTrackerProto.RUN_ANY_IN_BACKGROUND_RESTRICTED_PACKAGES);
@@ -1012,6 +1160,9 @@
uidAndPackage.second);
proto.end(token2);
}
+
+ mStatLogger.dumpProto(proto, ForceAppStandbyTrackerProto.STATS);
+
proto.end(token);
}
}