diff --git a/api/current.txt b/api/current.txt index 66847b8cbefae..9e949cfd595f6 100644 --- a/api/current.txt +++ b/api/current.txt @@ -42770,14 +42770,19 @@ package android.service.voice { method public android.content.Intent createReEnrollIntent(); method public android.content.Intent createUnEnrollIntent(); method public int getParameter(int); + method public int getSupportedAudioCapabilities(); method public int getSupportedRecognitionModes(); method @Nullable public android.service.voice.AlwaysOnHotwordDetector.ModelParamRange queryParameter(int); method public int setParameter(int, int); method public boolean startRecognition(int); method public boolean stopRecognition(); + field public static final int AUDIO_CAPABILITY_ECHO_CANCELLATION = 1; // 0x1 + field public static final int AUDIO_CAPABILITY_NOISE_SUPPRESSION = 2; // 0x2 field public static final int MODEL_PARAM_THRESHOLD_FACTOR = 0; // 0x0 field public static final int RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS = 2; // 0x2 field public static final int RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO = 1; // 0x1 + field public static final int RECOGNITION_FLAG_ENABLE_AUDIO_ECHO_CANCELLATION = 4; // 0x4 + field public static final int RECOGNITION_FLAG_ENABLE_AUDIO_NOISE_SUPPRESSION = 8; // 0x8 field public static final int RECOGNITION_MODE_USER_IDENTIFICATION = 2; // 0x2 field public static final int RECOGNITION_MODE_VOICE_TRIGGER = 1; // 0x1 field public static final int STATE_HARDWARE_UNAVAILABLE = -2; // 0xfffffffe diff --git a/api/system-current.txt b/api/system-current.txt index 6da15248f01dc..309b9894cb094 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -3567,7 +3567,10 @@ package android.hardware.soundtrigger { public static final class SoundTrigger.ModuleProperties implements android.os.Parcelable { method public int describeContents(); method public void writeToParcel(android.os.Parcel, int); + field public static final int CAPABILITY_ECHO_CANCELLATION = 1; // 0x1 + field public static final int CAPABILITY_NOISE_SUPPRESSION = 2; // 0x2 field @NonNull public static final android.os.Parcelable.Creator CREATOR; + field public final int audioCapabilities; field @NonNull public final String description; field public final int id; field @NonNull public final String implementor; @@ -4363,6 +4366,8 @@ package android.media.soundtrigger { method @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public boolean stopRecognition(); field public static final int RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS = 2; // 0x2 field public static final int RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO = 1; // 0x1 + field public static final int RECOGNITION_FLAG_ENABLE_AUDIO_ECHO_CANCELLATION = 4; // 0x4 + field public static final int RECOGNITION_FLAG_ENABLE_AUDIO_NOISE_SUPPRESSION = 8; // 0x8 } public abstract static class SoundTriggerDetector.Callback { diff --git a/core/java/android/hardware/soundtrigger/ConversionUtil.java b/core/java/android/hardware/soundtrigger/ConversionUtil.java index 43f3787e15da0..d43a619a7c1f8 100644 --- a/core/java/android/hardware/soundtrigger/ConversionUtil.java +++ b/core/java/android/hardware/soundtrigger/ConversionUtil.java @@ -20,6 +20,7 @@ import android.annotation.Nullable; import android.hardware.soundtrigger.ModelParams; import android.media.AudioFormat; import android.media.audio.common.AudioConfig; +import android.media.soundtrigger_middleware.AudioCapabilities; import android.media.soundtrigger_middleware.ConfidenceLevel; import android.media.soundtrigger_middleware.ModelParameterRange; import android.media.soundtrigger_middleware.Phrase; @@ -56,7 +57,8 @@ class ConversionUtil { properties.maxBufferMs, properties.concurrentCapture, properties.powerConsumptionMw, - properties.triggerInEvent + properties.triggerInEvent, + aidl2apiAudioCapabilities(properties.audioCapabilities) ); } @@ -145,6 +147,7 @@ class ConversionUtil { apiConfig.keyphrases[i]); } aidlConfig.data = Arrays.copyOf(apiConfig.data, apiConfig.data.length); + aidlConfig.audioCapabilities = api2aidlAudioCapabilities(apiConfig.audioCapabilities); return aidlConfig; } @@ -326,4 +329,26 @@ class ConversionUtil { } return new SoundTrigger.ModelParamRange(aidlRange.minInclusive, aidlRange.maxInclusive); } + + public static int aidl2apiAudioCapabilities(int aidlCapabilities) { + int result = 0; + if ((aidlCapabilities & AudioCapabilities.ECHO_CANCELLATION) != 0) { + result |= SoundTrigger.ModuleProperties.CAPABILITY_ECHO_CANCELLATION; + } + if ((aidlCapabilities & AudioCapabilities.NOISE_SUPPRESSION) != 0) { + result |= SoundTrigger.ModuleProperties.CAPABILITY_NOISE_SUPPRESSION; + } + return result; + } + + public static int api2aidlAudioCapabilities(int apiCapabilities) { + int result = 0; + if ((apiCapabilities & SoundTrigger.ModuleProperties.CAPABILITY_ECHO_CANCELLATION) != 0) { + result |= AudioCapabilities.ECHO_CANCELLATION; + } + if ((apiCapabilities & SoundTrigger.ModuleProperties.CAPABILITY_NOISE_SUPPRESSION) != 0) { + result |= AudioCapabilities.NOISE_SUPPRESSION; + } + return result; + } } diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java index d87200931830a..60e466e5f2785 100644 --- a/core/java/android/hardware/soundtrigger/SoundTrigger.java +++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java @@ -24,6 +24,7 @@ import static android.system.OsConstants.EPIPE; import static java.util.Objects.requireNonNull; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; @@ -42,6 +43,8 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.util.Log; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; import java.util.UUID; @@ -83,6 +86,30 @@ public class SoundTrigger { * ****************************************************************************/ public static final class ModuleProperties implements Parcelable { + + /** + * Bit field values of AudioCapabilities supported by the implemented HAL + * driver. + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, prefix = { "AUDIO_CAPABILITY_" }, value = { + CAPABILITY_ECHO_CANCELLATION, + CAPABILITY_NOISE_SUPPRESSION + }) + public @interface AudioCapabilities {} + + /** + * If set the underlying module supports AEC. + * Describes bit field {@link ModuleProperties#audioCapabilities} + */ + public static final int CAPABILITY_ECHO_CANCELLATION = 0x1; + /** + * If set, the underlying module supports noise suppression. + * Describes bit field {@link ModuleProperties#audioCapabilities} + */ + public static final int CAPABILITY_NOISE_SUPPRESSION = 0x2; + /** Unique module ID provided by the native service */ public final int id; @@ -137,12 +164,19 @@ public class SoundTrigger { * recognition callback event */ public final boolean returnsTriggerInEvent; + /** + * Bit field encoding of the AudioCapabilities + * supported by the firmware. + */ + @AudioCapabilities + public final int audioCapabilities; + ModuleProperties(int id, @NonNull String implementor, @NonNull String description, @NonNull String uuid, int version, @NonNull String supportedModelArch, int maxSoundModels, int maxKeyphrases, int maxUsers, int recognitionModes, boolean supportsCaptureTransition, int maxBufferMs, boolean supportsConcurrentCapture, int powerConsumptionMw, - boolean returnsTriggerInEvent) { + boolean returnsTriggerInEvent, int audioCapabilities) { this.id = id; this.implementor = requireNonNull(implementor); this.description = requireNonNull(description); @@ -158,6 +192,7 @@ public class SoundTrigger { this.supportsConcurrentCapture = supportsConcurrentCapture; this.powerConsumptionMw = powerConsumptionMw; this.returnsTriggerInEvent = returnsTriggerInEvent; + this.audioCapabilities = audioCapabilities; } public static final @android.annotation.NonNull Parcelable.Creator CREATOR @@ -187,10 +222,11 @@ public class SoundTrigger { boolean supportsConcurrentCapture = in.readByte() == 1; int powerConsumptionMw = in.readInt(); boolean returnsTriggerInEvent = in.readByte() == 1; + int audioCapabilities = in.readInt(); return new ModuleProperties(id, implementor, description, uuid, version, supportedModelArch, maxSoundModels, maxKeyphrases, maxUsers, recognitionModes, supportsCaptureTransition, maxBufferMs, supportsConcurrentCapture, - powerConsumptionMw, returnsTriggerInEvent); + powerConsumptionMw, returnsTriggerInEvent, audioCapabilities); } @Override @@ -210,6 +246,7 @@ public class SoundTrigger { dest.writeByte((byte) (supportsConcurrentCapture ? 1 : 0)); dest.writeInt(powerConsumptionMw); dest.writeByte((byte) (returnsTriggerInEvent ? 1 : 0)); + dest.writeInt(audioCapabilities); } @Override @@ -227,7 +264,8 @@ public class SoundTrigger { + ", supportsCaptureTransition=" + supportsCaptureTransition + ", maxBufferMs=" + maxBufferMs + ", supportsConcurrentCapture=" + supportsConcurrentCapture + ", powerConsumptionMw=" + powerConsumptionMw - + ", returnsTriggerInEvent=" + returnsTriggerInEvent + "]"; + + ", returnsTriggerInEvent=" + returnsTriggerInEvent + + ", audioCapabilities=" + audioCapabilities + "]"; } } @@ -1049,13 +1087,27 @@ public class SoundTrigger { @NonNull public final byte[] data; - @UnsupportedAppUsage + /** + * Bit field encoding of the AudioCapabilities + * supported by the firmware. + */ + @ModuleProperties.AudioCapabilities + public final int audioCapabilities; + public RecognitionConfig(boolean captureRequested, boolean allowMultipleTriggers, - @Nullable KeyphraseRecognitionExtra[] keyphrases, @Nullable byte[] data) { + @Nullable KeyphraseRecognitionExtra[] keyphrases, @Nullable byte[] data, + int audioCapabilities) { this.captureRequested = captureRequested; this.allowMultipleTriggers = allowMultipleTriggers; this.keyphrases = keyphrases != null ? keyphrases : new KeyphraseRecognitionExtra[0]; this.data = data != null ? data : new byte[0]; + this.audioCapabilities = audioCapabilities; + } + + @UnsupportedAppUsage + public RecognitionConfig(boolean captureRequested, boolean allowMultipleTriggers, + @Nullable KeyphraseRecognitionExtra[] keyphrases, @Nullable byte[] data) { + this(captureRequested, allowMultipleTriggers, keyphrases, data, 0); } public static final @android.annotation.NonNull Parcelable.Creator CREATOR @@ -1075,7 +1127,9 @@ public class SoundTrigger { KeyphraseRecognitionExtra[] keyphrases = in.createTypedArray(KeyphraseRecognitionExtra.CREATOR); byte[] data = in.readBlob(); - return new RecognitionConfig(captureRequested, allowMultipleTriggers, keyphrases, data); + int audioCapabilities = in.readInt(); + return new RecognitionConfig(captureRequested, allowMultipleTriggers, keyphrases, data, + audioCapabilities); } @Override @@ -1084,6 +1138,7 @@ public class SoundTrigger { dest.writeByte((byte) (allowMultipleTriggers ? 1 : 0)); dest.writeTypedArray(keyphrases, flags); dest.writeBlob(data); + dest.writeInt(audioCapabilities); } @Override @@ -1095,7 +1150,8 @@ public class SoundTrigger { public String toString() { return "RecognitionConfig [captureRequested=" + captureRequested + ", allowMultipleTriggers=" + allowMultipleTriggers + ", keyphrases=" - + Arrays.toString(keyphrases) + ", data=" + Arrays.toString(data) + "]"; + + Arrays.toString(keyphrases) + ", data=" + Arrays.toString(data) + + ", audioCapabilities=" + Integer.toHexString(audioCapabilities) + "]"; } } diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java index d7c6d0f265c68..0f339988ba3e3 100644 --- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java +++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java @@ -119,7 +119,9 @@ public class AlwaysOnHotwordDetector { @IntDef(flag = true, prefix = { "RECOGNITION_FLAG_" }, value = { RECOGNITION_FLAG_NONE, RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO, - RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS + RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS, + RECOGNITION_FLAG_ENABLE_AUDIO_ECHO_CANCELLATION, + RECOGNITION_FLAG_ENABLE_AUDIO_NOISE_SUPPRESSION, }) public @interface RecognitionFlags {} @@ -144,6 +146,26 @@ public class AlwaysOnHotwordDetector { */ public static final int RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS = 0x2; + /** + * Audio capabilities flag for {@link #startRecognition(int)} that indicates + * if the underlying recognition should use AEC. + * This capability may or may not be supported by the system, and support can be queried + * by calling {@link #getSupportedAudioCapabilities()}. The corresponding capabilities field for + * this flag is {@link #AUDIO_CAPABILITY_ECHO_CANCELLATION}. If this flag is passed without the + * audio capability supported, there will be no audio effect applied. + */ + public static final int RECOGNITION_FLAG_ENABLE_AUDIO_ECHO_CANCELLATION = 0x4; + + /** + * Audio capabilities flag for {@link #startRecognition(int)} that indicates + * if the underlying recognition should use noise suppression. + * This capability may or may not be supported by the system, and support can be queried + * by calling {@link #getSupportedAudioCapabilities()}. The corresponding capabilities field for + * this flag is {@link #AUDIO_CAPABILITY_NOISE_SUPPRESSION}. If this flag is passed without the + * audio capability supported, there will be no audio effect applied. + */ + public static final int RECOGNITION_FLAG_ENABLE_AUDIO_NOISE_SUPPRESSION = 0x8; + //---- Recognition mode flags. Return codes for getSupportedRecognitionModes() ----// // Must be kept in sync with the related attribute defined as searchKeyphraseRecognitionFlags. @@ -168,6 +190,30 @@ public class AlwaysOnHotwordDetector { public static final int RECOGNITION_MODE_USER_IDENTIFICATION = SoundTrigger.RECOGNITION_MODE_USER_IDENTIFICATION; + //-- Audio capabilities. Values in returned bit field for getSupportedAudioCapabilities() --// + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, prefix = { "AUDIO_CAPABILITY_" }, value = { + AUDIO_CAPABILITY_ECHO_CANCELLATION, + AUDIO_CAPABILITY_NOISE_SUPPRESSION, + }) + public @interface AudioCapabilities {} + + /** + * If set the underlying module supports AEC. + * Returned by {@link #getSupportedAudioCapabilities()} + */ + public static final int AUDIO_CAPABILITY_ECHO_CANCELLATION = + SoundTrigger.ModuleProperties.CAPABILITY_ECHO_CANCELLATION; + + /** + * If set, the underlying module supports noise suppression. + * Returned by {@link #getSupportedAudioCapabilities()} + */ + public static final int AUDIO_CAPABILITY_NOISE_SUPPRESSION = + SoundTrigger.ModuleProperties.CAPABILITY_NOISE_SUPPRESSION; + /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(flag = true, prefix = { "MODEL_PARAM_" }, value = { @@ -447,6 +493,37 @@ public class AlwaysOnHotwordDetector { return mKeyphraseMetadata.recognitionModeFlags; } + /** + * Get the audio capabilities supported by the platform which can be enabled when + * starting a recognition. + * + * @see #AUDIO_CAPABILITY_ECHO_CANCELLATION + * @see #AUDIO_CAPABILITY_NOISE_SUPPRESSION + * + * @return Bit field encoding of the AudioCapabilities supported. + */ + @AudioCapabilities + public int getSupportedAudioCapabilities() { + if (DBG) Slog.d(TAG, "getSupportedAudioCapabilities()"); + synchronized (mLock) { + return getSupportedAudioCapabilitiesLocked(); + } + } + + private int getSupportedAudioCapabilitiesLocked() { + try { + ModuleProperties properties = + mModelManagementService.getDspModuleProperties(mVoiceInteractionService); + if (properties != null) { + return properties.audioCapabilities; + } + + return 0; + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Starts recognition for the associated keyphrase. * @@ -711,12 +788,21 @@ public class AlwaysOnHotwordDetector { (recognitionFlags&RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO) != 0; boolean allowMultipleTriggers = (recognitionFlags&RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS) != 0; + + int audioCapabilities = 0; + if ((recognitionFlags & RECOGNITION_FLAG_ENABLE_AUDIO_ECHO_CANCELLATION) != 0) { + audioCapabilities |= AUDIO_CAPABILITY_ECHO_CANCELLATION; + } + if ((recognitionFlags & RECOGNITION_FLAG_ENABLE_AUDIO_NOISE_SUPPRESSION) != 0) { + audioCapabilities |= AUDIO_CAPABILITY_NOISE_SUPPRESSION; + } + int code = STATUS_ERROR; try { code = mModelManagementService.startRecognition(mVoiceInteractionService, mKeyphraseMetadata.id, mLocale.toLanguageTag(), mInternalCallback, new RecognitionConfig(captureTriggerAudio, allowMultipleTriggers, - recognitionExtra, null /* additional data */)); + recognitionExtra, null /* additional data */, audioCapabilities)); } catch (RemoteException e) { Slog.w(TAG, "RemoteException in startRecognition!", e); } diff --git a/media/java/android/media/soundtrigger/SoundTriggerDetector.java b/media/java/android/media/soundtrigger/SoundTriggerDetector.java index 77596a5de8153..118f65c214e11 100644 --- a/media/java/android/media/soundtrigger/SoundTriggerDetector.java +++ b/media/java/android/media/soundtrigger/SoundTriggerDetector.java @@ -25,6 +25,7 @@ import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.hardware.soundtrigger.IRecognitionStatusCallback; import android.hardware.soundtrigger.SoundTrigger; +import android.hardware.soundtrigger.SoundTrigger.ModuleProperties; import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig; import android.media.AudioFormat; import android.os.Handler; @@ -75,7 +76,9 @@ public final class SoundTriggerDetector { value = { RECOGNITION_FLAG_NONE, RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO, - RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS + RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS, + RECOGNITION_FLAG_ENABLE_AUDIO_ECHO_CANCELLATION, + RECOGNITION_FLAG_ENABLE_AUDIO_NOISE_SUPPRESSION, }) public @interface RecognitionFlags {} @@ -100,10 +103,33 @@ public final class SoundTriggerDetector { * triggers after a call to {@link #startRecognition(int)}, if the model * triggers multiple times. * When this isn't specified, the default behavior is to stop recognition once the - * trigger happenss, till the caller starts recognition again. + * trigger happens, till the caller starts recognition again. */ public static final int RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS = 0x2; + /** + * Audio capabilities flag for {@link #startRecognition(int)} that indicates + * if the underlying recognition should use AEC. + * This capability may or may not be supported by the system, and support can be queried + * by calling {@link SoundTriggerManager#getModuleProperties()} and checking + * {@link ModuleProperties#audioCapabilities}. The corresponding capabilities field for + * this flag is {@link SoundTrigger.ModuleProperties#CAPABILITY_ECHO_CANCELLATION}. + * If this flag is passed without the audio capability supported, there will be no audio effect + * applied. + */ + public static final int RECOGNITION_FLAG_ENABLE_AUDIO_ECHO_CANCELLATION = 0x4; + + /** + * Audio capabilities flag for {@link #startRecognition(int)} that indicates + * if the underlying recognition should use noise suppression. + * This capability may or may not be supported by the system, and support can be queried + * by calling {@link SoundTriggerManager#getModuleProperties()} and checking + * {@link ModuleProperties#audioCapabilities}. The corresponding capabilities field for + * this flag is {@link SoundTrigger.ModuleProperties#CAPABILITY_NOISE_SUPPRESSION}. If this flag + * is passed without the audio capability supported, there will be no audio effect applied. + */ + public static final int RECOGNITION_FLAG_ENABLE_AUDIO_NOISE_SUPPRESSION = 0x8; + /** * Additional payload for {@link Callback#onDetected}. */ @@ -267,11 +293,20 @@ public final class SoundTriggerDetector { boolean allowMultipleTriggers = (recognitionFlags & RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS) != 0; - int status = STATUS_OK; + + int audioCapabilities = 0; + if ((recognitionFlags & RECOGNITION_FLAG_ENABLE_AUDIO_ECHO_CANCELLATION) != 0) { + audioCapabilities |= SoundTrigger.ModuleProperties.CAPABILITY_ECHO_CANCELLATION; + } + if ((recognitionFlags & RECOGNITION_FLAG_ENABLE_AUDIO_NOISE_SUPPRESSION) != 0) { + audioCapabilities |= SoundTrigger.ModuleProperties.CAPABILITY_NOISE_SUPPRESSION; + } + + int status; try { status = mSoundTriggerService.startRecognition(new ParcelUuid(mSoundModelId), mRecognitionCallback, new RecognitionConfig(captureTriggerAudio, - allowMultipleTriggers, null, null)); + allowMultipleTriggers, null, null, audioCapabilities)); } catch (RemoteException e) { return false; } diff --git a/media/java/android/media/soundtrigger_middleware/AudioCapabilities.aidl b/media/java/android/media/soundtrigger_middleware/AudioCapabilities.aidl new file mode 100644 index 0000000000000..97a8849c7b077 --- /dev/null +++ b/media/java/android/media/soundtrigger_middleware/AudioCapabilities.aidl @@ -0,0 +1,32 @@ +/* + * 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.media.soundtrigger_middleware; + +/** + * AudioCapabilities supported by the implemented HAL driver. + * @hide + */ +@Backing(type="int") +enum AudioCapabilities { + /** + * If set the underlying module supports AEC. + */ + ECHO_CANCELLATION = 1 << 0, + /** + * If set, the underlying module supports noise suppression. + */ + NOISE_SUPPRESSION = 1 << 1, +} diff --git a/media/java/android/media/soundtrigger_middleware/RecognitionConfig.aidl b/media/java/android/media/soundtrigger_middleware/RecognitionConfig.aidl index c7642e8c12410..5c0eeb1e32b11 100644 --- a/media/java/android/media/soundtrigger_middleware/RecognitionConfig.aidl +++ b/media/java/android/media/soundtrigger_middleware/RecognitionConfig.aidl @@ -28,6 +28,12 @@ parcelable RecognitionConfig { /* Configuration for each key phrase. */ PhraseRecognitionExtra[] phraseRecognitionExtras; + /** + * Bit field encoding of the AudioCapabilities + * supported by the firmware. + */ + int audioCapabilities; + /** Opaque capture configuration data. */ byte[] data; } diff --git a/media/java/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl b/media/java/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl index 909f1a00006f3..9c56e7b98b3f2 100644 --- a/media/java/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl +++ b/media/java/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl @@ -58,4 +58,11 @@ parcelable SoundTriggerModuleProperties { * Rated power consumption when detection is active with TDB * silence/sound/speech ratio */ int powerConsumptionMw; + /** + * Bit field encoding of the AudioCapabilities + * supported by the firmware. + * This property is supported for soundtrigger HAL v2.3 and above. + * If running a previous version, this value will be 0. + */ + int audioCapabilities; } diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java b/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java index 8385f406f13dd..a641f06ac3caf 100644 --- a/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java +++ b/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java @@ -24,6 +24,7 @@ import android.hardware.soundtrigger.V2_3.ISoundTriggerHw; import android.hardware.soundtrigger.V2_3.Properties; import android.media.audio.common.AudioConfig; import android.media.audio.common.AudioOffloadInfo; +import android.media.soundtrigger_middleware.AudioCapabilities; import android.media.soundtrigger_middleware.ConfidenceLevel; import android.media.soundtrigger_middleware.ModelParameter; import android.media.soundtrigger_middleware.ModelParameterRange; @@ -74,6 +75,8 @@ class ConversionUtil { @NonNull Properties hidlProperties) { SoundTriggerModuleProperties aidlProperties = hidl2aidlProperties(hidlProperties.base); aidlProperties.supportedModelArch = hidlProperties.supportedModelArch; + aidlProperties.audioCapabilities = + hidl2aidlAudioCapabilities(hidlProperties.audioCapabilities); return aidlProperties; } @@ -209,16 +212,17 @@ class ConversionUtil { return hidlModel; } - static @NonNull - ISoundTriggerHw.RecognitionConfig aidl2hidlRecognitionConfig( + static @NonNull android.hardware.soundtrigger.V2_3.RecognitionConfig aidl2hidlRecognitionConfig( @NonNull RecognitionConfig aidlConfig) { - ISoundTriggerHw.RecognitionConfig hidlConfig = new ISoundTriggerHw.RecognitionConfig(); - hidlConfig.header.captureRequested = aidlConfig.captureRequested; + android.hardware.soundtrigger.V2_3.RecognitionConfig hidlConfig = + new android.hardware.soundtrigger.V2_3.RecognitionConfig(); + hidlConfig.base.header.captureRequested = aidlConfig.captureRequested; for (PhraseRecognitionExtra aidlPhraseExtra : aidlConfig.phraseRecognitionExtras) { - hidlConfig.header.phrases.add(aidl2hidlPhraseRecognitionExtra(aidlPhraseExtra)); + hidlConfig.base.header.phrases.add(aidl2hidlPhraseRecognitionExtra(aidlPhraseExtra)); } - hidlConfig.data = HidlMemoryUtil.byteArrayToHidlMemory(aidlConfig.data, + hidlConfig.base.data = HidlMemoryUtil.byteArrayToHidlMemory(aidlConfig.data, "SoundTrigger RecognitionConfig"); + hidlConfig.audioCapabilities = aidlConfig.audioCapabilities; return hidlConfig; } @@ -395,4 +399,17 @@ class ConversionUtil { return android.hardware.soundtrigger.V2_3.ModelParameter.INVALID; } } + + static int hidl2aidlAudioCapabilities(int hidlCapabilities) { + int aidlCapabilities = 0; + if ((hidlCapabilities + & android.hardware.soundtrigger.V2_3.AudioCapabilities.ECHO_CANCELLATION) != 0) { + aidlCapabilities |= AudioCapabilities.ECHO_CANCELLATION; + } + if ((hidlCapabilities + & android.hardware.soundtrigger.V2_3.AudioCapabilities.NOISE_SUPPRESSION) != 0) { + aidlCapabilities |= AudioCapabilities.NOISE_SUPPRESSION; + } + return aidlCapabilities; + } } diff --git a/services/core/java/com/android/server/soundtrigger_middleware/Hw2CompatUtil.java b/services/core/java/com/android/server/soundtrigger_middleware/Hw2CompatUtil.java index dbf91a984bdaa..a42d292770ae5 100644 --- a/services/core/java/com/android/server/soundtrigger_middleware/Hw2CompatUtil.java +++ b/services/core/java/com/android/server/soundtrigger_middleware/Hw2CompatUtil.java @@ -66,12 +66,17 @@ class Hw2CompatUtil { return model_2_0; } - static android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig convertRecognitionConfig_2_1_to_2_0( - android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig config) { + static android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig convertRecognitionConfig_2_3_to_2_1( + android.hardware.soundtrigger.V2_3.RecognitionConfig config) { + return config.base; + } + + static android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig convertRecognitionConfig_2_3_to_2_0( + android.hardware.soundtrigger.V2_3.RecognitionConfig config) { android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig config_2_0 = - config.header; + config.base.header; // Note: this mutates the input! - config_2_0.data = HidlMemoryUtil.hidlMemoryToByteList(config.data); + config_2_0.data = HidlMemoryUtil.hidlMemoryToByteList(config.base.data); return config_2_0; } diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHw2.java b/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHw2.java index 2f024a50a2768..8b434bd843639 100644 --- a/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHw2.java +++ b/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHw2.java @@ -92,12 +92,12 @@ public interface ISoundTriggerHw2 { void stopAllRecognitions(); /** - * @see android.hardware.soundtrigger.V2_2.ISoundTriggerHw#startRecognition_2_1(int, - * android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig, + * @see android.hardware.soundtrigger.V2_3.ISoundTriggerHw#startRecognition_2_3(int, + * android.hardware.soundtrigger.V2_3.RecognitionConfig, * android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback, int) */ void startRecognition(int modelHandle, - android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig config, + android.hardware.soundtrigger.V2_3.RecognitionConfig config, SoundTriggerHw2Compat.Callback callback, int cookie); /** diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java index 3354c561b57a2..2f087f46da869 100644 --- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java +++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java @@ -217,16 +217,15 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHw2 { @Override public void startRecognition(int modelHandle, - android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig config, + android.hardware.soundtrigger.V2_3.RecognitionConfig config, Callback callback, int cookie) { try { try { - int retval = as2_1().startRecognition_2_1(modelHandle, config, - new SoundTriggerCallback(callback), cookie); - handleHalStatus(retval, "startRecognition_2_1"); + int retval = as2_3().startRecognition_2_3(modelHandle, config); + handleHalStatus(retval, "startRecognition_2_3"); } catch (NotSupported e) { // Fall-back to the 2.0 version: - startRecognition_2_0(modelHandle, config, callback, cookie); + startRecognition_2_1(modelHandle, config, callback, cookie); } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); @@ -369,13 +368,31 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHw2 { return handle.get(); } + private void startRecognition_2_1(int modelHandle, + android.hardware.soundtrigger.V2_3.RecognitionConfig config, + Callback callback, int cookie) { + try { + try { + android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig config_2_1 = + Hw2CompatUtil.convertRecognitionConfig_2_3_to_2_1(config); + int retval = as2_1().startRecognition_2_1(modelHandle, config_2_1, + new SoundTriggerCallback(callback), cookie); + handleHalStatus(retval, "startRecognition_2_1"); + } catch (NotSupported e) { + // Fall-back to the 2.0 version: + startRecognition_2_0(modelHandle, config, callback, cookie); + } + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + private void startRecognition_2_0(int modelHandle, - android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig config, + android.hardware.soundtrigger.V2_3.RecognitionConfig config, Callback callback, int cookie) throws RemoteException { - android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig config_2_0 = - Hw2CompatUtil.convertRecognitionConfig_2_1_to_2_0(config); + Hw2CompatUtil.convertRecognitionConfig_2_3_to_2_0(config); int retval = as2_0().startRecognition(modelHandle, config_2_0, new SoundTriggerCallback(callback), cookie); handleHalStatus(retval, "startRecognition"); 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 81789e1362c0d..f024edecab3b7 100644 --- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java +++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java @@ -401,10 +401,10 @@ class SoundTriggerModule { notifyAbort(); return; } - android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig hidlConfig = + android.hardware.soundtrigger.V2_3.RecognitionConfig hidlConfig = ConversionUtil.aidl2hidlRecognitionConfig(config); - hidlConfig.header.captureDevice = mSession.mDeviceHandle; - hidlConfig.header.captureHandle = mSession.mIoHandle; + hidlConfig.base.header.captureDevice = mSession.mDeviceHandle; + hidlConfig.base.header.captureHandle = mSession.mIoHandle; mHalService.startRecognition(mHandle, hidlConfig, this, 0); setState(ModelState.ACTIVE); } diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java index 0f75816082fd8..cc170af2c57bb 100644 --- a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java +++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java @@ -37,6 +37,7 @@ import android.hardware.audio.common.V2_0.Uuid; import android.hardware.soundtrigger.V2_3.OptionalModelParameterRange; import android.media.audio.common.AudioChannelMask; import android.media.audio.common.AudioFormat; +import android.media.soundtrigger_middleware.AudioCapabilities; import android.media.soundtrigger_middleware.ConfidenceLevel; import android.media.soundtrigger_middleware.ISoundTriggerCallback; import android.media.soundtrigger_middleware.ISoundTriggerModule; @@ -59,6 +60,7 @@ import android.os.HwParcel; import android.os.IHwBinder; import android.os.IHwInterface; import android.os.RemoteException; +import android.util.Pair; import org.junit.Before; import org.junit.Test; @@ -161,6 +163,9 @@ public class SoundTriggerMiddlewareImplTest { new android.hardware.soundtrigger.V2_3.Properties(); properties.base = createDefaultProperties(supportConcurrentCapture); properties.supportedModelArch = "supportedModelArch"; + properties.audioCapabilities = + android.hardware.soundtrigger.V2_3.AudioCapabilities.ECHO_CANCELLATION + | android.hardware.soundtrigger.V2_3.AudioCapabilities.NOISE_SUPPRESSION; return properties; } @@ -185,8 +190,11 @@ public class SoundTriggerMiddlewareImplTest { if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) { assertEquals("supportedModelArch", properties.supportedModelArch); + assertEquals(AudioCapabilities.ECHO_CANCELLATION | AudioCapabilities.NOISE_SUPPRESSION, + properties.audioCapabilities); } else { assertEquals("", properties.supportedModelArch); + assertEquals(0, properties.audioCapabilities); } } @@ -330,12 +338,17 @@ public class SoundTriggerMiddlewareImplTest { mService = new SoundTriggerMiddlewareImpl(mHalDriver, mAudioSessionProvider); } - private int loadGenericModel_2_0(ISoundTriggerModule module, int hwHandle) - throws RemoteException { + private Pair loadGenericModel_2_0(ISoundTriggerModule module, + int hwHandle) throws RemoteException { SoundModel model = createGenericSoundModel(); ArgumentCaptor modelCaptor = ArgumentCaptor.forClass( android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel.class); + ArgumentCaptor callbackCaptor = + ArgumentCaptor.forClass( + android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.class); + ArgumentCaptor cookieCaptor = ArgumentCaptor.forClass(Integer.class); + doAnswer(invocation -> { android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback callback = invocation.getArgument(1); @@ -354,7 +367,8 @@ public class SoundTriggerMiddlewareImplTest { modelEvent.model = hwHandle; callback.soundModelCallback(modelEvent, callbackCookie); return null; - }).when(mHalDriver).loadSoundModel(modelCaptor.capture(), any(), anyInt(), any()); + }).when(mHalDriver).loadSoundModel(modelCaptor.capture(), callbackCaptor.capture(), + cookieCaptor.capture(), any()); when(mAudioSessionProvider.acquireSession()).thenReturn( new SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession(101, 102, 103)); @@ -371,17 +385,23 @@ public class SoundTriggerMiddlewareImplTest { assertEquals(model.vendorUuid, ConversionUtil.hidl2aidlUuid(hidlModel.vendorUuid)); assertArrayEquals(new Byte[]{91, 92, 93, 94, 95}, hidlModel.data.toArray()); - return handle; + return new Pair<>(handle, + new SoundTriggerHwCallback(callbackCaptor.getValue(), cookieCaptor.getValue())); } - private int loadGenericModel_2_1(ISoundTriggerModule module, int hwHandle) - throws RemoteException { + private Pair loadGenericModel_2_1(ISoundTriggerModule module, + int hwHandle) throws RemoteException { android.hardware.soundtrigger.V2_1.ISoundTriggerHw driver = (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver; SoundModel model = createGenericSoundModel(); ArgumentCaptor modelCaptor = ArgumentCaptor.forClass( android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel.class); + ArgumentCaptor callbackCaptor = + ArgumentCaptor.forClass( + android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.class); + ArgumentCaptor cookieCaptor = ArgumentCaptor.forClass(Integer.class); + doAnswer(invocation -> { android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback callback = invocation.getArgument(1); @@ -400,7 +420,8 @@ public class SoundTriggerMiddlewareImplTest { modelEvent.header.model = hwHandle; callback.soundModelCallback_2_1(modelEvent, callbackCookie); return null; - }).when(driver).loadSoundModel_2_1(modelCaptor.capture(), any(), anyInt(), any()); + }).when(driver).loadSoundModel_2_1(modelCaptor.capture(), callbackCaptor.capture(), + cookieCaptor.capture(), any()); when(mAudioSessionProvider.acquireSession()).thenReturn( new SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession(101, 102, 103)); @@ -418,10 +439,12 @@ public class SoundTriggerMiddlewareImplTest { assertArrayEquals(new byte[]{91, 92, 93, 94, 95}, HidlMemoryUtil.hidlMemoryToByteArray(hidlModel.data)); - return handle; + return new Pair<>(handle, + new SoundTriggerHwCallback(callbackCaptor.getValue(), cookieCaptor.getValue())); } - private int loadGenericModel(ISoundTriggerModule module, int hwHandle) throws RemoteException { + private Pair loadGenericModel(ISoundTriggerModule module, + int hwHandle) throws RemoteException { if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) { return loadGenericModel_2_1(module, hwHandle); } else { @@ -429,12 +452,17 @@ public class SoundTriggerMiddlewareImplTest { } } - private int loadPhraseModel_2_0(ISoundTriggerModule module, int hwHandle) - throws RemoteException { + private Pair loadPhraseModel_2_0(ISoundTriggerModule module, + int hwHandle) throws RemoteException { PhraseSoundModel model = createPhraseSoundModel(); ArgumentCaptor modelCaptor = ArgumentCaptor.forClass( android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel.class); + ArgumentCaptor callbackCaptor = + ArgumentCaptor.forClass( + android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.class); + ArgumentCaptor cookieCaptor = ArgumentCaptor.forClass(Integer.class); + doAnswer(invocation -> { android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback callback = invocation.getArgument( @@ -456,7 +484,8 @@ public class SoundTriggerMiddlewareImplTest { modelEvent.model = hwHandle; callback.soundModelCallback(modelEvent, callbackCookie); return null; - }).when(mHalDriver).loadPhraseSoundModel(modelCaptor.capture(), any(), anyInt(), any()); + }).when(mHalDriver).loadPhraseSoundModel(modelCaptor.capture(), callbackCaptor.capture(), + cookieCaptor.capture(), any()); when(mAudioSessionProvider.acquireSession()).thenReturn( new SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession(101, 102, 103)); @@ -488,11 +517,12 @@ public class SoundTriggerMiddlewareImplTest { | android.hardware.soundtrigger.V2_0.RecognitionMode.USER_IDENTIFICATION, hidlPhrase.recognitionModes); - return handle; + return new Pair<>(handle, + new SoundTriggerHwCallback(callbackCaptor.getValue(), cookieCaptor.getValue())); } - private int loadPhraseModel_2_1(ISoundTriggerModule module, int hwHandle) - throws RemoteException { + private Pair loadPhraseModel_2_1(ISoundTriggerModule module, + int hwHandle) throws RemoteException { android.hardware.soundtrigger.V2_1.ISoundTriggerHw driver = (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver; @@ -500,6 +530,11 @@ public class SoundTriggerMiddlewareImplTest { ArgumentCaptor modelCaptor = ArgumentCaptor.forClass( android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel.class); + ArgumentCaptor callbackCaptor = + ArgumentCaptor.forClass( + android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.class); + ArgumentCaptor cookieCaptor = ArgumentCaptor.forClass(Integer.class); + doAnswer(invocation -> { android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback callback = invocation.getArgument( @@ -521,7 +556,8 @@ public class SoundTriggerMiddlewareImplTest { modelEvent.header.model = hwHandle; callback.soundModelCallback_2_1(modelEvent, callbackCookie); return null; - }).when(driver).loadPhraseSoundModel_2_1(modelCaptor.capture(), any(), anyInt(), any()); + }).when(driver).loadPhraseSoundModel_2_1(modelCaptor.capture(), callbackCaptor.capture(), + cookieCaptor.capture(), any()); when(mAudioSessionProvider.acquireSession()).thenReturn( new SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession(101, 102, 103)); @@ -554,10 +590,12 @@ public class SoundTriggerMiddlewareImplTest { | android.hardware.soundtrigger.V2_0.RecognitionMode.USER_IDENTIFICATION, hidlPhrase.recognitionModes); - return handle; + return new Pair<>(handle, + new SoundTriggerHwCallback(callbackCaptor.getValue(), cookieCaptor.getValue())); } - private int loadPhraseModel(ISoundTriggerModule module, int hwHandle) throws RemoteException { + private Pair loadPhraseModel( + ISoundTriggerModule module, int hwHandle) throws RemoteException { if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) { return loadPhraseModel_2_1(module, hwHandle); } else { @@ -572,18 +610,14 @@ public class SoundTriggerMiddlewareImplTest { verify(mAudioSessionProvider).releaseSession(101); } - private SoundTriggerHwCallback startRecognition_2_0(ISoundTriggerModule module, int handle, + private void startRecognition_2_0(ISoundTriggerModule module, int handle, int hwHandle) throws RemoteException { ArgumentCaptor configCaptor = ArgumentCaptor.forClass( android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig.class); - ArgumentCaptor callbackCaptor = - ArgumentCaptor.forClass( - android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.class); - ArgumentCaptor cookieCaptor = ArgumentCaptor.forClass(Integer.class); - when(mHalDriver.startRecognition(eq(hwHandle), configCaptor.capture(), - callbackCaptor.capture(), cookieCaptor.capture())).thenReturn(0); + when(mHalDriver.startRecognition(eq(hwHandle), configCaptor.capture(), any(), anyInt())) + .thenReturn(0); RecognitionConfig config = createRecognitionConfig(); @@ -606,11 +640,9 @@ public class SoundTriggerMiddlewareImplTest { assertEquals(234, halLevel.userId); assertEquals(34, halLevel.levelPercent); assertArrayEquals(new Byte[]{5, 4, 3, 2, 1}, halConfig.data.toArray()); - - return new SoundTriggerHwCallback(callbackCaptor.getValue(), cookieCaptor.getValue()); } - private SoundTriggerHwCallback startRecognition_2_1(ISoundTriggerModule module, int handle, + private void startRecognition_2_1(ISoundTriggerModule module, int handle, int hwHandle) throws RemoteException { android.hardware.soundtrigger.V2_1.ISoundTriggerHw driver = (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver; @@ -618,13 +650,9 @@ public class SoundTriggerMiddlewareImplTest { ArgumentCaptor configCaptor = ArgumentCaptor.forClass( android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig.class); - ArgumentCaptor callbackCaptor = - ArgumentCaptor.forClass( - android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.class); - ArgumentCaptor cookieCaptor = ArgumentCaptor.forClass(Integer.class); - when(driver.startRecognition_2_1(eq(hwHandle), configCaptor.capture(), - callbackCaptor.capture(), cookieCaptor.capture())).thenReturn(0); + when(driver.startRecognition_2_1(eq(hwHandle), configCaptor.capture(), any(), anyInt())) + .thenReturn(0); RecognitionConfig config = createRecognitionConfig(); @@ -648,16 +676,56 @@ public class SoundTriggerMiddlewareImplTest { assertEquals(34, halLevel.levelPercent); assertArrayEquals(new byte[]{5, 4, 3, 2, 1}, HidlMemoryUtil.hidlMemoryToByteArray(halConfig.data)); - - return new SoundTriggerHwCallback(callbackCaptor.getValue(), cookieCaptor.getValue()); } - private SoundTriggerHwCallback startRecognition(ISoundTriggerModule module, int handle, + private void startRecognition_2_3(ISoundTriggerModule module, int handle, int hwHandle) throws RemoteException { - if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) { - return startRecognition_2_1(module, handle, hwHandle); + android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver = + (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver; + + ArgumentCaptor + configCaptor = ArgumentCaptor.forClass( + android.hardware.soundtrigger.V2_3.RecognitionConfig.class); + + when(driver.startRecognition_2_3(eq(hwHandle), configCaptor.capture())).thenReturn(0); + + RecognitionConfig config = createRecognitionConfig(); + + module.startRecognition(handle, config); + verify(driver).startRecognition_2_3(eq(hwHandle), any()); + + android.hardware.soundtrigger.V2_3.RecognitionConfig halConfigExtended = + configCaptor.getValue(); + android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig halConfig_2_1 = + halConfigExtended.base; + + assertTrue(halConfig_2_1.header.captureRequested); + assertEquals(102, halConfig_2_1.header.captureHandle); + assertEquals(103, halConfig_2_1.header.captureDevice); + assertEquals(1, halConfig_2_1.header.phrases.size()); + android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halPhraseExtra = + halConfig_2_1.header.phrases.get(0); + assertEquals(123, halPhraseExtra.id); + assertEquals(4, halPhraseExtra.confidenceLevel); + assertEquals(5, halPhraseExtra.recognitionModes); + assertEquals(1, halPhraseExtra.levels.size()); + android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel = halPhraseExtra.levels.get(0); + assertEquals(234, halLevel.userId); + assertEquals(34, halLevel.levelPercent); + assertArrayEquals(new byte[]{5, 4, 3, 2, 1}, + HidlMemoryUtil.hidlMemoryToByteArray(halConfig_2_1.data)); + assertEquals(AudioCapabilities.ECHO_CANCELLATION + | AudioCapabilities.NOISE_SUPPRESSION, halConfigExtended.audioCapabilities); + } + + private void startRecognition(ISoundTriggerModule module, int handle, + int hwHandle) throws RemoteException { + if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) { + startRecognition_2_3(module, handle, hwHandle); + } else if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) { + startRecognition_2_1(module, handle, hwHandle); } else { - return startRecognition_2_0(module, handle, hwHandle); + startRecognition_2_0(module, handle, hwHandle); } } @@ -672,6 +740,8 @@ public class SoundTriggerMiddlewareImplTest { config.phraseRecognitionExtras[0].levels[0].userId = 234; config.phraseRecognitionExtras[0].levels[0].levelPercent = 34; config.data = new byte[]{5, 4, 3, 2, 1}; + config.audioCapabilities = AudioCapabilities.ECHO_CANCELLATION + | AudioCapabilities.NOISE_SUPPRESSION; return config; } @@ -687,6 +757,9 @@ public class SoundTriggerMiddlewareImplTest { if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) { verify((android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver, never()).startRecognition_2_1(anyInt(), any(), any(), anyInt()); + } else if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) { + verify((android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver, + never()).startRecognition_2_3(anyInt(), any()); } } @@ -798,7 +871,7 @@ public class SoundTriggerMiddlewareImplTest { ISoundTriggerModule module = mService.attach(0, callback); final int hwHandle = 7; - int handle = loadGenericModel(module, hwHandle); + int handle = loadGenericModel(module, hwHandle).first; unloadModel(module, handle, hwHandle); module.detach(); } @@ -810,7 +883,7 @@ public class SoundTriggerMiddlewareImplTest { ISoundTriggerModule module = mService.attach(0, callback); final int hwHandle = 73; - int handle = loadPhraseModel(module, hwHandle); + int handle = loadPhraseModel(module, hwHandle).first; unloadModel(module, handle, hwHandle); module.detach(); } @@ -823,7 +896,7 @@ public class SoundTriggerMiddlewareImplTest { // Load the model. final int hwHandle = 7; - int handle = loadGenericModel(module, hwHandle); + int handle = loadGenericModel(module, hwHandle).first; // Initiate a recognition. startRecognition(module, handle, hwHandle); @@ -844,7 +917,7 @@ public class SoundTriggerMiddlewareImplTest { // Load the model. final int hwHandle = 67; - int handle = loadPhraseModel(module, hwHandle); + int handle = loadPhraseModel(module, hwHandle).first; // Initiate a recognition. startRecognition(module, handle, hwHandle); @@ -865,10 +938,12 @@ public class SoundTriggerMiddlewareImplTest { // Load the model. final int hwHandle = 7; - int handle = loadGenericModel(module, hwHandle); + Pair modelHandles = loadGenericModel(module, hwHandle); + int handle = modelHandles.first; + SoundTriggerHwCallback hwCallback = modelHandles.second; // Initiate a recognition. - SoundTriggerHwCallback hwCallback = startRecognition(module, handle, hwHandle); + startRecognition(module, handle, hwHandle); // Signal a capture from the driver. hwCallback.sendRecognitionEvent(hwHandle, @@ -894,10 +969,12 @@ public class SoundTriggerMiddlewareImplTest { // Load the model. final int hwHandle = 7; - int handle = loadPhraseModel(module, hwHandle); + Pair modelHandles = loadPhraseModel(module, hwHandle); + int handle = modelHandles.first; + SoundTriggerHwCallback hwCallback = modelHandles.second; // Initiate a recognition. - SoundTriggerHwCallback hwCallback = startRecognition(module, handle, hwHandle); + startRecognition(module, handle, hwHandle); // Signal a capture from the driver. hwCallback.sendPhraseRecognitionEvent(hwHandle, @@ -930,10 +1007,12 @@ public class SoundTriggerMiddlewareImplTest { // Load the model. final int hwHandle = 17; - int handle = loadGenericModel(module, hwHandle); + Pair modelHandles = loadGenericModel(module, hwHandle); + int handle = modelHandles.first; + SoundTriggerHwCallback hwCallback = modelHandles.second; // Initiate a recognition. - SoundTriggerHwCallback hwCallback = startRecognition(module, handle, hwHandle); + startRecognition(module, handle, hwHandle); // Force a trigger. module.forceRecognitionEvent(handle); @@ -973,10 +1052,12 @@ public class SoundTriggerMiddlewareImplTest { // Load the model. final int hwHandle = 17; - int handle = loadPhraseModel(module, hwHandle); + Pair modelHandles = loadPhraseModel(module, hwHandle); + int handle = modelHandles.first; + SoundTriggerHwCallback hwCallback = modelHandles.second; // Initiate a recognition. - SoundTriggerHwCallback hwCallback = startRecognition(module, handle, hwHandle); + startRecognition(module, handle, hwHandle); // Force a trigger. module.forceRecognitionEvent(handle); @@ -1013,7 +1094,7 @@ public class SoundTriggerMiddlewareImplTest { // Load the model. final int hwHandle = 11; - int handle = loadGenericModel(module, hwHandle); + int handle = loadGenericModel(module, hwHandle).first; // Initiate a recognition. startRecognition(module, handle, hwHandle); @@ -1061,7 +1142,7 @@ public class SoundTriggerMiddlewareImplTest { // Load the model. final int hwHandle = 11; - int handle = loadPhraseModel(module, hwHandle); + int handle = loadPhraseModel(module, hwHandle).first; // Initiate a recognition. startRecognition(module, handle, hwHandle); @@ -1109,7 +1190,7 @@ public class SoundTriggerMiddlewareImplTest { // Load the model. final int hwHandle = 13; - int handle = loadGenericModel(module, hwHandle); + int handle = loadGenericModel(module, hwHandle).first; // Initiate a recognition. startRecognition(module, handle, hwHandle); @@ -1145,7 +1226,7 @@ public class SoundTriggerMiddlewareImplTest { // Load the model. final int hwHandle = 13; - int handle = loadPhraseModel(module, hwHandle); + int handle = loadPhraseModel(module, hwHandle).first; // Initiate a recognition. startRecognition(module, handle, hwHandle); @@ -1182,7 +1263,7 @@ public class SoundTriggerMiddlewareImplTest { ISoundTriggerCallback callback = createCallbackMock(); ISoundTriggerModule module = mService.attach(0, callback); final int hwHandle = 12; - int modelHandle = loadGenericModel(module, hwHandle); + int modelHandle = loadGenericModel(module, hwHandle).first; doAnswer((Answer) invocation -> { android.hardware.soundtrigger.V2_3.ISoundTriggerHw.queryParameterCallback @@ -1218,7 +1299,7 @@ public class SoundTriggerMiddlewareImplTest { ISoundTriggerCallback callback = createCallbackMock(); ISoundTriggerModule module = mService.attach(0, callback); final int hwHandle = 13; - int modelHandle = loadGenericModel(module, hwHandle); + int modelHandle = loadGenericModel(module, hwHandle).first; ModelParameterRange range = module.queryModelParameterSupport(modelHandle, ModelParameter.THRESHOLD_FACTOR); @@ -1239,7 +1320,7 @@ public class SoundTriggerMiddlewareImplTest { ISoundTriggerCallback callback = createCallbackMock(); ISoundTriggerModule module = mService.attach(0, callback); final int hwHandle = 13; - int modelHandle = loadGenericModel(module, hwHandle); + int modelHandle = loadGenericModel(module, hwHandle).first; doAnswer(invocation -> { android.hardware.soundtrigger.V2_3.ISoundTriggerHw.queryParameterCallback @@ -1272,7 +1353,7 @@ public class SoundTriggerMiddlewareImplTest { ISoundTriggerCallback callback = createCallbackMock(); ISoundTriggerModule module = mService.attach(0, callback); final int hwHandle = 14; - int modelHandle = loadGenericModel(module, hwHandle); + int modelHandle = loadGenericModel(module, hwHandle).first; doAnswer(invocation -> { android.hardware.soundtrigger.V2_3.ISoundTriggerHw.getParameterCallback @@ -1304,7 +1385,7 @@ public class SoundTriggerMiddlewareImplTest { ISoundTriggerCallback callback = createCallbackMock(); ISoundTriggerModule module = mService.attach(0, callback); final int hwHandle = 17; - int modelHandle = loadGenericModel(module, hwHandle); + int modelHandle = loadGenericModel(module, hwHandle).first; when(driver.setParameter(hwHandle, android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR,