diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 50c3c78ce9d44..c1b304f398686 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -12399,17 +12399,17 @@ package android.service.voice { method @NonNull public android.service.voice.HotwordDetectedResult.Builder setScore(int); } - public abstract class HotwordDetectionService extends android.app.Service { + public abstract class HotwordDetectionService extends android.app.Service implements android.service.voice.SandboxedDetectionServiceBase { ctor public HotwordDetectionService(); - method public static int getMaxCustomInitializationStatus(); + method @Deprecated public static int getMaxCustomInitializationStatus(); method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent); method public void onDetect(@NonNull android.service.voice.AlwaysOnHotwordDetector.EventPayload, long, @NonNull android.service.voice.HotwordDetectionService.Callback); method public void onDetect(@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 onStopDetection(); 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_SUCCESS = 0; // 0x0 - field public static final int INITIALIZATION_STATUS_UNKNOWN = 100; // 0x64 + field @Deprecated public static final int INITIALIZATION_STATUS_SUCCESS = 0; // 0x0 + field @Deprecated public static final int INITIALIZATION_STATUS_UNKNOWN = 100; // 0x64 field public static final String SERVICE_INTERFACE = "android.service.voice.HotwordDetectionService"; } @@ -12456,6 +12456,13 @@ package android.service.voice { method @NonNull public android.service.voice.HotwordRejectedResult.Builder setConfidenceLevel(int); } + public interface SandboxedDetectionServiceBase { + method public static int getMaxCustomInitializationStatus(); + 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_SUCCESS = 0; // 0x0 + field public static final int INITIALIZATION_STATUS_UNKNOWN = 100; // 0x64 + } + public class VoiceInteractionService extends android.app.Service { method @NonNull public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(String, java.util.Locale, android.service.voice.AlwaysOnHotwordDetector.Callback); method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(String, java.util.Locale, @Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory, android.service.voice.AlwaysOnHotwordDetector.Callback); diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 1cfd64466911e..bb2af73c2b1f1 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -2560,7 +2560,7 @@ package android.service.voice { method @NonNull public android.service.voice.AlwaysOnHotwordDetector.EventPayload.Builder setKeyphraseRecognitionExtras(@NonNull java.util.List); } - public abstract class HotwordDetectionService extends android.app.Service { + public abstract class HotwordDetectionService extends android.app.Service implements android.service.voice.SandboxedDetectionServiceBase { field public static final boolean ENABLE_PROXIMITY_RESULT = true; } diff --git a/core/java/android/service/voice/HotwordDetectionService.java b/core/java/android/service/voice/HotwordDetectionService.java index a47c09662a518..f3d4809a19a84 100644 --- a/core/java/android/service/voice/HotwordDetectionService.java +++ b/core/java/android/service/voice/HotwordDetectionService.java @@ -33,7 +33,6 @@ import android.content.Intent; import android.hardware.soundtrigger.SoundTrigger; import android.media.AudioFormat; import android.media.AudioSystem; -import android.os.Bundle; import android.os.IBinder; import android.os.IRemoteCallback; import android.os.ParcelFileDescriptor; @@ -72,22 +71,13 @@ import java.util.function.IntConsumer; * @hide */ @SystemApi -public abstract class HotwordDetectionService extends Service { +public abstract class HotwordDetectionService extends Service + implements SandboxedDetectionServiceBase { private static final String TAG = "HotwordDetectionService"; private static final boolean DBG = false; private static final long UPDATE_TIMEOUT_MILLIS = 20000; - /** @hide */ - public static final String KEY_INITIALIZATION_STATUS = "initialization_status"; - - /** - * The maximum number of initialization status for some application specific failed reasons. - * - * @hide - */ - public static final int MAXIMUM_NUMBER_OF_INITIALIZATION_STATUS_CUSTOM_ERROR = 2; - /** * Feature flag for Attention Service. * @@ -98,14 +88,22 @@ public abstract class HotwordDetectionService extends Service { /** * Indicates that the updated status is successful. + * + * @deprecated Replaced with {@link SandboxedDetectionServiceBase#INITIALIZATION_STATUS_SUCCESS} */ - public static final int INITIALIZATION_STATUS_SUCCESS = 0; + @Deprecated + public static final int INITIALIZATION_STATUS_SUCCESS = + SandboxedDetectionServiceBase.INITIALIZATION_STATUS_SUCCESS; /** * Indicates that the callback wasn’t invoked within the timeout. * This is used by system. + * + * @deprecated Replaced with {@link SandboxedDetectionServiceBase#INITIALIZATION_STATUS_UNKNOWN} */ - public static final int INITIALIZATION_STATUS_UNKNOWN = 100; + @Deprecated + public static final int INITIALIZATION_STATUS_UNKNOWN = + SandboxedDetectionServiceBase.INITIALIZATION_STATUS_UNKNOWN; /** * Source for the given audio stream. @@ -254,8 +252,11 @@ public abstract class HotwordDetectionService extends Service { * Note: The value 0 is reserved for success. * * @hide + * @deprecated Replaced with + * {@link SandboxedDetectionServiceBase#getMaxCustomInitializationStatus()} */ @SystemApi + @Deprecated public static int getMaxCustomInitializationStatus() { return MAXIMUM_NUMBER_OF_INITIALIZATION_STATUS_CUSTOM_ERROR; } @@ -305,21 +306,10 @@ public abstract class HotwordDetectionService extends Service { * {@link AlwaysOnHotwordDetector#updateState(PersistableBundle, SharedMemory)} requests an * update of the hotword detection parameters. * - * @param options Application configuration data to provide to the - * {@link HotwordDetectionService}. PersistableBundle does not allow any remotable objects or - * other contents that can be used to communicate with other processes. - * @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; the allowed values are - * {@link #INITIALIZATION_STATUS_SUCCESS}, 1<->{@link #getMaxCustomInitializationStatus()}. - * This is non-null only when the {@link HotwordDetectionService} is being initialized; and it - * is null if the state is updated after that. - * + * {@inheritDoc} * @hide */ + @Override @SystemApi public void onUpdateState( @Nullable PersistableBundle options, @@ -371,23 +361,8 @@ public abstract class HotwordDetectionService extends Service { private void onUpdateStateInternal(@Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory, IRemoteCallback callback) { - IntConsumer intConsumer = null; - if (callback != null) { - intConsumer = - value -> { - if (value > getMaxCustomInitializationStatus()) { - throw new IllegalArgumentException( - "The initialization status is invalid for " + value); - } - try { - Bundle status = new Bundle(); - status.putInt(KEY_INITIALIZATION_STATUS, value); - callback.sendResult(status); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - }; - } + IntConsumer intConsumer = + SandboxedDetectionServiceBase.createInitializationStatusConsumer(callback); onUpdateState(options, sharedMemory, UPDATE_TIMEOUT_MILLIS, intConsumer); } diff --git a/core/java/android/service/voice/HotwordDetector.java b/core/java/android/service/voice/HotwordDetector.java index b7f7d54fc0553..669c22b73edcf 100644 --- a/core/java/android/service/voice/HotwordDetector.java +++ b/core/java/android/service/voice/HotwordDetector.java @@ -35,7 +35,7 @@ import android.util.AndroidException; import java.io.PrintWriter; /** - * Basic functionality for hotword detectors. + * Basic functionality for sandboxed detectors. * * @hide */ @@ -81,7 +81,7 @@ public interface HotwordDetector { int DETECTOR_TYPE_TRUSTED_HOTWORD_SOFTWARE = 2; /** - * Starts hotword recognition. + * Starts sandboxed detection recognition. *

* On calling this, the system streams audio from the device microphone to this application's * {@link HotwordDetectionService}. Audio is streamed until {@link #stopRecognition()} is @@ -107,7 +107,7 @@ public interface HotwordDetector { boolean startRecognition() throws IllegalDetectorStateException; /** - * Stops hotword recognition. + * Stops sandboxed detection recognition. * * @return {@code true} if the request to stop recognition succeeded * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33 @@ -142,14 +142,13 @@ public interface HotwordDetector { @Nullable PersistableBundle options) throws IllegalDetectorStateException; /** - * Set configuration and pass read-only data to hotword detection service. + * Set configuration and pass read-only data to sandboxed detection service. * - * @param options Application configuration data to provide to the - * {@link HotwordDetectionService}. PersistableBundle does not allow any remotable - * objects or other contents that can be used to communicate with other processes. - * @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 options Application configuration data to provide to sandboxed detection services. + * PersistableBundle does not allow any remotable objects or other contents that can be used to + * communicate with other processes. + * @param sharedMemory The unrestricted data blob to provide to sandboxed detection services. + * Use this to provide model data or other such data to the trusted process. * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33 * or above and the detector is not able to perform the operation based on the * underlying state. This can be thrown even if the state has been checked before @@ -157,19 +156,19 @@ public interface HotwordDetector { * and the state of the detector can change concurrently to the caller calling this * method. * @throws IllegalStateException if this HotwordDetector wasn't specified to use a - * {@link HotwordDetectionService} when it was created. + * sandboxed detection service when it was created. */ void updateState(@Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory) throws IllegalDetectorStateException; /** - * Invalidates this hotword detector so that any future calls to this result + * Invalidates this detector so that any future calls to this result * in an {@link IllegalStateException} when a caller has a target SDK below API level 33 * or an {@link IllegalDetectorStateException} when a caller has a target SDK of API level 33 * or above. * *

If there are no other {@link HotwordDetector} instances linked to the - * {@link HotwordDetectionService}, the service will be shutdown. + * sandboxed detection service, the service will be shutdown. */ default void destroy() { throw new UnsupportedOperationException("Not implemented. Must override in a subclass."); @@ -249,9 +248,9 @@ public interface HotwordDetector { * short amount of time to report its initialization state. * * @param status Info about initialization state of {@link HotwordDetectionService}; the - * allowed values are {@link HotwordDetectionService#INITIALIZATION_STATUS_SUCCESS}, - * 1<->{@link HotwordDetectionService#getMaxCustomInitializationStatus()}, - * {@link HotwordDetectionService#INITIALIZATION_STATUS_UNKNOWN}. + * allowed values are {@link SandboxedDetectionServiceBase#INITIALIZATION_STATUS_SUCCESS}, + * 1<->{@link SandboxedDetectionServiceBase#getMaxCustomInitializationStatus()}, + * {@link SandboxedDetectionServiceBase#INITIALIZATION_STATUS_UNKNOWN}. */ void onHotwordDetectionServiceInitialized(int status); diff --git a/core/java/android/service/voice/SandboxedDetectionServiceBase.java b/core/java/android/service/voice/SandboxedDetectionServiceBase.java new file mode 100644 index 0000000000000..43331642bd1ef --- /dev/null +++ b/core/java/android/service/voice/SandboxedDetectionServiceBase.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2022 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.DurationMillisLong; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.os.Bundle; +import android.os.IRemoteCallback; +import android.os.PersistableBundle; +import android.os.RemoteException; +import android.os.SharedMemory; + +import java.util.function.IntConsumer; + +/** + * Base for all sandboxed detection services, providing a common interface for initialization. + * + * @hide + */ +@SystemApi +public interface SandboxedDetectionServiceBase { + + /** + * Indicates that the updated status is successful. + */ + int INITIALIZATION_STATUS_SUCCESS = 0; + + /** + * Indicates that the callback wasn’t invoked within the timeout. + * This is used by system. + */ + int INITIALIZATION_STATUS_UNKNOWN = 100; + + /** @hide */ + String KEY_INITIALIZATION_STATUS = "initialization_status"; + + /** + * The maximum number of initialization status for some application specific failed reasons. + * + * @hide + */ + int MAXIMUM_NUMBER_OF_INITIALIZATION_STATUS_CUSTOM_ERROR = 2; + + /** + * Returns the maximum number of initialization status for some application specific failed + * reasons. + * + * Note: The value 0 is reserved for success. + */ + static int getMaxCustomInitializationStatus() { + return MAXIMUM_NUMBER_OF_INITIALIZATION_STATUS_CUSTOM_ERROR; + } + + /** + * Creates a {@link IntConsumer} that sends the initialization status to the + * {@link VoiceInteractionService} via {@link IRemoteCallback}. + * + * @hide + */ + static IntConsumer createInitializationStatusConsumer(IRemoteCallback callback) { + IntConsumer intConsumer = null; + if (callback != null) { + intConsumer = + value -> { + if (value > SandboxedDetectionServiceBase + .getMaxCustomInitializationStatus()) { + throw new IllegalArgumentException( + "The initialization status is invalid for " + value); + } + try { + Bundle status = new Bundle(); + status.putInt(KEY_INITIALIZATION_STATUS, value); + callback.sendResult(status); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + }; + } + return intConsumer; + } + + /** + * Called when sandboxed detectors that extend {@link HotwordDetector} are created or + * {@link HotwordDetector#updateState(PersistableBundle, SharedMemory)} requests an + * update of the sandboxed detection parameters. + * + * @param options Application configuration data to provide to sandboxed detection services. + * PersistableBundle does not allow any remotable objects or other contents that can be used to + * communicate with other processes. + * @param sharedMemory The unrestricted data blob to provide to sandboxed detection services. + * Use this to provide model 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; the allowed values are + * {@link #INITIALIZATION_STATUS_SUCCESS}, 1<->{@link #getMaxCustomInitializationStatus()}. + * This is non-null only when sandboxed detection services are being initialized; and it + * is null if the state is updated after that. + */ + void onUpdateState( + @Nullable PersistableBundle options, + @Nullable SharedMemory sharedMemory, + @DurationMillisLong long callbackTimeoutMillis, + @Nullable IntConsumer statusCallback); +}