summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/java/android/hardware/soundtrigger/SoundTrigger.java275
-rw-r--r--core/java/android/hardware/soundtrigger/SoundTriggerModule.java19
-rw-r--r--core/java/android/service/voice/AlwaysOnHotwordDetector.java2
-rw-r--r--core/jni/android_hardware_SoundTrigger.cpp136
-rw-r--r--media/java/android/media/AudioFormat.java14
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java3
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerHelper.java18
-rw-r--r--tests/SoundTriggerTests/src/android/hardware/soundtrigger/SoundTriggerTest.java67
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 adee740..7a49eb5 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<ModuleProperties> 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,23 +934,127 @@ 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<SoundModelEvent> CREATOR
+ = new Parcelable.Creator<SoundModelEvent>() {
+ 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.
* @return - {@link #STATUS_OK} in case of success
@@ -891,6 +1096,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
*/
public abstract void onServiceDied();
diff --git a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
index 4a54fd8..1a8723d 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 279bf40..b9fce85 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 c9a0b1e..f0d7a35 100644
--- a/core/jni/android_hardware_SoundTrigger.cpp
+++ b/core/jni/android_hardware_SoundTrigger.cpp
@@ -29,6 +29,7 @@
#include <utils/Vector.h>
#include <binder/IMemory.h>
#include <binder/MemoryDealer.h>
+#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, "<init>",
- "(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, "<init>",
- "(IIZIII[B)V");
+ "(IIZIIIZLandroid/media/AudioFormat;[B)V");
jclass keyphraseRecognitionEventClass = env->FindClass(kKeyphraseRecognitionEventClassPathName);
gKeyphraseRecognitionEventClass = (jclass) env->NewGlobalRef(keyphraseRecognitionEventClass);
gKeyphraseRecognitionEventCstor = env->GetMethodID(keyphraseRecognitionEventClass, "<init>",
- "(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, "<init>",
- "(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, "<init>", "(III)V");
+
+ jclass soundModelEventClass = env->FindClass(kSoundModelEventClassPathName);
+ gSoundModelEventClass = (jclass) env->NewGlobalRef(soundModelEventClass);
+ gSoundModelEventCstor = env->GetMethodID(soundModelEventClass, "<init>",
+ "(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 8d99d6a..d93d81b 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 8913eb9..b4c221f 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 f3ede88..fd36bfc 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 4372ff9..65a3d8a 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();