Fix onHandleAssist behaviour when showSession is called with flags = 0

The code to handle voice interaction sessions had two problems:

1. it was not handling correctly the case where
VoiceInteractionSessionConnection.showLocked was called before a session
started, without requiring any assist data (flags = 0).
Now, when the session is set in VoiceInteractionSessionConnection,
the pending `onHandleAssist` are called.

2. AssistState delivered with `onHandleAssist` had the wrong IBinder
inside ActivityId. it contained ActivityRecord.appToken, but to work
properly ActivityRecord.assistToken was required. Tests have been updated
to catch this (atest CtsVoiceInteractionTestCases).

Bug: 178020517
Test: atest CtsAssistTestCases
Test: atest CtsVoiceInteractionTestCases
Change-Id: Id10937f0655d6837ccb85250c369bdeccd261ed8
This commit is contained in:
Nicolo' Mazzucato
2021-03-19 23:39:02 +00:00
parent 9f15b8d17a
commit d4e6ec0ea8
7 changed files with 95 additions and 26 deletions

View File

@@ -0,0 +1,52 @@
/*
* Copyright (C) 2021 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.wm;
import android.os.IBinder;
/**
* Class needed to expose some {@link ActivityRecord} fields in order to provide
* {@link android.service.voice.VoiceInteractionSession#onHandleAssist(AssistState)}
*
* @hide
*/
public class ActivityAssistInfo {
private final IBinder mActivityToken;
private final IBinder mAssistToken;
private final int mTaskId;
public ActivityAssistInfo(ActivityRecord activityRecord) {
this.mActivityToken = activityRecord.appToken;
this.mAssistToken = activityRecord.assistToken;
this.mTaskId = activityRecord.getTask().mTaskId;
}
/** @hide */
public IBinder getActivityToken() {
return mActivityToken;
}
/** @hide */
public IBinder getAssistToken() {
return mAssistToken;
}
/** @hide */
public int getTaskId() {
return mTaskId;
}
}

View File

@@ -32,7 +32,6 @@ import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.service.voice.IVoiceInteractionSession;
import android.util.Pair;
import android.util.proto.ProtoOutputStream;
import android.window.TaskSnapshot;
@@ -166,7 +165,7 @@ public abstract class ActivityTaskManagerInternal {
* Returns the top activity from each of the currently visible root tasks, and the related task
* id. The first entry will be the focused activity.
*/
public abstract List<Pair<IBinder, Integer>> getTopVisibleActivities();
public abstract List<ActivityAssistInfo> getTopVisibleActivities();
/**
* Returns whether {@code uid} has any resumed activity.

View File

@@ -215,7 +215,6 @@ import android.text.format.TimeMigrationUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
@@ -5075,7 +5074,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
@Override
public List<Pair<IBinder, Integer>> getTopVisibleActivities() {
public List<ActivityAssistInfo> getTopVisibleActivities() {
synchronized (mGlobalLock) {
return mRootWindowContainer.getTopVisibleActivities();
}

View File

@@ -1817,8 +1817,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
* @return a list of pairs, containing activities and their task id which are the top ones in
* each visible root task. The first entry will be the focused activity.
*/
List<Pair<IBinder, Integer>> getTopVisibleActivities() {
final ArrayList<Pair<IBinder, Integer>> topVisibleActivities = new ArrayList<>();
List<ActivityAssistInfo> getTopVisibleActivities() {
final ArrayList<ActivityAssistInfo> topVisibleActivities = new ArrayList<>();
final Task topFocusedRootTask = getTopDisplayFocusedRootTask();
// Traverse all displays.
forAllRootTasks(rootTask -> {
@@ -1826,8 +1826,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
if (rootTask.shouldBeVisible(null /* starting */)) {
final ActivityRecord top = rootTask.getTopNonFinishingActivity();
if (top != null) {
Pair<IBinder, Integer> visibleActivity = new Pair<>(top.appToken,
top.getTask().mTaskId);
ActivityAssistInfo visibleActivity = new ActivityAssistInfo(top);
if (rootTask == topFocusedRootTask) {
topVisibleActivities.add(0, visibleActivity);
} else {

View File

@@ -7,6 +7,14 @@
"exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
},
{
"name": "CtsAssistTestCases",
"options": [
{
"exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
}
]
}

View File

@@ -50,7 +50,6 @@ import android.service.voice.IVoiceInteractionSession;
import android.service.voice.VoiceInteractionService;
import android.service.voice.VoiceInteractionServiceInfo;
import android.system.OsConstants;
import android.util.Pair;
import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.view.IWindowManager;
@@ -60,6 +59,7 @@ import com.android.internal.app.IVoiceActionCheckCallback;
import com.android.internal.app.IVoiceInteractionSessionShowCallback;
import com.android.internal.app.IVoiceInteractor;
import com.android.server.LocalServices;
import com.android.server.wm.ActivityAssistInfo;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal.ActivityTokens;
@@ -187,24 +187,23 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne
mSessionComponentName, mUser, mContext, this,
mInfo.getServiceInfo().applicationInfo.uid, mHandler);
}
List<Pair<IBinder, Integer>> allVisibleActivities =
List<ActivityAssistInfo> allVisibleActivities =
LocalServices.getService(ActivityTaskManagerInternal.class)
.getTopVisibleActivities();
List<Pair<IBinder, Integer>> visibleActivities = null;
List<ActivityAssistInfo> visibleActivities = null;
if (activityToken != null) {
visibleActivities = new ArrayList();
int activitiesCount = allVisibleActivities.size();
for (int i = 0; i < activitiesCount; i++) {
if (allVisibleActivities.get(i).first == activityToken) {
visibleActivities.add(
new Pair<>(activityToken, allVisibleActivities.get(i).second));
ActivityAssistInfo info = allVisibleActivities.get(i);
if (info.getActivityToken() == activityToken) {
visibleActivities.add(info);
break;
}
}
} else {
visibleActivities = LocalServices.getService(ActivityTaskManagerInternal.class)
.getTopVisibleActivities();
visibleActivities = allVisibleActivities;
}
return mActiveSession.showLocked(args, flags, mDisabledShowContext, showCallback,
visibleActivities);

View File

@@ -56,7 +56,6 @@ import android.service.voice.IVoiceInteractionSession;
import android.service.voice.IVoiceInteractionSessionService;
import android.service.voice.VoiceInteractionService;
import android.service.voice.VoiceInteractionSession;
import android.util.Pair;
import android.util.Slog;
import android.view.IWindowManager;
@@ -68,6 +67,7 @@ import com.android.server.am.AssistDataRequester;
import com.android.server.am.AssistDataRequester.AssistDataRequesterCallbacks;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.uri.UriGrantsManagerInternal;
import com.android.server.wm.ActivityAssistInfo;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -102,6 +102,7 @@ final class VoiceInteractionSessionConnection implements ServiceConnection,
IVoiceInteractionSession mSession;
IVoiceInteractor mInteractor;
ArrayList<IVoiceInteractionSessionShowCallback> mPendingShowCallbacks = new ArrayList<>();
private List<ActivityAssistInfo> mPendingHandleAssistWithoutData = new ArrayList<>();
AssistDataRequester mAssistDataRequester;
IVoiceInteractionSessionShowCallback mShowCallback =
@@ -192,7 +193,7 @@ final class VoiceInteractionSessionConnection implements ServiceConnection,
public boolean showLocked(Bundle args, int flags, int disabledContext,
IVoiceInteractionSessionShowCallback showCallback,
List<Pair<IBinder, Integer>> topActivities) {
List<ActivityAssistInfo> topActivities) {
if (mBound) {
if (!mFullyBound) {
mFullyBound = mContext.bindServiceAsUser(mBindIntent, mFullConnection,
@@ -216,7 +217,7 @@ final class VoiceInteractionSessionConnection implements ServiceConnection,
int topActivitiesCount = topActivities.size();
final ArrayList<IBinder> topActivitiesToken = new ArrayList<>(topActivitiesCount);
for (int i = 0; i < topActivitiesCount; i++) {
topActivitiesToken.add(topActivities.get(i).first);
topActivitiesToken.add(topActivities.get(i).getActivityToken());
}
mAssistDataRequester.requestAssistData(topActivitiesToken,
fetchData,
@@ -243,8 +244,16 @@ final class VoiceInteractionSessionConnection implements ServiceConnection,
} else {
doHandleAssistWithoutData(topActivities);
}
} else if (showCallback != null) {
mPendingShowCallbacks.add(showCallback);
} else {
if (showCallback != null) {
mPendingShowCallbacks.add(showCallback);
}
if (!assistDataRequestNeeded) {
// If no data are required we are not passing trough mAssistDataRequester. As
// a consequence, when a new session is delivered it is needed to process those
// requests manually.
mPendingHandleAssistWithoutData = topActivities;
}
}
mCallback.onSessionShown(this);
return true;
@@ -258,17 +267,17 @@ final class VoiceInteractionSessionConnection implements ServiceConnection,
return false;
}
private void doHandleAssistWithoutData(List<Pair<IBinder, Integer>> topActivities) {
private void doHandleAssistWithoutData(List<ActivityAssistInfo> topActivities) {
final int activityCount = topActivities.size();
for (int i = 0; i < activityCount; i++) {
final Pair<IBinder, Integer> topActivity = topActivities.get(i);
final IBinder activityId = topActivity.first;
final int taskId = topActivity.second;
final ActivityAssistInfo topActivity = topActivities.get(i);
final IBinder assistToken = topActivity.getAssistToken();
final int taskId = topActivity.getTaskId();
final int activityIndex = i;
try {
mSession.handleAssist(
taskId,
activityId,
assistToken,
/* assistData = */ null,
/* assistStructure = */ null,
/* assistContent = */ null,
@@ -468,6 +477,10 @@ final class VoiceInteractionSessionConnection implements ServiceConnection,
} catch (RemoteException e) {
}
mAssistDataRequester.processPendingAssistData();
if (!mPendingHandleAssistWithoutData.isEmpty()) {
doHandleAssistWithoutData(mPendingHandleAssistWithoutData);
mPendingHandleAssistWithoutData.clear();
}
}
return true;
}