From 08992ac57e973d6bf32693725ebb341a481e5944 Mon Sep 17 00:00:00 2001 From: Christopher Tate Date: Tue, 21 Mar 2017 11:37:06 -0700 Subject: [PATCH] API refactor: context.startForegroundService() Rather than require an a-priori Notification be supplied in order to start a service directly into the foreground state, we adopt a two-stage compound operation for undertaking ongoing service work even from a background execution state. Context#startForegroundService() is not subject to background restrictions, with the requirement that the service formally enter the foreground state via startForeground() within 5 seconds. If the service does not do so, it is stopped by the OS and the app is blamed with a service ANR. We also introduce a new flavor of PendingIntent that starts a service into this two-stage "promises to call startForeground()" sequence, so that deferred and second-party launches can take advantage of it. Bug 36130212 Test: CTS Change-Id: I96d6b23fcfc27d8fa606827b7d48a093611b2345 (cherry picked from commit 79047c62b58fb0a0ddf28e2b90fe4d17e05bc528) --- api/current.txt | 6 +- api/system-current.txt | 6 +- api/test-current.txt | 6 +- core/java/android/app/ActivityManager.java | 7 ++ core/java/android/app/ContextImpl.java | 27 +++-- core/java/android/app/IActivityManager.aidl | 2 +- .../java/android/app/NotificationManager.java | 3 + core/java/android/app/PendingIntent.java | 38 ++++++- core/java/android/content/Context.java | 44 +++++++- core/java/android/content/ContextWrapper.java | 13 ++- .../android/view/ContextThemeWrapper.java | 4 +- .../com/android/server/am/ActiveServices.java | 105 ++++++++++++++---- .../server/am/ActivityManagerService.java | 16 ++- .../am/ActivityManagerShellCommand.java | 2 +- .../server/am/PendingIntentRecord.java | 8 +- .../com/android/server/am/ServiceRecord.java | 2 + .../server/pm/PackageManagerService.java | 2 +- .../src/android/test/mock/MockContext.java | 12 ++ .../bridge/android/BridgeContext.java | 12 ++ 19 files changed, 265 insertions(+), 50 deletions(-) diff --git a/api/current.txt b/api/current.txt index e1ca18c8cbbe0..b51c2d33c9e32 100644 --- a/api/current.txt +++ b/api/current.txt @@ -5586,7 +5586,7 @@ package android.app { method public boolean removeAutomaticZenRule(java.lang.String); method public final void setInterruptionFilter(int); method public void setNotificationPolicy(android.app.NotificationManager.Policy); - method public android.content.ComponentName startServiceInForeground(android.content.Intent, int, android.app.Notification); + method public deprecated android.content.ComponentName startServiceInForeground(android.content.Intent, int, android.app.Notification); method public boolean updateAutomaticZenRule(java.lang.String, android.app.AutomaticZenRule); field public static final java.lang.String ACTION_INTERRUPTION_FILTER_CHANGED = "android.app.action.INTERRUPTION_FILTER_CHANGED"; field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED = "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED"; @@ -5641,6 +5641,7 @@ package android.app { method public java.lang.String getCreatorPackage(); method public int getCreatorUid(); method public android.os.UserHandle getCreatorUserHandle(); + method public static android.app.PendingIntent getForegroundService(android.content.Context, int, android.content.Intent, int); method public android.content.IntentSender getIntentSender(); method public static android.app.PendingIntent getService(android.content.Context, int, android.content.Intent, int); method public deprecated java.lang.String getTargetPackage(); @@ -8858,6 +8859,7 @@ package android.content { method public abstract void startActivities(android.content.Intent[], android.os.Bundle); method public abstract void startActivity(android.content.Intent); method public abstract void startActivity(android.content.Intent, android.os.Bundle); + method public abstract android.content.ComponentName startForegroundService(android.content.Intent); method public abstract boolean startInstrumentation(android.content.ComponentName, java.lang.String, android.os.Bundle); method public abstract void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException; method public abstract void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException; @@ -9049,6 +9051,7 @@ package android.content { method public void startActivities(android.content.Intent[], android.os.Bundle); method public void startActivity(android.content.Intent); method public void startActivity(android.content.Intent, android.os.Bundle); + method public android.content.ComponentName startForegroundService(android.content.Intent); method public boolean startInstrumentation(android.content.ComponentName, java.lang.String, android.os.Bundle); method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException; method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException; @@ -40679,6 +40682,7 @@ package android.test.mock { method public void startActivities(android.content.Intent[], android.os.Bundle); method public void startActivity(android.content.Intent); method public void startActivity(android.content.Intent, android.os.Bundle); + method public android.content.ComponentName startForegroundService(android.content.Intent); method public boolean startInstrumentation(android.content.ComponentName, java.lang.String, android.os.Bundle); method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException; method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException; diff --git a/api/system-current.txt b/api/system-current.txt index 92e2a50b40d52..9e8b67c573b67 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -5780,7 +5780,7 @@ package android.app { method public boolean removeAutomaticZenRule(java.lang.String); method public final void setInterruptionFilter(int); method public void setNotificationPolicy(android.app.NotificationManager.Policy); - method public android.content.ComponentName startServiceInForeground(android.content.Intent, int, android.app.Notification); + method public deprecated android.content.ComponentName startServiceInForeground(android.content.Intent, int, android.app.Notification); method public boolean updateAutomaticZenRule(java.lang.String, android.app.AutomaticZenRule); field public static final java.lang.String ACTION_INTERRUPTION_FILTER_CHANGED = "android.app.action.INTERRUPTION_FILTER_CHANGED"; field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED = "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED"; @@ -5835,6 +5835,7 @@ package android.app { method public java.lang.String getCreatorPackage(); method public int getCreatorUid(); method public android.os.UserHandle getCreatorUserHandle(); + method public static android.app.PendingIntent getForegroundService(android.content.Context, int, android.content.Intent, int); method public android.content.IntentSender getIntentSender(); method public static android.app.PendingIntent getService(android.content.Context, int, android.content.Intent, int); method public deprecated java.lang.String getTargetPackage(); @@ -9353,6 +9354,7 @@ package android.content { method public abstract void startActivities(android.content.Intent[], android.os.Bundle); method public abstract void startActivity(android.content.Intent); method public abstract void startActivity(android.content.Intent, android.os.Bundle); + method public abstract android.content.ComponentName startForegroundService(android.content.Intent); method public abstract boolean startInstrumentation(android.content.ComponentName, java.lang.String, android.os.Bundle); method public abstract void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException; method public abstract void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException; @@ -9558,6 +9560,7 @@ package android.content { method public void startActivities(android.content.Intent[], android.os.Bundle); method public void startActivity(android.content.Intent); method public void startActivity(android.content.Intent, android.os.Bundle); + method public android.content.ComponentName startForegroundService(android.content.Intent); method public boolean startInstrumentation(android.content.ComponentName, java.lang.String, android.os.Bundle); method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException; method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException; @@ -44117,6 +44120,7 @@ package android.test.mock { method public void startActivities(android.content.Intent[], android.os.Bundle); method public void startActivity(android.content.Intent); method public void startActivity(android.content.Intent, android.os.Bundle); + method public android.content.ComponentName startForegroundService(android.content.Intent); method public boolean startInstrumentation(android.content.ComponentName, java.lang.String, android.os.Bundle); method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException; method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException; diff --git a/api/test-current.txt b/api/test-current.txt index 3d184b23004ca..3bf9dc89dce7a 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -5599,7 +5599,7 @@ package android.app { method public boolean removeAutomaticZenRule(java.lang.String); method public final void setInterruptionFilter(int); method public void setNotificationPolicy(android.app.NotificationManager.Policy); - method public android.content.ComponentName startServiceInForeground(android.content.Intent, int, android.app.Notification); + method public deprecated android.content.ComponentName startServiceInForeground(android.content.Intent, int, android.app.Notification); method public boolean updateAutomaticZenRule(java.lang.String, android.app.AutomaticZenRule); field public static final java.lang.String ACTION_INTERRUPTION_FILTER_CHANGED = "android.app.action.INTERRUPTION_FILTER_CHANGED"; field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED = "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED"; @@ -5654,6 +5654,7 @@ package android.app { method public java.lang.String getCreatorPackage(); method public int getCreatorUid(); method public android.os.UserHandle getCreatorUserHandle(); + method public static android.app.PendingIntent getForegroundService(android.content.Context, int, android.content.Intent, int); method public android.content.IntentSender getIntentSender(); method public static android.app.PendingIntent getService(android.content.Context, int, android.content.Intent, int); method public deprecated java.lang.String getTargetPackage(); @@ -8890,6 +8891,7 @@ package android.content { method public abstract void startActivities(android.content.Intent[], android.os.Bundle); method public abstract void startActivity(android.content.Intent); method public abstract void startActivity(android.content.Intent, android.os.Bundle); + method public abstract android.content.ComponentName startForegroundService(android.content.Intent); method public abstract boolean startInstrumentation(android.content.ComponentName, java.lang.String, android.os.Bundle); method public abstract void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException; method public abstract void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException; @@ -9082,6 +9084,7 @@ package android.content { method public void startActivities(android.content.Intent[], android.os.Bundle); method public void startActivity(android.content.Intent); method public void startActivity(android.content.Intent, android.os.Bundle); + method public android.content.ComponentName startForegroundService(android.content.Intent); method public boolean startInstrumentation(android.content.ComponentName, java.lang.String, android.os.Bundle); method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException; method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException; @@ -40883,6 +40886,7 @@ package android.test.mock { method public void startActivities(android.content.Intent[], android.os.Bundle); method public void startActivity(android.content.Intent); method public void startActivity(android.content.Intent, android.os.Bundle); + method public android.content.ComponentName startForegroundService(android.content.Intent); method public boolean startInstrumentation(android.content.ComponentName, java.lang.String, android.os.Bundle); method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException; method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException; diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 9f2f669b64c8f..4004bd6686b1f 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -405,6 +405,13 @@ public class ActivityManager { */ public static final int INTENT_SENDER_SERVICE = 4; + /** + * Type for IActivityManaqer.getIntentSender: this PendingIntent is + * for a startForegroundService operation. + * @hide + */ + public static final int INTENT_SENDER_FOREGROUND_SERVICE = 5; + /** @hide User operation call: success! */ public static final int USER_OP_SUCCESS = 0; diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 4c080c9e6a953..467ba996ad4f9 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -1447,14 +1447,21 @@ class ContextImpl extends Context { @Override public ComponentName startService(Intent service) { warnIfCallingFromSystemProcess(); - return startServiceCommon(service, -1, null, mUser); + return startServiceCommon(service, -1, null, false, mUser); } + @Override + public ComponentName startForegroundService(Intent service) { + warnIfCallingFromSystemProcess(); + return startServiceCommon(service, -1, null, true, mUser); + } + + // STOPSHIP: remove when NotificationManager.startServiceInForeground() is retired @Override public ComponentName startServiceInForeground(Intent service, int id, Notification notification) { warnIfCallingFromSystemProcess(); - return startServiceCommon(service, id, notification, mUser); + return startServiceCommon(service, id, notification, false, mUser); } @Override @@ -1465,24 +1472,30 @@ class ContextImpl extends Context { @Override public ComponentName startServiceAsUser(Intent service, UserHandle user) { - return startServiceCommon(service, -1, null, user); + return startServiceCommon(service, -1, null, false, user); } + @Override + public ComponentName startForegroundServiceAsUser(Intent service, UserHandle user) { + return startServiceCommon(service, -1, null, true, user); + } + + // STOPSHIP: remove when NotificationManager.startServiceInForeground() is retired @Override public ComponentName startServiceInForegroundAsUser(Intent service, int id, Notification notification, UserHandle user) { - return startServiceCommon(service, id, notification, user); + return startServiceCommon(service, id, notification, false, user); } private ComponentName startServiceCommon(Intent service, int id, Notification notification, - UserHandle user) { + boolean requireForeground, UserHandle user) { try { validateServiceIntent(service); service.prepareToLeaveProcess(this); ComponentName cn = ActivityManager.getService().startService( mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded( - getContentResolver()), id, notification, getOpPackageName(), - user.getIdentifier()); + getContentResolver()), id, notification, requireForeground, + getOpPackageName(), user.getIdentifier()); if (cn != null) { if (cn.getPackageName().equals("!")) { throw new SecurityException( diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index b9d1d91fabdaf..0a5e4bef8dca0 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -130,7 +130,7 @@ interface IActivityManager { PendingIntent getRunningServiceControlPanel(in ComponentName service); ComponentName startService(in IApplicationThread caller, in Intent service, in String resolvedType, int id, in Notification notification, - in String callingPackage, int userId); + boolean requireForeground, in String callingPackage, int userId); int stopService(in IApplicationThread caller, in Intent service, in String resolvedType, int userId); int bindService(in IApplicationThread caller, in IBinder token, in Intent service, diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 75998f2eb36d3..72c59781ae801 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -1171,8 +1171,11 @@ public class NotificationManager * @return If the service is being started or is already running, the * {@link ComponentName} of the actual service that was started is * returned; else if the service does not exist null is returned. + * + * @deprecated STOPSHIP transition away from this for O */ @Nullable + @Deprecated public ComponentName startServiceInForeground(Intent service, int id, Notification notification) { return mContext.startServiceInForeground(service, id, notification); diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java index 7d1a16ab1045b..dc432afb5c06d 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -596,6 +596,42 @@ public final class PendingIntent implements Parcelable { */ public static PendingIntent getService(Context context, int requestCode, @NonNull Intent intent, @Flags int flags) { + return buildServicePendingIntent(context, requestCode, intent, flags, + ActivityManager.INTENT_SENDER_SERVICE); + } + + /** + * Retrieve a PendingIntent that will start a foreground service, like calling + * {@link Context#startService Context.startForegroundService()}. The start + * arguments given to the service will come from the extras of the Intent. + * + *

For security reasons, the {@link android.content.Intent} + * you supply here should almost always be an explicit intent, + * that is specify an explicit component to be delivered to through + * {@link Intent#setClass(android.content.Context, Class) Intent.setClass}

+ * + * @param context The Context in which this PendingIntent should start + * the service. + * @param requestCode Private request code for the sender + * @param intent An Intent describing the service to be started. + * @param flags May be {@link #FLAG_ONE_SHOT}, {@link #FLAG_NO_CREATE}, + * {@link #FLAG_CANCEL_CURRENT}, {@link #FLAG_UPDATE_CURRENT}, + * {@link #FLAG_IMMUTABLE} or any of the flags as supported by + * {@link Intent#fillIn Intent.fillIn()} to control which unspecified parts + * of the intent that can be supplied when the actual send happens. + * + * @return Returns an existing or new PendingIntent matching the given + * parameters. May return null only if {@link #FLAG_NO_CREATE} has been + * supplied. + */ + public static PendingIntent getForegroundService(Context context, int requestCode, + @NonNull Intent intent, @Flags int flags) { + return buildServicePendingIntent(context, requestCode, intent, flags, + ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE); + } + + private static PendingIntent buildServicePendingIntent(Context context, int requestCode, + Intent intent, int flags, int serviceKind) { String packageName = context.getPackageName(); String resolvedType = intent != null ? intent.resolveTypeIfNeeded( context.getContentResolver()) : null; @@ -603,7 +639,7 @@ public final class PendingIntent implements Parcelable { intent.prepareToLeaveProcess(context); IIntentSender target = ActivityManager.getService().getIntentSender( - ActivityManager.INTENT_SENDER_SERVICE, packageName, + serviceKind, packageName, null, null, requestCode, new Intent[] { intent }, resolvedType != null ? new String[] { resolvedType } : null, flags, null, UserHandle.myUserId()); diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 3a8a4206e105b..1803bbe246104 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -2576,7 +2576,7 @@ public abstract class Context { * {@link ComponentName} of the actual service that was started is * returned; else if the service does not exist null is returned. * - * @throws SecurityException If the caller does not permission to access the service + * @throws SecurityException If the caller does not have permission to access the service * or the service can not be found. * @throws IllegalStateException If the application is in a state where the service * can not be started (such as not in the foreground in a state when services are allowed). @@ -2587,12 +2587,48 @@ public abstract class Context { @Nullable public abstract ComponentName startService(Intent service); + /** + * Similar to {@link #startService(Intent)}, but with an implicit promise that the + * Service will call {@link android.app.Service#startForeground(int, Notification) + * startForeground(int, Notification)} once it begins running. The service is given + * an amount of time comparable to the ANR interval to do this, otherwise the system + * will automatically stop the service and declare the app ANR. + * + *

Unlike the ordinary {@link #startService(Intent)}, this method can be used + * at any time, regardless of whether the app hosting the service is in a foreground + * state. + * + * @param service Identifies the service to be started. The Intent must be + * fully explicit (supplying a component name). Additional values + * may be included in the Intent extras to supply arguments along with + * this specific start call. + * + * @return If the service is being started or is already running, the + * {@link ComponentName} of the actual service that was started is + * returned; else if the service does not exist null is returned. + * + * @throws SecurityException If the caller does not have permission to access the service + * or the service can not be found. + * + * @see #stopService + * @see android.app.Service#startForeground(int, Notification) + */ + @Nullable + public abstract ComponentName startForegroundService(Intent service); + + /** + * @hide like {@link #startForegroundService(Intent)} but for a specific user. + */ + @Nullable + public abstract ComponentName startForegroundServiceAsUser(Intent service, UserHandle user); + /** * Start a service directly into the "foreground service" state. Unlike {@link #startService}, * this method can be used from within background operations like broadcast receivers * or scheduled jobs. The API entry point for this is in NotificationManager in order to * preserve appropriate public package layering. * @hide + * @deprecated STOPSHIP remove in favor of two-step startForegroundService() + startForeground() */ @Nullable public abstract ComponentName startServiceInForeground(Intent service, @@ -2620,7 +2656,7 @@ public abstract class Context { * @return If there is a service matching the given Intent that is already * running, then it is stopped and {@code true} is returned; else {@code false} is returned. * - * @throws SecurityException If the caller does not permission to access the service + * @throws SecurityException If the caller does not have permission to access the service * or the service can not be found. * @throws IllegalStateException If the application is in a state where the service * can not be started (such as not in the foreground in a state when services are allowed). @@ -2638,7 +2674,9 @@ public abstract class Context { /** * @hide like {@link #startServiceInForeground(Intent, int, Notification)} * but for a specific user. + * @deprecated STOPSHIP remove when trial API is turned off */ + @Deprecated @Nullable public abstract ComponentName startServiceInForegroundAsUser(Intent service, int id, Notification notification, UserHandle user); @@ -2685,7 +2723,7 @@ public abstract class Context { * {@code false} is returned if the connection is not made so you will not * receive the service object. * - * @throws SecurityException If the caller does not permission to access the service + * @throws SecurityException If the caller does not have permission to access the service * or the service can not be found. * * @see #unbindService diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index 6b0bbfaedc3eb..75784a69c74c6 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -644,7 +644,12 @@ public class ContextWrapper extends Context { return mBase.startService(service); } - /** @hide */ + @Override + public ComponentName startForegroundService(Intent service) { + return mBase.startForegroundService(service); + } + + /** @hide STOPSHIP remove when trial API is turned down */ @Override public ComponentName startServiceInForeground(Intent service, int id, Notification notification) { @@ -664,6 +669,12 @@ public class ContextWrapper extends Context { /** @hide */ @Override + public ComponentName startForegroundServiceAsUser(Intent service, UserHandle user) { + return mBase.startForegroundServiceAsUser(service, user); + } + + /** @hide STOPSHIP removed when trial API is turned down */ + @Override public ComponentName startServiceInForegroundAsUser(Intent service, int id, Notification notification, UserHandle user) { return mBase.startServiceInForegroundAsUser(service, id, notification, user); diff --git a/core/java/android/view/ContextThemeWrapper.java b/core/java/android/view/ContextThemeWrapper.java index 86318e91b8854..d3cc175d3e1c4 100644 --- a/core/java/android/view/ContextThemeWrapper.java +++ b/core/java/android/view/ContextThemeWrapper.java @@ -36,8 +36,8 @@ public class ContextThemeWrapper extends ContextWrapper { /** * Creates a new context wrapper with no theme and no base context. - *

- * Note: A base context must be attached + *

+ * Note: A base context must be attached * using {@link #attachBaseContext(Context)} before calling any other * method on the newly constructed context wrapper. */ diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index b4f8f61971f89..8b0665c6d3121 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -17,7 +17,6 @@ package com.android.server.am; import static android.content.pm.PackageManager.PERMISSION_GRANTED; -import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; import static com.android.server.am.ActivityManagerDebugConfig.*; import java.io.FileDescriptor; @@ -96,6 +95,10 @@ public final class ActiveServices { // How long we wait for a service to finish executing. static final int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10; + // How long the startForegroundService() grace period is to get around to + // calling startForeground() before we ANR + stop it. + static final int SERVICE_START_FOREGROUND_TIMEOUT = 5*1000; + // How long a service needs to be running until restarting its process // is no longer considered to be a relaunch of the service. static final int SERVICE_RESTART_DURATION = 1*1000; @@ -307,8 +310,8 @@ public final class ActiveServices { } ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType, - int id, Notification notification, - int callingPid, int callingUid, String callingPackage, final int userId) + int id, Notification notification, int callingPid, int callingUid, + boolean fgRequired, String callingPackage, final int userId) throws TransactionTooLargeException { if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "startService: " + service + " type=" + resolvedType + " args=" + service.getExtras()); @@ -345,8 +348,9 @@ public final class ActiveServices { return null; } - // Non-null notification means this is a start directly into the foreground - if (!r.startRequested && notification == null) { + // If this isn't a direct-to-foreground start, check our ability to kick off an + // arbitrary service + if (!r.startRequested && !fgRequired) { final long token = Binder.clearCallingIdentity(); try { // Before going further -- if this app is not allowed to start services in the @@ -392,12 +396,13 @@ public final class ActiveServices { r.lastActivity = SystemClock.uptimeMillis(); r.startRequested = true; r.delayedStop = false; + r.fgRequired = fgRequired; r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(), service, neededGrants)); final ServiceMap smap = getServiceMapLocked(r.userId); boolean addToStarting = false; - if (!callerFg && r.app == null + if (!callerFg && !fgRequired && r.app == null && mAm.mUserController.hasStartedUserState(r.userId)) { ProcessRecord proc = mAm.getProcessRecordLocked(r.processName, r.appInfo.uid, false); if (proc == null || proc.curProcState > ActivityManager.PROCESS_STATE_RECEIVER) { @@ -449,9 +454,9 @@ public final class ActiveServices { Slog.v(TAG_SERVICE, sb.toString()); } } else if (DEBUG_DELAYED_STARTS) { - if (callerFg) { + if (callerFg || fgRequired) { Slog.v(TAG_SERVICE, "Not potential delay (callerFg=" + callerFg + " uid=" - + callingUid + " pid=" + callingPid + "): " + r); + + callingUid + " pid=" + callingPid + " fgRequired=" + fgRequired + "): " + r); } else if (r.app != null) { Slog.v(TAG_SERVICE, "Not potential delay (cur app=" + r.app + "): " + r); } else { @@ -461,6 +466,7 @@ public final class ActiveServices { } ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting); + // STOPSHIP deprecated; remove when NotificationManager.startServiceInForeground is retired if (notification != null) { setServiceForegroundInnerLocked(r, id, notification, 0); } @@ -540,7 +546,7 @@ public final class ActiveServices { if (first) { smap.rescheduleDelayedStartsLocked(); } - } else if (callerFg) { + } else if (callerFg || r.fgRequired) { smap.ensureNotStartingBackgroundLocked(r); } @@ -756,8 +762,17 @@ public final class ActiveServices { } } } + if (r.fgRequired) { + if (DEBUG_BACKGROUND_CHECK) { + Slog.i(TAG, "Service called startForeground() as required: " + r); + } + r.fgRequired = false; + r.fgWaiting = false; + mAm.mHandler.removeMessages( + ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG, r); + } if (r.foregroundId != id) { - cancelForegroudNotificationLocked(r); + cancelForegroundNotificationLocked(r); r.foregroundId = id; } notification.flags |= Notification.FLAG_FOREGROUND_SERVICE; @@ -779,7 +794,7 @@ public final class ActiveServices { } } if ((flags & Service.STOP_FOREGROUND_REMOVE) != 0) { - cancelForegroudNotificationLocked(r); + cancelForegroundNotificationLocked(r); r.foregroundId = 0; r.foregroundNoti = null; } else if (r.appInfo.targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) { @@ -792,7 +807,7 @@ public final class ActiveServices { } } - private void cancelForegroudNotificationLocked(ServiceRecord r) { + private void cancelForegroundNotificationLocked(ServiceRecord r) { if (r.foregroundId != 0) { // First check to see if this app has any other active foreground services // with the same notification ID. If so, we shouldn't actually cancel it, @@ -1631,7 +1646,7 @@ public final class ActiveServices { r.makeRestarting(mAm.mProcessStats.getMemFactorLocked(), now); } - cancelForegroudNotificationLocked(r); + cancelForegroundNotificationLocked(r); mAm.mHandler.removeCallbacks(r.restarter); mAm.mHandler.postAtTime(r.restarter, r.nextRestartTime); @@ -1718,7 +1733,9 @@ public final class ActiveServices { return null; } - if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Bringing up " + r + " " + r.intent); + if (DEBUG_SERVICE) { + Slog.v(TAG_SERVICE, "Bringing up " + r + " " + r.intent + " fg=" + r.fgRequired); + } // We are now bringing the service up, so no longer in the // restarting state. @@ -1944,8 +1961,10 @@ public final class ActiveServices { ServiceRecord.StartItem si = null; try { si = r.pendingStarts.remove(0); - if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Sending arguments to: " - + r + " " + r.intent + " args=" + si.intent); + if (DEBUG_SERVICE) { + Slog.v(TAG_SERVICE, "Sending arguments to: " + + r + " " + r.intent + " args=" + si.intent); + } if (si.intent == null && N > 1) { // If somehow we got a dummy null intent in the middle, // then skip it. DO NOT skip a null intent when it is @@ -1966,6 +1985,19 @@ public final class ActiveServices { oomAdjusted = true; mAm.updateOomAdjLocked(r.app); } + if (r.fgRequired && !r.fgWaiting) { + if (!r.isForeground) { + if (DEBUG_BACKGROUND_CHECK) { + Slog.i(TAG, "Launched service must call startForeground() within timeout: " + r); + } + scheduleServiceForegroundTransitionTimeoutLocked(r); + } else { + if (DEBUG_BACKGROUND_CHECK) { + Slog.i(TAG, "Service already foreground; no new timeout: " + r); + } + r.fgRequired = false; + } + } int flags = 0; if (si.deliveryCount > 1) { flags |= Service.START_FLAG_RETRY; @@ -2101,7 +2133,7 @@ public final class ActiveServices { } } - cancelForegroudNotificationLocked(r); + cancelForegroundNotificationLocked(r); r.isForeground = false; r.foregroundId = 0; r.foregroundNoti = null; @@ -2925,23 +2957,53 @@ public final class ActiveServices { } } + void serviceForegroundTimeout(ServiceRecord r) { + ProcessRecord app; + synchronized (mAm) { + if (!r.fgRequired) { + return; + } + + if (DEBUG_BACKGROUND_CHECK) { + Slog.i(TAG, "Service foreground-required timeout for " + r); + } + app = r.app; + r.fgWaiting = false; + stopServiceLocked(r); + } + + if (app != null) { + mAm.mAppErrors.appNotResponding(app, null, null, false, + "Context.startForegroundService() did not then call Service.startForeground()"); + } + } + void scheduleServiceTimeoutLocked(ProcessRecord proc) { if (proc.executingServices.size() == 0 || proc.thread == null) { return; } - long now = SystemClock.uptimeMillis(); Message msg = mAm.mHandler.obtainMessage( ActivityManagerService.SERVICE_TIMEOUT_MSG); msg.obj = proc; - mAm.mHandler.sendMessageAtTime(msg, - proc.execServicesFg ? (now+SERVICE_TIMEOUT) : (now+ SERVICE_BACKGROUND_TIMEOUT)); + mAm.mHandler.sendMessageDelayed(msg, + proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT); + } + + void scheduleServiceForegroundTransitionTimeoutLocked(ServiceRecord r) { + if (r.app.executingServices.size() == 0 || r.app.thread == null) { + return; + } + Message msg = mAm.mHandler.obtainMessage( + ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG); + msg.obj = r; + r.fgWaiting = true; + mAm.mHandler.sendMessageDelayed(msg, SERVICE_START_FOREGROUND_TIMEOUT); } final class ServiceDumper { private final FileDescriptor fd; private final PrintWriter pw; private final String[] args; - private final int opti; private final boolean dumpAll; private final String dumpPackage; private final ItemMatcher matcher; @@ -2962,7 +3024,6 @@ public final class ActiveServices { this.fd = fd; this.pw = pw; this.args = args; - this.opti = opti; this.dumpAll = dumpAll; this.dumpPackage = dumpPackage; matcher = new ItemMatcher(); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index f602e43fe1e85..b4da152a18ae1 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -1726,6 +1726,7 @@ public class ActivityManagerService extends IActivityManager.Stub static final int HANDLE_TRUST_STORAGE_UPDATE_MSG = 63; static final int REPORT_LOCKED_BOOT_COMPLETE_MSG = 64; static final int NOTIFY_VR_SLEEPING_MSG = 65; + static final int SERVICE_FOREGROUND_TIMEOUT_MSG = 66; static final int START_USER_SWITCH_FG_MSG = 712; static final int FIRST_ACTIVITY_STACK_MSG = 100; @@ -1991,6 +1992,9 @@ public class ActivityManagerService extends IActivityManager.Stub } mServices.serviceTimeout((ProcessRecord)msg.obj); } break; + case SERVICE_FOREGROUND_TIMEOUT_MSG: { + mServices.serviceForegroundTimeout((ServiceRecord)msg.obj); + } break; case UPDATE_TIME_ZONE: { synchronized (ActivityManagerService.this) { for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) { @@ -17898,7 +17902,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public ComponentName startService(IApplicationThread caller, Intent service, - String resolvedType, int id, Notification notification, + String resolvedType, int id, Notification notification, boolean requireForeground, String callingPackage, int userId) throws TransactionTooLargeException { enforceNotIsolatedCaller("startService"); @@ -17912,28 +17916,28 @@ public class ActivityManagerService extends IActivityManager.Stub } if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, - "startService: " + service + " type=" + resolvedType); + "*** startService: " + service + " type=" + resolvedType + " fg=" + requireForeground); synchronized(this) { final int callingPid = Binder.getCallingPid(); final int callingUid = Binder.getCallingUid(); final long origId = Binder.clearCallingIdentity(); ComponentName res = mServices.startServiceLocked(caller, service, - resolvedType, id, notification, - callingPid, callingUid, callingPackage, userId); + resolvedType, id, notification, callingPid, callingUid, + requireForeground, callingPackage, userId); Binder.restoreCallingIdentity(origId); return res; } } ComponentName startServiceInPackage(int uid, Intent service, String resolvedType, - String callingPackage, int userId) + boolean fgRequired, String callingPackage, int userId) throws TransactionTooLargeException { synchronized(this) { if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "startServiceInPackage: " + service + " type=" + resolvedType); final long origId = Binder.clearCallingIdentity(); ComponentName res = mServices.startServiceLocked(null, service, - resolvedType, 0, null, -1, uid, callingPackage, userId); + resolvedType, 0, null, -1, uid, fgRequired, callingPackage, userId); Binder.restoreCallingIdentity(origId); return res; } diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index 9b6d13a5d8a0e..a9bd872279293 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -519,7 +519,7 @@ final class ActivityManagerShellCommand extends ShellCommand { pw.println("Starting service: " + intent); pw.flush(); ComponentName cn = mInterface.startService(null, intent, intent.getType(), - -1, null, SHELL_PACKAGE_NAME, mUserId); + -1, null, false, SHELL_PACKAGE_NAME, mUserId); if (cn == null) { err.println("Error: Not found; no service started."); return -1; diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java index c494171fbfbbd..f05bfb6174d3f 100644 --- a/services/core/java/com/android/server/am/PendingIntentRecord.java +++ b/services/core/java/com/android/server/am/PendingIntentRecord.java @@ -175,6 +175,8 @@ final class PendingIntentRecord extends IIntentSender.Stub { return "broadcastIntent"; case ActivityManager.INTENT_SENDER_SERVICE: return "startService"; + case ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE: + return "startForegroundService"; case ActivityManager.INTENT_SENDER_ACTIVITY_RESULT: return "activityResult"; } @@ -318,9 +320,11 @@ final class PendingIntentRecord extends IIntentSender.Stub { } break; case ActivityManager.INTENT_SENDER_SERVICE: + case ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE: try { - owner.startServiceInPackage(uid, finalIntent, - resolvedType, key.packageName, userId); + owner.startServiceInPackage(uid, finalIntent, resolvedType, + key.type == ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE, + key.packageName, userId); } catch (RuntimeException e) { Slog.w(TAG, "Unable to send startService intent", e); } catch (TransactionTooLargeException e) { diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index dfbe59f1c8f22..44ebf50eb31a9 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -92,6 +92,8 @@ final class ServiceRecord extends Binder { ServiceState restartTracker; // tracking service restart boolean whitelistManager; // any bindings to this service have BIND_ALLOW_WHITELIST_MANAGEMENT? boolean delayed; // are we waiting to start this service in the background? + boolean fgRequired; // is the service required to go foreground after starting? + boolean fgWaiting; // is a timeout for going foreground already scheduled? boolean isForeground; // is service currently in foreground mode? int foregroundId; // Notification ID of last foreground req. Notification foregroundNoti; // Notification record of foreground state. diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 899847f177ebd..a8e096c59e1bc 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -12968,7 +12968,7 @@ public class PackageManagerService extends IPackageManager.Stub { IActivityManager am = ActivityManager.getService(); if (am != null) { try { - am.startService(null, intent, null, -1, null, mContext.getOpPackageName(), + am.startService(null, intent, null, -1, null, false, mContext.getOpPackageName(), UserHandle.USER_SYSTEM); } catch (RemoteException e) { } diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java index da1d9984eea57..dca74ffdcc8c1 100644 --- a/test-runner/src/android/test/mock/MockContext.java +++ b/test-runner/src/android/test/mock/MockContext.java @@ -525,6 +525,12 @@ public class MockContext extends Context { throw new UnsupportedOperationException(); } + @Override + public ComponentName startForegroundService(Intent service) { + throw new UnsupportedOperationException(); + } + + /** STOPSHIP remove when trial API is turned down */ @Override public ComponentName startServiceInForeground(Intent service, int id, Notification notification) { @@ -544,6 +550,12 @@ public class MockContext extends Context { /** @hide */ @Override + public ComponentName startForegroundServiceAsUser(Intent service, UserHandle user) { + throw new UnsupportedOperationException(); + } + + /** @hide STOPSHIP removed when trial API is turned down */ + @Override public ComponentName startServiceInForegroundAsUser(Intent service, int id, Notification notification, UserHandle user) { throw new UnsupportedOperationException(); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java index 5c281507b4612..06272c80f99e1 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java @@ -1852,6 +1852,18 @@ public class BridgeContext extends Context { return null; } + @Override + public ComponentName startForegroundService(Intent service) { + // pass + return null; + } + + @Override + public ComponentName startForegroundServiceAsUser(Intent service, UserHandle user) { + // pass + return null; + } + @Override public ComponentName startServiceInForeground(Intent service, int id, Notification notification) {