Merge "[NGA v2] Reliable Visible Activity Lookup" into sc-v2-dev am: 80fcbc8fe0

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15810445

Change-Id: Ibfce3c573e7cc136226599500e438527a1565075
This commit is contained in:
TreeHugger Robot
2021-09-28 18:02:38 +00:00
committed by Automerger Merge Worker
13 changed files with 717 additions and 1 deletions

View File

@@ -39017,6 +39017,13 @@ package android.service.textservice {
package android.service.voice {
public final class VisibleActivityInfo implements android.os.Parcelable {
method public int describeContents();
method @NonNull public android.service.voice.VoiceInteractionSession.ActivityId getActivityId();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.service.voice.VisibleActivityInfo> CREATOR;
}
public class VoiceInteractionService extends android.app.Service {
ctor public VoiceInteractionService();
method public int getDisabledShowContext();
@@ -39078,6 +39085,7 @@ package android.service.voice {
method public void onTaskStarted(android.content.Intent, int);
method public void onTrimMemory(int);
method public final void performDirectAction(@NonNull android.app.DirectAction, @Nullable android.os.Bundle, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.os.Bundle>);
method public final void registerVisibleActivityCallback(@NonNull java.util.concurrent.Executor, @NonNull android.service.voice.VoiceInteractionSession.VisibleActivityCallback);
method public final void requestDirectActions(@NonNull android.service.voice.VoiceInteractionSession.ActivityId, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.util.List<android.app.DirectAction>>);
method public void setContentView(android.view.View);
method public void setDisabledShowContext(int);
@@ -39087,6 +39095,7 @@ package android.service.voice {
method public void show(android.os.Bundle, int);
method public void startAssistantActivity(android.content.Intent);
method public void startVoiceActivity(android.content.Intent);
method public final void unregisterVisibleActivityCallback(@NonNull android.service.voice.VoiceInteractionSession.VisibleActivityCallback);
field public static final int SHOW_SOURCE_ACTIVITY = 16; // 0x10
field public static final int SHOW_SOURCE_APPLICATION = 8; // 0x8
field public static final int SHOW_SOURCE_ASSIST_GESTURE = 4; // 0x4
@@ -39160,6 +39169,11 @@ package android.service.voice {
method public boolean isActive();
}
public static interface VoiceInteractionSession.VisibleActivityCallback {
method public default void onInvisible(@NonNull android.service.voice.VoiceInteractionSession.ActivityId);
method public default void onVisible(@NonNull android.service.voice.VisibleActivityInfo);
}
public abstract class VoiceInteractionSessionService extends android.app.Service {
ctor public VoiceInteractionSessionService();
method public android.os.IBinder onBind(android.content.Intent);

View File

@@ -2397,6 +2397,10 @@ package android.service.voice {
method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public void triggerHardwareRecognitionEventForTest(int, int, boolean, int, int, int, boolean, @NonNull android.media.AudioFormat, @Nullable byte[]);
}
public final class VisibleActivityInfo implements android.os.Parcelable {
ctor public VisibleActivityInfo(int, @NonNull android.os.IBinder);
}
}
package android.service.watchdog {

View File

@@ -652,4 +652,23 @@ public abstract class ActivityManagerInternal {
public abstract int sendIntentSender(IIntentSender target, IBinder allowlistToken, int code,
Intent intent, String resolvedType,
IIntentReceiver finishedReceiver, String requiredPermission, Bundle options);
/**
* Sets the provider to communicate between voice interaction manager service and
* ActivityManagerService.
*/
public abstract void setVoiceInteractionManagerProvider(
@Nullable VoiceInteractionManagerProvider provider);
/**
* Provides the interface to communicate between voice interaction manager service and
* ActivityManagerService.
*/
public interface VoiceInteractionManagerProvider {
/**
* Notifies the service when a high-level activity event has been changed, for example,
* an activity was resumed or stopped.
*/
void notifyActivityEventChanged();
}
}

View File

@@ -22,6 +22,7 @@ import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.os.IBinder;
import android.service.voice.VisibleActivityInfo;
import com.android.internal.app.IVoiceInteractionSessionShowCallback;
@@ -39,4 +40,5 @@ oneway interface IVoiceInteractionSession {
void closeSystemDialogs();
void onLockscreenShown();
void destroy();
void updateVisibleActivityInfo(in VisibleActivityInfo visibleActivityInfo, int type);
}

View File

@@ -0,0 +1,19 @@
/*
* 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 android.service.voice;
parcelable VisibleActivityInfo;

View File

@@ -0,0 +1,205 @@
/*
* 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 android.service.voice;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.os.CancellationSignal;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import com.android.internal.util.DataClass;
import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
/**
* The class is used to represent a visible activity information. The system provides this to
* services that need to know {@link android.service.voice.VoiceInteractionSession.ActivityId}.
*/
@DataClass(
genConstructor = false,
genEqualsHashCode = true,
genHiddenConstDefs = false,
genGetters = false,
genToString = true
)
public final class VisibleActivityInfo implements Parcelable {
/**
* Indicates that it is a new visible activity.
*
* @hide
*/
public static final int TYPE_ACTIVITY_ADDED = 1;
/**
* Indicates that it has become a invisible activity.
*
* @hide
*/
public static final int TYPE_ACTIVITY_REMOVED = 2;
/**
* The identifier of the task this activity is in.
*/
private final int mTaskId;
/**
* Token for targeting this activity for assist purposes.
*/
@NonNull
private final IBinder mAssistToken;
/** @hide */
@TestApi
public VisibleActivityInfo(
int taskId,
@NonNull IBinder assistToken) {
Objects.requireNonNull(assistToken);
mTaskId = taskId;
mAssistToken = assistToken;
}
/**
* Returns the {@link android.service.voice.VoiceInteractionSession.ActivityId} of this
* visible activity which can be used to interact with an activity, for example through
* {@link VoiceInteractionSession#requestDirectActions(VoiceInteractionSession.ActivityId,
* CancellationSignal, Executor, Consumer)}.
*/
public @NonNull VoiceInteractionSession.ActivityId getActivityId() {
return new VoiceInteractionSession.ActivityId(mTaskId, mAssistToken);
}
// Code below generated by codegen v1.0.23.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
//
// To regenerate run:
// $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/voice/VisibleActivityInfo.java
//
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
// Settings > Editor > Code Style > Formatter Control
//@formatter:off
@Override
@DataClass.Generated.Member
public String toString() {
// You can override field toString logic by defining methods like:
// String fieldNameToString() { ... }
return "VisibleActivityInfo { " +
"taskId = " + mTaskId + ", " +
"assistToken = " + mAssistToken +
" }";
}
@Override
@DataClass.Generated.Member
public boolean equals(@Nullable Object o) {
// You can override field equality logic by defining either of the methods like:
// boolean fieldNameEquals(VisibleActivityInfo other) { ... }
// boolean fieldNameEquals(FieldType otherValue) { ... }
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
@SuppressWarnings("unchecked")
VisibleActivityInfo that = (VisibleActivityInfo) o;
//noinspection PointlessBooleanExpression
return true
&& mTaskId == that.mTaskId
&& Objects.equals(mAssistToken, that.mAssistToken);
}
@Override
@DataClass.Generated.Member
public int hashCode() {
// You can override field hashCode logic by defining methods like:
// int fieldNameHashCode() { ... }
int _hash = 1;
_hash = 31 * _hash + mTaskId;
_hash = 31 * _hash + Objects.hashCode(mAssistToken);
return _hash;
}
@Override
@DataClass.Generated.Member
public void writeToParcel(@NonNull Parcel dest, int flags) {
// You can override field parcelling by defining methods like:
// void parcelFieldName(Parcel dest, int flags) { ... }
dest.writeInt(mTaskId);
dest.writeStrongBinder(mAssistToken);
}
@Override
@DataClass.Generated.Member
public int describeContents() { return 0; }
/** @hide */
@SuppressWarnings({"unchecked", "RedundantCast"})
@DataClass.Generated.Member
/* package-private */ VisibleActivityInfo(@NonNull Parcel in) {
// You can override field unparcelling by defining methods like:
// static FieldType unparcelFieldName(Parcel in) { ... }
int taskId = in.readInt();
IBinder assistToken = (IBinder) in.readStrongBinder();
this.mTaskId = taskId;
this.mAssistToken = assistToken;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mAssistToken);
// onConstructed(); // You can define this method to get a callback
}
@DataClass.Generated.Member
public static final @NonNull Parcelable.Creator<VisibleActivityInfo> CREATOR
= new Parcelable.Creator<VisibleActivityInfo>() {
@Override
public VisibleActivityInfo[] newArray(int size) {
return new VisibleActivityInfo[size];
}
@Override
public VisibleActivityInfo createFromParcel(@NonNull Parcel in) {
return new VisibleActivityInfo(in);
}
};
@DataClass.Generated(
time = 1632383555284L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/service/voice/VisibleActivityInfo.java",
inputSignatures = "public static final int TYPE_ACTIVITY_ADDED\npublic static final int TYPE_ACTIVITY_REMOVED\nprivate final int mTaskId\nprivate final @android.annotation.NonNull android.os.IBinder mAssistToken\npublic @android.annotation.NonNull android.service.voice.VoiceInteractionSession.ActivityId getActivityId()\nclass VisibleActivityInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genEqualsHashCode=true, genHiddenConstDefs=false, genGetters=false, genToString=true)")
@Deprecated
private void __metadata() {}
//@formatter:on
// End of generated code
}

View File

@@ -22,7 +22,6 @@ import android.os.IBinder;
import com.android.internal.annotations.Immutable;
/**
* @hide
* Private interface to the VoiceInteractionManagerService for use by ActivityManagerService.

View File

@@ -74,9 +74,11 @@ import com.android.internal.util.function.pooled.PooledLambda;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -177,6 +179,10 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall
ICancellationSignal mKillCallback;
private final Map<VisibleActivityCallback, Executor> mVisibleActivityCallbacks =
new ArrayMap<>();
private final List<VisibleActivityInfo> mVisibleActivityInfos = new ArrayList<>();
final IVoiceInteractor mInteractor = new IVoiceInteractor.Stub() {
@Override
public IVoiceInteractorRequest startConfirmation(String callingPackage,
@@ -352,6 +358,13 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall
public void destroy() {
mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_DESTROY));
}
@Override
public void updateVisibleActivityInfo(VisibleActivityInfo visibleActivityInfo, int type) {
mHandlerCaller.sendMessage(
mHandlerCaller.obtainMessageIO(MSG_UPDATE_VISIBLE_ACTIVITY_INFO, type,
visibleActivityInfo));
}
};
/**
@@ -843,6 +856,9 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall
static final int MSG_SHOW = 106;
static final int MSG_HIDE = 107;
static final int MSG_ON_LOCKSCREEN_SHOWN = 108;
static final int MSG_UPDATE_VISIBLE_ACTIVITY_INFO = 109;
static final int MSG_REGISTER_VISIBLE_ACTIVITY_CALLBACK = 110;
static final int MSG_UNREGISTER_VISIBLE_ACTIVITY_CALLBACK = 111;
class MyCallbacks implements HandlerCaller.Callback, SoftInputWindow.Callback {
@Override
@@ -928,6 +944,27 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall
if (DEBUG) Log.d(TAG, "onLockscreenShown");
onLockscreenShown();
break;
case MSG_UPDATE_VISIBLE_ACTIVITY_INFO:
if (DEBUG) {
Log.d(TAG, "doUpdateVisibleActivityInfo: visibleActivityInfo=" + msg.obj
+ " type=" + msg.arg1);
}
doUpdateVisibleActivityInfo((VisibleActivityInfo) msg.obj, msg.arg1);
break;
case MSG_REGISTER_VISIBLE_ACTIVITY_CALLBACK:
if (DEBUG) {
Log.d(TAG, "doRegisterVisibleActivityCallback");
}
args = (SomeArgs) msg.obj;
doRegisterVisibleActivityCallback((Executor) args.arg1,
(VisibleActivityCallback) args.arg2);
break;
case MSG_UNREGISTER_VISIBLE_ACTIVITY_CALLBACK:
if (DEBUG) {
Log.d(TAG, "doUnregisterVisibleActivityCallback");
}
doUnregisterVisibleActivityCallback((VisibleActivityCallback) msg.obj);
break;
}
if (args != null) {
args.recycle();
@@ -1122,6 +1159,86 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall
}
}
private void doUpdateVisibleActivityInfo(VisibleActivityInfo visibleActivityInfo, int type) {
if (mVisibleActivityCallbacks.isEmpty()) {
return;
}
switch (type) {
case VisibleActivityInfo.TYPE_ACTIVITY_ADDED:
informVisibleActivityChanged(visibleActivityInfo, type);
mVisibleActivityInfos.add(visibleActivityInfo);
break;
case VisibleActivityInfo.TYPE_ACTIVITY_REMOVED:
informVisibleActivityChanged(visibleActivityInfo, type);
mVisibleActivityInfos.remove(visibleActivityInfo);
break;
}
}
private void doRegisterVisibleActivityCallback(@NonNull @CallbackExecutor Executor executor,
@NonNull VisibleActivityCallback callback) {
if (mVisibleActivityCallbacks.containsKey(callback)) {
if (DEBUG) {
Log.d(TAG, "doRegisterVisibleActivityCallback: callback has registered");
}
return;
}
int preCallbackCount = mVisibleActivityCallbacks.size();
mVisibleActivityCallbacks.put(callback, executor);
if (preCallbackCount == 0) {
try {
mSystemService.startListeningVisibleActivityChanged(mToken);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
} else {
for (int i = 0; i < mVisibleActivityInfos.size(); i++) {
final VisibleActivityInfo visibleActivityInfo = mVisibleActivityInfos.get(i);
executor.execute(() -> callback.onVisible(visibleActivityInfo));
}
}
}
private void doUnregisterVisibleActivityCallback(@NonNull VisibleActivityCallback callback) {
mVisibleActivityCallbacks.remove(callback);
if (mVisibleActivityCallbacks.size() == 0) {
mVisibleActivityInfos.clear();
try {
mSystemService.stopListeningVisibleActivityChanged(mToken);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
}
}
private void informVisibleActivityChanged(VisibleActivityInfo visibleActivityInfo, int type) {
for (Map.Entry<VisibleActivityCallback, Executor> e :
mVisibleActivityCallbacks.entrySet()) {
final Executor executor = e.getValue();
final VisibleActivityCallback visibleActivityCallback = e.getKey();
switch (type) {
case VisibleActivityInfo.TYPE_ACTIVITY_ADDED:
Binder.withCleanCallingIdentity(() -> {
executor.execute(
() -> visibleActivityCallback.onVisible(visibleActivityInfo));
});
break;
case VisibleActivityInfo.TYPE_ACTIVITY_REMOVED:
Binder.withCleanCallingIdentity(() -> {
executor.execute(() -> visibleActivityCallback.onInvisible(
visibleActivityInfo.getActivityId()));
});
break;
}
}
}
void ensureWindowCreated() {
if (mInitialized) {
return;
@@ -1925,6 +2042,45 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall
public void onCancelRequest(Request request) {
}
/**
* Registers a callback that will be notified when visible activities have been changed.
*
* @param executor The handler to receive the callback.
* @param callback The callback to receive the response.
*
* @throws IllegalStateException if calling this method before onCreate().
*/
public final void registerVisibleActivityCallback(@NonNull @CallbackExecutor Executor executor,
@NonNull VisibleActivityCallback callback) {
if (DEBUG) {
Log.d(TAG, "registerVisibleActivityCallback");
}
if (mToken == null) {
throw new IllegalStateException("Can't call before onCreate()");
}
Objects.requireNonNull(executor);
Objects.requireNonNull(callback);
mHandlerCaller.sendMessage(
mHandlerCaller.obtainMessageOO(MSG_REGISTER_VISIBLE_ACTIVITY_CALLBACK, executor,
callback));
}
/**
* Unregisters the callback.
*
* @param callback The callback to receive the response.
*/
public final void unregisterVisibleActivityCallback(@NonNull VisibleActivityCallback callback) {
if (DEBUG) {
Log.d(TAG, "unregisterVisibleActivityCallback");
}
Objects.requireNonNull(callback);
mHandlerCaller.sendMessage(
mHandlerCaller.obtainMessageO(MSG_UNREGISTER_VISIBLE_ACTIVITY_CALLBACK, callback));
}
/**
* Print the Service's state into the given stream. This gets invoked by
* {@link VoiceInteractionSessionService} when its Service
@@ -1974,6 +2130,17 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall
}
}
/**
* Callback interface for receiving visible activity changes used for assistant usage.
*/
public interface VisibleActivityCallback {
/** Callback to inform that an activity has become visible. */
default void onVisible(@NonNull VisibleActivityInfo activityInfo) {}
/** Callback to inform that a visible activity has gone. */
default void onInvisible(@NonNull ActivityId activityId) {}
}
/**
* Represents assist state captured when this session was started.
* It contains the various assist data objects and a reference to

View File

@@ -272,4 +272,14 @@ interface IVoiceInteractionManagerService {
void triggerHardwareRecognitionEventForTest(
in SoundTrigger.KeyphraseRecognitionEvent event,
in IHotwordRecognitionStatusCallback callback);
/**
* Starts to listen the status of visible activity.
*/
void startListeningVisibleActivityChanged(in IBinder token);
/**
* Stops to listen the status of visible activity.
*/
void stopListeningVisibleActivityChanged(in IBinder token);
}

View File

@@ -1550,6 +1550,13 @@ public class ActivityManagerService extends IActivityManager.Stub
private static final int INDEX_TOTAL_MEMTRACK_GL = 14;
private static final int INDEX_LAST = 15;
/**
* Used to notify activity lifecycle events.
*/
@Nullable
volatile ActivityManagerInternal.VoiceInteractionManagerProvider
mVoiceInteractionManagerProvider;
final class UiHandler extends Handler {
public UiHandler() {
super(com.android.server.UiThread.get().getLooper(), null, true);
@@ -1886,6 +1893,14 @@ public class ActivityManagerService extends IActivityManager.Stub
return mAppOpsService;
}
/**
* Sets the internal voice interaction manager service.
*/
private void setVoiceInteractionManagerProvider(
@Nullable ActivityManagerInternal.VoiceInteractionManagerProvider provider) {
mVoiceInteractionManagerProvider = provider;
}
static class MemBinder extends Binder {
ActivityManagerService mActivityManagerService;
private final PriorityDump.PriorityDumper mPriorityDumper =
@@ -2737,6 +2752,11 @@ public class ActivityManagerService extends IActivityManager.Stub
|| event == Event.ACTIVITY_DESTROYED)) {
contentCaptureService.notifyActivityEvent(userId, activity, event);
}
// TODO(b/201234353): Move the logic to client side.
if (mVoiceInteractionManagerProvider != null && (event == Event.ACTIVITY_PAUSED
|| event == Event.ACTIVITY_RESUMED || event == Event.ACTIVITY_STOPPED)) {
mVoiceInteractionManagerProvider.notifyActivityEventChanged();
}
}
/**
@@ -16375,6 +16395,12 @@ public class ActivityManagerService extends IActivityManager.Stub
return ActivityManagerService.this.sendIntentSender(target, allowlistToken, code,
intent, resolvedType, finishedReceiver, requiredPermission, options);
}
@Override
public void setVoiceInteractionManagerProvider(
@Nullable ActivityManagerInternal.VoiceInteractionManagerProvider provider) {
ActivityManagerService.this.setVoiceInteractionManagerProvider(provider);
}
}
long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {

View File

@@ -167,6 +167,16 @@ public class VoiceInteractionManagerService extends SystemService {
public void onStart() {
publishBinderService(Context.VOICE_INTERACTION_MANAGER_SERVICE, mServiceStub);
publishLocalService(VoiceInteractionManagerInternal.class, new LocalService());
mAmInternal.setVoiceInteractionManagerProvider(
new ActivityManagerInternal.VoiceInteractionManagerProvider() {
@Override
public void notifyActivityEventChanged() {
if (DEBUG) {
Slog.d(TAG, "call notifyActivityEventChanged");
}
mServiceStub.notifyActivityEventChanged();
}
});
}
@Override
@@ -386,6 +396,14 @@ public class VoiceInteractionManagerService extends SystemService {
return mImpl.supportsLocalVoiceInteraction();
}
void notifyActivityEventChanged() {
synchronized (this) {
if (mImpl == null) return;
Binder.withCleanCallingIdentity(() -> mImpl.notifyActivityEventChangedLocked());
}
}
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
@@ -1109,6 +1127,40 @@ public class VoiceInteractionManagerService extends SystemService {
}
}
@Override
public void startListeningVisibleActivityChanged(@NonNull IBinder token) {
synchronized (this) {
if (mImpl == null) {
Slog.w(TAG, "startListeningVisibleActivityChanged without running"
+ " voice interaction service");
return;
}
final long caller = Binder.clearCallingIdentity();
try {
mImpl.startListeningVisibleActivityChangedLocked(token);
} finally {
Binder.restoreCallingIdentity(caller);
}
}
}
@Override
public void stopListeningVisibleActivityChanged(@NonNull IBinder token) {
synchronized (this) {
if (mImpl == null) {
Slog.w(TAG, "stopListeningVisibleActivityChanged without running"
+ " voice interaction service");
return;
}
final long caller = Binder.clearCallingIdentity();
try {
mImpl.stopListeningVisibleActivityChangedLocked(token);
} finally {
Binder.restoreCallingIdentity(caller);
}
}
}
//----------------- Hotword Detection/Validation APIs --------------------------------//
@Override

View File

@@ -414,6 +414,44 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne
return mInfo.getSupportsLocalInteraction();
}
public void startListeningVisibleActivityChangedLocked(@NonNull IBinder token) {
if (DEBUG) {
Slog.d(TAG, "startListeningVisibleActivityChangedLocked: token=" + token);
}
if (mActiveSession == null || token != mActiveSession.mToken) {
Slog.w(TAG, "startListeningVisibleActivityChangedLocked does not match"
+ " active session");
return;
}
mActiveSession.startListeningVisibleActivityChangedLocked();
}
public void stopListeningVisibleActivityChangedLocked(@NonNull IBinder token) {
if (DEBUG) {
Slog.d(TAG, "stopListeningVisibleActivityChangedLocked: token=" + token);
}
if (mActiveSession == null || token != mActiveSession.mToken) {
Slog.w(TAG, "stopListeningVisibleActivityChangedLocked does not match"
+ " active session");
return;
}
mActiveSession.stopListeningVisibleActivityChangedLocked();
}
public void notifyActivityEventChangedLocked() {
if (DEBUG) {
Slog.d(TAG, "notifyActivityEventChangedLocked");
}
if (mActiveSession == null || !mActiveSession.mShown) {
if (DEBUG) {
Slog.d(TAG, "notifyActivityEventChangedLocked not allowed on no session or"
+ " hidden session");
}
return;
}
mActiveSession.notifyActivityEventChangedLocked();
}
public void updateStateLocked(
@NonNull Identity voiceInteractorIdentity,
@Nullable PersistableBundle options,

View File

@@ -56,6 +56,7 @@ import android.os.UserHandle;
import android.provider.Settings;
import android.service.voice.IVoiceInteractionSession;
import android.service.voice.IVoiceInteractionSessionService;
import android.service.voice.VisibleActivityInfo;
import android.service.voice.VoiceInteractionService;
import android.service.voice.VoiceInteractionSession;
import android.util.Slog;
@@ -71,16 +72,20 @@ 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 com.android.server.wm.ActivityTaskManagerInternal;
import java.io.PrintWriter;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
final class VoiceInteractionSessionConnection implements ServiceConnection,
AssistDataRequesterCallbacks {
static final String TAG = "VoiceInteractionServiceManager";
static final boolean DEBUG = false;
static final int POWER_BOOST_TIMEOUT_MS = Integer.parseInt(
System.getProperty("vendor.powerhal.interaction.max", "200"));
static final int BOOST_TIMEOUT_MS = 300;
@@ -114,6 +119,10 @@ final class VoiceInteractionSessionConnection implements ServiceConnection,
ArrayList<IVoiceInteractionSessionShowCallback> mPendingShowCallbacks = new ArrayList<>();
private List<ActivityAssistInfo> mPendingHandleAssistWithoutData = new ArrayList<>();
AssistDataRequester mAssistDataRequester;
private boolean mListeningVisibleActivity;
private final ScheduledExecutorService mScheduledExecutorService =
Executors.newSingleThreadScheduledExecutor();
private final List<VisibleActivityInfo> mVisibleActivityInfos = new ArrayList<>();
private final PowerManagerInternal mPowerManagerInternal;
private PowerBoostSetter mSetPowerBoostRunnable;
private final Handler mFgHandler;
@@ -496,6 +505,8 @@ final class VoiceInteractionSessionConnection implements ServiceConnection,
}
public void cancelLocked(boolean finishTask) {
mListeningVisibleActivity = false;
mVisibleActivityInfos.clear();
hideLocked();
mCanceled = true;
if (mBound) {
@@ -569,6 +580,156 @@ final class VoiceInteractionSessionConnection implements ServiceConnection,
mPendingShowCallbacks.clear();
}
void startListeningVisibleActivityChangedLocked() {
if (DEBUG) {
Slog.d(TAG, "startListeningVisibleActivityChangedLocked");
}
mListeningVisibleActivity = true;
mVisibleActivityInfos.clear();
mScheduledExecutorService.execute(() -> {
if (DEBUG) {
Slog.d(TAG, "call updateVisibleActivitiesLocked from enable listening");
}
synchronized (mLock) {
updateVisibleActivitiesLocked();
}
});
}
void stopListeningVisibleActivityChangedLocked() {
if (DEBUG) {
Slog.d(TAG, "stopListeningVisibleActivityChangedLocked");
}
mListeningVisibleActivity = false;
mVisibleActivityInfos.clear();
}
void notifyActivityEventChangedLocked() {
if (DEBUG) {
Slog.d(TAG, "notifyActivityEventChangedLocked");
}
if (!mListeningVisibleActivity) {
if (DEBUG) {
Slog.d(TAG, "not enable listening visible activity");
}
return;
}
mScheduledExecutorService.execute(() -> {
if (DEBUG) {
Slog.d(TAG, "call updateVisibleActivitiesLocked from activity event");
}
synchronized (mLock) {
updateVisibleActivitiesLocked();
}
});
}
private List<VisibleActivityInfo> getVisibleActivityInfosLocked() {
if (DEBUG) {
Slog.d(TAG, "getVisibleActivityInfosLocked");
}
List<ActivityAssistInfo> allVisibleActivities =
LocalServices.getService(ActivityTaskManagerInternal.class)
.getTopVisibleActivities();
if (DEBUG) {
Slog.d(TAG,
"getVisibleActivityInfosLocked: allVisibleActivities=" + allVisibleActivities);
}
if (allVisibleActivities == null || allVisibleActivities.isEmpty()) {
Slog.w(TAG, "no visible activity");
return null;
}
final int count = allVisibleActivities.size();
final List<VisibleActivityInfo> visibleActivityInfos = new ArrayList<>(count);
for (int i = 0; i < count; i++) {
ActivityAssistInfo info = allVisibleActivities.get(i);
if (DEBUG) {
Slog.d(TAG, " : activityToken=" + info.getActivityToken()
+ ", assistToken=" + info.getAssistToken()
+ ", taskId=" + info.getTaskId());
}
visibleActivityInfos.add(
new VisibleActivityInfo(info.getTaskId(), info.getAssistToken()));
}
return visibleActivityInfos;
}
private void updateVisibleActivitiesLocked() {
if (DEBUG) {
Slog.d(TAG, "updateVisibleActivitiesLocked");
}
if (mSession == null) {
return;
}
if (!mShown || !mListeningVisibleActivity || mCanceled) {
return;
}
final List<VisibleActivityInfo> newVisibleActivityInfos = getVisibleActivityInfosLocked();
if (newVisibleActivityInfos == null || newVisibleActivityInfos.isEmpty()) {
updateVisibleActivitiesChangedLocked(mVisibleActivityInfos,
VisibleActivityInfo.TYPE_ACTIVITY_REMOVED);
mVisibleActivityInfos.clear();
return;
}
if (mVisibleActivityInfos.isEmpty()) {
updateVisibleActivitiesChangedLocked(newVisibleActivityInfos,
VisibleActivityInfo.TYPE_ACTIVITY_ADDED);
mVisibleActivityInfos.addAll(newVisibleActivityInfos);
return;
}
final List<VisibleActivityInfo> addedActivities = new ArrayList<>();
final List<VisibleActivityInfo> removedActivities = new ArrayList<>();
removedActivities.addAll(mVisibleActivityInfos);
for (int i = 0; i < newVisibleActivityInfos.size(); i++) {
final VisibleActivityInfo candidateVisibleActivityInfo = newVisibleActivityInfos.get(i);
if (!removedActivities.isEmpty() && removedActivities.contains(
candidateVisibleActivityInfo)) {
removedActivities.remove(candidateVisibleActivityInfo);
} else {
addedActivities.add(candidateVisibleActivityInfo);
}
}
if (!addedActivities.isEmpty()) {
updateVisibleActivitiesChangedLocked(addedActivities,
VisibleActivityInfo.TYPE_ACTIVITY_ADDED);
}
if (!removedActivities.isEmpty()) {
updateVisibleActivitiesChangedLocked(removedActivities,
VisibleActivityInfo.TYPE_ACTIVITY_REMOVED);
}
mVisibleActivityInfos.clear();
mVisibleActivityInfos.addAll(newVisibleActivityInfos);
}
private void updateVisibleActivitiesChangedLocked(
List<VisibleActivityInfo> visibleActivityInfos, int type) {
if (visibleActivityInfos == null || visibleActivityInfos.isEmpty()) {
return;
}
if (mSession == null) {
return;
}
try {
for (int i = 0; i < visibleActivityInfos.size(); i++) {
mSession.updateVisibleActivityInfo(visibleActivityInfos.get(i), type);
}
} catch (RemoteException e) {
if (DEBUG) {
Slog.w(TAG, "updateVisibleActivitiesChangedLocked RemoteException : " + e);
}
}
if (DEBUG) {
Slog.d(TAG, "updateVisibleActivitiesChangedLocked type=" + type + ", count="
+ visibleActivityInfos.size());
}
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
synchronized (mLock) {