Introduce a new flag to enable crashing the app on FGS timeout.
Decouple what happens to an app after one of its foreground services
times out. With this new flag enabled, instead of ANRing the app, crash
the app.
Bug: 339526947
Test: atest CtsFgsTimeoutTestCases
Flag: android.app.enable_fgs_timeout_crash_behavior
Change-Id: I8270f50294af17d8bff1ab00ddb33b88d0a68283
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index caaaf51..d4812dd 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -50,6 +50,7 @@
import android.app.RemoteServiceException.CannotPostForegroundServiceNotificationException;
import android.app.RemoteServiceException.CrashedByAdbException;
import android.app.RemoteServiceException.ForegroundServiceDidNotStartInTimeException;
+import android.app.RemoteServiceException.ForegroundServiceDidNotStopInTimeException;
import android.app.RemoteServiceException.MissingRequestPasswordComplexityPermissionException;
import android.app.assist.AssistContent;
import android.app.assist.AssistStructure;
@@ -2236,6 +2237,9 @@
case ForegroundServiceDidNotStartInTimeException.TYPE_ID:
throw generateForegroundServiceDidNotStartInTimeException(message, extras);
+ case ForegroundServiceDidNotStopInTimeException.TYPE_ID:
+ throw generateForegroundServiceDidNotStopInTimeException(message, extras);
+
case CannotPostForegroundServiceNotificationException.TYPE_ID:
throw new CannotPostForegroundServiceNotificationException(message);
@@ -2266,6 +2270,15 @@
throw new ForegroundServiceDidNotStartInTimeException(message, inner);
}
+ private ForegroundServiceDidNotStopInTimeException
+ generateForegroundServiceDidNotStopInTimeException(String message, Bundle extras) {
+ final String serviceClassName =
+ ForegroundServiceDidNotStopInTimeException.getServiceClassNameFromExtras(extras);
+ final Exception inner = (serviceClassName == null) ? null
+ : Service.getStartForegroundServiceStackTrace(serviceClassName);
+ throw new ForegroundServiceDidNotStopInTimeException(message, inner);
+ }
+
class H extends Handler {
public static final int BIND_APPLICATION = 110;
@UnsupportedAppUsage
diff --git a/core/java/android/app/RemoteServiceException.java b/core/java/android/app/RemoteServiceException.java
index c5ad110..c624c43 100644
--- a/core/java/android/app/RemoteServiceException.java
+++ b/core/java/android/app/RemoteServiceException.java
@@ -71,6 +71,33 @@
}
/**
+ * Exception used to crash an app process when it didn't stop after hitting its time limit.
+ *
+ * @hide
+ */
+ public static class ForegroundServiceDidNotStopInTimeException extends RemoteServiceException {
+ /** The type ID passed to {@link IApplicationThread#scheduleCrash}. */
+ public static final int TYPE_ID = 7;
+
+ private static final String KEY_SERVICE_CLASS_NAME = "serviceclassname";
+
+ public ForegroundServiceDidNotStopInTimeException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+ public static Bundle createExtrasForService(@NonNull ComponentName service) {
+ Bundle b = new Bundle();
+ b.putString(KEY_SERVICE_CLASS_NAME, service.getClassName());
+ return b;
+ }
+
+ @Nullable
+ public static String getServiceClassNameFromExtras(@Nullable Bundle extras) {
+ return (extras == null) ? null : extras.getString(KEY_SERVICE_CLASS_NAME);
+ }
+ }
+
+ /**
* Exception used to crash an app process when the system received a RemoteException
* while posting a notification of a foreground service.
*
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index 726064e..aaddaa6 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -1198,8 +1198,7 @@
* Callback called when a particular foreground service type has timed out.
*
* <p>This callback is meant to give the app a small grace period of a few seconds to finish
- * the foreground service of the associated type - if it fails to do so, the app will be
- * declared an ANR.
+ * the foreground service of the associated type - if it fails to do so, the app will crash.
*
* <p>The foreground service of the associated type can be stopped within the time limit by
* {@link android.app.Service#stopSelf()},
diff --git a/core/java/android/app/activity_manager.aconfig b/core/java/android/app/activity_manager.aconfig
index 9cf83b9..bb24fd1 100644
--- a/core/java/android/app/activity_manager.aconfig
+++ b/core/java/android/app/activity_manager.aconfig
@@ -60,3 +60,13 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ namespace: "backstage_power"
+ name: "enable_fgs_timeout_crash_behavior"
+ description: "Enable the new behavior where the app is crashed once an FGS times out."
+ bug: "339526947"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index d776700..408cff0 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -157,6 +157,7 @@
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.RemoteServiceException.ForegroundServiceDidNotStartInTimeException;
+import android.app.RemoteServiceException.ForegroundServiceDidNotStopInTimeException;
import android.app.Service;
import android.app.ServiceStartArgs;
import android.app.StartForegroundCalledOnStoppedServiceException;
@@ -784,7 +785,7 @@
ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG,
"SERVICE_FOREGROUND_TIMEOUT");
this.mFGSAnrTimer = new ServiceAnrTimer(service,
- ActivityManagerService.SERVICE_FGS_ANR_TIMEOUT_MSG,
+ ActivityManagerService.SERVICE_FGS_CRASH_TIMEOUT_MSG,
"FGS_TIMEOUT");
}
@@ -2458,12 +2459,14 @@
+ " foreground service type "
+ ServiceInfo.foregroundServiceTypeToLabel(
foregroundServiceType);
- if (!android.app.Flags.gateFgsTimeoutAnrBehavior()) {
+ // Only throw an exception if the new ANR behavior
+ // ("do nothing") is not gated or the new crashing logic gate
+ // is enabled; otherwise, reset the limit temporarily.
+ if (!android.app.Flags.gateFgsTimeoutAnrBehavior()
+ || android.app.Flags.enableFgsTimeoutCrashBehavior()) {
throw new ForegroundServiceStartNotAllowedException(
exceptionMsg);
} else {
- // Only throw an exception above while the new ANR behavior
- // is not gated, otherwise, reset the limit temporarily.
Slog.wtf(TAG, exceptionMsg);
fgsTypeInfo.reset();
}
@@ -3938,12 +3941,12 @@
Slog.w(TAG_SERVICE, "Exception from scheduleTimeoutServiceForType: " + e);
}
- // ANR the service after giving the service some time to clean up.
- mFGSAnrTimer.start(sr, mAm.mConstants.mFgsAnrExtraWaitDuration);
+ // Crash the service after giving the service some time to clean up.
+ mFGSAnrTimer.start(sr, mAm.mConstants.mFgsCrashExtraWaitDuration);
}
}
- void onFgsAnrTimeout(ServiceRecord sr) {
+ void onFgsCrashTimeout(ServiceRecord sr) {
final int fgsType = getTimeLimitedFgsType(sr.foregroundServiceType);
if (fgsType == ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE) {
return; // no timed out FGS type was found (either it was stopped or it switched types)
@@ -3958,20 +3961,36 @@
return;
}
- final TimeoutRecord tr = TimeoutRecord.forFgsTimeout(reason);
- tr.mLatencyTracker.waitingOnAMSLockStarted();
- synchronized (mAm) {
- tr.mLatencyTracker.waitingOnAMSLockEnded();
-
- Slog.e(TAG_SERVICE, "FGS ANR'ed: " + sr);
- traceInstant("FGS ANR: ", sr);
- if (sr.app != null) {
- mAm.appNotResponding(sr.app, tr);
+ if (android.app.Flags.enableFgsTimeoutCrashBehavior()) {
+ // Crash the app
+ synchronized (mAm) {
+ Slog.e(TAG_SERVICE, "FGS Crashed: " + sr);
+ traceInstant("FGS Crash: ", sr);
+ if (sr.app != null) {
+ mAm.crashApplicationWithTypeWithExtras(sr.app.uid, sr.app.getPid(),
+ sr.app.info.packageName, sr.app.userId, reason, false /*force*/,
+ ForegroundServiceDidNotStopInTimeException.TYPE_ID,
+ ForegroundServiceDidNotStopInTimeException
+ .createExtrasForService(sr.getComponentName()));
+ }
}
+ } else {
+ // ANR the app if the new crash behavior is not enabled
+ final TimeoutRecord tr = TimeoutRecord.forFgsTimeout(reason);
+ tr.mLatencyTracker.waitingOnAMSLockStarted();
+ synchronized (mAm) {
+ tr.mLatencyTracker.waitingOnAMSLockEnded();
- // TODO: Can we close the ANR dialog here, if it's still shown? Currently, the ANR
- // dialog really doesn't remember the "cause" (especially if there have been multiple
- // ANRs), so it's not doable.
+ Slog.e(TAG_SERVICE, "FGS ANR'ed: " + sr);
+ traceInstant("FGS ANR: ", sr);
+ if (sr.app != null) {
+ mAm.appNotResponding(sr.app, tr);
+ }
+
+ // TODO: Can we close the ANR dialog here, if it's still shown? Currently, the ANR
+ // dialog really doesn't remember the "cause" (especially if there have been
+ // multiple ANRs), so it's not doable.
+ }
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 9e06b75..26aa053 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -1115,17 +1115,17 @@
/**
* If a service of a timeout-enforced type doesn't finish within this duration after its
- * timeout, then we'll declare an ANR.
+ * timeout, then we'll crash the app.
* i.e. if the time limit for a type is 1 hour, and this extra duration is 10 seconds, then
- * the app will be ANR'ed 1 hour and 10 seconds after it started.
+ * the app will crash 1 hour and 10 seconds after it started.
*/
- private static final String KEY_FGS_ANR_EXTRA_WAIT_DURATION = "fgs_anr_extra_wait_duration";
+ private static final String KEY_FGS_CRASH_EXTRA_WAIT_DURATION = "fgs_crash_extra_wait_duration";
- /** @see #KEY_FGS_ANR_EXTRA_WAIT_DURATION */
- static final long DEFAULT_FGS_ANR_EXTRA_WAIT_DURATION = 10_000;
+ /** @see #KEY_FGS_CRASH_EXTRA_WAIT_DURATION */
+ static final long DEFAULT_FGS_CRASH_EXTRA_WAIT_DURATION = 10_000;
- /** @see #KEY_FGS_ANR_EXTRA_WAIT_DURATION */
- public volatile long mFgsAnrExtraWaitDuration = DEFAULT_FGS_ANR_EXTRA_WAIT_DURATION;
+ /** @see #KEY_FGS_CRASH_EXTRA_WAIT_DURATION */
+ public volatile long mFgsCrashExtraWaitDuration = DEFAULT_FGS_CRASH_EXTRA_WAIT_DURATION;
/** @see #KEY_USE_TIERED_CACHED_ADJ */
public boolean USE_TIERED_CACHED_ADJ = DEFAULT_USE_TIERED_CACHED_ADJ;
@@ -1315,8 +1315,8 @@
case KEY_SHORT_FGS_ANR_EXTRA_WAIT_DURATION:
updateShortFgsAnrExtraWaitDuration();
break;
- case KEY_FGS_ANR_EXTRA_WAIT_DURATION:
- updateFgsAnrExtraWaitDuration();
+ case KEY_FGS_CRASH_EXTRA_WAIT_DURATION:
+ updateFgsCrashExtraWaitDuration();
break;
case KEY_PROACTIVE_KILLS_ENABLED:
updateProactiveKillsEnabled();
@@ -2199,11 +2199,11 @@
DEFAULT_DATA_SYNC_FGS_TIMEOUT_DURATION);
}
- private void updateFgsAnrExtraWaitDuration() {
- mFgsAnrExtraWaitDuration = DeviceConfig.getLong(
+ private void updateFgsCrashExtraWaitDuration() {
+ mFgsCrashExtraWaitDuration = DeviceConfig.getLong(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- KEY_FGS_ANR_EXTRA_WAIT_DURATION,
- DEFAULT_FGS_ANR_EXTRA_WAIT_DURATION);
+ KEY_FGS_CRASH_EXTRA_WAIT_DURATION,
+ DEFAULT_FGS_CRASH_EXTRA_WAIT_DURATION);
}
private void updateEnableWaitForFinishAttachApplication() {
@@ -2456,8 +2456,8 @@
pw.print("="); pw.println(mMediaProcessingFgsTimeoutDuration);
pw.print(" "); pw.print(KEY_DATA_SYNC_FGS_TIMEOUT_DURATION);
pw.print("="); pw.println(mDataSyncFgsTimeoutDuration);
- pw.print(" "); pw.print(KEY_FGS_ANR_EXTRA_WAIT_DURATION);
- pw.print("="); pw.println(mFgsAnrExtraWaitDuration);
+ pw.print(" "); pw.print(KEY_FGS_CRASH_EXTRA_WAIT_DURATION);
+ pw.print("="); pw.println(mFgsCrashExtraWaitDuration);
pw.print(" "); pw.print(KEY_USE_TIERED_CACHED_ADJ);
pw.print("="); pw.println(USE_TIERED_CACHED_ADJ);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 1b3b198..20c3e7f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1670,7 +1670,7 @@
static final int BIND_APPLICATION_TIMEOUT_SOFT_MSG = 82;
static final int BIND_APPLICATION_TIMEOUT_HARD_MSG = 83;
static final int SERVICE_FGS_TIMEOUT_MSG = 84;
- static final int SERVICE_FGS_ANR_TIMEOUT_MSG = 85;
+ static final int SERVICE_FGS_CRASH_TIMEOUT_MSG = 85;
static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -2041,8 +2041,8 @@
case SERVICE_FGS_TIMEOUT_MSG: {
mServices.onFgsTimeout((ServiceRecord) msg.obj);
} break;
- case SERVICE_FGS_ANR_TIMEOUT_MSG: {
- mServices.onFgsAnrTimeout((ServiceRecord) msg.obj);
+ case SERVICE_FGS_CRASH_TIMEOUT_MSG: {
+ mServices.onFgsCrashTimeout((ServiceRecord) msg.obj);
} break;
}
}