Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 17675424 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "update HotwordDetector exception throws"

parents d3258e0a d7f42e08
Loading
Loading
Loading
Loading
+20 −17
Original line number Diff line number Diff line
@@ -11866,20 +11866,20 @@ package android.service.trust {
package android.service.voice {
  public class AlwaysOnHotwordDetector implements android.service.voice.HotwordDetector {
    method @Nullable public android.content.Intent createEnrollIntent();
    method @Nullable public android.content.Intent createReEnrollIntent();
    method @Nullable public android.content.Intent createUnEnrollIntent();
    method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public int getParameter(int);
    method @Nullable public android.content.Intent createEnrollIntent() throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
    method @Nullable public android.content.Intent createReEnrollIntent() throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
    method @Nullable public android.content.Intent createUnEnrollIntent() throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
    method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public int getParameter(int) throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
    method public int getSupportedAudioCapabilities();
    method public int getSupportedRecognitionModes();
    method @Nullable @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public android.service.voice.AlwaysOnHotwordDetector.ModelParamRange queryParameter(int);
    method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public int setParameter(int, int);
    method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean startRecognition(int, @NonNull byte[]);
    method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean startRecognition(int);
    method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean startRecognition();
    method public boolean startRecognition(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, @Nullable android.os.PersistableBundle);
    method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean stopRecognition();
    method public final void updateState(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory);
    method public int getSupportedRecognitionModes() throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
    method @Nullable @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public android.service.voice.AlwaysOnHotwordDetector.ModelParamRange queryParameter(int) throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
    method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public int setParameter(int, int) throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
    method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean startRecognition(int, @NonNull byte[]) throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
    method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean startRecognition(int) throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
    method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean startRecognition() throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
    method public boolean startRecognition(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, @Nullable android.os.PersistableBundle) throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
    method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean stopRecognition() throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
    method public final void updateState(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory) throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
    field public static final int AUDIO_CAPABILITY_ECHO_CANCELLATION = 1; // 0x1
    field public static final int AUDIO_CAPABILITY_NOISE_SUPPRESSION = 2; // 0x2
    field public static final int MODEL_PARAM_THRESHOLD_FACTOR = 0; // 0x0
@@ -11986,10 +11986,10 @@ package android.service.voice {
  public interface HotwordDetector {
    method public default void destroy();
    method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean startRecognition();
    method public boolean startRecognition(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, @Nullable android.os.PersistableBundle);
    method public boolean stopRecognition();
    method public void updateState(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory);
    method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean startRecognition() throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
    method public boolean startRecognition(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, @Nullable android.os.PersistableBundle) throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
    method public boolean stopRecognition() throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
    method public void updateState(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory) throws android.service.voice.HotwordDetector.IllegalDetectorStateException;
  }
  public static interface HotwordDetector.Callback {
@@ -12002,6 +12002,9 @@ package android.service.voice {
    method public void onRejected(@NonNull android.service.voice.HotwordRejectedResult);
  }
  public static class HotwordDetector.IllegalDetectorStateException extends android.util.AndroidException {
  }
  public final class HotwordRejectedResult implements android.os.Parcelable {
    method public int describeContents();
    method public int getConfidenceLevel();
+19 −11
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityThread;
import android.app.compat.CompatChanges;
import android.media.AudioFormat;
import android.media.permission.Identity;
import android.os.Handler;
@@ -80,7 +81,7 @@ abstract class AbstractHotwordDetector implements HotwordDetector {
    public boolean startRecognition(
            @NonNull ParcelFileDescriptor audioStream,
            @NonNull AudioFormat audioFormat,
            @Nullable PersistableBundle options) {
            @Nullable PersistableBundle options) throws IllegalDetectorStateException {
        if (DEBUG) {
            Slog.i(TAG, "#recognizeHotword");
        }
@@ -105,19 +106,22 @@ abstract class AbstractHotwordDetector implements HotwordDetector {
     * Set configuration and pass read-only data to hotword 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.
     *         {@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.
     *
     * @throws IllegalStateException if this AlwaysOnHotwordDetector wasn't specified to use a
     * {@link HotwordDetectionService} when it was created. In addition, if this
     * AlwaysOnHotwordDetector is in an invalid or error state.
     * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of
     *         Android Tiramisu or above and attempts to start a recognition when the detector is
     *         not able based on the state. Because the caller receives updates via an asynchronous
     *         callback and the state of the detector can change without caller's knowledge, a
     *         checked exception is thrown.
     * @throws IllegalStateException if this HotwordDetector wasn't specified to use a
     *         {@link HotwordDetectionService} when it was created.
     */
    @Override
    public void updateState(@Nullable PersistableBundle options,
            @Nullable SharedMemory sharedMemory) {
            @Nullable SharedMemory sharedMemory) throws IllegalDetectorStateException {
        if (DEBUG) {
            Slog.d(TAG, "updateState()");
        }
@@ -163,9 +167,13 @@ abstract class AbstractHotwordDetector implements HotwordDetector {
        }
    }

    protected void throwIfDetectorIsNoLongerActive() {
    protected void throwIfDetectorIsNoLongerActive() throws IllegalDetectorStateException {
        if (!mIsDetectorActive.get()) {
            Slog.e(TAG, "attempting to use a destroyed detector which is no longer active");
            if (CompatChanges.isChangeEnabled(HOTWORD_DETECTOR_THROW_CHECKED_EXCEPTION)) {
                throw new IllegalDetectorStateException(
                        "attempting to use a destroyed detector which is no longer active");
            }
            throw new IllegalStateException(
                    "attempting to use a destroyed detector which is no longer active");
        }
+224 −73

File changed.

Preview size limit exceeded, changes collapsed.

+62 −10
Original line number Diff line number Diff line
@@ -23,10 +23,14 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.media.AudioFormat;
import android.os.Build;
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.SharedMemory;
import android.util.AndroidException;

/**
 * Basic functionality for hotword detectors.
@@ -36,6 +40,23 @@ import android.os.SharedMemory;
@SystemApi
public interface HotwordDetector {

    /**
     * Prior to API level 33, API calls of {@link android.service.voice.HotwordDetector} could
     * return both {@link java.lang.IllegalStateException} or
     * {@link java.lang.UnsupportedOperationException} depending on the detector's underlying state.
     * This lead to confusing behavior as the underlying state of the detector can be modified
     * without the knowledge of the caller via system service layer updates.
     *
     * This change ID, when enabled, changes the API calls to only throw checked exception
     * {@link android.service.voice.HotwordDetector.IllegalDetectorStateException} when checking
     * against state information modified by both the caller and the system services.
     *
     * @hide
     */
    @ChangeId
    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
    long HOTWORD_DETECTOR_THROW_CHECKED_EXCEPTION = 226355112L;

    /**
     * Indicates that it is a non-trusted hotword detector.
     *
@@ -74,16 +95,26 @@ public interface HotwordDetector {
     * Calling this again while recognition is active does nothing.
     *
     * @return true if the request to start recognition succeeded
     * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33
     *         or above and attempts to start a recognition when the detector is not able based on
     *         the state. This can be thrown even if the state has been checked before calling this
     *         method because the caller receives updates via an asynchronous callback, and the
     *         state of the detector can change concurrently to the caller calling this method.
     */
    @RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD})
    boolean startRecognition();
    boolean startRecognition() throws IllegalDetectorStateException;

    /**
     * Stops hotword recognition.
     *
     * @return true if the request to stop recognition succeeded
     * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33
     *         or above and attempts to stop a recognition when the detector is not able based on
     *         the state. This can be thrown even if the state has been checked before calling this
     *         method because the caller receives updates via an asynchronous callback, and the
     *         state of the detector can change concurrently to the caller calling this method.
     */
    boolean stopRecognition();
    boolean stopRecognition() throws IllegalDetectorStateException;

    /**
     * Starts hotword recognition on audio coming from an external connected microphone.
@@ -97,26 +128,37 @@ public interface HotwordDetector {
     *         PersistableBundle does not allow any remotable objects or other contents that can be
     *         used to communicate with other processes.
     * @return true if the request to start recognition succeeded
     * @throws IllegalDetectorStateException Thrown when a caller has a target SDK of API level 33
     *         or above and attempts to start a recognition when the detector is not able based on
     *         the state. This can be thrown even if the state has been checked before calling this
     *         method because the caller receives updates via an asynchronous callback, and the
     *         state of the detector can change concurrently to the caller calling this method.
     */
    boolean startRecognition(
            @NonNull ParcelFileDescriptor audioStream,
            @NonNull AudioFormat audioFormat,
            @Nullable PersistableBundle options);
            @Nullable PersistableBundle options) throws IllegalDetectorStateException;

    /**
     * Set configuration and pass read-only data to hotword 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.
     *         {@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.
     *
     * @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
     *         calling this method because the caller receives updates via an asynchronous callback,
     *         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.
     */
    void updateState(@Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory);
    void updateState(@Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory)
            throws IllegalDetectorStateException;

    /**
     * Invalidates this hotword detector so that any future calls to this result
@@ -205,4 +247,14 @@ public interface HotwordDetector {
         */
        void onHotwordDetectionServiceRestarted();
    }

    /**
     * {@link HotwordDetector} specific exception thrown when the underlying state of the detector
     * is invalid for the given action.
     */
    class IllegalDetectorStateException extends AndroidException {
        IllegalDetectorStateException(String message) {
            super(message);
        }
    }
}
+2 −2
Original line number Diff line number Diff line
@@ -78,7 +78,7 @@ class SoftwareHotwordDetector extends AbstractHotwordDetector {

    @RequiresPermission(RECORD_AUDIO)
    @Override
    public boolean startRecognition() {
    public boolean startRecognition() throws IllegalDetectorStateException {
        if (DEBUG) {
            Slog.i(TAG, "#startRecognition");
        }
@@ -101,7 +101,7 @@ class SoftwareHotwordDetector extends AbstractHotwordDetector {
    /** TODO: stopRecognition */
    @RequiresPermission(RECORD_AUDIO)
    @Override
    public boolean stopRecognition() {
    public boolean stopRecognition() throws IllegalDetectorStateException {
        if (DEBUG) {
            Slog.i(TAG, "#stopRecognition");
        }
Loading