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;
             }
         }