Loading core/java/android/app/ActivityThread.java +13 −0 Original line number Diff line number Diff line Loading @@ -50,6 +50,7 @@ import android.app.RemoteServiceException.BadUserInitiatedJobNotificationExcepti 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; Loading Loading @@ -2236,6 +2237,9 @@ public final class ActivityThread extends ClientTransactionHandler case ForegroundServiceDidNotStartInTimeException.TYPE_ID: throw generateForegroundServiceDidNotStartInTimeException(message, extras); case ForegroundServiceDidNotStopInTimeException.TYPE_ID: throw generateForegroundServiceDidNotStopInTimeException(message, extras); case CannotPostForegroundServiceNotificationException.TYPE_ID: throw new CannotPostForegroundServiceNotificationException(message); Loading Loading @@ -2266,6 +2270,15 @@ public final class ActivityThread extends ClientTransactionHandler 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 Loading core/java/android/app/RemoteServiceException.java +27 −0 Original line number Diff line number Diff line Loading @@ -70,6 +70,33 @@ public class RemoteServiceException extends AndroidRuntimeException { } } /** * 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. Loading core/java/android/app/Service.java +1 −2 Original line number Diff line number Diff line Loading @@ -1198,8 +1198,7 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac * 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()}, Loading core/java/android/app/activity_manager.aconfig +10 −0 Original line number Diff line number Diff line Loading @@ -60,3 +60,13 @@ flag { 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 } } services/core/java/com/android/server/am/ActiveServices.java +38 −19 Original line number Diff line number Diff line Loading @@ -157,6 +157,7 @@ import android.app.Notification; 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; Loading Loading @@ -784,7 +785,7 @@ public final class ActiveServices { 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"); } Loading Loading @@ -2456,12 +2457,14 @@ public final class ActiveServices { + " 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(); } Loading Loading @@ -3936,12 +3939,12 @@ public final class ActiveServices { 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) Loading @@ -3956,6 +3959,21 @@ public final class ActiveServices { return; } 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) { Loading @@ -3968,8 +3986,9 @@ public final class ActiveServices { } // 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. // dialog really doesn't remember the "cause" (especially if there have been // multiple ANRs), so it's not doable. } } } Loading Loading
core/java/android/app/ActivityThread.java +13 −0 Original line number Diff line number Diff line Loading @@ -50,6 +50,7 @@ import android.app.RemoteServiceException.BadUserInitiatedJobNotificationExcepti 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; Loading Loading @@ -2236,6 +2237,9 @@ public final class ActivityThread extends ClientTransactionHandler case ForegroundServiceDidNotStartInTimeException.TYPE_ID: throw generateForegroundServiceDidNotStartInTimeException(message, extras); case ForegroundServiceDidNotStopInTimeException.TYPE_ID: throw generateForegroundServiceDidNotStopInTimeException(message, extras); case CannotPostForegroundServiceNotificationException.TYPE_ID: throw new CannotPostForegroundServiceNotificationException(message); Loading Loading @@ -2266,6 +2270,15 @@ public final class ActivityThread extends ClientTransactionHandler 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 Loading
core/java/android/app/RemoteServiceException.java +27 −0 Original line number Diff line number Diff line Loading @@ -70,6 +70,33 @@ public class RemoteServiceException extends AndroidRuntimeException { } } /** * 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. Loading
core/java/android/app/Service.java +1 −2 Original line number Diff line number Diff line Loading @@ -1198,8 +1198,7 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac * 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()}, Loading
core/java/android/app/activity_manager.aconfig +10 −0 Original line number Diff line number Diff line Loading @@ -60,3 +60,13 @@ flag { 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 } }
services/core/java/com/android/server/am/ActiveServices.java +38 −19 Original line number Diff line number Diff line Loading @@ -157,6 +157,7 @@ import android.app.Notification; 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; Loading Loading @@ -784,7 +785,7 @@ public final class ActiveServices { 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"); } Loading Loading @@ -2456,12 +2457,14 @@ public final class ActiveServices { + " 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(); } Loading Loading @@ -3936,12 +3939,12 @@ public final class ActiveServices { 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) Loading @@ -3956,6 +3959,21 @@ public final class ActiveServices { return; } 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) { Loading @@ -3968,8 +3986,9 @@ public final class ActiveServices { } // 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. // dialog really doesn't remember the "cause" (especially if there have been // multiple ANRs), so it's not doable. } } } Loading