1/ Refactor out logic to fetch assist data asynchronously

- This logic will also be used when the recents component is started to
  fetch assist data associated with the current activity.

Bug: 67864419
Test: com.android.server.am.AssistDataRequesterTest
Test: CtsVoiceInteractionTestCases

Change-Id: I4cb3c2536a104d7071bc0a1fd6e31dfc2c0ce8d0
This commit is contained in:
Winson Chung
2017-10-23 16:25:49 -07:00
parent 35c1a5d51b
commit da2818fe89
11 changed files with 748 additions and 254 deletions

View File

@@ -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 \

View File

@@ -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);

View File

@@ -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.

View File

@@ -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);
}

View File

@@ -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;
@@ -76,11 +77,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;
@@ -206,9 +207,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");
@@ -264,6 +265,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mRemoteFillService.onFillRequest(request);
}
@Override
public void onHandleAssistScreenshot(Bitmap screenshot) {
// Do nothing
}
};
/**

View File

@@ -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;
@@ -365,6 +366,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;
@@ -775,7 +777,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;
@@ -784,7 +786,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;
@@ -4667,15 +4670,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");
}
@@ -4690,15 +4685,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,
@@ -12913,7 +12900,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,
@@ -12921,7 +12908,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(),
@@ -12929,7 +12916,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,
@@ -12997,7 +12984,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
void pendingAssistExtrasTimedOut(PendingAssistExtras pae) {
IResultReceiver receiver;
IAssistDataReceiver receiver;
synchronized (this) {
mPendingAssistExtras.remove(pae);
receiver = pae.receiver;
@@ -13009,7 +12996,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) {
}
}
@@ -13047,7 +13034,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);
@@ -13069,7 +13056,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
if (sendReceiver != null) {
try {
sendReceiver.send(0, sendBundle);
sendReceiver.onHandleAssistData(sendBundle);
} catch (RemoteException e) {
}
return;

View File

@@ -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);
}
}

View File

@@ -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) {
}
});

View File

@@ -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);
}
}
}

View File

@@ -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() {

View File

@@ -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() {