Add callback to report HotwordDetectionService status
Bug: 182788844 Test: Test: atest CtsVoiceInteractionTestCases Test: atest CtsVoiceInteractionTestCases --instant Change-Id: I12f0a265fc4fce0bbfe0983f39b4dcd947e3cf64
This commit is contained in:
@@ -10372,6 +10372,7 @@ package android.service.voice {
|
||||
public abstract static class AlwaysOnHotwordDetector.Callback implements android.service.voice.HotwordDetector.Callback {
|
||||
ctor public AlwaysOnHotwordDetector.Callback();
|
||||
method public abstract void onAvailabilityChanged(int);
|
||||
method public void onHotwordDetectionServiceInitialized(int);
|
||||
method public void onRejected(@Nullable android.service.voice.HotwordRejectedResult);
|
||||
}
|
||||
|
||||
@@ -10419,7 +10420,11 @@ package android.service.voice {
|
||||
method public void onDetect(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, long, @NonNull android.service.voice.HotwordDetectionService.Callback);
|
||||
method public void onDetect(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, @NonNull android.service.voice.HotwordDetectionService.Callback);
|
||||
method public void onDetect(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, @Nullable android.os.PersistableBundle, @NonNull android.service.voice.HotwordDetectionService.Callback);
|
||||
method public void onUpdateState(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory);
|
||||
method public void onUpdateState(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory, long, @Nullable java.util.function.IntConsumer);
|
||||
field public static final int INITIALIZATION_STATUS_CUSTOM_ERROR_1 = 1; // 0x1
|
||||
field public static final int INITIALIZATION_STATUS_CUSTOM_ERROR_2 = 2; // 0x2
|
||||
field public static final int INITIALIZATION_STATUS_SUCCESS = 0; // 0x0
|
||||
field public static final int INITIALIZATION_STATUS_UNKNOWN = 100; // 0x64
|
||||
field public static final String SERVICE_INTERFACE = "android.service.voice.HotwordDetectionService";
|
||||
}
|
||||
|
||||
@@ -10441,6 +10446,7 @@ package android.service.voice {
|
||||
public static interface HotwordDetector.Callback {
|
||||
method public void onDetected(@NonNull android.service.voice.AlwaysOnHotwordDetector.EventPayload);
|
||||
method public void onError();
|
||||
method public void onHotwordDetectionServiceInitialized(int);
|
||||
method public void onRecognitionPaused();
|
||||
method public void onRecognitionResumed();
|
||||
method public void onRejected(@Nullable android.service.voice.HotwordRejectedResult);
|
||||
|
||||
@@ -48,6 +48,7 @@ import android.os.ParcelFileDescriptor;
|
||||
import android.os.PersistableBundle;
|
||||
import android.os.RemoteException;
|
||||
import android.os.SharedMemory;
|
||||
import android.service.voice.HotwordDetectionService.InitializationStatus;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.app.IHotwordRecognitionStatusCallback;
|
||||
@@ -260,6 +261,7 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector {
|
||||
private static final int MSG_DETECTION_PAUSE = 4;
|
||||
private static final int MSG_DETECTION_RESUME = 5;
|
||||
private static final int MSG_HOTWORD_REJECTED = 6;
|
||||
private static final int MSG_HOTWORD_STATUS_REPORTED = 7;
|
||||
|
||||
private final String mText;
|
||||
private final Locale mLocale;
|
||||
@@ -523,6 +525,15 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector {
|
||||
*/
|
||||
public void onRejected(@Nullable HotwordRejectedResult result) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the {@link HotwordDetectionService} is created by the system and given a
|
||||
* short amount of time to report it's initialization state.
|
||||
*
|
||||
* @param status Info about initialization state of {@link HotwordDetectionService}.
|
||||
*/
|
||||
public void onHotwordDetectionServiceInitialized(@InitializationStatus int status) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -559,7 +570,7 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector {
|
||||
mTargetSdkVersion = targetSdkVersion;
|
||||
mSupportHotwordDetectionService = supportHotwordDetectionService;
|
||||
if (mSupportHotwordDetectionService) {
|
||||
updateState(options, sharedMemory);
|
||||
updateStateLocked(options, sharedMemory, mInternalCallback);
|
||||
}
|
||||
try {
|
||||
Identity identity = new Identity();
|
||||
@@ -583,20 +594,34 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector {
|
||||
* such data to the trusted process.
|
||||
*
|
||||
* @throws IllegalStateException if this AlwaysOnHotwordDetector wasn't specified to use a
|
||||
* {@link HotwordDetectionService} when it was created.
|
||||
* {@link HotwordDetectionService} when it was created. In addition, if this
|
||||
* AlwaysOnHotwordDetector is in an invalid or error state.
|
||||
*/
|
||||
public final void updateState(@Nullable PersistableBundle options,
|
||||
@Nullable SharedMemory sharedMemory) {
|
||||
if (DBG) {
|
||||
Slog.d(TAG, "updateState()");
|
||||
}
|
||||
if (!mSupportHotwordDetectionService) {
|
||||
throw new IllegalStateException(
|
||||
"updateState called, but it doesn't support hotword detection service");
|
||||
synchronized (mLock) {
|
||||
if (!mSupportHotwordDetectionService) {
|
||||
throw new IllegalStateException(
|
||||
"updateState called, but it doesn't support hotword detection service");
|
||||
}
|
||||
if (mAvailability == STATE_INVALID || mAvailability == STATE_ERROR) {
|
||||
throw new IllegalStateException(
|
||||
"updateState called on an invalid detector or error state");
|
||||
}
|
||||
updateStateLocked(options, sharedMemory, null /* callback */);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateStateLocked(@Nullable PersistableBundle options,
|
||||
@Nullable SharedMemory sharedMemory, IHotwordRecognitionStatusCallback callback) {
|
||||
if (DBG) {
|
||||
Slog.d(TAG, "updateStateLocked()");
|
||||
}
|
||||
try {
|
||||
mModelManagementService.updateState(options, sharedMemory);
|
||||
mModelManagementService.updateState(options, sharedMemory, callback);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
@@ -1147,6 +1172,18 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector {
|
||||
Slog.i(TAG, "onRecognitionResumed");
|
||||
mHandler.sendEmptyMessage(MSG_DETECTION_RESUME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStatusReported(int status) {
|
||||
if (DBG) {
|
||||
Slog.d(TAG, "onStatusReported(" + status + ")");
|
||||
} else {
|
||||
Slog.i(TAG, "onStatusReported");
|
||||
}
|
||||
Message message = Message.obtain(mHandler, MSG_HOTWORD_STATUS_REPORTED);
|
||||
message.arg1 = status;
|
||||
message.sendToTarget();
|
||||
}
|
||||
}
|
||||
|
||||
class MyHandler extends Handler {
|
||||
@@ -1178,6 +1215,9 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector {
|
||||
case MSG_HOTWORD_REJECTED:
|
||||
mExternalCallback.onRejected((HotwordRejectedResult) msg.obj);
|
||||
break;
|
||||
case MSG_HOTWORD_STATUS_REPORTED:
|
||||
mExternalCallback.onHotwordDetectionServiceInitialized(msg.arg1);
|
||||
break;
|
||||
default:
|
||||
super.handleMessage(msg);
|
||||
}
|
||||
|
||||
@@ -37,10 +37,13 @@ import android.os.RemoteException;
|
||||
import android.os.SharedMemory;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.app.IHotwordRecognitionStatusCallback;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.Locale;
|
||||
import java.util.function.IntConsumer;
|
||||
|
||||
/**
|
||||
* Implemented by an application that wants to offer detection for hotword. The system will
|
||||
@@ -54,6 +57,39 @@ public abstract class HotwordDetectionService extends Service {
|
||||
// TODO (b/177502877): Set the Debug flag to false before shipping.
|
||||
private static final boolean DBG = true;
|
||||
|
||||
private static final long UPDATE_TIMEOUT_MILLIS = 5000;
|
||||
|
||||
/** @hide */
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef(flag = true, prefix = { "INITIALIZATION_STATUS_" }, value = {
|
||||
INITIALIZATION_STATUS_SUCCESS,
|
||||
INITIALIZATION_STATUS_CUSTOM_ERROR_1,
|
||||
INITIALIZATION_STATUS_CUSTOM_ERROR_2,
|
||||
INITIALIZATION_STATUS_UNKNOWN,
|
||||
})
|
||||
public @interface InitializationStatus {}
|
||||
|
||||
/**
|
||||
* Indicates that the updated status is successful.
|
||||
*/
|
||||
public static final int INITIALIZATION_STATUS_SUCCESS = 0;
|
||||
|
||||
/**
|
||||
* Indicates that the updated status is failure for some application specific reasons.
|
||||
*/
|
||||
public static final int INITIALIZATION_STATUS_CUSTOM_ERROR_1 = 1;
|
||||
|
||||
/**
|
||||
* Indicates that the updated status is failure for some application specific reasons.
|
||||
*/
|
||||
public static final int INITIALIZATION_STATUS_CUSTOM_ERROR_2 = 2;
|
||||
|
||||
/**
|
||||
* Indicates that the callback wasn’t invoked within the timeout.
|
||||
* This is used by system.
|
||||
*/
|
||||
public static final int INITIALIZATION_STATUS_UNKNOWN = 100;
|
||||
|
||||
/**
|
||||
* Source for the given audio stream.
|
||||
*
|
||||
@@ -104,15 +140,16 @@ public abstract class HotwordDetectionService extends Service {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState(PersistableBundle options, SharedMemory sharedMemory)
|
||||
throws RemoteException {
|
||||
public void updateState(PersistableBundle options, SharedMemory sharedMemory,
|
||||
IHotwordRecognitionStatusCallback callback) throws RemoteException {
|
||||
if (DBG) {
|
||||
Log.d(TAG, "#updateState");
|
||||
}
|
||||
mHandler.sendMessage(obtainMessage(HotwordDetectionService::onUpdateState,
|
||||
mHandler.sendMessage(obtainMessage(HotwordDetectionService::onUpdateStateInternal,
|
||||
HotwordDetectionService.this,
|
||||
options,
|
||||
sharedMemory));
|
||||
sharedMemory,
|
||||
callback));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -207,12 +244,20 @@ public abstract class HotwordDetectionService extends Service {
|
||||
* @param sharedMemory The unrestricted data blob to provide to the
|
||||
* {@link HotwordDetectionService}. Use this to provide the hotword models data or other
|
||||
* such data to the trusted process.
|
||||
* @param callbackTimeoutMillis Timeout in milliseconds for the operation to invoke the
|
||||
* statusCallback.
|
||||
* @param statusCallback Use this to return the updated result. This is non-null only when the
|
||||
* {@link HotwordDetectionService} is being initialized; and it is null if the state is updated
|
||||
* after that.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public void onUpdateState(@Nullable PersistableBundle options,
|
||||
@Nullable SharedMemory sharedMemory) {
|
||||
public void onUpdateState(
|
||||
@Nullable PersistableBundle options,
|
||||
@Nullable SharedMemory sharedMemory,
|
||||
@DurationMillisLong long callbackTimeoutMillis,
|
||||
@Nullable @InitializationStatus IntConsumer statusCallback) {
|
||||
// TODO: Handle the unimplemented case by throwing?
|
||||
}
|
||||
|
||||
@@ -268,6 +313,23 @@ public abstract class HotwordDetectionService extends Service {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private void onUpdateStateInternal(@Nullable PersistableBundle options,
|
||||
@Nullable SharedMemory sharedMemory, IHotwordRecognitionStatusCallback callback) {
|
||||
// TODO (b/183684347): Implement timeout case.
|
||||
IntConsumer intConsumer = null;
|
||||
if (callback != null) {
|
||||
intConsumer =
|
||||
value -> {
|
||||
try {
|
||||
callback.onStatusReported(value);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
};
|
||||
}
|
||||
onUpdateState(options, sharedMemory, UPDATE_TIMEOUT_MILLIS, intConsumer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for returning the detection result.
|
||||
*
|
||||
|
||||
@@ -27,6 +27,7 @@ import android.annotation.SystemApi;
|
||||
import android.media.AudioFormat;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.PersistableBundle;
|
||||
import android.service.voice.HotwordDetectionService.InitializationStatus;
|
||||
|
||||
/**
|
||||
* Basic functionality for hotword detectors.
|
||||
@@ -144,5 +145,13 @@ public interface HotwordDetector {
|
||||
* {@link HotwordDetectionService}.
|
||||
*/
|
||||
void onRejected(@Nullable HotwordRejectedResult result);
|
||||
|
||||
/**
|
||||
* Called when the {@link HotwordDetectionService} is created by the system and given a
|
||||
* short amount of time to report it's initialization state.
|
||||
*
|
||||
* @param status Info about initialization state of {@link HotwordDetectionService}.
|
||||
*/
|
||||
void onHotwordDetectionServiceInitialized(@InitializationStatus int status);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ import android.os.PersistableBundle;
|
||||
import android.os.SharedMemory;
|
||||
import android.service.voice.IDspHotwordDetectionCallback;
|
||||
|
||||
import com.android.internal.app.IHotwordRecognitionStatusCallback;
|
||||
|
||||
/**
|
||||
* Provide the interface to communicate with hotword detection service.
|
||||
*
|
||||
@@ -41,5 +43,6 @@ oneway interface IHotwordDetectionService {
|
||||
in PersistableBundle options,
|
||||
in IDspHotwordDetectionCallback callback);
|
||||
|
||||
void updateState(in PersistableBundle options, in SharedMemory sharedMemory);
|
||||
void updateState(in PersistableBundle options, in SharedMemory sharedMemory,
|
||||
in IHotwordRecognitionStatusCallback callback);
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ class SoftwareHotwordDetector extends AbstractHotwordDetector {
|
||||
mHandler = new Handler(Looper.getMainLooper());
|
||||
|
||||
try {
|
||||
mManagerService.updateState(options, sharedMemory);
|
||||
mManagerService.updateState(options, sharedMemory, null /* callback */);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
|
||||
@@ -66,4 +66,12 @@ oneway interface IHotwordRecognitionStatusCallback {
|
||||
* Called when the recognition is resumed after it was temporarily paused.
|
||||
*/
|
||||
void onRecognitionResumed();
|
||||
|
||||
/**
|
||||
* Called when the {@link HotwordDetectionService} reported the result for requesting update
|
||||
* state action.
|
||||
*
|
||||
* @param status The status about the result of requesting update state action.
|
||||
*/
|
||||
void onStatusReported(int status);
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import android.os.PersistableBundle;
|
||||
import android.os.RemoteCallback;
|
||||
import android.os.SharedMemory;
|
||||
|
||||
import com.android.internal.app.IHotwordRecognitionStatusCallback;
|
||||
import com.android.internal.app.IVoiceActionCheckCallback;
|
||||
import com.android.internal.app.IVoiceInteractionSessionShowCallback;
|
||||
import com.android.internal.app.IVoiceInteractor;
|
||||
@@ -238,8 +239,12 @@ interface IVoiceInteractionManagerService {
|
||||
* @param sharedMemory The unrestricted data blob to provide to the
|
||||
* {@link HotwordDetectionService}. Use this to provide the hotword models data or other
|
||||
* such data to the trusted process.
|
||||
* @param callback Use this to report {@link HotwordDetectionService} status.
|
||||
*/
|
||||
void updateState(in PersistableBundle options, in SharedMemory sharedMemory);
|
||||
void updateState(
|
||||
in PersistableBundle options,
|
||||
in SharedMemory sharedMemory,
|
||||
in IHotwordRecognitionStatusCallback callback);
|
||||
|
||||
/**
|
||||
* Requests to shutdown hotword detection service.
|
||||
|
||||
@@ -93,7 +93,7 @@ final class HotwordDetectionConnection {
|
||||
|
||||
HotwordDetectionConnection(Object lock, Context context, ComponentName serviceName,
|
||||
int userId, boolean bindInstantServiceAllowed, @Nullable PersistableBundle options,
|
||||
@Nullable SharedMemory sharedMemory) {
|
||||
@Nullable SharedMemory sharedMemory, IHotwordRecognitionStatusCallback callback) {
|
||||
mLock = lock;
|
||||
mContext = context;
|
||||
mDetectionComponentName = serviceName;
|
||||
@@ -111,7 +111,7 @@ final class HotwordDetectionConnection {
|
||||
mBound = connected;
|
||||
if (connected) {
|
||||
try {
|
||||
service.updateState(options, sharedMemory);
|
||||
service.updateState(options, sharedMemory, callback);
|
||||
} catch (RemoteException e) {
|
||||
// TODO: (b/181842909) Report an error to voice interactor
|
||||
Slog.w(TAG, "Failed to updateState for HotwordDetectionService", e);
|
||||
@@ -146,7 +146,7 @@ final class HotwordDetectionConnection {
|
||||
|
||||
void updateStateLocked(PersistableBundle options, SharedMemory sharedMemory) {
|
||||
mRemoteHotwordDetectionService.run(
|
||||
service -> service.updateState(options, sharedMemory));
|
||||
service -> service.updateState(options, sharedMemory, null /* callback */));
|
||||
}
|
||||
|
||||
void startListeningFromMic(
|
||||
|
||||
@@ -1026,7 +1026,7 @@ public class VoiceInteractionManagerService extends SystemService {
|
||||
|
||||
@Override
|
||||
public void updateState(@Nullable PersistableBundle options,
|
||||
@Nullable SharedMemory sharedMemory) {
|
||||
@Nullable SharedMemory sharedMemory, IHotwordRecognitionStatusCallback callback) {
|
||||
enforceCallingPermission(Manifest.permission.MANAGE_HOTWORD_DETECTION);
|
||||
synchronized (this) {
|
||||
enforceIsCurrentVoiceInteractionService();
|
||||
@@ -1037,7 +1037,7 @@ public class VoiceInteractionManagerService extends SystemService {
|
||||
}
|
||||
final long caller = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mImpl.updateStateLocked(options, sharedMemory);
|
||||
mImpl.updateStateLocked(options, sharedMemory, callback);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(caller);
|
||||
}
|
||||
|
||||
@@ -405,7 +405,7 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne
|
||||
}
|
||||
|
||||
public void updateStateLocked(@Nullable PersistableBundle options,
|
||||
@Nullable SharedMemory sharedMemory) {
|
||||
@Nullable SharedMemory sharedMemory, IHotwordRecognitionStatusCallback callback) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "updateStateLocked");
|
||||
}
|
||||
@@ -427,7 +427,7 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne
|
||||
if (mHotwordDetectionConnection == null) {
|
||||
mHotwordDetectionConnection = new HotwordDetectionConnection(mServiceStub, mContext,
|
||||
mHotwordDetectionComponentName, mUser, /* bindInstantServiceAllowed= */ false,
|
||||
options, sharedMemory);
|
||||
options, sharedMemory, callback);
|
||||
} else {
|
||||
mHotwordDetectionConnection.updateStateLocked(options, sharedMemory);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user