1 /*
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 
17 package com.android.server.soundtrigger_middleware;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertNotNull;
23 import static org.junit.Assert.assertNull;
24 import static org.junit.Assert.fail;
25 import static org.junit.Assume.assumeTrue;
26 import static org.mockito.ArgumentMatchers.any;
27 import static org.mockito.ArgumentMatchers.anyInt;
28 import static org.mockito.ArgumentMatchers.anyLong;
29 import static org.mockito.ArgumentMatchers.eq;
30 import static org.mockito.Mockito.atMost;
31 import static org.mockito.Mockito.clearInvocations;
32 import static org.mockito.Mockito.doAnswer;
33 import static org.mockito.Mockito.mock;
34 import static org.mockito.Mockito.verify;
35 import static org.mockito.Mockito.verifyNoMoreInteractions;
36 import static org.mockito.Mockito.when;
37 
38 import android.media.soundtrigger.ModelParameterRange;
39 import android.media.soundtrigger.Properties;
40 import android.media.soundtrigger.RecognitionConfig;
41 import android.media.soundtrigger.RecognitionStatus;
42 import android.media.soundtrigger.Status;
43 import android.media.soundtrigger_middleware.PhraseRecognitionEventSys;
44 import android.media.soundtrigger_middleware.RecognitionEventSys;
45 import android.os.HwParcel;
46 import android.os.IBinder;
47 import android.os.IHwBinder;
48 import android.os.IHwInterface;
49 import android.os.RemoteException;
50 
51 import org.junit.After;
52 import org.junit.Before;
53 import org.junit.Test;
54 import org.junit.runner.RunWith;
55 import org.junit.runners.Parameterized;
56 import org.mockito.ArgumentCaptor;
57 
58 import java.util.concurrent.atomic.AtomicReference;
59 
60 @RunWith(Parameterized.class)
61 public class SoundHw2CompatTest {
62     @Parameterized.Parameter public String mVersion;
63 
64     private final Runnable mRebootRunnable = mock(Runnable.class);
65     private ISoundTriggerHal mCanonical;
66     private android.hardware.soundtrigger.V2_0.ISoundTriggerHw mHalDriver;
67 
68     // We run the test once for every version of the underlying driver.
69     @Parameterized.Parameters
data()70     public static Object[] data() {
71         return new String[]{"V2_0", "V2_1", "V2_2", "V2_3"};
72     }
73 
74     @Before
setUp()75     public void setUp() throws Exception {
76         mHalDriver = (android.hardware.soundtrigger.V2_0.ISoundTriggerHw) mock(Class.forName(
77                 String.format("android.hardware.soundtrigger.%s.ISoundTriggerHw", mVersion)));
78 
79         clearInvocations(mRebootRunnable);
80 
81         // This binder is associated with the mock, so it can be cast to either version of the
82         // HAL interface.
83         final IHwBinder binder = new IHwBinder() {
84             @Override
85             public void transact(int code, HwParcel request, HwParcel reply, int flags)
86                     throws RemoteException {
87                 // This is a little hacky, but a very easy way to gracefully reject a request for
88                 // an unsupported interface (after queryLocalInterface() returns null, the client
89                 // will attempt a remote transaction to obtain the interface. RemoteException will
90                 // cause it to give up).
91                 throw new RemoteException();
92             }
93 
94             @Override
95             public IHwInterface queryLocalInterface(String descriptor) {
96                 if (descriptor.equals("android.hardware.soundtrigger@2.0::ISoundTriggerHw")
97                         || descriptor.equals("android.hardware.soundtrigger@2.1::ISoundTriggerHw")
98                         && mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw
99                         || descriptor.equals("android.hardware.soundtrigger@2.2::ISoundTriggerHw")
100                         && mHalDriver instanceof android.hardware.soundtrigger.V2_2.ISoundTriggerHw
101                         || descriptor.equals("android.hardware.soundtrigger@2.3::ISoundTriggerHw")
102                         && mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
103                     return mHalDriver;
104                 }
105                 return null;
106             }
107 
108             @Override
109             public boolean linkToDeath(DeathRecipient recipient, long cookie) {
110                 try {
111                     return mHalDriver.linkToDeath(recipient, cookie);
112                 } catch (RemoteException e) {
113                     throw e.rethrowAsRuntimeException();
114                 }
115             }
116 
117             @Override
118             public boolean unlinkToDeath(DeathRecipient recipient) {
119                 try {
120                     return mHalDriver.unlinkToDeath(recipient);
121                 } catch (RemoteException e) {
122                     throw e.rethrowAsRuntimeException();
123                 }
124             }
125         };
126         when(mHalDriver.asBinder()).thenReturn(binder);
127 
128         android.hardware.soundtrigger.V2_3.Properties halProperties =
129                 TestUtil.createDefaultProperties_2_3();
130         doAnswer(invocation -> {
131             ((android.hardware.soundtrigger.V2_0.ISoundTriggerHw.getPropertiesCallback) invocation.getArgument(
132                     0)).onValues(0, halProperties.base);
133             return null;
134         }).when(mHalDriver).getProperties(any());
135 
136         if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
137             android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver =
138                     (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
139             doAnswer(invocation -> {
140                 ((android.hardware.soundtrigger.V2_3.ISoundTriggerHw.getProperties_2_3Callback) invocation.getArgument(
141                         0)).onValues(0, halProperties);
142                 return null;
143             }).when(driver).getProperties_2_3(any());
144         }
145 
146         mCanonical = SoundTriggerHw2Compat.create(mHalDriver, mRebootRunnable, null);
147 
148         // During initialization any method can be called, but after we're starting to enforce that
149         // no additional methods are called.
150         clearInvocations(mHalDriver);
151     }
152 
153     @After
tearDown()154     public void tearDown() {
155         mCanonical.detach();
156         verifyNoMoreInteractions(mHalDriver);
157         verifyNoMoreInteractions(mRebootRunnable);
158     }
159 
160     @Test
testSetUpAndTearDown()161     public void testSetUpAndTearDown() {
162     }
163 
164     @Test
testReboot()165     public void testReboot() {
166         mCanonical.reboot();
167         verify(mRebootRunnable).run();
168     }
169 
170     @Test
testGetProperties()171     public void testGetProperties() throws Exception {
172         Properties properties = mCanonical.getProperties();
173 
174         if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
175             android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver =
176                     (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
177             // It is OK for the SUT to cache the properties, so the underlying method doesn't
178             // need to be called every single time.
179             verify(driver, atMost(1)).getProperties_2_3(any());
180             TestUtil.validateDefaultProperties(properties);
181         } else {
182             // It is OK for the SUT to cache the properties, so the underlying method doesn't
183             // need to be called every single time.
184             verify(mHalDriver, atMost(1)).getProperties(any());
185             TestUtil.validateDefaultProperties(properties, 0, "");
186         }
187     }
188 
loadGenericModel_2_0(ISoundTriggerHal.ModelCallback canonicalCallback)189     private int loadGenericModel_2_0(ISoundTriggerHal.ModelCallback canonicalCallback)
190             throws Exception {
191         final int handle = 29;
192         ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel> modelCaptor =
193                 ArgumentCaptor.forClass(
194                         android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel.class);
195         ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback> callbackCaptor =
196                 ArgumentCaptor.forClass(
197                         android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.class);
198 
199         doAnswer(invocation -> {
200             android.hardware.soundtrigger.V2_0.ISoundTriggerHw.loadSoundModelCallback
201                     resultCallback = invocation.getArgument(3);
202 
203             // This is the return of this method.
204             resultCallback.onValues(0, handle);
205             return null;
206         }).when(mHalDriver).loadSoundModel(any(), any(), anyInt(), any());
207 
208         assertEquals(handle,
209                 mCanonical.loadSoundModel(TestUtil.createGenericSoundModel(), canonicalCallback));
210 
211         verify(mHalDriver).loadSoundModel(modelCaptor.capture(), callbackCaptor.capture(), anyInt(),
212                 any());
213 
214         TestUtil.validateGenericSoundModel_2_0(modelCaptor.getValue());
215         validateCallback_2_0(callbackCaptor.getValue(), canonicalCallback);
216         return handle;
217     }
218 
loadGenericModel_2_1(ISoundTriggerHal.ModelCallback canonicalCallback)219     private int loadGenericModel_2_1(ISoundTriggerHal.ModelCallback canonicalCallback)
220             throws Exception {
221         final android.hardware.soundtrigger.V2_1.ISoundTriggerHw driver_2_1 =
222                 (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
223 
224         final int handle = 29;
225         AtomicReference<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel> model =
226                 new AtomicReference<>();
227         ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback> callbackCaptor =
228                 ArgumentCaptor.forClass(
229                         android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.class);
230 
231         doAnswer(invocation -> {
232             // We need to dup the model, as it gets invalidated after the call returns.
233             model.set(TestUtil.dupModel_2_1(invocation.getArgument(0)));
234             android.hardware.soundtrigger.V2_1.ISoundTriggerHw.loadSoundModel_2_1Callback
235                     resultCallback = invocation.getArgument(3);
236 
237             // This is the return of this method.
238             resultCallback.onValues(0, handle);
239             return null;
240         }).when(driver_2_1).loadSoundModel_2_1(any(), any(), anyInt(), any());
241 
242         assertEquals(handle,
243                 mCanonical.loadSoundModel(TestUtil.createGenericSoundModel(), canonicalCallback));
244 
245         verify(driver_2_1).loadSoundModel_2_1(any(), callbackCaptor.capture(), anyInt(), any());
246 
247         TestUtil.validateGenericSoundModel_2_1(model.get());
248         validateCallback_2_1(callbackCaptor.getValue(), canonicalCallback);
249         return handle;
250     }
251 
loadGenericModel(ISoundTriggerHal.ModelCallback canonicalCallback)252     private int loadGenericModel(ISoundTriggerHal.ModelCallback canonicalCallback)
253             throws Exception {
254         if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
255             return loadGenericModel_2_1(canonicalCallback);
256         } else {
257             return loadGenericModel_2_0(canonicalCallback);
258         }
259     }
260 
261     @Test
testLoadGenericModel()262     public void testLoadGenericModel() throws Exception {
263         ISoundTriggerHal.ModelCallback canonicalCallback = mock(
264                 ISoundTriggerHal.ModelCallback.class);
265         loadGenericModel(canonicalCallback);
266     }
267 
268     @Test
testMaxModels()269     public void testMaxModels() throws Exception {
270         // Register global callback.
271         ISoundTriggerHal.GlobalCallback globalCallback = mock(
272                 ISoundTriggerHal.GlobalCallback.class);
273         mCanonical.registerCallback(globalCallback);
274 
275         ISoundTriggerHal.ModelCallback canonicalCallback = mock(
276                 ISoundTriggerHal.ModelCallback.class);
277         final int maxModels = TestUtil.createDefaultProperties_2_0().maxSoundModels;
278         int[] modelHandles = new int[maxModels];
279 
280         // Load as many models as we're allowed.
281         for (int i = 0; i < maxModels; ++i) {
282             modelHandles[i] = loadGenericModel(canonicalCallback);
283             verifyNoMoreInteractions(mHalDriver);
284             clearInvocations(mHalDriver);
285         }
286 
287         // Now try to load an additional one and expect failure without invoking the underlying
288         // driver.
289         try {
290             mCanonical.loadPhraseSoundModel(TestUtil.createPhraseSoundModel(), canonicalCallback);
291             fail("Expected an exception");
292         } catch (RecoverableException e) {
293             assertEquals(Status.RESOURCE_CONTENTION, e.errorCode);
294         }
295 
296         // Unload a single model and expect a onResourcesAvailable().
297         mCanonical.unloadSoundModel(modelHandles[0]);
298         verify(mHalDriver).unloadSoundModel(modelHandles[0]);
299 
300         mCanonical.flushCallbacks();
301         verify(globalCallback).onResourcesAvailable();
302     }
303 
loadPhraseModel_2_0(ISoundTriggerHal.ModelCallback canonicalCallback)304     private void loadPhraseModel_2_0(ISoundTriggerHal.ModelCallback canonicalCallback)
305             throws Exception {
306         final int handle = 29;
307         ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel>
308                 modelCaptor = ArgumentCaptor.forClass(
309                 android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel.class);
310         ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback> callbackCaptor =
311                 ArgumentCaptor.forClass(
312                         android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.class);
313 
314         doAnswer(invocation -> {
315             android.hardware.soundtrigger.V2_0.ISoundTriggerHw.loadPhraseSoundModelCallback
316                     resultCallback = invocation.getArgument(3);
317 
318             // This is the return of this method.
319             resultCallback.onValues(0, handle);
320             return null;
321         }).when(mHalDriver).loadPhraseSoundModel(any(), any(), anyInt(), any());
322 
323         assertEquals(handle, mCanonical.loadPhraseSoundModel(TestUtil.createPhraseSoundModel(),
324                 canonicalCallback));
325 
326         verify(mHalDriver).loadPhraseSoundModel(modelCaptor.capture(), callbackCaptor.capture(),
327                 anyInt(), any());
328 
329         TestUtil.validatePhraseSoundModel_2_0(modelCaptor.getValue());
330         validateCallback_2_0(callbackCaptor.getValue(), canonicalCallback);
331     }
332 
loadPhraseModel_2_1(ISoundTriggerHal.ModelCallback canonicalCallback)333     private void loadPhraseModel_2_1(ISoundTriggerHal.ModelCallback canonicalCallback)
334             throws Exception {
335         final android.hardware.soundtrigger.V2_1.ISoundTriggerHw driver_2_1 =
336                 (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
337 
338         final int handle = 29;
339         AtomicReference<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel> model =
340                 new AtomicReference<>();
341         ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback> callbackCaptor =
342                 ArgumentCaptor.forClass(
343                         android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.class);
344 
345         doAnswer(invocation -> {
346             // We need to dup the model, as it gets invalidated after the call returns.
347             model.set(TestUtil.dupPhraseModel_2_1(invocation.getArgument(0)));
348 
349             android.hardware.soundtrigger.V2_1.ISoundTriggerHw.loadPhraseSoundModel_2_1Callback
350                     resultCallback = invocation.getArgument(3);
351 
352             // This is the return of this method.
353             resultCallback.onValues(0, handle);
354             return null;
355         }).when(driver_2_1).loadPhraseSoundModel_2_1(any(), any(), anyInt(), any());
356 
357         assertEquals(handle, mCanonical.loadPhraseSoundModel(TestUtil.createPhraseSoundModel(),
358                 canonicalCallback));
359 
360         verify(driver_2_1).loadPhraseSoundModel_2_1(any(), callbackCaptor.capture(), anyInt(),
361                 any());
362 
363         TestUtil.validatePhraseSoundModel_2_1(model.get());
364         validateCallback_2_1(callbackCaptor.getValue(), canonicalCallback);
365     }
366 
loadPhraseModel(ISoundTriggerHal.ModelCallback canonicalCallback)367     public void loadPhraseModel(ISoundTriggerHal.ModelCallback canonicalCallback) throws Exception {
368         if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
369             loadPhraseModel_2_1(canonicalCallback);
370         } else {
371             loadPhraseModel_2_0(canonicalCallback);
372         }
373     }
374 
375     @Test
testLoadPhraseModel()376     public void testLoadPhraseModel() throws Exception {
377         ISoundTriggerHal.ModelCallback canonicalCallback = mock(
378                 ISoundTriggerHal.ModelCallback.class);
379         loadPhraseModel(canonicalCallback);
380     }
381 
382     @Test
testUnloadModel()383     public void testUnloadModel() throws Exception {
384         mCanonical.unloadSoundModel(14);
385         verify(mHalDriver).unloadSoundModel(14);
386     }
387 
startRecognition_2_0(int handle, ISoundTriggerHal.ModelCallback canonicalCallback)388     private void startRecognition_2_0(int handle, ISoundTriggerHal.ModelCallback canonicalCallback)
389             throws Exception {
390         ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig>
391                 configCaptor = ArgumentCaptor.forClass(
392                 android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig.class);
393         ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback> callbackCaptor =
394                 ArgumentCaptor.forClass(
395                         android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.class);
396 
397         when(mHalDriver.startRecognition(eq(handle), any(), any(), anyInt())).thenReturn(0);
398 
399         RecognitionConfig config = TestUtil.createRecognitionConfig();
400         mCanonical.startRecognition(handle, 203, 204, config);
401         verify(mHalDriver).startRecognition(eq(handle), configCaptor.capture(),
402                 callbackCaptor.capture(), anyInt());
403 
404         TestUtil.validateRecognitionConfig_2_0(configCaptor.getValue(), 203, 204);
405         validateCallback_2_0(callbackCaptor.getValue(), canonicalCallback);
406     }
407 
startRecognition_2_1(int handle, ISoundTriggerHal.ModelCallback canonicalCallback)408     private void startRecognition_2_1(int handle, ISoundTriggerHal.ModelCallback canonicalCallback)
409             throws Exception {
410         final android.hardware.soundtrigger.V2_1.ISoundTriggerHw driver_2_1 =
411                 (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
412 
413         ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig>
414                 configCaptor = ArgumentCaptor.forClass(
415                 android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig.class);
416         ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback> callbackCaptor =
417                 ArgumentCaptor.forClass(
418                         android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.class);
419 
420         when(driver_2_1.startRecognition_2_1(eq(handle), any(), any(), anyInt())).thenReturn(0);
421 
422         RecognitionConfig config = TestUtil.createRecognitionConfig();
423         mCanonical.startRecognition(handle, 505, 506, config);
424         verify(driver_2_1).startRecognition_2_1(eq(handle), configCaptor.capture(),
425                 callbackCaptor.capture(), anyInt());
426 
427         TestUtil.validateRecognitionConfig_2_1(configCaptor.getValue(), 505, 506);
428         validateCallback_2_1(callbackCaptor.getValue(), canonicalCallback);
429     }
430 
startRecognition_2_3(int handle)431     private void startRecognition_2_3(int handle) throws Exception {
432         final android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver_2_3 =
433                 (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
434         ArgumentCaptor<android.hardware.soundtrigger.V2_3.RecognitionConfig> configCaptor =
435                 ArgumentCaptor.forClass(android.hardware.soundtrigger.V2_3.RecognitionConfig.class);
436 
437         when(driver_2_3.startRecognition_2_3(eq(handle), any())).thenReturn(0);
438 
439         RecognitionConfig config = TestUtil.createRecognitionConfig();
440         mCanonical.startRecognition(handle, 808, 909, config);
441         verify(driver_2_3).startRecognition_2_3(eq(handle), configCaptor.capture());
442         TestUtil.validateRecognitionConfig_2_3(configCaptor.getValue(), 808, 909);
443     }
444 
startRecognition(int handle, ISoundTriggerHal.ModelCallback canonicalCallback)445     private void startRecognition(int handle, ISoundTriggerHal.ModelCallback canonicalCallback)
446             throws Exception {
447         if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
448             startRecognition_2_3(handle);
449         } else if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
450             startRecognition_2_1(handle, canonicalCallback);
451         } else {
452             startRecognition_2_0(handle, canonicalCallback);
453         }
454     }
455 
456     @Test
testStartRecognition()457     public void testStartRecognition() throws Exception {
458         // First load.
459         ISoundTriggerHal.ModelCallback canonicalCallback = mock(
460                 ISoundTriggerHal.ModelCallback.class);
461         final int handle = loadGenericModel(canonicalCallback);
462 
463         // Then start.
464         startRecognition(handle, canonicalCallback);
465     }
466 
467     @Test
testStopRecognition()468     public void testStopRecognition() throws Exception {
469         mCanonical.stopRecognition(17);
470         verify(mHalDriver).stopRecognition(17);
471     }
472 
473     @Test
testForceRecognition()474     public void testForceRecognition() throws Exception {
475         if (mHalDriver instanceof android.hardware.soundtrigger.V2_2.ISoundTriggerHw) {
476             android.hardware.soundtrigger.V2_2.ISoundTriggerHw driver_2_2 =
477                     (android.hardware.soundtrigger.V2_2.ISoundTriggerHw) mHalDriver;
478             mCanonical.forceRecognitionEvent(14);
479             verify(driver_2_2).getModelState(14);
480         } else {
481             try {
482                 mCanonical.forceRecognitionEvent(14);
483                 fail("Expected an exception");
484             } catch (RecoverableException e) {
485                 assertEquals(Status.OPERATION_NOT_SUPPORTED, e.errorCode);
486             }
487         }
488     }
489 
490     @Test
testGetParameter()491     public void testGetParameter() throws Exception {
492         assumeTrue(mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw);
493 
494         android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver_2_3 =
495                 (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
496 
497         doAnswer(invocation -> {
498             android.hardware.soundtrigger.V2_3.ISoundTriggerHw.getParameterCallback resultCallback =
499                     invocation.getArgument(2);
500 
501             // This is the return of this method.
502             resultCallback.onValues(0, 99);
503             return null;
504         }).when(driver_2_3).getParameter(eq(21), eq(47), any());
505 
506         assertEquals(99, mCanonical.getModelParameter(21, 47));
507         verify(driver_2_3).getParameter(eq(21), eq(47), any());
508     }
509 
510     @Test
testSetParameter()511     public void testSetParameter() throws Exception {
512         assumeTrue(mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw);
513 
514         android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver_2_3 =
515                 (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
516 
517         mCanonical.setModelParameter(212, 247, 80);
518         verify(driver_2_3).setParameter(212, 247, 80);
519     }
520 
521     @Test
testQueryParameterSupported()522     public void testQueryParameterSupported() throws Exception {
523         assumeTrue(mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw);
524 
525         android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver_2_3 =
526                 (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
527 
528         doAnswer(invocation -> {
529             android.hardware.soundtrigger.V2_3.ISoundTriggerHw.queryParameterCallback
530                     resultCallback = invocation.getArgument(2);
531 
532             // This is the return of this method.
533             android.hardware.soundtrigger.V2_3.ModelParameterRange range =
534                     new android.hardware.soundtrigger.V2_3.ModelParameterRange();
535             range.start = 34;
536             range.end = 45;
537             android.hardware.soundtrigger.V2_3.OptionalModelParameterRange optionalRange =
538                     new android.hardware.soundtrigger.V2_3.OptionalModelParameterRange();
539             optionalRange.range(range);
540             resultCallback.onValues(0, optionalRange);
541             return null;
542         }).when(driver_2_3).queryParameter(eq(11), eq(12), any());
543 
544         ModelParameterRange range = mCanonical.queryParameter(11, 12);
545         assertNotNull(range);
546         assertEquals(34, range.minInclusive);
547         assertEquals(45, range.maxInclusive);
548         verify(driver_2_3).queryParameter(eq(11), eq(12), any());
549     }
550 
551     @Test
testQueryParameterNotSupported()552     public void testQueryParameterNotSupported() throws Exception {
553         if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
554             android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver_2_3 =
555                     (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
556 
557             doAnswer(invocation -> {
558                 android.hardware.soundtrigger.V2_3.ISoundTriggerHw.queryParameterCallback
559                         resultCallback = invocation.getArgument(2);
560 
561                 // This is the return of this method.
562                 android.hardware.soundtrigger.V2_3.OptionalModelParameterRange optionalRange =
563                         new android.hardware.soundtrigger.V2_3.OptionalModelParameterRange();
564                 resultCallback.onValues(0, optionalRange);
565                 return null;
566             }).when(driver_2_3).queryParameter(eq(11), eq(12), any());
567 
568             ModelParameterRange range = mCanonical.queryParameter(11, 12);
569             assertNull(range);
570             verify(driver_2_3).queryParameter(eq(11), eq(12), any());
571         } else {
572             ModelParameterRange range = mCanonical.queryParameter(11, 12);
573             assertNull(range);
574         }
575     }
576 
testGlobalCallback_2_0()577     private void testGlobalCallback_2_0() {
578         ISoundTriggerHal.GlobalCallback canonicalCallback = mock(
579                 ISoundTriggerHal.GlobalCallback.class);
580         mCanonical.registerCallback(canonicalCallback);
581         // We just care that it doesn't throw.
582     }
583 
584     @Test
testGlobalCallback()585     public void testGlobalCallback() {
586         testGlobalCallback_2_0();
587     }
588 
589     @Test
testLinkToDeath()590     public void testLinkToDeath() throws Exception {
591         IBinder.DeathRecipient canonicalRecipient = mock(IBinder.DeathRecipient.class);
592         when(mHalDriver.linkToDeath(any(), anyLong())).thenReturn(true);
593         mCanonical.linkToDeath(canonicalRecipient);
594 
595         ArgumentCaptor<IHwBinder.DeathRecipient> recipientCaptor = ArgumentCaptor.forClass(
596                 IHwBinder.DeathRecipient.class);
597         ArgumentCaptor<Long> cookieCaptor = ArgumentCaptor.forClass(Long.class);
598         verify(mHalDriver).linkToDeath(recipientCaptor.capture(), cookieCaptor.capture());
599 
600         recipientCaptor.getValue().serviceDied(cookieCaptor.getValue());
601         mCanonical.flushCallbacks();
602         verify(canonicalRecipient).binderDied();
603 
604         mCanonical.unlinkToDeath(canonicalRecipient);
605         verify(mHalDriver).unlinkToDeath(recipientCaptor.getValue());
606     }
607 
608     @Test
testInterfaceDescriptor()609     public void testInterfaceDescriptor() throws Exception {
610         when(mHalDriver.interfaceDescriptor()).thenReturn("ABCD");
611         assertEquals("ABCD", mCanonical.interfaceDescriptor());
612         verify(mHalDriver).interfaceDescriptor();
613     }
614 
validateCallback_2_0( android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback hwCallback, ISoundTriggerHal.ModelCallback canonicalCallback)615     private void validateCallback_2_0(
616             android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback hwCallback,
617             ISoundTriggerHal.ModelCallback canonicalCallback) throws Exception {
618         {
619             final int handle = 85;
620             final int status =
621                     android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionStatus.ABORT;
622             ArgumentCaptor<RecognitionEventSys> eventCaptor = ArgumentCaptor.forClass(
623                     RecognitionEventSys.class);
624 
625             hwCallback.recognitionCallback(TestUtil.createRecognitionEvent_2_0(handle, status), 99);
626             mCanonical.flushCallbacks();
627             verify(canonicalCallback).recognitionCallback(eq(handle), eventCaptor.capture());
628             RecognitionEventSys lastEvent = eventCaptor.getValue();
629             assertThat(lastEvent.halEventReceivedMillis).isGreaterThan(0);
630             TestUtil.validateRecognitionEvent(lastEvent.recognitionEvent,
631                     RecognitionStatus.ABORTED,
632                     false);
633         }
634 
635         {
636             final int handle = 92;
637             final int status =
638                     android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionStatus.SUCCESS;
639             ArgumentCaptor<PhraseRecognitionEventSys> eventCaptor = ArgumentCaptor.forClass(
640                     PhraseRecognitionEventSys.class);
641 
642             hwCallback.phraseRecognitionCallback(
643                     TestUtil.createPhraseRecognitionEvent_2_0(handle, status), 99);
644             mCanonical.flushCallbacks();
645             verify(canonicalCallback).phraseRecognitionCallback(eq(handle), eventCaptor.capture());
646             PhraseRecognitionEventSys lastEvent = eventCaptor.getValue();
647             assertThat(lastEvent.halEventReceivedMillis).isGreaterThan(0);
648             TestUtil.validatePhraseRecognitionEvent(lastEvent.phraseRecognitionEvent,
649                     RecognitionStatus.SUCCESS, false);
650         }
651         verifyNoMoreInteractions(canonicalCallback);
652         clearInvocations(canonicalCallback);
653     }
654 
validateCallback_2_1( android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback hwCallback, ISoundTriggerHal.ModelCallback canonicalCallback)655     private void validateCallback_2_1(
656             android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback hwCallback,
657             ISoundTriggerHal.ModelCallback canonicalCallback) throws Exception {
658         {
659             final int handle = 85;
660             final int status =
661                     android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionStatus.ABORT;
662             ArgumentCaptor<RecognitionEventSys> eventCaptor = ArgumentCaptor.forClass(
663                     RecognitionEventSys.class);
664 
665             hwCallback.recognitionCallback_2_1(TestUtil.createRecognitionEvent_2_1(handle, status),
666                     99);
667             mCanonical.flushCallbacks();
668             verify(canonicalCallback).recognitionCallback(eq(handle), eventCaptor.capture());
669             RecognitionEventSys lastEvent = eventCaptor.getValue();
670             assertThat(lastEvent.halEventReceivedMillis).isGreaterThan(0);
671             TestUtil.validateRecognitionEvent(lastEvent.recognitionEvent,
672                     RecognitionStatus.ABORTED,
673                     false);
674         }
675 
676         {
677             final int handle = 87;
678             final int status = 3; // FORCED;
679             ArgumentCaptor<RecognitionEventSys> eventCaptor = ArgumentCaptor.forClass(
680                     RecognitionEventSys.class);
681 
682             hwCallback.recognitionCallback_2_1(TestUtil.createRecognitionEvent_2_1(handle, status),
683                     99);
684             mCanonical.flushCallbacks();
685             verify(canonicalCallback).recognitionCallback(eq(handle), eventCaptor.capture());
686             RecognitionEventSys lastEvent = eventCaptor.getValue();
687             assertThat(lastEvent.halEventReceivedMillis).isGreaterThan(0);
688             TestUtil.validateRecognitionEvent(lastEvent.recognitionEvent,
689                     RecognitionStatus.FORCED,
690                     true);
691         }
692 
693         {
694             final int handle = 92;
695             final int status =
696                     android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionStatus.SUCCESS;
697             ArgumentCaptor<PhraseRecognitionEventSys> eventCaptor = ArgumentCaptor.forClass(
698                     PhraseRecognitionEventSys.class);
699 
700             hwCallback.phraseRecognitionCallback_2_1(
701                     TestUtil.createPhraseRecognitionEvent_2_1(handle, status), 99);
702             mCanonical.flushCallbacks();
703             verify(canonicalCallback).phraseRecognitionCallback(eq(handle), eventCaptor.capture());
704             PhraseRecognitionEventSys lastEvent = eventCaptor.getValue();
705             assertThat(lastEvent.halEventReceivedMillis).isGreaterThan(0);
706             TestUtil.validatePhraseRecognitionEvent(lastEvent.phraseRecognitionEvent,
707                     RecognitionStatus.SUCCESS, false);
708         }
709 
710         {
711             final int handle = 102;
712             final int status = 3; // FORCED;
713             ArgumentCaptor<PhraseRecognitionEventSys> eventCaptor = ArgumentCaptor.forClass(
714                     PhraseRecognitionEventSys.class);
715 
716             hwCallback.phraseRecognitionCallback_2_1(
717                     TestUtil.createPhraseRecognitionEvent_2_1(handle, status), 99);
718             mCanonical.flushCallbacks();
719             verify(canonicalCallback).phraseRecognitionCallback(eq(handle), eventCaptor.capture());
720             PhraseRecognitionEventSys lastEvent = eventCaptor.getValue();
721             assertThat(lastEvent.halEventReceivedMillis).isGreaterThan(0);
722             TestUtil.validatePhraseRecognitionEvent(lastEvent.phraseRecognitionEvent,
723                     RecognitionStatus.FORCED, true);
724         }
725         verifyNoMoreInteractions(canonicalCallback);
726         clearInvocations(canonicalCallback);
727     }
728 }
729