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:
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
19
core/java/android/service/voice/VisibleActivityInfo.aidl
Normal file
19
core/java/android/service/voice/VisibleActivityInfo.aidl
Normal 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;
|
||||
205
core/java/android/service/voice/VisibleActivityInfo.java
Normal file
205
core/java/android/service/voice/VisibleActivityInfo.java
Normal 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
|
||||
|
||||
}
|
||||
@@ -22,7 +22,6 @@ import android.os.IBinder;
|
||||
|
||||
import com.android.internal.annotations.Immutable;
|
||||
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* Private interface to the VoiceInteractionManagerService for use by ActivityManagerService.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user