diff options
| -rw-r--r-- | Android.mk | 2 | ||||
| -rw-r--r-- | core/java/android/app/IActivityManager.aidl | 5 | ||||
| -rw-r--r-- | core/java/android/view/IWindowManager.aidl | 4 | ||||
| -rw-r--r-- | core/java/com/android/internal/app/IAssistDataReceiver.aidl (renamed from core/java/com/android/internal/app/IAssistScreenshotReceiver.aidl) | 8 | ||||
| -rw-r--r-- | services/autofill/java/com/android/server/autofill/Session.java | 12 | ||||
| -rw-r--r-- | services/core/java/com/android/server/am/ActivityManagerService.java | 41 | ||||
| -rw-r--r-- | services/core/java/com/android/server/am/AssistDataRequester.java | 306 | ||||
| -rw-r--r-- | services/core/java/com/android/server/wm/WindowManagerService.java | 6 | ||||
| -rw-r--r-- | services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java | 316 | ||||
| -rw-r--r-- | services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java | 8 | ||||
| -rw-r--r-- | services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java | 294 |
11 files changed, 748 insertions, 254 deletions
diff --git a/Android.mk b/Android.mk index 9890bb401404..62f750c41a8b 100644 --- a/Android.mk +++ b/Android.mk @@ -380,7 +380,7 @@ LOCAL_SRC_FILES += \ 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 49513d1825de..322450e5a18d 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -63,6 +63,7 @@ import android.os.ParcelFileDescriptor; 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 @@ interface IActivityManager { 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 @@ interface IActivityManager { 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 6e49bac38116..3d38dc41aa9a 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 @@ interface IWindowManager /** * 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 index a987a166b496..9c9ffef0066f 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 010995f2c33b..eaf605f1f6eb 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.ComponentName; 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.IAutoFillManagerClient; 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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState /** * 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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState 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 f2e049325497..cf0915832827 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.AssistUtils; 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 class ActivityManagerService extends IActivityManager.Stub 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 class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub @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 @@ public class ActivityManagerService extends IActivityManager.Stub } @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 @@ public class ActivityManagerService extends IActivityManager.Stub } @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 @@ public class ActivityManagerService extends IActivityManager.Stub } 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 @@ public class ActivityManagerService extends IActivityManager.Stub } void pendingAssistExtrasTimedOut(PendingAssistExtras pae) { - IResultReceiver receiver; + IAssistDataReceiver receiver; synchronized (this) { mPendingAssistExtras.remove(pae); receiver = pae.receiver; @@ -13034,7 +13021,7 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub } } // 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 @@ public class ActivityManagerService extends IActivityManager.Stub } 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 000000000000..60f3d221cd19 --- /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 f31ea67c8edf..70035c355768 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.animation.Animation; 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 @@ public class WindowManagerService extends IWindowManager.Stub * 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 @@ public class WindowManagerService extends IWindowManager.Stub 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 000000000000..bec46db2b7ab --- /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 b040a6324dbb..7541b92dbe2c 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 com.android.server.LocalServices; 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 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne 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 d394d6311ba5..c9b0eacca587 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.VoiceInteractionService; 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 @@ final class VoiceInteractionSessionConnection implements ServiceConnection { 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 class VoiceInteractionSessionConnection implements ServiceConnection { } }; - 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 @@ final class VoiceInteractionSessionConnection implements ServiceConnection { 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 @@ final class VoiceInteractionSessionConnection implements ServiceConnection { } 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 @@ final class VoiceInteractionSessionConnection implements ServiceConnection { | 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 @@ final class VoiceInteractionSessionConnection implements ServiceConnection { mShowFlags = 0; } catch (RemoteException e) { } - deliverSessionDataLocked(); } else if (showCallback != null) { mPendingShowCallbacks.add(showCallback); } @@ -328,6 +227,59 @@ final class VoiceInteractionSessionConnection implements ServiceConnection { 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 @@ final class VoiceInteractionSessionConnection implements ServiceConnection { 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 @@ final class VoiceInteractionSessionConnection implements ServiceConnection { } } - 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 @@ final class VoiceInteractionSessionConnection implements ServiceConnection { } 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 @@ final class VoiceInteractionSessionConnection implements ServiceConnection { mShowFlags = 0; } catch (RemoteException e) { } - deliverSessionDataLocked(); + mAssistDataRequester.processPendingAssistData(); } return true; } @@ -587,10 +462,7 @@ final class VoiceInteractionSessionConnection implements ServiceConnection { 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() { |