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:
@@ -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)));
|
||||
}
|
||||
}
|
||||
@@ -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 */);
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
@@ -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>
|
||||
@@ -1 +0,0 @@
|
||||
include /media/java/android/media/soundtrigger/OWNERS
|
||||
@@ -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() {
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user