Voice state + transcription in VoiceInteractionSvc

Voice state as well as voice transcription can be provided by the
VoiceInteractionService. These get proxied to the AssistManager which
can update the system UI to reflect the state & transcription.

Test: TBD
Bug: 122740752
Bug: 123080754
Change-Id: I79cac1d89fe0123bf25a05d551cb4ef40ae1368e
This commit is contained in:
Matt Casey
2019-01-14 14:56:06 -05:00
parent 9d67bb2c2d
commit 4cff16fda9
6 changed files with 201 additions and 15 deletions

View File

@@ -41671,6 +41671,7 @@ package android.service.voice {
public class VoiceInteractionService extends android.app.Service {
ctor public VoiceInteractionService();
method public final void clearTranscription(boolean);
method public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(String, java.util.Locale, android.service.voice.AlwaysOnHotwordDetector.Callback);
method public int getDisabledShowContext();
method public static boolean isActiveService(android.content.Context, android.content.ComponentName);
@@ -41680,9 +41681,15 @@ package android.service.voice {
method public void onReady();
method public void onShutdown();
method public void setDisabledShowContext(int);
method public final void setTranscription(@NonNull String);
method public final void setVoiceState(int);
method public void showSession(android.os.Bundle, int);
field public static final String SERVICE_INTERFACE = "android.service.voice.VoiceInteractionService";
field public static final String SERVICE_META_DATA = "android.voice_interaction";
field public static final int VOICE_STATE_CONDITIONAL_LISTENING = 1; // 0x1
field public static final int VOICE_STATE_FULFILLING = 3; // 0x3
field public static final int VOICE_STATE_LISTENING = 2; // 0x2
field public static final int VOICE_STATE_NONE = 0; // 0x0
}
public class VoiceInteractionSession implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback {

View File

@@ -16,6 +16,7 @@
package android.service.voice;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
@@ -40,6 +41,8 @@ import com.android.internal.util.function.pooled.PooledLambda;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
@@ -77,6 +80,33 @@ public class VoiceInteractionService extends Service {
*/
public static final String SERVICE_META_DATA = "android.voice_interaction";
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"VOICE_STATE_"}, value = {
VOICE_STATE_NONE,
VOICE_STATE_CONDITIONAL_LISTENING,
VOICE_STATE_LISTENING,
VOICE_STATE_FULFILLING})
public @interface VoiceState {
}
/**
* Voice assistant inactive.
*/
public static final int VOICE_STATE_NONE = 0;
/**
* Voice assistant listening, but will only trigger if it hears a request it can fulfill.
*/
public static final int VOICE_STATE_CONDITIONAL_LISTENING = 1;
/**
* Voice assistant is listening to user speech.
*/
public static final int VOICE_STATE_LISTENING = 2;
/**
* Voice assistant is fulfilling an action requested by the user.
*/
public static final int VOICE_STATE_FULFILLING = 3;
IVoiceInteractionService mInterface = new IVoiceInteractionService.Stub() {
@Override
public void ready() {
@@ -341,6 +371,43 @@ public class VoiceInteractionService extends Service {
}
}
/**
* Requests that the voice state UI indicate the given state.
*
* @param state value indicating whether the assistant is listening, fulfilling, etc.
*/
public final void setVoiceState(@VoiceState int state) {
try {
mSystemService.setVoiceState(state);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Displays the given voice transcription contents.
*/
public final void setTranscription(@NonNull String transcription) {
try {
mSystemService.setTranscription(transcription);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Hides transcription.
*
* @param immediate if {@code true}, remove before transcription animation completes.
*/
public final void clearTranscription(boolean immediate) {
try {
mSystemService.clearTranscription(immediate);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("VOICE INTERACTION");

View File

@@ -151,4 +151,19 @@ interface IVoiceInteractionManagerService {
*/
void getActiveServiceSupportedActions(in List<String> voiceActions,
in IVoiceActionCheckCallback callback);
/**
* Sets the transcribed voice to the given string.
*/
void setTranscription(String transcription);
/**
* Indicates that the transcription session is finished.
*/
void clearTranscription(boolean immediate);
/**
* Sets the voice state indication based upon the given value.
*/
void setVoiceState(int state);
}

View File

@@ -26,4 +26,20 @@
* Called when a voice session is hidden.
*/
void onVoiceSessionHidden();
/**
* Called when voice assistant transcription has been updated to the given string.
*/
void onTranscriptionUpdate(in String transcription);
/**
* Called when voice transcription is completed.
*/
void onTranscriptionComplete(in boolean immediate);
/**
* Called when the voice assistant's state has changed. Values are from
* VoiceInteractionService's VOICE_STATE* constants.
*/
void onVoiceStateChange(in int state);
}

View File

@@ -47,6 +47,10 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController;
public class AssistManager implements ConfigurationChangedReceiver {
private static final String TAG = "AssistManager";
// Note that VERBOSE logging may leak PII (e.g. transcription contents).
private static final boolean VERBOSE = false;
private static final String ASSIST_ICON_METADATA_NAME =
"com.android.systemui.action_assist_icon";
@@ -103,16 +107,41 @@ public class AssistManager implements ConfigurationChangedReceiver {
protected void registerVoiceInteractionSessionListener() {
mAssistUtils.registerVoiceInteractionSessionListener(
new IVoiceInteractionSessionListener.Stub() {
@Override
public void onVoiceSessionShown() throws RemoteException {
Log.v(TAG, "Voice open");
}
@Override
public void onVoiceSessionShown() throws RemoteException {
if (VERBOSE) {
Log.v(TAG, "Voice open");
}
}
@Override
public void onVoiceSessionHidden() throws RemoteException {
Log.v(TAG, "Voice closed");
}
});
@Override
public void onVoiceSessionHidden() throws RemoteException {
if (VERBOSE) {
Log.v(TAG, "Voice closed");
}
}
@Override
public void onTranscriptionUpdate(String transcription) {
if (VERBOSE) {
Log.v(TAG, "Transcription Updated: \"" + transcription + "\"");
}
}
@Override
public void onTranscriptionComplete(boolean immediate) {
if (VERBOSE) {
Log.v(TAG, "Transcription complete (immediate=" + immediate + ")");
}
}
@Override
public void onVoiceStateChange(int state) {
if (VERBOSE) {
Log.v(TAG, "Voice state is now " + state);
}
}
});
}
public void onConfigurationChanged(Configuration newConfiguration) {
@@ -291,8 +320,10 @@ public class AssistManager implements ConfigurationChangedReceiver {
}
}
} catch (PackageManager.NameNotFoundException e) {
Log.v(TAG, "Assistant component "
+ component.flattenToShortString() + " not found");
if (VERBOSE) {
Log.v(TAG, "Assistant component "
+ component.flattenToShortString() + " not found");
}
} catch (Resources.NotFoundException nfe) {
Log.w(TAG, "Failed to swap drawable from "
+ component.flattenToShortString(), nfe);

View File

@@ -19,9 +19,6 @@ package com.android.server.voiceinteraction;
import android.Manifest;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import com.android.internal.app.IVoiceActionCheckCallback;
import com.android.server.wm.ActivityTaskManagerInternal;
import android.app.AppGlobals;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -62,8 +59,9 @@ import android.util.ArraySet;
import android.util.Log;
import android.util.Slog;
import com.android.internal.app.IVoiceInteractionSessionListener;
import com.android.internal.app.IVoiceActionCheckCallback;
import com.android.internal.app.IVoiceInteractionManagerService;
import com.android.internal.app.IVoiceInteractionSessionListener;
import com.android.internal.app.IVoiceInteractionSessionShowCallback;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.content.PackageMonitor;
@@ -75,6 +73,7 @@ import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.UiThread;
import com.android.server.soundtrigger.SoundTriggerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -1205,6 +1204,57 @@ public class VoiceInteractionManagerService extends SystemService {
mSoundTriggerInternal.dump(fd, pw, args);
}
@Override
public void setTranscription(String transcription) {
synchronized (this) {
final int size = mVoiceInteractionSessionListeners.beginBroadcast();
for (int i = 0; i < size; ++i) {
final IVoiceInteractionSessionListener listener =
mVoiceInteractionSessionListeners.getBroadcastItem(i);
try {
listener.onTranscriptionUpdate(transcription);
} catch (RemoteException e) {
Slog.e(TAG, "Error delivering voice transcription.", e);
}
}
mVoiceInteractionSessionListeners.finishBroadcast();
}
}
@Override
public void clearTranscription(boolean immediate) {
synchronized (this) {
final int size = mVoiceInteractionSessionListeners.beginBroadcast();
for (int i = 0; i < size; ++i) {
final IVoiceInteractionSessionListener listener =
mVoiceInteractionSessionListeners.getBroadcastItem(i);
try {
listener.onTranscriptionComplete(immediate);
} catch (RemoteException e) {
Slog.e(TAG, "Error delivering transcription complete event.", e);
}
}
mVoiceInteractionSessionListeners.finishBroadcast();
}
}
@Override
public void setVoiceState(int state) {
synchronized (this) {
final int size = mVoiceInteractionSessionListeners.beginBroadcast();
for (int i = 0; i < size; ++i) {
final IVoiceInteractionSessionListener listener =
mVoiceInteractionSessionListeners.getBroadcastItem(i);
try {
listener.onVoiceStateChange(state);
} catch (RemoteException e) {
Slog.e(TAG, "Error delivering voice state change.", e);
}
}
mVoiceInteractionSessionListeners.finishBroadcast();
}
}
private void enforceCallingPermission(String permission) {
if (mContext.checkCallingOrSelfPermission(permission)
!= PackageManager.PERMISSION_GRANTED) {