diff --git a/core/java/android/hardware/soundtrigger/ConversionUtil.java b/core/java/android/hardware/soundtrigger/ConversionUtil.java new file mode 100644 index 0000000000000..8231c58a105e6 --- /dev/null +++ b/core/java/android/hardware/soundtrigger/ConversionUtil.java @@ -0,0 +1,329 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.soundtrigger; + +import android.hardware.soundtrigger.ModelParams; +import android.media.AudioFormat; +import android.media.audio.common.AudioConfig; +import android.media.soundtrigger_middleware.ConfidenceLevel; +import android.media.soundtrigger_middleware.ModelParameterRange; +import android.media.soundtrigger_middleware.Phrase; +import android.media.soundtrigger_middleware.PhraseRecognitionEvent; +import android.media.soundtrigger_middleware.PhraseRecognitionExtra; +import android.media.soundtrigger_middleware.PhraseSoundModel; +import android.media.soundtrigger_middleware.RecognitionConfig; +import android.media.soundtrigger_middleware.RecognitionEvent; +import android.media.soundtrigger_middleware.RecognitionMode; +import android.media.soundtrigger_middleware.SoundModel; +import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor; +import android.media.soundtrigger_middleware.SoundTriggerModuleProperties; + +import android.annotation.Nullable; + +import java.util.Arrays; +import java.util.UUID; + +/** @hide */ +class ConversionUtil { + public static SoundTrigger.ModuleProperties aidl2apiModuleDescriptor( + SoundTriggerModuleDescriptor aidlDesc) { + SoundTriggerModuleProperties properties = aidlDesc.properties; + return new SoundTrigger.ModuleProperties( + aidlDesc.handle, + properties.implementor, + properties.description, + properties.uuid, + properties.version, + properties.maxSoundModels, + properties.maxKeyPhrases, + properties.maxUsers, + aidl2apiRecognitionModes(properties.recognitionModes), + properties.captureTransition, + properties.maxBufferMs, + properties.concurrentCapture, + properties.powerConsumptionMw, + properties.triggerInEvent + ); + } + + public static int aidl2apiRecognitionModes(int aidlModes) { + int result = 0; + if ((aidlModes & RecognitionMode.VOICE_TRIGGER) != 0) { + result |= SoundTrigger.RECOGNITION_MODE_VOICE_TRIGGER; + } + if ((aidlModes & RecognitionMode.USER_IDENTIFICATION) != 0) { + result |= SoundTrigger.RECOGNITION_MODE_USER_IDENTIFICATION; + } + if ((aidlModes & RecognitionMode.USER_AUTHENTICATION) != 0) { + result |= SoundTrigger.RECOGNITION_MODE_USER_AUTHENTICATION; + } + if ((aidlModes & RecognitionMode.GENERIC_TRIGGER) != 0) { + result |= SoundTrigger.RECOGNITION_MODE_GENERIC; + } + return result; + } + + public static int api2aidlRecognitionModes(int apiModes) { + int result = 0; + if ((apiModes & SoundTrigger.RECOGNITION_MODE_VOICE_TRIGGER) != 0) { + result |= RecognitionMode.VOICE_TRIGGER; + } + if ((apiModes & SoundTrigger.RECOGNITION_MODE_USER_IDENTIFICATION) != 0) { + result |= RecognitionMode.USER_IDENTIFICATION; + } + if ((apiModes & SoundTrigger.RECOGNITION_MODE_USER_AUTHENTICATION) != 0) { + result |= RecognitionMode.USER_AUTHENTICATION; + } + if ((apiModes & SoundTrigger.RECOGNITION_MODE_GENERIC) != 0) { + result |= RecognitionMode.GENERIC_TRIGGER; + } + return result; + } + + + public static SoundModel api2aidlGenericSoundModel(SoundTrigger.GenericSoundModel apiModel) { + return api2aidlSoundModel(apiModel); + } + + public static SoundModel api2aidlSoundModel(SoundTrigger.SoundModel apiModel) { + SoundModel aidlModel = new SoundModel(); + aidlModel.type = apiModel.type; + aidlModel.uuid = api2aidlUuid(apiModel.uuid); + aidlModel.vendorUuid = api2aidlUuid(apiModel.vendorUuid); + aidlModel.data = Arrays.copyOf(apiModel.data, apiModel.data.length); + return aidlModel; + } + + public static String api2aidlUuid(UUID apiUuid) { + return apiUuid.toString(); + } + + public static PhraseSoundModel api2aidlPhraseSoundModel( + SoundTrigger.KeyphraseSoundModel apiModel) { + PhraseSoundModel aidlModel = new PhraseSoundModel(); + aidlModel.common = api2aidlSoundModel(apiModel); + aidlModel.phrases = new Phrase[apiModel.keyphrases.length]; + for (int i = 0; i < apiModel.keyphrases.length; ++i) { + aidlModel.phrases[i] = api2aidlPhrase(apiModel.keyphrases[i]); + } + return aidlModel; + } + + public static Phrase api2aidlPhrase(SoundTrigger.Keyphrase apiPhrase) { + Phrase aidlPhrase = new Phrase(); + aidlPhrase.id = apiPhrase.id; + aidlPhrase.recognitionModes = api2aidlRecognitionModes(apiPhrase.recognitionModes); + aidlPhrase.users = Arrays.copyOf(apiPhrase.users, apiPhrase.users.length); + aidlPhrase.locale = apiPhrase.locale; + aidlPhrase.text = apiPhrase.text; + return aidlPhrase; + } + + public static RecognitionConfig api2aidlRecognitionConfig( + SoundTrigger.RecognitionConfig apiConfig) { + RecognitionConfig aidlConfig = new RecognitionConfig(); + aidlConfig.captureRequested = apiConfig.captureRequested; + // apiConfig.allowMultipleTriggers is ignored by the lower layers. + aidlConfig.phraseRecognitionExtras = + new PhraseRecognitionExtra[apiConfig.keyphrases.length]; + for (int i = 0; i < apiConfig.keyphrases.length; ++i) { + aidlConfig.phraseRecognitionExtras[i] = api2aidlPhraseRecognitionExtra( + apiConfig.keyphrases[i]); + } + aidlConfig.data = Arrays.copyOf(apiConfig.data, apiConfig.data.length); + return aidlConfig; + } + + public static PhraseRecognitionExtra api2aidlPhraseRecognitionExtra( + SoundTrigger.KeyphraseRecognitionExtra apiExtra) { + PhraseRecognitionExtra aidlExtra = new PhraseRecognitionExtra(); + aidlExtra.id = apiExtra.id; + aidlExtra.recognitionModes = api2aidlRecognitionModes(apiExtra.recognitionModes); + aidlExtra.confidenceLevel = apiExtra.coarseConfidenceLevel; + aidlExtra.levels = new ConfidenceLevel[apiExtra.confidenceLevels.length]; + for (int i = 0; i < apiExtra.confidenceLevels.length; ++i) { + aidlExtra.levels[i] = api2aidlConfidenceLevel(apiExtra.confidenceLevels[i]); + } + return aidlExtra; + } + + public static SoundTrigger.KeyphraseRecognitionExtra aidl2apiPhraseRecognitionExtra( + PhraseRecognitionExtra aidlExtra) { + SoundTrigger.ConfidenceLevel[] apiLevels = + new SoundTrigger.ConfidenceLevel[aidlExtra.levels.length]; + for (int i = 0; i < aidlExtra.levels.length; ++i) { + apiLevels[i] = aidl2apiConfidenceLevel(aidlExtra.levels[i]); + } + return new SoundTrigger.KeyphraseRecognitionExtra(aidlExtra.id, + aidl2apiRecognitionModes(aidlExtra.recognitionModes), + aidlExtra.confidenceLevel, apiLevels); + } + + public static ConfidenceLevel api2aidlConfidenceLevel( + SoundTrigger.ConfidenceLevel apiLevel) { + ConfidenceLevel aidlLevel = new ConfidenceLevel(); + aidlLevel.levelPercent = apiLevel.confidenceLevel; + aidlLevel.userId = apiLevel.userId; + return aidlLevel; + } + + public static SoundTrigger.ConfidenceLevel aidl2apiConfidenceLevel( + ConfidenceLevel apiLevel) { + return new SoundTrigger.ConfidenceLevel(apiLevel.userId, apiLevel.levelPercent); + } + + public static SoundTrigger.RecognitionEvent aidl2apiRecognitionEvent( + int modelHandle, RecognitionEvent aidlEvent) { + return new SoundTrigger.GenericRecognitionEvent( + aidlEvent.status, + modelHandle, aidlEvent.captureAvailable, aidlEvent.captureSession, + aidlEvent.captureDelayMs, aidlEvent.capturePreambleMs, aidlEvent.triggerInData, + aidl2apiAudioFormat(aidlEvent.audioConfig), aidlEvent.data); + } + + public static SoundTrigger.RecognitionEvent aidl2apiPhraseRecognitionEvent( + int modelHandle, + PhraseRecognitionEvent aidlEvent) { + SoundTrigger.KeyphraseRecognitionExtra[] apiExtras = + new SoundTrigger.KeyphraseRecognitionExtra[aidlEvent.phraseExtras.length]; + for (int i = 0; i < aidlEvent.phraseExtras.length; ++i) { + apiExtras[i] = aidl2apiPhraseRecognitionExtra(aidlEvent.phraseExtras[i]); + } + return new SoundTrigger.KeyphraseRecognitionEvent(aidlEvent.common.status, modelHandle, + aidlEvent.common.captureAvailable, + aidlEvent.common.captureSession, aidlEvent.common.captureDelayMs, + aidlEvent.common.capturePreambleMs, aidlEvent.common.triggerInData, + aidl2apiAudioFormat(aidlEvent.common.audioConfig), aidlEvent.common.data, + apiExtras); + } + + public static AudioFormat aidl2apiAudioFormat(AudioConfig audioConfig) { + AudioFormat.Builder apiBuilder = new AudioFormat.Builder(); + apiBuilder.setSampleRate(audioConfig.sampleRateHz); + apiBuilder.setChannelMask(aidl2apiChannelInMask(audioConfig.channelMask)); + apiBuilder.setEncoding(aidl2apiEncoding(audioConfig.format)); + return apiBuilder.build(); + } + + public static int aidl2apiEncoding(int aidlFormat) { + switch (aidlFormat) { + case android.media.audio.common.AudioFormat.PCM + | android.media.audio.common.AudioFormat.PCM_SUB_16_BIT: + return AudioFormat.ENCODING_PCM_16BIT; + + case android.media.audio.common.AudioFormat.PCM + | android.media.audio.common.AudioFormat.PCM_SUB_8_BIT: + return AudioFormat.ENCODING_PCM_8BIT; + + case android.media.audio.common.AudioFormat.PCM + | android.media.audio.common.AudioFormat.PCM_SUB_FLOAT: + case android.media.audio.common.AudioFormat.PCM + | android.media.audio.common.AudioFormat.PCM_SUB_8_24_BIT: + case android.media.audio.common.AudioFormat.PCM + | android.media.audio.common.AudioFormat.PCM_SUB_24_BIT_PACKED: + case android.media.audio.common.AudioFormat.PCM + | android.media.audio.common.AudioFormat.PCM_SUB_32_BIT: + return AudioFormat.ENCODING_PCM_FLOAT; + + case android.media.audio.common.AudioFormat.AC3: + return AudioFormat.ENCODING_AC3; + + case android.media.audio.common.AudioFormat.E_AC3: + return AudioFormat.ENCODING_E_AC3; + + case android.media.audio.common.AudioFormat.DTS: + return AudioFormat.ENCODING_DTS; + + case android.media.audio.common.AudioFormat.DTS_HD: + return AudioFormat.ENCODING_DTS_HD; + + case android.media.audio.common.AudioFormat.MP3: + return AudioFormat.ENCODING_MP3; + + case android.media.audio.common.AudioFormat.AAC + | android.media.audio.common.AudioFormat.AAC_SUB_LC: + return AudioFormat.ENCODING_AAC_LC; + + case android.media.audio.common.AudioFormat.AAC + | android.media.audio.common.AudioFormat.AAC_SUB_HE_V1: + return AudioFormat.ENCODING_AAC_HE_V1; + + case android.media.audio.common.AudioFormat.AAC + | android.media.audio.common.AudioFormat.AAC_SUB_HE_V2: + return AudioFormat.ENCODING_AAC_HE_V2; + + case android.media.audio.common.AudioFormat.IEC61937: + return AudioFormat.ENCODING_IEC61937; + + case android.media.audio.common.AudioFormat.DOLBY_TRUEHD: + return AudioFormat.ENCODING_DOLBY_TRUEHD; + + case android.media.audio.common.AudioFormat.AAC + | android.media.audio.common.AudioFormat.AAC_SUB_ELD: + return AudioFormat.ENCODING_AAC_ELD; + + case android.media.audio.common.AudioFormat.AAC + | android.media.audio.common.AudioFormat.AAC_SUB_XHE: + return AudioFormat.ENCODING_AAC_XHE; + + case android.media.audio.common.AudioFormat.AC4: + return AudioFormat.ENCODING_AC4; + + case android.media.audio.common.AudioFormat.E_AC3 + | android.media.audio.common.AudioFormat.E_AC3_SUB_JOC: + return AudioFormat.ENCODING_E_AC3_JOC; + + case android.media.audio.common.AudioFormat.MAT: + case android.media.audio.common.AudioFormat.MAT + | android.media.audio.common.AudioFormat.MAT_SUB_1_0: + case android.media.audio.common.AudioFormat.MAT + | android.media.audio.common.AudioFormat.MAT_SUB_2_0: + case android.media.audio.common.AudioFormat.MAT + | android.media.audio.common.AudioFormat.MAT_SUB_2_1: + return AudioFormat.ENCODING_DOLBY_MAT; + + case android.media.audio.common.AudioFormat.DEFAULT: + return AudioFormat.ENCODING_DEFAULT; + + default: + return AudioFormat.ENCODING_INVALID; + } + } + + public static int api2aidlModelParameter(int apiParam) { + switch (apiParam) { + case ModelParams.THRESHOLD_FACTOR: + return android.media.soundtrigger_middleware.ModelParameter.THRESHOLD_FACTOR; + default: + return android.media.soundtrigger_middleware.ModelParameter.INVALID; + } + } + + public static int aidl2apiChannelInMask(int aidlMask) { + // We're assuming AudioFormat.CHANNEL_IN_* constants are kept in sync with + // android.media.audio.common.AudioChannelMask. + return aidlMask; + } + + public static SoundTrigger.ModelParamRange aidl2apiModelParameterRange( + @Nullable ModelParameterRange aidlRange) { + if (aidlRange == null) { + return null; + } + return new SoundTrigger.ModelParamRange(aidlRange.minInclusive, aidlRange.maxInclusive); + } +} diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java index 86f3eec4109ee..5484df416e79a 100644 --- a/core/java/android/hardware/soundtrigger/SoundTrigger.java +++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java @@ -22,18 +22,29 @@ import static android.system.OsConstants.ENOSYS; import static android.system.OsConstants.EPERM; import static android.system.OsConstants.EPIPE; +import static java.util.Objects.requireNonNull; + import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.UnsupportedAppUsage; import android.app.ActivityThread; +import android.content.Context; import android.media.AudioFormat; +import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService; +import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor; import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; import android.os.Parcel; import android.os.Parcelable; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Log; import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.UUID; /** @@ -44,6 +55,7 @@ import java.util.UUID; */ @SystemApi public class SoundTrigger { + private static final String TAG = "SoundTrigger"; private SoundTrigger() { } @@ -119,15 +131,15 @@ public class SoundTrigger { * recognition callback event */ public final boolean returnsTriggerInEvent; - ModuleProperties(int id, String implementor, String description, - String uuid, int version, int maxSoundModels, int maxKeyphrases, + ModuleProperties(int id, @NonNull String implementor, @NonNull String description, + @NonNull String uuid, int version, int maxSoundModels, int maxKeyphrases, int maxUsers, int recognitionModes, boolean supportsCaptureTransition, int maxBufferMs, boolean supportsConcurrentCapture, int powerConsumptionMw, boolean returnsTriggerInEvent) { this.id = id; - this.implementor = implementor; - this.description = description; - this.uuid = UUID.fromString(uuid); + this.implementor = requireNonNull(implementor); + this.description = requireNonNull(description); + this.uuid = UUID.fromString(requireNonNull(uuid)); this.version = version; this.maxSoundModels = maxSoundModels; this.maxKeyphrases = maxKeyphrases; @@ -231,6 +243,7 @@ public class SoundTrigger { /** Unique sound model identifier */ @UnsupportedAppUsage + @NonNull public final UUID uuid; /** Sound model type (e.g. TYPE_KEYPHRASE); */ @@ -238,17 +251,20 @@ public class SoundTrigger { /** Unique sound model vendor identifier */ @UnsupportedAppUsage + @NonNull public final UUID vendorUuid; /** Opaque data. For use by vendor implementation and enrollment application */ @UnsupportedAppUsage + @NonNull public final byte[] data; - public SoundModel(UUID uuid, UUID vendorUuid, int type, byte[] data) { - this.uuid = uuid; - this.vendorUuid = vendorUuid; + public SoundModel(@NonNull UUID uuid, @Nullable UUID vendorUuid, int type, + @Nullable byte[] data) { + this.uuid = requireNonNull(uuid); + this.vendorUuid = vendorUuid != null ? vendorUuid : new UUID(0, 0); this.type = type; - this.data = data; + this.data = data != null ? data : new byte[0]; } @Override @@ -271,8 +287,6 @@ public class SoundTrigger { if (!(obj instanceof SoundModel)) return false; SoundModel other = (SoundModel) obj; - if (!Arrays.equals(data, other.data)) - return false; if (type != other.type) return false; if (uuid == null) { @@ -285,6 +299,8 @@ public class SoundTrigger { return false; } else if (!vendorUuid.equals(other.vendorUuid)) return false; + if (!Arrays.equals(data, other.data)) + return false; return true; } } @@ -306,24 +322,28 @@ public class SoundTrigger { /** Locale of the keyphrase. JAVA Locale string e.g en_US */ @UnsupportedAppUsage + @NonNull public final String locale; /** Key phrase text */ @UnsupportedAppUsage + @NonNull public final String text; /** Users this key phrase has been trained for. countains sound trigger specific user IDs * derived from system user IDs {@link android.os.UserHandle#getIdentifier()}. */ @UnsupportedAppUsage + @NonNull public final int[] users; @UnsupportedAppUsage - public Keyphrase(int id, int recognitionModes, String locale, String text, int[] users) { + public Keyphrase(int id, int recognitionModes, @NonNull String locale, @NonNull String text, + @Nullable int[] users) { this.id = id; this.recognitionModes = recognitionModes; - this.locale = locale; - this.text = text; - this.users = users; + this.locale = requireNonNull(locale); + this.text = requireNonNull(text); + this.users = users != null ? users : new int[0]; } public static final @android.annotation.NonNull Parcelable.Creator CREATOR @@ -427,13 +447,15 @@ public class SoundTrigger { public static class KeyphraseSoundModel extends SoundModel implements Parcelable { /** Key phrases in this sound model */ @UnsupportedAppUsage + @NonNull public final Keyphrase[] keyphrases; // keyword phrases in model @UnsupportedAppUsage public KeyphraseSoundModel( - UUID uuid, UUID vendorUuid, byte[] data, Keyphrase[] keyphrases) { + @NonNull UUID uuid, @NonNull UUID vendorUuid, @Nullable byte[] data, + @Nullable Keyphrase[] keyphrases) { super(uuid, vendorUuid, TYPE_KEYPHRASE, data); - this.keyphrases = keyphrases; + this.keyphrases = keyphrases != null ? keyphrases : new Keyphrase[0]; } public static final @android.annotation.NonNull Parcelable.Creator CREATOR @@ -528,7 +550,8 @@ public class SoundTrigger { }; @UnsupportedAppUsage - public GenericSoundModel(UUID uuid, UUID vendorUuid, byte[] data) { + public GenericSoundModel(@NonNull UUID uuid, @NonNull UUID vendorUuid, + @Nullable byte[] data) { super(uuid, vendorUuid, TYPE_GENERIC_SOUND, data); } @@ -648,6 +671,12 @@ public class SoundTrigger { * @hide */ public static final int RECOGNITION_MODE_USER_AUTHENTICATION = 0x4; + /** + * Generic (non-speech) recognition. + * + * @hide + */ + public static final int RECOGNITION_MODE_GENERIC = 0x8; /** * Status codes for {@link RecognitionEvent} @@ -739,6 +768,7 @@ public class SoundTrigger { * * @hide */ + @NonNull public final AudioFormat captureFormat; /** * Opaque data for use by system applications who know about voice engine internals, @@ -747,13 +777,14 @@ public class SoundTrigger { * @hide */ @UnsupportedAppUsage + @NonNull public final byte[] data; /** @hide */ @UnsupportedAppUsage public RecognitionEvent(int status, int soundModelHandle, boolean captureAvailable, int captureSession, int captureDelayMs, int capturePreambleMs, - boolean triggerInData, AudioFormat captureFormat, byte[] data) { + boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data) { this.status = status; this.soundModelHandle = soundModelHandle; this.captureAvailable = captureAvailable; @@ -761,8 +792,8 @@ public class SoundTrigger { this.captureDelayMs = captureDelayMs; this.capturePreambleMs = capturePreambleMs; this.triggerInData = triggerInData; - this.captureFormat = captureFormat; - this.data = data; + this.captureFormat = requireNonNull(captureFormat); + this.data = data != null ? data : new byte[0]; } /** @@ -965,19 +996,21 @@ public class SoundTrigger { /** List of all keyphrases in the sound model for which recognition should be performed with * options for each keyphrase. */ @UnsupportedAppUsage + @NonNull public final KeyphraseRecognitionExtra keyphrases[]; /** Opaque data for use by system applications who know about voice engine internals, * typically during enrollment. */ @UnsupportedAppUsage + @NonNull public final byte[] data; @UnsupportedAppUsage public RecognitionConfig(boolean captureRequested, boolean allowMultipleTriggers, - KeyphraseRecognitionExtra[] keyphrases, byte[] data) { + @Nullable KeyphraseRecognitionExtra[] keyphrases, @Nullable byte[] data) { this.captureRequested = captureRequested; this.allowMultipleTriggers = allowMultipleTriggers; - this.keyphrases = keyphrases; - this.data = data; + this.keyphrases = keyphrases != null ? keyphrases : new KeyphraseRecognitionExtra[0]; + this.data = data != null ? data : new byte[0]; } public static final @android.annotation.NonNull Parcelable.Creator CREATOR @@ -1126,15 +1159,17 @@ public class SoundTrigger { /** Confidence levels for all users recognized (KeyphraseRecognitionEvent) or to * be recognized (RecognitionConfig) */ @UnsupportedAppUsage + @NonNull public final ConfidenceLevel[] confidenceLevels; @UnsupportedAppUsage public KeyphraseRecognitionExtra(int id, int recognitionModes, int coarseConfidenceLevel, - ConfidenceLevel[] confidenceLevels) { + @Nullable ConfidenceLevel[] confidenceLevels) { this.id = id; this.recognitionModes = recognitionModes; this.coarseConfidenceLevel = coarseConfidenceLevel; - this.confidenceLevels = confidenceLevels; + this.confidenceLevels = + confidenceLevels != null ? confidenceLevels : new ConfidenceLevel[0]; } public static final @android.annotation.NonNull Parcelable.Creator CREATOR @@ -1217,16 +1252,18 @@ public class SoundTrigger { public static class KeyphraseRecognitionEvent extends RecognitionEvent implements Parcelable { /** Indicates if the key phrase is present in the buffered audio available for capture */ @UnsupportedAppUsage + @NonNull public final KeyphraseRecognitionExtra[] keyphraseExtras; @UnsupportedAppUsage public KeyphraseRecognitionEvent(int status, int soundModelHandle, boolean captureAvailable, int captureSession, int captureDelayMs, int capturePreambleMs, - boolean triggerInData, AudioFormat captureFormat, byte[] data, - KeyphraseRecognitionExtra[] keyphraseExtras) { + boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data, + @Nullable KeyphraseRecognitionExtra[] keyphraseExtras) { super(status, soundModelHandle, captureAvailable, captureSession, captureDelayMs, capturePreambleMs, triggerInData, captureFormat, data); - this.keyphraseExtras = keyphraseExtras; + this.keyphraseExtras = + keyphraseExtras != null ? keyphraseExtras : new KeyphraseRecognitionExtra[0]; } public static final @android.annotation.NonNull Parcelable.Creator CREATOR @@ -1343,8 +1380,8 @@ public class SoundTrigger { @UnsupportedAppUsage public GenericRecognitionEvent(int status, int soundModelHandle, boolean captureAvailable, int captureSession, int captureDelayMs, - int capturePreambleMs, boolean triggerInData, AudioFormat captureFormat, - byte[] data) { + int capturePreambleMs, boolean triggerInData, @NonNull AudioFormat captureFormat, + @Nullable byte[] data) { super(status, soundModelHandle, captureAvailable, captureSession, captureDelayMs, capturePreambleMs, triggerInData, captureFormat, data); @@ -1408,13 +1445,14 @@ public class SoundTrigger { /** The updated sound model handle */ public final int soundModelHandle; /** New sound model data */ + @NonNull public final byte[] data; @UnsupportedAppUsage - SoundModelEvent(int status, int soundModelHandle, byte[] data) { + SoundModelEvent(int status, int soundModelHandle, @Nullable byte[] data) { this.status = status; this.soundModelHandle = soundModelHandle; - this.data = data; + this.data = data != null ? data : new byte[0]; } public static final @android.annotation.NonNull Parcelable.Creator CREATOR @@ -1498,8 +1536,9 @@ public class SoundTrigger { * @hide */ public static final int SERVICE_STATE_DISABLED = 1; - - /** + private static Object mServiceLock = new Object(); + private static ISoundTriggerMiddlewareService mService; + /** * @return returns current package name. */ static String getCurrentOpPackageName() { @@ -1523,24 +1562,21 @@ public class SoundTrigger { * @hide */ @UnsupportedAppUsage - public static int listModules(ArrayList modules) { - return listModules(getCurrentOpPackageName(), modules); + public static int listModules(@NonNull ArrayList modules) { + try { + SoundTriggerModuleDescriptor[] descs = getService().listModules(); + modules.clear(); + modules.ensureCapacity(descs.length); + for (SoundTriggerModuleDescriptor desc : descs) { + modules.add(ConversionUtil.aidl2apiModuleDescriptor(desc)); + } + return STATUS_OK; + } catch (RemoteException e) { + Log.e(TAG, "Exception caught", e); + return STATUS_DEAD_OBJECT; + } } - /** - * Returns a list of descriptors for all hardware modules loaded. - * @param opPackageName - * @param modules A ModuleProperties array where the list will be returned. - * @return - {@link #STATUS_OK} in case of success - * - {@link #STATUS_ERROR} in case of unspecified error - * - {@link #STATUS_PERMISSION_DENIED} if the caller does not have system permission - * - {@link #STATUS_NO_INIT} if the native service cannot be reached - * - {@link #STATUS_BAD_VALUE} if modules is null - * - {@link #STATUS_DEAD_OBJECT} if the binder transaction to the native service fails - */ - private static native int listModules(String opPackageName, - ArrayList modules); - /** * Get an interface on a hardware module to control sound models and recognition on * this module. @@ -1553,14 +1589,40 @@ public class SoundTrigger { * @hide */ @UnsupportedAppUsage - public static SoundTriggerModule attachModule(int moduleId, - StatusListener listener, - Handler handler) { - if (listener == null) { + public static @NonNull SoundTriggerModule attachModule(int moduleId, + @NonNull StatusListener listener, + @Nullable Handler handler) { + Looper looper = handler != null ? handler.getLooper() : Looper.getMainLooper(); + try { + return new SoundTriggerModule(getService(), moduleId, listener, looper); + } catch (RemoteException e) { + Log.e(TAG, "", e); return null; } - SoundTriggerModule module = new SoundTriggerModule(moduleId, listener, handler); - return module; + } + + private static ISoundTriggerMiddlewareService getService() { + synchronized (mServiceLock) { + while (true) { + IBinder binder = null; + try { + binder = + ServiceManager.getServiceOrThrow( + Context.SOUND_TRIGGER_MIDDLEWARE_SERVICE); + binder.linkToDeath(() -> { + synchronized (mServiceLock) { + mService = null; + } + }, 0); + mService = ISoundTriggerMiddlewareService.Stub.asInterface(binder); + break; + } catch (Exception e) { + Log.e(TAG, "Failed to bind to soundtrigger service", e); + } + } + return mService; + } + } /** diff --git a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java index b16ef5c433463..7cf5600192394 100644 --- a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java +++ b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java @@ -16,14 +16,23 @@ package android.hardware.soundtrigger; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UnsupportedAppUsage; -import android.hardware.soundtrigger.SoundTrigger.ModelParamRange; +import android.media.soundtrigger_middleware.ISoundTriggerCallback; +import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService; +import android.media.soundtrigger_middleware.ISoundTriggerModule; +import android.media.soundtrigger_middleware.ModelParameterRange; +import android.media.soundtrigger_middleware.PhraseRecognitionEvent; +import android.media.soundtrigger_middleware.PhraseSoundModel; +import android.media.soundtrigger_middleware.RecognitionEvent; +import android.media.soundtrigger_middleware.SoundModel; import android.os.Handler; +import android.os.IBinder; import android.os.Looper; import android.os.Message; - -import java.lang.ref.WeakReference; +import android.os.RemoteException; +import android.util.Log; /** * The SoundTriggerModule provides APIs to control sound models and sound detection @@ -32,39 +41,47 @@ import java.lang.ref.WeakReference; * @hide */ public class SoundTriggerModule { - @UnsupportedAppUsage - private long mNativeContext; + private static final String TAG = "SoundTriggerModule"; - @UnsupportedAppUsage - private int mId; - private NativeEventHandlerDelegate mEventHandlerDelegate; - - // to be kept in sync with core/jni/android_hardware_SoundTrigger.cpp private static final int EVENT_RECOGNITION = 1; private static final int EVENT_SERVICE_DIED = 2; - private static final int EVENT_SOUNDMODEL = 3; - private static final int EVENT_SERVICE_STATE_CHANGE = 4; + private static final int EVENT_SERVICE_STATE_CHANGE = 3; + @UnsupportedAppUsage + private int mId; + private EventHandlerDelegate mEventHandlerDelegate; + private ISoundTriggerModule mService; - SoundTriggerModule(int moduleId, SoundTrigger.StatusListener listener, Handler handler) { + SoundTriggerModule(@NonNull ISoundTriggerMiddlewareService service, + int moduleId, @NonNull SoundTrigger.StatusListener listener, @NonNull Looper looper) + throws RemoteException { mId = moduleId; - mEventHandlerDelegate = new NativeEventHandlerDelegate(listener, handler); - native_setup(SoundTrigger.getCurrentOpPackageName(), - new WeakReference(this)); + mEventHandlerDelegate = new EventHandlerDelegate(listener, looper); + mService = service.attach(moduleId, mEventHandlerDelegate); + mService.asBinder().linkToDeath(mEventHandlerDelegate, 0); } - private native void native_setup(String opPackageName, Object moduleThis); @Override protected void finalize() { - native_finalize(); + detach(); } - private native void native_finalize(); /** * Detach from this module. The {@link SoundTrigger.StatusListener} callback will not be called * anymore and associated resources will be released. - * */ + * All models must have been unloaded prior to detaching. + */ @UnsupportedAppUsage - public native void detach(); + public synchronized void detach() { + try { + if (mService != null) { + mService.asBinder().unlinkToDeath(mEventHandlerDelegate, 0); + mService.detach(); + mService = null; + } + } catch (Exception e) { + handleException(e); + } + } /** * Load a {@link SoundTrigger.SoundModel} to the hardware. A sound model must be loaded in @@ -82,7 +99,26 @@ public class SoundTriggerModule { * - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence */ @UnsupportedAppUsage - public native int loadSoundModel(SoundTrigger.SoundModel model, int[] soundModelHandle); + public synchronized int loadSoundModel(@NonNull SoundTrigger.SoundModel model, + @NonNull int[] soundModelHandle) { + try { + if (model instanceof SoundTrigger.GenericSoundModel) { + SoundModel aidlModel = ConversionUtil.api2aidlGenericSoundModel( + (SoundTrigger.GenericSoundModel) model); + soundModelHandle[0] = mService.loadModel(aidlModel); + return SoundTrigger.STATUS_OK; + } + if (model instanceof SoundTrigger.KeyphraseSoundModel) { + PhraseSoundModel aidlModel = ConversionUtil.api2aidlPhraseSoundModel( + (SoundTrigger.KeyphraseSoundModel) model); + soundModelHandle[0] = mService.loadPhraseModel(aidlModel); + return SoundTrigger.STATUS_OK; + } + return SoundTrigger.STATUS_BAD_VALUE; + } catch (Exception e) { + return handleException(e); + } + } /** * Unload a {@link SoundTrigger.SoundModel} and abort any pendiong recognition @@ -97,7 +133,14 @@ public class SoundTriggerModule { * service fails */ @UnsupportedAppUsage - public native int unloadSoundModel(int soundModelHandle); + public synchronized int unloadSoundModel(int soundModelHandle) { + try { + mService.unloadModel(soundModelHandle); + return SoundTrigger.STATUS_OK; + } catch (Exception e) { + return handleException(e); + } + } /** * Start listening to all key phrases in a {@link SoundTrigger.SoundModel}. @@ -117,7 +160,16 @@ public class SoundTriggerModule { * - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence */ @UnsupportedAppUsage - public native int startRecognition(int soundModelHandle, SoundTrigger.RecognitionConfig config); + public synchronized int startRecognition(int soundModelHandle, + SoundTrigger.RecognitionConfig config) { + try { + mService.startRecognition(soundModelHandle, + ConversionUtil.api2aidlRecognitionConfig(config)); + return SoundTrigger.STATUS_OK; + } catch (Exception e) { + return handleException(e); + } + } /** * Stop listening to all key phrases in a {@link SoundTrigger.SoundModel} @@ -133,12 +185,20 @@ public class SoundTriggerModule { * - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence */ @UnsupportedAppUsage - public native int stopRecognition(int soundModelHandle); + public synchronized int stopRecognition(int soundModelHandle) { + try { + mService.stopRecognition(soundModelHandle); + return SoundTrigger.STATUS_OK; + } catch (Exception e) { + return handleException(e); + } + } /** * Get the current state of a {@link SoundTrigger.SoundModel}. - * The state will be returned asynchronously as a {@link SoundTrigger#RecognitionEvent} - * in the callback registered in the {@link SoundTrigger.startRecognition} method. + * The state will be returned asynchronously as a {@link SoundTrigger.RecognitionEvent} + * in the callback registered in the + * {@link SoundTrigger#attachModule(int, SoundTrigger.StatusListener, Handler)} method. * @param soundModelHandle The sound model handle indicating which model's state to return * @return - {@link SoundTrigger#STATUS_OK} in case of success * - {@link SoundTrigger#STATUS_ERROR} in case of unspecified error @@ -150,46 +210,71 @@ public class SoundTriggerModule { * service fails * - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence */ - public native int getModelState(int soundModelHandle); + public synchronized int getModelState(int soundModelHandle) { + try { + mService.forceRecognitionEvent(soundModelHandle); + return SoundTrigger.STATUS_OK; + } catch (Exception e) { + return handleException(e); + } + } /** * Set a model specific {@link ModelParams} with the given value. This - * parameter will keep its value for the duration the model is loaded regardless of starting and + * parameter will keep its value for the duration the model is loaded regardless of starting + * and * stopping recognition. Once the model is unloaded, the value will be lost. - * {@link SoundTriggerModule#isParameterSupported} should be checked first before calling this - * method. + * {@link SoundTriggerModule#queryParameter(int, int)} should be checked first before calling + * this method. * * @param soundModelHandle handle of model to apply parameter - * @param modelParam {@link ModelParams} - * @param value Value to set + * @param modelParam {@link ModelParams} + * @param value Value to set * @return - {@link SoundTrigger#STATUS_OK} in case of success - * - {@link SoundTrigger#STATUS_NO_INIT} if the native service cannot be reached - * - {@link SoundTrigger#STATUS_BAD_VALUE} invalid input parameter - * - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence or - * if API is not supported by HAL + * - {@link SoundTrigger#STATUS_NO_INIT} if the native service cannot be reached + * - {@link SoundTrigger#STATUS_BAD_VALUE} invalid input parameter + * - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence or + * if API is not supported by HAL */ - public native int setParameter(int soundModelHandle, - @ModelParams int modelParam, int value); + public synchronized int setParameter(int soundModelHandle, @ModelParams int modelParam, + int value) { + try { + mService.setModelParameter(soundModelHandle, + ConversionUtil.api2aidlModelParameter(modelParam), value); + return SoundTrigger.STATUS_OK; + } catch (Exception e) { + return handleException(e); + } + } /** * Get a model specific {@link ModelParams}. This parameter will keep its value * for the duration the model is loaded regardless of starting and stopping recognition. * Once the model is unloaded, the value will be lost. If the value is not set, a default * value is returned. See {@link ModelParams} for parameter default values. - * {@link SoundTriggerModule#isParameterSupported} should be checked first before + * {@link SoundTriggerModule#queryParameter(int, int)} should be checked first before * calling this method. Otherwise, an exception can be thrown. * * @param soundModelHandle handle of model to get parameter - * @param modelParam {@link ModelParams} + * @param modelParam {@link ModelParams} * @return value of parameter * @throws UnsupportedOperationException if hal or model do not support this API. - * {@link SoundTriggerModule#isParameterSupported} should be checked first. - * @throws IllegalArgumentException if invalid model handle or parameter is passed. - * {@link SoundTriggerModule#isParameterSupported} should be checked first. + * {@link SoundTriggerModule#queryParameter(int, int)} + * should + * be checked first. + * @throws IllegalArgumentException if invalid model handle or parameter is passed. + * {@link SoundTriggerModule#queryParameter(int, int)} + * should be checked first. */ - public native int getParameter(int soundModelHandle, - @ModelParams int modelParam) - throws UnsupportedOperationException, IllegalArgumentException; + public synchronized int getParameter(int soundModelHandle, @ModelParams int modelParam) + throws UnsupportedOperationException, IllegalArgumentException { + try { + return mService.getModelParameter(soundModelHandle, + ConversionUtil.api2aidlModelParameter(modelParam)); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } /** * Determine if parameter control is supported for the given model handle. @@ -197,85 +282,98 @@ public class SoundTriggerModule { * {@link SoundTriggerModule#getParameter}. * * @param soundModelHandle handle of model to get parameter - * @param modelParam {@link ModelParams} + * @param modelParam {@link ModelParams} * @return supported range of parameter, null if not supported */ @Nullable - public native ModelParamRange queryParameter(int soundModelHandle, @ModelParams int modelParam); - - private class NativeEventHandlerDelegate { - private final Handler mHandler; - - NativeEventHandlerDelegate(final SoundTrigger.StatusListener listener, - Handler handler) { - // find the looper for our new event handler - Looper looper; - if (handler != null) { - looper = handler.getLooper(); - } else { - looper = Looper.getMainLooper(); - } - - // construct the event handler with this looper - if (looper != null) { - // implement the event handler delegate - mHandler = new Handler(looper) { - @Override - public void handleMessage(Message msg) { - switch(msg.what) { - case EVENT_RECOGNITION: - if (listener != null) { - listener.onRecognition( - (SoundTrigger.RecognitionEvent)msg.obj); - } - break; - case EVENT_SOUNDMODEL: - if (listener != null) { - listener.onSoundModelUpdate( - (SoundTrigger.SoundModelEvent)msg.obj); - } - break; - case EVENT_SERVICE_STATE_CHANGE: - if (listener != null) { - listener.onServiceStateChange(msg.arg1); - } - break; - case EVENT_SERVICE_DIED: - if (listener != null) { - listener.onServiceDied(); - } - break; - default: - break; - } - } - }; - } else { - mHandler = null; - } - } - - Handler handler() { - return mHandler; + public synchronized SoundTrigger.ModelParamRange queryParameter(int soundModelHandle, + @ModelParams int modelParam) { + try { + return ConversionUtil.aidl2apiModelParameterRange(mService.queryModelParameterSupport( + soundModelHandle, + ConversionUtil.api2aidlModelParameter(modelParam))); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); } } - @SuppressWarnings("unused") - @UnsupportedAppUsage - private static void postEventFromNative(Object module_ref, - int what, int arg1, int arg2, Object obj) { - SoundTriggerModule module = (SoundTriggerModule)((WeakReference)module_ref).get(); - if (module == null) { - return; + private int handleException(Exception e) { + Log.e(TAG, "", e); + if (e instanceof NullPointerException) { + return SoundTrigger.STATUS_NO_INIT; + } + if (e instanceof RemoteException) { + return SoundTrigger.STATUS_DEAD_OBJECT; + } + if (e instanceof IllegalArgumentException) { + return SoundTrigger.STATUS_BAD_VALUE; + } + if (e instanceof IllegalStateException) { + return SoundTrigger.STATUS_INVALID_OPERATION; + } + return SoundTrigger.STATUS_ERROR; + } + + private class EventHandlerDelegate extends ISoundTriggerCallback.Stub implements + IBinder.DeathRecipient { + private final Handler mHandler; + + EventHandlerDelegate(@NonNull final SoundTrigger.StatusListener listener, + @NonNull Looper looper) { + + // construct the event handler with this looper + // implement the event handler delegate + mHandler = new Handler(looper) { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case EVENT_RECOGNITION: + listener.onRecognition( + (SoundTrigger.RecognitionEvent) msg.obj); + break; + case EVENT_SERVICE_STATE_CHANGE: + listener.onServiceStateChange(msg.arg1); + break; + case EVENT_SERVICE_DIED: + listener.onServiceDied(); + break; + default: + Log.e(TAG, "Unknown message: " + msg.toString()); + break; + } + } + }; } - NativeEventHandlerDelegate delegate = module.mEventHandlerDelegate; - if (delegate != null) { - Handler handler = delegate.handler(); - if (handler != null) { - Message m = handler.obtainMessage(what, arg1, arg2, obj); - handler.sendMessage(m); - } + @Override + public synchronized void onRecognition(int handle, RecognitionEvent event) + throws RemoteException { + Message m = mHandler.obtainMessage(EVENT_RECOGNITION, + ConversionUtil.aidl2apiRecognitionEvent(handle, event)); + mHandler.sendMessage(m); + } + + @Override + public synchronized void onPhraseRecognition(int handle, PhraseRecognitionEvent event) + throws RemoteException { + Message m = mHandler.obtainMessage(EVENT_RECOGNITION, + ConversionUtil.aidl2apiPhraseRecognitionEvent(handle, event)); + mHandler.sendMessage(m); + } + + @Override + public synchronized void onRecognitionAvailabilityChange(boolean available) + throws RemoteException { + Message m = mHandler.obtainMessage(EVENT_SERVICE_STATE_CHANGE, + available ? SoundTrigger.SERVICE_STATE_ENABLED + : SoundTrigger.SERVICE_STATE_DISABLED); + mHandler.sendMessage(m); + } + + @Override + public synchronized void binderDied() { + Message m = mHandler.obtainMessage(EVENT_SERVICE_DIED); + mHandler.sendMessage(m); } } } diff --git a/core/jni/Android.bp b/core/jni/Android.bp index b91d3595fe6aa..a2f6a62157ed5 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -177,7 +177,6 @@ cc_library_shared { "android_hardware_HardwareBuffer.cpp", "android_hardware_SensorManager.cpp", "android_hardware_SerialPort.cpp", - "android_hardware_SoundTrigger.cpp", "android_hardware_UsbDevice.cpp", "android_hardware_UsbDeviceConnection.cpp", "android_hardware_UsbRequest.cpp", @@ -259,7 +258,6 @@ cc_library_shared { "libpdfium", "libimg_utils", "libnetd_client", - "libsoundtrigger", "libprocessgroup", "libnativebridge_lazy", "libnativeloader_lazy", diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index b8fd3ad43ce56..f7a994f7071fb 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -80,7 +80,6 @@ extern int register_android_hardware_camera2_DngCreator(JNIEnv *env); extern int register_android_hardware_HardwareBuffer(JNIEnv *env); extern int register_android_hardware_SensorManager(JNIEnv *env); extern int register_android_hardware_SerialPort(JNIEnv *env); -extern int register_android_hardware_SoundTrigger(JNIEnv *env); extern int register_android_hardware_UsbDevice(JNIEnv *env); extern int register_android_hardware_UsbDeviceConnection(JNIEnv *env); extern int register_android_hardware_UsbRequest(JNIEnv *env); @@ -1508,7 +1507,6 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_hardware_HardwareBuffer), REG_JNI(register_android_hardware_SensorManager), REG_JNI(register_android_hardware_SerialPort), - REG_JNI(register_android_hardware_SoundTrigger), REG_JNI(register_android_hardware_UsbDevice), REG_JNI(register_android_hardware_UsbDeviceConnection), REG_JNI(register_android_hardware_UsbRequest), diff --git a/core/jni/android_hardware_SoundTrigger.cpp b/core/jni/android_hardware_SoundTrigger.cpp deleted file mode 100644 index 4376b0b4ac428..0000000000000 --- a/core/jni/android_hardware_SoundTrigger.cpp +++ /dev/null @@ -1,1071 +0,0 @@ -/* -** -** Copyright 2014, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -//#define LOG_NDEBUG 0 -#define LOG_TAG "SoundTrigger-JNI" -#include - -#include "jni.h" -#include -#include -#include "core_jni_helpers.h" -#include -#include -#include -#include -#include -#include -#include -#include "android_media_AudioFormat.h" - -using namespace android; - -static jclass gArrayListClass; -static struct { - jmethodID add; -} gArrayListMethods; - -static jclass gUUIDClass; -static struct { - jmethodID toString; -} gUUIDMethods; - -static const char* const kUnsupportedOperationExceptionClassPathName = - "java/lang/UnsupportedOperationException"; -static jclass gUnsupportedOperationExceptionClass; -static const char* const kIllegalArgumentExceptionClassPathName = - "java/lang/IllegalArgumentException"; -static jclass gIllegalArgumentExceptionClass; - -static const char* const kSoundTriggerClassPathName = "android/hardware/soundtrigger/SoundTrigger"; -static jclass gSoundTriggerClass; - -static const char* const kModuleClassPathName = "android/hardware/soundtrigger/SoundTriggerModule"; -static jclass gModuleClass; -static struct { - jfieldID mNativeContext; - jfieldID mId; -} gModuleFields; -static jmethodID gPostEventFromNative; - -static const char* const kModulePropertiesClassPathName = - "android/hardware/soundtrigger/SoundTrigger$ModuleProperties"; -static jclass gModulePropertiesClass; -static jmethodID gModulePropertiesCstor; - -static const char* const kSoundModelClassPathName = - "android/hardware/soundtrigger/SoundTrigger$SoundModel"; -static jclass gSoundModelClass; -static struct { - jfieldID uuid; - jfieldID vendorUuid; - jfieldID data; -} gSoundModelFields; - -static const char* const kGenericSoundModelClassPathName = - "android/hardware/soundtrigger/SoundTrigger$GenericSoundModel"; -static jclass gGenericSoundModelClass; - -static const char* const kKeyphraseClassPathName = - "android/hardware/soundtrigger/SoundTrigger$Keyphrase"; -static jclass gKeyphraseClass; -static struct { - jfieldID id; - jfieldID recognitionModes; - jfieldID locale; - jfieldID text; - jfieldID users; -} gKeyphraseFields; - -static const char* const kKeyphraseSoundModelClassPathName = - "android/hardware/soundtrigger/SoundTrigger$KeyphraseSoundModel"; -static jclass gKeyphraseSoundModelClass; -static struct { - jfieldID keyphrases; -} gKeyphraseSoundModelFields; - -static const char* const kModelParamRangeClassPathName = - "android/hardware/soundtrigger/SoundTrigger$ModelParamRange"; -static jclass gModelParamRangeClass; -static jmethodID gModelParamRangeCstor; - -static const char* const kRecognitionConfigClassPathName = - "android/hardware/soundtrigger/SoundTrigger$RecognitionConfig"; -static jclass gRecognitionConfigClass; -static struct { - jfieldID captureRequested; - jfieldID keyphrases; - jfieldID data; -} gRecognitionConfigFields; - -static const char* const kRecognitionEventClassPathName = - "android/hardware/soundtrigger/SoundTrigger$RecognitionEvent"; -static jclass gRecognitionEventClass; -static jmethodID gRecognitionEventCstor; - -static const char* const kKeyphraseRecognitionEventClassPathName = - "android/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionEvent"; -static jclass gKeyphraseRecognitionEventClass; -static jmethodID gKeyphraseRecognitionEventCstor; - -static const char* const kGenericRecognitionEventClassPathName = - "android/hardware/soundtrigger/SoundTrigger$GenericRecognitionEvent"; -static jclass gGenericRecognitionEventClass; -static jmethodID gGenericRecognitionEventCstor; - -static const char* const kKeyphraseRecognitionExtraClassPathName = - "android/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionExtra"; -static jclass gKeyphraseRecognitionExtraClass; -static jmethodID gKeyphraseRecognitionExtraCstor; -static struct { - jfieldID id; - jfieldID recognitionModes; - jfieldID coarseConfidenceLevel; - jfieldID confidenceLevels; -} gKeyphraseRecognitionExtraFields; - -static const char* const kConfidenceLevelClassPathName = - "android/hardware/soundtrigger/SoundTrigger$ConfidenceLevel"; -static jclass gConfidenceLevelClass; -static jmethodID gConfidenceLevelCstor; -static struct { - jfieldID userId; - jfieldID confidenceLevel; -} gConfidenceLevelFields; - -static const char* const kAudioFormatClassPathName = - "android/media/AudioFormat"; -static jclass gAudioFormatClass; -static jmethodID gAudioFormatCstor; - -static const char* const kSoundModelEventClassPathName = - "android/hardware/soundtrigger/SoundTrigger$SoundModelEvent"; -static jclass gSoundModelEventClass; -static jmethodID gSoundModelEventCstor; - -static Mutex gLock; - -enum { - SOUNDTRIGGER_STATUS_OK = 0, - SOUNDTRIGGER_STATUS_ERROR = INT_MIN, - SOUNDTRIGGER_PERMISSION_DENIED = -1, - SOUNDTRIGGER_STATUS_NO_INIT = -19, - SOUNDTRIGGER_STATUS_BAD_VALUE = -22, - SOUNDTRIGGER_STATUS_DEAD_OBJECT = -32, - SOUNDTRIGGER_INVALID_OPERATION = -38, -}; - -enum { - SOUNDTRIGGER_EVENT_RECOGNITION = 1, - SOUNDTRIGGER_EVENT_SERVICE_DIED = 2, - SOUNDTRIGGER_EVENT_SOUNDMODEL = 3, - SOUNDTRIGGER_EVENT_SERVICE_STATE_CHANGE = 4, -}; - -static jint throwUnsupportedOperationException(JNIEnv *env) -{ - return env->ThrowNew(gUnsupportedOperationExceptionClass, nullptr); -} - -static jint throwIllegalArgumentException(JNIEnv *env) -{ - return env->ThrowNew(gIllegalArgumentExceptionClass, nullptr); -} - -// ---------------------------------------------------------------------------- -// ref-counted object for callbacks -class JNISoundTriggerCallback: public SoundTriggerCallback -{ -public: - JNISoundTriggerCallback(JNIEnv* env, jobject thiz, jobject weak_thiz); - ~JNISoundTriggerCallback(); - - virtual void onRecognitionEvent(struct sound_trigger_recognition_event *event); - virtual void onSoundModelEvent(struct sound_trigger_model_event *event); - virtual void onServiceStateChange(sound_trigger_service_state_t state); - virtual void onServiceDied(); - -private: - jclass mClass; // Reference to SoundTrigger class - jobject mObject; // Weak ref to SoundTrigger Java object to call on -}; - -JNISoundTriggerCallback::JNISoundTriggerCallback(JNIEnv* env, jobject thiz, jobject weak_thiz) -{ - - // Hold onto the SoundTriggerModule class for use in calling the static method - // that posts events to the application thread. - jclass clazz = env->GetObjectClass(thiz); - if (clazz == NULL) { - ALOGE("Can't find class %s", kModuleClassPathName); - return; - } - mClass = (jclass)env->NewGlobalRef(clazz); - - // We use a weak reference so the SoundTriggerModule object can be garbage collected. - // The reference is only used as a proxy for callbacks. - mObject = env->NewGlobalRef(weak_thiz); -} - -JNISoundTriggerCallback::~JNISoundTriggerCallback() -{ - // remove global references - JNIEnv *env = AndroidRuntime::getJNIEnv(); - env->DeleteGlobalRef(mObject); - env->DeleteGlobalRef(mClass); -} - -void JNISoundTriggerCallback::onRecognitionEvent(struct sound_trigger_recognition_event *event) -{ - JNIEnv *env = AndroidRuntime::getJNIEnv(); - jobject jEvent = NULL; - jbyteArray jData = NULL; - - if (event->data_size) { - jData = env->NewByteArray(event->data_size); - jbyte *nData = env->GetByteArrayElements(jData, NULL); - memcpy(nData, (char *)event + event->data_offset, event->data_size); - env->ReleaseByteArrayElements(jData, nData, 0); - } - - jobject jAudioFormat = NULL; - if (event->trigger_in_data || event->capture_available) { - jint channelMask = (jint)audio_channel_mask_get_bits(event->audio_config.channel_mask); - jint channelIndexMask = (jint)AUDIO_CHANNEL_NONE; - - switch (audio_channel_mask_get_representation(event->audio_config.channel_mask)) { - case AUDIO_CHANNEL_REPRESENTATION_INDEX: - channelIndexMask = channelMask; - channelMask = (jint)AUDIO_CHANNEL_NONE; - break; - default: - break; - } - jAudioFormat = env->NewObject(gAudioFormatClass, - gAudioFormatCstor, - audioFormatFromNative(event->audio_config.format), - event->audio_config.sample_rate, - channelMask, - channelIndexMask); - - } - if (event->type == SOUND_MODEL_TYPE_KEYPHRASE) { - struct sound_trigger_phrase_recognition_event *phraseEvent = - (struct sound_trigger_phrase_recognition_event *)event; - - jobjectArray jExtras = env->NewObjectArray(phraseEvent->num_phrases, - gKeyphraseRecognitionExtraClass, NULL); - if (jExtras == NULL) { - return; - } - - for (size_t i = 0; i < phraseEvent->num_phrases; i++) { - jobjectArray jConfidenceLevels = env->NewObjectArray( - phraseEvent->phrase_extras[i].num_levels, - gConfidenceLevelClass, NULL); - - if (jConfidenceLevels == NULL) { - return; - } - for (size_t j = 0; j < phraseEvent->phrase_extras[i].num_levels; j++) { - jobject jConfidenceLevel = env->NewObject(gConfidenceLevelClass, - gConfidenceLevelCstor, - phraseEvent->phrase_extras[i].levels[j].user_id, - phraseEvent->phrase_extras[i].levels[j].level); - env->SetObjectArrayElement(jConfidenceLevels, j, jConfidenceLevel); - env->DeleteLocalRef(jConfidenceLevel); - } - - jobject jNewExtra = env->NewObject(gKeyphraseRecognitionExtraClass, - gKeyphraseRecognitionExtraCstor, - phraseEvent->phrase_extras[i].id, - phraseEvent->phrase_extras[i].recognition_modes, - phraseEvent->phrase_extras[i].confidence_level, - jConfidenceLevels); - - if (jNewExtra == NULL) { - return; - } - env->SetObjectArrayElement(jExtras, i, jNewExtra); - env->DeleteLocalRef(jNewExtra); - env->DeleteLocalRef(jConfidenceLevels); - } - jEvent = env->NewObject(gKeyphraseRecognitionEventClass, gKeyphraseRecognitionEventCstor, - event->status, event->model, event->capture_available, - event->capture_session, event->capture_delay_ms, - event->capture_preamble_ms, event->trigger_in_data, - jAudioFormat, jData, jExtras); - env->DeleteLocalRef(jExtras); - } else if (event->type == SOUND_MODEL_TYPE_GENERIC) { - jEvent = env->NewObject(gGenericRecognitionEventClass, gGenericRecognitionEventCstor, - event->status, event->model, event->capture_available, - event->capture_session, event->capture_delay_ms, - event->capture_preamble_ms, event->trigger_in_data, - jAudioFormat, jData); - } else { - jEvent = env->NewObject(gRecognitionEventClass, gRecognitionEventCstor, - event->status, event->model, event->capture_available, - event->capture_session, event->capture_delay_ms, - event->capture_preamble_ms, event->trigger_in_data, - jAudioFormat, jData); - } - - if (jAudioFormat != NULL) { - env->DeleteLocalRef(jAudioFormat); - } - if (jData != NULL) { - env->DeleteLocalRef(jData); - } - - env->CallStaticVoidMethod(mClass, gPostEventFromNative, mObject, - SOUNDTRIGGER_EVENT_RECOGNITION, 0, 0, jEvent); - - env->DeleteLocalRef(jEvent); - if (env->ExceptionCheck()) { - ALOGW("An exception occurred while notifying an event."); - env->ExceptionClear(); - } -} - -void JNISoundTriggerCallback::onSoundModelEvent(struct sound_trigger_model_event *event) -{ - JNIEnv *env = AndroidRuntime::getJNIEnv(); - jobject jEvent = NULL; - jbyteArray jData = NULL; - - if (event->data_size) { - jData = env->NewByteArray(event->data_size); - jbyte *nData = env->GetByteArrayElements(jData, NULL); - memcpy(nData, (char *)event + event->data_offset, event->data_size); - env->ReleaseByteArrayElements(jData, nData, 0); - } - - jEvent = env->NewObject(gSoundModelEventClass, gSoundModelEventCstor, - event->status, event->model, jData); - - env->DeleteLocalRef(jData); - env->CallStaticVoidMethod(mClass, gPostEventFromNative, mObject, - SOUNDTRIGGER_EVENT_SOUNDMODEL, 0, 0, jEvent); - env->DeleteLocalRef(jEvent); - if (env->ExceptionCheck()) { - ALOGW("An exception occurred while notifying an event."); - env->ExceptionClear(); - } -} - -void JNISoundTriggerCallback::onServiceStateChange(sound_trigger_service_state_t state) -{ - JNIEnv *env = AndroidRuntime::getJNIEnv(); - env->CallStaticVoidMethod(mClass, gPostEventFromNative, mObject, - SOUNDTRIGGER_EVENT_SERVICE_STATE_CHANGE, state, 0, NULL); - if (env->ExceptionCheck()) { - ALOGW("An exception occurred while notifying an event."); - env->ExceptionClear(); - } -} - -void JNISoundTriggerCallback::onServiceDied() -{ - JNIEnv *env = AndroidRuntime::getJNIEnv(); - - env->CallStaticVoidMethod(mClass, gPostEventFromNative, mObject, - SOUNDTRIGGER_EVENT_SERVICE_DIED, 0, 0, NULL); - if (env->ExceptionCheck()) { - ALOGW("An exception occurred while notifying an event."); - env->ExceptionClear(); - } -} - -// ---------------------------------------------------------------------------- - -static sp getSoundTrigger(JNIEnv* env, jobject thiz) -{ - Mutex::Autolock l(gLock); - SoundTrigger* const st = (SoundTrigger*)env->GetLongField(thiz, - gModuleFields.mNativeContext); - return sp(st); -} - -static sp setSoundTrigger(JNIEnv* env, jobject thiz, const sp& module) -{ - Mutex::Autolock l(gLock); - sp old = (SoundTrigger*)env->GetLongField(thiz, - gModuleFields.mNativeContext); - if (module.get()) { - module->incStrong((void*)setSoundTrigger); - } - if (old != 0) { - old->decStrong((void*)setSoundTrigger); - } - env->SetLongField(thiz, gModuleFields.mNativeContext, (jlong)module.get()); - return old; -} - - -static jint -android_hardware_SoundTrigger_listModules(JNIEnv *env, jobject clazz, - jstring opPackageName, jobject jModules) -{ - ALOGV("listModules"); - - if (jModules == NULL) { - ALOGE("listModules NULL AudioPatch ArrayList"); - return SOUNDTRIGGER_STATUS_BAD_VALUE; - } - if (!env->IsInstanceOf(jModules, gArrayListClass)) { - ALOGE("listModules not an arraylist"); - return SOUNDTRIGGER_STATUS_BAD_VALUE; - } - - unsigned int numModules = 0; - struct sound_trigger_module_descriptor *nModules = NULL; - - ScopedUtfChars opPackageNameStr(env, opPackageName); - const String16 opPackageNameString16 = String16(opPackageNameStr.c_str()); - - status_t status = SoundTrigger::listModules(opPackageNameString16, nModules, &numModules); - if (status != NO_ERROR || numModules == 0) { - return (jint)status; - } - - nModules = (struct sound_trigger_module_descriptor *) - calloc(numModules, sizeof(struct sound_trigger_module_descriptor)); - - status = SoundTrigger::listModules(opPackageNameString16, nModules, &numModules); - ALOGV("listModules SoundTrigger::listModules status %d numModules %d", status, numModules); - - if (status != NO_ERROR) { - numModules = 0; - } - - for (size_t i = 0; i < numModules; i++) { - char str[SOUND_TRIGGER_MAX_STRING_LEN]; - - jstring implementor = env->NewStringUTF(nModules[i].properties.implementor); - jstring description = env->NewStringUTF(nModules[i].properties.description); - SoundTrigger::guidToString(&nModules[i].properties.uuid, - str, - SOUND_TRIGGER_MAX_STRING_LEN); - jstring uuid = env->NewStringUTF(str); - - ALOGV("listModules module %zu id %d description %s maxSoundModels %d", - i, nModules[i].handle, nModules[i].properties.description, - nModules[i].properties.max_sound_models); - - jobject newModuleDesc = env->NewObject(gModulePropertiesClass, gModulePropertiesCstor, - nModules[i].handle, - implementor, description, uuid, - nModules[i].properties.version, - nModules[i].properties.max_sound_models, - nModules[i].properties.max_key_phrases, - nModules[i].properties.max_users, - nModules[i].properties.recognition_modes, - nModules[i].properties.capture_transition, - nModules[i].properties.max_buffer_ms, - nModules[i].properties.concurrent_capture, - nModules[i].properties.power_consumption_mw, - nModules[i].properties.trigger_in_event); - - env->DeleteLocalRef(implementor); - env->DeleteLocalRef(description); - env->DeleteLocalRef(uuid); - if (newModuleDesc == NULL) { - status = SOUNDTRIGGER_STATUS_ERROR; - goto exit; - } - env->CallBooleanMethod(jModules, gArrayListMethods.add, newModuleDesc); - } - -exit: - free(nModules); - return (jint) status; -} - -static void -android_hardware_SoundTrigger_setup(JNIEnv *env, jobject thiz, - jstring opPackageName, jobject weak_this) -{ - ALOGV("setup"); - - ScopedUtfChars opPackageNameStr(env, opPackageName); - const String16 opPackageNameString16 = String16(opPackageNameStr.c_str()); - - sp callback = new JNISoundTriggerCallback(env, thiz, weak_this); - - sound_trigger_module_handle_t handle = - (sound_trigger_module_handle_t)env->GetIntField(thiz, gModuleFields.mId); - - sp module = SoundTrigger::attach(opPackageNameString16, handle, callback); - if (module == 0) { - return; - } - - setSoundTrigger(env, thiz, module); -} - -static void -android_hardware_SoundTrigger_detach(JNIEnv *env, jobject thiz) -{ - ALOGV("detach"); - sp module = setSoundTrigger(env, thiz, 0); - ALOGV("detach module %p", module.get()); - if (module != 0) { - ALOGV("detach module->detach()"); - module->detach(); - } -} - -static void -android_hardware_SoundTrigger_finalize(JNIEnv *env, jobject thiz) -{ - ALOGV("finalize"); - sp module = getSoundTrigger(env, thiz); - if (module != 0) { - ALOGW("SoundTrigger finalized without being detached"); - } - android_hardware_SoundTrigger_detach(env, thiz); -} - -static jint -android_hardware_SoundTrigger_loadSoundModel(JNIEnv *env, jobject thiz, - jobject jSoundModel, jintArray jHandle) -{ - jint status = SOUNDTRIGGER_STATUS_OK; - jbyte *nData = NULL; - struct sound_trigger_sound_model *nSoundModel; - jbyteArray jData; - sp memoryDealer; - sp memory; - size_t size; - sound_model_handle_t handle = 0; - jobject jUuid; - jstring jUuidString; - const char *nUuidString; - - ALOGV("loadSoundModel"); - sp module = getSoundTrigger(env, thiz); - if (module == NULL) { - return SOUNDTRIGGER_STATUS_ERROR; - } - if (jHandle == NULL) { - return SOUNDTRIGGER_STATUS_BAD_VALUE; - } - jsize jHandleLen = env->GetArrayLength(jHandle); - if (jHandleLen == 0) { - return SOUNDTRIGGER_STATUS_BAD_VALUE; - } - jint *nHandle = env->GetIntArrayElements(jHandle, NULL); - if (nHandle == NULL) { - return SOUNDTRIGGER_STATUS_ERROR; - } - if (!env->IsInstanceOf(jSoundModel, gSoundModelClass)) { - status = SOUNDTRIGGER_STATUS_BAD_VALUE; - goto exit; - } - size_t offset; - sound_trigger_sound_model_type_t type; - if (env->IsInstanceOf(jSoundModel, gKeyphraseSoundModelClass)) { - offset = sizeof(struct sound_trigger_phrase_sound_model); - type = SOUND_MODEL_TYPE_KEYPHRASE; - } else if (env->IsInstanceOf(jSoundModel, gGenericSoundModelClass)) { - offset = sizeof(struct sound_trigger_generic_sound_model); - type = SOUND_MODEL_TYPE_GENERIC; - } else { - offset = sizeof(struct sound_trigger_sound_model); - type = SOUND_MODEL_TYPE_UNKNOWN; - } - - jUuid = env->GetObjectField(jSoundModel, gSoundModelFields.uuid); - jUuidString = (jstring)env->CallObjectMethod(jUuid, gUUIDMethods.toString); - nUuidString = env->GetStringUTFChars(jUuidString, NULL); - sound_trigger_uuid_t nUuid; - SoundTrigger::stringToGuid(nUuidString, &nUuid); - env->ReleaseStringUTFChars(jUuidString, nUuidString); - env->DeleteLocalRef(jUuidString); - - sound_trigger_uuid_t nVendorUuid; - jUuid = env->GetObjectField(jSoundModel, gSoundModelFields.vendorUuid); - if (jUuid != NULL) { - jUuidString = (jstring)env->CallObjectMethod(jUuid, gUUIDMethods.toString); - nUuidString = env->GetStringUTFChars(jUuidString, NULL); - SoundTrigger::stringToGuid(nUuidString, &nVendorUuid); - env->ReleaseStringUTFChars(jUuidString, nUuidString); - env->DeleteLocalRef(jUuidString); - } else { - SoundTrigger::stringToGuid("00000000-0000-0000-0000-000000000000", &nVendorUuid); - } - - jData = (jbyteArray)env->GetObjectField(jSoundModel, gSoundModelFields.data); - if (jData == NULL) { - status = SOUNDTRIGGER_STATUS_BAD_VALUE; - goto exit; - } - size = env->GetArrayLength(jData); - - nData = env->GetByteArrayElements(jData, NULL); - if (jData == NULL) { - status = SOUNDTRIGGER_STATUS_ERROR; - goto exit; - } - - memoryDealer = new MemoryDealer(offset + size, "SoundTrigge-JNI::LoadModel"); - if (memoryDealer == 0) { - status = SOUNDTRIGGER_STATUS_ERROR; - goto exit; - } - memory = memoryDealer->allocate(offset + size); - if (memory == 0 || memory->unsecurePointer() == NULL) { - status = SOUNDTRIGGER_STATUS_ERROR; - goto exit; - } - - nSoundModel = (struct sound_trigger_sound_model *)memory->unsecurePointer(); - - nSoundModel->type = type; - nSoundModel->uuid = nUuid; - nSoundModel->vendor_uuid = nVendorUuid; - nSoundModel->data_size = size; - nSoundModel->data_offset = offset; - memcpy((char *)nSoundModel + offset, nData, size); - if (type == SOUND_MODEL_TYPE_KEYPHRASE) { - struct sound_trigger_phrase_sound_model *phraseModel = - (struct sound_trigger_phrase_sound_model *)nSoundModel; - - jobjectArray jPhrases = - (jobjectArray)env->GetObjectField(jSoundModel, gKeyphraseSoundModelFields.keyphrases); - if (jPhrases == NULL) { - status = SOUNDTRIGGER_STATUS_BAD_VALUE; - goto exit; - } - - size_t numPhrases = env->GetArrayLength(jPhrases); - phraseModel->num_phrases = numPhrases; - ALOGV("loadSoundModel numPhrases %zu", numPhrases); - for (size_t i = 0; i < numPhrases; i++) { - jobject jPhrase = env->GetObjectArrayElement(jPhrases, i); - phraseModel->phrases[i].id = - env->GetIntField(jPhrase,gKeyphraseFields.id); - phraseModel->phrases[i].recognition_mode = - env->GetIntField(jPhrase,gKeyphraseFields.recognitionModes); - - jintArray jUsers = (jintArray)env->GetObjectField(jPhrase, gKeyphraseFields.users); - phraseModel->phrases[i].num_users = env->GetArrayLength(jUsers); - jint *nUsers = env->GetIntArrayElements(jUsers, NULL); - memcpy(phraseModel->phrases[i].users, - nUsers, - phraseModel->phrases[i].num_users * sizeof(int)); - env->ReleaseIntArrayElements(jUsers, nUsers, 0); - env->DeleteLocalRef(jUsers); - - jstring jLocale = (jstring)env->GetObjectField(jPhrase, gKeyphraseFields.locale); - const char *nLocale = env->GetStringUTFChars(jLocale, NULL); - strncpy(phraseModel->phrases[i].locale, - nLocale, - SOUND_TRIGGER_MAX_LOCALE_LEN); - jstring jText = (jstring)env->GetObjectField(jPhrase, gKeyphraseFields.text); - const char *nText = env->GetStringUTFChars(jText, NULL); - strncpy(phraseModel->phrases[i].text, - nText, - SOUND_TRIGGER_MAX_STRING_LEN); - - env->ReleaseStringUTFChars(jLocale, nLocale); - env->DeleteLocalRef(jLocale); - env->ReleaseStringUTFChars(jText, nText); - env->DeleteLocalRef(jText); - ALOGV("loadSoundModel phrases %zu text %s locale %s", - i, phraseModel->phrases[i].text, phraseModel->phrases[i].locale); - env->DeleteLocalRef(jPhrase); - } - env->DeleteLocalRef(jPhrases); - } else if (type == SOUND_MODEL_TYPE_GENERIC) { - /* No initialization needed */ - } - status = module->loadSoundModel(memory, &handle); - ALOGV("loadSoundModel status %d handle %d", status, handle); - -exit: - if (nHandle != NULL) { - nHandle[0] = (jint)handle; - env->ReleaseIntArrayElements(jHandle, nHandle, NULL); - } - if (nData != NULL) { - env->ReleaseByteArrayElements(jData, nData, NULL); - } - return status; -} - -static jint -android_hardware_SoundTrigger_unloadSoundModel(JNIEnv *env, jobject thiz, - jint jHandle) -{ - jint status = SOUNDTRIGGER_STATUS_OK; - ALOGV("unloadSoundModel"); - sp module = getSoundTrigger(env, thiz); - if (module == NULL) { - return SOUNDTRIGGER_STATUS_ERROR; - } - status = module->unloadSoundModel((sound_model_handle_t)jHandle); - - return status; -} - -static jint -android_hardware_SoundTrigger_startRecognition(JNIEnv *env, jobject thiz, - jint jHandle, jobject jConfig) -{ - jint status = SOUNDTRIGGER_STATUS_OK; - ALOGV("startRecognition"); - sp module = getSoundTrigger(env, thiz); - if (module == NULL) { - return SOUNDTRIGGER_STATUS_ERROR; - } - - if (!env->IsInstanceOf(jConfig, gRecognitionConfigClass)) { - return SOUNDTRIGGER_STATUS_BAD_VALUE; - } - - jbyteArray jData = (jbyteArray)env->GetObjectField(jConfig, gRecognitionConfigFields.data); - jsize dataSize = 0; - jbyte *nData = NULL; - if (jData != NULL) { - dataSize = env->GetArrayLength(jData); - if (dataSize == 0) { - return SOUNDTRIGGER_STATUS_BAD_VALUE; - } - nData = env->GetByteArrayElements(jData, NULL); - if (nData == NULL) { - return SOUNDTRIGGER_STATUS_ERROR; - } - } - - size_t totalSize = sizeof(struct sound_trigger_recognition_config) + dataSize; - sp memoryDealer = - new MemoryDealer(totalSize, "SoundTrigge-JNI::StartRecognition"); - if (memoryDealer == 0) { - return SOUNDTRIGGER_STATUS_ERROR; - } - sp memory = memoryDealer->allocate(totalSize); - if (memory == 0 || memory->unsecurePointer() == NULL) { - return SOUNDTRIGGER_STATUS_ERROR; - } - if (dataSize != 0) { - memcpy((char *)memory->unsecurePointer() + sizeof(struct sound_trigger_recognition_config), - nData, - dataSize); - env->ReleaseByteArrayElements(jData, nData, 0); - } - env->DeleteLocalRef(jData); - struct sound_trigger_recognition_config *config = - (struct sound_trigger_recognition_config *)memory->unsecurePointer(); - config->data_size = dataSize; - config->data_offset = sizeof(struct sound_trigger_recognition_config); - config->capture_requested = env->GetBooleanField(jConfig, - gRecognitionConfigFields.captureRequested); - - config->num_phrases = 0; - jobjectArray jPhrases = - (jobjectArray)env->GetObjectField(jConfig, gRecognitionConfigFields.keyphrases); - if (jPhrases != NULL) { - config->num_phrases = env->GetArrayLength(jPhrases); - } - ALOGV("startRecognition num phrases %d", config->num_phrases); - for (size_t i = 0; i < config->num_phrases; i++) { - jobject jPhrase = env->GetObjectArrayElement(jPhrases, i); - config->phrases[i].id = env->GetIntField(jPhrase, - gKeyphraseRecognitionExtraFields.id); - config->phrases[i].recognition_modes = env->GetIntField(jPhrase, - gKeyphraseRecognitionExtraFields.recognitionModes); - config->phrases[i].confidence_level = env->GetIntField(jPhrase, - gKeyphraseRecognitionExtraFields.coarseConfidenceLevel); - config->phrases[i].num_levels = 0; - jobjectArray jConfidenceLevels = (jobjectArray)env->GetObjectField(jPhrase, - gKeyphraseRecognitionExtraFields.confidenceLevels); - if (jConfidenceLevels != NULL) { - config->phrases[i].num_levels = env->GetArrayLength(jConfidenceLevels); - } - ALOGV("startRecognition phrase %zu num_levels %d", i, config->phrases[i].num_levels); - for (size_t j = 0; j < config->phrases[i].num_levels; j++) { - jobject jConfidenceLevel = env->GetObjectArrayElement(jConfidenceLevels, j); - config->phrases[i].levels[j].user_id = env->GetIntField(jConfidenceLevel, - gConfidenceLevelFields.userId); - config->phrases[i].levels[j].level = env->GetIntField(jConfidenceLevel, - gConfidenceLevelFields.confidenceLevel); - env->DeleteLocalRef(jConfidenceLevel); - } - ALOGV("startRecognition phrases %zu", i); - env->DeleteLocalRef(jConfidenceLevels); - env->DeleteLocalRef(jPhrase); - } - env->DeleteLocalRef(jPhrases); - - status = module->startRecognition(jHandle, memory); - return status; -} - -static jint -android_hardware_SoundTrigger_stopRecognition(JNIEnv *env, jobject thiz, - jint jHandle) -{ - jint status = SOUNDTRIGGER_STATUS_OK; - ALOGV("stopRecognition"); - sp module = getSoundTrigger(env, thiz); - if (module == NULL) { - return SOUNDTRIGGER_STATUS_ERROR; - } - status = module->stopRecognition(jHandle); - return status; -} - -static jint -android_hardware_SoundTrigger_getModelState(JNIEnv *env, jobject thiz, - jint jHandle) -{ - jint status = SOUNDTRIGGER_STATUS_OK; - ALOGV("getModelState"); - sp module = getSoundTrigger(env, thiz); - if (module == NULL) { - return SOUNDTRIGGER_STATUS_ERROR; - } - status = module->getModelState(jHandle); - return status; -} - -static jint -android_hardware_SoundTrigger_setParameter(JNIEnv *env, jobject thiz, - jint jHandle, jint jModelParam, jint jValue) -{ - ALOGV("setParameter"); - sp module = getSoundTrigger(env, thiz); - if (module == NULL) { - return SOUNDTRIGGER_STATUS_NO_INIT; - } - return module->setParameter(jHandle, (sound_trigger_model_parameter_t) jModelParam, jValue); -} - -static jint -android_hardware_SoundTrigger_getParameter(JNIEnv *env, jobject thiz, - jint jHandle, jint jModelParam) -{ - ALOGV("getParameter"); - sp module = getSoundTrigger(env, thiz); - if (module == NULL) { - throwUnsupportedOperationException(env); - return -1; - } - - jint nValue; - jint status = module->getParameter(jHandle, - (sound_trigger_model_parameter_t) jModelParam, &nValue); - - switch (status) { - case 0: - return nValue; - case -EINVAL: - throwIllegalArgumentException(env); - break; - default: - throwUnsupportedOperationException(env); - break; - } - - return -1; -} - -static jobject -android_hardware_SoundTrigger_queryParameter(JNIEnv *env, jobject thiz, - jint jHandle, jint jModelParam) -{ - ALOGV("queryParameter"); - sp module = getSoundTrigger(env, thiz); - if (module == nullptr) { - return nullptr; - } - - sound_trigger_model_parameter_range_t nRange; - jint nValue = module->queryParameter(jHandle, - (sound_trigger_model_parameter_t) jModelParam, &nRange); - - if (nValue != 0) { - ALOGE("failed to query parameter error code: %d", nValue); - return nullptr; - } - - return env->NewObject(gModelParamRangeClass, gModelParamRangeCstor, nRange.start, nRange.end); -} - -static const JNINativeMethod gMethods[] = { - {"listModules", - "(Ljava/lang/String;Ljava/util/ArrayList;)I", - (void *)android_hardware_SoundTrigger_listModules}, -}; - - -static const JNINativeMethod gModuleMethods[] = { - {"native_setup", - "(Ljava/lang/String;Ljava/lang/Object;)V", - (void *)android_hardware_SoundTrigger_setup}, - {"native_finalize", - "()V", - (void *)android_hardware_SoundTrigger_finalize}, - {"detach", - "()V", - (void *)android_hardware_SoundTrigger_detach}, - {"loadSoundModel", - "(Landroid/hardware/soundtrigger/SoundTrigger$SoundModel;[I)I", - (void *)android_hardware_SoundTrigger_loadSoundModel}, - {"unloadSoundModel", - "(I)I", - (void *)android_hardware_SoundTrigger_unloadSoundModel}, - {"startRecognition", - "(ILandroid/hardware/soundtrigger/SoundTrigger$RecognitionConfig;)I", - (void *)android_hardware_SoundTrigger_startRecognition}, - {"stopRecognition", - "(I)I", - (void *)android_hardware_SoundTrigger_stopRecognition}, - {"getModelState", - "(I)I", - (void *)android_hardware_SoundTrigger_getModelState}, - {"setParameter", - "(III)I", - (void *)android_hardware_SoundTrigger_setParameter}, - {"getParameter", - "(II)I", - (void *)android_hardware_SoundTrigger_getParameter}, - {"queryParameter", - "(II)Landroid/hardware/soundtrigger/SoundTrigger$ModelParamRange;", - (void *)android_hardware_SoundTrigger_queryParameter} -}; - -int register_android_hardware_SoundTrigger(JNIEnv *env) -{ - jclass arrayListClass = FindClassOrDie(env, "java/util/ArrayList"); - gArrayListClass = MakeGlobalRefOrDie(env, arrayListClass); - gArrayListMethods.add = GetMethodIDOrDie(env, arrayListClass, "add", "(Ljava/lang/Object;)Z"); - - jclass uuidClass = FindClassOrDie(env, "java/util/UUID"); - gUUIDClass = MakeGlobalRefOrDie(env, uuidClass); - gUUIDMethods.toString = GetMethodIDOrDie(env, uuidClass, "toString", "()Ljava/lang/String;"); - - jclass exUClass = FindClassOrDie(env, kUnsupportedOperationExceptionClassPathName); - gUnsupportedOperationExceptionClass = MakeGlobalRefOrDie(env, exUClass); - - jclass exIClass = FindClassOrDie(env, kIllegalArgumentExceptionClassPathName); - gIllegalArgumentExceptionClass = MakeGlobalRefOrDie(env, exIClass); - - jclass lClass = FindClassOrDie(env, kSoundTriggerClassPathName); - gSoundTriggerClass = MakeGlobalRefOrDie(env, lClass); - - jclass moduleClass = FindClassOrDie(env, kModuleClassPathName); - gModuleClass = MakeGlobalRefOrDie(env, moduleClass); - gPostEventFromNative = GetStaticMethodIDOrDie(env, moduleClass, "postEventFromNative", - "(Ljava/lang/Object;IIILjava/lang/Object;)V"); - gModuleFields.mNativeContext = GetFieldIDOrDie(env, moduleClass, "mNativeContext", "J"); - gModuleFields.mId = GetFieldIDOrDie(env, moduleClass, "mId", "I"); - - jclass modulePropertiesClass = FindClassOrDie(env, kModulePropertiesClassPathName); - gModulePropertiesClass = MakeGlobalRefOrDie(env, modulePropertiesClass); - gModulePropertiesCstor = GetMethodIDOrDie(env, modulePropertiesClass, "", - "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;IIIIIZIZIZ)V"); - - jclass soundModelClass = FindClassOrDie(env, kSoundModelClassPathName); - gSoundModelClass = MakeGlobalRefOrDie(env, soundModelClass); - gSoundModelFields.uuid = GetFieldIDOrDie(env, soundModelClass, "uuid", "Ljava/util/UUID;"); - gSoundModelFields.vendorUuid = GetFieldIDOrDie(env, soundModelClass, "vendorUuid", - "Ljava/util/UUID;"); - gSoundModelFields.data = GetFieldIDOrDie(env, soundModelClass, "data", "[B"); - - jclass genericSoundModelClass = FindClassOrDie(env, kGenericSoundModelClassPathName); - gGenericSoundModelClass = MakeGlobalRefOrDie(env, genericSoundModelClass); - - jclass keyphraseClass = FindClassOrDie(env, kKeyphraseClassPathName); - gKeyphraseClass = MakeGlobalRefOrDie(env, keyphraseClass); - gKeyphraseFields.id = GetFieldIDOrDie(env, keyphraseClass, "id", "I"); - gKeyphraseFields.recognitionModes = GetFieldIDOrDie(env, keyphraseClass, "recognitionModes", - "I"); - gKeyphraseFields.locale = GetFieldIDOrDie(env, keyphraseClass, "locale", "Ljava/lang/String;"); - gKeyphraseFields.text = GetFieldIDOrDie(env, keyphraseClass, "text", "Ljava/lang/String;"); - gKeyphraseFields.users = GetFieldIDOrDie(env, keyphraseClass, "users", "[I"); - - jclass keyphraseSoundModelClass = FindClassOrDie(env, kKeyphraseSoundModelClassPathName); - gKeyphraseSoundModelClass = MakeGlobalRefOrDie(env, keyphraseSoundModelClass); - gKeyphraseSoundModelFields.keyphrases = GetFieldIDOrDie(env, keyphraseSoundModelClass, - "keyphrases", - "[Landroid/hardware/soundtrigger/SoundTrigger$Keyphrase;"); - - jclass modelParamRangeClass = FindClassOrDie(env, kModelParamRangeClassPathName); - gModelParamRangeClass = MakeGlobalRefOrDie(env, modelParamRangeClass); - gModelParamRangeCstor = GetMethodIDOrDie(env, modelParamRangeClass, "", "(II)V"); - - jclass recognitionEventClass = FindClassOrDie(env, kRecognitionEventClassPathName); - gRecognitionEventClass = MakeGlobalRefOrDie(env, recognitionEventClass); - gRecognitionEventCstor = GetMethodIDOrDie(env, recognitionEventClass, "", - "(IIZIIIZLandroid/media/AudioFormat;[B)V"); - - jclass keyphraseRecognitionEventClass = FindClassOrDie(env, - kKeyphraseRecognitionEventClassPathName); - gKeyphraseRecognitionEventClass = MakeGlobalRefOrDie(env, keyphraseRecognitionEventClass); - gKeyphraseRecognitionEventCstor = GetMethodIDOrDie(env, keyphraseRecognitionEventClass, "", - "(IIZIIIZLandroid/media/AudioFormat;[B[Landroid/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionExtra;)V"); - - jclass genericRecognitionEventClass = FindClassOrDie(env, - kGenericRecognitionEventClassPathName); - gGenericRecognitionEventClass = MakeGlobalRefOrDie(env, genericRecognitionEventClass); - gGenericRecognitionEventCstor = GetMethodIDOrDie(env, genericRecognitionEventClass, "", - "(IIZIIIZLandroid/media/AudioFormat;[B)V"); - - jclass keyRecognitionConfigClass = FindClassOrDie(env, kRecognitionConfigClassPathName); - gRecognitionConfigClass = MakeGlobalRefOrDie(env, keyRecognitionConfigClass); - gRecognitionConfigFields.captureRequested = GetFieldIDOrDie(env, keyRecognitionConfigClass, - "captureRequested", "Z"); - gRecognitionConfigFields.keyphrases = GetFieldIDOrDie(env, keyRecognitionConfigClass, - "keyphrases", "[Landroid/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionExtra;"); - gRecognitionConfigFields.data = GetFieldIDOrDie(env, keyRecognitionConfigClass, "data", "[B"); - - jclass keyphraseRecognitionExtraClass = FindClassOrDie(env, - kKeyphraseRecognitionExtraClassPathName); - gKeyphraseRecognitionExtraClass = MakeGlobalRefOrDie(env, keyphraseRecognitionExtraClass); - gKeyphraseRecognitionExtraCstor = GetMethodIDOrDie(env, keyphraseRecognitionExtraClass, - "", "(III[Landroid/hardware/soundtrigger/SoundTrigger$ConfidenceLevel;)V"); - gKeyphraseRecognitionExtraFields.id = GetFieldIDOrDie(env, gKeyphraseRecognitionExtraClass, - "id", "I"); - gKeyphraseRecognitionExtraFields.recognitionModes = GetFieldIDOrDie(env, - gKeyphraseRecognitionExtraClass, "recognitionModes", "I"); - gKeyphraseRecognitionExtraFields.coarseConfidenceLevel = GetFieldIDOrDie(env, - gKeyphraseRecognitionExtraClass, "coarseConfidenceLevel", "I"); - gKeyphraseRecognitionExtraFields.confidenceLevels = GetFieldIDOrDie(env, - gKeyphraseRecognitionExtraClass, "confidenceLevels", - "[Landroid/hardware/soundtrigger/SoundTrigger$ConfidenceLevel;"); - - jclass confidenceLevelClass = FindClassOrDie(env, kConfidenceLevelClassPathName); - gConfidenceLevelClass = MakeGlobalRefOrDie(env, confidenceLevelClass); - gConfidenceLevelCstor = GetMethodIDOrDie(env, confidenceLevelClass, "", "(II)V"); - gConfidenceLevelFields.userId = GetFieldIDOrDie(env, confidenceLevelClass, "userId", "I"); - gConfidenceLevelFields.confidenceLevel = GetFieldIDOrDie(env, confidenceLevelClass, - "confidenceLevel", "I"); - - jclass audioFormatClass = FindClassOrDie(env, kAudioFormatClassPathName); - gAudioFormatClass = MakeGlobalRefOrDie(env, audioFormatClass); - gAudioFormatCstor = GetMethodIDOrDie(env, audioFormatClass, "", "(IIII)V"); - - jclass soundModelEventClass = FindClassOrDie(env, kSoundModelEventClassPathName); - gSoundModelEventClass = MakeGlobalRefOrDie(env, soundModelEventClass); - gSoundModelEventCstor = GetMethodIDOrDie(env, soundModelEventClass, "", "(II[B)V"); - - - RegisterMethodsOrDie(env, kSoundTriggerClassPathName, gMethods, NELEM(gMethods)); - return RegisterMethodsOrDie(env, kModuleClassPathName, gModuleMethods, NELEM(gModuleMethods)); -} diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java index 3444be9353951..81789e1362c0d 100644 --- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java +++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java @@ -65,7 +65,7 @@ import java.util.Set; * {@link android.media.soundtrigger_middleware.Status} constants. Any other exception * thrown should be regarded as a bug in the implementation or one of its dependencies * (assuming correct usage). - *
  • The implementation is designed for testibility by featuring dependency injection (the + *
  • The implementation is designed for testability by featuring dependency injection (the * underlying HAL driver instances are passed to the ctor) and by minimizing dependencies * on Android runtime. *
  • The implementation is thread-safe. This is achieved by a simplistic model, where all entry-