From 6cf25139a0c4625a753f84183c641e83e89ec8fb Mon Sep 17 00:00:00 2001 From: Jing Ji Date: Wed, 5 May 2021 20:51:01 -0700 Subject: [PATCH] Kill process if it's cached & idle and in forced-app-standby mode This behavior could be turned OFF via the flag kill_fas_cached_idle in device_config. Bug: 152573287 Test: atest FrameworksServicesTests:ActivityManagerTest Test: atest FrameworksMockingServicesTests:AppStateTrackerTest Change-Id: Ifa6950582fbf6a24595b36163810f3c7e9345394 --- .../com/android/server/AppStateTracker.java | 26 +++- .../android/server/AppStateTrackerImpl.java | 95 ++++++++++-- .../java/android/app/ApplicationExitInfo.java | 9 ++ core/proto/android/app/appexit_enums.proto | 9 +- .../com/android/server/am/ActiveServices.java | 20 ++- .../server/am/ActivityManagerConstants.java | 25 +++- .../server/am/ActivityManagerService.java | 4 + .../com/android/server/am/OomAdjuster.java | 2 + .../com/android/server/am/ProcessList.java | 55 +++++++ .../android/server/am/ProcessStateRecord.java | 19 ++- .../android/server/AppStateTrackerTest.java | 26 ++++ .../server/am/ActivityManagerTest.java | 137 ++++++++++++++++++ 12 files changed, 400 insertions(+), 27 deletions(-) diff --git a/apex/jobscheduler/framework/java/com/android/server/AppStateTracker.java b/apex/jobscheduler/framework/java/com/android/server/AppStateTracker.java index b0b9abccd2291..3c89016ec6057 100644 --- a/apex/jobscheduler/framework/java/com/android/server/AppStateTracker.java +++ b/apex/jobscheduler/framework/java/com/android/server/AppStateTracker.java @@ -25,19 +25,29 @@ public interface AppStateTracker { String TAG = "AppStateTracker"; /** - * Register a {@link ServiceStateListener} to listen for forced-app-standby changes that should - * affect services. + * Register a {@link ForcedAppStandbyListener} to listen for forced-app-standby changes that + * should affect services etc. */ - void addServiceStateListener(@NonNull ServiceStateListener listener); + void addForcedAppStandbyListener(@NonNull ForcedAppStandbyListener listener); /** - * A listener to listen to forced-app-standby changes that should affect services. + * @return {code true} if the given UID/package has been in forced app standby mode. */ - interface ServiceStateListener { + boolean isAppInForcedAppStandby(int uid, @NonNull String packageName); + + /** + * A listener to listen to forced-app-standby changes that should affect services etc. + */ + interface ForcedAppStandbyListener { /** - * Called when an app goes into forced app standby and its foreground - * services need to be removed from that state. + * Called when an app goes in/out of forced app standby. */ - void stopForegroundServicesForUidPackage(int uid, String packageName); + void updateForceAppStandbyForUidPackage(int uid, String packageName, boolean standby); + + /** + * Called when all apps' forced-app-standby states need to be re-evaluated, due to + * enable/disable certain feature flags. + */ + void updateForcedAppStandbyForAllApps(); } } diff --git a/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java index c332a598c30b9..1deb3656dabe0 100644 --- a/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java +++ b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java @@ -60,8 +60,10 @@ import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener; import java.io.PrintWriter; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Objects; +import java.util.Set; /** * Class to keep track of the information related to "force app standby", which includes: @@ -160,16 +162,46 @@ public class AppStateTrackerImpl implements AppStateTracker { @GuardedBy("mLock") boolean mForcedAppStandbyEnabled; + /** + * A lock-free set of (uid, packageName) pairs in forced app standby mode. + * + *

+ * It's bascially shadowing the {@link #mRunAnyRestrictedPackages} together with + * the {@link #mForcedAppStandbyEnabled} and the {@link #mForceAllAppsStandby} - mutations on + * them would result in copy-on-write. + * + * Note: when {@link #mForcedAppStandbyEnabled} is {@code false}, it'll be set to an empty set. + * when {@link #mForceAllAppsStandby} is {@code true}, it'll be set to null; + *

+ */ + volatile Set> mForcedAppStandbyUidPackages = Collections.emptySet(); + @Override - public void addServiceStateListener(@NonNull ServiceStateListener listener) { + public void addForcedAppStandbyListener(@NonNull ForcedAppStandbyListener listener) { addListener(new Listener() { @Override - public void stopForegroundServicesForUidPackage(int uid, String packageName) { - listener.stopForegroundServicesForUidPackage(uid, packageName); + public void updateForceAppStandbyForUidPackage(int uid, String packageName, + boolean standby) { + listener.updateForceAppStandbyForUidPackage(uid, packageName, standby); + } + + @Override + public void updateForcedAppStandbyForAllApps() { + listener.updateForcedAppStandbyForAllApps(); } }); } + @Override + public boolean isAppInForcedAppStandby(int uid, @NonNull String packageName) { + final Set> fasUidPkgs = mForcedAppStandbyUidPackages; + if (fasUidPkgs == null) { + // Meaning the mForceAllAppsStandby is true. + return true; + } + return fasUidPkgs.contains(Pair.create(uid, packageName)); + } + interface Stats { int UID_FG_STATE_CHANGED = 0; int UID_ACTIVE_STATE_CHANGED = 1; @@ -233,6 +265,7 @@ public class AppStateTrackerImpl implements AppStateTracker { return; } mForcedAppStandbyEnabled = enabled; + updateForcedAppStandbyUidPackagesLocked(); if (DEBUG) { Slog.d(TAG, "Forced app standby feature flag changed: " + mForcedAppStandbyEnabled); @@ -277,7 +310,11 @@ public class AppStateTrackerImpl implements AppStateTracker { if (!sender.isRunAnyInBackgroundAppOpsAllowed(uid, packageName)) { Slog.v(TAG, "Package " + packageName + "/" + uid + " toggled into fg service restriction"); - stopForegroundServicesForUidPackage(uid, packageName); + updateForceAppStandbyForUidPackage(uid, packageName, true); + } else { + Slog.v(TAG, "Package " + packageName + "/" + uid + + " toggled out of fg service restriction"); + updateForceAppStandbyForUidPackage(uid, packageName, false); } } @@ -342,6 +379,7 @@ public class AppStateTrackerImpl implements AppStateTracker { private void onForceAllAppsStandbyChanged(AppStateTrackerImpl sender) { updateAllJobs(); updateAllAlarms(); + updateForcedAppStandbyForAllApps(); } /** @@ -366,10 +404,17 @@ public class AppStateTrackerImpl implements AppStateTracker { } /** - * Called when an app goes into forced app standby and its foreground - * services need to be removed from that state. + * Called when an app goes in/out of forced app standby. */ - public void stopForegroundServicesForUidPackage(int uid, String packageName) { + public void updateForceAppStandbyForUidPackage(int uid, String packageName, + boolean standby) { + } + + /** + * Called when all apps' forced-app-standby states need to be re-evaluated due to changes of + * feature flags such as {@link #mForcedAppStandbyEnabled} or {@link #mForceAllAppsStandby}. + */ + public void updateForcedAppStandbyForAllApps() { } /** @@ -438,9 +483,12 @@ public class AppStateTrackerImpl implements AppStateTracker { final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); // No need to notify for state change as all the alarms and jobs should be // removed too. - mExemptedBucketPackages.remove(userId, pkgName); - mRunAnyRestrictedPackages.remove(Pair.create(uid, pkgName)); - mActiveUids.delete(uid); + synchronized (mLock) { + mExemptedBucketPackages.remove(userId, pkgName); + mRunAnyRestrictedPackages.remove(Pair.create(uid, pkgName)); + updateForcedAppStandbyUidPackagesLocked(); + mActiveUids.delete(uid); + } } break; } @@ -580,6 +628,29 @@ public class AppStateTrackerImpl implements AppStateTracker { } } } + updateForcedAppStandbyUidPackagesLocked(); + } + + /** + * Update the {@link #mForcedAppStandbyUidPackages} upon mutations on + * {@link #mRunAnyRestrictedPackages}, {@link #mForcedAppStandbyEnabled} or + * {@link #mForceAllAppsStandby}. + */ + @GuardedBy("mLock") + private void updateForcedAppStandbyUidPackagesLocked() { + if (!mForcedAppStandbyEnabled) { + mForcedAppStandbyUidPackages = Collections.emptySet(); + return; + } + if (mForceAllAppsStandby) { + mForcedAppStandbyUidPackages = null; + return; + } + Set> fasUidPkgs = new ArraySet<>(); + for (int i = 0, size = mRunAnyRestrictedPackages.size(); i < size; i++) { + fasUidPkgs.add(mRunAnyRestrictedPackages.valueAt(i)); + } + mForcedAppStandbyUidPackages = Collections.unmodifiableSet(fasUidPkgs); } private void updateForceAllAppStandbyState() { @@ -601,6 +672,7 @@ public class AppStateTrackerImpl implements AppStateTracker { return; } mForceAllAppsStandby = enable; + updateForcedAppStandbyUidPackagesLocked(); mHandler.notifyForceAllAppsStandbyChanged(); } @@ -645,6 +717,7 @@ public class AppStateTrackerImpl implements AppStateTracker { } else { mRunAnyRestrictedPackages.removeAt(index); } + updateForcedAppStandbyUidPackagesLocked(); return true; } @@ -896,6 +969,7 @@ public class AppStateTrackerImpl implements AppStateTracker { if (unblockAlarms) { l.unblockAllUnrestrictedAlarms(); } + l.updateForcedAppStandbyForAllApps(); } mStatLogger.logDurationStat( Stats.FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED, start); @@ -966,6 +1040,7 @@ public class AppStateTrackerImpl implements AppStateTracker { mRunAnyRestrictedPackages.removeAt(i); } } + updateForcedAppStandbyUidPackagesLocked(); cleanUpArrayForUser(mActiveUids, removedUserId); mExemptedBucketPackages.remove(removedUserId); } diff --git a/core/java/android/app/ApplicationExitInfo.java b/core/java/android/app/ApplicationExitInfo.java index 8574678a5a027..c97ffbcfe5e71 100644 --- a/core/java/android/app/ApplicationExitInfo.java +++ b/core/java/android/app/ApplicationExitInfo.java @@ -325,6 +325,15 @@ public final class ApplicationExitInfo implements Parcelable { */ public static final int SUBREASON_ISOLATED_NOT_NEEDED = 17; + /** + * The process was killed because it's in forced-app-standby state, and it's cached and + * its uid state is idle; this would be set only when the reason is {@link #REASON_OTHER}. + * + * For internal use only. + * @hide + */ + public static final int SUBREASON_CACHED_IDLE_FORCED_APP_STANDBY = 18; + // If there is any OEM code which involves additional app kill reasons, it should // be categorized in {@link #REASON_OTHER}, with subreason code starting from 1000. diff --git a/core/proto/android/app/appexit_enums.proto b/core/proto/android/app/appexit_enums.proto index 491e1dc203b58..298d06213806f 100644 --- a/core/proto/android/app/appexit_enums.proto +++ b/core/proto/android/app/appexit_enums.proto @@ -213,10 +213,15 @@ enum AppExitSubReasonCode { * The process was killed because it's isolated and was in a cached state. */ SUBREASON_ISOLATED_NOT_NEEDED = 17; + + /** + * The process was killed because it's in forced-app-standby state, and it's cached and + * its uid state is idle; this would be set only when the reason is {@link #REASON_OTHER}. + */ + SUBREASON_CACHED_IDLE_FORCED_APP_STANDBY = 18; } -/** - * The relative importance level that the system places on a process. +/** * The relative importance level that the system places on a process. * Keep sync with the definitions in * {@link android.app.ActivityManager.RunningAppProcessInfo} */ diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 5700bb367b041..1bd53ae247309 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -309,11 +309,23 @@ public final class ActiveServices { * Watch for apps being put into forced app standby, so we can step their fg * services down. */ - class ForcedStandbyListener implements AppStateTracker.ServiceStateListener { + class ForcedStandbyListener implements AppStateTracker.ForcedAppStandbyListener { @Override - public void stopForegroundServicesForUidPackage(final int uid, final String packageName) { + public void updateForceAppStandbyForUidPackage(int uid, String packageName, + boolean standby) { synchronized (mAm) { - stopAllForegroundServicesLocked(uid, packageName); + if (standby) { + stopAllForegroundServicesLocked(uid, packageName); + } + mAm.mProcessList.updateForceAppStandbyForUidPackageLocked( + uid, packageName, standby); + } + } + + @Override + public void updateForcedAppStandbyForAllApps() { + synchronized (mAm) { + mAm.mProcessList.updateForcedAppStandbyForAllAppsLocked(); } } } @@ -499,7 +511,7 @@ public final class ActiveServices { void systemServicesReady() { AppStateTracker ast = LocalServices.getService(AppStateTracker.class); - ast.addServiceStateListener(new ForcedStandbyListener()); + ast.addForcedAppStandbyListener(new ForcedStandbyListener()); mAppWidgetManagerInternal = LocalServices.getService(AppWidgetManagerInternal.class); setAllowListWhileInUsePermissionInFgs(); } diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index f7ce6dd0144d6..2048ef2fcc6a8 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -52,7 +52,8 @@ final class ActivityManagerConstants extends ContentObserver { private static final String TAG = "ActivityManagerConstants"; // Key names stored in the settings value. - private static final String KEY_BACKGROUND_SETTLE_TIME = "background_settle_time"; + static final String KEY_BACKGROUND_SETTLE_TIME = "background_settle_time"; + private static final String KEY_FGSERVICE_MIN_SHOWN_TIME = "fgservice_min_shown_time"; private static final String KEY_FGSERVICE_MIN_REPORT_TIME @@ -108,9 +109,9 @@ final class ActivityManagerConstants extends ContentObserver { static final String KEY_FG_TO_BG_FGS_GRACE_DURATION = "fg_to_bg_fgs_grace_duration"; static final String KEY_FGS_START_FOREGROUND_TIMEOUT = "fgs_start_foreground_timeout"; static final String KEY_FGS_ATOM_SAMPLE_RATE = "fgs_atom_sample_rate"; + static final String KEY_KILL_FAS_CACHED_IDLE = "kill_fas_cached_idle"; private static final int DEFAULT_MAX_CACHED_PROCESSES = 32; - private static final long DEFAULT_BACKGROUND_SETTLE_TIME = 60*1000; private static final long DEFAULT_FGSERVICE_MIN_SHOWN_TIME = 2*1000; private static final long DEFAULT_FGSERVICE_MIN_REPORT_TIME = 3*1000; private static final long DEFAULT_FGSERVICE_SCREEN_ON_BEFORE_TIME = 1*1000; @@ -151,6 +152,10 @@ final class ActivityManagerConstants extends ContentObserver { private static final long DEFAULT_FG_TO_BG_FGS_GRACE_DURATION = 5 * 1000; private static final int DEFAULT_FGS_START_FOREGROUND_TIMEOUT_MS = 10 * 1000; private static final float DEFAULT_FGS_ATOM_SAMPLE_RATE = 1; // 100 % + + static final long DEFAULT_BACKGROUND_SETTLE_TIME = 60 * 1000; + static final boolean DEFAULT_KILL_FAS_CACHED_IDLE = true; + /** * Same as {@link TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED} */ @@ -482,6 +487,12 @@ final class ActivityManagerConstants extends ContentObserver { */ volatile float mFgsAtomSampleRate = DEFAULT_FGS_ATOM_SAMPLE_RATE; + /** + * Whether or not to kill apps in force-app-standby state and it's cached, its UID state is + * idle. + */ + volatile boolean mKillForceAppStandByAndCachedIdle = DEFAULT_KILL_FAS_CACHED_IDLE; + private final ActivityManagerService mService; private ContentResolver mResolver; private final KeyValueListParser mParser = new KeyValueListParser(','); @@ -687,6 +698,9 @@ final class ActivityManagerConstants extends ContentObserver { case KEY_FGS_ATOM_SAMPLE_RATE: updateFgsAtomSamplePercent(); break; + case KEY_KILL_FAS_CACHED_IDLE: + updateKillFasCachedIdle(); + break; default: break; } @@ -1019,6 +1033,13 @@ final class ActivityManagerConstants extends ContentObserver { DEFAULT_FGS_ATOM_SAMPLE_RATE); } + private void updateKillFasCachedIdle() { + mKillForceAppStandByAndCachedIdle = DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_KILL_FAS_CACHED_IDLE, + DEFAULT_KILL_FAS_CACHED_IDLE); + } + private void updateImperceptibleKillExemptions() { IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES.clear(); IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES.addAll(mDefaultImperceptibleKillExemptPackages); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 6a3cb6f22886c..3a8b547fad369 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -14267,6 +14267,10 @@ public class ActivityManagerService extends IActivityManager.Stub final int capability = uidRec != null ? uidRec.getSetCapability() : 0; final boolean ephemeral = uidRec != null ? uidRec.isEphemeral() : isEphemeralLocked(uid); + if (uidRec != null && uidRec.isIdle() && (change & UidRecord.CHANGE_IDLE) != 0) { + mProcessList.killAppIfForceStandbyAndCachedIdleLocked(uidRec); + } + if (uidRec != null && !uidRec.isIdle() && (change & UidRecord.CHANGE_GONE) != 0) { // If this uid is going away, and we haven't yet reported it is gone, // then do so now. diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index b413010772a45..40db086f377b8 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -2863,6 +2863,8 @@ public class OomAdjuster { + " target=" + state.getAdjTarget() + " capability=" + item.capability); } + mProcessList.killAppIfForceStandbyAndCachedIdleLocked(app); + return success; } diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index af3f658ad800c..8ea5004599df6 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -125,6 +125,7 @@ import com.android.internal.os.Zygote; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.MemInfoReader; +import com.android.server.AppStateTracker; import com.android.server.LocalServices; import com.android.server.ServiceThread; import com.android.server.SystemConfig; @@ -2333,6 +2334,12 @@ public final class ProcessList { allowlistedAppDataInfoMap = null; } + AppStateTracker ast = LocalServices.getService(AppStateTracker.class); + if (ast != null) { + app.mState.setForcedAppStandby(ast.isAppInForcedAppStandby( + app.info.uid, app.info.packageName)); + } + final Process.ProcessStartResult startResult; if (hostingRecord.usesWebviewZygote()) { startResult = startWebView(entryPoint, @@ -4959,6 +4966,54 @@ public final class ProcessList { return true; } + @GuardedBy("mService") + void updateForceAppStandbyForUidPackageLocked(int uid, String packageName, boolean standby) { + final UidRecord uidRec = getUidRecordLOSP(uid); + if (uidRec != null) { + uidRec.forEachProcess(app -> { + if (TextUtils.equals(app.info.packageName, packageName)) { + app.mState.setForcedAppStandby(standby); + killAppIfForceStandbyAndCachedIdleLocked(app); + } + }); + } + } + + @GuardedBy("mService") + void updateForcedAppStandbyForAllAppsLocked() { + if (!mService.mConstants.mKillForceAppStandByAndCachedIdle) { + return; + } + final AppStateTracker ast = LocalServices.getService(AppStateTracker.class); + for (int i = mLruProcesses.size() - 1; i >= 0; i--) { + final ProcessRecord app = mLruProcesses.get(i); + final boolean standby = ast.isAppInForcedAppStandby( + app.info.uid, app.info.packageName); + app.mState.setForcedAppStandby(standby); + if (standby) { + killAppIfForceStandbyAndCachedIdleLocked(app); + } + } + } + + @GuardedBy("mService") + void killAppIfForceStandbyAndCachedIdleLocked(ProcessRecord app) { + final UidRecord uidRec = app.getUidRecord(); + if (mService.mConstants.mKillForceAppStandByAndCachedIdle + && uidRec != null && uidRec.isIdle() + && app.isCached() && app.mState.isForcedAppStandby()) { + app.killLocked("cached idle & forced-app-standby", + ApplicationExitInfo.REASON_OTHER, + ApplicationExitInfo.SUBREASON_CACHED_IDLE_FORCED_APP_STANDBY, + true); + } + } + + @GuardedBy("mService") + void killAppIfForceStandbyAndCachedIdleLocked(UidRecord uidRec) { + uidRec.forEachProcess(app -> killAppIfForceStandbyAndCachedIdleLocked(app)); + } + /** * Called by ActivityManagerService when a process died. */ diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java index 94e7f34a194a7..6429b79f61114 100644 --- a/services/core/java/com/android/server/am/ProcessStateRecord.java +++ b/services/core/java/com/android/server/am/ProcessStateRecord.java @@ -341,6 +341,12 @@ final class ProcessStateRecord { @GuardedBy("mService") private @ReasonCode int mAllowStartFgs = REASON_DENIED; + /** + * Whether or not this process has been in forced-app-standby state. + */ + @GuardedBy("mService") + private boolean mForcedAppStandby; + /** * Debugging: primary thing impacting oom_adj. */ @@ -1275,6 +1281,16 @@ final class ProcessStateRecord { return mAllowStartFgs; } + @GuardedBy("mService") + void setForcedAppStandby(boolean standby) { + mForcedAppStandby = standby; + } + + @GuardedBy("mService") + boolean isForcedAppStandby() { + return mForcedAppStandby; + } + @GuardedBy("mService") void updateLastInvisibleTime(boolean hasVisibleActivities) { if (hasVisibleActivities) { @@ -1337,7 +1353,8 @@ final class ProcessStateRecord { pw.print(" pendingUiClean="); pw.println(mApp.mProfile.hasPendingUiClean()); } pw.print(prefix); pw.print("cached="); pw.print(mCached); - pw.print(" empty="); pw.println(mEmpty); + pw.print(" empty="); pw.print(mEmpty); + pw.print(" forcedAppStandby="); pw.println(mForcedAppStandby); if (mServiceB) { pw.print(prefix); pw.print("serviceb="); pw.print(mServiceB); pw.print(" serviceHighRam="); pw.println(mServiceHighRam); diff --git a/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java index 607fb4760236d..624e7dd3be4f4 100644 --- a/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java @@ -1003,6 +1003,8 @@ public class AppStateTrackerTest { verify(l, times(0)).updateAllJobs(); verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(1)).updateJobsForUidPackage(eq(UID_10_2), eq(PACKAGE_2), anyBoolean()); + verify(l, times(1)).updateForceAppStandbyForUidPackage(eq(UID_10_2), eq(PACKAGE_2), + eq(true)); verify(l, times(0)).updateAllAlarms(); verify(l, times(0)).updateAlarmsForUid(anyInt()); @@ -1017,6 +1019,8 @@ public class AppStateTrackerTest { verify(l, times(0)).updateAllJobs(); verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(1)).updateJobsForUidPackage(eq(UID_10_2), eq(PACKAGE_2), anyBoolean()); + verify(l, times(1)).updateForceAppStandbyForUidPackage(eq(UID_10_2), eq(PACKAGE_2), + eq(false)); verify(l, times(0)).updateAllAlarms(); verify(l, times(0)).updateAlarmsForUid(anyInt()); @@ -1030,6 +1034,7 @@ public class AppStateTrackerTest { verify(l, times(0)).updateAllJobs(); verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); + verify(l, times(0)).updateForceAppStandbyForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateAllAlarms(); verify(l, times(0)).updateAlarmsForUid(anyInt()); @@ -1047,6 +1052,8 @@ public class AppStateTrackerTest { verify(l, times(1)).updateAllJobs(); verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(1)).updateJobsForUidPackage(eq(UID_10_2), eq(PACKAGE_2), anyBoolean()); + verify(l, times(1)).updateForceAppStandbyForUidPackage(eq(UID_10_2), eq(PACKAGE_2), + eq(true)); verify(l, times(1)).updateAllAlarms(); verify(l, times(0)).updateAlarmsForUid(anyInt()); @@ -1063,6 +1070,7 @@ public class AppStateTrackerTest { verify(l, times(1)).updateAllJobs(); verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); + verify(l, times(0)).updateForceAppStandbyForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(1)).updateAllAlarms(); verify(l, times(0)).updateAlarmsForUid(anyInt()); @@ -1081,6 +1089,7 @@ public class AppStateTrackerTest { verify(l, times(1)).updateAllJobs(); verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); + verify(l, times(0)).updateForceAppStandbyForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(1)).updateAllAlarms(); verify(l, times(0)).updateAlarmsForUid(anyInt()); @@ -1095,6 +1104,7 @@ public class AppStateTrackerTest { verify(l, times(1)).updateAllJobs(); verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); + verify(l, times(0)).updateForceAppStandbyForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(1)).updateAllAlarms(); verify(l, times(0)).updateAlarmsForUid(anyInt()); @@ -1111,6 +1121,7 @@ public class AppStateTrackerTest { verify(l, times(1)).updateAllJobs(); verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); + verify(l, times(0)).updateForceAppStandbyForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateAllAlarms(); verify(l, times(0)).updateAlarmsForUid(anyInt()); @@ -1126,6 +1137,7 @@ public class AppStateTrackerTest { verify(l, times(1)).updateAllJobs(); verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); + verify(l, times(0)).updateForceAppStandbyForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateAllAlarms(); verify(l, times(0)).updateAlarmsForUid(anyInt()); @@ -1142,6 +1154,7 @@ public class AppStateTrackerTest { verify(l, times(1)).updateAllJobs(); verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); + verify(l, times(0)).updateForceAppStandbyForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(1)).updateAllAlarms(); verify(l, times(0)).updateAlarmsForUid(anyInt()); @@ -1158,6 +1171,7 @@ public class AppStateTrackerTest { verify(l, times(2)).updateAllJobs(); verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); + verify(l, times(0)).updateForceAppStandbyForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(1)).updateAllAlarms(); verify(l, times(0)).updateAlarmsForUid(anyInt()); @@ -1172,6 +1186,7 @@ public class AppStateTrackerTest { verify(l, times(1)).updateAllJobs(); verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); + verify(l, times(0)).updateForceAppStandbyForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(1)).updateAllAlarms(); verify(l, times(0)).updateAlarmsForUid(anyInt()); @@ -1188,6 +1203,7 @@ public class AppStateTrackerTest { verify(l, times(1)).updateAllJobs(); verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); + verify(l, times(0)).updateForceAppStandbyForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateAllAlarms(); verify(l, times(0)).updateAlarmsForUid(anyInt()); @@ -1203,6 +1219,7 @@ public class AppStateTrackerTest { verify(l, times(1)).updateAllJobs(); verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); + verify(l, times(0)).updateForceAppStandbyForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateAllAlarms(); verify(l, times(0)).updateAlarmsForUid(anyInt()); @@ -1225,6 +1242,7 @@ public class AppStateTrackerTest { verify(l, times(0)).updateAllJobs(); verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); + verify(l, times(0)).updateForceAppStandbyForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateAllAlarms(); verify(l, times(1)).updateAlarmsForUid(eq(UID_10_1)); @@ -1240,6 +1258,7 @@ public class AppStateTrackerTest { verify(l, times(0)).updateAllJobs(); verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); + verify(l, times(0)).updateForceAppStandbyForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateAllAlarms(); verify(l, times(1)).updateAlarmsForUid(eq(UID_10_1)); @@ -1255,6 +1274,7 @@ public class AppStateTrackerTest { verify(l, times(0)).updateAllJobs(); verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); + verify(l, times(0)).updateForceAppStandbyForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateAllAlarms(); verify(l, times(1)).updateAlarmsForUid(eq(UID_10_1)); @@ -1270,6 +1290,7 @@ public class AppStateTrackerTest { verify(l, times(0)).updateAllJobs(); verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); + verify(l, times(0)).updateForceAppStandbyForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateAllAlarms(); verify(l, times(1)).updateAlarmsForUid(eq(UID_10_1)); @@ -1286,6 +1307,7 @@ public class AppStateTrackerTest { verify(l, times(1)).updateAllJobs(); verify(l, times(0)).updateJobsForUid(eq(UID_10_1), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); + verify(l, times(0)).updateForceAppStandbyForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(1)).updateAllAlarms(); verify(l, times(0)).updateAlarmsForUid(eq(UID_10_1)); @@ -1301,6 +1323,7 @@ public class AppStateTrackerTest { verify(l, times(0)).updateAllJobs(); verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); + verify(l, times(0)).updateForceAppStandbyForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateAllAlarms(); verify(l, times(1)).updateAlarmsForUid(eq(UID_10_1)); @@ -1316,6 +1339,7 @@ public class AppStateTrackerTest { verify(l, times(0)).updateAllJobs(); verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); + verify(l, times(0)).updateForceAppStandbyForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateAllAlarms(); verify(l, times(1)).updateAlarmsForUid(eq(UID_10_1)); @@ -1331,6 +1355,7 @@ public class AppStateTrackerTest { verify(l, times(0)).updateAllJobs(); verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); + verify(l, times(0)).updateForceAppStandbyForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateAllAlarms(); verify(l, times(1)).updateAlarmsForUid(eq(UID_10_1)); @@ -1346,6 +1371,7 @@ public class AppStateTrackerTest { verify(l, times(0)).updateAllJobs(); verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); + verify(l, times(0)).updateForceAppStandbyForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateAllAlarms(); verify(l, times(1)).updateAlarmsForUid(eq(UID_10_1)); diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java index 6bca5e449b34b..eace99f61e605 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java @@ -52,6 +52,7 @@ import android.server.wm.settings.SettingsSession; import android.support.test.uiautomator.UiDevice; import android.test.suitebuilder.annotation.LargeTest; import android.text.TextUtils; +import android.util.KeyValueListParser; import android.util.Log; import androidx.test.InstrumentationRegistry; @@ -417,6 +418,142 @@ public class ActivityManagerTest { return false; } + @LargeTest + @Test + public void testKillAppIfFasCachedIdle() throws Exception { + final long shortTimeoutMs = 5_000; + final long backgroundSettleMs = 10_000; + final PackageManager pm = mContext.getPackageManager(); + final int uid = pm.getPackageUid(TEST_APP, 0); + final MyUidImportanceListener uidListener1 = new MyUidImportanceListener(uid); + final MyUidImportanceListener uidListener2 = new MyUidImportanceListener(uid); + SettingsSession amConstantsSettings = null; + DeviceConfigSession killForceAppStandByAndCachedIdle = null; + final ActivityManager am = mContext.getSystemService(ActivityManager.class); + final CountDownLatch[] latchHolder = new CountDownLatch[1]; + final H handler = new H(Looper.getMainLooper(), latchHolder); + final Messenger messenger = new Messenger(handler); + try { + am.addOnUidImportanceListener(uidListener1, + RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE); + am.addOnUidImportanceListener(uidListener2, RunningAppProcessInfo.IMPORTANCE_GONE); + toggleScreenOn(true); + + killForceAppStandByAndCachedIdle = new DeviceConfigSession<>( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + ActivityManagerConstants.KEY_KILL_FAS_CACHED_IDLE, + DeviceConfig::getBoolean, + ActivityManagerConstants.DEFAULT_KILL_FAS_CACHED_IDLE); + killForceAppStandByAndCachedIdle.set(true); + amConstantsSettings = new SettingsSession<>( + Settings.Global.getUriFor(Settings.Global.ACTIVITY_MANAGER_CONSTANTS), + Settings.Global::getString, Settings.Global::putString); + final KeyValueListParser parser = new KeyValueListParser(','); + long currentBackgroundSettleMs = + ActivityManagerConstants.DEFAULT_BACKGROUND_SETTLE_TIME; + try { + parser.setString(amConstantsSettings.get()); + currentBackgroundSettleMs = parser.getLong( + ActivityManagerConstants.KEY_BACKGROUND_SETTLE_TIME, + ActivityManagerConstants.DEFAULT_BACKGROUND_SETTLE_TIME); + } catch (IllegalArgumentException e) { + } + // Drain queue to make sure the existing UID_IDLE_MSG has been processed. + Thread.sleep(currentBackgroundSettleMs); + amConstantsSettings.set( + ActivityManagerConstants.KEY_BACKGROUND_SETTLE_TIME + "=" + backgroundSettleMs); + runShellCommand("cmd appops set " + TEST_APP + " RUN_ANY_IN_BACKGROUND allow"); + + final Intent intent = new Intent(ACTION_FGS_STATS_TEST); + final ComponentName cn = ComponentName.unflattenFromString( + TEST_APP + "/" + TEST_FGS_CLASS); + final Bundle bundle = new Bundle(); + intent.setComponent(cn); + bundle.putBinder(EXTRA_MESSENGER, messenger.getBinder()); + intent.putExtras(bundle); + + // Start the FGS. + latchHolder[0] = new CountDownLatch(1); + mContext.startForegroundService(intent); + assertTrue("Timed out to start fg service", uidListener1.waitFor( + RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE, shortTimeoutMs)); + assertTrue("Timed out to get the remote messenger", latchHolder[0].await( + shortTimeoutMs, TimeUnit.MILLISECONDS)); + assertFalse("FGS shouldn't be killed", uidListener2.waitFor( + RunningAppProcessInfo.IMPORTANCE_GONE, backgroundSettleMs + shortTimeoutMs)); + + // Stop the FGS, it shouldn't be killed because it's not in FAS state. + latchHolder[0] = new CountDownLatch(1); + handler.sendRemoteMessage(H.MSG_STOP_FOREGROUND, 0, 0, null); + assertTrue("Timed out to wait for stop fg", latchHolder[0].await( + shortTimeoutMs, TimeUnit.MILLISECONDS)); + assertFalse("FGS shouldn't be killed", uidListener2.waitFor( + RunningAppProcessInfo.IMPORTANCE_GONE, backgroundSettleMs + shortTimeoutMs)); + + // Set the FAS state. + runShellCommand("cmd appops set " + TEST_APP + " RUN_ANY_IN_BACKGROUND deny"); + // Now it should've been killed. + assertTrue("Should have been killed", uidListener2.waitFor( + RunningAppProcessInfo.IMPORTANCE_GONE, backgroundSettleMs + shortTimeoutMs)); + + // Start the FGS. + // Temporarily allow RUN_ANY_IN_BACKGROUND to start FGS. + runShellCommand("cmd appops set " + TEST_APP + " RUN_ANY_IN_BACKGROUND allow"); + latchHolder[0] = new CountDownLatch(1); + mContext.startForegroundService(intent); + assertTrue("Timed out to start fg service", uidListener1.waitFor( + RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE, shortTimeoutMs)); + assertTrue("Timed out to get the remote messenger", latchHolder[0].await( + shortTimeoutMs, TimeUnit.MILLISECONDS)); + runShellCommand("cmd appops set " + TEST_APP + " RUN_ANY_IN_BACKGROUND deny"); + // It shouldn't be killed since it's not cached. + assertFalse("FGS shouldn't be killed", uidListener2.waitFor( + RunningAppProcessInfo.IMPORTANCE_GONE, backgroundSettleMs + shortTimeoutMs)); + + // Stop the FGS, it should get killed because it's cached & uid idle & in FAS state. + latchHolder[0] = new CountDownLatch(1); + handler.sendRemoteMessage(H.MSG_STOP_FOREGROUND, 0, 0, null); + assertTrue("Timed out to wait for stop fg", latchHolder[0].await( + shortTimeoutMs, TimeUnit.MILLISECONDS)); + assertTrue("Should have been killed", uidListener2.waitFor( + RunningAppProcessInfo.IMPORTANCE_GONE, backgroundSettleMs + shortTimeoutMs)); + + // Disable this FAS cached idle kill feature. + killForceAppStandByAndCachedIdle.set(false); + + // Start the FGS. + // Temporarily allow RUN_ANY_IN_BACKGROUND to start FGS. + runShellCommand("cmd appops set " + TEST_APP + " RUN_ANY_IN_BACKGROUND allow"); + latchHolder[0] = new CountDownLatch(1); + mContext.startForegroundService(intent); + assertTrue("Timed out to start fg service", uidListener1.waitFor( + RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE, shortTimeoutMs)); + assertTrue("Timed out to get the remote messenger", latchHolder[0].await( + shortTimeoutMs, TimeUnit.MILLISECONDS)); + runShellCommand("cmd appops set " + TEST_APP + " RUN_ANY_IN_BACKGROUND deny"); + assertFalse("FGS shouldn't be killed", uidListener2.waitFor( + RunningAppProcessInfo.IMPORTANCE_GONE, backgroundSettleMs + shortTimeoutMs)); + + // Stop the FGS, it shouldn't be killed because the feature has been turned off. + latchHolder[0] = new CountDownLatch(1); + handler.sendRemoteMessage(H.MSG_STOP_FOREGROUND, 0, 0, null); + assertTrue("Timed out to wait for stop fg", latchHolder[0].await( + shortTimeoutMs, TimeUnit.MILLISECONDS)); + assertFalse("FGS shouldn't be killed", uidListener2.waitFor( + RunningAppProcessInfo.IMPORTANCE_GONE, backgroundSettleMs + shortTimeoutMs)); + } finally { + runShellCommand("cmd appops set " + TEST_APP + " RUN_ANY_IN_BACKGROUND default"); + if (amConstantsSettings != null) { + amConstantsSettings.close(); + } + if (killForceAppStandByAndCachedIdle != null) { + killForceAppStandByAndCachedIdle.close(); + } + am.removeOnUidImportanceListener(uidListener1); + am.removeOnUidImportanceListener(uidListener2); + } + } + /** * Make sure the screen state. */