• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package android.hardware.soundtrigger;
18 
19 import static org.mockito.Matchers.any;
20 import static org.mockito.Mockito.reset;
21 import static org.mockito.Mockito.spy;
22 import static org.mockito.Mockito.timeout;
23 import static org.mockito.Mockito.verify;
24 
25 import android.content.Context;
26 import android.hardware.soundtrigger.SoundTrigger.GenericRecognitionEvent;
27 import android.hardware.soundtrigger.SoundTrigger.GenericSoundModel;
28 import android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionEvent;
29 import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
30 import android.media.soundtrigger.SoundTriggerManager;
31 import android.os.ParcelUuid;
32 import android.os.ServiceManager;
33 import android.test.AndroidTestCase;
34 import android.test.suitebuilder.annotation.LargeTest;
35 import android.test.suitebuilder.annotation.SmallTest;
36 
37 import com.android.internal.app.ISoundTriggerService;
38 
39 import org.mockito.MockitoAnnotations;
40 
41 import java.io.DataOutputStream;
42 import java.net.InetAddress;
43 import java.net.Socket;
44 import java.util.ArrayList;
45 import java.util.HashSet;
46 import java.util.Random;
47 import java.util.UUID;
48 
49 public class GenericSoundModelTest extends AndroidTestCase {
50     static final int MSG_DETECTION_ERROR = -1;
51     static final int MSG_DETECTION_RESUME = 0;
52     static final int MSG_DETECTION_PAUSE = 1;
53     static final int MSG_KEYPHRASE_TRIGGER = 2;
54     static final int MSG_GENERIC_TRIGGER = 4;
55 
56     private Random random = new Random();
57     private HashSet<UUID> loadedModelUuids;
58     private ISoundTriggerService soundTriggerService;
59     private SoundTriggerManager soundTriggerManager;
60 
61     @Override
setUp()62     public void setUp() throws Exception {
63         super.setUp();
64         MockitoAnnotations.initMocks(this);
65 
66         Context context = getContext();
67         soundTriggerService = ISoundTriggerService.Stub.asInterface(
68                 ServiceManager.getService(Context.SOUND_TRIGGER_SERVICE));
69         soundTriggerManager = (SoundTriggerManager) context.getSystemService(
70                 Context.SOUND_TRIGGER_SERVICE);
71 
72         loadedModelUuids = new HashSet<UUID>();
73     }
74 
75     @Override
tearDown()76     public void tearDown() throws Exception {
77         for (UUID modelUuid : loadedModelUuids) {
78             soundTriggerService.deleteSoundModel(new ParcelUuid(modelUuid));
79         }
80         super.tearDown();
81     }
82 
new_sound_model()83     GenericSoundModel new_sound_model() {
84         // Create sound model
85         byte[] data = new byte[1024];
86         random.nextBytes(data);
87         UUID modelUuid = UUID.randomUUID();
88         UUID mVendorUuid = UUID.randomUUID();
89         return new GenericSoundModel(modelUuid, mVendorUuid, data);
90     }
91 
92     @SmallTest
testUpdateGenericSoundModel()93     public void testUpdateGenericSoundModel() throws Exception {
94         GenericSoundModel model = new_sound_model();
95 
96         // Update sound model
97         soundTriggerService.updateSoundModel(model);
98         loadedModelUuids.add(model.getUuid());
99 
100         // Confirm it was updated
101         GenericSoundModel returnedModel =
102                 soundTriggerService.getSoundModel(new ParcelUuid(model.getUuid()));
103         assertEquals(model, returnedModel);
104     }
105 
106     @SmallTest
testDeleteGenericSoundModel()107     public void testDeleteGenericSoundModel() throws Exception {
108         GenericSoundModel model = new_sound_model();
109 
110         // Update sound model
111         soundTriggerService.updateSoundModel(model);
112         loadedModelUuids.add(model.getUuid());
113 
114         // Delete sound model
115         soundTriggerService.deleteSoundModel(new ParcelUuid(model.getUuid()));
116         loadedModelUuids.remove(model.getUuid());
117 
118         // Confirm it was deleted
119         GenericSoundModel returnedModel =
120                 soundTriggerService.getSoundModel(new ParcelUuid(model.getUuid()));
121         assertEquals(null, returnedModel);
122     }
123 
124     @LargeTest
testStartStopGenericSoundModel()125     public void testStartStopGenericSoundModel() throws Exception {
126         GenericSoundModel model = new_sound_model();
127 
128         boolean captureTriggerAudio = true;
129         boolean allowMultipleTriggers = true;
130         RecognitionConfig config = new RecognitionConfig(captureTriggerAudio, allowMultipleTriggers,
131                 null, null);
132         TestRecognitionStatusCallback spyCallback = spy(new TestRecognitionStatusCallback());
133 
134         // Update and start sound model recognition
135         soundTriggerService.updateSoundModel(model);
136         loadedModelUuids.add(model.getUuid());
137         int r = soundTriggerService.startRecognition(new ParcelUuid(model.getUuid()), spyCallback,
138                 config);
139         assertEquals("Could Not Start Recognition with code: " + r,
140                 android.hardware.soundtrigger.SoundTrigger.STATUS_OK, r);
141 
142         // Stop recognition
143         r = soundTriggerService.stopRecognition(new ParcelUuid(model.getUuid()), spyCallback);
144         assertEquals("Could Not Stop Recognition with code: " + r,
145                 android.hardware.soundtrigger.SoundTrigger.STATUS_OK, r);
146     }
147 
148     @LargeTest
testTriggerGenericSoundModel()149     public void testTriggerGenericSoundModel() throws Exception {
150         GenericSoundModel model = new_sound_model();
151 
152         boolean captureTriggerAudio = true;
153         boolean allowMultipleTriggers = true;
154         RecognitionConfig config = new RecognitionConfig(captureTriggerAudio, allowMultipleTriggers,
155                 null, null);
156         TestRecognitionStatusCallback spyCallback = spy(new TestRecognitionStatusCallback());
157 
158         // Update and start sound model
159         soundTriggerService.updateSoundModel(model);
160         loadedModelUuids.add(model.getUuid());
161         soundTriggerService.startRecognition(new ParcelUuid(model.getUuid()), spyCallback, config);
162 
163         // Send trigger to stub HAL
164         Socket socket = new Socket(InetAddress.getLocalHost(), 14035);
165         DataOutputStream out = new DataOutputStream(socket.getOutputStream());
166         out.writeBytes("trig " + model.getUuid().toString() + "\r\n");
167         out.flush();
168         socket.close();
169 
170         // Verify trigger was received
171         verify(spyCallback, timeout(100)).onGenericSoundTriggerDetected(any());
172     }
173 
174     /**
175      * Tests a more complicated pattern of loading, unloading, triggering, starting and stopping
176      * recognition. Intended to find unexpected errors that occur in unexpected states.
177      */
178     @LargeTest
testFuzzGenericSoundModel()179     public void testFuzzGenericSoundModel() throws Exception {
180         int numModels = 2;
181 
182         final int STATUS_UNLOADED = 0;
183         final int STATUS_LOADED = 1;
184         final int STATUS_STARTED = 2;
185 
186         class ModelInfo {
187             int status;
188             GenericSoundModel model;
189 
190             public ModelInfo(GenericSoundModel model, int status) {
191                 this.status = status;
192                 this.model = model;
193             }
194         }
195 
196         Random predictableRandom = new Random(100);
197 
198         ArrayList modelInfos = new ArrayList<ModelInfo>();
199         for(int i=0; i<numModels; i++) {
200             // Create sound model
201             byte[] data = new byte[1024];
202             predictableRandom.nextBytes(data);
203             UUID modelUuid = UUID.randomUUID();
204             UUID mVendorUuid = UUID.randomUUID();
205             GenericSoundModel model = new GenericSoundModel(modelUuid, mVendorUuid, data);
206             ModelInfo modelInfo = new ModelInfo(model, STATUS_UNLOADED);
207             modelInfos.add(modelInfo);
208         }
209 
210         boolean captureTriggerAudio = true;
211         boolean allowMultipleTriggers = true;
212         RecognitionConfig config = new RecognitionConfig(captureTriggerAudio, allowMultipleTriggers,
213                 null, null);
214         TestRecognitionStatusCallback spyCallback = spy(new TestRecognitionStatusCallback());
215 
216 
217         int numOperationsToRun = 100;
218         for(int i=0; i<numOperationsToRun; i++) {
219             // Select a random model
220             int modelInfoIndex = predictableRandom.nextInt(modelInfos.size());
221             ModelInfo modelInfo = (ModelInfo) modelInfos.get(modelInfoIndex);
222 
223             // Perform a random operation
224             int operation = predictableRandom.nextInt(5);
225 
226             if (operation == 0 && modelInfo.status == STATUS_UNLOADED) {
227                 // Update and start sound model
228                 soundTriggerService.updateSoundModel(modelInfo.model);
229                 loadedModelUuids.add(modelInfo.model.getUuid());
230                 modelInfo.status = STATUS_LOADED;
231             } else if (operation == 1 && modelInfo.status == STATUS_LOADED) {
232                 // Start the sound model
233                 int r = soundTriggerService.startRecognition(new ParcelUuid(
234                                 modelInfo.model.getUuid()),
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.getUuid() + "\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(
253                                 modelInfo.model.getUuid()),
254                         spyCallback);
255                 assertEquals("Could Not Stop Recognition with code: " + r,
256                         android.hardware.soundtrigger.SoundTrigger.STATUS_OK, r);
257                 modelInfo.status = STATUS_LOADED;
258             } else if (operation == 4 && modelInfo.status != STATUS_UNLOADED) {
259                 // Delete sound model
260                 soundTriggerService.deleteSoundModel(new ParcelUuid(modelInfo.model.getUuid()));
261                 loadedModelUuids.remove(modelInfo.model.getUuid());
262 
263                 // Confirm it was deleted
264                 GenericSoundModel returnedModel = soundTriggerService.getSoundModel(
265                         new ParcelUuid(modelInfo.model.getUuid()));
266                 assertEquals(null, returnedModel);
267                 modelInfo.status = STATUS_UNLOADED;
268             }
269         }
270     }
271 
272     public class TestRecognitionStatusCallback extends IRecognitionStatusCallback.Stub {
273         @Override
onGenericSoundTriggerDetected(GenericRecognitionEvent recognitionEvent)274         public void onGenericSoundTriggerDetected(GenericRecognitionEvent recognitionEvent) {
275         }
276 
277         @Override
onKeyphraseDetected(KeyphraseRecognitionEvent recognitionEvent)278         public void onKeyphraseDetected(KeyphraseRecognitionEvent recognitionEvent) {
279         }
280 
281         @Override
onError(int status)282         public void onError(int status) {
283         }
284 
285         @Override
onRecognitionPaused()286         public void onRecognitionPaused() {
287         }
288 
289         @Override
onRecognitionResumed()290         public void onRecognitionResumed() {
291         }
292     }
293 }
294