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.
*/