From d20f8d5095aa87c8ea4acdcfae1fe1d004c1ac6f Mon Sep 17 00:00:00 2001 From: Atneya Nair Date: Thu, 27 Apr 2023 17:21:52 -0700 Subject: [PATCH] Cache dropped callbacks in STInstrumentation Sessions can receive callbacks before the client attaches their callback. To avoid flake, if there is no callback registered, save the messages in order, and dispatch them when the callback is registered. Bug: 279774505 Test: AlwaysOnHotwordDetectorTest --iterations 20 Test: SoundTriggerManagerTest --iterations 20 Change-Id: I2dc96e0a3d52149eb0bad75ffd45869b16460057 --- .../SoundTriggerInstrumentation.java | 33 ++++++++++++++++--- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/media/java/android/media/soundtrigger/SoundTriggerInstrumentation.java b/media/java/android/media/soundtrigger/SoundTriggerInstrumentation.java index 80bc5c07dd667..3dfc58788e8a5 100644 --- a/media/java/android/media/soundtrigger/SoundTriggerInstrumentation.java +++ b/media/java/android/media/soundtrigger/SoundTriggerInstrumentation.java @@ -231,9 +231,17 @@ public final class SoundTriggerInstrumentation { */ public void setModelCallback(@NonNull @CallbackExecutor Executor executor, @NonNull ModelCallback callback) { + Objects.requireNonNull(callback); + Objects.requireNonNull(executor); synchronized (SoundTriggerInstrumentation.this.mLock) { - mModelCallback = Objects.requireNonNull(callback); - mModelExecutor = Objects.requireNonNull(executor); + if (mModelCallback == null) { + for (var droppedConsumer : mDroppedConsumerList) { + executor.execute(() -> droppedConsumer.accept(callback)); + } + mDroppedConsumerList.clear(); + } + mModelCallback = callback; + mModelExecutor = executor; } } @@ -267,9 +275,11 @@ public final class SoundTriggerInstrumentation { private void wrap(Consumer consumer) { synchronized (SoundTriggerInstrumentation.this.mLock) { - if (mModelCallback != null && mModelExecutor != null) { + if (mModelCallback != null) { final ModelCallback callback = mModelCallback; mModelExecutor.execute(() -> consumer.accept(callback)); + } else { + mDroppedConsumerList.add(consumer); } } } @@ -282,6 +292,8 @@ public final class SoundTriggerInstrumentation { private ModelCallback mModelCallback = null; @GuardedBy("SoundTriggerInstrumentation.this.mLock") private Executor mModelExecutor = null; + @GuardedBy("SoundTriggerInstrumentation.this.mLock") + private final List> mDroppedConsumerList = new ArrayList<>(); } /** @@ -374,9 +386,18 @@ public final class SoundTriggerInstrumentation { */ public void setRecognitionCallback(@NonNull @CallbackExecutor Executor executor, @NonNull RecognitionCallback callback) { + Objects.requireNonNull(callback); + Objects.requireNonNull(executor); synchronized (SoundTriggerInstrumentation.this.mLock) { + if (mRecognitionCallback == null) { + for (var droppedConsumer : mDroppedConsumerList) { + executor.execute(() -> droppedConsumer.accept(callback)); + } + mDroppedConsumerList.clear(); + } mRecognitionCallback = callback; mRecognitionExecutor = executor; + } } @@ -401,9 +422,11 @@ public final class SoundTriggerInstrumentation { private void wrap(Consumer consumer) { synchronized (SoundTriggerInstrumentation.this.mLock) { - if (mRecognitionCallback != null && mRecognitionExecutor != null) { + if (mRecognitionCallback != null) { final RecognitionCallback callback = mRecognitionCallback; mRecognitionExecutor.execute(() -> consumer.accept(callback)); + } else { + mDroppedConsumerList.add(consumer); } } } @@ -416,6 +439,8 @@ public final class SoundTriggerInstrumentation { private Executor mRecognitionExecutor = null; @GuardedBy("SoundTriggerInstrumentation.this.mLock") private RecognitionCallback mRecognitionCallback = null; + @GuardedBy("SoundTriggerInstrumentation.this.mLock") + private final List> mDroppedConsumerList = new ArrayList<>(); } // Implementation of injection interface passed to the HAL.