diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index e001c7df506ee..a61ade08bac68 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -1104,6 +1104,11 @@ public final class ActivityThread extends ClientTransactionHandler sendMessage(H.STOP_SERVICE, token); } + @Override + public final void scheduleTimeoutService(IBinder token, int startId) { + sendMessage(H.TIMEOUT_SERVICE, token, startId); + } + @Override public final void bindApplication(String processName, ApplicationInfo appInfo, String sdkSandboxClientAppVolumeUuid, String sdkSandboxClientAppPackage, @@ -2081,6 +2086,7 @@ public final class ActivityThread extends ClientTransactionHandler public static final int SET_CONTENT_CAPTURE_OPTIONS_CALLBACK = 164; public static final int DUMP_GFXINFO = 165; public static final int DUMP_RESOURCES = 166; + public static final int TIMEOUT_SERVICE = 167; public static final int INSTRUMENT_WITHOUT_RESTART = 170; public static final int FINISH_INSTRUMENTATION_WITHOUT_RESTART = 171; @@ -2135,6 +2141,7 @@ public final class ActivityThread extends ClientTransactionHandler case FINISH_INSTRUMENTATION_WITHOUT_RESTART: return "FINISH_INSTRUMENTATION_WITHOUT_RESTART"; case DUMP_RESOURCES: return "DUMP_RESOURCES"; + case TIMEOUT_SERVICE: return "TIMEOUT_SERVICE"; } } return Integer.toString(code); @@ -2201,6 +2208,11 @@ public final class ActivityThread extends ClientTransactionHandler schedulePurgeIdler(); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break; + case TIMEOUT_SERVICE: + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceTimeout"); + handleTimeoutService((IBinder) msg.obj, msg.arg1); + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + break; case CONFIGURATION_CHANGED: mConfigurationController.handleConfigurationChanged((Configuration) msg.obj); break; @@ -4726,6 +4738,27 @@ public final class ActivityThread extends ClientTransactionHandler //Slog.i(TAG, "Running services: " + mServices); } + private void handleTimeoutService(IBinder token, int startId) { + Service s = mServices.get(token); + 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()) + } catch (Exception e) { + if (!mInstrumentation.onException(s, e)) { + throw new RuntimeException( + "Unable to timeout service " + s + + ": " + e.toString(), e); + } + Slog.i(TAG, "handleTimeoutService: exception for " + token, e); + } + } else { + Slog.wtf(TAG, "handleTimeoutService: token=" + token + " not found."); + } + } /** * Resume the activity. * @param r Target activity record. diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl index 843b6846d0533..595c7f7ab0085 100644 --- a/core/java/android/app/IApplicationThread.aidl +++ b/core/java/android/app/IApplicationThread.aidl @@ -165,4 +165,5 @@ oneway interface IApplicationThread { void updateUiTranslationState(IBinder activityToken, int state, in TranslationSpec sourceSpec, in TranslationSpec targetSpec, in List viewIds, in UiTranslationSpec uiTranslationSpec); + void scheduleTimeoutService(IBinder token, int startId); } diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java index 754e3b628f792..291edbb74c35b 100644 --- a/core/java/android/app/Service.java +++ b/core/java/android/app/Service.java @@ -1107,6 +1107,15 @@ 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. + onTimeout(startId); + } + /** * Callback called on timeout for {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_SHORT_SERVICE}. * diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index 8b5b795b8ff54..046403d409af9 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -956,6 +956,20 @@ final class ActivityManagerConstants extends ContentObserver { /** @see #KEY_SHORT_FGS_TIMEOUT_DURATION */ public static volatile long mShortFgsTimeoutDuration = DEFAULT_SHORT_FGS_TIMEOUT_DURATION; + /** + * If a "short service" doesn't finish within this after the timeout ( + * {@link #KEY_SHORT_FGS_TIMEOUT_DURATION}), then we'll lower the procstate. + */ + private static final String KEY_SHORT_FGS_PROC_STATE_EXTRA_WAIT_DURATION = + "short_fgs_proc_state_extra_wait_duration"; + + /** @see #KEY_SHORT_FGS_PROC_STATE_EXTRA_WAIT_DURATION */ + static final long DEFAULT_SHORT_FGS_PROC_STATE_EXTRA_WAIT_DURATION = 5_000; + + /** @see #KEY_SHORT_FGS_PROC_STATE_EXTRA_WAIT_DURATION */ + public static volatile long mShortFgsProcStateExtraWaitDuration = + DEFAULT_SHORT_FGS_PROC_STATE_EXTRA_WAIT_DURATION; + /** * If a "short service" doesn't finish within this after the timeout ( * {@link #KEY_SHORT_FGS_TIMEOUT_DURATION}), then we'll declare an ANR. @@ -966,7 +980,7 @@ final class ActivityManagerConstants extends ContentObserver { "short_fgs_anr_extra_wait_duration"; /** @see #KEY_SHORT_FGS_ANR_EXTRA_WAIT_DURATION */ - static final long DEFAULT_SHORT_FGS_ANR_EXTRA_WAIT_DURATION = 5_000; + static final long DEFAULT_SHORT_FGS_ANR_EXTRA_WAIT_DURATION = 10_000; /** @see #KEY_SHORT_FGS_ANR_EXTRA_WAIT_DURATION */ public static volatile long mShortFgsAnrExtraWaitDuration = @@ -1116,6 +1130,9 @@ final class ActivityManagerConstants extends ContentObserver { case KEY_SHORT_FGS_TIMEOUT_DURATION: updateShortFgsTimeoutDuration(); break; + case KEY_SHORT_FGS_PROC_STATE_EXTRA_WAIT_DURATION: + updateShortFgsProcStateExtraWaitDuration(); + break; case KEY_SHORT_FGS_ANR_EXTRA_WAIT_DURATION: updateShortFgsAnrExtraWaitDuration(); break; @@ -1828,6 +1845,13 @@ final class ActivityManagerConstants extends ContentObserver { DEFAULT_SHORT_FGS_TIMEOUT_DURATION); } + private void updateShortFgsProcStateExtraWaitDuration() { + mShortFgsProcStateExtraWaitDuration = DeviceConfig.getLong( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_SHORT_FGS_PROC_STATE_EXTRA_WAIT_DURATION, + DEFAULT_SHORT_FGS_PROC_STATE_EXTRA_WAIT_DURATION); + } + private void updateShortFgsAnrExtraWaitDuration() { mShortFgsAnrExtraWaitDuration = DeviceConfig.getLong( DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, @@ -2009,6 +2033,8 @@ final class ActivityManagerConstants extends ContentObserver { pw.print(" "); pw.print(KEY_SHORT_FGS_TIMEOUT_DURATION); pw.print("="); pw.println(mShortFgsTimeoutDuration); + pw.print(" "); pw.print(KEY_SHORT_FGS_PROC_STATE_EXTRA_WAIT_DURATION); + pw.print("="); pw.println(mShortFgsProcStateExtraWaitDuration); pw.print(" "); pw.print(KEY_SHORT_FGS_ANR_EXTRA_WAIT_DURATION); pw.print("="); pw.println(mShortFgsAnrExtraWaitDuration);