Extend assist context to foreground services

Add Service.onProvideAssistData(Bundle) which will be
called on foreground Services that have the new attr
in their manifest of provideAssistData = true;

Rename private reference to e.g. "getTopActivityExtras"
as "getAssistContextExtras" - do not rename the relevant
permission, since it is already public.

In ActivityManagerService, request extras both from the
top activity and from any foreground services with the
above attribute. Extend PendingActivityExtras as
PendingAssistExtras with a list of Services from which
extras are expected.

Reduce the timeout to or reporting extras from 4 sec to
just 500 ms.

Bug: 9526331
Change-Id: Ia03b96e8189033a68ae9c514c8cea0199a19bce8
diff --git a/api/current.txt b/api/current.txt
index 0e1bc4b..b0e1dab 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4228,6 +4228,7 @@
     method public void onCreate();
     method public void onDestroy();
     method public void onLowMemory();
+    method public void onProvideAssistData(android.os.Bundle);
     method public void onRebind(android.content.Intent);
     method public deprecated void onStart(android.content.Intent, int);
     method public int onStartCommand(android.content.Intent, int, int);
@@ -6272,6 +6273,8 @@
     field public static final deprecated java.lang.String EXTRA_ALLOW_REPLACE = "android.intent.extra.ALLOW_REPLACE";
     field public static final java.lang.String EXTRA_ASSIST_CONTEXT = "android.intent.extra.ASSIST_CONTEXT";
     field public static final java.lang.String EXTRA_ASSIST_PACKAGE = "android.intent.extra.ASSIST_PACKAGE";
+    field public static final java.lang.String EXTRA_ASSIST_SERVICES_CONTEXTS = "android.intent.extra.ASSIST_SERVICES_CONTEXTS";
+    field public static final java.lang.String EXTRA_ASSIST_SERVICES_PACKAGES = "android.intent.extra.ASSIST_SERVICES_PACKAGES";
     field public static final java.lang.String EXTRA_BCC = "android.intent.extra.BCC";
     field public static final java.lang.String EXTRA_BUG_REPORT = "android.intent.extra.BUG_REPORT";
     field public static final java.lang.String EXTRA_CC = "android.intent.extra.CC";
@@ -7336,6 +7339,7 @@
     method public void dump(android.util.Printer, java.lang.String);
     field public static final android.os.Parcelable.Creator CREATOR;
     field public static final int FLAG_ISOLATED_PROCESS = 2; // 0x2
+    field public static final int FLAG_PROVIDE_ASSIST_DATA = 4; // 0x4
     field public static final int FLAG_SINGLE_USER = 1073741824; // 0x40000000
     field public static final int FLAG_STOP_WITH_TASK = 1; // 0x1
     field public int flags;
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 404f6aa..cc964c2 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1921,20 +1921,21 @@
             return true;
         }
 
-        case GET_TOP_ACTIVITY_EXTRAS_TRANSACTION: {
+        case GET_ASSIST_CONTEXT_EXTRAS_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             int requestType = data.readInt();
-            Bundle res = getTopActivityExtras(requestType);
+            Bundle res = getAssistContextExtras(requestType);
             reply.writeNoException();
             reply.writeBundle(res);
             return true;
         }
 
-        case REPORT_TOP_ACTIVITY_EXTRAS_TRANSACTION: {
+        case REPORT_ASSIST_CONTEXT_EXTRAS_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             IBinder token = data.readStrongBinder();
             Bundle extras = data.readBundle();
-            reportTopActivityExtras(token, extras);
+            int index = data.readInt();
+            reportAssistContextExtras(token, extras, index);
             reply.writeNoException();
             return true;
         }
@@ -4456,12 +4457,12 @@
         return res;
     }
 
-    public Bundle getTopActivityExtras(int requestType) throws RemoteException {
+    public Bundle getAssistContextExtras(int requestType) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeInt(requestType);
-        mRemote.transact(GET_TOP_ACTIVITY_EXTRAS_TRANSACTION, data, reply, 0);
+        mRemote.transact(GET_ASSIST_CONTEXT_EXTRAS_TRANSACTION, data, reply, 0);
         reply.readException();
         Bundle res = reply.readBundle();
         data.recycle();
@@ -4469,13 +4470,15 @@
         return res;
     }
 
-    public void reportTopActivityExtras(IBinder token, Bundle extras) throws RemoteException {
+    public void reportAssistContextExtras(IBinder token, Bundle extras, int index)
+            throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeStrongBinder(token);
         data.writeBundle(extras);
-        mRemote.transact(REPORT_TOP_ACTIVITY_EXTRAS_TRANSACTION, data, reply, 0);
+        data.writeInt(index);
+        mRemote.transact(REPORT_ASSIST_CONTEXT_EXTRAS_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
         reply.recycle();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index d9f9d61..64653cd 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -529,10 +529,11 @@
         CompatibilityInfo info;
     }
 
-    static final class RequestActivityExtras {
+    static final class RequestAssistContextExtras {
         IBinder activityToken;
         IBinder requestToken;
         int requestType;
+        int index;
     }
 
     private native void dumpGraphicsInfo(FileDescriptor fd);
@@ -1197,13 +1198,14 @@
         }
 
         @Override
-        public void requestActivityExtras(IBinder activityToken, IBinder requestToken,
-                int requestType) {
-            RequestActivityExtras cmd = new RequestActivityExtras();
+        public void requestAssistContextExtras(IBinder activityToken, IBinder requestToken,
+                int requestType, int index) {
+            RequestAssistContextExtras cmd = new RequestAssistContextExtras();
             cmd.activityToken = activityToken;
             cmd.requestToken = requestToken;
             cmd.requestType = requestType;
-            queueOrSendMessage(H.REQUEST_ACTIVITY_EXTRAS, cmd);
+            cmd.index = index;
+            queueOrSendMessage(H.REQUEST_ASSIST_CONTEXT_EXTRAS, cmd);
         }
 
         private void printRow(PrintWriter pw, String format, Object...objs) {
@@ -1292,7 +1294,7 @@
         public static final int TRIM_MEMORY             = 140;
         public static final int DUMP_PROVIDER           = 141;
         public static final int UNSTABLE_PROVIDER_DIED  = 142;
-        public static final int REQUEST_ACTIVITY_EXTRAS = 143;
+        public static final int REQUEST_ASSIST_CONTEXT_EXTRAS = 143;
         public static final int TRANSLUCENT_CONVERSION_COMPLETE = 144;
         String codeToString(int code) {
             if (DEBUG_MESSAGES) {
@@ -1340,7 +1342,7 @@
                     case TRIM_MEMORY: return "TRIM_MEMORY";
                     case DUMP_PROVIDER: return "DUMP_PROVIDER";
                     case UNSTABLE_PROVIDER_DIED: return "UNSTABLE_PROVIDER_DIED";
-                    case REQUEST_ACTIVITY_EXTRAS: return "REQUEST_ACTIVITY_EXTRAS";
+                    case REQUEST_ASSIST_CONTEXT_EXTRAS: return "REQUEST_ASSIST_CONTEXT_EXTRAS";
                     case TRANSLUCENT_CONVERSION_COMPLETE: return "TRANSLUCENT_CONVERSION_COMPLETE";
                 }
             }
@@ -1553,8 +1555,8 @@
                 case UNSTABLE_PROVIDER_DIED:
                     handleUnstableProviderDied((IBinder)msg.obj, false);
                     break;
-                case REQUEST_ACTIVITY_EXTRAS:
-                    handleRequestActivityExtras((RequestActivityExtras)msg.obj);
+                case REQUEST_ASSIST_CONTEXT_EXTRAS:
+                    handleRequestAssistContextExtras((RequestAssistContextExtras)msg.obj);
                     break;
                 case TRANSLUCENT_CONVERSION_COMPLETE:
                     handleTranslucentConversionComplete((IBinder)msg.obj, msg.arg1 == 1);
@@ -2275,19 +2277,24 @@
         performNewIntents(data.token, data.intents);
     }
 
-    public void handleRequestActivityExtras(RequestActivityExtras cmd) {
+    public void handleRequestAssistContextExtras(RequestAssistContextExtras cmd) {
         Bundle data = new Bundle();
         ActivityClientRecord r = mActivities.get(cmd.activityToken);
         if (r != null) {
             r.activity.getApplication().dispatchOnProvideAssistData(r.activity, data);
             r.activity.onProvideAssistData(data);
+        } else {
+            Service service = mServices.get(cmd.activityToken);
+            if (service != null) {
+                service.onProvideAssistData(data);
+            }
         }
         if (data.isEmpty()) {
             data = null;
         }
         IActivityManager mgr = ActivityManagerNative.getDefault();
         try {
-            mgr.reportTopActivityExtras(cmd.requestToken, data);
+            mgr.reportAssistContextExtras(cmd.requestToken, data, cmd.index);
         } catch (RemoteException e) {
         }
     }
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index 6f18e84..df5b476 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -599,13 +599,14 @@
             return true;
         }
 
-        case REQUEST_ACTIVITY_EXTRAS_TRANSACTION:
+        case REQUEST_ASSIST_CONTEXT_EXTRAS_TRANSACTION:
         {
             data.enforceInterface(IApplicationThread.descriptor);
             IBinder activityToken = data.readStrongBinder();
             IBinder requestToken = data.readStrongBinder();
             int requestType = data.readInt();
-            requestActivityExtras(activityToken, requestToken, requestType);
+            int index = data.readInt();
+            requestAssistContextExtras(activityToken, requestToken, requestType, index);
             reply.writeNoException();
             return true;
         }
@@ -1238,14 +1239,16 @@
     }
 
     @Override
-    public void requestActivityExtras(IBinder activityToken, IBinder requestToken, int requestType)
-            throws RemoteException {
+    public void requestAssistContextExtras(IBinder activityToken, IBinder requestToken,
+            int requestType, int index) throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
         data.writeStrongBinder(activityToken);
         data.writeStrongBinder(requestToken);
         data.writeInt(requestType);
-        mRemote.transact(REQUEST_ACTIVITY_EXTRAS_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
+        data.writeInt(index);
+        mRemote.transact(REQUEST_ASSIST_CONTEXT_EXTRAS_TRANSACTION, data, null,
+                IBinder.FLAG_ONEWAY);
         data.recycle();
     }
 
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 3eda58c..3851eb3 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -386,9 +386,10 @@
 
     public long inputDispatchingTimedOut(int pid, boolean aboveSystem) throws RemoteException;
 
-    public Bundle getTopActivityExtras(int requestType) throws RemoteException;
+    public Bundle getAssistContextExtras(int requestType) throws RemoteException;
 
-    public void reportTopActivityExtras(IBinder token, Bundle extras) throws RemoteException;
+    public void reportAssistContextExtras(IBinder token, Bundle extras, int index)
+            throws RemoteException;
 
     public void killUid(int uid, String reason) throws RemoteException;
 
@@ -662,8 +663,8 @@
     int INPUT_DISPATCHING_TIMED_OUT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+158;
     int CLEAR_PENDING_BACKUP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+159;
     int GET_INTENT_FOR_INTENT_SENDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+160;
-    int GET_TOP_ACTIVITY_EXTRAS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+161;
-    int REPORT_TOP_ACTIVITY_EXTRAS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+162;
+    int GET_ASSIST_CONTEXT_EXTRAS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+161;
+    int REPORT_ASSIST_CONTEXT_EXTRAS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+162;
     int GET_LAUNCHED_FROM_PACKAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+163;
     int KILL_UID_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+164;
     int SET_USER_IS_MONKEY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+165;
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index 8e882df..4b46544 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -132,8 +132,8 @@
     void dumpGfxInfo(FileDescriptor fd, String[] args) throws RemoteException;
     void dumpDbInfo(FileDescriptor fd, String[] args) throws RemoteException;
     void unstableProviderDied(IBinder provider) throws RemoteException;
-    void requestActivityExtras(IBinder activityToken, IBinder requestToken, int requestType)
-            throws RemoteException;
+    void requestAssistContextExtras(IBinder activityToken, IBinder requestToken, int requestType,
+            int index) throws RemoteException;
     void scheduleTranslucentConversionComplete(IBinder token, boolean timeout)
             throws RemoteException;
     void setProcessState(int state) throws RemoteException;
@@ -186,7 +186,7 @@
     int DUMP_PROVIDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+44;
     int DUMP_DB_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+45;
     int UNSTABLE_PROVIDER_DIED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+46;
-    int REQUEST_ACTIVITY_EXTRAS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+47;
+    int REQUEST_ASSIST_CONTEXT_EXTRAS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+47;
     int SCHEDULE_TRANSLUCENT_CONVERSION_COMPLETE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+48;
     int SET_PROCESS_STATE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+49;
 }
diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java
index 7dfc589f..f9c245e 100644
--- a/core/java/android/app/SearchManager.java
+++ b/core/java/android/app/SearchManager.java
@@ -869,7 +869,7 @@
             intent.setComponent(comp);
             if (inclContext) {
                 IActivityManager am = ActivityManagerNative.getDefault();
-                Bundle extras = am.getTopActivityExtras(0);
+                Bundle extras = am.getAssistContextExtras(0);
                 if (extras != null) {
                     intent.replaceExtras(extras);
                 }
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index 3967740..7f8ddfd 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -23,6 +23,7 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.os.Build;
+import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.IBinder;
 import android.util.Log;
@@ -307,6 +308,18 @@
     }
 
     /**
+     * This is called on foreground services when the user is requesting an assist, to build a
+     * full {@link Intent#ACTION_ASSIST} Intent with all of the context of the current
+     * running foreground services.  You can override this method to place into the bundle
+     * anything you would like to appear as an item in the
+     * {@link Intent#EXTRA_SERVICES_ASSIST_CONTEXT} part of the assist Intent.
+     * This method will not be called if this service is not in the foreground.
+     * The default implementation does nothing.
+     */
+    public void onProvideAssistData(Bundle data) {
+    }
+
+    /**
      * @deprecated Implement {@link #onStartCommand(Intent, int, int)} instead.
      */
     @Deprecated
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 205ca6b..ff350b9 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1152,9 +1152,10 @@
     /**
      * Activity Action: Perform assist action.
      * <p>
-     * Input: {@link #EXTRA_ASSIST_PACKAGE} and {@link #EXTRA_ASSIST_CONTEXT} can provide
-     * additional optional contextual information about where the user was when they requested
-     * the assist.
+     * Input: {@link #EXTRA_ASSIST_PACKAGE}, {@link #EXTRA_ASSIST_CONTEXT},
+     * {@link #EXTRA_ASSIST_SERVICES_PACKAGES}, and {@link #EXTRA_ASSIST_SERVICES_CONTEXTS} can
+     * provide additional optional contextual information about where the user was when they
+     * requested the assist.
      * Output: nothing.
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
@@ -1163,9 +1164,10 @@
     /**
      * Activity Action: Perform voice assist action.
      * <p>
-     * Input: {@link #EXTRA_ASSIST_PACKAGE} and {@link #EXTRA_ASSIST_CONTEXT} can provide
-     * additional optional contextual information about where the user was when they requested
-     * the voice assist.
+     * Input: {@link #EXTRA_ASSIST_PACKAGE}, {@link #EXTRA_ASSIST_CONTEXT},
+     * {@link #EXTRA_ASSIST_SERVICES_PACKAGES}, and {@link #EXTRA_ASSIST_SERVICES_CONTEXTS} can
+     * provide additional optional contextual information about where the user was when they
+     * requested the voice assist.
      * Output: nothing.
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
@@ -1189,6 +1191,25 @@
             = "android.intent.extra.ASSIST_CONTEXT";
 
     /**
+     * An optional field on {@link #ACTION_ASSIST} and {@link #ACTION_VOICE_ASSIST}
+     * containing an the names of the application package of foreground services at the time
+     * of the assist request.  This is an array of {@link String}s, with one entry
+     * per service.
+     */
+    public static final String EXTRA_ASSIST_SERVICES_PACKAGES
+            = "android.intent.extra.ASSIST_SERVICES_PACKAGES";
+
+    /**
+     * An optional field on {@link #ACTION_ASSIST} and {@link #ACTION_VOICE_ASSIST}
+     * containing additional contextual information supplied by the current
+     * foreground services at the time of the assist request.  This is an array
+     * of {@link Bundle}s of additional data, with one {@link Bundle} per service.
+     */
+    public static final String EXTRA_ASSIST_SERVICES_CONTEXTS
+            = "android.intent.extra.ASSIST_SERVICES_CONTEXTS";
+
+
+    /**
      * Activity Action: List all available applications
      * <p>Input: Nothing.
      * <p>Output: nothing.
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index aaa2640..b432164 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3005,6 +3005,11 @@
 
         s.info.flags = 0;
         if (sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestService_provideAssistData,
+                false)) {
+            s.info.flags |= ServiceInfo.FLAG_PROVIDE_ASSIST_DATA;
+        }
+        if (sa.getBoolean(
                 com.android.internal.R.styleable.AndroidManifestService_stopWithTask,
                 false)) {
             s.info.flags |= ServiceInfo.FLAG_STOP_WITH_TASK;
diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java
index 796c2a4..a57a853 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -49,6 +49,14 @@
     public static final int FLAG_ISOLATED_PROCESS = 0x0002;
 
     /**
+     * Bit in {@link #flags}: If set,
+     * {@link Service#onProvideAssistData(android.os.Bundle)} will be
+     * called on the service when it is running in the foreground. Set from
+     * the {@link android.R.attr#provideAssistData} attribute.
+     */
+    public static final int FLAG_PROVIDE_ASSIST_DATA = 0x0004;
+
+    /**
      * Bit in {@link #flags}: If set, a single instance of the service will
      * run for all users on the device.  Set from the
      * {@link android.R.attr#singleUser} attribute.
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 728b1b0..60ed0e5 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1379,6 +1379,9 @@
              component specific values). -->
         <attr name="enabled" />
         <attr name="exported" />
+        <!-- If set to true, onProvideAssistData will be called on this service when this service
+             is running in the foreground. -->
+        <attr name="provideAssistData" format="boolean" />
         <!-- If set to true, this service with be automatically stopped
              when the user remove a task rooted in an activity owned by
              the application.  The default is false. -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index d3dbb2a..f267124 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -831,8 +831,8 @@
     <string name="permlab_getTopActivityInfo">get current app info</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_getTopActivityInfo">Allows the holder to retrieve private information
-        about the current application in the foreground of the screen.</string>
-    
+        about the current application and services in the foreground of the screen.</string>
+
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_runSetActivityWatcher">monitor and control all app launching</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index cb1747f..a164ad1 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -183,6 +183,7 @@
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
@@ -305,8 +306,13 @@
     // Maximum number of users we allow to be running at a time.
     static final int MAX_RUNNING_USERS = 3;
 
-    // How long to wait in getTopActivityExtras for the activity to respond with the result.
-    static final int PENDING_ACTIVITY_RESULT_TIMEOUT = 2*2000;
+    // How long to wait in getAssistContextExtras for the activity and foreground services
+    // to respond with the result.
+    static final int PENDING_ASSIST_EXTRAS_TIMEOUT = 500;
+
+    // Index for assist context bundle pertaining to the top activity. Non-negative indices
+    // correspond to assist context bundles from foreground services.
+    static final int TOP_ACTIVITY_ASSIST_EXTRAS_INDEX = -1;
 
     static final int MY_PID = Process.myPid();
 
@@ -382,25 +388,38 @@
      */
     private final ArrayList<TaskRecord> mRecentTasks = new ArrayList<TaskRecord>();
 
-    public class PendingActivityExtras extends Binder implements Runnable {
+    public class PendingAssistExtras extends Binder implements Runnable {
         public final ActivityRecord activity;
-        public boolean haveResult = false;
-        public Bundle result = null;
-        public PendingActivityExtras(ActivityRecord _activity) {
+        public final List<ServiceRecord> services;
+        public int numPending;
+        public int numRespondedServices = 0;
+        public Bundle activityExtras = null;
+        public Bundle[] servicesExtras;
+        public PendingAssistExtras(ActivityRecord _activity, List<ServiceRecord> _services) {
             activity = _activity;
+            services = _services;
+            numPending = services.size() + 1;
         }
         @Override
         public void run() {
-            Slog.w(TAG, "getTopActivityExtras failed: timeout retrieving from " + activity);
+            if (activityExtras == null) {
+                Slog.w(TAG, "getAssistContextExtras failed: timeout retrieving from " + activity);
+            }
+            for (int i = 0; i < services.size(); i++) {
+                if (servicesExtras[i] == null) {
+                    Slog.w(TAG, "getAssistContextExtras failed: timeout retrieving from "
+                            + services.get(i));
+                }
+            }
             synchronized (this) {
-                haveResult = true;
+                numPending = 0;
                 notifyAll();
             }
         }
     }
 
-    final ArrayList<PendingActivityExtras> mPendingActivityExtras
-            = new ArrayList<PendingActivityExtras>();
+    final ArrayList<PendingAssistExtras> mPendingAssistExtras
+            = new ArrayList<PendingAssistExtras>();
 
     /**
      * Process management.
@@ -7983,60 +8002,110 @@
         return true;
     }
 
-    public Bundle getTopActivityExtras(int requestType) {
+    public Bundle getAssistContextExtras(int requestType) {
         enforceCallingPermission(android.Manifest.permission.GET_TOP_ACTIVITY_INFO,
-                "getTopActivityExtras()");
-        PendingActivityExtras pae;
+                "getAssistContextExtras()");
+        PendingAssistExtras pae;
         Bundle extras = new Bundle();
+        List<ServiceRecord> foregroundServices;
         synchronized (this) {
-            ActivityRecord activity = getFocusedStack().mResumedActivity;
-            if (activity == null) {
-                Slog.w(TAG, "getTopActivityExtras failed: no resumed activity");
-                return null;
+            Collection<ServiceRecord> allServices = mServices.mServiceMap.getAllServices(
+                    Binder.getCallingUid());
+            foregroundServices = new ArrayList<ServiceRecord>();
+            for (ServiceRecord record : allServices) {
+                if ((record.serviceInfo.flags & ServiceInfo.FLAG_PROVIDE_ASSIST_DATA) > 0 &&
+                        record.isForeground) {
+                    if (record.app == null || record.app.thread == null) {
+                        Slog.w(TAG, "getAssistContextExtras error: no process for " + record);
+                        continue;
+                    }
+                    if (record.app.pid == Binder.getCallingPid()) {
+                        Slog.w(TAG, "getAssistContextExtras error: request process same as " +
+                                record);
+                        continue;
+                    }
+                    foregroundServices.add(record);
+                }
             }
-            extras.putString(Intent.EXTRA_ASSIST_PACKAGE, activity.packageName);
+
+            ActivityRecord activity = getFocusedStack().mResumedActivity;
+            boolean validActivity = true;
+            if (activity == null) {
+                Slog.w(TAG, "getAssistContextExtras error: no resumed activity");
+                validActivity = false;
+            }
             if (activity.app == null || activity.app.thread == null) {
-                Slog.w(TAG, "getTopActivityExtras failed: no process for " + activity);
-                return extras;
+                Slog.w(TAG, "getAssistContextExtras error: no process for " + activity);
+                validActivity = false;
             }
             if (activity.app.pid == Binder.getCallingPid()) {
-                Slog.w(TAG, "getTopActivityExtras failed: request process same as " + activity);
-                return extras;
+                Slog.w(TAG, "getAssistContextExtras error: request process same as " + activity);
+                validActivity = false;
             }
-            pae = new PendingActivityExtras(activity);
+
+            pae = new PendingAssistExtras(activity, foregroundServices);
             try {
-                activity.app.thread.requestActivityExtras(activity.appToken, pae, requestType);
-                mPendingActivityExtras.add(pae);
-                mHandler.postDelayed(pae, PENDING_ACTIVITY_RESULT_TIMEOUT);
+                if (validActivity) {
+                    activity.app.thread.requestAssistContextExtras(activity.appToken, pae,
+                            requestType, -1);
+                }
+                for (int i = 0; i < foregroundServices.size(); i++) {
+                    ServiceRecord record = foregroundServices.get(i);
+                    record.app.thread.requestAssistContextExtras(record, pae, requestType, i);
+                }
+                mPendingAssistExtras.add(pae);
+                mHandler.postDelayed(pae, PENDING_ASSIST_EXTRAS_TIMEOUT);
             } catch (RemoteException e) {
-                Slog.w(TAG, "getTopActivityExtras failed: crash calling " + activity);
-                return extras;
+                Slog.w(TAG, "getAssistContextExtras failed: crash fetching extras.", e);
             }
         }
         synchronized (pae) {
-            while (!pae.haveResult) {
+            while (pae.numPending > 0) {
                 try {
                     pae.wait();
                 } catch (InterruptedException e) {
                 }
             }
-            if (pae.result != null) {
-                extras.putBundle(Intent.EXTRA_ASSIST_CONTEXT, pae.result);
+            if (pae.activityExtras != null) {
+                extras.putBundle(Intent.EXTRA_ASSIST_CONTEXT, pae.activityExtras);
+                extras.putString(Intent.EXTRA_ASSIST_PACKAGE, pae.activity.packageName);
+            }
+            if (pae.numRespondedServices > 0) {
+                Bundle[] servicesExtras = new Bundle[pae.numRespondedServices];
+                String[] servicesPackages = new String[pae.numRespondedServices];
+                int extrasIndex = 0;
+                for (int i = 0; i < foregroundServices.size(); i++) {
+                    if (pae.servicesExtras[i] != null) {
+                        servicesExtras[extrasIndex] = pae.servicesExtras[i];
+                        ServiceRecord record = foregroundServices.get(i);
+                        servicesPackages[extrasIndex] = record.packageName;
+                        extrasIndex++;
+                    }
+                }
+                extras.putParcelableArray(Intent.EXTRA_ASSIST_SERVICES_CONTEXTS, servicesExtras);
+                extras.putStringArray(Intent.EXTRA_ASSIST_SERVICES_PACKAGES, servicesPackages);
             }
         }
         synchronized (this) {
-            mPendingActivityExtras.remove(pae);
+            mPendingAssistExtras.remove(pae);
             mHandler.removeCallbacks(pae);
         }
         return extras;
     }
 
-    public void reportTopActivityExtras(IBinder token, Bundle extras) {
-        PendingActivityExtras pae = (PendingActivityExtras)token;
+    public void reportAssistContextExtras(IBinder token, Bundle extras, int index) {
+        PendingAssistExtras pae = (PendingAssistExtras)token;
         synchronized (pae) {
-            pae.result = extras;
-            pae.haveResult = true;
-            pae.notifyAll();
+            if (index == TOP_ACTIVITY_ASSIST_EXTRAS_INDEX) {
+                pae.activityExtras = extras;
+            } else {
+                pae.servicesExtras[index] = extras;
+                pae.numRespondedServices++;
+            }
+            pae.numPending--;
+            if (pae.numPending == 0) {
+                pae.notifyAll();
+            }
         }
     }