OSDN Git Service

Work on issue #28221912: Starting service as foreground might...
authorDianne Hackborn <hackbod@google.com>
Mon, 18 Apr 2016 20:55:25 +0000 (13:55 -0700)
committerDianne Hackborn <hackbod@google.com>
Mon, 18 Apr 2016 21:48:09 +0000 (14:48 -0700)
...kill previous notification.

Add new platform API to detach a notification from a service
without dismissing it.

Also, while I am here, add some more @IntDefs.

Change-Id: I3bb46d9cd3db7f73716c8ced19c20fea800eb30d

api/current.txt
api/system-current.txt
api/test-current.txt
core/java/android/app/ActivityManagerNative.java
core/java/android/app/IActivityManager.java
core/java/android/app/Service.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/ServiceRecord.java

index 5df8750..a69a9eb 100644 (file)
@@ -5554,6 +5554,7 @@ package android.app {
     method public boolean onUnbind(android.content.Intent);
     method public final void startForeground(int, android.app.Notification);
     method public final void stopForeground(boolean);
+    method public final void stopForeground(int);
     method public final void stopSelf();
     method public final void stopSelf(int);
     method public final boolean stopSelfResult(int);
@@ -5564,6 +5565,8 @@ package android.app {
     field public static final int START_REDELIVER_INTENT = 3; // 0x3
     field public static final int START_STICKY = 1; // 0x1
     field public static final int START_STICKY_COMPATIBILITY = 0; // 0x0
+    field public static final int STOP_FOREGROUND_DETACH = 2; // 0x2
+    field public static final int STOP_FOREGROUND_REMOVE = 1; // 0x1
   }
 
   public abstract class SharedElementCallback {
index acb699d..de74d67 100644 (file)
@@ -5689,6 +5689,7 @@ package android.app {
     method public boolean onUnbind(android.content.Intent);
     method public final void startForeground(int, android.app.Notification);
     method public final void stopForeground(boolean);
+    method public final void stopForeground(int);
     method public final void stopSelf();
     method public final void stopSelf(int);
     method public final boolean stopSelfResult(int);
@@ -5699,6 +5700,8 @@ package android.app {
     field public static final int START_REDELIVER_INTENT = 3; // 0x3
     field public static final int START_STICKY = 1; // 0x1
     field public static final int START_STICKY_COMPATIBILITY = 0; // 0x0
+    field public static final int STOP_FOREGROUND_DETACH = 2; // 0x2
+    field public static final int STOP_FOREGROUND_REMOVE = 1; // 0x1
   }
 
   public abstract class SharedElementCallback {
index e4b11bd..2773b45 100644 (file)
@@ -5554,6 +5554,7 @@ package android.app {
     method public boolean onUnbind(android.content.Intent);
     method public final void startForeground(int, android.app.Notification);
     method public final void stopForeground(boolean);
+    method public final void stopForeground(int);
     method public final void stopSelf();
     method public final void stopSelf(int);
     method public final boolean stopSelfResult(int);
@@ -5564,6 +5565,8 @@ package android.app {
     field public static final int START_REDELIVER_INTENT = 3; // 0x3
     field public static final int START_STICKY = 1; // 0x1
     field public static final int START_STICKY_COMPATIBILITY = 0; // 0x0
+    field public static final int STOP_FOREGROUND_DETACH = 2; // 0x2
+    field public static final int STOP_FOREGROUND_REMOVE = 1; // 0x1
   }
 
   public abstract class SharedElementCallback {
index a82b950..ae2ca84 100644 (file)
@@ -1111,8 +1111,8 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
             if (data.readInt() != 0) {
                 notification = Notification.CREATOR.createFromParcel(data);
             }
-            boolean removeNotification = data.readInt() != 0;
-            setServiceForeground(className, token, id, notification, removeNotification);
+            int sflags = data.readInt();
+            setServiceForeground(className, token, id, notification, sflags);
             reply.writeNoException();
             return true;
         }
@@ -4300,7 +4300,7 @@ class ActivityManagerProxy implements IActivityManager
         return res;
     }
     public void setServiceForeground(ComponentName className, IBinder token,
-            int id, Notification notification, boolean removeNotification) throws RemoteException {
+            int id, Notification notification, int flags) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
@@ -4313,7 +4313,7 @@ class ActivityManagerProxy implements IActivityManager
         } else {
             data.writeInt(0);
         }
-        data.writeInt(removeNotification ? 1 : 0);
+        data.writeInt(flags);
         mRemote.transact(SET_SERVICE_FOREGROUND_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
index 1a4e98c..b28b5e6 100644 (file)
@@ -235,7 +235,7 @@ public interface IActivityManager extends IInterface {
     public boolean stopServiceToken(ComponentName className, IBinder token,
             int startId) throws RemoteException;
     public void setServiceForeground(ComponentName className, IBinder token,
-            int id, Notification notification, boolean keepNotification) throws RemoteException;
+            int id, Notification notification, int flags) throws RemoteException;
     public int bindService(IApplicationThread caller, IBinder token, Intent service,
             String resolvedType, IServiceConnection connection, int flags,
             String callingPackage, int userId) throws RemoteException;
index 21a3543..4fe4f98 100644 (file)
@@ -16,6 +16,7 @@
 
 package android.app;
 
+import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.content.ComponentCallbacks2;
 import android.content.ComponentName;
@@ -30,6 +31,8 @@ import android.util.Log;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 
 /**
  * A Service is an application component representing either an application's desire
@@ -300,6 +303,32 @@ import java.io.PrintWriter;
 public abstract class Service extends ContextWrapper implements ComponentCallbacks2 {
     private static final String TAG = "Service";
 
+    /**
+     * Flag for {@link #stopForeground(int)}: if set, the notification previously provided
+     * to {@link #startForeground} will be removed.  Otherwise it will remain
+     * until a later call (to {@link #startForeground(int, Notification)} or
+     * {@link #stopForeground(int)} removes it, or the service is destroyed.
+     */
+    public static final int STOP_FOREGROUND_REMOVE = 1<<0;
+
+    /**
+     * Flag for {@link #stopForeground(int)}: if set, the notification previously provided
+     * to {@link #startForeground} will be detached from the service.  Only makes sense
+     * when {@link #STOP_FOREGROUND_REMOVE} is <b>not</b> set -- in this case, the notification
+     * will remain shown, but be completely detached from the service and so no longer changed
+     * except through direct calls to the notification manager.
+     */
+    public static final int STOP_FOREGROUND_DETACH = 1<<1;
+
+    /** @hide */
+    @IntDef(flag = true,
+            value = {
+                STOP_FOREGROUND_REMOVE,
+                STOP_FOREGROUND_DETACH
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface StopForegroundFlags {}
+
     public Service() {
         super(null);
     }
@@ -377,7 +406,7 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac
      * alarm goes off.
      */
     public static final int START_NOT_STICKY = 2;
-    
+
     /**
      * Constant to return from {@link #onStartCommand}: if this service's
      * process is killed while it is started (after returning from
@@ -392,7 +421,18 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac
      * pending events will be delivered at the point of restart).
      */
     public static final int START_REDELIVER_INTENT = 3;
-    
+
+    /** @hide */
+    @IntDef(flag = false,
+            value = {
+                START_STICKY_COMPATIBILITY,
+                START_STICKY,
+                START_NOT_STICKY,
+                START_REDELIVER_INTENT,
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface StartResult {}
+
     /**
      * Special constant for reporting that we are done processing
      * {@link #onTaskRemoved(Intent)}.
@@ -414,7 +454,17 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac
      * {@link #onStartCommand(Intent, int, int)}.
      */
     public static final int START_FLAG_RETRY = 0x0002;
-    
+
+    /** @hide */
+    @IntDef(flag = true,
+            value = {
+                START_FLAG_REDELIVERY,
+                START_FLAG_RETRY,
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface StartArgFlags {}
+
+
     /**
      * Called by the system every time a client explicitly starts the service by calling 
      * {@link android.content.Context#startService}, providing the arguments it supplied and a 
@@ -455,7 +505,7 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac
      * 
      * @see #stopSelfResult(int)
      */
-    public int onStartCommand(Intent intent, int flags, int startId) {
+    public @StartResult int onStartCommand(Intent intent, @StartArgFlags int flags, int startId) {
         onStart(intent, startId);
         return mStartCompatibility ? START_STICKY_COMPATIBILITY : START_STICKY;
     }
@@ -652,28 +702,37 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac
         try {
             mActivityManager.setServiceForeground(
                     new ComponentName(this, mClassName), mToken, id,
-                    notification, true);
+                    notification, 0);
         } catch (RemoteException ex) {
         }
     }
     
     /**
+     * Synonym for {@link #stopForeground(int)}.
+     * @param removeNotification If true, the {@link #STOP_FOREGROUND_REMOVE} flag
+     * will be supplied.
+     * @see #stopForeground(int)
+     * @see #startForeground(int, Notification)
+     */
+    public final void stopForeground(boolean removeNotification) {
+        stopForeground(removeNotification ? STOP_FOREGROUND_REMOVE : 0);
+    }
+
+    /**
      * Remove this service from foreground state, allowing it to be killed if
      * more memory is needed.
-     * @param removeNotification If true, the notification previously provided
-     * to {@link #startForeground} will be removed.  Otherwise it will remain
-     * until a later call removes it (or the service is destroyed).
+     * @param flags Additional behavior options: {@link #STOP_FOREGROUND_REMOVE},
+     * {@link #STOP_FOREGROUND_DETACH}.
      * @see #startForeground(int, Notification)
      */
-    public final void stopForeground(boolean removeNotification) {
+    public final void stopForeground(@StopForegroundFlags int flags) {
         try {
             mActivityManager.setServiceForeground(
-                    new ComponentName(this, mClassName), mToken, 0, null,
-                    removeNotification);
+                    new ComponentName(this, mClassName), mToken, 0, null, flags);
         } catch (RemoteException ex) {
         }
     }
-    
+
     /**
      * Print the Service's state into the given stream.  This gets invoked if
      * you run "adb shell dumpsys activity service &lt;yourservicename&gt;"
index 5d1cb8a..5b8d98c 100755 (executable)
@@ -689,7 +689,7 @@ public final class ActiveServices {
     }
 
     public void setServiceForegroundLocked(ComponentName className, IBinder token,
-            int id, Notification notification, boolean removeNotification) {
+            int id, Notification notification, int flags) {
         final int userId = UserHandle.getCallingUserId();
         final long origId = Binder.clearCallingIdentity();
         try {
@@ -719,12 +719,16 @@ public final class ActiveServices {
                             updateServiceForegroundLocked(r.app, true);
                         }
                     }
-                    if (removeNotification) {
+                    if ((flags & Service.STOP_FOREGROUND_REMOVE) != 0) {
                         r.cancelNotification();
                         r.foregroundId = 0;
                         r.foregroundNoti = null;
                     } else if (r.appInfo.targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
                         r.stripForegroundServiceFlagFromNotification();
+                        if ((flags & Service.STOP_FOREGROUND_DETACH) != 0) {
+                            r.foregroundId = 0;
+                            r.foregroundNoti = null;
+                        }
                     }
                 }
             }
index f126e4c..afc91d6 100644 (file)
@@ -16462,10 +16462,9 @@ public final class ActivityManagerService extends ActivityManagerNative
 
     @Override
     public void setServiceForeground(ComponentName className, IBinder token,
-            int id, Notification notification, boolean removeNotification) {
+            int id, Notification notification, int flags) {
         synchronized(this) {
-            mServices.setServiceForegroundLocked(className, token, id, notification,
-                    removeNotification);
+            mServices.setServiceForegroundLocked(className, token, id, notification, flags);
         }
     }
 
index 5075c3a..bc297de 100644 (file)
@@ -516,7 +516,7 @@ final class ServiceRecord extends Binder {
                         // If it gave us a garbage notification, it doesn't
                         // get to be foreground.
                         ams.setServiceForeground(name, ServiceRecord.this,
-                                0, null, true);
+                                0, null, 0);
                         ams.crashApplication(appUid, appPid, localPackageName,
                                 "Bad notification for startForeground: " + e);
                     }