diff options
11 files changed, 156 insertions, 23 deletions
diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 49b8a901bcbb..062c56069215 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -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); diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java index 73e0da16e049..8ca0e7ccff37 100644 --- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java +++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java @@ -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); } diff --git a/core/java/android/service/voice/HotwordDetectionService.java b/core/java/android/service/voice/HotwordDetectionService.java index fb731a094f90..7c14c2e19eb1 100644 --- a/core/java/android/service/voice/HotwordDetectionService.java +++ b/core/java/android/service/voice/HotwordDetectionService.java @@ -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. * diff --git a/core/java/android/service/voice/HotwordDetector.java b/core/java/android/service/voice/HotwordDetector.java index 26491245914f..f4e5ddaa31a6 100644 --- a/core/java/android/service/voice/HotwordDetector.java +++ b/core/java/android/service/voice/HotwordDetector.java @@ -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); } } diff --git a/core/java/android/service/voice/IHotwordDetectionService.aidl b/core/java/android/service/voice/IHotwordDetectionService.aidl index cb140f9346fa..cac8333a5855 100644 --- a/core/java/android/service/voice/IHotwordDetectionService.aidl +++ b/core/java/android/service/voice/IHotwordDetectionService.aidl @@ -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); } diff --git a/core/java/android/service/voice/SoftwareHotwordDetector.java b/core/java/android/service/voice/SoftwareHotwordDetector.java index f49a9d45ae06..376596b9b0d0 100644 --- a/core/java/android/service/voice/SoftwareHotwordDetector.java +++ b/core/java/android/service/voice/SoftwareHotwordDetector.java @@ -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(); } diff --git a/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl b/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl index 23314e7be622..9bec505c801d 100644 --- a/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl +++ b/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl @@ -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); } diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl index bb6233bc772d..fffeb024caae 100644 --- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl +++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl @@ -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. diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java index bbfb0d6a392b..5d541e9cab5d 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java @@ -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( diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index d66f21fba1ab..ccaeaf991467 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -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); } diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java index efa7f78e22aa..6922ccc03d0b 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java @@ -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); } |