Ytai Ben-Tsvi | 93c117c86 | 2019-11-25 12:43:28 -0800 | [diff] [blame] | 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 org.junit.Assert.assertArrayEquals; |
| 20 | import static org.junit.Assert.assertEquals; |
| 21 | import static org.junit.Assert.assertNotNull; |
| 22 | import static org.junit.Assert.assertNull; |
| 23 | import static org.junit.Assert.assertTrue; |
| 24 | import static org.mockito.ArgumentMatchers.any; |
| 25 | import static org.mockito.ArgumentMatchers.anyBoolean; |
| 26 | import static org.mockito.ArgumentMatchers.anyInt; |
| 27 | import static org.mockito.ArgumentMatchers.eq; |
| 28 | import static org.mockito.Mockito.clearInvocations; |
| 29 | import static org.mockito.Mockito.doAnswer; |
| 30 | import static org.mockito.Mockito.mock; |
| 31 | import static org.mockito.Mockito.never; |
| 32 | import static org.mockito.Mockito.verify; |
| 33 | import static org.mockito.Mockito.when; |
| 34 | |
| 35 | import android.hardware.audio.common.V2_0.AudioConfig; |
| 36 | import android.hardware.audio.common.V2_0.Uuid; |
| 37 | import android.hardware.soundtrigger.V2_3.OptionalModelParameterRange; |
| 38 | import android.media.audio.common.AudioChannelMask; |
| 39 | import android.media.audio.common.AudioFormat; |
| 40 | import android.media.soundtrigger_middleware.ConfidenceLevel; |
| 41 | import android.media.soundtrigger_middleware.ISoundTriggerCallback; |
| 42 | import android.media.soundtrigger_middleware.ISoundTriggerModule; |
| 43 | import android.media.soundtrigger_middleware.ModelParameter; |
| 44 | import android.media.soundtrigger_middleware.ModelParameterRange; |
| 45 | import android.media.soundtrigger_middleware.Phrase; |
| 46 | import android.media.soundtrigger_middleware.PhraseRecognitionEvent; |
| 47 | import android.media.soundtrigger_middleware.PhraseRecognitionExtra; |
| 48 | import android.media.soundtrigger_middleware.PhraseSoundModel; |
| 49 | import android.media.soundtrigger_middleware.RecognitionConfig; |
| 50 | import android.media.soundtrigger_middleware.RecognitionEvent; |
| 51 | import android.media.soundtrigger_middleware.RecognitionMode; |
| 52 | import android.media.soundtrigger_middleware.RecognitionStatus; |
| 53 | import android.media.soundtrigger_middleware.SoundModel; |
| 54 | import android.media.soundtrigger_middleware.SoundModelType; |
| 55 | import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor; |
| 56 | import android.media.soundtrigger_middleware.SoundTriggerModuleProperties; |
| 57 | import android.os.HidlMemoryUtil; |
| 58 | import android.os.HwParcel; |
| 59 | import android.os.IHwBinder; |
| 60 | import android.os.IHwInterface; |
| 61 | import android.os.RemoteException; |
| 62 | |
| 63 | import androidx.test.runner.AndroidJUnit4; |
| 64 | |
| 65 | import org.junit.Before; |
| 66 | import org.junit.Test; |
| 67 | import org.junit.runner.RunWith; |
| 68 | import org.junit.runners.Parameterized; |
| 69 | import org.mockito.ArgumentCaptor; |
| 70 | import org.mockito.Mock; |
| 71 | import org.mockito.Mockito; |
| 72 | import org.mockito.stubbing.Answer; |
| 73 | |
| 74 | @RunWith(Parameterized.class) |
| 75 | public 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 | } |