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

Commit 6b6d178b authored by Nicholas Ambur's avatar Nicholas Ambur
Browse files

expose HAL keyphrase trigger data to EventPayload

The SoundTrigger HAL can deliver additional context about the kephrase
that was triggered along with the trigger audio. This change moves to
expose the information which was previously suppressed by the Android
framework.

Data exposed:
- Raw data field

Previously raw data was only exposed when the data was of a specific
audio format. This change allows the raw data to be retrieved regardless
of format.

Test: atest AlwaysOnHotwordDetectorEventPayloadTest
CTS-Coverage-Bug: 215375531
Bug: 215066299
Change-Id: Ie75773853d3b338e092dfbf3bd6ccef61a1420c9
parent dc5dff86
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -11755,8 +11755,12 @@ package android.service.voice {
  public static class AlwaysOnHotwordDetector.EventPayload {
    method @Nullable public android.os.ParcelFileDescriptor getAudioStream();
    method @Nullable public android.media.AudioFormat getCaptureAudioFormat();
    method @Nullable public byte[] getData();
    method public int getDataFormat();
    method @Nullable public android.service.voice.HotwordDetectedResult getHotwordDetectedResult();
    method @Nullable public byte[] getTriggerAudio();
    method @Deprecated @Nullable public byte[] getTriggerAudio();
    field public static final int DATA_FORMAT_RAW = 0; // 0x0
    field public static final int DATA_FORMAT_TRIGGER_AUDIO = 1; // 0x1
  }
  public static final class AlwaysOnHotwordDetector.ModelParamRange {
+4 −0
Original line number Diff line number Diff line
@@ -2398,6 +2398,10 @@ package android.service.voice {
    method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public void triggerHardwareRecognitionEventForTest(int, int, boolean, int, int, int, boolean, @NonNull android.media.AudioFormat, @Nullable byte[]);
  }

  public static class AlwaysOnHotwordDetector.EventPayload {
    ctor public AlwaysOnHotwordDetector.EventPayload(boolean, boolean, @Nullable android.media.AudioFormat, int, @Nullable byte[], @Nullable android.service.voice.HotwordDetectedResult, @Nullable android.os.ParcelFileDescriptor);
  }

  public final class VisibleActivityInfo implements android.os.Parcelable {
    ctor public VisibleActivityInfo(int, @NonNull android.os.IBinder);
  }
+81 −10
Original line number Diff line number Diff line
@@ -336,7 +336,39 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector {
     * Additional payload for {@link Callback#onDetected}.
     */
    public static class EventPayload {
        private final boolean mTriggerAvailable;

        /**
         * Flags for describing the data format provided in the event payload.
         *
         * @hide
         */
        @Retention(RetentionPolicy.SOURCE)
        @IntDef(prefix = {"DATA_FORMAT_"}, value = {
                DATA_FORMAT_RAW,
                DATA_FORMAT_TRIGGER_AUDIO,
        })
        public @interface DataFormat {
        }

        /**
         * Data format is not strictly defined by the framework, and the
         * {@link android.hardware.soundtrigger.SoundTriggerModule} voice engine may populate this
         * field in any format.
         */
        public static final int DATA_FORMAT_RAW = 0;

        /**
         * Data format is defined as trigger audio.
         *
         * <p>When this format is used, {@link #getCaptureAudioFormat()} can be used to understand
         * further the audio format for reading the data.
         *
         * @see AlwaysOnHotwordDetector#RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO
         */
        public static final int DATA_FORMAT_TRIGGER_AUDIO = 1;

        @DataFormat
        private final int mDataFormat;
        // Indicates if {@code captureSession} can be used to continue capturing more audio
        // from the DSP hardware.
        private final boolean mCaptureAvailable;
@@ -349,16 +381,16 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector {
        private final HotwordDetectedResult mHotwordDetectedResult;
        private final ParcelFileDescriptor mAudioStream;

        EventPayload(boolean triggerAvailable, boolean captureAvailable,
        EventPayload(boolean triggerInData, boolean captureAvailable,
                AudioFormat audioFormat, int captureSession, byte[] data) {
            this(triggerAvailable, captureAvailable, audioFormat, captureSession, data, null,
            this(triggerInData, captureAvailable, audioFormat, captureSession, data, null,
                    null);
        }

        EventPayload(boolean triggerAvailable, boolean captureAvailable,
        EventPayload(boolean triggerInData, boolean captureAvailable,
                AudioFormat audioFormat, int captureSession, byte[] data,
                HotwordDetectedResult hotwordDetectedResult) {
            this(triggerAvailable, captureAvailable, audioFormat, captureSession, data,
            this(triggerInData, captureAvailable, audioFormat, captureSession, data,
                    hotwordDetectedResult, null);
        }

@@ -372,13 +404,20 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector {
            this(false, false, audioFormat, -1, null, hotwordDetectedResult, audioStream);
        }

        private EventPayload(boolean triggerAvailable, boolean captureAvailable,
                AudioFormat audioFormat, int captureSession, byte[] data,
                HotwordDetectedResult hotwordDetectedResult, ParcelFileDescriptor audioStream) {
            mTriggerAvailable = triggerAvailable;
        /** @hide */
        @TestApi
        public EventPayload(boolean triggerInData, boolean captureAvailable,
                @Nullable AudioFormat audioFormat, int captureSession, @Nullable byte[] data,
                @Nullable HotwordDetectedResult hotwordDetectedResult,
                @Nullable ParcelFileDescriptor audioStream) {
            mCaptureAvailable = captureAvailable;
            mCaptureSession = captureSession;
            mAudioFormat = audioFormat;
            if (triggerInData) {
                mDataFormat = DATA_FORMAT_TRIGGER_AUDIO;
            } else {
                mDataFormat = DATA_FORMAT_RAW;
            }
            mData = data;
            mHotwordDetectedResult = hotwordDetectedResult;
            mAudioStream = audioStream;
@@ -400,16 +439,48 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector {
         * {@link #getCaptureAudioFormat()}.
         *
         * @see AlwaysOnHotwordDetector#RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO
         * @deprecated Use {@link #getData()} instead.
         */
        @Deprecated
        @Nullable
        public byte[] getTriggerAudio() {
            if (mTriggerAvailable) {
            if (mDataFormat == DATA_FORMAT_TRIGGER_AUDIO) {
                return mData;
            } else {
                return null;
            }
        }

        /**
         * Conveys the format of the additional data that is triggered with the keyphrase event.
         *
         * @see AlwaysOnHotwordDetector#RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO
         * @see DataFormat
         */
        @DataFormat
        public int getDataFormat() {
            return mDataFormat;
        }

        /**
         * Gets additional raw data that is triggered with the keyphrase event.
         *
         * <p>A {@link android.hardware.soundtrigger.SoundTriggerModule} may populate this
         * field with opaque data for use by system applications who know about voice
         * engine internals. Data may be null if the field is not populated by the
         * {@link android.hardware.soundtrigger.SoundTriggerModule}.
         *
         * <p>If {@link #getDataFormat()} is {@link #DATA_FORMAT_TRIGGER_AUDIO}, then the
         * entirety of this buffer is expected to be of the format from
         * {@link #getCaptureAudioFormat()}.
         *
         * @see AlwaysOnHotwordDetector#RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO
         */
        @Nullable
        public byte[] getData() {
            return mData;
        }

        /**
         * Gets the session ID to start a capture from the DSP.
         * This may be null if streaming capture isn't possible.