OSDN Git Service

API refactor: context.startForegroundService()
authorChristopher Tate <ctate@google.com>
Tue, 21 Mar 2017 18:37:06 +0000 (11:37 -0700)
committerChris Tate <ctate@android.com>
Thu, 30 Mar 2017 18:31:24 +0000 (18:31 +0000)
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)

19 files changed:
api/current.txt
api/system-current.txt
api/test-current.txt
core/java/android/app/ActivityManager.java
core/java/android/app/ContextImpl.java
core/java/android/app/IActivityManager.aidl
core/java/android/app/NotificationManager.java
core/java/android/app/PendingIntent.java
core/java/android/content/Context.java
core/java/android/content/ContextWrapper.java
core/java/android/view/ContextThemeWrapper.java
services/core/java/com/android/server/am/ActiveServices.java
services/core/java/com/android/server/am/ActivityManagerService.java
services/core/java/com/android/server/am/ActivityManagerShellCommand.java
services/core/java/com/android/server/am/PendingIntentRecord.java
services/core/java/com/android/server/am/ServiceRecord.java
services/core/java/com/android/server/pm/PackageManagerService.java
test-runner/src/android/test/mock/MockContext.java
tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java

index e1ca18c..b51c2d3 100644 (file)
@@ -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;
index 92e2a50..9e8b67c 100644 (file)
@@ -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;
index 3d184b2..3bf9dc8 100644 (file)
@@ -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;
index 9f2f669..4004bd6 100644 (file)
@@ -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;
 
index 4c080c9..467ba99 100644 (file)
@@ -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(
index b9d1d91..0a5e4be 100644 (file)
@@ -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,
index 75998f2..72c5978 100644 (file)
@@ -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);
index 7d1a16a..dc432af 100644 (file)
@@ -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.
+     *
+     * <p class="note">For security reasons, the {@link android.content.Intent}
+     * you supply here should almost always be an <em>explicit intent</em>,
+     * that is specify an explicit component to be delivered to through
+     * {@link Intent#setClass(android.content.Context, Class) Intent.setClass}</p>
+     *
+     * @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());
index 3a8a420..1803bbe 100644 (file)
@@ -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).
@@ -2588,11 +2588,47 @@ public abstract class Context {
     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.
+     *
+     * <p>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
index 6b0bbfa..75784a6 100644 (file)
@@ -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);
index 86318e9..d3cc175 100644 (file)
@@ -36,8 +36,8 @@ public class ContextThemeWrapper extends ContextWrapper {
 
     /**
      * Creates a new context wrapper with no theme and no base context.
-     * <p>
-     * <stong>Note:</strong> A base context <strong>must</strong> be attached
+     * <p class="note">
+     * <strong>Note:</strong> A base context <strong>must</strong> be attached
      * using {@link #attachBaseContext(Context)} before calling any other
      * method on the newly constructed context wrapper.
      */
index b4f8f61..8b0665c 100644 (file)
@@ -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();
index f602e43..b4da152 100644 (file)
@@ -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;
         }
index 9b6d13a..a9bd872 100644 (file)
@@ -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;
index c494171..f05bfb6 100644 (file)
@@ -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) {
index dfbe59f..44ebf50 100644 (file)
@@ -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.
index 899847f..a8e096c 100644 (file)
@@ -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) {
             }
index da1d998..dca74ff 100644 (file)
@@ -526,6 +526,12 @@ public class MockContext extends Context {
     }
 
     @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) {
         throw new UnsupportedOperationException();
@@ -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();
index 5c28150..06272c8 100644 (file)
@@ -1853,6 +1853,18 @@ public class BridgeContext extends Context {
     }
 
     @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) {
         // pass