Add new API to propagate contextual data to the assist action
When launching an assist, we have a new API allowing the
current foreground activity/application to provide additional
arbitrary contextual information that is stuffed in the
assist intent before it is launched.
Change-Id: I0b2a6f5a266dc42cc0175327fa76774f814af3b4
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index d6ddeb65..18ccd53 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1346,6 +1346,20 @@
}
/**
+ * This is called when the user is requesting an assist, to build a full
+ * {@link Intent#ACTION_ASSIST} Intent with all of the context of the current
+ * application. You can override this method to place into the bundle anything
+ * you would like to appear in the {@link Intent#EXTRA_ASSIST_CONTEXT} part
+ * of the assist Intent. The default implementation does nothing.
+ *
+ * <p>This function will be called after any global assist callbacks that had
+ * been registered with {@link Application#registerOnProvideAssistData
+ * Application.registerOnProvideAssistData}.
+ */
+ public void onProvideAssistData(Bundle data) {
+ }
+
+ /**
* Called when you are no longer visible to the user. You will next
* receive either {@link #onRestart}, {@link #onDestroy}, or nothing,
* depending on later user activity.
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 61b2067..bc27a57d 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1818,6 +1818,24 @@
return true;
}
+ case GET_TOP_ACTIVITY_EXTRAS_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ int requestType = data.readInt();
+ Bundle res = getTopActivityExtras(requestType);
+ reply.writeNoException();
+ reply.writeBundle(res);
+ return true;
+ }
+
+ case REPORT_TOP_ACTIVITY_EXTRAS_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder token = data.readStrongBinder();
+ Bundle extras = data.readBundle();
+ reportTopActivityExtras(token, extras);
+ reply.writeNoException();
+ return true;
+ }
+
}
return super.onTransact(code, data, reply, flags);
@@ -4149,5 +4167,30 @@
return res;
}
+ public Bundle getTopActivityExtras(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);
+ reply.readException();
+ Bundle res = reply.readBundle();
+ data.recycle();
+ reply.recycle();
+ return res;
+ }
+
+ public void reportTopActivityExtras(IBinder token, Bundle extras) 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);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
private IBinder mRemote;
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 12716456..570fb80 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -533,6 +533,12 @@
String pkg;
CompatibilityInfo info;
}
+
+ static final class RequestActivityExtras {
+ IBinder activityToken;
+ IBinder requestToken;
+ int requestType;
+ }
private native void dumpGraphicsInfo(FileDescriptor fd);
@@ -1108,6 +1114,16 @@
queueOrSendMessage(H.UNSTABLE_PROVIDER_DIED, provider);
}
+ @Override
+ public void requestActivityExtras(IBinder activityToken, IBinder requestToken,
+ int requestType) {
+ RequestActivityExtras cmd = new RequestActivityExtras();
+ cmd.activityToken = activityToken;
+ cmd.requestToken = requestToken;
+ cmd.requestType = requestType;
+ queueOrSendMessage(H.REQUEST_ACTIVITY_EXTRAS, cmd);
+ }
+
private void printRow(PrintWriter pw, String format, Object...objs) {
pw.println(String.format(format, objs));
}
@@ -1173,6 +1189,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;
String codeToString(int code) {
if (DEBUG_MESSAGES) {
switch (code) {
@@ -1219,6 +1236,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";
}
}
return Integer.toString(code);
@@ -1430,6 +1448,9 @@
case UNSTABLE_PROVIDER_DIED:
handleUnstableProviderDied((IBinder)msg.obj, false);
break;
+ case REQUEST_ACTIVITY_EXTRAS:
+ handleRequestActivityExtras((RequestActivityExtras)msg.obj);
+ break;
}
if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
}
@@ -2322,6 +2343,23 @@
performNewIntents(data.token, data.intents);
}
+ public void handleRequestActivityExtras(RequestActivityExtras 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);
+ }
+ if (data.isEmpty()) {
+ data = null;
+ }
+ IActivityManager mgr = ActivityManagerNative.getDefault();
+ try {
+ mgr.reportTopActivityExtras(cmd.requestToken, data);
+ } catch (RemoteException e) {
+ }
+ }
+
private static final ThreadLocal<Intent> sCurrentBroadcastIntent = new ThreadLocal<Intent>();
/**
diff --git a/core/java/android/app/Application.java b/core/java/android/app/Application.java
index 3a67cec..132388e 100644
--- a/core/java/android/app/Application.java
+++ b/core/java/android/app/Application.java
@@ -22,6 +22,7 @@
import android.content.ComponentCallbacks2;
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
@@ -45,6 +46,7 @@
new ArrayList<ComponentCallbacks>();
private ArrayList<ActivityLifecycleCallbacks> mActivityLifecycleCallbacks =
new ArrayList<ActivityLifecycleCallbacks>();
+ private ArrayList<OnProvideAssistData> mAssistCallbacks = null;
/** @hide */
public LoadedApk mLoadedApk;
@@ -59,6 +61,21 @@
void onActivityDestroyed(Activity activity);
}
+ /**
+ * Callback interface for use with {@link Application#registerOnProvideAssistData}
+ * and {@link Application#unregisterOnProvideAssistData}.
+ */
+ public interface OnProvideAssistData {
+ /**
+ * This is called when the user is requesting an assist, to build a full
+ * {@link Intent#ACTION_ASSIST} Intent with all of the context of the current
+ * application. You can override this method to place into the bundle anything
+ * you would like to appear in the {@link Intent#EXTRA_ASSIST_CONTEXT} part
+ * of the assist Intent.
+ */
+ public void onProvideAssistData(Activity activity, Bundle data);
+ }
+
public Application() {
super(null);
}
@@ -137,7 +154,24 @@
mActivityLifecycleCallbacks.remove(callback);
}
}
-
+
+ public void registerOnProvideAssistData(OnProvideAssistData callback) {
+ synchronized (this) {
+ if (mAssistCallbacks == null) {
+ mAssistCallbacks = new ArrayList<OnProvideAssistData>();
+ }
+ mAssistCallbacks.add(callback);
+ }
+ }
+
+ public void unregisterOnProvideAssistData(OnProvideAssistData callback) {
+ synchronized (this) {
+ if (mAssistCallbacks != null) {
+ mAssistCallbacks.remove(callback);
+ }
+ }
+ }
+
// ------------------ Internal API ------------------
/**
@@ -232,4 +266,19 @@
}
return callbacks;
}
+
+ /* package */ void dispatchOnProvideAssistData(Activity activity, Bundle data) {
+ Object[] callbacks;
+ synchronized (this) {
+ if (mAssistCallbacks == null) {
+ return;
+ }
+ callbacks = mAssistCallbacks.toArray();
+ }
+ if (callbacks != null) {
+ for (int i=0; i<callbacks.length; i++) {
+ ((OnProvideAssistData)callbacks[i]).onProvideAssistData(activity, data);
+ }
+ }
+ }
}
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index 63aa5f9..f0e367c 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -587,6 +587,17 @@
reply.writeNoException();
return true;
}
+
+ case REQUEST_ACTIVITY_EXTRAS_TRANSACTION:
+ {
+ data.enforceInterface(IApplicationThread.descriptor);
+ IBinder activityToken = data.readStrongBinder();
+ IBinder requestToken = data.readStrongBinder();
+ int requestType = data.readInt();
+ requestActivityExtras(activityToken, requestToken, requestType);
+ reply.writeNoException();
+ return true;
+ }
}
return super.onTransact(code, data, reply, flags);
@@ -1185,4 +1196,15 @@
mRemote.transact(UNSTABLE_PROVIDER_DIED_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
data.recycle();
}
+
+ public void requestActivityExtras(IBinder activityToken, IBinder requestToken, int requestType)
+ 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.recycle();
+ }
}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 8af17a4..baac07f 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -368,6 +368,10 @@
public long inputDispatchingTimedOut(int pid, boolean aboveSystem) throws RemoteException;
+ public Bundle getTopActivityExtras(int requestType) throws RemoteException;
+
+ public void reportTopActivityExtras(IBinder token, Bundle extras) throws RemoteException;
+
/*
* Private non-Binder interfaces
*/
@@ -624,4 +628,6 @@
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;
}
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index 03a26d4..8516694 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -130,6 +130,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;
String descriptor = "android.app.IApplicationThread";
@@ -179,4 +181,5 @@
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;
}
diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java
index 6382cee..7dfc589f 100644
--- a/core/java/android/app/SearchManager.java
+++ b/core/java/android/app/SearchManager.java
@@ -846,8 +846,8 @@
*
* @hide
*/
- public Intent getAssistIntent(Context context) {
- return getAssistIntent(context, UserHandle.myUserId());
+ public Intent getAssistIntent(Context context, boolean inclContext) {
+ return getAssistIntent(context, inclContext, UserHandle.myUserId());
}
/**
@@ -856,7 +856,7 @@
*
* @hide
*/
- public Intent getAssistIntent(Context context, int userHandle) {
+ public Intent getAssistIntent(Context context, boolean inclContext, int userHandle) {
try {
if (mService == null) {
return null;
@@ -867,6 +867,13 @@
}
Intent intent = new Intent(Intent.ACTION_ASSIST);
intent.setComponent(comp);
+ if (inclContext) {
+ IActivityManager am = ActivityManagerNative.getDefault();
+ Bundle extras = am.getTopActivityExtras(0);
+ if (extras != null) {
+ intent.replaceExtras(extras);
+ }
+ }
return intent;
} catch (RemoteException re) {
Log.e(TAG, "getAssistIntent() failed: " + re);
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 89b1bbd..2ffb63c 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1140,14 +1140,33 @@
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_WEB_SEARCH = "android.intent.action.WEB_SEARCH";
+
/**
* Activity Action: Perform assist action.
* <p>
- * Input: nothing
+ * 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.
* Output: nothing.
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_ASSIST = "android.intent.action.ASSIST";
+
+ /**
+ * An optional field on {@link #ACTION_ASSIST} containing the name of the current
+ * foreground application package at the time the assist was invoked.
+ */
+ public static final String EXTRA_ASSIST_PACKAGE
+ = "android.intent.extra.ASSIST_PACKAGE";
+
+ /**
+ * An optional field on {@link #ACTION_ASSIST} containing additional contextual
+ * information supplied by the current foreground app at the time of the assist
+ * request. This is a {@link Bundle} of additional data.
+ */
+ public static final String EXTRA_ASSIST_CONTEXT
+ = "android.intent.extra.ASSIST_CONTEXT";
+
/**
* Activity Action: List all available applications
* <p>Input: Nothing.
diff --git a/core/java/android/view/SimulatedDpad.java b/core/java/android/view/SimulatedDpad.java
index 0a37fdd..1ee416c 100644
--- a/core/java/android/view/SimulatedDpad.java
+++ b/core/java/android/view/SimulatedDpad.java
@@ -182,7 +182,7 @@
Intent intent =
((SearchManager)mContext.getSystemService(Context.SEARCH_SERVICE))
- .getAssistIntent(mContext, UserHandle.USER_CURRENT_OR_SELF);
+ .getAssistIntent(mContext, false, UserHandle.USER_CURRENT_OR_SELF);
if (intent != null) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index a69870b..aafc4bf 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1702,6 +1702,13 @@
android:description="@string/permdesc_stopAppSwitches"
android:protectionLevel="signature|system" />
+ <!-- Allows an application to retrieve private information about
+ the current top activity, such as any assist context it can provide. -->
+ <permission android:name="android.permission.GET_TOP_ACTIVITY_INFO"
+ android:label="@string/permlab_getTopActivityInfo"
+ android:description="@string/permdesc_getTopActivityInfo"
+ android:protectionLevel="signature" />
+
<!-- Allows an application to retrieve the current state of keys and
switches. This is only for use by the system.
@deprecated The API that used this permission has been removed. -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index fa26089..7f0fc0b 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -784,6 +784,12 @@
another app.</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_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>
+
+ <!-- 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. -->
<string name="permdesc_runSetActivityWatcher">Allows the app to