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

Commit 471556d1 authored by Nicholas Ambur's avatar Nicholas Ambur Committed by Cherrypicker Worker
Browse files

RecognitionEvent.getHalEventTimestampMillis API

RecognitionEvent API for exposing the timestamp
which represents the time the SoundTrigger HAL emitted a
RecognitionEvent

Test: atest CtsVoiceInteractionHostTestCases
Test: atest SoundTriggerTest
Bug: 265852186
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:1d92efdd4af397d547b595ac9cd395c4df1e0156)
Merged-In: I9f2dff736cf29661046a7995b58dd03a106efb16
Change-Id: I9f2dff736cf29661046a7995b58dd03a106efb16
parent 3467c244
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -5967,6 +5967,7 @@ package android.hardware.soundtrigger {
    method @Nullable public android.media.AudioFormat getCaptureFormat();
    method public int getCaptureSession();
    method public byte[] getData();
    method public long getHalEventReceivedMillis();
    method public boolean isCaptureAvailable();
  }
+1 −1
Original line number Diff line number Diff line
@@ -1639,7 +1639,7 @@ package android.hardware.soundtrigger {
  }

  public static class SoundTrigger.RecognitionEvent {
    ctor public SoundTrigger.RecognitionEvent(int, int, boolean, int, int, int, boolean, @NonNull android.media.AudioFormat, @Nullable byte[]);
    ctor public SoundTrigger.RecognitionEvent(int, int, boolean, int, int, int, boolean, @NonNull android.media.AudioFormat, @Nullable byte[], long);
  }

}
+13 −14
Original line number Diff line number Diff line
@@ -223,13 +223,13 @@ public class ConversionUtil {
            int modelHandle, int captureSession, RecognitionEvent aidlEvent) {
        // The API recognition event doesn't allow for a null audio format, even though it doesn't
        // always make sense. We thus replace it with a default.
        AudioFormat audioFormat = aidl2apiAudioFormatWithDefault(
                aidlEvent.audioConfig, true /*isInput*/);
        return new SoundTrigger.GenericRecognitionEvent(
                aidlEvent.status,
                modelHandle, aidlEvent.captureAvailable, captureSession,
                aidlEvent.captureDelayMs, aidlEvent.capturePreambleMs, aidlEvent.triggerInData,
                audioFormat, aidlEvent.data, aidlEvent.recognitionStillActive);
        AudioFormat audioFormat = aidl2apiAudioFormatWithDefault(aidlEvent.audioConfig,
                true /*isInput*/);
        // TODO(b/265852186) propagate a timestamp from aidl interfaces
        return new SoundTrigger.GenericRecognitionEvent(aidlEvent.status, modelHandle,
                aidlEvent.captureAvailable, captureSession, aidlEvent.captureDelayMs,
                aidlEvent.capturePreambleMs, aidlEvent.triggerInData, audioFormat, aidlEvent.data,
                aidlEvent.recognitionStillActive, -1 /* halEventReceivedMillis */);
    }

    public static SoundTrigger.RecognitionEvent aidl2apiPhraseRecognitionEvent(
@@ -242,14 +242,13 @@ public class ConversionUtil {
        }
        // The API recognition event doesn't allow for a null audio format, even though it doesn't
        // always make sense. We thus replace it with a default.
        AudioFormat audioFormat = aidl2apiAudioFormatWithDefault(
                aidlEvent.common.audioConfig, true /*isInput*/);
        AudioFormat audioFormat = aidl2apiAudioFormatWithDefault(aidlEvent.common.audioConfig,
                true /*isInput*/);
        // TODO(b/265852186) propagate a timestamp from aidl interfaces
        return new SoundTrigger.KeyphraseRecognitionEvent(aidlEvent.common.status, modelHandle,
                aidlEvent.common.captureAvailable,
                captureSession, aidlEvent.common.captureDelayMs,
                aidlEvent.common.capturePreambleMs, aidlEvent.common.triggerInData,
                audioFormat, aidlEvent.common.data,
                apiExtras);
                aidlEvent.common.captureAvailable, captureSession, aidlEvent.common.captureDelayMs,
                aidlEvent.common.capturePreambleMs, aidlEvent.common.triggerInData, audioFormat,
                aidlEvent.common.data, apiExtras, -1 /* halEventReceivedMillis */);
    }

    // In case of a null input returns a non-null valid output.
+82 −35
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import static android.system.OsConstants.EPIPE;

import static java.util.Objects.requireNonNull;

import android.annotation.ElapsedRealtimeLong;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -36,17 +37,13 @@ import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.app.ActivityThread;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.media.AudioFormat;
import android.media.permission.ClearCallingIdentityContext;
import android.media.permission.Identity;
import android.media.permission.SafeCloseable;
import android.media.soundtrigger.Status;
import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
@@ -56,6 +53,7 @@ import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
import android.os.SystemClock;
import android.util.Log;

import java.lang.annotation.Retention;
@@ -1187,23 +1185,38 @@ public class SoundTrigger {
         * @hide
         */
        public final boolean recognitionStillActive;
        /**
         * Timestamp of when the trigger event from SoundTriggerHal was received by the
         * framework.
         *
         * <p>Clock monotonic including suspend time or its equivalent on the system,
         * in the same units and timebase as {@link SystemClock#elapsedRealtime()}.
         *
         * <p>Value represents elapsed realtime in milliseconds when the event was received from the
         * HAL. The value will be -1 if the event was not generated from the HAL.
         *
         * @hide
         */
        @ElapsedRealtimeLong
        public final long halEventReceivedMillis;

        /** @hide */
        @TestApi
        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
        public RecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
                int captureSession, int captureDelayMs, int capturePreambleMs,
                boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data) {
            this(status, soundModelHandle, captureAvailable, captureSession, captureDelayMs,
                    capturePreambleMs, triggerInData, captureFormat, data,
                    status == RECOGNITION_STATUS_GET_STATE_RESPONSE);
                boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data,
                @ElapsedRealtimeLong long halEventReceivedMillis) {
            this(status, soundModelHandle, captureAvailable,
                    captureSession, captureDelayMs, capturePreambleMs, triggerInData, captureFormat,
                    data, status == RECOGNITION_STATUS_GET_STATE_RESPONSE, halEventReceivedMillis);
        }

        /** @hide */
        public RecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
                int captureSession, int captureDelayMs, int capturePreambleMs,
                boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data,
                boolean recognitionStillActive) {
                boolean recognitionStillActive, @ElapsedRealtimeLong long halEventReceivedMillis) {
            this.status = status;
            this.soundModelHandle = soundModelHandle;
            this.captureAvailable = captureAvailable;
@@ -1214,6 +1227,7 @@ public class SoundTrigger {
            this.captureFormat = requireNonNull(captureFormat);
            this.data = data != null ? data : new byte[0];
            this.recognitionStillActive = recognitionStillActive;
            this.halEventReceivedMillis = halEventReceivedMillis;
        }

        /**
@@ -1256,6 +1270,21 @@ public class SoundTrigger {
            return data;
        }

        /**
         * Timestamp of when the trigger event from SoundTriggerHal was received by the
         * framework.
         *
         * Clock monotonic including suspend time or its equivalent on the system,
         * in the same units and timebase as {@link SystemClock#elapsedRealtime()}.
         *
         * @return Elapsed realtime in milliseconds when the event was received from the HAL.
         *      Returns -1 if the event was not generated from the HAL.
         */
        @ElapsedRealtimeLong
        public long getHalEventReceivedMillis() {
            return halEventReceivedMillis;
        }

        /** @hide */
        public static final @android.annotation.NonNull Parcelable.Creator<RecognitionEvent> CREATOR
                = new Parcelable.Creator<RecognitionEvent>() {
@@ -1290,9 +1319,10 @@ public class SoundTrigger {
            }
            byte[] data = in.readBlob();
            boolean recognitionStillActive = in.readBoolean();
            long halEventReceivedMillis = in.readLong();
            return new RecognitionEvent(status, soundModelHandle, captureAvailable, captureSession,
                    captureDelayMs, capturePreambleMs, triggerInData, captureFormat, data,
                    recognitionStillActive);
                    recognitionStillActive, halEventReceivedMillis);
        }

        /** @hide */
@@ -1319,6 +1349,7 @@ public class SoundTrigger {
            }
            dest.writeBlob(data);
            dest.writeBoolean(recognitionStillActive);
            dest.writeLong(halEventReceivedMillis);
        }
        @Override
        public int hashCode() {
@@ -1338,6 +1369,7 @@ public class SoundTrigger {
            result = prime * result + soundModelHandle;
            result = prime * result + status;
            result = result + (recognitionStillActive ? 1289 : 1291);
            result = prime * result + Long.hashCode(halEventReceivedMillis);
            return result;
        }

@@ -1364,6 +1396,9 @@ public class SoundTrigger {
                return false;
            if (soundModelHandle != other.soundModelHandle)
                return false;
            if (halEventReceivedMillis != other.halEventReceivedMillis) {
                return false;
            }
            if (status != other.status)
                return false;
            if (triggerInData != other.triggerInData)
@@ -1400,6 +1435,7 @@ public class SoundTrigger {
                        (", channelMask=" + captureFormat.getChannelMask()))
                    + ", data=" + (data == null ? 0 : data.length)
                    + ", recognitionStillActive=" + recognitionStillActive
                    + ", halEventReceivedMillis=" + halEventReceivedMillis
                    + "]";
        }
    }
@@ -1820,19 +1856,22 @@ public class SoundTrigger {
        public KeyphraseRecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
                int captureSession, int captureDelayMs, int capturePreambleMs,
                boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data,
                @Nullable KeyphraseRecognitionExtra[] keyphraseExtras) {
                @Nullable KeyphraseRecognitionExtra[] keyphraseExtras,
                @ElapsedRealtimeLong long halEventReceivedMillis) {
            this(status, soundModelHandle, captureAvailable, captureSession, captureDelayMs,
                    capturePreambleMs, triggerInData, captureFormat, data, keyphraseExtras,
                    status == RECOGNITION_STATUS_GET_STATE_RESPONSE);
                    status == RECOGNITION_STATUS_GET_STATE_RESPONSE, halEventReceivedMillis);
        }

        public KeyphraseRecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
        public KeyphraseRecognitionEvent(int status, int soundModelHandle,
                boolean captureAvailable,
                int captureSession, int captureDelayMs, int capturePreambleMs,
                boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data,
                @Nullable KeyphraseRecognitionExtra[] keyphraseExtras,
                boolean recognitionStillActive) {
            super(status, soundModelHandle, captureAvailable, captureSession, captureDelayMs,
                    capturePreambleMs, triggerInData, captureFormat, data, recognitionStillActive);
                boolean recognitionStillActive, @ElapsedRealtimeLong long halEventReceivedMillis) {
            super(status, soundModelHandle, captureAvailable,
                    captureSession, captureDelayMs, capturePreambleMs, triggerInData, captureFormat,
                    data, recognitionStillActive, halEventReceivedMillis);
            this.keyphraseExtras =
                    keyphraseExtras != null ? keyphraseExtras : new KeyphraseRecognitionExtra[0];
        }
@@ -1869,11 +1908,13 @@ public class SoundTrigger {
            }
            byte[] data = in.readBlob();
            boolean recognitionStillActive = in.readBoolean();
            long halEventReceivedMillis = in.readLong();
            KeyphraseRecognitionExtra[] keyphraseExtras =
                    in.createTypedArray(KeyphraseRecognitionExtra.CREATOR);
            return new KeyphraseRecognitionEvent(status, soundModelHandle, captureAvailable,
                    captureSession, captureDelayMs, capturePreambleMs, triggerInData,
                    captureFormat, data, keyphraseExtras, recognitionStillActive);
            return new KeyphraseRecognitionEvent(status, soundModelHandle,
                    captureAvailable, captureSession, captureDelayMs, capturePreambleMs,
                    triggerInData, captureFormat, data, keyphraseExtras, recognitionStillActive,
                    halEventReceivedMillis);
        }

        @Override
@@ -1895,6 +1936,7 @@ public class SoundTrigger {
            }
            dest.writeBlob(data);
            dest.writeBoolean(recognitionStillActive);
            dest.writeLong(halEventReceivedMillis);
            dest.writeTypedArray(keyphraseExtras, flags);
        }

@@ -1929,9 +1971,11 @@ public class SoundTrigger {
        public String toString() {
            return "KeyphraseRecognitionEvent [keyphraseExtras=" + Arrays.toString(keyphraseExtras)
                    + ", status=" + status
                    + ", soundModelHandle=" + soundModelHandle + ", captureAvailable="
                    + captureAvailable + ", captureSession=" + captureSession + ", captureDelayMs="
                    + captureDelayMs + ", capturePreambleMs=" + capturePreambleMs
                    + ", soundModelHandle=" + soundModelHandle
                    + ", captureAvailable=" + captureAvailable
                    + ", captureSession=" + captureSession
                    + ", captureDelayMs=" + captureDelayMs
                    + ", capturePreambleMs=" + capturePreambleMs
                    + ", triggerInData=" + triggerInData
                    + ((captureFormat == null) ? "" :
                        (", sampleRate=" + captureFormat.getSampleRate()))
@@ -1941,6 +1985,7 @@ public class SoundTrigger {
                        (", channelMask=" + captureFormat.getChannelMask()))
                    + ", data=" + (data == null ? 0 : data.length)
                    + ", recognitionStillActive=" + recognitionStillActive
                    + ", halEventReceivedMillis=" + halEventReceivedMillis
                    + "]";
        }
    }
@@ -1953,21 +1998,23 @@ public class SoundTrigger {
     */
    public static class GenericRecognitionEvent extends RecognitionEvent implements Parcelable {
        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
        public GenericRecognitionEvent(int status, int soundModelHandle,
                boolean captureAvailable, int captureSession, int captureDelayMs,
                int capturePreambleMs, boolean triggerInData, @NonNull AudioFormat captureFormat,
                @Nullable byte[] data) {
            this(status, soundModelHandle, captureAvailable, captureSession, captureDelayMs,
        public GenericRecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
                int captureSession, int captureDelayMs, int capturePreambleMs,
                boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data,
                @ElapsedRealtimeLong long halEventReceivedMillis) {
            this(status, soundModelHandle, captureAvailable,
                    captureSession, captureDelayMs,
                    capturePreambleMs, triggerInData, captureFormat, data,
                    status == RECOGNITION_STATUS_GET_STATE_RESPONSE);
                    status == RECOGNITION_STATUS_GET_STATE_RESPONSE, halEventReceivedMillis);
        }

        public GenericRecognitionEvent(int status, int soundModelHandle,
                boolean captureAvailable, int captureSession, int captureDelayMs,
                int capturePreambleMs, boolean triggerInData, @NonNull AudioFormat captureFormat,
                @Nullable byte[] data, boolean recognitionStillActive) {
            super(status, soundModelHandle, captureAvailable, captureSession, captureDelayMs,
                    capturePreambleMs, triggerInData, captureFormat, data, recognitionStillActive);
        public GenericRecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
                int captureSession, int captureDelayMs, int capturePreambleMs,
                boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data,
                boolean recognitionStillActive, @ElapsedRealtimeLong long halEventReceivedMillis) {
            super(status, soundModelHandle, captureAvailable,
                    captureSession, captureDelayMs, capturePreambleMs, triggerInData, captureFormat,
                    data, recognitionStillActive, halEventReceivedMillis);
        }

        public static final @android.annotation.NonNull Parcelable.Creator<GenericRecognitionEvent> CREATOR
@@ -1986,7 +2033,7 @@ public class SoundTrigger {
            return new GenericRecognitionEvent(event.status, event.soundModelHandle,
                    event.captureAvailable, event.captureSession, event.captureDelayMs,
                    event.capturePreambleMs, event.triggerInData, event.captureFormat, event.data,
                    event.recognitionStillActive);
                    event.recognitionStillActive, event.halEventReceivedMillis);
        }

        @Override
+2 −1
Original line number Diff line number Diff line
@@ -603,6 +603,7 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
                    setKeyphraseRecognitionExtras(
                            Arrays.asList(keyphraseRecognitionEvent.keyphraseExtras));
                }
                setHalEventReceivedMillis(keyphraseRecognitionEvent.getHalEventReceivedMillis());
            }

            /**
@@ -950,7 +951,7 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
                        new KeyphraseRecognitionEvent(status, soundModelHandle, captureAvailable,
                                captureSession, captureDelayMs, capturePreambleMs, triggerInData,
                                captureFormat, data, keyphraseRecognitionExtras.toArray(
                                new KeyphraseRecognitionExtra[0])),
                                new KeyphraseRecognitionExtra[0]), halEventReceivedMillis),
                        mInternalCallback);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
Loading