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.SoundTriggerModuleDescriptor; 36 import android.os.ParcelFileDescriptor; 37 import android.os.SharedMemory; 38 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 Properties 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 byte[] data = apiModel.getData(); 113 aidlModel.data = byteArrayToSharedMemory(data, "SoundTrigger SoundModel"); 114 aidlModel.dataSize = data.length; 115 return aidlModel; 116 } 117 api2aidlUuid(UUID apiUuid)118 public static String api2aidlUuid(UUID apiUuid) { 119 return apiUuid.toString(); 120 } 121 api2aidlPhraseSoundModel( SoundTrigger.KeyphraseSoundModel apiModel)122 public static PhraseSoundModel api2aidlPhraseSoundModel( 123 SoundTrigger.KeyphraseSoundModel apiModel) { 124 PhraseSoundModel aidlModel = new PhraseSoundModel(); 125 aidlModel.common = api2aidlSoundModel(apiModel); 126 aidlModel.phrases = new Phrase[apiModel.getKeyphrases().length]; 127 for (int i = 0; i < apiModel.getKeyphrases().length; ++i) { 128 aidlModel.phrases[i] = api2aidlPhrase(apiModel.getKeyphrases()[i]); 129 } 130 return aidlModel; 131 } 132 api2aidlPhrase(SoundTrigger.Keyphrase apiPhrase)133 public static Phrase api2aidlPhrase(SoundTrigger.Keyphrase apiPhrase) { 134 Phrase aidlPhrase = new Phrase(); 135 aidlPhrase.id = apiPhrase.getId(); 136 aidlPhrase.recognitionModes = api2aidlRecognitionModes(apiPhrase.getRecognitionModes()); 137 aidlPhrase.users = Arrays.copyOf(apiPhrase.getUsers(), apiPhrase.getUsers().length); 138 aidlPhrase.locale = apiPhrase.getLocale().toLanguageTag(); 139 aidlPhrase.text = apiPhrase.getText(); 140 return aidlPhrase; 141 } 142 api2aidlRecognitionConfig( SoundTrigger.RecognitionConfig apiConfig)143 public static RecognitionConfig api2aidlRecognitionConfig( 144 SoundTrigger.RecognitionConfig apiConfig) { 145 RecognitionConfig aidlConfig = new RecognitionConfig(); 146 aidlConfig.captureRequested = apiConfig.captureRequested; 147 // apiConfig.allowMultipleTriggers is ignored by the lower layers. 148 aidlConfig.phraseRecognitionExtras = 149 new PhraseRecognitionExtra[apiConfig.keyphrases.length]; 150 for (int i = 0; i < apiConfig.keyphrases.length; ++i) { 151 aidlConfig.phraseRecognitionExtras[i] = api2aidlPhraseRecognitionExtra( 152 apiConfig.keyphrases[i]); 153 } 154 aidlConfig.data = Arrays.copyOf(apiConfig.data, apiConfig.data.length); 155 aidlConfig.audioCapabilities = api2aidlAudioCapabilities(apiConfig.audioCapabilities); 156 return aidlConfig; 157 } 158 api2aidlPhraseRecognitionExtra( SoundTrigger.KeyphraseRecognitionExtra apiExtra)159 public static PhraseRecognitionExtra api2aidlPhraseRecognitionExtra( 160 SoundTrigger.KeyphraseRecognitionExtra apiExtra) { 161 PhraseRecognitionExtra aidlExtra = new PhraseRecognitionExtra(); 162 aidlExtra.id = apiExtra.id; 163 aidlExtra.recognitionModes = api2aidlRecognitionModes(apiExtra.recognitionModes); 164 aidlExtra.confidenceLevel = apiExtra.coarseConfidenceLevel; 165 aidlExtra.levels = new ConfidenceLevel[apiExtra.confidenceLevels.length]; 166 for (int i = 0; i < apiExtra.confidenceLevels.length; ++i) { 167 aidlExtra.levels[i] = api2aidlConfidenceLevel(apiExtra.confidenceLevels[i]); 168 } 169 return aidlExtra; 170 } 171 aidl2apiPhraseRecognitionExtra( PhraseRecognitionExtra aidlExtra)172 public static SoundTrigger.KeyphraseRecognitionExtra aidl2apiPhraseRecognitionExtra( 173 PhraseRecognitionExtra aidlExtra) { 174 SoundTrigger.ConfidenceLevel[] apiLevels = 175 new SoundTrigger.ConfidenceLevel[aidlExtra.levels.length]; 176 for (int i = 0; i < aidlExtra.levels.length; ++i) { 177 apiLevels[i] = aidl2apiConfidenceLevel(aidlExtra.levels[i]); 178 } 179 return new SoundTrigger.KeyphraseRecognitionExtra(aidlExtra.id, 180 aidl2apiRecognitionModes(aidlExtra.recognitionModes), 181 aidlExtra.confidenceLevel, apiLevels); 182 } 183 api2aidlConfidenceLevel( SoundTrigger.ConfidenceLevel apiLevel)184 public static ConfidenceLevel api2aidlConfidenceLevel( 185 SoundTrigger.ConfidenceLevel apiLevel) { 186 ConfidenceLevel aidlLevel = new ConfidenceLevel(); 187 aidlLevel.levelPercent = apiLevel.confidenceLevel; 188 aidlLevel.userId = apiLevel.userId; 189 return aidlLevel; 190 } 191 aidl2apiConfidenceLevel( ConfidenceLevel apiLevel)192 public static SoundTrigger.ConfidenceLevel aidl2apiConfidenceLevel( 193 ConfidenceLevel apiLevel) { 194 return new SoundTrigger.ConfidenceLevel(apiLevel.userId, apiLevel.levelPercent); 195 } 196 aidl2apiRecognitionEvent( int modelHandle, int captureSession, RecognitionEvent aidlEvent)197 public static SoundTrigger.RecognitionEvent aidl2apiRecognitionEvent( 198 int modelHandle, int captureSession, RecognitionEvent aidlEvent) { 199 // The API recognition event doesn't allow for a null audio format, even though it doesn't 200 // always make sense. We thus replace it with a default. 201 AudioFormat audioFormat = aidl2apiAudioFormatWithDefault( 202 aidlEvent.audioConfig, true /*isInput*/); 203 return new SoundTrigger.GenericRecognitionEvent( 204 aidlEvent.status, 205 modelHandle, aidlEvent.captureAvailable, captureSession, 206 aidlEvent.captureDelayMs, aidlEvent.capturePreambleMs, aidlEvent.triggerInData, 207 audioFormat, aidlEvent.data, aidlEvent.recognitionStillActive); 208 } 209 aidl2apiPhraseRecognitionEvent( int modelHandle, int captureSession, PhraseRecognitionEvent aidlEvent)210 public static SoundTrigger.RecognitionEvent aidl2apiPhraseRecognitionEvent( 211 int modelHandle, int captureSession, 212 PhraseRecognitionEvent aidlEvent) { 213 SoundTrigger.KeyphraseRecognitionExtra[] apiExtras = 214 new SoundTrigger.KeyphraseRecognitionExtra[aidlEvent.phraseExtras.length]; 215 for (int i = 0; i < aidlEvent.phraseExtras.length; ++i) { 216 apiExtras[i] = aidl2apiPhraseRecognitionExtra(aidlEvent.phraseExtras[i]); 217 } 218 // The API recognition event doesn't allow for a null audio format, even though it doesn't 219 // always make sense. We thus replace it with a default. 220 AudioFormat audioFormat = aidl2apiAudioFormatWithDefault( 221 aidlEvent.common.audioConfig, true /*isInput*/); 222 return new SoundTrigger.KeyphraseRecognitionEvent(aidlEvent.common.status, modelHandle, 223 aidlEvent.common.captureAvailable, 224 captureSession, aidlEvent.common.captureDelayMs, 225 aidlEvent.common.capturePreambleMs, aidlEvent.common.triggerInData, 226 audioFormat, aidlEvent.common.data, 227 apiExtras); 228 } 229 230 // In case of a null input returns a non-null valid output. aidl2apiAudioFormatWithDefault( @ullable AudioConfig audioConfig, boolean isInput)231 public static AudioFormat aidl2apiAudioFormatWithDefault( 232 @Nullable AudioConfig audioConfig, boolean isInput) { 233 if (audioConfig != null) { 234 return AidlConversion.aidl2api_AudioConfig_AudioFormat(audioConfig, isInput); 235 } 236 return new AudioFormat.Builder().build(); 237 } 238 api2aidlModelParameter(int apiParam)239 public static int api2aidlModelParameter(int apiParam) { 240 switch (apiParam) { 241 case ModelParams.THRESHOLD_FACTOR: 242 return android.media.soundtrigger.ModelParameter.THRESHOLD_FACTOR; 243 default: 244 return android.media.soundtrigger.ModelParameter.INVALID; 245 } 246 } 247 aidl2apiModelParameterRange( @ullable ModelParameterRange aidlRange)248 public static SoundTrigger.ModelParamRange aidl2apiModelParameterRange( 249 @Nullable ModelParameterRange aidlRange) { 250 if (aidlRange == null) { 251 return null; 252 } 253 return new SoundTrigger.ModelParamRange(aidlRange.minInclusive, aidlRange.maxInclusive); 254 } 255 aidl2apiAudioCapabilities(int aidlCapabilities)256 public static int aidl2apiAudioCapabilities(int aidlCapabilities) { 257 int result = 0; 258 if ((aidlCapabilities & AudioCapabilities.ECHO_CANCELLATION) != 0) { 259 result |= SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_ECHO_CANCELLATION; 260 } 261 if ((aidlCapabilities & AudioCapabilities.NOISE_SUPPRESSION) != 0) { 262 result |= SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_NOISE_SUPPRESSION; 263 } 264 return result; 265 } 266 api2aidlAudioCapabilities(int apiCapabilities)267 public static int api2aidlAudioCapabilities(int apiCapabilities) { 268 int result = 0; 269 if ((apiCapabilities & SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_ECHO_CANCELLATION) 270 != 0) { 271 result |= AudioCapabilities.ECHO_CANCELLATION; 272 } 273 if ((apiCapabilities & SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_NOISE_SUPPRESSION) 274 != 0) { 275 result |= AudioCapabilities.NOISE_SUPPRESSION; 276 } 277 return result; 278 } 279 byteArrayToSharedMemory(byte[] data, String name)280 private static @Nullable ParcelFileDescriptor byteArrayToSharedMemory(byte[] data, String name) { 281 if (data.length == 0) { 282 return null; 283 } 284 285 try { 286 SharedMemory shmem = SharedMemory.create(name != null ? name : "", data.length); 287 ByteBuffer buffer = shmem.mapReadWrite(); 288 buffer.put(data); 289 shmem.unmap(buffer); 290 ParcelFileDescriptor fd = shmem.getFdDup(); 291 shmem.close(); 292 return fd; 293 } catch (Exception e) { 294 throw new RuntimeException(e); 295 } 296 } 297 } 298