Add Cts for validation of HotwordDetectedResult
Bug: 190688148
Test: atest CtsVoiceInteractionTestCases
Test: atest CtsVoiceInteractionTestCases --instant
Change-Id: Ia5241ab9330e8ec9d93429cfc3c666afca2f8f9e
diff --git a/tests/tests/voiceinteraction/Android.bp b/tests/tests/voiceinteraction/Android.bp
index df37450..9ba5e42 100644
--- a/tests/tests/voiceinteraction/Android.bp
+++ b/tests/tests/voiceinteraction/Android.bp
@@ -24,6 +24,7 @@
"ctstestrunner-axt",
"compatibility-device-util-axt",
"androidx.test.ext.junit",
+ "testng",
],
srcs: [
"src/**/*.java",
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 2aaf0cc..7ec2d60 100644
--- a/tests/tests/voiceinteraction/common/src/android/voiceinteraction/common/Utils.java
+++ b/tests/tests/voiceinteraction/common/src/android/voiceinteraction/common/Utils.java
@@ -18,6 +18,8 @@
import android.app.VoiceInteractor.PickOptionRequest.Option;
import android.content.LocusId;
import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.util.Log;
import java.util.ArrayList;
@@ -44,6 +46,9 @@
public static final long OPERATION_TIMEOUT_MS = 5000;
+ /** CDD restricts the max size of each successful hotword result is 100 bytes. */
+ public static final int MAX_HOTWORD_DETECTED_RESULT_SIZE = 100;
+
/** Decide which VoiceInteractionService should be started for testing. */
public static final int HOTWORD_DETECTION_SERVICE_NONE = 0;
public static final int HOTWORD_DETECTION_SERVICE_BASIC = 1;
@@ -227,4 +232,22 @@
}
return false;
}
+
+ public static int getParcelableSize(Parcelable parcelable) {
+ final Parcel p = Parcel.obtain();
+ parcelable.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ final int size = p.dataSize();
+ p.recycle();
+ return size;
+ }
+
+ public static int bitCount(int value) {
+ int bits = 0;
+ while (value > 0) {
+ bits++;
+ value = value >> 1;
+ }
+ return bits;
+ }
}
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 da8c6e8..b23d0ae 100644
--- a/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainHotwordDetectionService.java
+++ b/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainHotwordDetectionService.java
@@ -58,7 +58,27 @@
// TODO: Check the capture session (needs to be reflectively accessed).
byte[] data = eventPayload.getTriggerAudio();
if (data != null && data.length > 0) {
- callback.onDetected(DETECTED_RESULT);
+ // Create the unaccepted HotwordDetectedResult first to test the protection in the
+ // onDetected callback function of HotwordDetectionService. When the bundle data of
+ // HotwordDetectedResult is larger than max bundle size, it will throw the
+ // IllegalArgumentException.
+ PersistableBundle persistableBundle = new PersistableBundle();
+ HotwordDetectedResult hotwordDetectedResult =
+ new HotwordDetectedResult.Builder()
+ .setExtras(persistableBundle)
+ .build();
+ int key = 0;
+ do {
+ persistableBundle.putInt(Integer.toString(key), 0);
+ key++;
+ } while (Utils.getParcelableSize(persistableBundle)
+ <= HotwordDetectedResult.getMaxBundleSize());
+
+ try {
+ callback.onDetected(hotwordDetectedResult);
+ } catch (IllegalArgumentException e) {
+ callback.onDetected(DETECTED_RESULT);
+ }
} else {
callback.onRejected(REJECTED_RESULT);
}
diff --git a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/HotwordDetectedResultTest.java b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/HotwordDetectedResultTest.java
index 3e245de..81254fdb 100644
--- a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/HotwordDetectedResultTest.java
+++ b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/HotwordDetectedResultTest.java
@@ -18,10 +18,14 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.testng.Assert.assertThrows;
+
+import android.media.AudioRecord;
import android.media.MediaSyncEvent;
import android.os.Parcel;
import android.os.PersistableBundle;
import android.service.voice.HotwordDetectedResult;
+import android.voiceinteraction.common.Utils;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -33,17 +37,93 @@
@Test
public void testHotwordDetectedResult_getMaxBundleSize() throws Exception {
- assertThat(HotwordDetectedResult.getMaxBundleSize()).isEqualTo(50);
+ assertThat(HotwordDetectedResult.getMaxBundleSize() >= 0).isTrue();
+ }
+
+ @Test
+ public void testHotwordDetectedResult_totalSize() throws Exception {
+ final int bitsForConfidenceLevel = Utils.bitCount(
+ HotwordDetectedResult.CONFIDENCE_LEVEL_VERY_HIGH);
+ final int bitsForHotwordOffsetMillis = Integer.SIZE;
+ final int bitsForHotwordDurationMillis = Integer.SIZE;
+ final int bitsForAudioChannel = Integer.SIZE;
+ final int bitsForHotwordDetectionPersonalized = 1;
+ final int bitsForScore = Utils.bitCount(HotwordDetectedResult.getMaxScore());
+ final int bitsForPersonalizedScore = Utils.bitCount(HotwordDetectedResult.getMaxScore());
+ final int bitsForHotwordPhraseId = Utils.bitCount(
+ HotwordDetectedResult.getMaxHotwordPhraseId());
+
+ final int totalSize =
+ bitsForConfidenceLevel + bitsForHotwordOffsetMillis + bitsForHotwordDurationMillis
+ + bitsForAudioChannel + bitsForHotwordDetectionPersonalized + bitsForScore
+ + bitsForPersonalizedScore + bitsForHotwordPhraseId
+ + HotwordDetectedResult.getMaxBundleSize() * Byte.SIZE;
+
+ assertThat(totalSize <= Utils.MAX_HOTWORD_DETECTED_RESULT_SIZE * Byte.SIZE).isTrue();
+ }
+
+ @Test
+ public void testHotwordDetectedResult_bundleExceedMaxBundleSize() throws Exception {
+ final PersistableBundle persistableBundle = new PersistableBundle();
+ int key = 0;
+ do {
+ persistableBundle.putInt(Integer.toString(key), 0);
+ key++;
+ } while (Utils.getParcelableSize(persistableBundle)
+ <= HotwordDetectedResult.getMaxBundleSize());
+
+ assertThrows(IllegalArgumentException.class,
+ () -> new HotwordDetectedResult.Builder().setExtras(persistableBundle).build());
}
@Test
public void testHotwordDetectedResult_getMaxHotwordPhraseId() throws Exception {
- assertThat(HotwordDetectedResult.getMaxHotwordPhraseId()).isEqualTo(63);
+ assertThat(HotwordDetectedResult.getMaxHotwordPhraseId() >= 63).isTrue();
+ }
+
+ @Test
+ public void testHotwordDetectedResult_setInvalidHotwordPhraseId() throws Exception {
+ assertThrows(IllegalArgumentException.class,
+ () -> new HotwordDetectedResult.Builder().setHotwordPhraseId(-1).build());
+
+ assertThrows(IllegalArgumentException.class,
+ () -> new HotwordDetectedResult.Builder().setHotwordPhraseId(
+ HotwordDetectedResult.getMaxHotwordPhraseId() + 1).build());
}
@Test
public void testHotwordDetectedResult_getMaxScore() throws Exception {
- assertThat(HotwordDetectedResult.getMaxScore()).isEqualTo(255);
+ assertThat(HotwordDetectedResult.getMaxScore() >= 255).isTrue();
+ }
+
+ @Test
+ public void testHotwordDetectedResult_setInvalidScore() throws Exception {
+ assertThrows(IllegalArgumentException.class,
+ () -> new HotwordDetectedResult.Builder().setScore(-1).build());
+
+ assertThrows(IllegalArgumentException.class,
+ () -> new HotwordDetectedResult.Builder().setScore(
+ HotwordDetectedResult.getMaxScore() + 1).build());
+ }
+
+ @Test
+ public void testHotwordDetectedResult_setInvalidPersonalizedScore() throws Exception {
+ assertThrows(IllegalArgumentException.class,
+ () -> new HotwordDetectedResult.Builder().setPersonalizedScore(-1).build());
+
+ assertThrows(IllegalArgumentException.class,
+ () -> new HotwordDetectedResult.Builder().setPersonalizedScore(
+ HotwordDetectedResult.getMaxScore() + 1).build());
+ }
+
+ @Test
+ public void testHotwordDetectedResult_setInvalidHotwordDurationMillis() throws Exception {
+ assertThrows(IllegalArgumentException.class,
+ () -> new HotwordDetectedResult.Builder().setHotwordDurationMillis(-1).build());
+
+ assertThrows(IllegalArgumentException.class,
+ () -> new HotwordDetectedResult.Builder().setHotwordDurationMillis(
+ (int) AudioRecord.getMaxSharedAudioHistoryMillis() + 1).build());
}
@Test