Add test case to test the onDetect function of HotwordDetectionService

Bug: 184685043
Bug: 183239093
Test: atest CtsVoiceInteractionTestCases
Test: atest CtsVoiceInteractionTestCases --instant
Change-Id: Id1e8695d44d7f8d5e6c6a2429987924a93b362e4
diff --git a/tests/tests/voiceinteraction/common/src/android/voiceinteraction/common/Utils.java b/tests/tests/voiceinteraction/common/src/android/voiceinteraction/common/Utils.java
index dd60884..4d8a54c 100644
--- a/tests/tests/voiceinteraction/common/src/android/voiceinteraction/common/Utils.java
+++ b/tests/tests/voiceinteraction/common/src/android/voiceinteraction/common/Utils.java
@@ -56,12 +56,15 @@
     public static final int VOICE_INTERACTION_SERVICE_NORMAL_TEST = 0;
     public static final int HOTWORD_DETECTION_SERVICE_TRIGGER_TEST = 1;
     public static final int HOTWORD_DETECTION_SERVICE_TRIGGER_WITHOUT_PERMISSION_TEST = 2;
+    public static final int HOTWORD_DETECTION_SERVICE_DSP_ONDETECT_TEST = 3;
 
     public static final int HOTWORD_DETECTION_SERVICE_TRIGGER_SUCCESS = 1;
     public static final int HOTWORD_DETECTION_SERVICE_TRIGGER_ILLEGAL_STATE_EXCEPTION = 2;
     public static final int HOTWORD_DETECTION_SERVICE_TRIGGER_SECURITY_EXCEPTION = 3;
     public static final int HOTWORD_DETECTION_SERVICE_TRIGGER_SHARED_MEMORY_NOT_READ_ONLY = 4;
 
+    public static final int HOTWORD_DETECTION_SERVICE_ONDETECT_SUCCESS = 1;
+
     public static final String TESTCASE_TYPE = "testcase_type";
     public static final String TESTINFO = "testinfo";
     public static final String BROADCAST_INTENT = "android.intent.action.VOICE_TESTAPP";
@@ -136,6 +139,8 @@
 
     public static final String BROADCAST_HOTWORD_DETECTION_SERVICE_TRIGGER_RESULT_INTENT =
             "android.intent.action.HOTWORD_DETECTION_SERVICE_TRIGGER_RESULT";
+    public static final String BROADCAST_HOTWORD_DETECTION_SERVICE_DSP_ONDETECT_RESULT_INTENT =
+            "android.intent.action.HOTWORD_DETECTION_SERVICE_DSP_ONDETECT_RESULT";
     public static final String KEY_SERVICE_TYPE = "serviceType";
     public static final String KEY_TEST_EVENT = "testEvent";
     public static final String KEY_TEST_RESULT = "testResult";
diff --git a/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/BasicVoiceInteractionService.java b/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/BasicVoiceInteractionService.java
index 2af7e13..5fb5e11 100644
--- a/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/BasicVoiceInteractionService.java
+++ b/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/BasicVoiceInteractionService.java
@@ -19,6 +19,7 @@
 import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
 
 import android.content.Intent;
+import android.media.AudioFormat;
 import android.os.PersistableBundle;
 import android.os.SharedMemory;
 import android.service.voice.AlwaysOnHotwordDetector;
@@ -30,6 +31,7 @@
 
 import java.nio.ByteBuffer;
 import java.util.Locale;
+import java.util.Objects;
 
 /**
  * This service included a basic HotwordDetectionService for testing.
@@ -43,6 +45,7 @@
     public static byte[] FAKE_BYTE_ARRAY_DATA = new byte[] {1, 2, 3};
 
     private boolean mReady = false;
+    private AlwaysOnHotwordDetector mAlwaysOnHotwordDetector = null;
 
     @Override
     public void onReady() {
@@ -61,21 +64,32 @@
         }
 
         final int testEvent = intent.getIntExtra(Utils.KEY_TEST_EVENT, -1);
+        Log.i(TAG, "testEvent = " + testEvent);
         if (testEvent == Utils.HOTWORD_DETECTION_SERVICE_TRIGGER_TEST) {
             runWithShellPermissionIdentity(() -> {
-                callCreateAlwaysOnHotwordDetector();
+                mAlwaysOnHotwordDetector = callCreateAlwaysOnHotwordDetector();
             });
         } else if (testEvent == Utils.HOTWORD_DETECTION_SERVICE_TRIGGER_WITHOUT_PERMISSION_TEST) {
             callCreateAlwaysOnHotwordDetector();
+        } else if (testEvent == Utils.HOTWORD_DETECTION_SERVICE_DSP_ONDETECT_TEST) {
+            runWithShellPermissionIdentity(() -> {
+                if (mAlwaysOnHotwordDetector != null) {
+                    mAlwaysOnHotwordDetector.triggerHardwareRecognitionEventForTest(/* status */ 0,
+                            /* soundModelHandle */ 100, /* captureAvailable */ true,
+                            /* captureSession */ 101, /* captureDelayMs */ 1000,
+                            /* capturePreambleMs */ 1001, /* triggerInData */ true,
+                            createFakeAudioFormat(), new byte[1024]);
+                }
+            });
         }
 
         return START_NOT_STICKY;
     }
 
-    private void callCreateAlwaysOnHotwordDetector() {
+    private AlwaysOnHotwordDetector callCreateAlwaysOnHotwordDetector() {
         Log.i(TAG, "callCreateAlwaysOnHotwordDetector()");
         try {
-            createAlwaysOnHotwordDetector(/* keyphrase */ "Hello Google",
+            return createAlwaysOnHotwordDetector(/* keyphrase */ "Hello Google",
                     Locale.forLanguageTag("en-US"),
                     createFakePersistableBundleData(),
                     createFakeSharedMemoryData(),
@@ -88,6 +102,7 @@
                         @Override
                         public void onDetected(AlwaysOnHotwordDetector.EventPayload eventPayload) {
                             Log.i(TAG, "onDetected");
+                            broadcastOnDetectedEvent();
                         }
 
                         @Override
@@ -121,6 +136,7 @@
                     Utils.BROADCAST_HOTWORD_DETECTION_SERVICE_TRIGGER_RESULT_INTENT,
                     Utils.HOTWORD_DETECTION_SERVICE_TRIGGER_SECURITY_EXCEPTION);
         }
+        return null;
     }
 
     private void broadcastIntentWithResult(String intentName, int result) {
@@ -150,6 +166,13 @@
         return persistableBundle;
     }
 
+    private AudioFormat createFakeAudioFormat() {
+        return new AudioFormat.Builder()
+                .setSampleRate(32000)
+                .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
+                .setChannelMask(AudioFormat.CHANNEL_IN_MONO).build();
+    }
+
     private void verifyHotwordDetectionServiceInitializedStatus(int status) {
         if (status == HotwordDetectionService.INITIALIZATION_STATUS_SUCCESS) {
             broadcastIntentWithResult(
@@ -157,4 +180,10 @@
                     Utils.HOTWORD_DETECTION_SERVICE_TRIGGER_SUCCESS);
         }
     }
+
+    private void broadcastOnDetectedEvent() {
+        broadcastIntentWithResult(
+                Utils.BROADCAST_HOTWORD_DETECTION_SERVICE_DSP_ONDETECT_RESULT_INTENT,
+                Utils.HOTWORD_DETECTION_SERVICE_ONDETECT_SUCCESS);
+    }
 }
diff --git a/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainHotwordDetectionService.java b/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainHotwordDetectionService.java
index e37ec08..8f2535f 100644
--- a/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainHotwordDetectionService.java
+++ b/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainHotwordDetectionService.java
@@ -28,6 +28,8 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import java.io.IOException;
+import java.io.InputStream;
 import java.util.function.IntConsumer;
 
 public class MainHotwordDetectionService extends HotwordDetectionService {
@@ -44,7 +46,39 @@
             Log.w(TAG, "callback is null");
             return;
         }
-        callback.onDetected(null);
+        if (audioStream == null) {
+            Log.w(TAG, "audioStream is null");
+            return;
+        }
+
+        long startTime = System.currentTimeMillis();
+        try (InputStream fis =
+                     new ParcelFileDescriptor.AutoCloseInputStream(audioStream)) {
+
+            // We added the fake audio data and set "hotword!" string at the head. Then we simulated
+            // to verify the audio data with "hotword!" in HotwordDetectionService. If the audio
+            // data includes "hotword!", it means that the hotword is valid.
+            while (fis.available() < 8) {
+                try {
+                    Thread.sleep(10);
+                } catch (InterruptedException e) {
+                    // Nothing
+                }
+                if (System.currentTimeMillis() - startTime > timeoutMillis) {
+                    Log.w(TAG, "Over timeout");
+                    return;
+                }
+            }
+            Log.d(TAG, "fis.available() = " + fis.available());
+            byte[] buffer = new byte[8];
+            fis.read(buffer, 0, 8);
+            if(isSame(buffer, new byte[] {'h', 'o', 't', 'w', 'o', 'r', 'd', '!'}, buffer.length)) {
+                Log.d(TAG, "call callback.onDetected");
+                callback.onDetected(null);
+            }
+        } catch (IOException e) {
+            Log.w(TAG, "Failed to read data : ", e);
+        }
     }
 
     @Override
@@ -81,4 +115,19 @@
             statusCallback.accept(INITIALIZATION_STATUS_SUCCESS);
         }
     }
+
+    private boolean isSame(byte[] array1, byte[] array2, int length) {
+        if (length <= 0) {
+            return false;
+        }
+        if (array1 == null || array2 == null || array1.length < length || array2.length < length) {
+            return false;
+        }
+        for (int i = 0; i < length; i++) {
+            if (array1[i] != array2[i]) {
+                return false;
+            }
+        }
+        return true;
+    }
 }
diff --git a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/HotwordDetectionServiceBasicTest.java b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/HotwordDetectionServiceBasicTest.java
index 6fe406e..700f891 100644
--- a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/HotwordDetectionServiceBasicTest.java
+++ b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/HotwordDetectionServiceBasicTest.java
@@ -80,6 +80,42 @@
         receiver.unregisterQuietly();
     }
 
+    @Test
+    public void testHotwordDetectionService_onDetectFromDsp_success()
+            throws Throwable {
+        // Create AlwaysOnHotwordDetector and wait the HotwordDetectionService ready
+        final BlockingBroadcastReceiver receiver = new BlockingBroadcastReceiver(mContext,
+                Utils.BROADCAST_HOTWORD_DETECTION_SERVICE_TRIGGER_RESULT_INTENT);
+        receiver.register();
+
+        mActivityTestRule.getScenario().onActivity(activity -> {
+            activity.triggerHotwordDetectionServiceTest(
+                    Utils.HOTWORD_DETECTION_SERVICE_BASIC,
+                    Utils.HOTWORD_DETECTION_SERVICE_TRIGGER_TEST);
+        });
+
+        receiver.awaitForBroadcast(TIMEOUT_MS);
+        receiver.unregisterQuietly();
+
+        // Use AlwaysOnHotwordDetector to test the onDetect function of HotwordDetectionService
+        final BlockingBroadcastReceiver onDetectReceiver = new BlockingBroadcastReceiver(mContext,
+                Utils.BROADCAST_HOTWORD_DETECTION_SERVICE_DSP_ONDETECT_RESULT_INTENT);
+        onDetectReceiver.register();
+
+        mActivityTestRule.getScenario().onActivity(activity -> {
+            activity.triggerHotwordDetectionServiceTest(
+                    Utils.HOTWORD_DETECTION_SERVICE_BASIC,
+                    Utils.HOTWORD_DETECTION_SERVICE_DSP_ONDETECT_TEST);
+        });
+
+        final Intent intent = onDetectReceiver.awaitForBroadcast(TIMEOUT_MS);
+        assertThat(intent).isNotNull();
+        assertThat(intent.getIntExtra(Utils.KEY_TEST_RESULT, -1)).isEqualTo(
+                Utils.HOTWORD_DETECTION_SERVICE_ONDETECT_SUCCESS);
+
+        onDetectReceiver.unregisterQuietly();
+    }
+
     @Override
     public String getVoiceInteractionService() {
         return "android.voiceinteraction.cts/"