From d3b8223377b8046280e4c09e728edc600171f941 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Wed, 30 Jul 2014 08:57:39 -0700 Subject: [PATCH] SoundTrigger API update. Add sound model update callback. Add native service state change callback. Add vendor UUID in sound model description. Add coarse confidence level in recognition event. Add capture format in recognition event. Bug: 12378680. Change-Id: Id63437819ec7b9a4a69e1ff6185b747e20cad95e --- .../hardware/soundtrigger/SoundTrigger.java | 275 ++++++++++++++++-- .../soundtrigger/SoundTriggerModule.java | 19 +- .../voice/AlwaysOnHotwordDetector.java | 2 +- core/jni/android_hardware_SoundTrigger.cpp | 136 +++++++-- media/java/android/media/AudioFormat.java | 14 + .../voiceinteraction/DatabaseHelper.java | 3 +- .../voiceinteraction/SoundTriggerHelper.java | 18 ++ .../soundtrigger/SoundTriggerTest.java | 67 +++-- 8 files changed, 462 insertions(+), 72 deletions(-) diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java index adee740982176..7a49eb5cee10b 100644 --- a/core/java/android/hardware/soundtrigger/SoundTrigger.java +++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java @@ -16,6 +16,7 @@ package android.hardware.soundtrigger; +import android.media.AudioFormat; import android.os.Handler; import android.os.Parcel; import android.os.Parcelable; @@ -86,11 +87,15 @@ public class SoundTrigger { /** Rated power consumption when detection is active with TDB silence/sound/speech ratio */ public final int powerConsumptionMw; + /** Returns the trigger (key phrase) capture in the binary data of the + * recognition callback event */ + public final boolean returnsTriggerInEvent; + ModuleProperties(int id, String implementor, String description, String uuid, int version, int maxSoundModels, int maxKeyphrases, int maxUsers, int recognitionModes, boolean supportsCaptureTransition, int maxBufferMs, boolean supportsConcurrentCapture, - int powerConsumptionMw) { + int powerConsumptionMw, boolean returnsTriggerInEvent) { this.id = id; this.implementor = implementor; this.description = description; @@ -104,6 +109,7 @@ public class SoundTrigger { this.maxBufferMs = maxBufferMs; this.supportsConcurrentCapture = supportsConcurrentCapture; this.powerConsumptionMw = powerConsumptionMw; + this.returnsTriggerInEvent = returnsTriggerInEvent; } public static final Parcelable.Creator CREATOR @@ -131,10 +137,11 @@ public class SoundTrigger { int maxBufferMs = in.readInt(); boolean supportsConcurrentCapture = in.readByte() == 1; int powerConsumptionMw = in.readInt(); + boolean returnsTriggerInEvent = in.readByte() == 1; return new ModuleProperties(id, implementor, description, uuid, version, maxSoundModels, maxKeyphrases, maxUsers, recognitionModes, supportsCaptureTransition, maxBufferMs, supportsConcurrentCapture, - powerConsumptionMw); + powerConsumptionMw, returnsTriggerInEvent); } @Override @@ -152,6 +159,7 @@ public class SoundTrigger { dest.writeInt(maxBufferMs); dest.writeByte((byte) (supportsConcurrentCapture ? 1 : 0)); dest.writeInt(powerConsumptionMw); + dest.writeByte((byte) (returnsTriggerInEvent ? 1 : 0)); } @Override @@ -167,7 +175,8 @@ public class SoundTrigger { + maxUsers + ", recognitionModes=" + recognitionModes + ", supportsCaptureTransition=" + supportsCaptureTransition + ", maxBufferMs=" + maxBufferMs + ", supportsConcurrentCapture=" + supportsConcurrentCapture - + ", powerConsumptionMw=" + powerConsumptionMw + "]"; + + ", powerConsumptionMw=" + powerConsumptionMw + + ", returnsTriggerInEvent=" + returnsTriggerInEvent + "]"; } } @@ -190,11 +199,15 @@ public class SoundTrigger { /** Sound model type (e.g. TYPE_KEYPHRASE); */ public final int type; + /** Unique sound model vendor identifier */ + public final UUID vendorUuid; + /** Opaque data. For use by vendor implementation and enrollment application */ public final byte[] data; - public SoundModel(UUID uuid, int type, byte[] data) { + public SoundModel(UUID uuid, UUID vendorUuid, int type, byte[] data) { this.uuid = uuid; + this.vendorUuid = vendorUuid; this.type = type; this.data = data; } @@ -329,8 +342,9 @@ public class SoundTrigger { /** Key phrases in this sound model */ public final Keyphrase[] keyphrases; // keyword phrases in model - public KeyphraseSoundModel(UUID id, byte[] data, Keyphrase[] keyphrases) { - super(id, TYPE_KEYPHRASE, data); + public KeyphraseSoundModel( + UUID uuid, UUID vendorUuid, byte[] data, Keyphrase[] keyphrases) { + super(uuid, vendorUuid, TYPE_KEYPHRASE, data); this.keyphrases = keyphrases; } @@ -347,9 +361,14 @@ public class SoundTrigger { private static KeyphraseSoundModel fromParcel(Parcel in) { UUID uuid = UUID.fromString(in.readString()); + UUID vendorUuid = null; + int length = in.readInt(); + if (length >= 0) { + vendorUuid = UUID.fromString(in.readString()); + } byte[] data = in.readBlob(); Keyphrase[] keyphrases = in.createTypedArray(Keyphrase.CREATOR); - return new KeyphraseSoundModel(uuid, data, keyphrases); + return new KeyphraseSoundModel(uuid, vendorUuid, data, keyphrases); } @Override @@ -360,14 +379,21 @@ public class SoundTrigger { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(uuid.toString()); + if (vendorUuid == null) { + dest.writeInt(-1); + } else { + dest.writeInt(vendorUuid.toString().length()); + dest.writeString(vendorUuid.toString()); + } dest.writeBlob(data); dest.writeTypedArray(keyphrases, flags); } @Override public String toString() { - return "KeyphraseSoundModel [keyphrases=" + Arrays.toString(keyphrases) + ", uuid=" - + uuid + ", type=" + type + ", data=" + (data == null ? 0 : data.length) + "]"; + return "KeyphraseSoundModel [keyphrases=" + Arrays.toString(keyphrases) + + ", uuid=" + uuid + ", vendorUuid=" + vendorUuid + + ", type=" + type + ", data=" + (data == null ? 0 : data.length) + "]"; } } @@ -411,18 +437,26 @@ public class SoundTrigger { public final int captureDelayMs; /** Duration in ms of audio captured before the start of the trigger. 0 if none. */ public final int capturePreambleMs; + /** True if the trigger (key phrase capture is present in binary data */ + public final boolean triggerInData; + /** Audio format of either the trigger in event data or to use for capture of the + * rest of the utterance */ + public AudioFormat captureFormat; /** Opaque data for use by system applications who know about voice engine internals, * typically during enrollment. */ public final byte[] data; public RecognitionEvent(int status, int soundModelHandle, boolean captureAvailable, - int captureSession, int captureDelayMs, int capturePreambleMs, byte[] data) { + int captureSession, int captureDelayMs, int capturePreambleMs, + boolean triggerInData, AudioFormat captureFormat, byte[] data) { this.status = status; this.soundModelHandle = soundModelHandle; this.captureAvailable = captureAvailable; this.captureSession = captureSession; this.captureDelayMs = captureDelayMs; this.capturePreambleMs = capturePreambleMs; + this.triggerInData = triggerInData; + this.captureFormat = captureFormat; this.data = data; } @@ -444,9 +478,21 @@ public class SoundTrigger { int captureSession = in.readInt(); int captureDelayMs = in.readInt(); int capturePreambleMs = in.readInt(); + boolean triggerInData = in.readByte() == 1; + AudioFormat captureFormat = null; + if (triggerInData) { + int sampleRate = in.readInt(); + int encoding = in.readInt(); + int channelMask = in.readInt(); + captureFormat = (new AudioFormat.Builder()) + .setChannelMask(channelMask) + .setEncoding(encoding) + .setSampleRate(sampleRate) + .build(); + } byte[] data = in.readBlob(); return new RecognitionEvent(status, soundModelHandle, captureAvailable, captureSession, - captureDelayMs, capturePreambleMs, data); + captureDelayMs, capturePreambleMs, triggerInData, captureFormat, data); } @Override @@ -462,6 +508,14 @@ public class SoundTrigger { dest.writeInt(captureSession); dest.writeInt(captureDelayMs); dest.writeInt(capturePreambleMs); + if (triggerInData && (captureFormat != null)) { + dest.writeByte((byte)1); + dest.writeInt(captureFormat.getSampleRate()); + dest.writeInt(captureFormat.getEncoding()); + dest.writeInt(captureFormat.getChannelMask()); + } else { + dest.writeByte((byte)0); + } dest.writeBlob(data); } @@ -473,6 +527,12 @@ public class SoundTrigger { result = prime * result + captureDelayMs; result = prime * result + capturePreambleMs; result = prime * result + captureSession; + result = prime * result + (triggerInData ? 1231 : 1237); + if (captureFormat != null) { + result = prime * result + captureFormat.getSampleRate(); + result = prime * result + captureFormat.getEncoding(); + result = prime * result + captureFormat.getChannelMask(); + } result = prime * result + Arrays.hashCode(data); result = prime * result + soundModelHandle; result = prime * result + status; @@ -502,6 +562,14 @@ public class SoundTrigger { return false; if (status != other.status) return false; + if (triggerInData != other.triggerInData) + return false; + if (captureFormat.getSampleRate() != other.captureFormat.getSampleRate()) + return false; + if (captureFormat.getEncoding() != other.captureFormat.getEncoding()) + return false; + if (captureFormat.getChannelMask() != other.captureFormat.getChannelMask()) + return false; return true; } @@ -511,6 +579,13 @@ public class SoundTrigger { + ", captureAvailable=" + captureAvailable + ", captureSession=" + captureSession + ", captureDelayMs=" + captureDelayMs + ", capturePreambleMs=" + capturePreambleMs + + ", triggerInData=" + triggerInData + + ((captureFormat == null) ? "" : + (", sampleRate=" + captureFormat.getSampleRate())) + + ((captureFormat == null) ? "" : + (", encoding=" + captureFormat.getEncoding())) + + ((captureFormat == null) ? "" : + (", channelMask=" + captureFormat.getChannelMask())) + ", data=" + (data == null ? 0 : data.length) + "]"; } } @@ -673,14 +748,19 @@ public class SoundTrigger { /** Recognition modes matched for this event */ public final int recognitionModes; + /** Confidence level for mode RECOGNITION_MODE_VOICE_TRIGGER when user identification + * is not performed */ + public final int coarseConfidenceLevel; + /** Confidence levels for all users recognized (KeyphraseRecognitionEvent) or to * be recognized (RecognitionConfig) */ public final ConfidenceLevel[] confidenceLevels; - public KeyphraseRecognitionExtra(int id, int recognitionModes, - ConfidenceLevel[] confidenceLevels) { + public KeyphraseRecognitionExtra(int id, int recognitionModes, int coarseConfidenceLevel, + ConfidenceLevel[] confidenceLevels) { this.id = id; this.recognitionModes = recognitionModes; + this.coarseConfidenceLevel = coarseConfidenceLevel; this.confidenceLevels = confidenceLevels; } @@ -698,14 +778,17 @@ public class SoundTrigger { private static KeyphraseRecognitionExtra fromParcel(Parcel in) { int id = in.readInt(); int recognitionModes = in.readInt(); + int coarseConfidenceLevel = in.readInt(); ConfidenceLevel[] confidenceLevels = in.createTypedArray(ConfidenceLevel.CREATOR); - return new KeyphraseRecognitionExtra(id, recognitionModes, confidenceLevels); + return new KeyphraseRecognitionExtra(id, recognitionModes, coarseConfidenceLevel, + confidenceLevels); } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(id); dest.writeInt(recognitionModes); + dest.writeInt(coarseConfidenceLevel); dest.writeTypedArray(confidenceLevels, flags); } @@ -721,6 +804,7 @@ public class SoundTrigger { result = prime * result + Arrays.hashCode(confidenceLevels); result = prime * result + id; result = prime * result + recognitionModes; + result = prime * result + coarseConfidenceLevel; return result; } @@ -739,12 +823,15 @@ public class SoundTrigger { return false; if (recognitionModes != other.recognitionModes) return false; + if (coarseConfidenceLevel != other.coarseConfidenceLevel) + return false; return true; } @Override public String toString() { return "KeyphraseRecognitionExtra [id=" + id + ", recognitionModes=" + recognitionModes + + ", coarseConfidenceLevel=" + coarseConfidenceLevel + ", confidenceLevels=" + Arrays.toString(confidenceLevels) + "]"; } } @@ -756,15 +843,12 @@ public class SoundTrigger { /** Indicates if the key phrase is present in the buffered audio available for capture */ public final KeyphraseRecognitionExtra[] keyphraseExtras; - /** Additional data available for each recognized key phrases in the model */ - public final boolean keyphraseInCapture; - public KeyphraseRecognitionEvent(int status, int soundModelHandle, boolean captureAvailable, - int captureSession, int captureDelayMs, int capturePreambleMs, byte[] data, - boolean keyphraseInCapture, KeyphraseRecognitionExtra[] keyphraseExtras) { + int captureSession, int captureDelayMs, int capturePreambleMs, + boolean triggerInData, AudioFormat captureFormat, byte[] data, + KeyphraseRecognitionExtra[] keyphraseExtras) { super(status, soundModelHandle, captureAvailable, captureSession, captureDelayMs, - capturePreambleMs, data); - this.keyphraseInCapture = keyphraseInCapture; + capturePreambleMs, triggerInData, captureFormat, data); this.keyphraseExtras = keyphraseExtras; } @@ -786,13 +870,24 @@ public class SoundTrigger { int captureSession = in.readInt(); int captureDelayMs = in.readInt(); int capturePreambleMs = in.readInt(); + boolean triggerInData = in.readByte() == 1; + AudioFormat captureFormat = null; + if (triggerInData) { + int sampleRate = in.readInt(); + int encoding = in.readInt(); + int channelMask = in.readInt(); + captureFormat = (new AudioFormat.Builder()) + .setChannelMask(channelMask) + .setEncoding(encoding) + .setSampleRate(sampleRate) + .build(); + } byte[] data = in.readBlob(); - boolean keyphraseInCapture = in.readByte() == 1; KeyphraseRecognitionExtra[] keyphraseExtras = in.createTypedArray(KeyphraseRecognitionExtra.CREATOR); return new KeyphraseRecognitionEvent(status, soundModelHandle, captureAvailable, - captureSession, captureDelayMs, capturePreambleMs, data, keyphraseInCapture, - keyphraseExtras); + captureSession, captureDelayMs, capturePreambleMs, triggerInData, + captureFormat, data, keyphraseExtras); } @Override @@ -803,8 +898,15 @@ public class SoundTrigger { dest.writeInt(captureSession); dest.writeInt(captureDelayMs); dest.writeInt(capturePreambleMs); + if (triggerInData && (captureFormat != null)) { + dest.writeByte((byte)1); + dest.writeInt(captureFormat.getSampleRate()); + dest.writeInt(captureFormat.getEncoding()); + dest.writeInt(captureFormat.getChannelMask()); + } else { + dest.writeByte((byte)0); + } dest.writeBlob(data); - dest.writeByte((byte) (keyphraseInCapture ? 1 : 0)); dest.writeTypedArray(keyphraseExtras, flags); } @@ -818,7 +920,6 @@ public class SoundTrigger { final int prime = 31; int result = super.hashCode(); result = prime * result + Arrays.hashCode(keyphraseExtras); - result = prime * result + (keyphraseInCapture ? 1231 : 1237); return result; } @@ -833,22 +934,126 @@ public class SoundTrigger { KeyphraseRecognitionEvent other = (KeyphraseRecognitionEvent) obj; if (!Arrays.equals(keyphraseExtras, other.keyphraseExtras)) return false; - if (keyphraseInCapture != other.keyphraseInCapture) - return false; return true; } @Override public String toString() { return "KeyphraseRecognitionEvent [keyphraseExtras=" + Arrays.toString(keyphraseExtras) - + ", keyphraseInCapture=" + keyphraseInCapture + ", status=" + status + + ", status=" + status + ", soundModelHandle=" + soundModelHandle + ", captureAvailable=" + captureAvailable + ", captureSession=" + captureSession + ", captureDelayMs=" + captureDelayMs + ", capturePreambleMs=" + capturePreambleMs + + ", triggerInData=" + triggerInData + + ((captureFormat == null) ? "" : + (", sampleRate=" + captureFormat.getSampleRate())) + + ((captureFormat == null) ? "" : + (", encoding=" + captureFormat.getEncoding())) + + ((captureFormat == null) ? "" : + (", channelMask=" + captureFormat.getChannelMask())) + ", data=" + (data == null ? 0 : data.length) + "]"; } } + /** + * Status codes for {@link SoundModelEvent} + */ + /** Sound Model was updated */ + public static final int SOUNDMODEL_STATUS_UPDATED = 0; + + /** + * A SoundModelEvent is provided by the + * {@link StatusListener#onSoundModelUpdate(SoundModelEvent)} + * callback when a sound model has been updated by the implementation + */ + public static class SoundModelEvent implements Parcelable { + /** Status e.g {@link #SOUNDMODEL_STATUS_UPDATED} */ + public final int status; + /** The updated sound model handle */ + public final int soundModelHandle; + /** New sound model data */ + public final byte[] data; + + SoundModelEvent(int status, int soundModelHandle, byte[] data) { + this.status = status; + this.soundModelHandle = soundModelHandle; + this.data = data; + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + public SoundModelEvent createFromParcel(Parcel in) { + return SoundModelEvent.fromParcel(in); + } + + public SoundModelEvent[] newArray(int size) { + return new SoundModelEvent[size]; + } + }; + + private static SoundModelEvent fromParcel(Parcel in) { + int status = in.readInt(); + int soundModelHandle = in.readInt(); + byte[] data = in.readBlob(); + return new SoundModelEvent(status, soundModelHandle, data); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(status); + dest.writeInt(soundModelHandle); + dest.writeBlob(data); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + Arrays.hashCode(data); + result = prime * result + soundModelHandle; + result = prime * result + status; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + SoundModelEvent other = (SoundModelEvent) obj; + if (!Arrays.equals(data, other.data)) + return false; + if (soundModelHandle != other.soundModelHandle) + return false; + if (status != other.status) + return false; + return true; + } + + @Override + public String toString() { + return "SoundModelEvent [status=" + status + ", soundModelHandle=" + soundModelHandle + + ", data=" + (data == null ? 0 : data.length) + "]"; + } + } + + /** + * Native service state. {@link StatusListener#onServiceStateChange(int)} + */ + // Keep in sync with system/core/include/system/sound_trigger.h + /** Sound trigger service is enabled */ + public static final int SERVICE_STATE_ENABLED = 0; + /** Sound trigger service is disabled */ + public static final int SERVICE_STATE_DISABLED = 1; + /** * Returns a list of descriptors for all harware modules loaded. * @param modules A ModuleProperties array where the list will be returned. @@ -890,6 +1095,18 @@ public class SoundTrigger { */ public abstract void onRecognition(RecognitionEvent event); + /** + * Called when a sound model has been updated + */ + public abstract void onSoundModelUpdate(SoundModelEvent event); + + /** + * Called when the sound trigger native service state changes. + * @param state Native service state. One of {@link SoundTrigger#SERVICE_STATE_ENABLED}, + * {@link SoundTrigger#SERVICE_STATE_DISABLED} + */ + public abstract void onServiceStateChange(int state); + /** * Called when the sound trigger native service dies */ diff --git a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java index 4a54fd865d601..1a8723d9e4fab 100644 --- a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java +++ b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java @@ -36,8 +36,11 @@ public class SoundTriggerModule { 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; SoundTriggerModule(int moduleId, SoundTrigger.StatusListener listener, Handler handler) { mId = moduleId; @@ -133,10 +136,7 @@ public class SoundTriggerModule { if (handler != null) { looper = handler.getLooper(); } else { - looper = Looper.myLooper(); - if (looper == null) { - looper = Looper.getMainLooper(); - } + looper = Looper.getMainLooper(); } // construct the event handler with this looper @@ -152,6 +152,17 @@ public class SoundTriggerModule { (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(); diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java index 279bf401d8fe2..b9fce8515692f 100644 --- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java +++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java @@ -426,7 +426,7 @@ public class AlwaysOnHotwordDetector { KeyphraseRecognitionExtra[] recognitionExtra = new KeyphraseRecognitionExtra[1]; // TODO: Do we need to do something about the confidence level here? recognitionExtra[0] = new KeyphraseRecognitionExtra(mKeyphraseMetadata.id, - mKeyphraseMetadata.recognitionModeFlags, new ConfidenceLevel[0]); + mKeyphraseMetadata.recognitionModeFlags, 0, new ConfidenceLevel[0]); boolean captureTriggerAudio = (recognitionFlags&RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO) != 0; boolean allowMultipleTriggers = diff --git a/core/jni/android_hardware_SoundTrigger.cpp b/core/jni/android_hardware_SoundTrigger.cpp index c9a0b1ed3f061..f0d7a35c95142 100644 --- a/core/jni/android_hardware_SoundTrigger.cpp +++ b/core/jni/android_hardware_SoundTrigger.cpp @@ -29,6 +29,7 @@ #include #include #include +#include "android_media_AudioFormat.h" using namespace android; @@ -63,6 +64,7 @@ static const char* const kSoundModelClassPathName = static jclass gSoundModelClass; static struct { jfieldID uuid; + jfieldID vendorUuid; jfieldID data; } gSoundModelFields; @@ -110,6 +112,7 @@ static jmethodID gKeyphraseRecognitionExtraCstor; static struct { jfieldID id; jfieldID recognitionModes; + jfieldID coarseConfidenceLevel; jfieldID confidenceLevels; } gKeyphraseRecognitionExtraFields; @@ -122,6 +125,16 @@ static struct { 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 { @@ -137,6 +150,8 @@ enum { enum { SOUNDTRIGGER_EVENT_RECOGNITION = 1, SOUNDTRIGGER_EVENT_SERVICE_DIED = 2, + SOUNDTRIGGER_EVENT_SOUNDMODEL = 3, + SOUNDTRIGGER_EVENT_SERVICE_STATE_CHANGE = 4, }; // ---------------------------------------------------------------------------- @@ -148,6 +163,8 @@ public: ~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: @@ -183,10 +200,9 @@ JNISoundTriggerCallback::~JNISoundTriggerCallback() void JNISoundTriggerCallback::onRecognitionEvent(struct sound_trigger_recognition_event *event) { JNIEnv *env = AndroidRuntime::getJNIEnv(); - - jobject jEvent; - + jobject jEvent = NULL; jbyteArray jData = NULL; + if (event->data_size) { jData = env->NewByteArray(event->data_size); jbyte *nData = env->GetByteArrayElements(jData, NULL); @@ -194,6 +210,15 @@ void JNISoundTriggerCallback::onRecognitionEvent(struct sound_trigger_recognitio env->ReleaseByteArrayElements(jData, nData, 0); } + jobject jAudioFormat = NULL; + if (event->trigger_in_data) { + jAudioFormat = env->NewObject(gAudioFormatClass, + gAudioFormatCstor, + audioFormatFromNative(event->audio_config.format), + event->audio_config.sample_rate, + inChannelMaskFromNative(event->audio_config.channel_mask)); + + } if (event->type == SOUND_MODEL_TYPE_KEYPHRASE) { struct sound_trigger_phrase_recognition_event *phraseEvent = (struct sound_trigger_phrase_recognition_event *)event; @@ -225,6 +250,7 @@ void JNISoundTriggerCallback::onRecognitionEvent(struct sound_trigger_recognitio gKeyphraseRecognitionExtraCstor, phraseEvent->phrase_extras[i].id, phraseEvent->phrase_extras[i].recognition_modes, + phraseEvent->phrase_extras[i].confidence_level, jConfidenceLevels); if (jNewExtra == NULL) { @@ -236,19 +262,63 @@ void JNISoundTriggerCallback::onRecognitionEvent(struct sound_trigger_recognitio } jEvent = env->NewObject(gKeyphraseRecognitionEventClass, gKeyphraseRecognitionEventCstor, event->status, event->model, event->capture_available, - event->capture_session, event->capture_delay_ms, - event->capture_preamble_ms, jData, - phraseEvent->key_phrase_in_capture, jExtras); + event->capture_session, event->capture_delay_ms, + event->capture_preamble_ms, event->trigger_in_data, + jAudioFormat, jData, jExtras); + env->DeleteLocalRef(jAudioFormat); + env->DeleteLocalRef(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, jData); + event->capture_preamble_ms, event->trigger_in_data, + jAudioFormat, jData); + env->DeleteLocalRef(jAudioFormat); + 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(); @@ -336,7 +406,7 @@ android_hardware_SoundTrigger_listModules(JNIEnv *env, jobject clazz, SOUND_TRIGGER_MAX_STRING_LEN); jstring uuid = env->NewStringUTF(str); - ALOGV("listModules module %d id %d description %s maxSoundModels %d", + ALOGV("listModules module %zu id %d description %s maxSoundModels %d", i, nModules[i].handle, nModules[i].properties.description, nModules[i].properties.max_sound_models); @@ -351,7 +421,8 @@ android_hardware_SoundTrigger_listModules(JNIEnv *env, jobject clazz, 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.power_consumption_mw, + nModules[i].properties.trigger_in_event); env->DeleteLocalRef(implementor); env->DeleteLocalRef(description); @@ -463,6 +534,18 @@ android_hardware_SoundTrigger_loadSoundModel(JNIEnv *env, jobject thiz, 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; @@ -491,6 +574,7 @@ android_hardware_SoundTrigger_loadSoundModel(JNIEnv *env, jobject thiz, nSoundModel->type = type; nSoundModel->uuid = nUuid; + nSoundModel->vendor_uuid = nVendorUuid; nSoundModel->data_size = size; nSoundModel->data_offset = offset; memcpy((char *)nSoundModel + offset, nData, size); @@ -507,7 +591,7 @@ android_hardware_SoundTrigger_loadSoundModel(JNIEnv *env, jobject thiz, size_t numPhrases = env->GetArrayLength(jPhrases); phraseModel->num_phrases = numPhrases; - ALOGV("loadSoundModel numPhrases %d", numPhrases); + ALOGV("loadSoundModel numPhrases %zu", numPhrases); for (size_t i = 0; i < numPhrases; i++) { jobject jPhrase = env->GetObjectArrayElement(jPhrases, i); phraseModel->phrases[i].id = @@ -539,7 +623,7 @@ android_hardware_SoundTrigger_loadSoundModel(JNIEnv *env, jobject thiz, env->DeleteLocalRef(jLocale); env->ReleaseStringUTFChars(jText, nText); env->DeleteLocalRef(jText); - ALOGV("loadSoundModel phrases %d text %s locale %s", + ALOGV("loadSoundModel phrases %zu text %s locale %s", i, phraseModel->phrases[i].text, phraseModel->phrases[i].locale); env->DeleteLocalRef(jPhrase); } @@ -640,13 +724,15 @@ android_hardware_SoundTrigger_startRecognition(JNIEnv *env, jobject thiz, 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 %d num_levels %d", i, config->phrases[i].num_levels); + 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, @@ -655,7 +741,7 @@ android_hardware_SoundTrigger_startRecognition(JNIEnv *env, jobject thiz, gConfidenceLevelFields.confidenceLevel); env->DeleteLocalRef(jConfidenceLevel); } - ALOGV("startRecognition phrases %d", i); + ALOGV("startRecognition phrases %zu", i); env->DeleteLocalRef(jConfidenceLevels); env->DeleteLocalRef(jPhrase); } @@ -734,11 +820,12 @@ int register_android_hardware_SoundTrigger(JNIEnv *env) jclass modulePropertiesClass = env->FindClass(kModulePropertiesClassPathName); gModulePropertiesClass = (jclass) env->NewGlobalRef(modulePropertiesClass); gModulePropertiesCstor = env->GetMethodID(modulePropertiesClass, "", - "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;IIIIIZIZI)V"); + "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;IIIIIZIZIZ)V"); jclass soundModelClass = env->FindClass(kSoundModelClassPathName); gSoundModelClass = (jclass) env->NewGlobalRef(soundModelClass); gSoundModelFields.uuid = env->GetFieldID(soundModelClass, "uuid", "Ljava/util/UUID;"); + gSoundModelFields.vendorUuid = env->GetFieldID(soundModelClass, "vendorUuid", "Ljava/util/UUID;"); gSoundModelFields.data = env->GetFieldID(soundModelClass, "data", "[B"); jclass keyphraseClass = env->FindClass(kKeyphraseClassPathName); @@ -759,12 +846,12 @@ int register_android_hardware_SoundTrigger(JNIEnv *env) jclass recognitionEventClass = env->FindClass(kRecognitionEventClassPathName); gRecognitionEventClass = (jclass) env->NewGlobalRef(recognitionEventClass); gRecognitionEventCstor = env->GetMethodID(recognitionEventClass, "", - "(IIZIII[B)V"); + "(IIZIIIZLandroid/media/AudioFormat;[B)V"); jclass keyphraseRecognitionEventClass = env->FindClass(kKeyphraseRecognitionEventClassPathName); gKeyphraseRecognitionEventClass = (jclass) env->NewGlobalRef(keyphraseRecognitionEventClass); gKeyphraseRecognitionEventCstor = env->GetMethodID(keyphraseRecognitionEventClass, "", - "(IIZIII[BZ[Landroid/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionExtra;)V"); + "(IIZIIIZLandroid/media/AudioFormat;[B[Landroid/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionExtra;)V"); jclass keyRecognitionConfigClass = env->FindClass(kRecognitionConfigClassPathName); @@ -782,9 +869,12 @@ int register_android_hardware_SoundTrigger(JNIEnv *env) jclass keyphraseRecognitionExtraClass = env->FindClass(kKeyphraseRecognitionExtraClassPathName); gKeyphraseRecognitionExtraClass = (jclass) env->NewGlobalRef(keyphraseRecognitionExtraClass); gKeyphraseRecognitionExtraCstor = env->GetMethodID(keyphraseRecognitionExtraClass, "", - "(II[Landroid/hardware/soundtrigger/SoundTrigger$ConfidenceLevel;)V"); + "(III[Landroid/hardware/soundtrigger/SoundTrigger$ConfidenceLevel;)V"); gKeyphraseRecognitionExtraFields.id = env->GetFieldID(gKeyphraseRecognitionExtraClass, "id", "I"); - gKeyphraseRecognitionExtraFields.recognitionModes = env->GetFieldID(gKeyphraseRecognitionExtraClass, "recognitionModes", "I"); + gKeyphraseRecognitionExtraFields.recognitionModes = env->GetFieldID(gKeyphraseRecognitionExtraClass, + "recognitionModes", "I"); + gKeyphraseRecognitionExtraFields.coarseConfidenceLevel = env->GetFieldID(gKeyphraseRecognitionExtraClass, + "coarseConfidenceLevel", "I"); gKeyphraseRecognitionExtraFields.confidenceLevels = env->GetFieldID(gKeyphraseRecognitionExtraClass, "confidenceLevels", "[Landroid/hardware/soundtrigger/SoundTrigger$ConfidenceLevel;"); @@ -796,6 +886,16 @@ int register_android_hardware_SoundTrigger(JNIEnv *env) gConfidenceLevelFields.confidenceLevel = env->GetFieldID(confidenceLevelClass, "confidenceLevel", "I"); + jclass audioFormatClass = env->FindClass(kAudioFormatClassPathName); + gAudioFormatClass = (jclass) env->NewGlobalRef(audioFormatClass); + gAudioFormatCstor = env->GetMethodID(audioFormatClass, "", "(III)V"); + + jclass soundModelEventClass = env->FindClass(kSoundModelEventClassPathName); + gSoundModelEventClass = (jclass) env->NewGlobalRef(soundModelEventClass); + gSoundModelEventCstor = env->GetMethodID(soundModelEventClass, "", + "(II[B)V"); + + int status = AndroidRuntime::registerNativeMethods(env, kSoundTriggerClassPathName, gMethods, NELEM(gMethods)); diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java index 8d99d6a1c1faf..d93d81bfee71d 100644 --- a/media/java/android/media/AudioFormat.java +++ b/media/java/android/media/AudioFormat.java @@ -249,6 +249,20 @@ public class AudioFormat { private AudioFormat(int ignoredArgument) { } + /** + * Constructor used by the JNI + */ + // Update sound trigger JNI in core/jni/android_hardware_SoundTrigger.cpp when modifying this + // constructor + private AudioFormat(int encoding, int sampleRate, int channelMask) { + mEncoding = encoding; + mSampleRate = sampleRate; + mChannelMask = channelMask; + mPropertySetMask = AUDIO_FORMAT_HAS_PROPERTY_ENCODING | + AUDIO_FORMAT_HAS_PROPERTY_SAMPLE_RATE | + AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_MASK; + } + /** @hide */ public final static int AUDIO_FORMAT_HAS_PROPERTY_NONE = 0x0; /** @hide */ diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java index 8913eb958fa7b..b4c221fb39330 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java @@ -195,7 +195,8 @@ public class DatabaseHelper extends SQLiteOpenHelper { Keyphrase[] keyphrases = new Keyphrase[1]; keyphrases[0] = new Keyphrase( keyphraseId, recognitionModes, locale, text, users); - return new KeyphraseSoundModel(UUID.fromString(modelUuid), data, keyphrases); + return new KeyphraseSoundModel(UUID.fromString(modelUuid), + null /* FIXME use vendor UUID */, data, keyphrases); } Slog.w(TAG, "No SoundModel available for the given keyphrase"); } finally { diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerHelper.java index f3ede880faf48..fd36bfc432cad 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerHelper.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerHelper.java @@ -25,6 +25,7 @@ import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel; import android.hardware.soundtrigger.SoundTrigger.ModuleProperties; import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig; import android.hardware.soundtrigger.SoundTrigger.RecognitionEvent; +import android.hardware.soundtrigger.SoundTrigger.SoundModelEvent; import android.hardware.soundtrigger.SoundTriggerModule; import android.os.RemoteException; import android.util.Slog; @@ -330,6 +331,23 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { } } + public void onSoundModelUpdate(SoundModelEvent event) { + if (event == null) { + Slog.w(TAG, "Invalid sound model event!"); + return; + } + + if (DBG) Slog.d(TAG, "onSoundModelUpdate: " + event); + + //TODO: implement sound model update + } + + public void onServiceStateChange(int state) { + if (DBG) Slog.d(TAG, "onServiceStateChange, state: " + state); + + //TODO: implement service state update + } + @Override public void onServiceDied() { synchronized (this) { diff --git a/tests/SoundTriggerTests/src/android/hardware/soundtrigger/SoundTriggerTest.java b/tests/SoundTriggerTests/src/android/hardware/soundtrigger/SoundTriggerTest.java index 4372ff98824ca..65a3d8a337db0 100644 --- a/tests/SoundTriggerTests/src/android/hardware/soundtrigger/SoundTriggerTest.java +++ b/tests/SoundTriggerTests/src/android/hardware/soundtrigger/SoundTriggerTest.java @@ -23,6 +23,7 @@ import android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionEvent; import android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra; import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel; import android.hardware.soundtrigger.SoundTrigger.RecognitionEvent; +import android.media.AudioFormat; import android.os.Parcel; import android.test.InstrumentationTestCase; import android.test.suitebuilder.annotation.LargeTest; @@ -97,7 +98,8 @@ public class SoundTriggerTest extends InstrumentationTestCase { Keyphrase[] keyphrases = new Keyphrase[2]; keyphrases[0] = new Keyphrase(1, 0, "en-US", "hello", new int[] {0}); keyphrases[1] = new Keyphrase(2, 0, "fr-FR", "there", new int[] {1, 2}); - KeyphraseSoundModel ksm = new KeyphraseSoundModel(UUID.randomUUID(), null, keyphrases); + KeyphraseSoundModel ksm = new KeyphraseSoundModel(UUID.randomUUID(), UUID.randomUUID(), + null, keyphrases); // Write to a parcel Parcel parcel = Parcel.obtain(); @@ -119,8 +121,8 @@ public class SoundTriggerTest extends InstrumentationTestCase { Keyphrase[] keyphrases = new Keyphrase[2]; keyphrases[0] = new Keyphrase(1, 0, "en-US", "hello", new int[] {0}); keyphrases[1] = new Keyphrase(2, 0, "fr-FR", "there", new int[] {1, 2}); - KeyphraseSoundModel ksm = new KeyphraseSoundModel(UUID.randomUUID(), new byte[0], - keyphrases); + KeyphraseSoundModel ksm = new KeyphraseSoundModel(UUID.randomUUID(), UUID.randomUUID(), + new byte[0], keyphrases); // Write to a parcel Parcel parcel = Parcel.obtain(); @@ -141,7 +143,8 @@ public class SoundTriggerTest extends InstrumentationTestCase { public void testKeyphraseSoundModelParcelUnparcel_noKeyphrases() throws Exception { byte[] data = new byte[10]; mRandom.nextBytes(data); - KeyphraseSoundModel ksm = new KeyphraseSoundModel(UUID.randomUUID(), data, null); + KeyphraseSoundModel ksm = new KeyphraseSoundModel(UUID.randomUUID(), UUID.randomUUID(), + data, null); // Write to a parcel Parcel parcel = Parcel.obtain(); @@ -162,8 +165,8 @@ public class SoundTriggerTest extends InstrumentationTestCase { public void testKeyphraseSoundModelParcelUnparcel_zeroKeyphrases() throws Exception { byte[] data = new byte[10]; mRandom.nextBytes(data); - KeyphraseSoundModel ksm = new KeyphraseSoundModel(UUID.randomUUID(), data, - new Keyphrase[0]); + KeyphraseSoundModel ksm = new KeyphraseSoundModel(UUID.randomUUID(), UUID.randomUUID(), + data, new Keyphrase[0]); // Write to a parcel Parcel parcel = Parcel.obtain(); @@ -187,7 +190,8 @@ public class SoundTriggerTest extends InstrumentationTestCase { keyphrases[1] = new Keyphrase(2, 0, "fr-FR", "there", new int[] {1, 2}); byte[] data = new byte[200 * 1024]; mRandom.nextBytes(data); - KeyphraseSoundModel ksm = new KeyphraseSoundModel(UUID.randomUUID(), data, keyphrases); + KeyphraseSoundModel ksm = new KeyphraseSoundModel(UUID.randomUUID(), UUID.randomUUID(), + data, keyphrases); // Write to a parcel Parcel parcel = Parcel.obtain(); @@ -207,7 +211,7 @@ public class SoundTriggerTest extends InstrumentationTestCase { @SmallTest public void testRecognitionEventParcelUnparcel_noData() throws Exception { RecognitionEvent re = new RecognitionEvent(SoundTrigger.RECOGNITION_STATUS_SUCCESS, 1, - true, 2, 3, 4, null); + true, 2, 3, 4, false, null, null); // Write to a parcel Parcel parcel = Parcel.obtain(); @@ -224,7 +228,7 @@ public class SoundTriggerTest extends InstrumentationTestCase { @SmallTest public void testRecognitionEventParcelUnparcel_zeroData() throws Exception { RecognitionEvent re = new RecognitionEvent(SoundTrigger.RECOGNITION_STATUS_FAILURE, 1, - true, 2, 3, 4, new byte[1]); + true, 2, 3, 4, false, null, new byte[1]); // Write to a parcel Parcel parcel = Parcel.obtain(); @@ -243,7 +247,32 @@ public class SoundTriggerTest extends InstrumentationTestCase { byte[] data = new byte[200 * 1024]; mRandom.nextBytes(data); RecognitionEvent re = new RecognitionEvent(SoundTrigger.RECOGNITION_STATUS_ABORT, 1, - false, 2, 3, 4, data); + false, 2, 3, 4, false, null, data); + + // Write to a parcel + Parcel parcel = Parcel.obtain(); + re.writeToParcel(parcel, 0); + + // Read from it + parcel.setDataPosition(0); + RecognitionEvent unparceled = RecognitionEvent.CREATOR.createFromParcel(parcel); + + // Verify that they are the same + assertEquals(re, unparceled); + } + + @SmallTest + public void testRecognitionEventParcelUnparcel_largeAudioData() throws Exception { + byte[] data = new byte[200 * 1024]; + mRandom.nextBytes(data); + RecognitionEvent re = new RecognitionEvent(SoundTrigger.RECOGNITION_STATUS_ABORT, 1, + false, 2, 3, 4, true, + (new AudioFormat.Builder()) + .setChannelMask(AudioFormat.CHANNEL_IN_MONO) + .setEncoding(AudioFormat.ENCODING_PCM_16BIT) + .setSampleRate(16000) + .build(), + data); // Write to a parcel Parcel parcel = Parcel.obtain(); @@ -260,7 +289,7 @@ public class SoundTriggerTest extends InstrumentationTestCase { @SmallTest public void testKeyphraseRecognitionEventParcelUnparcel_noKeyphrases() throws Exception { KeyphraseRecognitionEvent re = new KeyphraseRecognitionEvent( - SoundTrigger.RECOGNITION_STATUS_SUCCESS, 1, true, 2, 3, 4, null, false, null); + SoundTrigger.RECOGNITION_STATUS_SUCCESS, 1, true, 2, 3, 4, false, null, null, null); // Write to a parcel Parcel parcel = Parcel.obtain(); @@ -279,8 +308,8 @@ public class SoundTriggerTest extends InstrumentationTestCase { public void testKeyphraseRecognitionEventParcelUnparcel_zeroData() throws Exception { KeyphraseRecognitionExtra[] kpExtra = new KeyphraseRecognitionExtra[0]; KeyphraseRecognitionEvent re = new KeyphraseRecognitionEvent( - SoundTrigger.RECOGNITION_STATUS_FAILURE, 2, true, 2, 3, 4, new byte[1], - true, kpExtra); + SoundTrigger.RECOGNITION_STATUS_FAILURE, 2, true, 2, 3, 4, false, null, new byte[1], + kpExtra); // Write to a parcel Parcel parcel = Parcel.obtain(); @@ -303,20 +332,20 @@ public class SoundTriggerTest extends InstrumentationTestCase { ConfidenceLevel cl1 = new ConfidenceLevel(1, 90); ConfidenceLevel cl2 = new ConfidenceLevel(2, 30); kpExtra[0] = new KeyphraseRecognitionExtra(1, - SoundTrigger.RECOGNITION_MODE_USER_IDENTIFICATION, + SoundTrigger.RECOGNITION_MODE_USER_IDENTIFICATION, 0, new ConfidenceLevel[] {cl1, cl2}); kpExtra[1] = new KeyphraseRecognitionExtra(1, - SoundTrigger.RECOGNITION_MODE_VOICE_TRIGGER, + SoundTrigger.RECOGNITION_MODE_VOICE_TRIGGER, 0, new ConfidenceLevel[] {cl2}); kpExtra[2] = new KeyphraseRecognitionExtra(1, - SoundTrigger.RECOGNITION_MODE_VOICE_TRIGGER, null); + SoundTrigger.RECOGNITION_MODE_VOICE_TRIGGER, 0, null); kpExtra[3] = new KeyphraseRecognitionExtra(1, - SoundTrigger.RECOGNITION_MODE_VOICE_TRIGGER, + SoundTrigger.RECOGNITION_MODE_VOICE_TRIGGER, 0, new ConfidenceLevel[0]); KeyphraseRecognitionEvent re = new KeyphraseRecognitionEvent( - SoundTrigger.RECOGNITION_STATUS_FAILURE, 1, true, 2, 3, 4, data, - false, kpExtra); + SoundTrigger.RECOGNITION_STATUS_FAILURE, 1, true, 2, 3, 4, false, null, data, + kpExtra); // Write to a parcel Parcel parcel = Parcel.obtain();