[Error API] add CTS tests for Error cases
Currently I add this one error case.
1.HotwordDetectionServiceFailure.ERROR_CODE_BINDING_DIED
Bug: 261549853
Test: atest CtsVoiceInteractionTestCases
Change-Id: If3078654adaff7076195f05b6fdfd4220e512ee6
diff --git a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/HotwordDetectionServiceBasicTest.java b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/HotwordDetectionServiceBasicTest.java
index 7f9b4ed..b12b698 100644
--- a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/HotwordDetectionServiceBasicTest.java
+++ b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/HotwordDetectionServiceBasicTest.java
@@ -45,7 +45,9 @@
import android.os.SystemClock;
import android.platform.test.annotations.AppModeFull;
import android.service.voice.AlwaysOnHotwordDetector;
+import android.service.voice.DetectorFailure;
import android.service.voice.HotwordDetectionService;
+import android.service.voice.HotwordDetectionServiceFailure;
import android.service.voice.HotwordDetector;
import android.service.voice.HotwordDetector.IllegalDetectorStateException;
import android.service.voice.HotwordRejectedResult;
@@ -224,7 +226,8 @@
assumeTrue("Not support multiple hotword detectors", enableMultipleHotwordDetectors);
// Create first AlwaysOnHotwordDetector, it's fine.
- AlwaysOnHotwordDetector alwaysOnHotwordDetector = createAlwaysOnHotwordDetector();
+ AlwaysOnHotwordDetector alwaysOnHotwordDetector =
+ createAlwaysOnHotwordDetector(/* useOnFailure= */ false);
// Create second AlwaysOnHotwordDetector, it will get the IllegalStateException due to
// the previous AlwaysOnHotwordDetector is not destroy.
@@ -279,7 +282,10 @@
@Test
public void testHotwordDetectionService_processDied_triggerOnError() throws Throwable {
// Create first AlwaysOnHotwordDetector
- AlwaysOnHotwordDetector alwaysOnHotwordDetector = createAlwaysOnHotwordDetector();
+ AlwaysOnHotwordDetector alwaysOnHotwordDetector =
+ createAlwaysOnHotwordDetector(/* useOnFailure= */ false);
+
+ mService.initOnErrorLatch();
// Use AlwaysOnHotwordDetector to test process died of HotwordDetectionService
runWithShellPermissionIdentity(() -> {
@@ -291,6 +297,45 @@
Helper.createFakeSharedMemoryData());
}, MANAGE_HOTWORD_DETECTION);
+ mService.waitOnErrorCalled();
+
+ // ActivityManager will schedule a timer to restart the HotwordDetectionService due to
+ // we crash the service in this test case. It may impact the other test cases when
+ // ActivityManager restarts the HotwordDetectionService again. Add the sleep time to wait
+ // ActivityManager to restart the HotwordDetectionService, so that the service can be
+ // destroyed after finishing this test case.
+ Thread.sleep(5000);
+
+ alwaysOnHotwordDetector.destroy();
+ }
+
+ @Test
+ public void testHotwordDetectionService_processDied_triggerOnFailure() throws Throwable {
+ // Create alwaysOnHotwordDetector with onFailure callback
+ AlwaysOnHotwordDetector alwaysOnHotwordDetector =
+ createAlwaysOnHotwordDetector(/* useOnFailure= */ true);
+
+ mService.initOnFailureLatch();
+
+ // Use AlwaysOnHotwordDetector to test process died of HotwordDetectionService
+ runWithShellPermissionIdentity(() -> {
+ PersistableBundle persistableBundle = new PersistableBundle();
+ persistableBundle.putInt(Helper.KEY_TEST_SCENARIO,
+ Helper.EXTRA_HOTWORD_DETECTION_SERVICE_ON_UPDATE_STATE_CRASH);
+ alwaysOnHotwordDetector.updateState(
+ persistableBundle,
+ Helper.createFakeSharedMemoryData());
+ }, MANAGE_HOTWORD_DETECTION);
+
+ mService.waitOnFailureCalled();
+
+ DetectorFailure detectorFailure = mService.getDetectorFailure();
+
+ assertThat(detectorFailure).isNotNull();
+ assertThat(detectorFailure).isInstanceOf(HotwordDetectionServiceFailure.class);
+ assertThat(((HotwordDetectionServiceFailure) detectorFailure).getErrorCode()).isEqualTo(
+ HotwordDetectionServiceFailure.ERROR_CODE_BINDING_DIED);
+
// ActivityManager will schedule a timer to restart the HotwordDetectionService due to
// we crash the service in this test case. It may impact the other test cases when
// ActivityManager restarts the HotwordDetectionService again. Add the sleep time to wait
@@ -312,7 +357,8 @@
// Create AlwaysOnHotwordDetector
startWatchingNoted();
- AlwaysOnHotwordDetector alwaysOnHotwordDetector = createAlwaysOnHotwordDetector();
+ AlwaysOnHotwordDetector alwaysOnHotwordDetector =
+ createAlwaysOnHotwordDetector(/* useOnFailure= */ false);
try {
adoptShellPermissionIdentityForHotword();
@@ -335,7 +381,8 @@
public void testHotwordDetectionService_onDetectFromDsp_success() throws Throwable {
startWatchingNoted();
// Create AlwaysOnHotwordDetector
- AlwaysOnHotwordDetector alwaysOnHotwordDetector = createAlwaysOnHotwordDetector();
+ AlwaysOnHotwordDetector alwaysOnHotwordDetector =
+ createAlwaysOnHotwordDetector(/* useOnFailure= */ false);
try {
adoptShellPermissionIdentityForHotword();
@@ -358,7 +405,8 @@
public void testHotwordDetectionService_onDetectFromDsp_rejection() throws Throwable {
startWatchingNoted();
// Create AlwaysOnHotwordDetector
- AlwaysOnHotwordDetector alwaysOnHotwordDetector = createAlwaysOnHotwordDetector();
+ AlwaysOnHotwordDetector alwaysOnHotwordDetector =
+ createAlwaysOnHotwordDetector(/* useOnFailure= */ false);
try {
mService.initDetectRejectLatch();
runWithShellPermissionIdentity(() -> {
@@ -392,7 +440,8 @@
public void testHotwordDetectionService_onDetectFromDsp_timeout() throws Throwable {
startWatchingNoted();
// Create AlwaysOnHotwordDetector
- AlwaysOnHotwordDetector alwaysOnHotwordDetector = createAlwaysOnHotwordDetector();
+ AlwaysOnHotwordDetector alwaysOnHotwordDetector =
+ createAlwaysOnHotwordDetector(/* useOnFailure= */ false);
// Update HotwordDetectionService options to delay detection, to cause a timeout
runWithShellPermissionIdentity(() -> {
PersistableBundle options = Helper.createFakePersistableBundleData();
@@ -432,7 +481,8 @@
public void testHotwordDetectionService_destroyDspDetector_activeDetectorRemoved()
throws Throwable {
// Create AlwaysOnHotwordDetector
- AlwaysOnHotwordDetector alwaysOnHotwordDetector = createAlwaysOnHotwordDetector();
+ AlwaysOnHotwordDetector alwaysOnHotwordDetector =
+ createAlwaysOnHotwordDetector(/* useOnFailure= */ false);
// destroy detector
alwaysOnHotwordDetector.destroy();
try {
@@ -459,7 +509,8 @@
public void testHotwordDetectionService_onDetectFromExternalSource_success() throws Throwable {
startWatchingNoted();
// Create AlwaysOnHotwordDetector
- AlwaysOnHotwordDetector alwaysOnHotwordDetector = createAlwaysOnHotwordDetector();
+ AlwaysOnHotwordDetector alwaysOnHotwordDetector =
+ createAlwaysOnHotwordDetector(/* useOnFailure= */ false);
try {
adoptShellPermissionIdentityForHotword();
@@ -617,7 +668,8 @@
Helper.isEnableMultipleDetectors());
// Create AlwaysOnHotwordDetector
- AlwaysOnHotwordDetector alwaysOnHotwordDetector = createAlwaysOnHotwordDetector();
+ AlwaysOnHotwordDetector alwaysOnHotwordDetector =
+ createAlwaysOnHotwordDetector(/* useOnFailure= */ false);
// Create SoftwareHotwordDetector
HotwordDetector softwareHotwordDetector = createSoftwareHotwordDetector();
@@ -644,7 +696,8 @@
@Test
public void testHotwordDetectionService_onHotwordDetectionServiceRestarted() throws Throwable {
// Create AlwaysOnHotwordDetector
- createAlwaysOnHotwordDetector();
+ AlwaysOnHotwordDetector alwaysOnHotwordDetector =
+ createAlwaysOnHotwordDetector(/* useOnFailure= */ false);
mService.initOnHotwordDetectionServiceRestartedLatch();
// force re-start by shell command
@@ -652,6 +705,9 @@
// wait onHotwordDetectionServiceRestarted() called
mService.waitOnHotwordDetectionServiceRestartedCalled();
+
+ // Destroy the always on detector
+ alwaysOnHotwordDetector.destroy();
}
private void verifySoftwareDetectorDetectSuccess(HotwordDetector softwareHotwordDetector)
@@ -687,9 +743,15 @@
/**
* Create AlwaysOnHotwordDetector and wait for ready
*/
- private AlwaysOnHotwordDetector createAlwaysOnHotwordDetector() throws Throwable {
+ private AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(boolean useOnFailure)
+ throws Throwable {
// Create AlwaysOnHotwordDetector and wait ready.
- mService.createAlwaysOnHotwordDetector();
+ if (useOnFailure) {
+ mService.createAlwaysOnHotwordDetectorWithOnFailureCallback(/* useExecutor= */
+ false, /* runOnMainThread= */ false);
+ } else {
+ mService.createAlwaysOnHotwordDetector();
+ }
mService.waitSandboxedDetectionServiceInitializedCalledOrException();
diff --git a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/services/CtsBasicVoiceInteractionService.java b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/services/CtsBasicVoiceInteractionService.java
index 7e67c58..72973fa 100644
--- a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/services/CtsBasicVoiceInteractionService.java
+++ b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/services/CtsBasicVoiceInteractionService.java
@@ -56,6 +56,8 @@
private CountDownLatch mOnDetectRejectLatch;
// The CountDownLatch waits for a service onError called
private CountDownLatch mOnErrorLatch;
+ // The CountDownLatch waits for a service onFailure called
+ private CountDownLatch mOnFailureLatch;
// The CountDownLatch waits for vqds
private CountDownLatch mOnQueryFinishRejectLatch;
// The CountDownLatch waits for a service onHotwordDetectionServiceRestarted called
@@ -65,7 +67,7 @@
private HotwordRejectedResult mRejectedResult;
private ArrayList<String> mStreamedQueries = new ArrayList<>();
private String mCurrentQuery = "";
-
+ private DetectorFailure mDetectorFailure = null;
public CtsBasicVoiceInteractionService() {
HandlerThread handlerThread = new HandlerThread("CtsBasicVoiceInteractionService");
handlerThread.start();
@@ -135,14 +137,18 @@
}
/**
- * Create AlwaysOnHotwordDetector.
+ * Create an AlwaysOnHotwordDetector, but it will not implement the onFailure method of
+ * AlwaysOnHotwordDetector.Callback. It will implement the onFailure method by using
+ * createAlwaysOnHotwordDetectorWithOnFailureCallback method.
*/
public void createAlwaysOnHotwordDetector() {
createAlwaysOnHotwordDetector(/* useExecutor= */ false, /* runOnMainThread= */ false);
}
/**
- * Create AlwaysOnHotwordDetector.
+ * Create an AlwaysOnHotwordDetector, but it will not implement the onFailure method of
+ * AlwaysOnHotwordDetector.Callback. It will implement the onFailure method by using
+ * createAlwaysOnHotwordDetectorWithOnFailureCallback method.
*/
public void createAlwaysOnHotwordDetector(boolean useExecutor, boolean runOnMainThread) {
Log.i(TAG, "createAlwaysOnHotwordDetector!!!!");
@@ -246,6 +252,96 @@
BIND_HOTWORD_DETECTION_SERVICE));
}
+ /**
+ * Create an AlwaysOnHotwordDetector with onFailure callback. The onFailure provides the error
+ * code, error message and suggested action the assistant application should take.
+ */
+ public void createAlwaysOnHotwordDetectorWithOnFailureCallback(boolean useExecutor,
+ boolean runOnMainThread) {
+ Log.i(TAG, "createAlwaysOnHotwordDetectorWithOnFailureCallback");
+ mServiceTriggerLatch = new CountDownLatch(1);
+
+ final AlwaysOnHotwordDetector.Callback callback = new AlwaysOnHotwordDetector.Callback() {
+ @Override
+ public void onAvailabilityChanged(int status) {
+ Log.i(TAG, "onAvailabilityChanged(" + status + ")");
+ mAvailabilityStatus = status;
+ setIsDetectorCallbackRunningOnMainThread(isRunningOnMainThread());
+ if (mAvailabilityChangeLatch != null) {
+ mAvailabilityChangeLatch.countDown();
+ }
+ }
+
+ @Override
+ public void onDetected(AlwaysOnHotwordDetector.EventPayload eventPayload) {
+ Log.i(TAG, "onDetected");
+ mDetectedResult = eventPayload;
+ setIsDetectorCallbackRunningOnMainThread(isRunningOnMainThread());
+ if (mOnDetectRejectLatch != null) {
+ mOnDetectRejectLatch.countDown();
+ }
+ }
+
+ @Override
+ public void onRejected(@NonNull HotwordRejectedResult result) {
+ Log.i(TAG, "onRejected");
+ mRejectedResult = result;
+ setIsDetectorCallbackRunningOnMainThread(isRunningOnMainThread());
+ if (mOnDetectRejectLatch != null) {
+ mOnDetectRejectLatch.countDown();
+ }
+ }
+
+ @Override
+ public void onError() {
+ Log.i(TAG, "onError");
+ }
+
+ @Override
+ public void onFailure(DetectorFailure detectorFailure) {
+ Log.i(TAG, "onFailure detectorFailure=" + detectorFailure);
+ mDetectorFailure = detectorFailure;
+ setIsDetectorCallbackRunningOnMainThread(isRunningOnMainThread());
+ if (mOnFailureLatch != null) {
+ mOnFailureLatch.countDown();
+ }
+ }
+
+ @Override
+ public void onRecognitionPaused() {
+ Log.i(TAG, "onRecognitionPaused");
+ }
+
+ @Override
+ public void onRecognitionResumed() {
+ Log.i(TAG, "onRecognitionResumed");
+ }
+
+ @Override
+ public void onHotwordDetectionServiceInitialized(int status) {
+ Log.i(TAG, "onHotwordDetectionServiceInitialized status = " + status);
+ if (status != HotwordDetectionService.INITIALIZATION_STATUS_SUCCESS) {
+ return;
+ }
+ mInitializedStatus = status;
+ setIsDetectorCallbackRunningOnMainThread(isRunningOnMainThread());
+ if (mServiceTriggerLatch != null) {
+ mServiceTriggerLatch.countDown();
+ }
+ }
+
+ @Override
+ public void onHotwordDetectionServiceRestarted() {
+ Log.i(TAG, "onHotwordDetectionServiceRestarted");
+ }
+ };
+
+ final Handler handler = runOnMainThread ? new Handler(Looper.getMainLooper()) : mHandler;
+ handler.post(() -> runWithShellPermissionIdentity(() -> {
+ mAlwaysOnHotwordDetector = callCreateAlwaysOnHotwordDetector(callback, useExecutor);
+ }, MANAGE_HOTWORD_DETECTION));
+ }
+
public void createSoftwareHotwordDetector() {
createSoftwareHotwordDetector(/* useExecutor= */ false, /* runOnMainThread= */ false);
}
@@ -429,6 +525,13 @@
}
/**
+ * Create a CountDownLatch that is used to wait for onFailure()
+ */
+ public void initOnFailureLatch() {
+ mOnFailureLatch = new CountDownLatch(1);
+ }
+
+ /**
* Returns the onDetected() result.
*/
public AlwaysOnHotwordDetector.EventPayload getHotwordServiceOnDetectedResult() {
@@ -463,6 +566,13 @@
}
/**
+ * Returns the OnFailure() result.
+ */
+ public DetectorFailure getDetectorFailure() {
+ return mDetectorFailure;
+ }
+
+ /**
* Wait for onAvailabilityChanged() callback called.
*/
public void waitAvailabilityChangedCalled() throws InterruptedException {
@@ -520,4 +630,17 @@
}
mOnErrorLatch = null;
}
+
+ /**
+ * Wait for onFailure() callback called.
+ */
+ public void waitOnFailureCalled() throws InterruptedException {
+ Log.d(TAG, "waitOnFailureCalled(), latch=" + mOnFailureLatch);
+ if (mOnFailureLatch == null
+ || !mOnFailureLatch.await(WAIT_TIMEOUT_IN_MS, TimeUnit.MILLISECONDS)) {
+ mOnFailureLatch = null;
+ throw new AssertionError("OnFailure() fail.");
+ }
+ mOnFailureLatch = null;
+ }
}