SoundTrigger test cleanup

- Remove stale stub hal test, as it is non-functional and has been
  superseded
- Create package/directory for stservice tests
- Move SoundTriggerTest to new package
- Fix SoundTriggerTest to prevent passing null to APIs which no longer
  accept null
- Split ConversionUtilTest into st service and middleware conversion
  tests

Fixes: 275434578
Test: atest FrameworkVoiceInteractionTests
Change-Id: If523b268d9defe266d998232041bcb0728554ef2
This commit is contained in:
Atneya Nair
2023-03-27 18:43:03 -07:00
parent c3d2c2e58d
commit 49172857ba
7 changed files with 135 additions and 450 deletions

View File

@@ -0,0 +1,99 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.soundtrigger;
import static android.hardware.soundtrigger.ConversionUtil.aidl2apiAudioFormatWithDefault;
import static android.hardware.soundtrigger.ConversionUtil.aidl2apiPhrase;
import static android.hardware.soundtrigger.ConversionUtil.aidl2apiRecognitionConfig;
import static android.hardware.soundtrigger.ConversionUtil.api2aidlPhrase;
import static android.hardware.soundtrigger.ConversionUtil.api2aidlRecognitionConfig;
import static android.hardware.soundtrigger.ConversionUtil.byteArrayToSharedMemory;
import static android.hardware.soundtrigger.ConversionUtil.sharedMemoryToByteArray;
import static android.hardware.soundtrigger.SoundTrigger.ConfidenceLevel;
import static android.hardware.soundtrigger.SoundTrigger.RECOGNITION_MODE_GENERIC;
import static android.hardware.soundtrigger.SoundTrigger.RECOGNITION_MODE_USER_AUTHENTICATION;
import static android.hardware.soundtrigger.SoundTrigger.RECOGNITION_MODE_USER_IDENTIFICATION;
import static android.hardware.soundtrigger.SoundTrigger.RECOGNITION_MODE_VOICE_TRIGGER;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import android.hardware.soundtrigger.ConversionUtil;
import android.hardware.soundtrigger.SoundTrigger;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.Locale;
@RunWith(AndroidJUnit4.class)
public class ConversionUtilTest {
private static final String TAG = "ConversionUtilTest";
@Test
public void testDefaultAudioFormatConstruction() {
// This method should generate a real format when passed null
final var format = aidl2apiAudioFormatWithDefault(
null /** exercise default **/,
true /** isInput **/
);
assertNotNull(format);
}
@Test
public void testRecognitionConfigRoundTrip() {
final int flags = SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_ECHO_CANCELLATION
| SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_NOISE_SUPPRESSION;
final var data = new byte[] {0x11, 0x22};
final var keyphrases = new SoundTrigger.KeyphraseRecognitionExtra[2];
keyphrases[0] = new SoundTrigger.KeyphraseRecognitionExtra(99,
RECOGNITION_MODE_VOICE_TRIGGER | RECOGNITION_MODE_USER_IDENTIFICATION, 13,
new ConfidenceLevel[] {new ConfidenceLevel(9999, 50),
new ConfidenceLevel(5000, 80)});
keyphrases[1] = new SoundTrigger.KeyphraseRecognitionExtra(101,
RECOGNITION_MODE_GENERIC, 8, new ConfidenceLevel[] {
new ConfidenceLevel(7777, 30),
new ConfidenceLevel(2222, 60)});
var apiconfig = new SoundTrigger.RecognitionConfig(true, false /** must be false **/,
keyphrases, data, flags);
assertEquals(apiconfig, aidl2apiRecognitionConfig(api2aidlRecognitionConfig(apiconfig)));
}
@Test
public void testByteArraySharedMemRoundTrip() {
final var data = new byte[] { 0x11, 0x22, 0x33, 0x44,
(byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef };
assertArrayEquals(data, sharedMemoryToByteArray(byteArrayToSharedMemory(data, "name"),
10000000));
}
@Test
public void testPhraseRoundTrip() {
final var users = new int[] {10001, 10002};
final var apiphrase = new SoundTrigger.Keyphrase(17 /** id **/,
RECOGNITION_MODE_VOICE_TRIGGER | RECOGNITION_MODE_USER_AUTHENTICATION,
Locale.forLanguageTag("no_NO"),
"Hello Android", /** keyphrase **/
users);
assertEquals(apiphrase, aidl2apiPhrase(api2aidlPhrase(apiphrase)));
}
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package android.hardware.soundtrigger;
package com.android.server.soundtrigger;
import android.hardware.soundtrigger.SoundTrigger.ConfidenceLevel;
import android.hardware.soundtrigger.SoundTrigger.Keyphrase;
@@ -22,6 +22,7 @@ import android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionEvent;
import android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra;
import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
import android.hardware.soundtrigger.SoundTrigger.RecognitionEvent;
import android.hardware.soundtrigger.SoundTrigger;
import android.media.AudioFormat;
import android.os.Parcel;
import android.test.InstrumentationTestCase;
@@ -50,10 +51,7 @@ public class SoundTriggerTest extends InstrumentationTestCase {
Keyphrase unparceled = Keyphrase.CREATOR.createFromParcel(parcel);
// Verify that they are the same
assertEquals(keyphrase.getId(), unparceled.getId());
assertNull(unparceled.getUsers());
assertEquals(keyphrase.getLocale(), unparceled.getLocale());
assertEquals(keyphrase.getText(), unparceled.getText());
assertEquals(keyphrase, unparceled);
}
@SmallTest
@@ -115,10 +113,7 @@ public class SoundTriggerTest extends InstrumentationTestCase {
KeyphraseSoundModel unparceled = KeyphraseSoundModel.CREATOR.createFromParcel(parcel);
// Verify that they are the same
assertEquals(ksm.getUuid(), unparceled.getUuid());
assertNull(unparceled.getData());
assertEquals(ksm.getType(), unparceled.getType());
assertTrue(Arrays.equals(keyphrases, unparceled.getKeyphrases()));
assertEquals(ksm, unparceled);
}
@SmallTest
@@ -162,10 +157,7 @@ public class SoundTriggerTest extends InstrumentationTestCase {
KeyphraseSoundModel unparceled = KeyphraseSoundModel.CREATOR.createFromParcel(parcel);
// Verify that they are the same
assertEquals(ksm.getUuid(), unparceled.getUuid());
assertEquals(ksm.getType(), unparceled.getType());
assertNull(unparceled.getKeyphrases());
assertTrue(Arrays.equals(ksm.getData(), unparceled.getData()));
assertEquals(ksm, unparceled);
}
@SmallTest
@@ -226,7 +218,11 @@ public class SoundTriggerTest extends InstrumentationTestCase {
3 /* captureDelayMs */,
4 /* capturePreambleMs */,
false /* triggerInData */,
null /* captureFormat */,
new AudioFormat.Builder()
.setSampleRate(16000)
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
.setChannelMask(AudioFormat.CHANNEL_IN_MONO)
.build(),
null /* data */,
12345678 /* halEventReceivedMillis */);
@@ -251,7 +247,11 @@ public class SoundTriggerTest extends InstrumentationTestCase {
3 /* captureDelayMs */,
4 /* capturePreambleMs */,
false /* triggerInData */,
null /* captureFormat */,
new AudioFormat.Builder()
.setSampleRate(16000)
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
.setChannelMask(AudioFormat.CHANNEL_IN_MONO)
.build(),
new byte[1] /* data */,
12345678 /* halEventReceivedMillis */);
@@ -278,7 +278,11 @@ public class SoundTriggerTest extends InstrumentationTestCase {
3 /* captureDelayMs */,
4 /* capturePreambleMs */,
false /* triggerInData */,
null /* captureFormat */,
new AudioFormat.Builder()
.setSampleRate(16000)
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
.setChannelMask(AudioFormat.CHANNEL_IN_MONO)
.build(),
data,
12345678 /* halEventReceivedMillis */);
@@ -335,7 +339,11 @@ public class SoundTriggerTest extends InstrumentationTestCase {
3 /* captureDelayMs */,
4 /* capturePreambleMs */,
false /* triggerInData */,
null /* captureFormat */,
new AudioFormat.Builder()
.setSampleRate(16000)
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
.setChannelMask(AudioFormat.CHANNEL_IN_MONO)
.build(),
null /* data */,
null /* keyphraseExtras */,
12345678 /* halEventReceivedMillis */);
@@ -364,7 +372,11 @@ public class SoundTriggerTest extends InstrumentationTestCase {
3 /* captureDelayMs */,
4 /* capturePreambleMs */,
false /* triggerInData */,
null /* captureFormat */,
new AudioFormat.Builder()
.setSampleRate(16000)
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
.setChannelMask(AudioFormat.CHANNEL_IN_MONO)
.build(),
new byte[1] /* data */,
kpExtra,
12345678 /* halEventReceivedMillis */);
@@ -409,7 +421,11 @@ public class SoundTriggerTest extends InstrumentationTestCase {
3 /* captureDelayMs */,
4 /* capturePreambleMs */,
false /* triggerInData */,
null /* captureFormat */,
new AudioFormat.Builder()
.setSampleRate(16000)
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
.setChannelMask(AudioFormat.CHANNEL_IN_MONO)
.build(),
data,
kpExtra,
12345678 /* halEventReceivedMillis */);

View File

@@ -16,36 +16,18 @@
package com.android.server.soundtrigger_middleware;
import static android.hardware.soundtrigger.ConversionUtil.aidl2apiAudioFormatWithDefault;
import static android.hardware.soundtrigger.ConversionUtil.aidl2apiPhrase;
import static android.hardware.soundtrigger.ConversionUtil.aidl2apiRecognitionConfig;
import static android.hardware.soundtrigger.ConversionUtil.api2aidlPhrase;
import static android.hardware.soundtrigger.ConversionUtil.api2aidlRecognitionConfig;
import static android.hardware.soundtrigger.ConversionUtil.byteArrayToSharedMemory;
import static android.hardware.soundtrigger.ConversionUtil.sharedMemoryToByteArray;
import static android.hardware.soundtrigger.SoundTrigger.ConfidenceLevel;
import static android.hardware.soundtrigger.SoundTrigger.RECOGNITION_MODE_GENERIC;
import static android.hardware.soundtrigger.SoundTrigger.RECOGNITION_MODE_USER_AUTHENTICATION;
import static android.hardware.soundtrigger.SoundTrigger.RECOGNITION_MODE_USER_IDENTIFICATION;
import static android.hardware.soundtrigger.SoundTrigger.RECOGNITION_MODE_VOICE_TRIGGER;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import android.hardware.audio.common.V2_0.Uuid;
import android.hardware.soundtrigger.SoundTrigger;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.Locale;
@RunWith(AndroidJUnit4.class)
public class ConversionUtilTest {
private static final String TAG = "ConversionUtilTest";
private static final String TAG = "SoundTriggerMiddlewareConversionUtilTest";
@Test
public void testUuidRoundTrip() {
@@ -62,54 +44,4 @@ public class ConversionUtilTest {
Uuid reconstructed = ConversionUtil.aidl2hidlUuid(aidl);
assertEquals(hidl, reconstructed);
}
@Test
public void testDefaultAudioFormatConstruction() {
// This method should generate a real format when passed null
final var format = aidl2apiAudioFormatWithDefault(
null /** exercise default **/,
true /** isInput **/
);
assertNotNull(format);
}
@Test
public void testRecognitionConfigRoundTrip() {
final int flags = SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_ECHO_CANCELLATION
| SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_NOISE_SUPPRESSION;
final var data = new byte[] {0x11, 0x22};
final var keyphrases = new SoundTrigger.KeyphraseRecognitionExtra[2];
keyphrases[0] = new SoundTrigger.KeyphraseRecognitionExtra(99,
RECOGNITION_MODE_VOICE_TRIGGER | RECOGNITION_MODE_USER_IDENTIFICATION, 13,
new ConfidenceLevel[] {new ConfidenceLevel(9999, 50),
new ConfidenceLevel(5000, 80)});
keyphrases[1] = new SoundTrigger.KeyphraseRecognitionExtra(101,
RECOGNITION_MODE_GENERIC, 8, new ConfidenceLevel[] {
new ConfidenceLevel(7777, 30),
new ConfidenceLevel(2222, 60)});
var apiconfig = new SoundTrigger.RecognitionConfig(true, false /** must be false **/,
keyphrases, data, flags);
assertEquals(apiconfig, aidl2apiRecognitionConfig(api2aidlRecognitionConfig(apiconfig)));
}
@Test
public void testByteArraySharedMemRoundTrip() {
final var data = new byte[] { 0x11, 0x22, 0x33, 0x44,
(byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef };
assertArrayEquals(data, sharedMemoryToByteArray(byteArrayToSharedMemory(data, "name"),
10000000));
}
@Test
public void testPhraseRoundTrip() {
final var users = new int[] {10001, 10002};
final var apiphrase = new SoundTrigger.Keyphrase(17 /** id **/,
RECOGNITION_MODE_VOICE_TRIGGER | RECOGNITION_MODE_USER_AUTHENTICATION,
Locale.forLanguageTag("no_NO"),
"Hello Android", /** keyphrase **/
users);
assertEquals(apiphrase, aidl2apiPhrase(api2aidlPhrase(apiphrase)));
}
}

View File

@@ -1,39 +0,0 @@
#
# Copyright (C) 2014 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
ifeq ($(SOUND_TRIGGER_USE_STUB_MODULE), 1)
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PRIVILEGED_MODULE := true
LOCAL_CERTIFICATE := platform
TARGET_OUT_DATA_APPS_PRIVILEGED := $(TARGET_OUT_DATA)/priv-app
else
LOCAL_SRC_FILES := src/android/hardware/soundtrigger/SoundTriggerTest.java
endif
LOCAL_STATIC_JAVA_LIBRARIES := mockito-target
LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
LOCAL_PACKAGE_NAME := SoundTriggerTests
LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
LOCAL_LICENSE_CONDITIONS := notice
LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE
LOCAL_PRIVATE_PLATFORM_APIS := true
include $(BUILD_PACKAGE)

View File

@@ -1,29 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.hardware.soundtrigger">
<uses-permission android:name="android.permission.MANAGE_SOUND_TRIGGER" />
<uses-permission android:name="android.permission.INTERNET" />
<application>
<uses-library android:name="android.test.runner" />
</application>
<instrumentation android:name="android.test.InstrumentationTestRunner"
android:targetPackage="android.hardware.soundtrigger"
android:label="Tests for android.hardware.soundtrigger" />
</manifest>

View File

@@ -1 +0,0 @@
include /media/java/android/media/soundtrigger/OWNERS

View File

@@ -1,293 +0,0 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.hardware.soundtrigger;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
import android.content.Context;
import android.hardware.soundtrigger.SoundTrigger.GenericRecognitionEvent;
import android.hardware.soundtrigger.SoundTrigger.GenericSoundModel;
import android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionEvent;
import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
import android.media.soundtrigger.SoundTriggerManager;
import android.os.ParcelUuid;
import android.os.ServiceManager;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.SmallTest;
import com.android.internal.app.ISoundTriggerService;
import org.mockito.MockitoAnnotations;
import java.io.DataOutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Random;
import java.util.UUID;
public class GenericSoundModelTest extends AndroidTestCase {
static final int MSG_DETECTION_ERROR = -1;
static final int MSG_DETECTION_RESUME = 0;
static final int MSG_DETECTION_PAUSE = 1;
static final int MSG_KEYPHRASE_TRIGGER = 2;
static final int MSG_GENERIC_TRIGGER = 4;
private Random random = new Random();
private HashSet<UUID> loadedModelUuids;
private ISoundTriggerService soundTriggerService;
private SoundTriggerManager soundTriggerManager;
@Override
public void setUp() throws Exception {
super.setUp();
MockitoAnnotations.initMocks(this);
Context context = getContext();
soundTriggerService = ISoundTriggerService.Stub.asInterface(
ServiceManager.getService(Context.SOUND_TRIGGER_SERVICE));
soundTriggerManager = (SoundTriggerManager) context.getSystemService(
Context.SOUND_TRIGGER_SERVICE);
loadedModelUuids = new HashSet<UUID>();
}
@Override
public void tearDown() throws Exception {
for (UUID modelUuid : loadedModelUuids) {
soundTriggerService.deleteSoundModel(new ParcelUuid(modelUuid));
}
super.tearDown();
}
GenericSoundModel new_sound_model() {
// Create sound model
byte[] data = new byte[1024];
random.nextBytes(data);
UUID modelUuid = UUID.randomUUID();
UUID mVendorUuid = UUID.randomUUID();
return new GenericSoundModel(modelUuid, mVendorUuid, data);
}
@SmallTest
public void testUpdateGenericSoundModel() throws Exception {
GenericSoundModel model = new_sound_model();
// Update sound model
soundTriggerService.updateSoundModel(model);
loadedModelUuids.add(model.getUuid());
// Confirm it was updated
GenericSoundModel returnedModel =
soundTriggerService.getSoundModel(new ParcelUuid(model.getUuid()));
assertEquals(model, returnedModel);
}
@SmallTest
public void testDeleteGenericSoundModel() throws Exception {
GenericSoundModel model = new_sound_model();
// Update sound model
soundTriggerService.updateSoundModel(model);
loadedModelUuids.add(model.getUuid());
// Delete sound model
soundTriggerService.deleteSoundModel(new ParcelUuid(model.getUuid()));
loadedModelUuids.remove(model.getUuid());
// Confirm it was deleted
GenericSoundModel returnedModel =
soundTriggerService.getSoundModel(new ParcelUuid(model.getUuid()));
assertEquals(null, returnedModel);
}
@LargeTest
public void testStartStopGenericSoundModel() throws Exception {
GenericSoundModel model = new_sound_model();
boolean captureTriggerAudio = true;
boolean allowMultipleTriggers = true;
RecognitionConfig config = new RecognitionConfig(captureTriggerAudio, allowMultipleTriggers,
null, null);
TestRecognitionStatusCallback spyCallback = spy(new TestRecognitionStatusCallback());
// Update and start sound model recognition
soundTriggerService.updateSoundModel(model);
loadedModelUuids.add(model.getUuid());
int r = soundTriggerService.startRecognition(new ParcelUuid(model.getUuid()), spyCallback,
config);
assertEquals("Could Not Start Recognition with code: " + r,
android.hardware.soundtrigger.SoundTrigger.STATUS_OK, r);
// Stop recognition
r = soundTriggerService.stopRecognition(new ParcelUuid(model.getUuid()), spyCallback);
assertEquals("Could Not Stop Recognition with code: " + r,
android.hardware.soundtrigger.SoundTrigger.STATUS_OK, r);
}
@LargeTest
public void testTriggerGenericSoundModel() throws Exception {
GenericSoundModel model = new_sound_model();
boolean captureTriggerAudio = true;
boolean allowMultipleTriggers = true;
RecognitionConfig config = new RecognitionConfig(captureTriggerAudio, allowMultipleTriggers,
null, null);
TestRecognitionStatusCallback spyCallback = spy(new TestRecognitionStatusCallback());
// Update and start sound model
soundTriggerService.updateSoundModel(model);
loadedModelUuids.add(model.getUuid());
soundTriggerService.startRecognition(new ParcelUuid(model.getUuid()), spyCallback, config);
// Send trigger to stub HAL
Socket socket = new Socket(InetAddress.getLocalHost(), 14035);
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
out.writeBytes("trig " + model.getUuid().toString() + "\r\n");
out.flush();
socket.close();
// Verify trigger was received
verify(spyCallback, timeout(100)).onGenericSoundTriggerDetected(any());
}
/**
* Tests a more complicated pattern of loading, unloading, triggering, starting and stopping
* recognition. Intended to find unexpected errors that occur in unexpected states.
*/
@LargeTest
public void testFuzzGenericSoundModel() throws Exception {
int numModels = 2;
final int STATUS_UNLOADED = 0;
final int STATUS_LOADED = 1;
final int STATUS_STARTED = 2;
class ModelInfo {
int status;
GenericSoundModel model;
public ModelInfo(GenericSoundModel model, int status) {
this.status = status;
this.model = model;
}
}
Random predictableRandom = new Random(100);
ArrayList modelInfos = new ArrayList<ModelInfo>();
for(int i=0; i<numModels; i++) {
// Create sound model
byte[] data = new byte[1024];
predictableRandom.nextBytes(data);
UUID modelUuid = UUID.randomUUID();
UUID mVendorUuid = UUID.randomUUID();
GenericSoundModel model = new GenericSoundModel(modelUuid, mVendorUuid, data);
ModelInfo modelInfo = new ModelInfo(model, STATUS_UNLOADED);
modelInfos.add(modelInfo);
}
boolean captureTriggerAudio = true;
boolean allowMultipleTriggers = true;
RecognitionConfig config = new RecognitionConfig(captureTriggerAudio, allowMultipleTriggers,
null, null);
TestRecognitionStatusCallback spyCallback = spy(new TestRecognitionStatusCallback());
int numOperationsToRun = 100;
for(int i=0; i<numOperationsToRun; i++) {
// Select a random model
int modelInfoIndex = predictableRandom.nextInt(modelInfos.size());
ModelInfo modelInfo = (ModelInfo) modelInfos.get(modelInfoIndex);
// Perform a random operation
int operation = predictableRandom.nextInt(5);
if (operation == 0 && modelInfo.status == STATUS_UNLOADED) {
// Update and start sound model
soundTriggerService.updateSoundModel(modelInfo.model);
loadedModelUuids.add(modelInfo.model.getUuid());
modelInfo.status = STATUS_LOADED;
} else if (operation == 1 && modelInfo.status == STATUS_LOADED) {
// Start the sound model
int r = soundTriggerService.startRecognition(new ParcelUuid(
modelInfo.model.getUuid()),
spyCallback, config);
assertEquals("Could Not Start Recognition with code: " + r,
android.hardware.soundtrigger.SoundTrigger.STATUS_OK, r);
modelInfo.status = STATUS_STARTED;
} else if (operation == 2 && modelInfo.status == STATUS_STARTED) {
// Send trigger to stub HAL
Socket socket = new Socket(InetAddress.getLocalHost(), 14035);
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
out.writeBytes("trig " + modelInfo.model.getUuid() + "\r\n");
out.flush();
socket.close();
// Verify trigger was received
verify(spyCallback, timeout(100)).onGenericSoundTriggerDetected(any());
reset(spyCallback);
} else if (operation == 3 && modelInfo.status == STATUS_STARTED) {
// Stop recognition
int r = soundTriggerService.stopRecognition(new ParcelUuid(
modelInfo.model.getUuid()),
spyCallback);
assertEquals("Could Not Stop Recognition with code: " + r,
android.hardware.soundtrigger.SoundTrigger.STATUS_OK, r);
modelInfo.status = STATUS_LOADED;
} else if (operation == 4 && modelInfo.status != STATUS_UNLOADED) {
// Delete sound model
soundTriggerService.deleteSoundModel(new ParcelUuid(modelInfo.model.getUuid()));
loadedModelUuids.remove(modelInfo.model.getUuid());
// Confirm it was deleted
GenericSoundModel returnedModel = soundTriggerService.getSoundModel(
new ParcelUuid(modelInfo.model.getUuid()));
assertEquals(null, returnedModel);
modelInfo.status = STATUS_UNLOADED;
}
}
}
public class TestRecognitionStatusCallback extends IRecognitionStatusCallback.Stub {
@Override
public void onGenericSoundTriggerDetected(GenericRecognitionEvent recognitionEvent) {
}
@Override
public void onKeyphraseDetected(KeyphraseRecognitionEvent recognitionEvent) {
}
@Override
public void onError(int status) {
}
@Override
public void onRecognitionPaused() {
}
@Override
public void onRecognitionResumed() {
}
}
}