Merge "1/ Refactor out logic to fetch assist data asynchronously"
diff --git a/Android.mk b/Android.mk
index 9890bb4..62f750c 100644
--- a/Android.mk
+++ b/Android.mk
@@ -380,7 +380,7 @@
core/java/android/speech/tts/ITextToSpeechService.aidl \
core/java/com/android/internal/app/IAppOpsCallback.aidl \
core/java/com/android/internal/app/IAppOpsService.aidl \
- core/java/com/android/internal/app/IAssistScreenshotReceiver.aidl \
+ core/java/com/android/internal/app/IAssistDataReceiver.aidl \
core/java/com/android/internal/app/IBatteryStats.aidl \
core/java/com/android/internal/app/ISoundTriggerService.aidl \
core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl \
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 49513d1..322450e 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -63,6 +63,7 @@
import android.os.PersistableBundle;
import android.os.StrictMode;
import android.service.voice.IVoiceInteractionSession;
+import com.android.internal.app.IAssistDataReceiver;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.os.IResultReceiver;
import com.android.internal.policy.IKeyguardDismissCallback;
@@ -453,7 +454,7 @@
void notifyCleartextNetwork(int uid, in byte[] firstPacket);
int createStackOnDisplay(int displayId);
void setTaskResizeable(int taskId, int resizeableMode);
- boolean requestAssistContextExtras(int requestType, in IResultReceiver receiver,
+ boolean requestAssistContextExtras(int requestType, in IAssistDataReceiver receiver,
in Bundle receiverExtras, in IBinder activityToken,
boolean focused, boolean newSessionId);
void resizeTask(int taskId, in Rect bounds, int resizeMode);
@@ -617,7 +618,7 @@
boolean updateDisplayOverrideConfiguration(in Configuration values, int displayId);
void unregisterTaskStackListener(ITaskStackListener listener);
void moveStackToDisplay(int stackId, int displayId);
- boolean requestAutofillData(in IResultReceiver receiver, in Bundle receiverExtras,
+ boolean requestAutofillData(in IAssistDataReceiver receiver, in Bundle receiverExtras,
in IBinder activityToken, int flags);
void dismissKeyguard(in IBinder token, in IKeyguardDismissCallback callback);
int restartUserInBackground(int userId);
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 6e49bac..3d38dc4 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -16,7 +16,7 @@
package android.view;
-import com.android.internal.app.IAssistScreenshotReceiver;
+import com.android.internal.app.IAssistDataReceiver;
import com.android.internal.os.IResultReceiver;
import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethodClient;
@@ -272,7 +272,7 @@
/**
* Used only for assist -- request a screenshot of the current application.
*/
- boolean requestAssistScreenshot(IAssistScreenshotReceiver receiver);
+ boolean requestAssistScreenshot(IAssistDataReceiver receiver);
/**
* Called by the status bar to notify Views of changes to System UI visiblity.
diff --git a/core/java/com/android/internal/app/IAssistScreenshotReceiver.aidl b/core/java/com/android/internal/app/IAssistDataReceiver.aidl
similarity index 74%
rename from core/java/com/android/internal/app/IAssistScreenshotReceiver.aidl
rename to core/java/com/android/internal/app/IAssistDataReceiver.aidl
index a987a16..9c9ffef 100644
--- a/core/java/com/android/internal/app/IAssistScreenshotReceiver.aidl
+++ b/core/java/com/android/internal/app/IAssistDataReceiver.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,8 +17,10 @@
package com.android.internal.app;
import android.graphics.Bitmap;
+import android.os.Bundle;
/** @hide */
-oneway interface IAssistScreenshotReceiver {
- void send(in Bitmap screenshot);
+oneway interface IAssistDataReceiver {
+ void onHandleAssistData(in Bundle resultData);
+ void onHandleAssistScreenshot(in Bitmap screenshot);
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 010995f..eaf605f 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -43,6 +43,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
+import android.graphics.Bitmap;
import android.graphics.Rect;
import android.metrics.LogMaker;
import android.os.Binder;
@@ -74,11 +75,11 @@
import android.view.autofill.IAutofillWindowPresenter;
import com.android.internal.R;
+import com.android.internal.app.IAssistDataReceiver;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.os.HandlerCaller;
-import com.android.internal.os.IResultReceiver;
import com.android.internal.util.ArrayUtils;
import com.android.server.autofill.ui.AutoFillUI;
import com.android.server.autofill.ui.PendingUi;
@@ -203,9 +204,9 @@
/**
* Receiver of assist data from the app's {@link Activity}.
*/
- private final IResultReceiver mAssistReceiver = new IResultReceiver.Stub() {
+ private final IAssistDataReceiver mAssistReceiver = new IAssistDataReceiver.Stub() {
@Override
- public void send(int resultCode, Bundle resultData) throws RemoteException {
+ public void onHandleAssistData(Bundle resultData) throws RemoteException {
final AssistStructure structure = resultData.getParcelable(KEY_STRUCTURE);
if (structure == null) {
Slog.e(TAG, "No assist structure - app might have crashed providing it");
@@ -261,6 +262,11 @@
mRemoteFillService.onFillRequest(request);
}
+
+ @Override
+ public void onHandleAssistScreenshot(Bitmap screenshot) {
+ // Do nothing
+ }
};
/**
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index f2e0493..cf09158 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -16,6 +16,7 @@
package com.android.server.am;
+import static android.Manifest.permission.BIND_VOICE_INTERACTION;
import static android.Manifest.permission.CHANGE_CONFIGURATION;
import static android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
@@ -366,6 +367,7 @@
import com.android.internal.app.DumpHeapActivity;
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsService;
+import com.android.internal.app.IAssistDataReceiver;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.app.ProcessMap;
import com.android.internal.app.SystemUserHomeActivity;
@@ -782,7 +784,7 @@
public final Bundle extras;
public final Intent intent;
public final String hint;
- public final IResultReceiver receiver;
+ public final IAssistDataReceiver receiver;
public final int userHandle;
public boolean haveResult = false;
public Bundle result = null;
@@ -791,7 +793,8 @@
public Bundle receiverExtras;
public PendingAssistExtras(ActivityRecord _activity, Bundle _extras, Intent _intent,
- String _hint, IResultReceiver _receiver, Bundle _receiverExtras, int _userHandle) {
+ String _hint, IAssistDataReceiver _receiver, Bundle _receiverExtras,
+ int _userHandle) {
activity = _activity;
extras = _extras;
intent = _intent;
@@ -4678,15 +4681,7 @@
Intent intent, String resolvedType, IVoiceInteractionSession session,
IVoiceInteractor interactor, int startFlags, ProfilerInfo profilerInfo,
Bundle bOptions, int userId) {
- if (checkCallingPermission(Manifest.permission.BIND_VOICE_INTERACTION)
- != PackageManager.PERMISSION_GRANTED) {
- String msg = "Permission Denial: startVoiceActivity() from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid()
- + " requires " + android.Manifest.permission.BIND_VOICE_INTERACTION;
- Slog.w(TAG, msg);
- throw new SecurityException(msg);
- }
+ enforceCallingPermission(BIND_VOICE_INTERACTION, "startVoiceActivity()");
if (session == null || interactor == null) {
throw new NullPointerException("null session or interactor");
}
@@ -4701,15 +4696,7 @@
@Override
public int startAssistantActivity(String callingPackage, int callingPid, int callingUid,
Intent intent, String resolvedType, Bundle bOptions, int userId) {
- if (checkCallingPermission(Manifest.permission.BIND_VOICE_INTERACTION)
- != PackageManager.PERMISSION_GRANTED) {
- final String msg = "Permission Denial: startAssistantActivity() from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid()
- + " requires " + Manifest.permission.BIND_VOICE_INTERACTION;
- Slog.w(TAG, msg);
- throw new SecurityException(msg);
- }
+ enforceCallingPermission(BIND_VOICE_INTERACTION, "startAssistantActivity()");
userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, false,
ALLOW_FULL_ONLY, "startAssistantActivity", null);
return mActivityStarter.startActivityMayWait(null, callingUid, callingPackage, intent,
@@ -12938,7 +12925,7 @@
}
@Override
- public boolean requestAssistContextExtras(int requestType, IResultReceiver receiver,
+ public boolean requestAssistContextExtras(int requestType, IAssistDataReceiver receiver,
Bundle receiverExtras, IBinder activityToken, boolean focused, boolean newSessionId) {
return enqueueAssistContext(requestType, null, null, receiver, receiverExtras,
activityToken, focused, newSessionId, UserHandle.getCallingUserId(), null,
@@ -12946,7 +12933,7 @@
}
@Override
- public boolean requestAutofillData(IResultReceiver receiver, Bundle receiverExtras,
+ public boolean requestAutofillData(IAssistDataReceiver receiver, Bundle receiverExtras,
IBinder activityToken, int flags) {
return enqueueAssistContext(ActivityManager.ASSIST_CONTEXT_AUTOFILL, null, null,
receiver, receiverExtras, activityToken, true, true, UserHandle.getCallingUserId(),
@@ -12954,7 +12941,7 @@
}
private PendingAssistExtras enqueueAssistContext(int requestType, Intent intent, String hint,
- IResultReceiver receiver, Bundle receiverExtras, IBinder activityToken,
+ IAssistDataReceiver receiver, Bundle receiverExtras, IBinder activityToken,
boolean focused, boolean newSessionId, int userHandle, Bundle args, long timeout,
int flags) {
enforceCallingPermission(android.Manifest.permission.GET_TOP_ACTIVITY_INFO,
@@ -13022,7 +13009,7 @@
}
void pendingAssistExtrasTimedOut(PendingAssistExtras pae) {
- IResultReceiver receiver;
+ IAssistDataReceiver receiver;
synchronized (this) {
mPendingAssistExtras.remove(pae);
receiver = pae.receiver;
@@ -13034,7 +13021,7 @@
sendBundle.putBundle(VoiceInteractionSession.KEY_RECEIVER_EXTRAS,
pae.receiverExtras);
try {
- pae.receiver.send(0, sendBundle);
+ pae.receiver.onHandleAssistData(sendBundle);
} catch (RemoteException e) {
}
}
@@ -13072,7 +13059,7 @@
}
}
// We are now ready to launch the assist activity.
- IResultReceiver sendReceiver = null;
+ IAssistDataReceiver sendReceiver = null;
Bundle sendBundle = null;
synchronized (this) {
buildAssistBundleLocked(pae, extras);
@@ -13094,7 +13081,7 @@
}
if (sendReceiver != null) {
try {
- sendReceiver.send(0, sendBundle);
+ sendReceiver.onHandleAssistData(sendBundle);
} catch (RemoteException e) {
}
return;
diff --git a/services/core/java/com/android/server/am/AssistDataRequester.java b/services/core/java/com/android/server/am/AssistDataRequester.java
new file mode 100644
index 0000000..60f3d22
--- /dev/null
+++ b/services/core/java/com/android/server/am/AssistDataRequester.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import static android.app.ActivityManager.ASSIST_CONTEXT_FULL;
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.OP_NONE;
+import static android.service.voice.VoiceInteractionSession.KEY_RECEIVER_EXTRAS;
+
+import android.app.AppOpsManager;
+import android.app.IActivityManager;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.view.IWindowManager;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.app.IAssistDataReceiver;
+import com.android.internal.logging.MetricsLogger;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Helper class to asynchronously fetch the assist data and screenshot from the current running
+ * activities. It manages received data and calls back to the owner when the owner is ready to
+ * receive the data itself.
+ */
+public class AssistDataRequester extends IAssistDataReceiver.Stub {
+
+ public static final String KEY_RECEIVER_EXTRA_COUNT = "count";
+ public static final String KEY_RECEIVER_EXTRA_INDEX = "index";
+
+ private IActivityManager mService;
+ private IWindowManager mWindowManager;
+ private Context mContext;
+ private AppOpsManager mAppOpsManager;
+
+ private AssistDataRequesterCallbacks mCallbacks;
+ private Object mCallbacksLock;
+
+ private int mRequestStructureAppOps;
+ private int mRequestScreenshotAppOps;
+ private boolean mCanceled;
+ private int mPendingDataCount;
+ private int mPendingScreenshotCount;
+ private final ArrayList<Bundle> mAssistData = new ArrayList<>();
+ private final ArrayList<Bitmap> mAssistScreenshot = new ArrayList<>();
+
+
+ /**
+ * Interface to handle the events from the fetcher.
+ */
+ public interface AssistDataRequesterCallbacks {
+ /**
+ * @return whether the currently received assist data can be handled by the callbacks.
+ */
+ @GuardedBy("mCallbacksLock")
+ boolean canHandleReceivedAssistDataLocked();
+
+ /**
+ * Called when we receive asynchronous assist data. This call is only made if the
+ * {@param fetchData} argument to requestAssistData() is true, and if the current activity
+ * allows assist data to be fetched. In addition, the callback will be made with the
+ * {@param mCallbacksLock} held, and only if {@link #canHandleReceivedAssistDataLocked()}
+ * is true.
+ */
+ @GuardedBy("mCallbacksLock")
+ void onAssistDataReceivedLocked(Bundle data, int activityIndex, int activityCount);
+
+ /**
+ * Called when we receive asynchronous assist screenshot. This call is only made if
+ * {@param fetchScreenshot} argument to requestAssistData() is true, and if the current
+ * activity allows assist data to be fetched. In addition, the callback will be made with
+ * the {@param mCallbacksLock} held, and only if
+ * {@link #canHandleReceivedAssistDataLocked()} is true.
+ */
+ @GuardedBy("mCallbacksLock")
+ void onAssistScreenshotReceivedLocked(Bitmap screenshot);
+ }
+
+ /**
+ * @param callbacks The callbacks to handle the asynchronous reply with the assist data.
+ * @param callbacksLock The lock for the requester to hold when calling any of the
+ * {@param callbacks}. The owner should also take care in locking
+ * appropriately when calling into this requester.
+ * @param requestStructureAppOps The app ops to check before requesting the assist structure
+ * @param requestScreenshotAppOps The app ops to check before requesting the assist screenshot.
+ * This can be {@link AppOpsManager#OP_NONE} to indicate that
+ * screenshots should never be fetched.
+ */
+ public AssistDataRequester(Context context, IActivityManager service,
+ IWindowManager windowManager, AppOpsManager appOpsManager,
+ AssistDataRequesterCallbacks callbacks, Object callbacksLock,
+ int requestStructureAppOps, int requestScreenshotAppOps) {
+ mCallbacks = callbacks;
+ mCallbacksLock = callbacksLock;
+ mWindowManager = windowManager;
+ mService = service;
+ mContext = context;
+ mAppOpsManager = appOpsManager;
+ mRequestStructureAppOps = requestStructureAppOps;
+ mRequestScreenshotAppOps = requestScreenshotAppOps;
+ }
+
+ /**
+ * Request that assist data be loaded asynchronously. The resulting data will be provided
+ * through the {@link AssistDataRequesterCallbacks}.
+ *
+ * @param activityTokens the list of visible activities
+ * @param fetchData whether or not to fetch the assist data, only applies if the caller is
+ * allowed to fetch the assist data, and the current activity allows assist data to be
+ * fetched from it
+ * @param fetchScreenshot whether or not to fetch the screenshot, only applies if fetchData is
+ * true, the caller is allowed to fetch the assist data, and the current activity allows
+ * assist data to be fetched from it
+ */
+ public void requestAssistData(List<IBinder> activityTokens, boolean fetchData,
+ boolean fetchScreenshot, int callingUid, String callingPackage) {
+ // TODO: Better handle the cancel case if a request can be reused
+ // TODO: Known issue, if the assist data is not allowed on the current activity, then no
+ // assist data is requested for any of the other activities
+
+ // Early exit if there are no activity to fetch for
+ if (activityTokens.isEmpty()) {
+ return;
+ }
+
+ // Ensure that the current activity supports assist data
+ boolean isAssistDataAllowed = false;
+ try {
+ isAssistDataAllowed = mService.isAssistDataAllowedOnCurrentActivity();
+ } catch (RemoteException e) {
+ // Should never happen
+ }
+ fetchData &= isAssistDataAllowed;
+ fetchScreenshot &= fetchData && isAssistDataAllowed
+ && (mRequestScreenshotAppOps != OP_NONE);
+
+ mCanceled = false;
+ mPendingDataCount = 0;
+ mPendingScreenshotCount = 0;
+ mAssistData.clear();
+ mAssistScreenshot.clear();
+
+ if (fetchData) {
+ if (mAppOpsManager.checkOpNoThrow(mRequestStructureAppOps, callingUid, callingPackage)
+ == MODE_ALLOWED) {
+ final int numActivities = activityTokens.size();
+ for (int i = 0; i < numActivities; i++) {
+ IBinder topActivity = activityTokens.get(i);
+ try {
+ MetricsLogger.count(mContext, "assist_with_context", 1);
+ Bundle receiverExtras = new Bundle();
+ receiverExtras.putInt(KEY_RECEIVER_EXTRA_INDEX, i);
+ receiverExtras.putInt(KEY_RECEIVER_EXTRA_COUNT, numActivities);
+ if (mService.requestAssistContextExtras(ASSIST_CONTEXT_FULL, this,
+ receiverExtras, topActivity, /* focused= */ i == 0,
+ /* newSessionId= */ i == 0)) {
+ mPendingDataCount++;
+ } else if (i == 0) {
+ // Wasn't allowed... given that, let's not do the screenshot either.
+ dispatchAssistDataReceived(null);
+ fetchScreenshot = false;
+ break;
+ }
+ } catch (RemoteException e) {
+ // Can't happen
+ }
+ }
+ } else {
+ // Wasn't allowed... given that, let's not do the screenshot either.
+ dispatchAssistDataReceived(null);
+ fetchScreenshot = false;
+ }
+ }
+
+ if (fetchScreenshot) {
+ if (mAppOpsManager.checkOpNoThrow(mRequestScreenshotAppOps, callingUid, callingPackage)
+ == MODE_ALLOWED) {
+ try {
+ MetricsLogger.count(mContext, "assist_with_screen", 1);
+ mPendingScreenshotCount++;
+ mWindowManager.requestAssistScreenshot(this);
+ } catch (RemoteException e) {
+ // Can't happen
+ }
+ } else {
+ dispatchAssistScreenshotReceived(null);
+ }
+ }
+ }
+
+ /**
+ * This call should only be made when the callbacks are capable of handling the received assist
+ * data.
+ */
+ public void processPendingAssistData() {
+ final int dataCount = mAssistData.size();
+ for (int i = 0; i < dataCount; i++) {
+ dispatchAssistDataReceived(mAssistData.get(i));
+ }
+ final int screenshotsCount = mAssistScreenshot.size();
+ for (int i = 0; i < screenshotsCount; i++) {
+ dispatchAssistScreenshotReceived(mAssistScreenshot.get(i));
+ }
+ }
+
+ public int getPendingDataCount() {
+ return mPendingDataCount;
+ }
+
+ public int getPendingScreenshotCount() {
+ return mPendingScreenshotCount;
+ }
+
+ /**
+ * Cancels the current request for the assist data.
+ */
+ public void cancel() {
+ // Reset the pending data count, if we receive new assist data after this point, it will
+ // be ignored
+ mCanceled = true;
+ mPendingDataCount = 0;
+ mPendingScreenshotCount = 0;
+ mAssistData.clear();
+ mAssistScreenshot.clear();
+ }
+
+ @Override
+ public void onHandleAssistData(Bundle data) {
+ synchronized (mCallbacksLock) {
+ if (mCanceled) {
+ return;
+ }
+ mPendingDataCount--;
+
+ if (mCallbacks.canHandleReceivedAssistDataLocked()) {
+ // Process any pending data and dispatch the new data as well
+ processPendingAssistData();
+ dispatchAssistDataReceived(data);
+ } else {
+ // Queue up the data for processing later
+ mAssistData.add(data);
+ }
+ }
+ }
+
+ @Override
+ public void onHandleAssistScreenshot(Bitmap screenshot) {
+ synchronized (mCallbacksLock) {
+ if (mCanceled) {
+ return;
+ }
+ mPendingScreenshotCount--;
+
+ if (mCallbacks.canHandleReceivedAssistDataLocked()) {
+ // Process any pending data and dispatch the new data as well
+ processPendingAssistData();
+ dispatchAssistScreenshotReceived(screenshot);
+ } else {
+ // Queue up the data for processing later
+ mAssistScreenshot.add(screenshot);
+ }
+ }
+ }
+
+ private void dispatchAssistDataReceived(Bundle data) {
+ int activityIndex = 0;
+ int activityCount = 0;
+ final Bundle receiverExtras = data != null ? data.getBundle(KEY_RECEIVER_EXTRAS) : null;
+ if (receiverExtras != null) {
+ activityIndex = receiverExtras.getInt(KEY_RECEIVER_EXTRA_INDEX);
+ activityCount = receiverExtras.getInt(KEY_RECEIVER_EXTRA_COUNT);
+ }
+ mCallbacks.onAssistDataReceivedLocked(data, activityIndex, activityCount);
+ }
+
+ private void dispatchAssistScreenshotReceived(Bitmap screenshot) {
+ mCallbacks.onAssistScreenshotReceivedLocked(screenshot);
+ }
+
+ public void dump(String prefix, PrintWriter pw) {
+ pw.print(prefix); pw.print("mPendingDataCount="); pw.println(mPendingDataCount);
+ pw.print(prefix); pw.print("mAssistData="); pw.println(mAssistData);
+ pw.print(prefix); pw.print("mPendingScreenshotCount="); pw.println(mPendingScreenshotCount);
+ pw.print(prefix); pw.print("mAssistScreenshot="); pw.println(mAssistScreenshot);
+ }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index f31ea67..70035c3 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -226,7 +226,7 @@
import android.view.inputmethod.InputMethodManagerInternal;
import com.android.internal.R;
-import com.android.internal.app.IAssistScreenshotReceiver;
+import com.android.internal.app.IAssistDataReceiver;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.internal.os.IResultReceiver;
import com.android.internal.policy.IKeyguardDismissCallback;
@@ -3783,7 +3783,7 @@
* of the target image.
*/
@Override
- public boolean requestAssistScreenshot(final IAssistScreenshotReceiver receiver) {
+ public boolean requestAssistScreenshot(final IAssistDataReceiver receiver) {
if (!checkCallingPermission(READ_FRAME_BUFFER,
"requestAssistScreenshot()")) {
throw new SecurityException("Requires READ_FRAME_BUFFER permission");
@@ -3795,7 +3795,7 @@
1f /* frameScale */, Bitmap.Config.ARGB_8888, false /* wallpaperOnly */,
false /* includeDecor */);
try {
- receiver.send(bm);
+ receiver.onHandleAssistScreenshot(bm);
} catch (RemoteException e) {
}
});
diff --git a/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java b/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java
new file mode 100644
index 0000000..bec46db
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_ERRORED;
+import static android.app.AppOpsManager.OP_ASSIST_SCREENSHOT;
+import static android.app.AppOpsManager.OP_ASSIST_STRUCTURE;
+import static android.graphics.Bitmap.Config.ARGB_8888;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import android.app.AppOpsManager;
+import android.app.IActivityManager;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
+import android.view.IWindowManager;
+
+import com.android.server.am.AssistDataRequester.AssistDataRequesterCallbacks;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Note: Currently, we only support fetching the screenshot for the current application, so the
+ * screenshot checks are hardcoded accordingly.
+ *
+ * runtest --path frameworks/base/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java
+ */
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class AssistDataRequesterTest extends ActivityTestsBase {
+
+ private static final String TAG = AssistDataRequesterTest.class.getSimpleName();
+
+ private static final boolean CURRENT_ACTIVITY_ASSIST_ALLOWED = true;
+ private static final boolean CALLER_ASSIST_STRUCTURE_ALLOWED = true;
+ private static final boolean CALLER_ASSIST_SCREENSHOT_ALLOWED = true;
+ private static final boolean FETCH_DATA = true;
+ private static final boolean FETCH_SCREENSHOTS = true;
+
+ private static final int TEST_UID = 0;
+ private static final String TEST_PACKAGE = "";
+
+ private Context mContext;
+ private AssistDataRequester mDataRequester;
+ private Callbacks mCallbacks;
+ private Object mCallbacksLock;
+ private Handler mHandler;
+ private IActivityManager mAm;
+ private IWindowManager mWm;
+ private AppOpsManager mAppOpsManager;
+
+ /**
+ * The requests to fetch assist data are done incrementally from the text thread, and we
+ * immediately post onto the main thread handler below, which would immediately make the
+ * callback and decrement the pending counts. In order to assert the pending counts, we defer
+ * the callbacks on the test-side until after we flip the gate, after which we can drain the
+ * main thread handler and make assertions on the actual callbacks
+ */
+ private CountDownLatch mGate;
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ mAm = mock(IActivityManager.class);
+ mWm = mock(IWindowManager.class);
+ mAppOpsManager = mock(AppOpsManager.class);
+ mContext = InstrumentationRegistry.getContext();
+ mHandler = new Handler(Looper.getMainLooper());
+ mCallbacksLock = new Object();
+ mCallbacks = new Callbacks();
+ mDataRequester = new AssistDataRequester(mContext, mAm, mWm, mAppOpsManager, mCallbacks,
+ mCallbacksLock, OP_ASSIST_STRUCTURE, OP_ASSIST_SCREENSHOT);
+
+ // Gate the continuation of the assist data callbacks until we are ready within the tests
+ mGate = new CountDownLatch(1);
+ doAnswer(invocation -> {
+ mHandler.post(() -> {
+ try {
+ mGate.await(10, TimeUnit.SECONDS);
+ mDataRequester.onHandleAssistData(new Bundle());
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Failed to wait", e);
+ }
+ });
+ return true;
+ }).when(mAm).requestAssistContextExtras(anyInt(), any(), any(), any(), anyBoolean(),
+ anyBoolean());
+ doAnswer(invocation -> {
+ mHandler.post(() -> {
+ try {
+ mGate.await(10, TimeUnit.SECONDS);
+ mDataRequester.onHandleAssistScreenshot(Bitmap.createBitmap(1, 1,
+ ARGB_8888));
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Failed to wait", e);
+ }
+ });
+ return true;
+ }).when(mWm).requestAssistScreenshot(any());
+ }
+
+ private void setupMocks(boolean currentActivityAssistAllowed, boolean assistStructureAllowed,
+ boolean assistScreenshotAllowed) throws Exception {
+ doReturn(currentActivityAssistAllowed).when(mAm).isAssistDataAllowedOnCurrentActivity();
+ doReturn(assistStructureAllowed ? MODE_ALLOWED : MODE_ERRORED).when(mAppOpsManager)
+ .checkOpNoThrow(eq(OP_ASSIST_STRUCTURE), anyInt(), anyString());
+ doReturn(assistScreenshotAllowed ? MODE_ALLOWED : MODE_ERRORED).when(mAppOpsManager)
+ .checkOpNoThrow(eq(OP_ASSIST_SCREENSHOT), anyInt(), anyString());
+ }
+
+ @Test
+ public void testRequestData() throws Exception {
+ setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
+ CALLER_ASSIST_SCREENSHOT_ALLOWED);
+
+ mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
+ TEST_UID, TEST_PACKAGE);
+ assertReceivedDataCount(5, 5, 1, 1);
+ }
+
+ @Test
+ public void testEmptyActivities_expectNoCallbacks() throws Exception {
+ setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
+ CALLER_ASSIST_SCREENSHOT_ALLOWED);
+
+ mDataRequester.requestAssistData(createActivityList(0), FETCH_DATA, FETCH_SCREENSHOTS,
+ TEST_UID, TEST_PACKAGE);
+ assertReceivedDataCount(0, 0, 0, 0);
+ }
+
+ @Test
+ public void testCurrentAppDisallow_expectNoCallbacks() throws Exception {
+ setupMocks(!CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
+ CALLER_ASSIST_SCREENSHOT_ALLOWED);
+
+ mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
+ TEST_UID, TEST_PACKAGE);
+ assertReceivedDataCount(0, 0, 0, 0);
+ }
+
+ @Test
+ public void testProcessPendingData() throws Exception {
+ setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
+ CALLER_ASSIST_SCREENSHOT_ALLOWED);
+
+ mCallbacks.canHandleReceivedData = false;
+ mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
+ TEST_UID, TEST_PACKAGE);
+ assertTrue(mDataRequester.getPendingDataCount() == 5);
+ assertTrue(mDataRequester.getPendingScreenshotCount() == 1);
+ mGate.countDown();
+ waitForIdle(mHandler);
+
+ // Callbacks still not ready to receive, but all pending data is received
+ assertTrue(mDataRequester.getPendingDataCount() == 0);
+ assertTrue(mDataRequester.getPendingScreenshotCount() == 0);
+ assertTrue(mCallbacks.receivedData.isEmpty());
+ assertTrue(mCallbacks.receivedScreenshots.isEmpty());
+
+ mCallbacks.canHandleReceivedData = true;
+ mDataRequester.processPendingAssistData();
+ assertTrue(mCallbacks.receivedData.size() == 5);
+ assertTrue(mCallbacks.receivedScreenshots.size() == 1);
+ }
+
+ @Test
+ public void testNoFetchData_expectNoCallbacks() throws Exception {
+ setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
+ CALLER_ASSIST_SCREENSHOT_ALLOWED);
+
+ mDataRequester.requestAssistData(createActivityList(5), !FETCH_DATA, FETCH_SCREENSHOTS,
+ TEST_UID, TEST_PACKAGE);
+ assertReceivedDataCount(0, 0, 0, 0);
+ }
+
+ @Test
+ public void testDisallowAssistStructure_expectNullDataCallbacks() throws Exception {
+ setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, !CALLER_ASSIST_STRUCTURE_ALLOWED,
+ CALLER_ASSIST_SCREENSHOT_ALLOWED);
+
+ mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
+ TEST_UID, TEST_PACKAGE);
+ // Expect a single null data when the appops is denied
+ assertReceivedDataCount(0, 1, 0, 0);
+ }
+
+ @Test
+ public void testDisallowAssistContextExtras_expectNullDataCallbacks() throws Exception {
+ setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
+ CALLER_ASSIST_SCREENSHOT_ALLOWED);
+ doReturn(false).when(mAm).requestAssistContextExtras(anyInt(), any(), any(), any(),
+ anyBoolean(), anyBoolean());
+
+ mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
+ TEST_UID, TEST_PACKAGE);
+ // Expect a single null data when requestAssistContextExtras() fails
+ assertReceivedDataCount(0, 1, 0, 0);
+ }
+
+ @Test
+ public void testNoFetchScreenshots_expectNoScreenshotCallbacks() throws Exception {
+ setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
+ CALLER_ASSIST_SCREENSHOT_ALLOWED);
+
+ mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, !FETCH_SCREENSHOTS,
+ TEST_UID, TEST_PACKAGE);
+ assertReceivedDataCount(5, 5, 0, 0);
+ }
+
+ @Test
+ public void testDisallowAssistScreenshot_expectNullScreenshotCallback() throws Exception {
+ setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
+ !CALLER_ASSIST_SCREENSHOT_ALLOWED);
+
+ mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
+ TEST_UID, TEST_PACKAGE);
+ // Expect a single null screenshot when the appops is denied
+ assertReceivedDataCount(5, 5, 0, 1);
+ }
+
+ private void assertReceivedDataCount(int numPendingData, int numReceivedData,
+ int numPendingScreenshots, int numReceivedScreenshots) throws Exception {
+ assertTrue("Expected " + numPendingData + " pending data, got "
+ + mDataRequester.getPendingDataCount(),
+ mDataRequester.getPendingDataCount() == numPendingData);
+ assertTrue("Expected " + numPendingScreenshots + " pending screenshots, got "
+ + mDataRequester.getPendingScreenshotCount(),
+ mDataRequester.getPendingScreenshotCount() == numPendingScreenshots);
+ mGate.countDown();
+ waitForIdle(mHandler);
+ assertTrue("Expected " + numReceivedData + " data, received "
+ + mCallbacks.receivedData.size(),
+ mCallbacks.receivedData.size() == numReceivedData);
+ assertTrue("Expected " + numReceivedScreenshots + " screenshots, received "
+ + mCallbacks.receivedScreenshots.size(),
+ mCallbacks.receivedScreenshots.size() == numReceivedScreenshots);
+ }
+
+ private List<IBinder> createActivityList(int size) {
+ ArrayList<IBinder> activities = new ArrayList<>();
+ for (int i = 0; i < size; i++) {
+ activities.add(mock(IBinder.class));
+ }
+ return activities;
+ }
+
+ public void waitForIdle(Handler h) throws Exception {
+ if (Looper.myLooper() == h.getLooper()) {
+ throw new RuntimeException("This method can not be called from the waiting looper");
+ }
+ CountDownLatch latch = new CountDownLatch(1);
+ h.post(() -> latch.countDown());
+ latch.await(2, TimeUnit.SECONDS);
+ }
+
+ private static class Callbacks implements AssistDataRequesterCallbacks {
+
+ boolean canHandleReceivedData = true;
+ ArrayList<Bundle> receivedData = new ArrayList<>();
+ ArrayList<Bitmap> receivedScreenshots = new ArrayList<>();
+
+ @Override
+ public boolean canHandleReceivedAssistDataLocked() {
+ return canHandleReceivedData;
+ }
+
+ @Override
+ public void onAssistDataReceivedLocked(Bundle data, int activityIndex, int activityCount) {
+ receivedData.add(data);
+ }
+
+ @Override
+ public void onAssistScreenshotReceivedLocked(Bitmap screenshot) {
+ receivedScreenshots.add(screenshot);
+ }
+ }
+}
\ No newline at end of file
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index b040a63..7541b92 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -55,6 +55,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.List;
class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConnection.Callback {
@@ -162,13 +163,16 @@
mInfo.getServiceInfo().applicationInfo.uid, mHandler);
}
List<IBinder> activityTokens = null;
- if (activityToken == null) {
+ if (activityToken != null) {
+ activityTokens = new ArrayList<>();
+ activityTokens.add(activityToken);
+ } else {
// Let's get top activities from all visible stacks
activityTokens = LocalServices.getService(ActivityManagerInternal.class)
.getTopVisibleActivities();
}
return mActiveSession.showLocked(args, flags, mDisabledShowContext, showCallback,
- activityToken, activityTokens);
+ activityTokens);
}
public boolean hideSessionLocked() {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index d394d63..c9b0eac 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -16,6 +16,13 @@
package com.android.server.voiceinteraction;
+import static android.app.AppOpsManager.OP_ASSIST_SCREENSHOT;
+import static android.app.AppOpsManager.OP_ASSIST_STRUCTURE;
+import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
+import static android.content.Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
+
import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.IActivityManager;
@@ -43,31 +50,24 @@
import android.service.voice.VoiceInteractionSession;
import android.util.Slog;
import android.view.IWindowManager;
-import android.view.WindowManager;
import com.android.internal.app.AssistUtils;
-import com.android.internal.app.IAssistScreenshotReceiver;
import com.android.internal.app.IVoiceInteractionSessionShowCallback;
import com.android.internal.app.IVoiceInteractor;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.os.IResultReceiver;
import com.android.server.LocalServices;
+import com.android.server.am.AssistDataRequester;
+import com.android.server.am.AssistDataRequester.AssistDataRequesterCallbacks;
import com.android.server.statusbar.StatusBarManagerInternal;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
-
-final class VoiceInteractionSessionConnection implements ServiceConnection {
+final class VoiceInteractionSessionConnection implements ServiceConnection,
+ AssistDataRequesterCallbacks {
final static String TAG = "VoiceInteractionServiceManager";
- private static final String KEY_RECEIVER_EXTRA_COUNT = "count";
- private static final String KEY_RECEIVER_EXTRA_INDEX = "index";
-
final IBinder mToken = new Binder();
final Object mLock;
final ComponentName mSessionComponentName;
@@ -90,27 +90,8 @@
IVoiceInteractionSessionService mService;
IVoiceInteractionSession mSession;
IVoiceInteractor mInteractor;
- boolean mHaveAssistData;
- int mPendingAssistDataCount;
- ArrayList<AssistDataForActivity> mAssistData = new ArrayList<>();
- boolean mHaveScreenshot;
- Bitmap mScreenshot;
ArrayList<IVoiceInteractionSessionShowCallback> mPendingShowCallbacks = new ArrayList<>();
-
- static class AssistDataForActivity {
- int activityIndex;
- int activityCount;
- Bundle data;
-
- public AssistDataForActivity(Bundle data) {
- this.data = data;
- Bundle receiverExtras = data.getBundle(VoiceInteractionSession.KEY_RECEIVER_EXTRAS);
- if (receiverExtras != null) {
- activityIndex = receiverExtras.getInt(KEY_RECEIVER_EXTRA_INDEX);
- activityCount = receiverExtras.getInt(KEY_RECEIVER_EXTRA_COUNT);
- }
- }
- }
+ AssistDataRequester mAssistDataRequester;
IVoiceInteractionSessionShowCallback mShowCallback =
new IVoiceInteractionSessionShowCallback.Stub() {
@@ -146,32 +127,6 @@
}
};
- final IResultReceiver mAssistReceiver = new IResultReceiver.Stub() {
- @Override
- public void send(int resultCode, Bundle resultData) throws RemoteException {
- synchronized (mLock) {
- if (mShown) {
- mHaveAssistData = true;
- mAssistData.add(new AssistDataForActivity(resultData));
- deliverSessionDataLocked();
- }
- }
- }
- };
-
- final IAssistScreenshotReceiver mScreenshotReceiver = new IAssistScreenshotReceiver.Stub() {
- @Override
- public void send(Bitmap screenshot) throws RemoteException {
- synchronized (mLock) {
- if (mShown) {
- mHaveScreenshot = true;
- mScreenshot = screenshot;
- deliverSessionDataLocked();
- }
- }
- }
- };
-
public VoiceInteractionSessionConnection(Object lock, ComponentName component, int user,
Context context, Callback callback, int callingUid, Handler handler) {
mLock = lock;
@@ -185,6 +140,9 @@
mIWindowManager = IWindowManager.Stub.asInterface(
ServiceManager.getService(Context.WINDOW_SERVICE));
mAppOps = context.getSystemService(AppOpsManager.class);
+ mAssistDataRequester = new AssistDataRequester(mContext, mAm, mIWindowManager,
+ (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE),
+ this, mLock, OP_ASSIST_STRUCTURE, OP_ASSIST_SCREENSHOT);
IBinder permOwner = null;
try {
permOwner = mAm.newUriPermissionOwner("voicesession:"
@@ -224,8 +182,7 @@
}
public boolean showLocked(Bundle args, int flags, int disabledContext,
- IVoiceInteractionSessionShowCallback showCallback, IBinder activityToken,
- List<IBinder> topActivities) {
+ IVoiceInteractionSessionShowCallback showCallback, List<IBinder> topActivities) {
if (mBound) {
if (!mFullyBound) {
mFullyBound = mContext.bindServiceAsUser(mBindIntent, mFullConnection,
@@ -233,75 +190,18 @@
| Context.BIND_FOREGROUND_SERVICE,
new UserHandle(mUser));
}
+
mShown = true;
- boolean isAssistDataAllowed = true;
- try {
- isAssistDataAllowed = mAm.isAssistDataAllowedOnCurrentActivity();
- } catch (RemoteException e) {
- }
- disabledContext |= getUserDisabledShowContextLocked();
- boolean structureEnabled = isAssistDataAllowed
- && (disabledContext&VoiceInteractionSession.SHOW_WITH_ASSIST) == 0;
- boolean screenshotEnabled = isAssistDataAllowed && structureEnabled
- && (disabledContext&VoiceInteractionSession.SHOW_WITH_SCREENSHOT) == 0;
mShowArgs = args;
mShowFlags = flags;
- mHaveAssistData = false;
- mPendingAssistDataCount = 0;
- boolean needDisclosure = false;
- if ((flags&VoiceInteractionSession.SHOW_WITH_ASSIST) != 0) {
- if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ASSIST_STRUCTURE, mCallingUid,
- mSessionComponentName.getPackageName()) == AppOpsManager.MODE_ALLOWED
- && structureEnabled) {
- mAssistData.clear();
- final int count = activityToken != null ? 1 : topActivities.size();
- for (int i = 0; i < count; i++) {
- IBinder topActivity = count == 1 ? activityToken : topActivities.get(i);
- try {
- MetricsLogger.count(mContext, "assist_with_context", 1);
- Bundle receiverExtras = new Bundle();
- receiverExtras.putInt(KEY_RECEIVER_EXTRA_INDEX, i);
- receiverExtras.putInt(KEY_RECEIVER_EXTRA_COUNT, count);
- if (mAm.requestAssistContextExtras(ActivityManager.ASSIST_CONTEXT_FULL,
- mAssistReceiver, receiverExtras, topActivity,
- /* focused= */ i == 0, /* newSessionId= */ i == 0)) {
- needDisclosure = true;
- mPendingAssistDataCount++;
- } else if (i == 0) {
- // Wasn't allowed... given that, let's not do the screenshot either.
- mHaveAssistData = true;
- mAssistData.clear();
- screenshotEnabled = false;
- break;
- }
- } catch (RemoteException e) {
- }
- }
- } else {
- mHaveAssistData = true;
- mAssistData.clear();
- }
- } else {
- mAssistData.clear();
- }
- mHaveScreenshot = false;
- if ((flags&VoiceInteractionSession.SHOW_WITH_SCREENSHOT) != 0) {
- if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ASSIST_SCREENSHOT, mCallingUid,
- mSessionComponentName.getPackageName()) == AppOpsManager.MODE_ALLOWED
- && screenshotEnabled) {
- try {
- MetricsLogger.count(mContext, "assist_with_screen", 1);
- needDisclosure = true;
- mIWindowManager.requestAssistScreenshot(mScreenshotReceiver);
- } catch (RemoteException e) {
- }
- } else {
- mHaveScreenshot = true;
- mScreenshot = null;
- }
- } else {
- mScreenshot = null;
- }
+
+ mAssistDataRequester.requestAssistData(topActivities,
+ (disabledContext & VoiceInteractionSession.SHOW_WITH_ASSIST) == 0,
+ (disabledContext & VoiceInteractionSession.SHOW_WITH_SCREENSHOT) == 0,
+ mCallingUid, mSessionComponentName.getPackageName());
+
+ boolean needDisclosure = mAssistDataRequester.getPendingDataCount() > 0
+ || mAssistDataRequester.getPendingScreenshotCount() > 0;
if (needDisclosure && AssistUtils.shouldDisclose(mContext, mSessionComponentName)) {
mHandler.post(mShowAssistDisclosureRunnable);
}
@@ -312,7 +212,6 @@
mShowFlags = 0;
} catch (RemoteException e) {
}
- deliverSessionDataLocked();
} else if (showCallback != null) {
mPendingShowCallbacks.add(showCallback);
}
@@ -328,6 +227,59 @@
return false;
}
+ @Override
+ public boolean canHandleReceivedAssistDataLocked() {
+ return mSession != null;
+ }
+
+ @Override
+ public void onAssistDataReceivedLocked(Bundle data, int activityIndex, int activityCount) {
+ if (data == null) {
+ try {
+ mSession.handleAssist(null, null, null, 0, 0);
+ } catch (RemoteException e) {
+ // Can't happen
+ }
+ } else {
+ final Bundle assistData = data.getBundle(VoiceInteractionSession.KEY_DATA);
+ final AssistStructure structure = data.getParcelable(
+ VoiceInteractionSession.KEY_STRUCTURE);
+ final AssistContent content = data.getParcelable(
+ VoiceInteractionSession.KEY_CONTENT);
+ final int uid = data.getInt(Intent.EXTRA_ASSIST_UID, -1);
+ if (uid >= 0 && content != null) {
+ Intent intent = content.getIntent();
+ if (intent != null) {
+ ClipData clipData = intent.getClipData();
+ if (clipData != null && Intent.isAccessUriMode(intent.getFlags())) {
+ grantClipDataPermissions(clipData, intent.getFlags(), uid,
+ mCallingUid, mSessionComponentName.getPackageName());
+ }
+ }
+ ClipData clipData = content.getClipData();
+ if (clipData != null) {
+ grantClipDataPermissions(clipData, FLAG_GRANT_READ_URI_PERMISSION,
+ uid, mCallingUid, mSessionComponentName.getPackageName());
+ }
+ }
+ try {
+ mSession.handleAssist(assistData, structure, content, activityIndex,
+ activityCount);
+ } catch (RemoteException e) {
+ // Can't happen
+ }
+ }
+ }
+
+ @Override
+ public void onAssistScreenshotReceivedLocked(Bitmap screenshot) {
+ try {
+ mSession.handleScreenshot(screenshot);
+ } catch (RemoteException e) {
+ // Can't happen
+ }
+ }
+
void grantUriPermission(Uri uri, int mode, int srcUid, int destUid, String destPkg) {
if (!"content".equals(uri.getScheme())) {
return;
@@ -341,7 +293,7 @@
int sourceUserId = ContentProvider.getUserIdFromUri(uri, mUser);
uri = ContentProvider.getUriWithoutUserId(uri);
mAm.grantUriPermissionFromOwner(mPermissionOwner, srcUid, destPkg,
- uri, Intent.FLAG_GRANT_READ_URI_PERMISSION, sourceUserId, mUser);
+ uri, FLAG_GRANT_READ_URI_PERMISSION, sourceUserId, mUser);
} catch (RemoteException e) {
} catch (SecurityException e) {
Slog.w(TAG, "Can't propagate permission", e);
@@ -370,89 +322,13 @@
}
}
- void deliverSessionDataLocked() {
- if (mSession == null) {
- return;
- }
- if (mHaveAssistData) {
- AssistDataForActivity assistData;
- if (mAssistData.isEmpty()) {
- // We're not actually going to get any data, deliver some nothing
- try {
- mSession.handleAssist(null, null, null, 0, 0);
- } catch (RemoteException e) {
- }
- } else {
- while (!mAssistData.isEmpty()) {
- if (mPendingAssistDataCount <= 0) {
- Slog.e(TAG, "mPendingAssistDataCount is " + mPendingAssistDataCount);
- }
- mPendingAssistDataCount--;
- assistData = mAssistData.remove(0);
- if (assistData.data == null) {
- try {
- mSession.handleAssist(null, null, null, assistData.activityIndex,
- assistData.activityCount);
- } catch (RemoteException e) {
- }
- } else {
- deliverSessionDataLocked(assistData);
- }
- }
- }
- if (mPendingAssistDataCount <= 0) {
- mHaveAssistData = false;
- } // else, more to come
- }
- if (mHaveScreenshot) {
- try {
- mSession.handleScreenshot(mScreenshot);
- } catch (RemoteException e) {
- }
- mScreenshot = null;
- mHaveScreenshot = false;
- }
- }
-
- private void deliverSessionDataLocked(AssistDataForActivity assistDataForActivity) {
- Bundle assistData = assistDataForActivity.data.getBundle(
- VoiceInteractionSession.KEY_DATA);
- AssistStructure structure = assistDataForActivity.data.getParcelable(
- VoiceInteractionSession.KEY_STRUCTURE);
- AssistContent content = assistDataForActivity.data.getParcelable(
- VoiceInteractionSession.KEY_CONTENT);
- int uid = assistDataForActivity.data.getInt(Intent.EXTRA_ASSIST_UID, -1);
- if (uid >= 0 && content != null) {
- Intent intent = content.getIntent();
- if (intent != null) {
- ClipData data = intent.getClipData();
- if (data != null && Intent.isAccessUriMode(intent.getFlags())) {
- grantClipDataPermissions(data, intent.getFlags(), uid,
- mCallingUid, mSessionComponentName.getPackageName());
- }
- }
- ClipData data = content.getClipData();
- if (data != null) {
- grantClipDataPermissions(data,
- Intent.FLAG_GRANT_READ_URI_PERMISSION,
- uid, mCallingUid, mSessionComponentName.getPackageName());
- }
- }
- try {
- mSession.handleAssist(assistData, structure, content,
- assistDataForActivity.activityIndex, assistDataForActivity.activityCount);
- } catch (RemoteException e) {
- }
- }
-
public boolean hideLocked() {
if (mBound) {
if (mShown) {
mShown = false;
mShowArgs = null;
mShowFlags = 0;
- mHaveAssistData = false;
- mAssistData.clear();
+ mAssistDataRequester.cancel();
mPendingShowCallbacks.clear();
if (mSession != null) {
try {
@@ -462,8 +338,7 @@
}
try {
mAm.revokeUriPermissionFromOwner(mPermissionOwner, null,
- Intent.FLAG_GRANT_READ_URI_PERMISSION
- | Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
+ FLAG_GRANT_READ_URI_PERMISSION | FLAG_GRANT_WRITE_URI_PERMISSION,
mUser);
} catch (RemoteException e) {
}
@@ -529,7 +404,7 @@
mShowFlags = 0;
} catch (RemoteException e) {
}
- deliverSessionDataLocked();
+ mAssistDataRequester.processPendingAssistData();
}
return true;
}
@@ -587,10 +462,7 @@
pw.print(prefix); pw.print("mSession="); pw.println(mSession);
pw.print(prefix); pw.print("mInteractor="); pw.println(mInteractor);
}
- pw.print(prefix); pw.print("mHaveAssistData="); pw.println(mHaveAssistData);
- if (mHaveAssistData) {
- pw.print(prefix); pw.print("mAssistData="); pw.println(mAssistData);
- }
+ mAssistDataRequester.dump(prefix, pw);
}
private Runnable mShowAssistDisclosureRunnable = new Runnable() {