1 /*
2  * Copyright (C) 2021 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.assertTrue;
22 
23 import android.annotation.NonNull;
24 import android.hardware.soundtrigger.V2_1.ISoundTriggerHw;
25 import android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback;
26 import android.media.AudioFormat;
27 import android.media.MediaFormat;
28 import android.media.audio.common.AudioChannelLayout;
29 import android.media.audio.common.AudioConfig;
30 import android.media.audio.common.AudioConfigBase;
31 import android.media.audio.common.AudioFormatDescription;
32 import android.media.audio.common.AudioFormatType;
33 import android.media.soundtrigger.AudioCapabilities;
34 import android.media.soundtrigger.ConfidenceLevel;
35 import android.media.soundtrigger.Phrase;
36 import android.media.soundtrigger.PhraseRecognitionEvent;
37 import android.media.soundtrigger.PhraseRecognitionExtra;
38 import android.media.soundtrigger.PhraseSoundModel;
39 import android.media.soundtrigger.Properties;
40 import android.media.soundtrigger.RecognitionConfig;
41 import android.media.soundtrigger.RecognitionEvent;
42 import android.media.soundtrigger.RecognitionMode;
43 import android.media.soundtrigger.RecognitionStatus;
44 import android.media.soundtrigger.SoundModel;
45 import android.media.soundtrigger.SoundModelType;
46 import android.os.HidlMemoryUtil;
47 import android.os.ParcelFileDescriptor;
48 import android.os.SharedMemory;
49 
50 import java.io.IOException;
51 import java.nio.ByteBuffer;
52 import java.util.List;
53 
54 /**
55  * Test utilities, aimed at generating populated objects of the various types and validating
56  * corresponding objects generated by the system under test.
57  */
58 class TestUtil {
59     private static final int AUDIO_FORMAT_MP3 = 0x01000000;  // matches native
60 
createGenericSoundModel()61     static SoundModel createGenericSoundModel() {
62         return createSoundModel(SoundModelType.GENERIC);
63     }
64 
createSoundModel(@oundModelType int type)65     private static SoundModel createSoundModel(@SoundModelType int type) {
66         SoundModel model = new SoundModel();
67         model.type = type;
68         model.uuid = "12345678-2345-3456-4567-abcdef987654";
69         model.vendorUuid = "87654321-5432-6543-7654-456789fedcba";
70         byte[] data = new byte[]{91, 92, 93, 94, 95};
71         model.data = byteArrayToParcelFileDescriptor(data);
72         model.dataSize = data.length;
73         return model;
74     }
75 
validateSoundModel_2_1(ISoundTriggerHw.SoundModel model, int type)76     private static void validateSoundModel_2_1(ISoundTriggerHw.SoundModel model, int type) {
77         assertEquals(type, model.header.type);
78         assertEquals("12345678-2345-3456-4567-abcdef987654",
79                 ConversionUtil.hidl2aidlUuid(model.header.uuid));
80         assertEquals("87654321-5432-6543-7654-456789fedcba",
81                 ConversionUtil.hidl2aidlUuid(model.header.vendorUuid));
82         assertArrayEquals(new byte[]{91, 92, 93, 94, 95},
83                 HidlMemoryUtil.hidlMemoryToByteArray(model.data));
84     }
85 
dupModel_2_1( android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel model)86     static android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel dupModel_2_1(
87             android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel model) {
88         android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel dup =
89                 new android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel();
90         dup.header = model.header;
91         try {
92             dup.data = model.data.dup();
93         } catch (IOException e) {
94             throw new RuntimeException(e);
95         }
96         return dup;
97     }
98 
validateSoundModel_2_0( android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel model, int type)99     private static void validateSoundModel_2_0(
100             android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel model, int type) {
101         assertEquals(type, model.type);
102         assertEquals("12345678-2345-3456-4567-abcdef987654",
103                 ConversionUtil.hidl2aidlUuid(model.uuid));
104         assertEquals("87654321-5432-6543-7654-456789fedcba",
105                 ConversionUtil.hidl2aidlUuid(model.vendorUuid));
106         assertArrayEquals(new Byte[]{91, 92, 93, 94, 95}, model.data.toArray());
107     }
108 
validateGenericSoundModel_2_1(ISoundTriggerHw.SoundModel model)109     static void validateGenericSoundModel_2_1(ISoundTriggerHw.SoundModel model) {
110         validateSoundModel_2_1(model, android.hardware.soundtrigger.V2_0.SoundModelType.GENERIC);
111     }
112 
validateGenericSoundModel_2_0( android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel model)113     static void validateGenericSoundModel_2_0(
114             android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel model) {
115         validateSoundModel_2_0(model, android.hardware.soundtrigger.V2_0.SoundModelType.GENERIC);
116     }
117 
createPhraseSoundModel()118     static PhraseSoundModel createPhraseSoundModel() {
119         PhraseSoundModel model = new PhraseSoundModel();
120         model.common = createSoundModel(SoundModelType.KEYPHRASE);
121         model.phrases = new Phrase[1];
122         model.phrases[0] = new Phrase();
123         model.phrases[0].id = 123;
124         model.phrases[0].users = new int[]{5, 6, 7};
125         model.phrases[0].locale = "locale";
126         model.phrases[0].text = "text";
127         model.phrases[0].recognitionModes =
128                 RecognitionMode.USER_AUTHENTICATION | RecognitionMode.USER_IDENTIFICATION;
129         return model;
130     }
131 
validatePhraseSoundModel_2_1(ISoundTriggerHw.PhraseSoundModel model)132     static void validatePhraseSoundModel_2_1(ISoundTriggerHw.PhraseSoundModel model) {
133         validateSoundModel_2_1(model.common,
134                 android.hardware.soundtrigger.V2_0.SoundModelType.KEYPHRASE);
135         validatePhrases_2_0(model.phrases);
136     }
137 
dupPhraseModel_2_1( android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel model)138     static android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel dupPhraseModel_2_1(
139             android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel model) {
140         android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel dup =
141                 new android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel();
142         dup.common = dupModel_2_1(model.common);
143         dup.phrases = model.phrases;
144         return dup;
145     }
146 
validatePhraseSoundModel_2_0( android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel model)147     static void validatePhraseSoundModel_2_0(
148             android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel model) {
149         validateSoundModel_2_0(model.common,
150                 android.hardware.soundtrigger.V2_0.SoundModelType.KEYPHRASE);
151         validatePhrases_2_0(model.phrases);
152     }
153 
validatePhrases_2_0( List<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Phrase> phrases)154     private static void validatePhrases_2_0(
155             List<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Phrase> phrases) {
156         assertEquals(1, phrases.size());
157         assertEquals(123, phrases.get(0).id);
158         assertArrayEquals(new Integer[]{5, 6, 7}, phrases.get(0).users.toArray());
159         assertEquals("locale", phrases.get(0).locale);
160         assertEquals("text", phrases.get(0).text);
161         assertEquals(RecognitionMode.USER_AUTHENTICATION | RecognitionMode.USER_IDENTIFICATION,
162                 phrases.get(0).recognitionModes);
163     }
164 
165     static android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties
createDefaultProperties_2_0()166             createDefaultProperties_2_0() {
167         android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties properties =
168                 new android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties();
169         properties.implementor = "implementor";
170         properties.description = "description";
171         properties.version = 123;
172         properties.uuid.timeLow = 1;
173         properties.uuid.timeMid = 2;
174         properties.uuid.versionAndTimeHigh = 3;
175         properties.uuid.variantAndClockSeqHigh = 4;
176         properties.uuid.node = new byte[]{5, 6, 7, 8, 9, 10};
177 
178         properties.maxSoundModels = 456;
179         properties.maxKeyPhrases = 567;
180         properties.maxUsers = 678;
181         properties.recognitionModes =
182                 android.hardware.soundtrigger.V2_0.RecognitionMode.VOICE_TRIGGER
183                         | android.hardware.soundtrigger.V2_0.RecognitionMode.USER_IDENTIFICATION
184                         | android.hardware.soundtrigger.V2_0.RecognitionMode.USER_AUTHENTICATION
185                         | android.hardware.soundtrigger.V2_0.RecognitionMode.GENERIC_TRIGGER;
186         properties.captureTransition = true;
187         properties.maxBufferMs = 321;
188         properties.concurrentCapture = true;
189         properties.triggerInEvent = true;
190         properties.powerConsumptionMw = 432;
191         return properties;
192     }
193 
createDefaultProperties_2_3()194     static android.hardware.soundtrigger.V2_3.Properties createDefaultProperties_2_3() {
195         android.hardware.soundtrigger.V2_3.Properties properties =
196                 new android.hardware.soundtrigger.V2_3.Properties();
197         properties.base = createDefaultProperties_2_0();
198         properties.supportedModelArch = "supportedModelArch";
199         properties.audioCapabilities =
200                 android.hardware.soundtrigger.V2_3.AudioCapabilities.ECHO_CANCELLATION
201                         | android.hardware.soundtrigger.V2_3.AudioCapabilities.NOISE_SUPPRESSION;
202         return properties;
203     }
204 
createDefaultProperties()205     static Properties createDefaultProperties() {
206         Properties properties = new Properties();
207         properties.implementor = "implementor";
208         properties.description = "description";
209         properties.version = 123;
210         properties.uuid = "00000001-0002-0003-0004-05060708090a";
211         properties.maxSoundModels = 456;
212         properties.maxKeyPhrases = 567;
213         properties.maxUsers = 678;
214         properties.recognitionModes =
215                 RecognitionMode.VOICE_TRIGGER | RecognitionMode.USER_IDENTIFICATION
216                         | RecognitionMode.USER_AUTHENTICATION | RecognitionMode.GENERIC_TRIGGER;
217         properties.captureTransition = true;
218         properties.maxBufferMs = 321;
219         properties.concurrentCapture = true;
220         properties.triggerInEvent = true;
221         properties.powerConsumptionMw = 432;
222         properties.supportedModelArch = "supportedModelArch";
223         properties.audioCapabilities =
224                 AudioCapabilities.ECHO_CANCELLATION | AudioCapabilities.NOISE_SUPPRESSION;
225         return properties;
226     }
227 
validateDefaultProperties(Properties properties)228     static void validateDefaultProperties(Properties properties) {
229         validateDefaultProperties(properties,
230                 AudioCapabilities.ECHO_CANCELLATION | AudioCapabilities.NOISE_SUPPRESSION,
231                 "supportedModelArch");
232     }
233 
validateDefaultProperties(Properties properties, @AudioCapabilities int audioCapabilities, @NonNull String supportedModelArch)234     static void validateDefaultProperties(Properties properties,
235             @AudioCapabilities int audioCapabilities, @NonNull String supportedModelArch) {
236         assertEquals("implementor", properties.implementor);
237         assertEquals("description", properties.description);
238         assertEquals(123, properties.version);
239         assertEquals("00000001-0002-0003-0004-05060708090a", properties.uuid);
240         assertEquals(456, properties.maxSoundModels);
241         assertEquals(567, properties.maxKeyPhrases);
242         assertEquals(678, properties.maxUsers);
243         assertEquals(RecognitionMode.GENERIC_TRIGGER | RecognitionMode.USER_AUTHENTICATION
244                         | RecognitionMode.USER_IDENTIFICATION | RecognitionMode.VOICE_TRIGGER,
245                 properties.recognitionModes);
246         assertTrue(properties.captureTransition);
247         assertEquals(321, properties.maxBufferMs);
248         assertEquals(true, properties.concurrentCapture);
249         assertTrue(properties.triggerInEvent);
250         assertEquals(432, properties.powerConsumptionMw);
251         assertEquals(supportedModelArch, properties.supportedModelArch);
252         assertEquals(audioCapabilities, properties.audioCapabilities);
253     }
254 
createRecognitionConfig()255     static RecognitionConfig createRecognitionConfig() {
256         RecognitionConfig config = new RecognitionConfig();
257         config.captureRequested = true;
258         config.phraseRecognitionExtras = new PhraseRecognitionExtra[]{new PhraseRecognitionExtra()};
259         config.phraseRecognitionExtras[0].id = 123;
260         config.phraseRecognitionExtras[0].confidenceLevel = 4;
261         config.phraseRecognitionExtras[0].recognitionModes = 5;
262         config.phraseRecognitionExtras[0].levels = new ConfidenceLevel[]{new ConfidenceLevel()};
263         config.phraseRecognitionExtras[0].levels[0].userId = 234;
264         config.phraseRecognitionExtras[0].levels[0].levelPercent = 34;
265         config.data = new byte[]{5, 4, 3, 2, 1};
266         config.audioCapabilities =
267                 AudioCapabilities.ECHO_CANCELLATION | AudioCapabilities.NOISE_SUPPRESSION;
268         return config;
269     }
270 
validateRecognitionConfig_2_0( android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig config, int captureDevice, int captureHandle)271     static void validateRecognitionConfig_2_0(
272             android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig config,
273             int captureDevice, int captureHandle) {
274         assertTrue(config.captureRequested);
275         assertEquals(captureDevice, config.captureDevice);
276         assertEquals(captureHandle, config.captureHandle);
277         assertEquals(1, config.phrases.size());
278         android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halPhraseExtra =
279                 config.phrases.get(0);
280         assertEquals(123, halPhraseExtra.id);
281         assertEquals(4, halPhraseExtra.confidenceLevel);
282         assertEquals(5, halPhraseExtra.recognitionModes);
283         assertEquals(1, halPhraseExtra.levels.size());
284         android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel = halPhraseExtra.levels.get(0);
285         assertEquals(234, halLevel.userId);
286         assertEquals(34, halLevel.levelPercent);
287         assertArrayEquals(new Byte[]{5, 4, 3, 2, 1}, config.data.toArray());
288     }
289 
validateRecognitionConfig_2_1( android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig config, int captureDevice, int captureHandle)290     static void validateRecognitionConfig_2_1(
291             android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig config,
292             int captureDevice, int captureHandle) {
293         assertTrue(config.header.captureRequested);
294         assertEquals(captureDevice, config.header.captureDevice);
295         assertEquals(captureHandle, config.header.captureHandle);
296         assertEquals(1, config.header.phrases.size());
297         android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halPhraseExtra =
298                 config.header.phrases.get(0);
299         assertEquals(123, halPhraseExtra.id);
300         assertEquals(4, halPhraseExtra.confidenceLevel);
301         assertEquals(5, halPhraseExtra.recognitionModes);
302         assertEquals(1, halPhraseExtra.levels.size());
303         android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel = halPhraseExtra.levels.get(0);
304         assertEquals(234, halLevel.userId);
305         assertEquals(34, halLevel.levelPercent);
306         assertArrayEquals(new byte[]{5, 4, 3, 2, 1},
307                 HidlMemoryUtil.hidlMemoryToByteArray(config.data));
308     }
309 
validateRecognitionConfig_2_3( android.hardware.soundtrigger.V2_3.RecognitionConfig config, int captureDevice, int captureHandle)310     static void validateRecognitionConfig_2_3(
311             android.hardware.soundtrigger.V2_3.RecognitionConfig config, int captureDevice,
312             int captureHandle) {
313         validateRecognitionConfig_2_1(config.base, captureDevice, captureHandle);
314 
315         assertEquals(AudioCapabilities.ECHO_CANCELLATION | AudioCapabilities.NOISE_SUPPRESSION,
316                 config.audioCapabilities);
317     }
318 
createRecognitionEvent_2_0( int hwHandle, int status)319     static android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent createRecognitionEvent_2_0(
320             int hwHandle, int status) {
321         android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent halEvent =
322                 new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent();
323         halEvent.status = status;
324         halEvent.type = SoundModelType.GENERIC;
325         halEvent.model = hwHandle;
326         halEvent.captureAvailable = true;
327         // This field is ignored.
328         halEvent.captureSession = 9999;
329         halEvent.captureDelayMs = 234;
330         halEvent.capturePreambleMs = 345;
331         halEvent.triggerInData = true;
332         halEvent.audioConfig.sampleRateHz = 456;
333         halEvent.audioConfig.channelMask = AudioFormat.CHANNEL_IN_MONO;  // matches native
334         halEvent.audioConfig.format = AUDIO_FORMAT_MP3;
335         // hwEvent.audioConfig.offloadInfo is irrelevant.
336         halEvent.data.add((byte) 31);
337         halEvent.data.add((byte) 32);
338         halEvent.data.add((byte) 33);
339         return halEvent;
340     }
341 
createAudioFormatMp3()342     static AudioFormatDescription createAudioFormatMp3() {
343         AudioFormatDescription format = new AudioFormatDescription();
344         format.type = AudioFormatType.NON_PCM;
345         format.encoding = MediaFormat.MIMETYPE_AUDIO_MPEG;  // MP3
346         return format;
347     }
348 
createRecognitionEvent(@ecognitionStatus int status, boolean recognitionStillActive)349     static RecognitionEvent createRecognitionEvent(@RecognitionStatus int status,
350             boolean recognitionStillActive) {
351         RecognitionEvent event = new RecognitionEvent();
352         event.status = status;
353         event.type = SoundModelType.GENERIC;
354         event.captureAvailable = true;
355         event.captureDelayMs = 234;
356         event.capturePreambleMs = 345;
357         event.triggerInData = true;
358         event.audioConfig = new AudioConfig();
359         event.audioConfig.base = new AudioConfigBase();
360         event.audioConfig.base.sampleRate = 456;
361         event.audioConfig.base.channelMask = AudioChannelLayout.layoutMask(
362                 AudioChannelLayout.LAYOUT_MONO);
363         event.audioConfig.base.format = createAudioFormatMp3();
364         //event.audioConfig.offloadInfo is irrelevant.
365         event.data = new byte[]{31, 32, 33};
366         event.recognitionStillActive = recognitionStillActive;
367         return event;
368     }
369 
createRecognitionEvent_2_1(int hwHandle, int status)370     static ISoundTriggerHwCallback.RecognitionEvent createRecognitionEvent_2_1(int hwHandle,
371             int status) {
372         ISoundTriggerHwCallback.RecognitionEvent halEvent =
373                 new ISoundTriggerHwCallback.RecognitionEvent();
374         halEvent.header = createRecognitionEvent_2_0(hwHandle, status);
375         halEvent.header.data.clear();
376         halEvent.data = HidlMemoryUtil.byteArrayToHidlMemory(new byte[]{31, 32, 33});
377         return halEvent;
378     }
379 
validateRecognitionEvent(RecognitionEvent event, @RecognitionStatus int status, boolean recognitionStillActive)380     static void validateRecognitionEvent(RecognitionEvent event, @RecognitionStatus int status,
381             boolean recognitionStillActive) {
382         assertEquals(status, event.status);
383         assertEquals(SoundModelType.GENERIC, event.type);
384         assertTrue(event.captureAvailable);
385         assertEquals(234, event.captureDelayMs);
386         assertEquals(345, event.capturePreambleMs);
387         assertTrue(event.triggerInData);
388         assertEquals(456, event.audioConfig.base.sampleRate);
389         assertEquals(AudioChannelLayout.layoutMask(AudioChannelLayout.LAYOUT_MONO),
390                 event.audioConfig.base.channelMask);
391         assertEquals(createAudioFormatMp3(), event.audioConfig.base.format);
392         assertArrayEquals(new byte[]{31, 32, 33}, event.data);
393         assertEquals(recognitionStillActive, event.recognitionStillActive);
394     }
395 
createPhraseRecognitionEvent(@ecognitionStatus int status, boolean recognitionStillActive)396     static PhraseRecognitionEvent createPhraseRecognitionEvent(@RecognitionStatus int status,
397             boolean recognitionStillActive) {
398         PhraseRecognitionEvent event = new PhraseRecognitionEvent();
399         event.common = createRecognitionEvent(status, recognitionStillActive);
400 
401         PhraseRecognitionExtra extra = new PhraseRecognitionExtra();
402         extra.id = 123;
403         extra.confidenceLevel = 52;
404         extra.recognitionModes = RecognitionMode.VOICE_TRIGGER | RecognitionMode.GENERIC_TRIGGER;
405         ConfidenceLevel level = new ConfidenceLevel();
406         level.userId = 31;
407         level.levelPercent = 43;
408         extra.levels = new ConfidenceLevel[]{level};
409         event.phraseExtras = new PhraseRecognitionExtra[]{extra};
410         return event;
411     }
412 
createPhraseRecognitionEvent_2_0( int hwHandle, int status)413     static android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent createPhraseRecognitionEvent_2_0(
414             int hwHandle, int status) {
415         android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent halEvent =
416                 new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent();
417         halEvent.common = createRecognitionEvent_2_0(hwHandle, status);
418 
419         android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halExtra =
420                 new android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra();
421         halExtra.id = 123;
422         halExtra.confidenceLevel = 52;
423         halExtra.recognitionModes = android.hardware.soundtrigger.V2_0.RecognitionMode.VOICE_TRIGGER
424                 | android.hardware.soundtrigger.V2_0.RecognitionMode.GENERIC_TRIGGER;
425         android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel =
426                 new android.hardware.soundtrigger.V2_0.ConfidenceLevel();
427         halLevel.userId = 31;
428         halLevel.levelPercent = 43;
429         halExtra.levels.add(halLevel);
430         halEvent.phraseExtras.add(halExtra);
431         return halEvent;
432     }
433 
createPhraseRecognitionEvent_2_1( int hwHandle, int status)434     static ISoundTriggerHwCallback.PhraseRecognitionEvent createPhraseRecognitionEvent_2_1(
435             int hwHandle, int status) {
436         ISoundTriggerHwCallback.PhraseRecognitionEvent halEvent =
437                 new ISoundTriggerHwCallback.PhraseRecognitionEvent();
438         halEvent.common = createRecognitionEvent_2_1(hwHandle, status);
439 
440         android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halExtra =
441                 new android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra();
442         halExtra.id = 123;
443         halExtra.confidenceLevel = 52;
444         halExtra.recognitionModes = android.hardware.soundtrigger.V2_0.RecognitionMode.VOICE_TRIGGER
445                 | android.hardware.soundtrigger.V2_0.RecognitionMode.GENERIC_TRIGGER;
446         android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel =
447                 new android.hardware.soundtrigger.V2_0.ConfidenceLevel();
448         halLevel.userId = 31;
449         halLevel.levelPercent = 43;
450         halExtra.levels.add(halLevel);
451         halEvent.phraseExtras.add(halExtra);
452         return halEvent;
453     }
454 
validatePhraseRecognitionEvent(PhraseRecognitionEvent event, @RecognitionStatus int status, boolean recognitionStillActive)455     static void validatePhraseRecognitionEvent(PhraseRecognitionEvent event,
456             @RecognitionStatus int status, boolean recognitionStillActive) {
457         validateRecognitionEvent(event.common, status, recognitionStillActive);
458 
459         assertEquals(1, event.phraseExtras.length);
460         assertEquals(123, event.phraseExtras[0].id);
461         assertEquals(52, event.phraseExtras[0].confidenceLevel);
462         assertEquals(RecognitionMode.VOICE_TRIGGER | RecognitionMode.GENERIC_TRIGGER,
463                 event.phraseExtras[0].recognitionModes);
464         assertEquals(1, event.phraseExtras[0].levels.length);
465         assertEquals(31, event.phraseExtras[0].levels[0].userId);
466         assertEquals(43, event.phraseExtras[0].levels[0].levelPercent);
467     }
468 
byteArrayToParcelFileDescriptor(byte[] data)469     static ParcelFileDescriptor byteArrayToParcelFileDescriptor(byte[] data) {
470         try (SharedMemory shmem = SharedMemory.create("", data.length)) {
471             ByteBuffer buffer = shmem.mapReadWrite();
472             buffer.put(data);
473             return shmem.getFdDup();
474         } catch (Exception e) {
475             throw new RuntimeException(e);
476         }
477     }
478 }
479