• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.AidlConversion;
22 import android.media.audio.common.AudioConfig;
23 import android.media.soundtrigger.AudioCapabilities;
24 import android.media.soundtrigger.ConfidenceLevel;
25 import android.media.soundtrigger.ModelParameterRange;
26 import android.media.soundtrigger.Phrase;
27 import android.media.soundtrigger.PhraseRecognitionEvent;
28 import android.media.soundtrigger.PhraseRecognitionExtra;
29 import android.media.soundtrigger.PhraseSoundModel;
30 import android.media.soundtrigger.Properties;
31 import android.media.soundtrigger.RecognitionConfig;
32 import android.media.soundtrigger.RecognitionEvent;
33 import android.media.soundtrigger.RecognitionMode;
34 import android.media.soundtrigger.SoundModel;
35 import android.media.soundtrigger_middleware.PhraseRecognitionEventSys;
36 import android.media.soundtrigger_middleware.RecognitionEventSys;
37 import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
38 import android.os.ParcelFileDescriptor;
39 import android.os.SharedMemory;
40 import android.system.ErrnoException;
41 
42 import java.nio.ByteBuffer;
43 import java.util.ArrayList;
44 import java.util.Arrays;
45 import java.util.Locale;
46 import java.util.UUID;
47 
48 /** @hide */
49 public class ConversionUtil {
aidl2apiModuleDescriptor( SoundTriggerModuleDescriptor aidlDesc)50     public static SoundTrigger.ModuleProperties aidl2apiModuleDescriptor(
51             SoundTriggerModuleDescriptor aidlDesc) {
52         Properties properties = aidlDesc.properties;
53         return new SoundTrigger.ModuleProperties(
54                 aidlDesc.handle,
55                 properties.implementor,
56                 properties.description,
57                 properties.uuid,
58                 properties.version,
59                 properties.supportedModelArch,
60                 properties.maxSoundModels,
61                 properties.maxKeyPhrases,
62                 properties.maxUsers,
63                 aidl2apiRecognitionModes(properties.recognitionModes),
64                 properties.captureTransition,
65                 properties.maxBufferMs,
66                 properties.concurrentCapture,
67                 properties.powerConsumptionMw,
68                 properties.triggerInEvent,
69                 aidl2apiAudioCapabilities(properties.audioCapabilities)
70         );
71     }
72 
aidl2apiRecognitionModes(int aidlModes)73     public static int aidl2apiRecognitionModes(int aidlModes) {
74         int result = 0;
75         if ((aidlModes & RecognitionMode.VOICE_TRIGGER) != 0) {
76             result |= SoundTrigger.RECOGNITION_MODE_VOICE_TRIGGER;
77         }
78         if ((aidlModes & RecognitionMode.USER_IDENTIFICATION) != 0) {
79             result |= SoundTrigger.RECOGNITION_MODE_USER_IDENTIFICATION;
80         }
81         if ((aidlModes & RecognitionMode.USER_AUTHENTICATION) != 0) {
82             result |= SoundTrigger.RECOGNITION_MODE_USER_AUTHENTICATION;
83         }
84         if ((aidlModes & RecognitionMode.GENERIC_TRIGGER) != 0) {
85             result |= SoundTrigger.RECOGNITION_MODE_GENERIC;
86         }
87         return result;
88     }
89 
api2aidlRecognitionModes(int apiModes)90     public static int api2aidlRecognitionModes(int apiModes) {
91         int result = 0;
92         if ((apiModes & SoundTrigger.RECOGNITION_MODE_VOICE_TRIGGER) != 0) {
93             result |= RecognitionMode.VOICE_TRIGGER;
94         }
95         if ((apiModes & SoundTrigger.RECOGNITION_MODE_USER_IDENTIFICATION) != 0) {
96             result |= RecognitionMode.USER_IDENTIFICATION;
97         }
98         if ((apiModes & SoundTrigger.RECOGNITION_MODE_USER_AUTHENTICATION) != 0) {
99             result |= RecognitionMode.USER_AUTHENTICATION;
100         }
101         if ((apiModes & SoundTrigger.RECOGNITION_MODE_GENERIC) != 0) {
102             result |= RecognitionMode.GENERIC_TRIGGER;
103         }
104         return result;
105     }
106 
107 
api2aidlGenericSoundModel(SoundTrigger.GenericSoundModel apiModel)108     public static SoundModel api2aidlGenericSoundModel(SoundTrigger.GenericSoundModel apiModel) {
109         return api2aidlSoundModel(apiModel);
110     }
111 
api2aidlSoundModel(SoundTrigger.SoundModel apiModel)112     public static SoundModel api2aidlSoundModel(SoundTrigger.SoundModel apiModel) {
113         SoundModel aidlModel = new SoundModel();
114         aidlModel.type = apiModel.getType();
115         aidlModel.uuid = api2aidlUuid(apiModel.getUuid());
116         aidlModel.vendorUuid = api2aidlUuid(apiModel.getVendorUuid());
117         byte[] data = apiModel.getData();
118         aidlModel.data = byteArrayToSharedMemory(data, "SoundTrigger SoundModel");
119         aidlModel.dataSize = data.length;
120         return aidlModel;
121     }
122 
api2aidlUuid(UUID apiUuid)123     public static String api2aidlUuid(UUID apiUuid) {
124         return apiUuid.toString();
125     }
126 
api2aidlPhraseSoundModel( SoundTrigger.KeyphraseSoundModel apiModel)127     public static PhraseSoundModel api2aidlPhraseSoundModel(
128             SoundTrigger.KeyphraseSoundModel apiModel) {
129         PhraseSoundModel aidlModel = new PhraseSoundModel();
130         aidlModel.common = api2aidlSoundModel(apiModel);
131         aidlModel.phrases = new Phrase[apiModel.getKeyphrases().length];
132         for (int i = 0; i < apiModel.getKeyphrases().length; ++i) {
133             aidlModel.phrases[i] = api2aidlPhrase(apiModel.getKeyphrases()[i]);
134         }
135         return aidlModel;
136     }
137 
api2aidlPhrase(SoundTrigger.Keyphrase apiPhrase)138     public static Phrase api2aidlPhrase(SoundTrigger.Keyphrase apiPhrase) {
139         Phrase aidlPhrase = new Phrase();
140         aidlPhrase.id = apiPhrase.getId();
141         aidlPhrase.recognitionModes = api2aidlRecognitionModes(apiPhrase.getRecognitionModes());
142         aidlPhrase.users = Arrays.copyOf(apiPhrase.getUsers(), apiPhrase.getUsers().length);
143         aidlPhrase.locale = apiPhrase.getLocale().toLanguageTag();
144         aidlPhrase.text = apiPhrase.getText();
145         return aidlPhrase;
146     }
147 
aidl2apiPhrase(Phrase aidlPhrase)148     public static SoundTrigger.Keyphrase aidl2apiPhrase(Phrase aidlPhrase) {
149         return new SoundTrigger.Keyphrase(aidlPhrase.id,
150                 aidl2apiRecognitionModes(aidlPhrase.recognitionModes),
151                 new Locale.Builder().setLanguageTag(aidlPhrase.locale).build(),
152                 aidlPhrase.text,
153                 Arrays.copyOf(aidlPhrase.users, aidlPhrase.users.length));
154     }
155 
api2aidlRecognitionConfig( SoundTrigger.RecognitionConfig apiConfig)156     public static RecognitionConfig api2aidlRecognitionConfig(
157             SoundTrigger.RecognitionConfig apiConfig) {
158         RecognitionConfig aidlConfig = new RecognitionConfig();
159         aidlConfig.captureRequested = apiConfig.isCaptureRequested();
160         // apiConfig.isMultipleTriggersAllowed() is ignored by the lower layers.
161         aidlConfig.phraseRecognitionExtras =
162                 new PhraseRecognitionExtra[apiConfig.getKeyphrases().size()];
163         for (int i = 0; i < apiConfig.getKeyphrases().size(); ++i) {
164             aidlConfig.phraseRecognitionExtras[i] = api2aidlPhraseRecognitionExtra(
165                     apiConfig.getKeyphrases().get(i));
166         }
167         aidlConfig.data = Arrays.copyOf(apiConfig.getData(), apiConfig.getData().length);
168         aidlConfig.audioCapabilities = api2aidlAudioCapabilities(apiConfig.getAudioCapabilities());
169         return aidlConfig;
170     }
171 
aidl2apiRecognitionConfig( RecognitionConfig aidlConfig)172     public static SoundTrigger.RecognitionConfig aidl2apiRecognitionConfig(
173             RecognitionConfig aidlConfig) {
174         var keyphrases = new ArrayList<SoundTrigger.KeyphraseRecognitionExtra>(
175             aidlConfig.phraseRecognitionExtras.length);
176         for (var extras : aidlConfig.phraseRecognitionExtras) {
177             keyphrases.add(aidl2apiPhraseRecognitionExtra(extras));
178         }
179         return new SoundTrigger.RecognitionConfig.Builder()
180             .setCaptureRequested(aidlConfig.captureRequested)
181             .setMultipleTriggersAllowed(false)
182             .setKeyphrases(keyphrases)
183             .setData(Arrays.copyOf(aidlConfig.data, aidlConfig.data.length))
184             .setAudioCapabilities(aidl2apiAudioCapabilities(aidlConfig.audioCapabilities))
185             .build();
186     }
187 
api2aidlPhraseRecognitionExtra( SoundTrigger.KeyphraseRecognitionExtra apiExtra)188     public static PhraseRecognitionExtra api2aidlPhraseRecognitionExtra(
189             SoundTrigger.KeyphraseRecognitionExtra apiExtra) {
190         PhraseRecognitionExtra aidlExtra = new PhraseRecognitionExtra();
191         aidlExtra.id = apiExtra.id;
192         aidlExtra.recognitionModes = api2aidlRecognitionModes(apiExtra.recognitionModes);
193         aidlExtra.confidenceLevel = apiExtra.coarseConfidenceLevel;
194         aidlExtra.levels = new ConfidenceLevel[apiExtra.confidenceLevels.length];
195         for (int i = 0; i < apiExtra.confidenceLevels.length; ++i) {
196             aidlExtra.levels[i] = api2aidlConfidenceLevel(apiExtra.confidenceLevels[i]);
197         }
198         return aidlExtra;
199     }
200 
aidl2apiPhraseRecognitionExtra( PhraseRecognitionExtra aidlExtra)201     public static SoundTrigger.KeyphraseRecognitionExtra aidl2apiPhraseRecognitionExtra(
202             PhraseRecognitionExtra aidlExtra) {
203         SoundTrigger.ConfidenceLevel[] apiLevels =
204                 new SoundTrigger.ConfidenceLevel[aidlExtra.levels.length];
205         for (int i = 0; i < aidlExtra.levels.length; ++i) {
206             apiLevels[i] = aidl2apiConfidenceLevel(aidlExtra.levels[i]);
207         }
208         return new SoundTrigger.KeyphraseRecognitionExtra(aidlExtra.id,
209                 aidl2apiRecognitionModes(aidlExtra.recognitionModes),
210                 aidlExtra.confidenceLevel, apiLevels);
211     }
212 
api2aidlConfidenceLevel( SoundTrigger.ConfidenceLevel apiLevel)213     public static ConfidenceLevel api2aidlConfidenceLevel(
214             SoundTrigger.ConfidenceLevel apiLevel) {
215         ConfidenceLevel aidlLevel = new ConfidenceLevel();
216         aidlLevel.levelPercent = apiLevel.confidenceLevel;
217         aidlLevel.userId = apiLevel.userId;
218         return aidlLevel;
219     }
220 
aidl2apiConfidenceLevel( ConfidenceLevel apiLevel)221     public static SoundTrigger.ConfidenceLevel aidl2apiConfidenceLevel(
222             ConfidenceLevel apiLevel) {
223         return new SoundTrigger.ConfidenceLevel(apiLevel.userId, apiLevel.levelPercent);
224     }
225 
aidl2apiRecognitionEvent(int modelHandle, int captureSession, RecognitionEventSys aidlEvent)226     public static SoundTrigger.RecognitionEvent aidl2apiRecognitionEvent(int modelHandle,
227             int captureSession, RecognitionEventSys aidlEvent) {
228         RecognitionEvent recognitionEvent = aidlEvent.recognitionEvent;
229         // The API recognition event doesn't allow for a null audio format, even though it doesn't
230         // always make sense. We thus replace it with a default.
231         AudioFormat audioFormat = aidl2apiAudioFormatWithDefault(recognitionEvent.audioConfig,
232                 true /*isInput*/);
233         return new SoundTrigger.GenericRecognitionEvent(recognitionEvent.status, modelHandle,
234                 recognitionEvent.captureAvailable, captureSession, recognitionEvent.captureDelayMs,
235                 recognitionEvent.capturePreambleMs, recognitionEvent.triggerInData, audioFormat,
236                 recognitionEvent.data,
237                 recognitionEvent.recognitionStillActive, aidlEvent.halEventReceivedMillis,
238                 aidlEvent.token);
239     }
240 
aidl2apiPhraseRecognitionEvent( int modelHandle, int captureSession, PhraseRecognitionEventSys aidlEvent)241     public static SoundTrigger.RecognitionEvent aidl2apiPhraseRecognitionEvent(
242             int modelHandle, int captureSession, PhraseRecognitionEventSys aidlEvent) {
243         PhraseRecognitionEvent recognitionEvent = aidlEvent.phraseRecognitionEvent;
244         SoundTrigger.KeyphraseRecognitionExtra[] apiExtras =
245                 new SoundTrigger.KeyphraseRecognitionExtra[recognitionEvent.phraseExtras.length];
246         for (int i = 0; i < recognitionEvent.phraseExtras.length; ++i) {
247             apiExtras[i] = aidl2apiPhraseRecognitionExtra(recognitionEvent.phraseExtras[i]);
248         }
249         // The API recognition event doesn't allow for a null audio format, even though it doesn't
250         // always make sense. We thus replace it with a default.
251         AudioFormat audioFormat = aidl2apiAudioFormatWithDefault(
252                 recognitionEvent.common.audioConfig,
253                 true /*isInput*/);
254         return new SoundTrigger.KeyphraseRecognitionEvent(recognitionEvent.common.status,
255                 modelHandle,
256                 recognitionEvent.common.captureAvailable, captureSession,
257                 recognitionEvent.common.captureDelayMs,
258                 recognitionEvent.common.capturePreambleMs, recognitionEvent.common.triggerInData,
259                 audioFormat,
260                 recognitionEvent.common.data, apiExtras, aidlEvent.halEventReceivedMillis,
261                 aidlEvent.token);
262     }
263 
264     // In case of a null input returns a non-null valid output.
aidl2apiAudioFormatWithDefault( @ullable AudioConfig audioConfig, boolean isInput)265     public static AudioFormat aidl2apiAudioFormatWithDefault(
266             @Nullable AudioConfig audioConfig, boolean isInput) {
267         if (audioConfig != null) {
268             return AidlConversion.aidl2api_AudioConfig_AudioFormat(audioConfig, isInput);
269         }
270         return new AudioFormat.Builder()
271             .setSampleRate(48000)
272             .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
273             .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
274             .build();
275     }
276 
api2aidlModelParameter(int apiParam)277     public static int api2aidlModelParameter(int apiParam) {
278         switch (apiParam) {
279             case ModelParams.THRESHOLD_FACTOR:
280                 return android.media.soundtrigger.ModelParameter.THRESHOLD_FACTOR;
281             default:
282                 return android.media.soundtrigger.ModelParameter.INVALID;
283         }
284     }
285 
aidl2apiModelParameterRange( @ullable ModelParameterRange aidlRange)286     public static SoundTrigger.ModelParamRange aidl2apiModelParameterRange(
287             @Nullable ModelParameterRange aidlRange) {
288         if (aidlRange == null) {
289             return null;
290         }
291         return new SoundTrigger.ModelParamRange(aidlRange.minInclusive, aidlRange.maxInclusive);
292     }
293 
aidl2apiAudioCapabilities(int aidlCapabilities)294     public static int aidl2apiAudioCapabilities(int aidlCapabilities) {
295         int result = 0;
296         if ((aidlCapabilities & AudioCapabilities.ECHO_CANCELLATION) != 0) {
297             result |= SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_ECHO_CANCELLATION;
298         }
299         if ((aidlCapabilities & AudioCapabilities.NOISE_SUPPRESSION) != 0) {
300             result |= SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_NOISE_SUPPRESSION;
301         }
302         return result;
303     }
304 
api2aidlAudioCapabilities(int apiCapabilities)305     public static int api2aidlAudioCapabilities(int apiCapabilities) {
306         int result = 0;
307         if ((apiCapabilities & SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_ECHO_CANCELLATION)
308                 != 0) {
309             result |= AudioCapabilities.ECHO_CANCELLATION;
310         }
311         if ((apiCapabilities & SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_NOISE_SUPPRESSION)
312                 != 0) {
313             result |= AudioCapabilities.NOISE_SUPPRESSION;
314         }
315         return result;
316     }
317 
byteArrayToSharedMemory(byte[] data, String name)318     public static @Nullable ParcelFileDescriptor byteArrayToSharedMemory(byte[] data, String name) {
319         if (data.length == 0) {
320             return null;
321         }
322 
323         try {
324             SharedMemory shmem = SharedMemory.create(name != null ? name : "", data.length);
325             ByteBuffer buffer = shmem.mapReadWrite();
326             buffer.put(data);
327             shmem.unmap(buffer);
328             ParcelFileDescriptor fd = shmem.getFdDup();
329             shmem.close();
330             return fd;
331         } catch (Exception e) {
332             throw new RuntimeException(e);
333         }
334     }
335 
sharedMemoryToByteArray(@ullable ParcelFileDescriptor pfd, int size)336     public static byte[] sharedMemoryToByteArray(@Nullable ParcelFileDescriptor pfd, int size) {
337         if (pfd == null || size == 0) {
338             return new byte[0];
339         }
340         try (SharedMemory mem = SharedMemory.fromFileDescriptor(pfd)) {
341             ByteBuffer buffer = mem.mapReadOnly();
342             byte[] data = new byte[(size > mem.getSize()) ? mem.getSize() : size];
343             buffer.get(data);
344             mem.unmap(buffer);
345             return data;
346         } catch (ErrnoException e) {
347             throw new RuntimeException(e);
348         }
349     }
350 }
351