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) {