blob: c0583ceb2fabfff31aa750a479aafb73fcdf55de [file] [log] [blame]
Ryan Bavettafc3ad3e2016-03-03 21:47:35 -08001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.hardware.soundtrigger;
18
Ryan Bavettac92bbd72016-03-06 20:50:10 -080019import static org.mockito.Matchers.any;
20import static org.mockito.Matchers.eq;
Ryan Bavettaa0319cb2016-03-28 12:52:02 -070021import static org.mockito.Mockito.reset;
Ryan Bavettac92bbd72016-03-06 20:50:10 -080022import static org.mockito.Mockito.spy;
23import static org.mockito.Mockito.timeout;
24import static org.mockito.Mockito.verify;
Ryan Bavettafc3ad3e2016-03-03 21:47:35 -080025
26import android.content.Context;
Ryan Bavettac92bbd72016-03-06 20:50:10 -080027import android.hardware.soundtrigger.SoundTrigger.GenericRecognitionEvent;
Ryan Bavettafc3ad3e2016-03-03 21:47:35 -080028import android.hardware.soundtrigger.SoundTrigger.GenericSoundModel;
Ryan Bavettac92bbd72016-03-06 20:50:10 -080029import android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionEvent;
30import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
Ryan Bavettafc3ad3e2016-03-03 21:47:35 -080031import android.media.soundtrigger.SoundTriggerManager;
32import android.os.ParcelUuid;
33import android.os.ServiceManager;
34import android.test.AndroidTestCase;
Ryan Bavettac92bbd72016-03-06 20:50:10 -080035import android.test.suitebuilder.annotation.LargeTest;
Ryan Bavettafc3ad3e2016-03-03 21:47:35 -080036import android.test.suitebuilder.annotation.SmallTest;
37
38import com.android.internal.app.ISoundTriggerService;
39
Ryan Bavettac92bbd72016-03-06 20:50:10 -080040import java.io.DataOutputStream;
41import java.net.InetAddress;
42import java.net.Socket;
43import java.util.ArrayList;
Ryan Bavettaa0319cb2016-03-28 12:52:02 -070044import java.util.HashSet;
Ryan Bavettafc3ad3e2016-03-03 21:47:35 -080045import java.util.Random;
46import java.util.UUID;
47
Ryan Bavettac92bbd72016-03-06 20:50:10 -080048import org.mockito.MockitoAnnotations;
49
Ryan Bavettafc3ad3e2016-03-03 21:47:35 -080050public class GenericSoundModelTest extends AndroidTestCase {
Ryan Bavettac92bbd72016-03-06 20:50:10 -080051 static final int MSG_DETECTION_ERROR = -1;
52 static final int MSG_DETECTION_RESUME = 0;
53 static final int MSG_DETECTION_PAUSE = 1;
54 static final int MSG_KEYPHRASE_TRIGGER = 2;
55 static final int MSG_GENERIC_TRIGGER = 4;
56
57 private Random random = new Random();
Ryan Bavettaa0319cb2016-03-28 12:52:02 -070058 private HashSet<UUID> loadedModelUuids;
Ryan Bavettac92bbd72016-03-06 20:50:10 -080059 private ISoundTriggerService soundTriggerService;
60 private SoundTriggerManager soundTriggerManager;
61
62 @Override
63 public void setUp() throws Exception {
64 super.setUp();
65 MockitoAnnotations.initMocks(this);
66
67 Context context = getContext();
68 soundTriggerService = ISoundTriggerService.Stub.asInterface(
69 ServiceManager.getService(Context.SOUND_TRIGGER_SERVICE));
70 soundTriggerManager = (SoundTriggerManager) context.getSystemService(
71 Context.SOUND_TRIGGER_SERVICE);
72
Ryan Bavettaa0319cb2016-03-28 12:52:02 -070073 loadedModelUuids = new HashSet<UUID>();
Ryan Bavettac92bbd72016-03-06 20:50:10 -080074 }
75
76 @Override
77 public void tearDown() throws Exception {
78 for (UUID modelUuid : loadedModelUuids) {
79 soundTriggerService.deleteSoundModel(new ParcelUuid(modelUuid));
80 }
81 super.tearDown();
82 }
83
84 GenericSoundModel new_sound_model() {
85 // Create sound model
86 byte[] data = new byte[1024];
87 random.nextBytes(data);
88 UUID modelUuid = UUID.randomUUID();
89 UUID mVendorUuid = UUID.randomUUID();
90 return new GenericSoundModel(modelUuid, mVendorUuid, data);
91 }
Ryan Bavettafc3ad3e2016-03-03 21:47:35 -080092
93 @SmallTest
94 public void testUpdateGenericSoundModel() throws Exception {
Ryan Bavettac92bbd72016-03-06 20:50:10 -080095 GenericSoundModel model = new_sound_model();
Ryan Bavettafc3ad3e2016-03-03 21:47:35 -080096
Ryan Bavettac92bbd72016-03-06 20:50:10 -080097 // Update sound model
98 soundTriggerService.updateSoundModel(model);
99 loadedModelUuids.add(model.uuid);
Ryan Bavettafc3ad3e2016-03-03 21:47:35 -0800100
Ryan Bavettac92bbd72016-03-06 20:50:10 -0800101 // Confirm it was updated
Ryan Bavettafc3ad3e2016-03-03 21:47:35 -0800102 GenericSoundModel returnedModel =
Ryan Bavettac92bbd72016-03-06 20:50:10 -0800103 soundTriggerService.getSoundModel(new ParcelUuid(model.uuid));
Ryan Bavettafc3ad3e2016-03-03 21:47:35 -0800104 assertEquals(model, returnedModel);
Ryan Bavettafc3ad3e2016-03-03 21:47:35 -0800105 }
106
Ryan Bavettafc3ad3e2016-03-03 21:47:35 -0800107 @SmallTest
108 public void testDeleteGenericSoundModel() throws Exception {
Ryan Bavettac92bbd72016-03-06 20:50:10 -0800109 GenericSoundModel model = new_sound_model();
Ryan Bavettafc3ad3e2016-03-03 21:47:35 -0800110
Ryan Bavettac92bbd72016-03-06 20:50:10 -0800111 // Update sound model
112 soundTriggerService.updateSoundModel(model);
113 loadedModelUuids.add(model.uuid);
Ryan Bavettafc3ad3e2016-03-03 21:47:35 -0800114
Ryan Bavettac92bbd72016-03-06 20:50:10 -0800115 // Delete sound model
116 soundTriggerService.deleteSoundModel(new ParcelUuid(model.uuid));
117 loadedModelUuids.remove(model.uuid);
Ryan Bavettafc3ad3e2016-03-03 21:47:35 -0800118
Ryan Bavettac92bbd72016-03-06 20:50:10 -0800119 // Confirm it was deleted
Ryan Bavettafc3ad3e2016-03-03 21:47:35 -0800120 GenericSoundModel returnedModel =
Ryan Bavettac92bbd72016-03-06 20:50:10 -0800121 soundTriggerService.getSoundModel(new ParcelUuid(model.uuid));
Ryan Bavettafc3ad3e2016-03-03 21:47:35 -0800122 assertEquals(null, returnedModel);
123 }
Ryan Bavettac92bbd72016-03-06 20:50:10 -0800124
125 @LargeTest
126 public void testStartStopGenericSoundModel() throws Exception {
127 GenericSoundModel model = new_sound_model();
128
129 boolean captureTriggerAudio = true;
130 boolean allowMultipleTriggers = true;
131 RecognitionConfig config = new RecognitionConfig(captureTriggerAudio, allowMultipleTriggers,
132 null, null);
133 TestRecognitionStatusCallback spyCallback = spy(new TestRecognitionStatusCallback());
134
135 // Update and start sound model recognition
136 soundTriggerService.updateSoundModel(model);
137 loadedModelUuids.add(model.uuid);
138 int r = soundTriggerService.startRecognition(new ParcelUuid(model.uuid), spyCallback,
139 config);
140 assertEquals("Could Not Start Recognition with code: " + r,
141 android.hardware.soundtrigger.SoundTrigger.STATUS_OK, r);
142
143 // Stop recognition
144 r = soundTriggerService.stopRecognition(new ParcelUuid(model.uuid), spyCallback);
145 assertEquals("Could Not Stop Recognition with code: " + r,
146 android.hardware.soundtrigger.SoundTrigger.STATUS_OK, r);
147 }
148
149 @LargeTest
150 public void testTriggerGenericSoundModel() throws Exception {
151 GenericSoundModel model = new_sound_model();
152
153 boolean captureTriggerAudio = true;
154 boolean allowMultipleTriggers = true;
155 RecognitionConfig config = new RecognitionConfig(captureTriggerAudio, allowMultipleTriggers,
156 null, null);
157 TestRecognitionStatusCallback spyCallback = spy(new TestRecognitionStatusCallback());
158
159 // Update and start sound model
160 soundTriggerService.updateSoundModel(model);
161 loadedModelUuids.add(model.uuid);
162 soundTriggerService.startRecognition(new ParcelUuid(model.uuid), spyCallback, config);
163
164 // Send trigger to stub HAL
165 Socket socket = new Socket(InetAddress.getLocalHost(), 14035);
166 DataOutputStream out = new DataOutputStream(socket.getOutputStream());
167 out.writeBytes("trig " + model.uuid.toString() + "\r\n");
168 out.flush();
169 socket.close();
170
171 // Verify trigger was received
172 verify(spyCallback, timeout(100)).onGenericSoundTriggerDetected(any());
173 }
174
Ryan Bavettaa0319cb2016-03-28 12:52:02 -0700175 /**
176 * Tests a more complicated pattern of loading, unloading, triggering, starting and stopping
177 * recognition. Intended to find unexpected errors that occur in unexpected states.
178 */
179 @LargeTest
180 public void testFuzzGenericSoundModel() throws Exception {
181 int numModels = 2;
182
183 final int STATUS_UNLOADED = 0;
184 final int STATUS_LOADED = 1;
185 final int STATUS_STARTED = 2;
186
187 class ModelInfo {
188 int status;
189 GenericSoundModel model;
190
191 public ModelInfo(GenericSoundModel model, int status) {
192 this.status = status;
193 this.model = model;
194 }
195 }
196
197 Random predictableRandom = new Random(100);
198
199 ArrayList modelInfos = new ArrayList<ModelInfo>();
200 for(int i=0; i<numModels; i++) {
201 // Create sound model
202 byte[] data = new byte[1024];
203 predictableRandom.nextBytes(data);
204 UUID modelUuid = UUID.randomUUID();
205 UUID mVendorUuid = UUID.randomUUID();
206 GenericSoundModel model = new GenericSoundModel(modelUuid, mVendorUuid, data);
207 ModelInfo modelInfo = new ModelInfo(model, STATUS_UNLOADED);
208 modelInfos.add(modelInfo);
209 }
210
211 boolean captureTriggerAudio = true;
212 boolean allowMultipleTriggers = true;
213 RecognitionConfig config = new RecognitionConfig(captureTriggerAudio, allowMultipleTriggers,
214 null, null);
215 TestRecognitionStatusCallback spyCallback = spy(new TestRecognitionStatusCallback());
216
217
218 int numOperationsToRun = 100;
219 for(int i=0; i<numOperationsToRun; i++) {
220 // Select a random model
221 int modelInfoIndex = predictableRandom.nextInt(modelInfos.size());
222 ModelInfo modelInfo = (ModelInfo) modelInfos.get(modelInfoIndex);
223
224 // Perform a random operation
225 int operation = predictableRandom.nextInt(5);
226
227 if (operation == 0 && modelInfo.status == STATUS_UNLOADED) {
228 // Update and start sound model
229 soundTriggerService.updateSoundModel(modelInfo.model);
230 loadedModelUuids.add(modelInfo.model.uuid);
231 modelInfo.status = STATUS_LOADED;
232 } else if (operation == 1 && modelInfo.status == STATUS_LOADED) {
233 // Start the sound model
234 int r = soundTriggerService.startRecognition(new ParcelUuid(modelInfo.model.uuid),
235 spyCallback, config);
236 assertEquals("Could Not Start Recognition with code: " + r,
237 android.hardware.soundtrigger.SoundTrigger.STATUS_OK, r);
238 modelInfo.status = STATUS_STARTED;
239 } else if (operation == 2 && modelInfo.status == STATUS_STARTED) {
240 // Send trigger to stub HAL
241 Socket socket = new Socket(InetAddress.getLocalHost(), 14035);
242 DataOutputStream out = new DataOutputStream(socket.getOutputStream());
243 out.writeBytes("trig " + modelInfo.model.uuid + "\r\n");
244 out.flush();
245 socket.close();
246
247 // Verify trigger was received
248 verify(spyCallback, timeout(100)).onGenericSoundTriggerDetected(any());
249 reset(spyCallback);
250 } else if (operation == 3 && modelInfo.status == STATUS_STARTED) {
251 // Stop recognition
252 int r = soundTriggerService.stopRecognition(new ParcelUuid(modelInfo.model.uuid),
253 spyCallback);
254 assertEquals("Could Not Stop Recognition with code: " + r,
255 android.hardware.soundtrigger.SoundTrigger.STATUS_OK, r);
256 modelInfo.status = STATUS_LOADED;
257 } else if (operation == 4 && modelInfo.status != STATUS_UNLOADED) {
258 // Delete sound model
259 soundTriggerService.deleteSoundModel(new ParcelUuid(modelInfo.model.uuid));
260 loadedModelUuids.remove(modelInfo.model.uuid);
261
262 // Confirm it was deleted
263 GenericSoundModel returnedModel =
264 soundTriggerService.getSoundModel(new ParcelUuid(modelInfo.model.uuid));
265 assertEquals(null, returnedModel);
266 modelInfo.status = STATUS_UNLOADED;
267 }
268 }
269 }
Ryan Bavettac92bbd72016-03-06 20:50:10 -0800270
271 public class TestRecognitionStatusCallback extends IRecognitionStatusCallback.Stub {
272 @Override
273 public void onGenericSoundTriggerDetected(GenericRecognitionEvent recognitionEvent) {
274 }
275
276 @Override
277 public void onKeyphraseDetected(KeyphraseRecognitionEvent recognitionEvent) {
278 }
279
280 @Override
281 public void onError(int status) {
282 }
283
284 @Override
285 public void onRecognitionPaused() {
286 }
287
288 @Override
289 public void onRecognitionResumed() {
290 }
291 }
Ryan Bavettafc3ad3e2016-03-03 21:47:35 -0800292}