From 3467c244966a4ec57de28b39bf50b50de7f74d59 Mon Sep 17 00:00:00 2001 From: Nicholas Ambur Date: Tue, 14 Feb 2023 09:27:33 +0000 Subject: [PATCH] AOHD.EventPayload.getHalEventTimestampMillis API AlwaysOnHotwordDetector.EventPayload API for exposing the timestamp which represents the time the SoundTrigger HAL emitted a RecognitionEvent Test: atest CtsVoiceInteractionHostTestCases Bug: 265852186 (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:51d3a40d4bee1fa4f8f44ec4e59380911ffa0684) Merged-In: I0f8d37e19002910bbfbf3bfd77cdecbf7c588e81 Change-Id: I0f8d37e19002910bbfbf3bfd77cdecbf7c588e81 --- core/api/system-current.txt | 1 + core/api/test-current.txt | 3 +- .../voice/AlwaysOnHotwordDetector.java | 47 +++++++++++++++++-- 3 files changed, 46 insertions(+), 5 deletions(-) diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 5a8ee9456f878..99688c78d7ec9 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -13042,6 +13042,7 @@ package android.service.voice { method @Nullable public android.media.AudioFormat getCaptureAudioFormat(); method @Nullable public byte[] getData(); method public int getDataFormat(); + method public long getHalEventReceivedMillis(); method @Nullable public android.service.voice.HotwordDetectedResult getHotwordDetectedResult(); method @NonNull public java.util.List getKeyphraseRecognitionExtras(); method @Deprecated @Nullable public byte[] getTriggerAudio(); diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 5a84b9782ee63..ceb883b0340cb 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -2875,7 +2875,7 @@ package android.service.voice { public class AlwaysOnHotwordDetector implements android.service.voice.HotwordDetector { method public void overrideAvailability(int); method public void resetAvailability(); - 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[], @NonNull java.util.List); + method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public void triggerHardwareRecognitionEventForTest(int, int, long, boolean, int, int, int, boolean, @NonNull android.media.AudioFormat, @Nullable byte[], @NonNull java.util.List); } public static final class AlwaysOnHotwordDetector.EventPayload.Builder { @@ -2887,6 +2887,7 @@ package android.service.voice { method @NonNull public android.service.voice.AlwaysOnHotwordDetector.EventPayload.Builder setCaptureSession(int); method @NonNull public android.service.voice.AlwaysOnHotwordDetector.EventPayload.Builder setData(@NonNull byte[]); method @NonNull public android.service.voice.AlwaysOnHotwordDetector.EventPayload.Builder setDataFormat(int); + method @NonNull public android.service.voice.AlwaysOnHotwordDetector.EventPayload.Builder setHalEventReceivedMillis(long); method @NonNull public android.service.voice.AlwaysOnHotwordDetector.EventPayload.Builder setHotwordDetectedResult(@NonNull android.service.voice.HotwordDetectedResult); method @NonNull public android.service.voice.AlwaysOnHotwordDetector.EventPayload.Builder setKeyphraseRecognitionExtras(@NonNull java.util.List); } diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java index ffa15f065a027..65f32e2cf4428 100644 --- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java +++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java @@ -20,6 +20,7 @@ import static android.Manifest.permission.CAPTURE_AUDIO_HOTWORD; import static android.Manifest.permission.RECORD_AUDIO; import static android.service.voice.VoiceInteractionService.MULTIPLE_ACTIVE_HOTWORD_DETECTORS; +import android.annotation.ElapsedRealtimeLong; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -54,6 +55,7 @@ import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; import android.os.RemoteException; import android.os.SharedMemory; +import android.os.SystemClock; import android.text.TextUtils; import android.util.Log; import android.util.Slog; @@ -401,6 +403,9 @@ public class AlwaysOnHotwordDetector extends AbstractDetector { private final ParcelFileDescriptor mAudioStream; private final List mKephraseExtras; + @ElapsedRealtimeLong + private final long mHalEventReceivedMillis; + private EventPayload(boolean captureAvailable, @Nullable AudioFormat audioFormat, int captureSession, @@ -408,7 +413,8 @@ public class AlwaysOnHotwordDetector extends AbstractDetector { @Nullable byte[] data, @Nullable HotwordDetectedResult hotwordDetectedResult, @Nullable ParcelFileDescriptor audioStream, - @NonNull List keyphraseExtras) { + @NonNull List keyphraseExtras, + @ElapsedRealtimeLong long halEventReceivedMillis) { mCaptureAvailable = captureAvailable; mCaptureSession = captureSession; mAudioFormat = audioFormat; @@ -417,6 +423,7 @@ public class AlwaysOnHotwordDetector extends AbstractDetector { mHotwordDetectedResult = hotwordDetectedResult; mAudioStream = audioStream; mKephraseExtras = keyphraseExtras; + mHalEventReceivedMillis = halEventReceivedMillis; } /** @@ -545,6 +552,21 @@ public class AlwaysOnHotwordDetector extends AbstractDetector { return mKephraseExtras; } + /** + * Timestamp of when the trigger event from SoundTriggerHal was received by the system + * server. + * + * 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 mHalEventReceivedMillis; + } + /** * Builder class for {@link EventPayload} objects * @@ -561,6 +583,8 @@ public class AlwaysOnHotwordDetector extends AbstractDetector { private HotwordDetectedResult mHotwordDetectedResult = null; private ParcelFileDescriptor mAudioStream = null; private List mKeyphraseExtras = Collections.emptyList(); + @ElapsedRealtimeLong + private long mHalEventReceivedMillis = -1; public Builder() {} @@ -681,6 +705,20 @@ public class AlwaysOnHotwordDetector extends AbstractDetector { return this; } + /** + * 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()}. + */ + @NonNull + public Builder setHalEventReceivedMillis( + @ElapsedRealtimeLong long halEventReceivedMillis) { + mHalEventReceivedMillis = halEventReceivedMillis; + return this; + } + /** * Builds an {@link EventPayload} instance */ @@ -688,7 +726,7 @@ public class AlwaysOnHotwordDetector extends AbstractDetector { public EventPayload build() { return new EventPayload(mCaptureAvailable, mAudioFormat, mCaptureSession, mDataFormat, mData, mHotwordDetectedResult, mAudioStream, - mKeyphraseExtras); + mKeyphraseExtras, mHalEventReceivedMillis); } } } @@ -897,8 +935,9 @@ public class AlwaysOnHotwordDetector extends AbstractDetector { @TestApi @RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD}) public void triggerHardwareRecognitionEventForTest(int status, int soundModelHandle, - boolean captureAvailable, int captureSession, int captureDelayMs, int capturePreambleMs, - boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data, + @ElapsedRealtimeLong long halEventReceivedMillis, boolean captureAvailable, + int captureSession, int captureDelayMs, int capturePreambleMs, boolean triggerInData, + @NonNull AudioFormat captureFormat, @Nullable byte[] data, @NonNull List keyphraseRecognitionExtras) { Log.d(TAG, "triggerHardwareRecognitionEventForTest()"); synchronized (mLock) {