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 android.hardware.soundtrigger;
18 
19 import android.annotation.Nullable;
20 import android.media.AudioFormat;
21 import android.media.audio.common.AudioConfig;
22 import android.media.soundtrigger_middleware.AudioCapabilities;
23 import android.media.soundtrigger_middleware.ConfidenceLevel;
24 import android.media.soundtrigger_middleware.ModelParameterRange;
25 import android.media.soundtrigger_middleware.Phrase;
26 import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
27 import android.media.soundtrigger_middleware.PhraseRecognitionExtra;
28 import android.media.soundtrigger_middleware.PhraseSoundModel;
29 import android.media.soundtrigger_middleware.RecognitionConfig;
30 import android.media.soundtrigger_middleware.RecognitionEvent;
31 import android.media.soundtrigger_middleware.RecognitionMode;
32 import android.media.soundtrigger_middleware.SoundModel;
33 import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
34 import android.media.soundtrigger_middleware.SoundTriggerModuleProperties;
35 import android.os.SharedMemory;
36 import android.system.ErrnoException;
37 
38 import java.io.FileDescriptor;
39 import java.nio.ByteBuffer;
40 import java.util.Arrays;
41 import java.util.UUID;
42 
43 /** @hide */
44 class ConversionUtil {
aidl2apiModuleDescriptor( SoundTriggerModuleDescriptor aidlDesc)45     public static SoundTrigger.ModuleProperties aidl2apiModuleDescriptor(
46             SoundTriggerModuleDescriptor aidlDesc) {
47         SoundTriggerModuleProperties properties = aidlDesc.properties;
48         return new SoundTrigger.ModuleProperties(
49                 aidlDesc.handle,
50                 properties.implementor,
51                 properties.description,
52                 properties.uuid,
53                 properties.version,
54                 properties.supportedModelArch,
55                 properties.maxSoundModels,
56                 properties.maxKeyPhrases,
57                 properties.maxUsers,
58                 aidl2apiRecognitionModes(properties.recognitionModes),
59                 properties.captureTransition,
60                 properties.maxBufferMs,
61                 properties.concurrentCapture,
62                 properties.powerConsumptionMw,
63                 properties.triggerInEvent,
64                 aidl2apiAudioCapabilities(properties.audioCapabilities)
65         );
66     }
67 
aidl2apiRecognitionModes(int aidlModes)68     public static int aidl2apiRecognitionModes(int aidlModes) {
69         int result = 0;
70         if ((aidlModes & RecognitionMode.VOICE_TRIGGER) != 0) {
71             result |= SoundTrigger.RECOGNITION_MODE_VOICE_TRIGGER;
72         }
73         if ((aidlModes & RecognitionMode.USER_IDENTIFICATION) != 0) {
74             result |= SoundTrigger.RECOGNITION_MODE_USER_IDENTIFICATION;
75         }
76         if ((aidlModes & RecognitionMode.USER_AUTHENTICATION) != 0) {
77             result |= SoundTrigger.RECOGNITION_MODE_USER_AUTHENTICATION;
78         }
79         if ((aidlModes & RecognitionMode.GENERIC_TRIGGER) != 0) {
80             result |= SoundTrigger.RECOGNITION_MODE_GENERIC;
81         }
82         return result;
83     }
84 
api2aidlRecognitionModes(int apiModes)85     public static int api2aidlRecognitionModes(int apiModes) {
86         int result = 0;
87         if ((apiModes & SoundTrigger.RECOGNITION_MODE_VOICE_TRIGGER) != 0) {
88             result |= RecognitionMode.VOICE_TRIGGER;
89         }
90         if ((apiModes & SoundTrigger.RECOGNITION_MODE_USER_IDENTIFICATION) != 0) {
91             result |= RecognitionMode.USER_IDENTIFICATION;
92         }
93         if ((apiModes & SoundTrigger.RECOGNITION_MODE_USER_AUTHENTICATION) != 0) {
94             result |= RecognitionMode.USER_AUTHENTICATION;
95         }
96         if ((apiModes & SoundTrigger.RECOGNITION_MODE_GENERIC) != 0) {
97             result |= RecognitionMode.GENERIC_TRIGGER;
98         }
99         return result;
100     }
101 
102 
api2aidlGenericSoundModel(SoundTrigger.GenericSoundModel apiModel)103     public static SoundModel api2aidlGenericSoundModel(SoundTrigger.GenericSoundModel apiModel) {
104         return api2aidlSoundModel(apiModel);
105     }
106 
api2aidlSoundModel(SoundTrigger.SoundModel apiModel)107     public static SoundModel api2aidlSoundModel(SoundTrigger.SoundModel apiModel) {
108         SoundModel aidlModel = new SoundModel();
109         aidlModel.type = apiModel.getType();
110         aidlModel.uuid = api2aidlUuid(apiModel.getUuid());
111         aidlModel.vendorUuid = api2aidlUuid(apiModel.getVendorUuid());
112         aidlModel.data = byteArrayToSharedMemory(apiModel.getData(), "SoundTrigger SoundModel");
113         aidlModel.dataSize = apiModel.getData().length;
114         return aidlModel;
115     }
116 
api2aidlUuid(UUID apiUuid)117     public static String api2aidlUuid(UUID apiUuid) {
118         return apiUuid.toString();
119     }
120 
api2aidlPhraseSoundModel( SoundTrigger.KeyphraseSoundModel apiModel)121     public static PhraseSoundModel api2aidlPhraseSoundModel(
122             SoundTrigger.KeyphraseSoundModel apiModel) {
123         PhraseSoundModel aidlModel = new PhraseSoundModel();
124         aidlModel.common = api2aidlSoundModel(apiModel);
125         aidlModel.phrases = new Phrase[apiModel.getKeyphrases().length];
126         for (int i = 0; i < apiModel.getKeyphrases().length; ++i) {
127             aidlModel.phrases[i] = api2aidlPhrase(apiModel.getKeyphrases()[i]);
128         }
129         return aidlModel;
130     }
131 
api2aidlPhrase(SoundTrigger.Keyphrase apiPhrase)132     public static Phrase api2aidlPhrase(SoundTrigger.Keyphrase apiPhrase) {
133         Phrase aidlPhrase = new Phrase();
134         aidlPhrase.id = apiPhrase.getId();
135         aidlPhrase.recognitionModes = api2aidlRecognitionModes(apiPhrase.getRecognitionModes());
136         aidlPhrase.users = Arrays.copyOf(apiPhrase.getUsers(), apiPhrase.getUsers().length);
137         aidlPhrase.locale = apiPhrase.getLocale().toLanguageTag();
138         aidlPhrase.text = apiPhrase.getText();
139         return aidlPhrase;
140     }
141 
api2aidlRecognitionConfig( SoundTrigger.RecognitionConfig apiConfig)142     public static RecognitionConfig api2aidlRecognitionConfig(
143             SoundTrigger.RecognitionConfig apiConfig) {
144         RecognitionConfig aidlConfig = new RecognitionConfig();
145         aidlConfig.captureRequested = apiConfig.captureRequested;
146         // apiConfig.allowMultipleTriggers is ignored by the lower layers.
147         aidlConfig.phraseRecognitionExtras =
148                 new PhraseRecognitionExtra[apiConfig.keyphrases.length];
149         for (int i = 0; i < apiConfig.keyphrases.length; ++i) {
150             aidlConfig.phraseRecognitionExtras[i] = api2aidlPhraseRecognitionExtra(
151                     apiConfig.keyphrases[i]);
152         }
153         aidlConfig.data = Arrays.copyOf(apiConfig.data, apiConfig.data.length);
154         aidlConfig.audioCapabilities = api2aidlAudioCapabilities(apiConfig.audioCapabilities);
155         return aidlConfig;
156     }
157 
api2aidlPhraseRecognitionExtra( SoundTrigger.KeyphraseRecognitionExtra apiExtra)158     public static PhraseRecognitionExtra api2aidlPhraseRecognitionExtra(
159             SoundTrigger.KeyphraseRecognitionExtra apiExtra) {
160         PhraseRecognitionExtra aidlExtra = new PhraseRecognitionExtra();
161         aidlExtra.id = apiExtra.id;
162         aidlExtra.recognitionModes = api2aidlRecognitionModes(apiExtra.recognitionModes);
163         aidlExtra.confidenceLevel = apiExtra.coarseConfidenceLevel;
164         aidlExtra.levels = new ConfidenceLevel[apiExtra.confidenceLevels.length];
165         for (int i = 0; i < apiExtra.confidenceLevels.length; ++i) {
166             aidlExtra.levels[i] = api2aidlConfidenceLevel(apiExtra.confidenceLevels[i]);
167         }
168         return aidlExtra;
169     }
170 
aidl2apiPhraseRecognitionExtra( PhraseRecognitionExtra aidlExtra)171     public static SoundTrigger.KeyphraseRecognitionExtra aidl2apiPhraseRecognitionExtra(
172             PhraseRecognitionExtra aidlExtra) {
173         SoundTrigger.ConfidenceLevel[] apiLevels =
174                 new SoundTrigger.ConfidenceLevel[aidlExtra.levels.length];
175         for (int i = 0; i < aidlExtra.levels.length; ++i) {
176             apiLevels[i] = aidl2apiConfidenceLevel(aidlExtra.levels[i]);
177         }
178         return new SoundTrigger.KeyphraseRecognitionExtra(aidlExtra.id,
179                 aidl2apiRecognitionModes(aidlExtra.recognitionModes),
180                 aidlExtra.confidenceLevel, apiLevels);
181     }
182 
api2aidlConfidenceLevel( SoundTrigger.ConfidenceLevel apiLevel)183     public static ConfidenceLevel api2aidlConfidenceLevel(
184             SoundTrigger.ConfidenceLevel apiLevel) {
185         ConfidenceLevel aidlLevel = new ConfidenceLevel();
186         aidlLevel.levelPercent = apiLevel.confidenceLevel;
187         aidlLevel.userId = apiLevel.userId;
188         return aidlLevel;
189     }
190 
aidl2apiConfidenceLevel( ConfidenceLevel apiLevel)191     public static SoundTrigger.ConfidenceLevel aidl2apiConfidenceLevel(
192             ConfidenceLevel apiLevel) {
193         return new SoundTrigger.ConfidenceLevel(apiLevel.userId, apiLevel.levelPercent);
194     }
195 
aidl2apiRecognitionEvent( int modelHandle, RecognitionEvent aidlEvent)196     public static SoundTrigger.RecognitionEvent aidl2apiRecognitionEvent(
197             int modelHandle, RecognitionEvent aidlEvent) {
198         // The API recognition event doesn't allow for a null audio format, even though it doesn't
199         // always make sense. We thus replace it with a default.
200         AudioFormat audioFormat = aidl2apiAudioFormatWithDefault(aidlEvent.audioConfig);
201         return new SoundTrigger.GenericRecognitionEvent(
202                 aidlEvent.status,
203                 modelHandle, aidlEvent.captureAvailable, aidlEvent.captureSession,
204                 aidlEvent.captureDelayMs, aidlEvent.capturePreambleMs, aidlEvent.triggerInData,
205                 audioFormat, aidlEvent.data);
206     }
207 
aidl2apiPhraseRecognitionEvent( int modelHandle, PhraseRecognitionEvent aidlEvent)208     public static SoundTrigger.RecognitionEvent aidl2apiPhraseRecognitionEvent(
209             int modelHandle,
210             PhraseRecognitionEvent aidlEvent) {
211         SoundTrigger.KeyphraseRecognitionExtra[] apiExtras =
212                 new SoundTrigger.KeyphraseRecognitionExtra[aidlEvent.phraseExtras.length];
213         for (int i = 0; i < aidlEvent.phraseExtras.length; ++i) {
214             apiExtras[i] = aidl2apiPhraseRecognitionExtra(aidlEvent.phraseExtras[i]);
215         }
216         // The API recognition event doesn't allow for a null audio format, even though it doesn't
217         // always make sense. We thus replace it with a default.
218         AudioFormat audioFormat = aidl2apiAudioFormatWithDefault(aidlEvent.common.audioConfig);
219         return new SoundTrigger.KeyphraseRecognitionEvent(aidlEvent.common.status, modelHandle,
220                 aidlEvent.common.captureAvailable,
221                 aidlEvent.common.captureSession, aidlEvent.common.captureDelayMs,
222                 aidlEvent.common.capturePreambleMs, aidlEvent.common.triggerInData,
223                 audioFormat, aidlEvent.common.data,
224                 apiExtras);
225     }
226 
aidl2apiAudioFormat(AudioConfig audioConfig)227     public static AudioFormat aidl2apiAudioFormat(AudioConfig audioConfig) {
228         AudioFormat.Builder apiBuilder = new AudioFormat.Builder();
229         apiBuilder.setSampleRate(audioConfig.sampleRateHz);
230         apiBuilder.setChannelMask(aidl2apiChannelInMask(audioConfig.channelMask));
231         apiBuilder.setEncoding(aidl2apiEncoding(audioConfig.format));
232         return apiBuilder.build();
233     }
234 
235     // Same as above, but in case of a null input returns a non-null valid output.
aidl2apiAudioFormatWithDefault(@ullable AudioConfig audioConfig)236     public static AudioFormat aidl2apiAudioFormatWithDefault(@Nullable AudioConfig audioConfig) {
237         if (audioConfig != null) {
238             return aidl2apiAudioFormat(audioConfig);
239         }
240         return new AudioFormat.Builder().build();
241     }
242 
aidl2apiEncoding(int aidlFormat)243     public static int aidl2apiEncoding(int aidlFormat) {
244         switch (aidlFormat) {
245             case android.media.audio.common.AudioFormat.PCM
246                     | android.media.audio.common.AudioFormat.PCM_SUB_16_BIT:
247                 return AudioFormat.ENCODING_PCM_16BIT;
248 
249             case android.media.audio.common.AudioFormat.PCM
250                     | android.media.audio.common.AudioFormat.PCM_SUB_8_BIT:
251                 return AudioFormat.ENCODING_PCM_8BIT;
252 
253             case android.media.audio.common.AudioFormat.PCM
254                     | android.media.audio.common.AudioFormat.PCM_SUB_FLOAT:
255             case android.media.audio.common.AudioFormat.PCM
256                     | android.media.audio.common.AudioFormat.PCM_SUB_8_24_BIT:
257             case android.media.audio.common.AudioFormat.PCM
258                     | android.media.audio.common.AudioFormat.PCM_SUB_24_BIT_PACKED:
259             case android.media.audio.common.AudioFormat.PCM
260                     | android.media.audio.common.AudioFormat.PCM_SUB_32_BIT:
261                 return AudioFormat.ENCODING_PCM_FLOAT;
262 
263             case android.media.audio.common.AudioFormat.AC3:
264                 return AudioFormat.ENCODING_AC3;
265 
266             case android.media.audio.common.AudioFormat.E_AC3:
267                 return AudioFormat.ENCODING_E_AC3;
268 
269             case android.media.audio.common.AudioFormat.DTS:
270                 return AudioFormat.ENCODING_DTS;
271 
272             case android.media.audio.common.AudioFormat.DTS_HD:
273                 return AudioFormat.ENCODING_DTS_HD;
274 
275             case android.media.audio.common.AudioFormat.MP3:
276                 return AudioFormat.ENCODING_MP3;
277 
278             case android.media.audio.common.AudioFormat.AAC
279                     | android.media.audio.common.AudioFormat.AAC_SUB_LC:
280                 return AudioFormat.ENCODING_AAC_LC;
281 
282             case android.media.audio.common.AudioFormat.AAC
283                     | android.media.audio.common.AudioFormat.AAC_SUB_HE_V1:
284                 return AudioFormat.ENCODING_AAC_HE_V1;
285 
286             case android.media.audio.common.AudioFormat.AAC
287                     | android.media.audio.common.AudioFormat.AAC_SUB_HE_V2:
288                 return AudioFormat.ENCODING_AAC_HE_V2;
289 
290             case android.media.audio.common.AudioFormat.IEC61937:
291                 return AudioFormat.ENCODING_IEC61937;
292 
293             case android.media.audio.common.AudioFormat.DOLBY_TRUEHD:
294                 return AudioFormat.ENCODING_DOLBY_TRUEHD;
295 
296             case android.media.audio.common.AudioFormat.AAC
297                     | android.media.audio.common.AudioFormat.AAC_SUB_ELD:
298                 return AudioFormat.ENCODING_AAC_ELD;
299 
300             case android.media.audio.common.AudioFormat.AAC
301                     | android.media.audio.common.AudioFormat.AAC_SUB_XHE:
302                 return AudioFormat.ENCODING_AAC_XHE;
303 
304             case android.media.audio.common.AudioFormat.AC4:
305                 return AudioFormat.ENCODING_AC4;
306 
307             case android.media.audio.common.AudioFormat.E_AC3
308                     | android.media.audio.common.AudioFormat.E_AC3_SUB_JOC:
309                 return AudioFormat.ENCODING_E_AC3_JOC;
310 
311             case android.media.audio.common.AudioFormat.MAT:
312             case android.media.audio.common.AudioFormat.MAT
313                     | android.media.audio.common.AudioFormat.MAT_SUB_1_0:
314             case android.media.audio.common.AudioFormat.MAT
315                     | android.media.audio.common.AudioFormat.MAT_SUB_2_0:
316             case android.media.audio.common.AudioFormat.MAT
317                     | android.media.audio.common.AudioFormat.MAT_SUB_2_1:
318                 return AudioFormat.ENCODING_DOLBY_MAT;
319 
320             case android.media.audio.common.AudioFormat.DEFAULT:
321                 return AudioFormat.ENCODING_DEFAULT;
322 
323             default:
324                 return AudioFormat.ENCODING_INVALID;
325         }
326     }
327 
api2aidlModelParameter(int apiParam)328     public static int api2aidlModelParameter(int apiParam) {
329         switch (apiParam) {
330             case ModelParams.THRESHOLD_FACTOR:
331                 return android.media.soundtrigger_middleware.ModelParameter.THRESHOLD_FACTOR;
332             default:
333                 return android.media.soundtrigger_middleware.ModelParameter.INVALID;
334         }
335     }
336 
aidl2apiChannelInMask(int aidlMask)337     public static int aidl2apiChannelInMask(int aidlMask) {
338         // We're assuming AudioFormat.CHANNEL_IN_* constants are kept in sync with
339         // android.media.audio.common.AudioChannelMask.
340         return aidlMask;
341     }
342 
aidl2apiModelParameterRange( @ullable ModelParameterRange aidlRange)343     public static SoundTrigger.ModelParamRange aidl2apiModelParameterRange(
344             @Nullable ModelParameterRange aidlRange) {
345         if (aidlRange == null) {
346             return null;
347         }
348         return new SoundTrigger.ModelParamRange(aidlRange.minInclusive, aidlRange.maxInclusive);
349     }
350 
aidl2apiAudioCapabilities(int aidlCapabilities)351     public static int aidl2apiAudioCapabilities(int aidlCapabilities) {
352         int result = 0;
353         if ((aidlCapabilities & AudioCapabilities.ECHO_CANCELLATION) != 0) {
354             result |= SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_ECHO_CANCELLATION;
355         }
356         if ((aidlCapabilities & AudioCapabilities.NOISE_SUPPRESSION) != 0) {
357             result |= SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_NOISE_SUPPRESSION;
358         }
359         return result;
360     }
361 
api2aidlAudioCapabilities(int apiCapabilities)362     public static int api2aidlAudioCapabilities(int apiCapabilities) {
363         int result = 0;
364         if ((apiCapabilities & SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_ECHO_CANCELLATION)
365                 != 0) {
366             result |= AudioCapabilities.ECHO_CANCELLATION;
367         }
368         if ((apiCapabilities & SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_NOISE_SUPPRESSION)
369                 != 0) {
370             result |= AudioCapabilities.NOISE_SUPPRESSION;
371         }
372         return result;
373     }
374 
byteArrayToSharedMemory(byte[] data, String name)375     private static @Nullable FileDescriptor byteArrayToSharedMemory(byte[] data, String name) {
376         if (data.length == 0) {
377             return null;
378         }
379 
380         try {
381             SharedMemory shmem = SharedMemory.create(name != null ? name : "", data.length);
382             ByteBuffer buffer = shmem.mapReadWrite();
383             buffer.put(data);
384             shmem.unmap(buffer);
385             return shmem.getFileDescriptor();
386         } catch (ErrnoException e) {
387             throw new RuntimeException(e);
388         }
389     }
390 }
391