blob: 82f32f88d3a2e01bb538d37b8cba09d04a3ecf23 [file] [log] [blame]
Ytai Ben-Tsvi93c117c862019-11-25 12:43:28 -08001/*
2 * Copyright (C) 2019 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 com.android.server.soundtrigger_middleware;
18
19import static org.junit.Assert.assertArrayEquals;
20import static org.junit.Assert.assertEquals;
21import static org.junit.Assert.assertNotNull;
22import static org.junit.Assert.assertNull;
23import static org.junit.Assert.assertTrue;
24import static org.mockito.ArgumentMatchers.any;
25import static org.mockito.ArgumentMatchers.anyBoolean;
26import static org.mockito.ArgumentMatchers.anyInt;
27import static org.mockito.ArgumentMatchers.eq;
28import static org.mockito.Mockito.clearInvocations;
29import static org.mockito.Mockito.doAnswer;
30import static org.mockito.Mockito.mock;
31import static org.mockito.Mockito.never;
32import static org.mockito.Mockito.verify;
33import static org.mockito.Mockito.when;
34
35import android.hardware.audio.common.V2_0.AudioConfig;
36import android.hardware.audio.common.V2_0.Uuid;
37import android.hardware.soundtrigger.V2_3.OptionalModelParameterRange;
38import android.media.audio.common.AudioChannelMask;
39import android.media.audio.common.AudioFormat;
40import android.media.soundtrigger_middleware.ConfidenceLevel;
41import android.media.soundtrigger_middleware.ISoundTriggerCallback;
42import android.media.soundtrigger_middleware.ISoundTriggerModule;
43import android.media.soundtrigger_middleware.ModelParameter;
44import android.media.soundtrigger_middleware.ModelParameterRange;
45import android.media.soundtrigger_middleware.Phrase;
46import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
47import android.media.soundtrigger_middleware.PhraseRecognitionExtra;
48import android.media.soundtrigger_middleware.PhraseSoundModel;
49import android.media.soundtrigger_middleware.RecognitionConfig;
50import android.media.soundtrigger_middleware.RecognitionEvent;
51import android.media.soundtrigger_middleware.RecognitionMode;
52import android.media.soundtrigger_middleware.RecognitionStatus;
53import android.media.soundtrigger_middleware.SoundModel;
54import android.media.soundtrigger_middleware.SoundModelType;
55import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
56import android.media.soundtrigger_middleware.SoundTriggerModuleProperties;
57import android.os.HidlMemoryUtil;
58import android.os.HwParcel;
59import android.os.IHwBinder;
60import android.os.IHwInterface;
61import android.os.RemoteException;
62
63import androidx.test.runner.AndroidJUnit4;
64
65import org.junit.Before;
66import org.junit.Test;
67import org.junit.runner.RunWith;
68import org.junit.runners.Parameterized;
69import org.mockito.ArgumentCaptor;
70import org.mockito.Mock;
71import org.mockito.Mockito;
72import org.mockito.stubbing.Answer;
73
74@RunWith(Parameterized.class)
75public class SoundTriggerMiddlewareImplTest {
76 private static final String TAG = "SoundTriggerMiddlewareImplTest";
77
78 // We run the test once for every version of the underlying driver.
79 @Parameterized.Parameters
80 public static Object[] data() {
81 return new Object[]{
82 mock(android.hardware.soundtrigger.V2_0.ISoundTriggerHw.class),
83 mock(android.hardware.soundtrigger.V2_1.ISoundTriggerHw.class),
84 mock(android.hardware.soundtrigger.V2_2.ISoundTriggerHw.class),
85 mock(android.hardware.soundtrigger.V2_3.ISoundTriggerHw.class),
86 };
87 }
88
89 @Mock
90 @Parameterized.Parameter
91 public android.hardware.soundtrigger.V2_0.ISoundTriggerHw mHalDriver;
92
93 @Mock
94 private SoundTriggerMiddlewareImpl.AudioSessionProvider mAudioSessionProvider = mock(
95 SoundTriggerMiddlewareImpl.AudioSessionProvider.class);
96
97 private SoundTriggerMiddlewareImpl mService;
98
99 private static ISoundTriggerCallback createCallbackMock() {
100 return mock(ISoundTriggerCallback.Stub.class, Mockito.CALLS_REAL_METHODS);
101 }
102
103 private static SoundModel createGenericSoundModel() {
104 return createSoundModel(SoundModelType.GENERIC);
105 }
106
107 private static SoundModel createSoundModel(int type) {
108 SoundModel model = new SoundModel();
109 model.type = type;
110 model.uuid = "12345678-2345-3456-4567-abcdef987654";
111 model.vendorUuid = "87654321-5432-6543-7654-456789fedcba";
112 model.data = new byte[]{91, 92, 93, 94, 95};
113 return model;
114 }
115
116 private static PhraseSoundModel createPhraseSoundModel() {
117 PhraseSoundModel model = new PhraseSoundModel();
118 model.common = createSoundModel(SoundModelType.KEYPHRASE);
119 model.phrases = new Phrase[1];
120 model.phrases[0] = new Phrase();
121 model.phrases[0].id = 123;
122 model.phrases[0].users = new int[]{5, 6, 7};
123 model.phrases[0].locale = "locale";
124 model.phrases[0].text = "text";
125 model.phrases[0].recognitionModes =
126 RecognitionMode.USER_AUTHENTICATION | RecognitionMode.USER_IDENTIFICATION;
127 return model;
128 }
129
130 private static android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties createDefaultProperties(
131 boolean supportConcurrentCapture) {
132 android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties properties =
133 new android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties();
134 properties.implementor = "implementor";
135 properties.description = "description";
136 properties.version = 123;
137 properties.uuid = new Uuid();
138 properties.uuid.timeLow = 1;
139 properties.uuid.timeMid = 2;
140 properties.uuid.versionAndTimeHigh = 3;
141 properties.uuid.variantAndClockSeqHigh = 4;
142 properties.uuid.node = new byte[]{5, 6, 7, 8, 9, 10};
143
144 properties.maxSoundModels = 456;
145 properties.maxKeyPhrases = 567;
146 properties.maxUsers = 678;
147 properties.recognitionModes = 789;
148 properties.captureTransition = true;
149 properties.maxBufferMs = 321;
150 properties.concurrentCapture = supportConcurrentCapture;
151 properties.triggerInEvent = true;
152 properties.powerConsumptionMw = 432;
153 return properties;
154 }
155
156 private static void validateDefaultProperties(SoundTriggerModuleProperties properties,
157 boolean supportConcurrentCapture) {
158 assertEquals("implementor", properties.implementor);
159 assertEquals("description", properties.description);
160 assertEquals(123, properties.version);
161 assertEquals("00000001-0002-0003-0004-05060708090a", properties.uuid);
162 assertEquals(456, properties.maxSoundModels);
163 assertEquals(567, properties.maxKeyPhrases);
164 assertEquals(678, properties.maxUsers);
165 assertEquals(789, properties.recognitionModes);
166 assertTrue(properties.captureTransition);
167 assertEquals(321, properties.maxBufferMs);
168 assertEquals(supportConcurrentCapture, properties.concurrentCapture);
169 assertTrue(properties.triggerInEvent);
170 assertEquals(432, properties.powerConsumptionMw);
171 }
172
173
174 private static android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent createRecognitionEvent_2_0(
175 int hwHandle,
176 int status) {
177 android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent halEvent =
178 new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent();
179 halEvent.status = status;
180 halEvent.type = SoundModelType.GENERIC;
181 halEvent.model = hwHandle;
182 halEvent.captureAvailable = true;
183 // This field is ignored.
184 halEvent.captureSession = 123;
185 halEvent.captureDelayMs = 234;
186 halEvent.capturePreambleMs = 345;
187 halEvent.triggerInData = true;
188 halEvent.audioConfig = new AudioConfig();
189 halEvent.audioConfig.sampleRateHz = 456;
190 halEvent.audioConfig.channelMask = AudioChannelMask.IN_LEFT;
191 halEvent.audioConfig.format = AudioFormat.MP3;
192 // hwEvent.audioConfig.offloadInfo is irrelevant.
193 halEvent.data.add((byte) 31);
194 halEvent.data.add((byte) 32);
195 halEvent.data.add((byte) 33);
196 return halEvent;
197 }
198
199 private static android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.RecognitionEvent createRecognitionEvent_2_1(
200 int hwHandle,
201 int status) {
202 android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.RecognitionEvent halEvent =
203 new android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.RecognitionEvent();
204 halEvent.header = createRecognitionEvent_2_0(hwHandle, status);
205 halEvent.header.data.clear();
206 halEvent.data = HidlMemoryUtil.byteArrayToHidlMemory(new byte[]{31, 32, 33});
207 return halEvent;
208 }
209
210 private static void validateRecognitionEvent(RecognitionEvent event, int status) {
211 assertEquals(status, event.status);
212 assertEquals(SoundModelType.GENERIC, event.type);
213 assertTrue(event.captureAvailable);
214 assertEquals(101, event.captureSession);
215 assertEquals(234, event.captureDelayMs);
216 assertEquals(345, event.capturePreambleMs);
217 assertTrue(event.triggerInData);
218 assertEquals(456, event.audioConfig.sampleRateHz);
219 assertEquals(AudioChannelMask.IN_LEFT, event.audioConfig.channelMask);
220 assertEquals(AudioFormat.MP3, event.audioConfig.format);
221 }
222
223 private static android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent createPhraseRecognitionEvent_2_0(
224 int hwHandle, int status) {
225 android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent halEvent =
226 new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent();
227 halEvent.common = createRecognitionEvent_2_0(hwHandle, status);
228
229 android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halExtra =
230 new android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra();
231 halExtra.id = 123;
232 halExtra.confidenceLevel = 52;
233 halExtra.recognitionModes = android.hardware.soundtrigger.V2_0.RecognitionMode.VOICE_TRIGGER
234 | android.hardware.soundtrigger.V2_0.RecognitionMode.GENERIC_TRIGGER;
235 android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel =
236 new android.hardware.soundtrigger.V2_0.ConfidenceLevel();
237 halLevel.userId = 31;
238 halLevel.levelPercent = 43;
239 halExtra.levels.add(halLevel);
240 halEvent.phraseExtras.add(halExtra);
241 return halEvent;
242 }
243
244 private static android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.PhraseRecognitionEvent createPhraseRecognitionEvent_2_1(
245 int hwHandle, int status) {
246 android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.PhraseRecognitionEvent halEvent =
247 new android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.PhraseRecognitionEvent();
248 halEvent.common = createRecognitionEvent_2_1(hwHandle, status);
249
250 android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halExtra =
251 new android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra();
252 halExtra.id = 123;
253 halExtra.confidenceLevel = 52;
254 halExtra.recognitionModes = android.hardware.soundtrigger.V2_0.RecognitionMode.VOICE_TRIGGER
255 | android.hardware.soundtrigger.V2_0.RecognitionMode.GENERIC_TRIGGER;
256 android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel =
257 new android.hardware.soundtrigger.V2_0.ConfidenceLevel();
258 halLevel.userId = 31;
259 halLevel.levelPercent = 43;
260 halExtra.levels.add(halLevel);
261 halEvent.phraseExtras.add(halExtra);
262 return halEvent;
263 }
264
265 private static void validatePhraseRecognitionEvent(PhraseRecognitionEvent event, int status) {
266 validateRecognitionEvent(event.common, status);
267
268 assertEquals(1, event.phraseExtras.length);
269 assertEquals(123, event.phraseExtras[0].id);
270 assertEquals(52, event.phraseExtras[0].confidenceLevel);
271 assertEquals(RecognitionMode.VOICE_TRIGGER | RecognitionMode.GENERIC_TRIGGER,
272 event.phraseExtras[0].recognitionModes);
273 assertEquals(1, event.phraseExtras[0].levels.length);
274 assertEquals(31, event.phraseExtras[0].levels[0].userId);
275 assertEquals(43, event.phraseExtras[0].levels[0].levelPercent);
276 }
277
278 private void initService(boolean supportConcurrentCapture) throws RemoteException {
279 doAnswer(invocation -> {
280 android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties properties =
281 createDefaultProperties(
282 supportConcurrentCapture);
283 ((android.hardware.soundtrigger.V2_0.ISoundTriggerHw.getPropertiesCallback) invocation.getArgument(
284 0)).onValues(0,
285 properties);
286 return null;
287 }).when(mHalDriver).getProperties(any());
288 mService = new SoundTriggerMiddlewareImpl(mHalDriver, mAudioSessionProvider);
289 }
290
291 private int loadGenericModel_2_0(ISoundTriggerModule module, int hwHandle)
292 throws RemoteException {
293 SoundModel model = createGenericSoundModel();
294 ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel> modelCaptor =
295 ArgumentCaptor.forClass(
296 android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel.class);
297 doAnswer(invocation -> {
298 android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback callback =
299 invocation.getArgument(1);
300 int callbackCookie = invocation.getArgument(2);
301 android.hardware.soundtrigger.V2_0.ISoundTriggerHw.loadSoundModelCallback
302 resultCallback = invocation.getArgument(3);
303
304 // This is the return of this method.
305 resultCallback.onValues(0, hwHandle);
306
307 // This is the async mCallback that comes after.
308 android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.ModelEvent modelEvent =
309 new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.ModelEvent();
310 modelEvent.status =
311 android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.SoundModelStatus.UPDATED;
312 modelEvent.model = hwHandle;
313 callback.soundModelCallback(modelEvent, callbackCookie);
314 return null;
315 }).when(mHalDriver).loadSoundModel(modelCaptor.capture(), any(), anyInt(), any());
316
317 when(mAudioSessionProvider.acquireSession()).thenReturn(
318 new SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession(101, 102, 103));
319
320 int handle = module.loadModel(model);
321 verify(mHalDriver).loadSoundModel(any(), any(), anyInt(), any());
322 verify(mAudioSessionProvider).acquireSession();
323
324 android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel hidlModel =
325 modelCaptor.getValue();
326 assertEquals(android.hardware.soundtrigger.V2_0.SoundModelType.GENERIC,
327 hidlModel.type);
328 assertEquals(model.uuid, ConversionUtil.hidl2aidlUuid(hidlModel.uuid));
329 assertEquals(model.vendorUuid, ConversionUtil.hidl2aidlUuid(hidlModel.vendorUuid));
330 assertArrayEquals(new Byte[]{91, 92, 93, 94, 95}, hidlModel.data.toArray());
331
332 return handle;
333 }
334
335 private int loadGenericModel_2_1(ISoundTriggerModule module, int hwHandle)
336 throws RemoteException {
337 android.hardware.soundtrigger.V2_1.ISoundTriggerHw driver =
338 (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
339 SoundModel model = createGenericSoundModel();
340 ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel> modelCaptor =
341 ArgumentCaptor.forClass(
342 android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel.class);
343 doAnswer(invocation -> {
344 android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback callback =
345 invocation.getArgument(1);
346 int callbackCookie = invocation.getArgument(2);
347 android.hardware.soundtrigger.V2_1.ISoundTriggerHw.loadSoundModel_2_1Callback
348 resultCallback = invocation.getArgument(3);
349
350 // This is the return of this method.
351 resultCallback.onValues(0, hwHandle);
352
353 // This is the async mCallback that comes after.
354 android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.ModelEvent modelEvent =
355 new android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.ModelEvent();
356 modelEvent.header.status =
357 android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.SoundModelStatus.UPDATED;
358 modelEvent.header.model = hwHandle;
359 callback.soundModelCallback_2_1(modelEvent, callbackCookie);
360 return null;
361 }).when(driver).loadSoundModel_2_1(modelCaptor.capture(), any(), anyInt(), any());
362
363 when(mAudioSessionProvider.acquireSession()).thenReturn(
364 new SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession(101, 102, 103));
365
366 int handle = module.loadModel(model);
367 verify(driver).loadSoundModel_2_1(any(), any(), anyInt(), any());
368 verify(mAudioSessionProvider).acquireSession();
369
370 android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel hidlModel =
371 modelCaptor.getValue();
372 assertEquals(android.hardware.soundtrigger.V2_0.SoundModelType.GENERIC,
373 hidlModel.header.type);
374 assertEquals(model.uuid, ConversionUtil.hidl2aidlUuid(hidlModel.header.uuid));
375 assertEquals(model.vendorUuid, ConversionUtil.hidl2aidlUuid(hidlModel.header.vendorUuid));
376 assertArrayEquals(new byte[]{91, 92, 93, 94, 95},
377 HidlMemoryUtil.hidlMemoryToByteArray(hidlModel.data));
378
379 return handle;
380 }
381
382 private int loadGenericModel(ISoundTriggerModule module, int hwHandle) throws RemoteException {
383 if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
384 return loadGenericModel_2_1(module, hwHandle);
385 } else {
386 return loadGenericModel_2_0(module, hwHandle);
387 }
388 }
389
390 private int loadPhraseModel_2_0(ISoundTriggerModule module, int hwHandle)
391 throws RemoteException {
392 PhraseSoundModel model = createPhraseSoundModel();
393 ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel>
394 modelCaptor = ArgumentCaptor.forClass(
395 android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel.class);
396 doAnswer(invocation -> {
397 android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback callback =
398 invocation.getArgument(
399 1);
400 int callbackCookie = invocation.getArgument(2);
401 android.hardware.soundtrigger.V2_0.ISoundTriggerHw.loadPhraseSoundModelCallback
402 resultCallback =
403 invocation.getArgument(
404 3);
405
406 // This is the return of this method.
407 resultCallback.onValues(0, hwHandle);
408
409 // This is the async mCallback that comes after.
410 android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.ModelEvent modelEvent =
411 new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.ModelEvent();
412 modelEvent.status =
413 android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.SoundModelStatus.UPDATED;
414 modelEvent.model = hwHandle;
415 callback.soundModelCallback(modelEvent, callbackCookie);
416 return null;
417 }).when(mHalDriver).loadPhraseSoundModel(modelCaptor.capture(), any(), anyInt(), any());
418
419 when(mAudioSessionProvider.acquireSession()).thenReturn(
420 new SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession(101, 102, 103));
421
422 int handle = module.loadPhraseModel(model);
423 verify(mHalDriver).loadPhraseSoundModel(any(), any(), anyInt(), any());
424 verify(mAudioSessionProvider).acquireSession();
425
426 android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel hidlModel =
427 modelCaptor.getValue();
428
429 // Validate common part.
430 assertEquals(android.hardware.soundtrigger.V2_0.SoundModelType.KEYPHRASE,
431 hidlModel.common.type);
432 assertEquals(model.common.uuid, ConversionUtil.hidl2aidlUuid(hidlModel.common.uuid));
433 assertEquals(model.common.vendorUuid,
434 ConversionUtil.hidl2aidlUuid(hidlModel.common.vendorUuid));
435 assertArrayEquals(new Byte[]{91, 92, 93, 94, 95}, hidlModel.common.data.toArray());
436
437 // Validate phrase part.
438 assertEquals(1, hidlModel.phrases.size());
439 android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Phrase hidlPhrase =
440 hidlModel.phrases.get(0);
441 assertEquals(123, hidlPhrase.id);
442 assertArrayEquals(new Integer[]{5, 6, 7}, hidlPhrase.users.toArray());
443 assertEquals("locale", hidlPhrase.locale);
444 assertEquals("text", hidlPhrase.text);
445 assertEquals(android.hardware.soundtrigger.V2_0.RecognitionMode.USER_AUTHENTICATION
446 | android.hardware.soundtrigger.V2_0.RecognitionMode.USER_IDENTIFICATION,
447 hidlPhrase.recognitionModes);
448
449 return handle;
450 }
451
452 private int loadPhraseModel_2_1(ISoundTriggerModule module, int hwHandle)
453 throws RemoteException {
454 android.hardware.soundtrigger.V2_1.ISoundTriggerHw driver =
455 (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
456
457 PhraseSoundModel model = createPhraseSoundModel();
458 ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel>
459 modelCaptor = ArgumentCaptor.forClass(
460 android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel.class);
461 doAnswer(invocation -> {
462 android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback callback =
463 invocation.getArgument(
464 1);
465 int callbackCookie = invocation.getArgument(2);
466 android.hardware.soundtrigger.V2_1.ISoundTriggerHw.loadPhraseSoundModel_2_1Callback
467 resultCallback =
468 invocation.getArgument(
469 3);
470
471 // This is the return of this method.
472 resultCallback.onValues(0, hwHandle);
473
474 // This is the async mCallback that comes after.
475 android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.ModelEvent modelEvent =
476 new android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.ModelEvent();
477 modelEvent.header.status =
478 android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.SoundModelStatus.UPDATED;
479 modelEvent.header.model = hwHandle;
480 callback.soundModelCallback_2_1(modelEvent, callbackCookie);
481 return null;
482 }).when(driver).loadPhraseSoundModel_2_1(modelCaptor.capture(), any(), anyInt(), any());
483
484 when(mAudioSessionProvider.acquireSession()).thenReturn(
485 new SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession(101, 102, 103));
486
487 int handle = module.loadPhraseModel(model);
488 verify(driver).loadPhraseSoundModel_2_1(any(), any(), anyInt(), any());
489 verify(mAudioSessionProvider).acquireSession();
490
491 android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel hidlModel =
492 modelCaptor.getValue();
493
494 // Validate common part.
495 assertEquals(android.hardware.soundtrigger.V2_0.SoundModelType.KEYPHRASE,
496 hidlModel.common.header.type);
497 assertEquals(model.common.uuid, ConversionUtil.hidl2aidlUuid(hidlModel.common.header.uuid));
498 assertEquals(model.common.vendorUuid,
499 ConversionUtil.hidl2aidlUuid(hidlModel.common.header.vendorUuid));
500 assertArrayEquals(new byte[]{91, 92, 93, 94, 95},
501 HidlMemoryUtil.hidlMemoryToByteArray(hidlModel.common.data));
502
503 // Validate phrase part.
504 assertEquals(1, hidlModel.phrases.size());
505 android.hardware.soundtrigger.V2_1.ISoundTriggerHw.Phrase hidlPhrase =
506 hidlModel.phrases.get(0);
507 assertEquals(123, hidlPhrase.id);
508 assertArrayEquals(new Integer[]{5, 6, 7}, hidlPhrase.users.toArray());
509 assertEquals("locale", hidlPhrase.locale);
510 assertEquals("text", hidlPhrase.text);
511 assertEquals(android.hardware.soundtrigger.V2_0.RecognitionMode.USER_AUTHENTICATION
512 | android.hardware.soundtrigger.V2_0.RecognitionMode.USER_IDENTIFICATION,
513 hidlPhrase.recognitionModes);
514
515 return handle;
516 }
517
518 private int loadPhraseModel(ISoundTriggerModule module, int hwHandle) throws RemoteException {
519 if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
520 return loadPhraseModel_2_1(module, hwHandle);
521 } else {
522 return loadPhraseModel_2_0(module, hwHandle);
523 }
524 }
525
526 private void unloadModel(ISoundTriggerModule module, int handle, int hwHandle)
527 throws RemoteException {
528 module.unloadModel(handle);
529 verify(mHalDriver).unloadSoundModel(hwHandle);
530 verify(mAudioSessionProvider).releaseSession(101);
531 }
532
533 private SoundTriggerHwCallback startRecognition_2_0(ISoundTriggerModule module, int handle,
534 int hwHandle) throws RemoteException {
535 ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig>
536 configCaptor = ArgumentCaptor.forClass(
537 android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig.class);
538 ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback> callbackCaptor =
539 ArgumentCaptor.forClass(
540 android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.class);
541 ArgumentCaptor<Integer> cookieCaptor = ArgumentCaptor.forClass(Integer.class);
542
543 when(mHalDriver.startRecognition(eq(hwHandle), configCaptor.capture(),
544 callbackCaptor.capture(), cookieCaptor.capture())).thenReturn(0);
545
546 RecognitionConfig config = createRecognitionConfig();
547
548 module.startRecognition(handle, config);
549 verify(mHalDriver).startRecognition(eq(hwHandle), any(), any(), anyInt());
550
551 android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig halConfig =
552 configCaptor.getValue();
553 assertTrue(halConfig.captureRequested);
554 assertEquals(102, halConfig.captureHandle);
555 assertEquals(103, halConfig.captureDevice);
556 assertEquals(1, halConfig.phrases.size());
557 android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halPhraseExtra =
558 halConfig.phrases.get(0);
559 assertEquals(123, halPhraseExtra.id);
560 assertEquals(4, halPhraseExtra.confidenceLevel);
561 assertEquals(5, halPhraseExtra.recognitionModes);
562 assertEquals(1, halPhraseExtra.levels.size());
563 android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel = halPhraseExtra.levels.get(0);
564 assertEquals(234, halLevel.userId);
565 assertEquals(34, halLevel.levelPercent);
566 assertArrayEquals(new Byte[]{5, 4, 3, 2, 1}, halConfig.data.toArray());
567
568 return new SoundTriggerHwCallback(callbackCaptor.getValue(), cookieCaptor.getValue());
569 }
570
571 private SoundTriggerHwCallback startRecognition_2_1(ISoundTriggerModule module, int handle,
572 int hwHandle) throws RemoteException {
573 android.hardware.soundtrigger.V2_1.ISoundTriggerHw driver =
574 (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
575
576 ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig>
577 configCaptor = ArgumentCaptor.forClass(
578 android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig.class);
579 ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback> callbackCaptor =
580 ArgumentCaptor.forClass(
581 android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.class);
582 ArgumentCaptor<Integer> cookieCaptor = ArgumentCaptor.forClass(Integer.class);
583
584 when(driver.startRecognition_2_1(eq(hwHandle), configCaptor.capture(),
585 callbackCaptor.capture(), cookieCaptor.capture())).thenReturn(0);
586
587 RecognitionConfig config = createRecognitionConfig();
588
589 module.startRecognition(handle, config);
590 verify(driver).startRecognition_2_1(eq(hwHandle), any(), any(), anyInt());
591
592 android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig halConfig =
593 configCaptor.getValue();
594 assertTrue(halConfig.header.captureRequested);
595 assertEquals(102, halConfig.header.captureHandle);
596 assertEquals(103, halConfig.header.captureDevice);
597 assertEquals(1, halConfig.header.phrases.size());
598 android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halPhraseExtra =
599 halConfig.header.phrases.get(0);
600 assertEquals(123, halPhraseExtra.id);
601 assertEquals(4, halPhraseExtra.confidenceLevel);
602 assertEquals(5, halPhraseExtra.recognitionModes);
603 assertEquals(1, halPhraseExtra.levels.size());
604 android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel = halPhraseExtra.levels.get(0);
605 assertEquals(234, halLevel.userId);
606 assertEquals(34, halLevel.levelPercent);
607 assertArrayEquals(new byte[]{5, 4, 3, 2, 1},
608 HidlMemoryUtil.hidlMemoryToByteArray(halConfig.data));
609
610 return new SoundTriggerHwCallback(callbackCaptor.getValue(), cookieCaptor.getValue());
611 }
612
613 private SoundTriggerHwCallback startRecognition(ISoundTriggerModule module, int handle,
614 int hwHandle) throws RemoteException {
615 if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
616 return startRecognition_2_1(module, handle, hwHandle);
617 } else {
618 return startRecognition_2_0(module, handle, hwHandle);
619 }
620 }
621
622 private RecognitionConfig createRecognitionConfig() {
623 RecognitionConfig config = new RecognitionConfig();
624 config.captureRequested = true;
625 config.phraseRecognitionExtras = new PhraseRecognitionExtra[]{new PhraseRecognitionExtra()};
626 config.phraseRecognitionExtras[0].id = 123;
627 config.phraseRecognitionExtras[0].confidenceLevel = 4;
628 config.phraseRecognitionExtras[0].recognitionModes = 5;
629 config.phraseRecognitionExtras[0].levels = new ConfidenceLevel[]{new ConfidenceLevel()};
630 config.phraseRecognitionExtras[0].levels[0].userId = 234;
631 config.phraseRecognitionExtras[0].levels[0].levelPercent = 34;
632 config.data = new byte[]{5, 4, 3, 2, 1};
633 return config;
634 }
635
636 private void stopRecognition(ISoundTriggerModule module, int handle, int hwHandle)
637 throws RemoteException {
638 when(mHalDriver.stopRecognition(hwHandle)).thenReturn(0);
639 module.stopRecognition(handle);
640 verify(mHalDriver).stopRecognition(hwHandle);
641 }
642
643 private void verifyNotStartRecognition() throws RemoteException {
644 verify(mHalDriver, never()).startRecognition(anyInt(), any(), any(), anyInt());
645 if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
646 verify((android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver,
647 never()).startRecognition_2_1(anyInt(), any(), any(), anyInt());
648 }
649 }
650
651
652 @Before
653 public void setUp() throws Exception {
654 clearInvocations(mHalDriver);
655 clearInvocations(mAudioSessionProvider);
656
657 // This binder is associated with the mock, so it can be cast to either version of the
658 // HAL interface.
659 final IHwBinder binder = new IHwBinder() {
660 @Override
661 public void transact(int code, HwParcel request, HwParcel reply, int flags)
662 throws RemoteException {
663 // This is a little hacky, but a very easy way to gracefully reject a request for
664 // an unsupported interface (after queryLocalInterface() returns null, the client
665 // will attempt a remote transaction to obtain the interface. RemoteException will
666 // cause it to give up).
667 throw new RemoteException();
668 }
669
670 @Override
671 public IHwInterface queryLocalInterface(String descriptor) {
672 if (descriptor.equals("android.hardware.soundtrigger@2.0::ISoundTriggerHw")
673 || descriptor.equals("android.hardware.soundtrigger@2.1::ISoundTriggerHw")
674 && mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw
675 || descriptor.equals("android.hardware.soundtrigger@2.2::ISoundTriggerHw")
676 && mHalDriver instanceof android.hardware.soundtrigger.V2_2.ISoundTriggerHw
677 || descriptor.equals("android.hardware.soundtrigger@2.3::ISoundTriggerHw")
678 && mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
679 return mHalDriver;
680 }
681 return null;
682 }
683
684 @Override
685 public boolean linkToDeath(DeathRecipient recipient, long cookie) {
686 throw new UnsupportedOperationException();
687 }
688
689 @Override
690 public boolean unlinkToDeath(DeathRecipient recipient) {
691 throw new UnsupportedOperationException();
692 }
693 };
694
695 when(mHalDriver.asBinder()).thenReturn(binder);
696 }
697
698 @Test
699 public void testSetUpAndTearDown() {
700 }
701
702 @Test
703 public void testListModules() throws Exception {
704 initService(true);
705 // Note: input and output properties are NOT the same type, even though they are in any way
706 // equivalent. One is a type that's exposed by the HAL and one is a type that's exposed by
707 // the service. The service actually performs a (trivial) conversion between the two.
708 SoundTriggerModuleDescriptor[] allDescriptors = mService.listModules();
709 assertEquals(1, allDescriptors.length);
710
711 SoundTriggerModuleProperties properties = allDescriptors[0].properties;
712
713 validateDefaultProperties(properties, true);
714 }
715
716 @Test
717 public void testAttachDetach() throws Exception {
718 // Normal attachment / detachment.
719 initService(true);
720 ISoundTriggerCallback callback = createCallbackMock();
721 ISoundTriggerModule module = mService.attach(0, callback);
722 verify(callback).onRecognitionAvailabilityChange(true);
723 assertNotNull(module);
724 module.detach();
725 }
726
727 @Test
728 public void testAttachDetachNotAvailable() throws Exception {
729 // Attachment / detachment during external capture, with a module not supporting concurrent
730 // capture.
731 initService(false);
732 ISoundTriggerCallback callback = createCallbackMock();
733 ISoundTriggerModule module = mService.attach(0, callback);
734 verify(callback).onRecognitionAvailabilityChange(false);
735 assertNotNull(module);
736 module.detach();
737 }
738
739 @Test
740 public void testAttachDetachAvailable() throws Exception {
741 // Attachment / detachment during external capture, with a module supporting concurrent
742 // capture.
743 initService(true);
744 ISoundTriggerCallback callback = createCallbackMock();
745 ISoundTriggerModule module = mService.attach(0, callback);
746 verify(callback).onRecognitionAvailabilityChange(true);
747 assertNotNull(module);
748 module.detach();
749 }
750
751 @Test
752 public void testLoadUnloadModel() throws Exception {
753 initService(true);
754 ISoundTriggerCallback callback = createCallbackMock();
755 ISoundTriggerModule module = mService.attach(0, callback);
756
757 final int hwHandle = 7;
758 int handle = loadGenericModel(module, hwHandle);
759 unloadModel(module, handle, hwHandle);
760 module.detach();
761 }
762
763 @Test
764 public void testLoadUnloadPhraseModel() throws Exception {
765 initService(true);
766 ISoundTriggerCallback callback = createCallbackMock();
767 ISoundTriggerModule module = mService.attach(0, callback);
768
769 final int hwHandle = 73;
770 int handle = loadPhraseModel(module, hwHandle);
771 unloadModel(module, handle, hwHandle);
772 module.detach();
773 }
774
775 @Test
776 public void testStartStopRecognition() throws Exception {
777 initService(true);
778 ISoundTriggerCallback callback = createCallbackMock();
779 ISoundTriggerModule module = mService.attach(0, callback);
780
781 // Load the model.
782 final int hwHandle = 7;
783 int handle = loadGenericModel(module, hwHandle);
784
785 // Initiate a recognition.
786 startRecognition(module, handle, hwHandle);
787
788 // Stop the recognition.
789 stopRecognition(module, handle, hwHandle);
790
791 // Unload the model.
792 unloadModel(module, handle, hwHandle);
793 module.detach();
794 }
795
796 @Test
797 public void testStartStopPhraseRecognition() throws Exception {
798 initService(true);
799 ISoundTriggerCallback callback = createCallbackMock();
800 ISoundTriggerModule module = mService.attach(0, callback);
801
802 // Load the model.
803 final int hwHandle = 67;
804 int handle = loadPhraseModel(module, hwHandle);
805
806 // Initiate a recognition.
807 startRecognition(module, handle, hwHandle);
808
809 // Stop the recognition.
810 stopRecognition(module, handle, hwHandle);
811
812 // Unload the model.
813 unloadModel(module, handle, hwHandle);
814 module.detach();
815 }
816
817 @Test
818 public void testRecognition() throws Exception {
819 initService(true);
820 ISoundTriggerCallback callback = createCallbackMock();
821 ISoundTriggerModule module = mService.attach(0, callback);
822
823 // Load the model.
824 final int hwHandle = 7;
825 int handle = loadGenericModel(module, hwHandle);
826
827 // Initiate a recognition.
828 SoundTriggerHwCallback hwCallback = startRecognition(module, handle, hwHandle);
829
830 // Signal a capture from the driver.
831 hwCallback.sendRecognitionEvent(hwHandle,
832 android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionStatus.SUCCESS);
833
834 ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
835 RecognitionEvent.class);
836 verify(callback).onRecognition(eq(handle), eventCaptor.capture());
837
838 // Validate the event.
839 validateRecognitionEvent(eventCaptor.getValue(), RecognitionStatus.SUCCESS);
840
841 // Unload the model.
842 unloadModel(module, handle, hwHandle);
843 module.detach();
844 }
845
846 @Test
847 public void testPhraseRecognition() throws Exception {
848 initService(true);
849 ISoundTriggerCallback callback = createCallbackMock();
850 ISoundTriggerModule module = mService.attach(0, callback);
851
852 // Load the model.
853 final int hwHandle = 7;
854 int handle = loadPhraseModel(module, hwHandle);
855
856 // Initiate a recognition.
857 SoundTriggerHwCallback hwCallback = startRecognition(module, handle, hwHandle);
858
859 // Signal a capture from the driver.
860 hwCallback.sendPhraseRecognitionEvent(hwHandle,
861 android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionStatus.SUCCESS);
862
863 ArgumentCaptor<PhraseRecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
864 PhraseRecognitionEvent.class);
865 verify(callback).onPhraseRecognition(eq(handle), eventCaptor.capture());
866
867 // Validate the event.
868 validatePhraseRecognitionEvent(eventCaptor.getValue(), RecognitionStatus.SUCCESS);
869
870 // Unload the model.
871 unloadModel(module, handle, hwHandle);
872 module.detach();
873 }
874
875 @Test
876 public void testForceRecognition() throws Exception {
877 if (!(mHalDriver instanceof android.hardware.soundtrigger.V2_2.ISoundTriggerHw)) {
878 return;
879 }
880
881 android.hardware.soundtrigger.V2_2.ISoundTriggerHw driver =
882 (android.hardware.soundtrigger.V2_2.ISoundTriggerHw) mHalDriver;
883
884 initService(true);
885 ISoundTriggerCallback callback = createCallbackMock();
886 ISoundTriggerModule module = mService.attach(0, callback);
887
888 // Load the model.
889 final int hwHandle = 17;
890 int handle = loadGenericModel(module, hwHandle);
891
892 // Initiate a recognition.
893 SoundTriggerHwCallback hwCallback = startRecognition(module, handle, hwHandle);
894
895 // Force a trigger.
896 module.forceRecognitionEvent(handle);
897 verify(driver).getModelState(hwHandle);
898
899 // Signal a capture from the driver.
900 // '3' means 'forced', there's no constant for that in the HAL.
901 hwCallback.sendRecognitionEvent(hwHandle, 3);
902
903 ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
904 RecognitionEvent.class);
905 verify(callback).onRecognition(eq(handle), eventCaptor.capture());
906
907 // Validate the event.
908 validateRecognitionEvent(eventCaptor.getValue(), RecognitionStatus.FORCED);
909
910 // Stop the recognition.
911 stopRecognition(module, handle, hwHandle);
912
913 // Unload the model.
914 unloadModel(module, handle, hwHandle);
915 module.detach();
916 }
917
918 @Test
919 public void testForcePhraseRecognition() throws Exception {
920 if (!(mHalDriver instanceof android.hardware.soundtrigger.V2_2.ISoundTriggerHw)) {
921 return;
922 }
923
924 android.hardware.soundtrigger.V2_2.ISoundTriggerHw driver =
925 (android.hardware.soundtrigger.V2_2.ISoundTriggerHw) mHalDriver;
926
927 initService(true);
928 ISoundTriggerCallback callback = createCallbackMock();
929 ISoundTriggerModule module = mService.attach(0, callback);
930
931 // Load the model.
932 final int hwHandle = 17;
933 int handle = loadPhraseModel(module, hwHandle);
934
935 // Initiate a recognition.
936 SoundTriggerHwCallback hwCallback = startRecognition(module, handle, hwHandle);
937
938 // Force a trigger.
939 module.forceRecognitionEvent(handle);
940 verify(driver).getModelState(hwHandle);
941
942 // Signal a capture from the driver.
943 // '3' means 'forced', there's no constant for that in the HAL.
944 hwCallback.sendPhraseRecognitionEvent(hwHandle, 3);
945
946 ArgumentCaptor<PhraseRecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
947 PhraseRecognitionEvent.class);
948 verify(callback).onPhraseRecognition(eq(handle), eventCaptor.capture());
949
950 // Validate the event.
951 validatePhraseRecognitionEvent(eventCaptor.getValue(), RecognitionStatus.FORCED);
952
953 // Stop the recognition.
954 stopRecognition(module, handle, hwHandle);
955
956 // Unload the model.
957 unloadModel(module, handle, hwHandle);
958 module.detach();
959 }
960
961 @Test
962 public void testAbortRecognition() throws Exception {
963 // Make sure the HAL doesn't support concurrent capture.
964 initService(false);
965 mService.setExternalCaptureState(false);
966
967 ISoundTriggerCallback callback = createCallbackMock();
968 ISoundTriggerModule module = mService.attach(0, callback);
969 verify(callback).onRecognitionAvailabilityChange(true);
970
971 // Load the model.
972 final int hwHandle = 11;
973 int handle = loadGenericModel(module, hwHandle);
974
975 // Initiate a recognition.
976 startRecognition(module, handle, hwHandle);
977
978 // Abort.
979 mService.setExternalCaptureState(true);
980
981 ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
982 RecognitionEvent.class);
983 verify(callback).onRecognition(eq(handle), eventCaptor.capture());
984
985 // Validate the event.
986 assertEquals(RecognitionStatus.ABORTED, eventCaptor.getValue().status);
987
988 // Make sure we are notified of the lost availability.
989 verify(callback).onRecognitionAvailabilityChange(false);
990
991 // Attempt to start a new recognition - should get an abort event immediately, without
992 // involving the HAL.
993 clearInvocations(callback);
994 clearInvocations(mHalDriver);
995 module.startRecognition(handle, createRecognitionConfig());
996 verify(callback).onRecognition(eq(handle), eventCaptor.capture());
997 assertEquals(RecognitionStatus.ABORTED, eventCaptor.getValue().status);
998 verifyNotStartRecognition();
999
1000 // Now enable it and make sure we are notified.
1001 mService.setExternalCaptureState(false);
1002 verify(callback).onRecognitionAvailabilityChange(true);
1003
1004 // Unload the model.
1005 unloadModel(module, handle, hwHandle);
1006 module.detach();
1007 }
1008
1009 @Test
1010 public void testAbortPhraseRecognition() throws Exception {
1011 // Make sure the HAL doesn't support concurrent capture.
1012 initService(false);
1013 mService.setExternalCaptureState(false);
1014
1015 ISoundTriggerCallback callback = createCallbackMock();
1016 ISoundTriggerModule module = mService.attach(0, callback);
1017 verify(callback).onRecognitionAvailabilityChange(true);
1018
1019 // Load the model.
1020 final int hwHandle = 11;
1021 int handle = loadPhraseModel(module, hwHandle);
1022
1023 // Initiate a recognition.
1024 startRecognition(module, handle, hwHandle);
1025
1026 // Abort.
1027 mService.setExternalCaptureState(true);
1028
1029 ArgumentCaptor<PhraseRecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
1030 PhraseRecognitionEvent.class);
1031 verify(callback).onPhraseRecognition(eq(handle), eventCaptor.capture());
1032
1033 // Validate the event.
1034 assertEquals(RecognitionStatus.ABORTED, eventCaptor.getValue().common.status);
1035
1036 // Make sure we are notified of the lost availability.
1037 verify(callback).onRecognitionAvailabilityChange(false);
1038
1039 // Attempt to start a new recognition - should get an abort event immediately, without
1040 // involving the HAL.
1041 clearInvocations(callback);
1042 clearInvocations(mHalDriver);
1043 module.startRecognition(handle, createRecognitionConfig());
1044 verify(callback).onPhraseRecognition(eq(handle), eventCaptor.capture());
1045 assertEquals(RecognitionStatus.ABORTED, eventCaptor.getValue().common.status);
1046 verifyNotStartRecognition();
1047
1048 // Now enable it and make sure we are notified.
1049 mService.setExternalCaptureState(false);
1050 verify(callback).onRecognitionAvailabilityChange(true);
1051
1052 // Unload the model.
1053 unloadModel(module, handle, hwHandle);
1054 module.detach();
1055 }
1056
1057 @Test
1058 public void testNotAbortRecognitionConcurrent() throws Exception {
1059 // Make sure the HAL supports concurrent capture.
1060 initService(true);
1061
1062 ISoundTriggerCallback callback = createCallbackMock();
1063 ISoundTriggerModule module = mService.attach(0, callback);
1064 verify(callback).onRecognitionAvailabilityChange(true);
1065 clearInvocations(callback);
1066
1067 // Load the model.
1068 final int hwHandle = 13;
1069 int handle = loadGenericModel(module, hwHandle);
1070
1071 // Initiate a recognition.
1072 startRecognition(module, handle, hwHandle);
1073
1074 // Signal concurrent capture. Shouldn't abort.
1075 mService.setExternalCaptureState(true);
1076 verify(callback, never()).onRecognition(anyInt(), any());
1077 verify(callback, never()).onRecognitionAvailabilityChange(anyBoolean());
1078
1079 // Stop the recognition.
1080 stopRecognition(module, handle, hwHandle);
1081
1082 // Initiating a new one should work fine.
1083 clearInvocations(mHalDriver);
1084 startRecognition(module, handle, hwHandle);
1085 verify(callback, never()).onRecognition(anyInt(), any());
1086 stopRecognition(module, handle, hwHandle);
1087
1088 // Unload the model.
1089 module.unloadModel(handle);
1090 module.detach();
1091 }
1092
1093 @Test
1094 public void testNotAbortPhraseRecognitionConcurrent() throws Exception {
1095 // Make sure the HAL supports concurrent capture.
1096 initService(true);
1097
1098 ISoundTriggerCallback callback = createCallbackMock();
1099 ISoundTriggerModule module = mService.attach(0, callback);
1100 verify(callback).onRecognitionAvailabilityChange(true);
1101 clearInvocations(callback);
1102
1103 // Load the model.
1104 final int hwHandle = 13;
1105 int handle = loadPhraseModel(module, hwHandle);
1106
1107 // Initiate a recognition.
1108 startRecognition(module, handle, hwHandle);
1109
1110 // Signal concurrent capture. Shouldn't abort.
1111 mService.setExternalCaptureState(true);
1112 verify(callback, never()).onPhraseRecognition(anyInt(), any());
1113 verify(callback, never()).onRecognitionAvailabilityChange(anyBoolean());
1114
1115 // Stop the recognition.
1116 stopRecognition(module, handle, hwHandle);
1117
1118 // Initiating a new one should work fine.
1119 clearInvocations(mHalDriver);
1120 startRecognition(module, handle, hwHandle);
1121 verify(callback, never()).onRecognition(anyInt(), any());
1122 stopRecognition(module, handle, hwHandle);
1123
1124 // Unload the model.
1125 module.unloadModel(handle);
1126 module.detach();
1127 }
1128
1129 @Test
1130 public void testParameterSupported() throws Exception {
1131 if (!(mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw)) {
1132 return;
1133 }
1134
1135 android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver =
1136 (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
1137
1138 initService(false);
1139 ISoundTriggerCallback callback = createCallbackMock();
1140 ISoundTriggerModule module = mService.attach(0, callback);
1141 final int hwHandle = 12;
1142 int modelHandle = loadGenericModel(module, hwHandle);
1143
1144 doAnswer((Answer<Void>) invocation -> {
1145 android.hardware.soundtrigger.V2_3.ISoundTriggerHw.queryParameterCallback
1146 resultCallback = invocation.getArgument(2);
1147 android.hardware.soundtrigger.V2_3.ModelParameterRange range =
1148 new android.hardware.soundtrigger.V2_3.ModelParameterRange();
1149 range.start = 23;
1150 range.end = 45;
1151 OptionalModelParameterRange optionalRange = new OptionalModelParameterRange();
1152 optionalRange.range(range);
1153 resultCallback.onValues(0, optionalRange);
1154 return null;
1155 }).when(driver).queryParameter(eq(hwHandle),
1156 eq(android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR), any());
1157
1158 ModelParameterRange range = module.queryModelParameterSupport(modelHandle,
1159 ModelParameter.THRESHOLD_FACTOR);
1160
1161 verify(driver).queryParameter(eq(hwHandle),
1162 eq(android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR), any());
1163
1164 assertEquals(23, range.minInclusive);
1165 assertEquals(45, range.maxInclusive);
1166 }
1167
1168 @Test
1169 public void testParameterNotSupportedOld() throws Exception {
1170 if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
1171 return;
1172 }
1173
1174 initService(false);
1175 ISoundTriggerCallback callback = createCallbackMock();
1176 ISoundTriggerModule module = mService.attach(0, callback);
1177 final int hwHandle = 13;
1178 int modelHandle = loadGenericModel(module, hwHandle);
1179
1180 ModelParameterRange range = module.queryModelParameterSupport(modelHandle,
1181 ModelParameter.THRESHOLD_FACTOR);
1182
1183 assertNull(range);
1184 }
1185
1186 @Test
1187 public void testParameterNotSupported() throws Exception {
1188 if (!(mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw)) {
1189 return;
1190 }
1191
1192 android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver =
1193 (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
1194
1195 initService(false);
1196 ISoundTriggerCallback callback = createCallbackMock();
1197 ISoundTriggerModule module = mService.attach(0, callback);
1198 final int hwHandle = 13;
1199 int modelHandle = loadGenericModel(module, hwHandle);
1200
1201 doAnswer(invocation -> {
1202 android.hardware.soundtrigger.V2_3.ISoundTriggerHw.queryParameterCallback
1203 resultCallback = invocation.getArgument(2);
1204 // This is the return of this method.
1205 resultCallback.onValues(0, new OptionalModelParameterRange());
1206 return null;
1207 }).when(driver).queryParameter(eq(hwHandle),
1208 eq(android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR), any());
1209
1210 ModelParameterRange range = module.queryModelParameterSupport(modelHandle,
1211 ModelParameter.THRESHOLD_FACTOR);
1212
1213 verify(driver).queryParameter(eq(hwHandle),
1214 eq(android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR), any());
1215
1216 assertNull(range);
1217 }
1218
1219 @Test
1220 public void testGetParameter() throws Exception {
1221 if (!(mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw)) {
1222 return;
1223 }
1224
1225 android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver =
1226 (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
1227
1228 initService(false);
1229 ISoundTriggerCallback callback = createCallbackMock();
1230 ISoundTriggerModule module = mService.attach(0, callback);
1231 final int hwHandle = 14;
1232 int modelHandle = loadGenericModel(module, hwHandle);
1233
1234 doAnswer(invocation -> {
1235 android.hardware.soundtrigger.V2_3.ISoundTriggerHw.getParameterCallback
1236 resultCallback = invocation.getArgument(2);
1237 // This is the return of this method.
1238 resultCallback.onValues(0, 234);
1239 return null;
1240 }).when(driver).getParameter(eq(hwHandle),
1241 eq(android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR), any());
1242
1243 int value = module.getModelParameter(modelHandle, ModelParameter.THRESHOLD_FACTOR);
1244
1245 verify(driver).getParameter(eq(hwHandle),
1246 eq(android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR), any());
1247
1248 assertEquals(234, value);
1249 }
1250
1251 @Test
1252 public void testSetParameter() throws Exception {
1253 if (!(mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw)) {
1254 return;
1255 }
1256
1257 android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver =
1258 (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
1259
1260 initService(false);
1261 ISoundTriggerCallback callback = createCallbackMock();
1262 ISoundTriggerModule module = mService.attach(0, callback);
1263 final int hwHandle = 17;
1264 int modelHandle = loadGenericModel(module, hwHandle);
1265
1266 when(driver.setParameter(hwHandle,
1267 android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR,
1268 456)).thenReturn(0);
1269
1270 module.setModelParameter(modelHandle, ModelParameter.THRESHOLD_FACTOR, 456);
1271
1272 verify(driver).setParameter(hwHandle,
1273 android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR, 456);
1274 }
1275
1276 private static class SoundTriggerHwCallback {
1277 private final android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback mCallback;
1278 private final int mCookie;
1279
1280 SoundTriggerHwCallback(android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback callback,
1281 int cookie) {
1282 mCallback = callback;
1283 mCookie = cookie;
1284 }
1285
1286 private void sendRecognitionEvent(int hwHandle, int status) throws RemoteException {
1287 if (mCallback instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback) {
1288 ((android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback) mCallback).recognitionCallback_2_1(
1289 createRecognitionEvent_2_1(hwHandle, status), mCookie);
1290 } else {
1291 mCallback.recognitionCallback(createRecognitionEvent_2_0(hwHandle, status),
1292 mCookie);
1293 }
1294 }
1295
1296 private void sendPhraseRecognitionEvent(int hwHandle, int status) throws RemoteException {
1297 if (mCallback instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback) {
1298 ((android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback) mCallback).phraseRecognitionCallback_2_1(
1299 createPhraseRecognitionEvent_2_1(hwHandle, status), mCookie);
1300 } else {
1301 mCallback.phraseRecognitionCallback(
1302 createPhraseRecognitionEvent_2_0(hwHandle, status), mCookie);
1303 }
1304 }
1305 }
1306}