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