Merge "Introduce a new flag to enable crashing the app on FGS timeout." into main
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 bef5c612..94bf813 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");
}
@@ -2456,12 +2457,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();
}
@@ -3936,12 +3939,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)
@@ -3956,20 +3959,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 00c2df6..46ed1fd 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;
}
}