Loading core/java/android/app/ActivityThread.java +6 −4 Original line number Diff line number Diff line Loading @@ -4763,14 +4763,16 @@ public final class ActivityThread extends ClientTransactionHandler if (s != null) { try { if (localLOGV) Slog.v(TAG, "Timeout short service " + s); s.callOnTimeout(startId); // TODO(short-service): Do we need "service executing" for timeout? // (see handleStopService()) // Unlike other service callbacks, we don't do serviceDoneExecuting() here. // "service executing" state is used to boost the procstate / oom-adj, but // for short-FGS timeout, we have a specific control for them anyway, so // we don't have to do that. s.callOnTimeout(startId); } catch (Exception e) { if (!mInstrumentation.onException(s, e)) { throw new RuntimeException( "Unable to timeout service " + s "Unable to call onTimeout on service " + s + ": " + e.toString(), e); } Slog.i(TAG, "handleTimeoutService: exception for " + token, e); Loading core/java/android/app/IActivityManager.aidl +3 −0 Original line number Diff line number Diff line Loading @@ -799,4 +799,7 @@ interface IActivityManager { * <p>Typically used only by automotive builds when the vehicle has multiple displays. */ @nullable int[] getSecondaryDisplayIdsForStartingBackgroundUsers(); /** Returns if the service is a short-service is still "alive" and past the timeout. */ boolean shouldServiceTimeOut(in ComponentName className, in IBinder token); } core/java/android/app/Service.java +15 −4 Original line number Diff line number Diff line Loading @@ -1119,10 +1119,21 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac /** @hide */ public final void callOnTimeout(int startId) { // TODO(short-service): Do we need any check here, to avoid races? // e.g. if the service is already stopped, but ActivityThread.handleTimeoutService() is // already scheduled, then we'll call this method anyway. It should be doable to prevent // that if we keep track of startForeground, stopForeground, and onDestroy. // Note, because all the service callbacks (and other similar callbacks, e.g. activity // callbacks) are delivered using the main handler, it's possible the service is already // stopped when before this method is called, so we do a double check here. if (mToken == null) { Log.w(TAG, "Service already destroyed, skipping onTimeout()"); return; } try { if (!mActivityManager.shouldServiceTimeOut( new ComponentName(this, mClassName), mToken)) { Log.w(TAG, "Service no longer relevant, skipping onTimeout()"); return; } } catch (RemoteException ex) { } onTimeout(startId); } Loading services/core/java/com/android/server/am/ActiveServices.java +57 −28 Original line number Diff line number Diff line Loading @@ -132,6 +132,7 @@ import android.app.compat.CompatChanges; import android.app.usage.UsageEvents; import android.appwidget.AppWidgetManagerInternal; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledAfter; import android.compat.annotation.EnabledSince; import android.compat.annotation.Overridable; import android.content.ComponentName; Loading @@ -150,6 +151,7 @@ import android.content.pm.ServiceInfo; import android.content.pm.ServiceInfo.ForegroundServiceType; import android.os.Binder; import android.os.Build; import android.os.Build.VERSION_CODES; import android.os.Bundle; import android.os.DeadObjectException; import android.os.Handler; Loading Loading @@ -390,6 +392,14 @@ public final class ActiveServices { @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.S) static final long FGS_START_EXCEPTION_CHANGE_ID = 174041399L; /** * If enabled, the FGS type check against the manifest FSG type will be enabled for * instant apps too. Before U, this check was only done for non-instant apps. */ @ChangeId @EnabledAfter(targetSdkVersion = VERSION_CODES.TIRAMISU) static final long FGS_TYPE_CHECK_FOR_INSTANT_APPS = 261055255L; final Runnable mLastAnrDumpClearer = new Runnable() { @Override public void run() { synchronized (mAm) { Loading Loading @@ -1818,10 +1828,8 @@ public final class ActiveServices { android.Manifest.permission.FOREGROUND_SERVICE, r.app.getPid(), r.appInfo.uid, "startForeground"); } // TODO(short-service): This part really should be above the if block, // so we'll apply the same check for instant apps too. int manifestType = r.serviceInfo.getForegroundServiceType(); } final int manifestType = r.serviceInfo.getForegroundServiceType(); // If passed in foreground service type is FOREGROUND_SERVICE_TYPE_MANIFEST, // consider it is the same as manifest foreground service type. if (foregroundServiceType == FOREGROUND_SERVICE_TYPE_MANIFEST) { Loading @@ -1836,11 +1844,19 @@ public final class ActiveServices { // FGS types yet. This debug flag will allow using FGS types that are // not set in the manifest. && !SystemProperties.getBoolean(prop, false)) { throw new IllegalArgumentException("foregroundServiceType " final String message = "foregroundServiceType " + String.format("0x%08X", foregroundServiceType) + " is not a subset of foregroundServiceType attribute " + String.format("0x%08X", manifestType) + " in service element of manifest file"); + " in service element of manifest file"; if (!r.appInfo.isInstantApp() || CompatChanges.isChangeEnabled(FGS_TYPE_CHECK_FOR_INSTANT_APPS, r.appInfo.uid)) { throw new IllegalArgumentException(message); } else { Slog.w(TAG, message + "\n" + "This will be an exception once the target SDK level is UDC"); } } if ((foregroundServiceType & FOREGROUND_SERVICE_TYPE_SHORT_SERVICE) != 0 && foregroundServiceType != FOREGROUND_SERVICE_TYPE_SHORT_SERVICE) { Loading @@ -1850,7 +1866,6 @@ public final class ActiveServices { // anyway, so we just remove the SHORT_SERVICE type. foregroundServiceType &= ~FOREGROUND_SERVICE_TYPE_SHORT_SERVICE; } } boolean alreadyStartedOp = false; boolean stopProcStatsOp = false; Loading Loading @@ -2981,6 +2996,20 @@ public final class ActiveServices { } } boolean shouldServiceTimeOutLocked(ComponentName className, IBinder token) { final int userId = UserHandle.getCallingUserId(); final long ident = Binder.clearCallingIdentity(); try { ServiceRecord sr = findServiceLocked(className, token, userId); if (sr == null) { return false; } return sr.shouldTriggerShortFgsTimeout(); } finally { Binder.restoreCallingIdentity(ident); } } void onShortFgsAnrTimeout(ServiceRecord sr) { final String reason = "A foreground service of FOREGROUND_SERVICE_TYPE_SHORT_SERVICE" + " did not stop within a timeout: " + sr.getComponentName(); Loading services/core/java/com/android/server/am/ActivityManagerService.java +7 −0 Original line number Diff line number Diff line Loading @@ -12982,6 +12982,13 @@ public class ActivityManagerService extends IActivityManager.Stub } } @Override public boolean shouldServiceTimeOut(ComponentName className, IBinder token) { synchronized (this) { return mServices.shouldServiceTimeOutLocked(className, token); } } @Override public int handleIncomingUser(int callingPid, int callingUid, int userId, boolean allowAll, boolean requireFull, String name, String callerPackage) { Loading
core/java/android/app/ActivityThread.java +6 −4 Original line number Diff line number Diff line Loading @@ -4763,14 +4763,16 @@ public final class ActivityThread extends ClientTransactionHandler if (s != null) { try { if (localLOGV) Slog.v(TAG, "Timeout short service " + s); s.callOnTimeout(startId); // TODO(short-service): Do we need "service executing" for timeout? // (see handleStopService()) // Unlike other service callbacks, we don't do serviceDoneExecuting() here. // "service executing" state is used to boost the procstate / oom-adj, but // for short-FGS timeout, we have a specific control for them anyway, so // we don't have to do that. s.callOnTimeout(startId); } catch (Exception e) { if (!mInstrumentation.onException(s, e)) { throw new RuntimeException( "Unable to timeout service " + s "Unable to call onTimeout on service " + s + ": " + e.toString(), e); } Slog.i(TAG, "handleTimeoutService: exception for " + token, e); Loading
core/java/android/app/IActivityManager.aidl +3 −0 Original line number Diff line number Diff line Loading @@ -799,4 +799,7 @@ interface IActivityManager { * <p>Typically used only by automotive builds when the vehicle has multiple displays. */ @nullable int[] getSecondaryDisplayIdsForStartingBackgroundUsers(); /** Returns if the service is a short-service is still "alive" and past the timeout. */ boolean shouldServiceTimeOut(in ComponentName className, in IBinder token); }
core/java/android/app/Service.java +15 −4 Original line number Diff line number Diff line Loading @@ -1119,10 +1119,21 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac /** @hide */ public final void callOnTimeout(int startId) { // TODO(short-service): Do we need any check here, to avoid races? // e.g. if the service is already stopped, but ActivityThread.handleTimeoutService() is // already scheduled, then we'll call this method anyway. It should be doable to prevent // that if we keep track of startForeground, stopForeground, and onDestroy. // Note, because all the service callbacks (and other similar callbacks, e.g. activity // callbacks) are delivered using the main handler, it's possible the service is already // stopped when before this method is called, so we do a double check here. if (mToken == null) { Log.w(TAG, "Service already destroyed, skipping onTimeout()"); return; } try { if (!mActivityManager.shouldServiceTimeOut( new ComponentName(this, mClassName), mToken)) { Log.w(TAG, "Service no longer relevant, skipping onTimeout()"); return; } } catch (RemoteException ex) { } onTimeout(startId); } Loading
services/core/java/com/android/server/am/ActiveServices.java +57 −28 Original line number Diff line number Diff line Loading @@ -132,6 +132,7 @@ import android.app.compat.CompatChanges; import android.app.usage.UsageEvents; import android.appwidget.AppWidgetManagerInternal; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledAfter; import android.compat.annotation.EnabledSince; import android.compat.annotation.Overridable; import android.content.ComponentName; Loading @@ -150,6 +151,7 @@ import android.content.pm.ServiceInfo; import android.content.pm.ServiceInfo.ForegroundServiceType; import android.os.Binder; import android.os.Build; import android.os.Build.VERSION_CODES; import android.os.Bundle; import android.os.DeadObjectException; import android.os.Handler; Loading Loading @@ -390,6 +392,14 @@ public final class ActiveServices { @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.S) static final long FGS_START_EXCEPTION_CHANGE_ID = 174041399L; /** * If enabled, the FGS type check against the manifest FSG type will be enabled for * instant apps too. Before U, this check was only done for non-instant apps. */ @ChangeId @EnabledAfter(targetSdkVersion = VERSION_CODES.TIRAMISU) static final long FGS_TYPE_CHECK_FOR_INSTANT_APPS = 261055255L; final Runnable mLastAnrDumpClearer = new Runnable() { @Override public void run() { synchronized (mAm) { Loading Loading @@ -1818,10 +1828,8 @@ public final class ActiveServices { android.Manifest.permission.FOREGROUND_SERVICE, r.app.getPid(), r.appInfo.uid, "startForeground"); } // TODO(short-service): This part really should be above the if block, // so we'll apply the same check for instant apps too. int manifestType = r.serviceInfo.getForegroundServiceType(); } final int manifestType = r.serviceInfo.getForegroundServiceType(); // If passed in foreground service type is FOREGROUND_SERVICE_TYPE_MANIFEST, // consider it is the same as manifest foreground service type. if (foregroundServiceType == FOREGROUND_SERVICE_TYPE_MANIFEST) { Loading @@ -1836,11 +1844,19 @@ public final class ActiveServices { // FGS types yet. This debug flag will allow using FGS types that are // not set in the manifest. && !SystemProperties.getBoolean(prop, false)) { throw new IllegalArgumentException("foregroundServiceType " final String message = "foregroundServiceType " + String.format("0x%08X", foregroundServiceType) + " is not a subset of foregroundServiceType attribute " + String.format("0x%08X", manifestType) + " in service element of manifest file"); + " in service element of manifest file"; if (!r.appInfo.isInstantApp() || CompatChanges.isChangeEnabled(FGS_TYPE_CHECK_FOR_INSTANT_APPS, r.appInfo.uid)) { throw new IllegalArgumentException(message); } else { Slog.w(TAG, message + "\n" + "This will be an exception once the target SDK level is UDC"); } } if ((foregroundServiceType & FOREGROUND_SERVICE_TYPE_SHORT_SERVICE) != 0 && foregroundServiceType != FOREGROUND_SERVICE_TYPE_SHORT_SERVICE) { Loading @@ -1850,7 +1866,6 @@ public final class ActiveServices { // anyway, so we just remove the SHORT_SERVICE type. foregroundServiceType &= ~FOREGROUND_SERVICE_TYPE_SHORT_SERVICE; } } boolean alreadyStartedOp = false; boolean stopProcStatsOp = false; Loading Loading @@ -2981,6 +2996,20 @@ public final class ActiveServices { } } boolean shouldServiceTimeOutLocked(ComponentName className, IBinder token) { final int userId = UserHandle.getCallingUserId(); final long ident = Binder.clearCallingIdentity(); try { ServiceRecord sr = findServiceLocked(className, token, userId); if (sr == null) { return false; } return sr.shouldTriggerShortFgsTimeout(); } finally { Binder.restoreCallingIdentity(ident); } } void onShortFgsAnrTimeout(ServiceRecord sr) { final String reason = "A foreground service of FOREGROUND_SERVICE_TYPE_SHORT_SERVICE" + " did not stop within a timeout: " + sr.getComponentName(); Loading
services/core/java/com/android/server/am/ActivityManagerService.java +7 −0 Original line number Diff line number Diff line Loading @@ -12982,6 +12982,13 @@ public class ActivityManagerService extends IActivityManager.Stub } } @Override public boolean shouldServiceTimeOut(ComponentName className, IBinder token) { synchronized (this) { return mServices.shouldServiceTimeOutLocked(className, token); } } @Override public int handleIncomingUser(int callingPid, int callingUid, int userId, boolean allowAll, boolean requireFull, String name, String callerPackage) {