summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSandeep Siddhartha <sansid@google.com>2014-07-17 16:21:54 -0700
committerSandeep Siddhartha <sansid@google.com>2014-07-20 11:22:55 -0700
commit055897208d659e9734a82def88be4a806ff55448 (patch)
tree4540186364f0a3fc3a3675119846448215696f68
parent6eb262c3515c927df19340b3eee8c74bc9478d16 (diff)
downloadframeworks_base-055897208d659e9734a82def88be4a806ff55448.zip
frameworks_base-055897208d659e9734a82def88be4a806ff55448.tar.gz
frameworks_base-055897208d659e9734a82def88be4a806ff55448.tar.bz2
Move sound trigger calls to VoiceInteractionManagerService
- This ensures that any data being loaded on the DSP comes from the framework Change-Id: Ie15f0994850ba8f298ca07c49fe0b89e066d9e2b
-rw-r--r--Android.mk1
-rw-r--r--api/current.txt2
-rw-r--r--core/java/android/hardware/soundtrigger/DspInfo.java57
-rw-r--r--core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl39
-rw-r--r--core/java/android/hardware/soundtrigger/Keyphrase.aidl4
-rw-r--r--core/java/android/hardware/soundtrigger/Keyphrase.java135
-rw-r--r--core/java/android/hardware/soundtrigger/KeyphraseSoundModel.aidl4
-rw-r--r--core/java/android/hardware/soundtrigger/KeyphraseSoundModel.java79
-rw-r--r--core/java/android/hardware/soundtrigger/SoundTrigger.aidl24
-rw-r--r--core/java/android/hardware/soundtrigger/SoundTrigger.java310
-rw-r--r--core/java/android/service/voice/AlwaysOnHotwordDetector.java85
-rw-r--r--core/java/android/service/voice/VoiceInteractionService.java5
-rw-r--r--core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl24
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java44
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerHelper.java (renamed from core/java/android/hardware/soundtrigger/SoundTriggerHelper.java)109
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java83
16 files changed, 602 insertions, 403 deletions
diff --git a/Android.mk b/Android.mk
index d4dd6fd..4321f8b 100644
--- a/Android.mk
+++ b/Android.mk
@@ -167,6 +167,7 @@ LOCAL_SRC_FILES += \
core/java/android/hardware/location/IGeofenceHardware.aidl \
core/java/android/hardware/location/IGeofenceHardwareCallback.aidl \
core/java/android/hardware/location/IGeofenceHardwareMonitorCallback.aidl \
+ core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl \
core/java/android/hardware/usb/IUsbManager.aidl \
core/java/android/net/IConnectivityManager.aidl \
core/java/android/net/IEthernetManager.aidl \
diff --git a/api/current.txt b/api/current.txt
index dbdb1e5..c80e2b1 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -27242,7 +27242,7 @@ package android.service.voice {
field public static final int RECOGNITION_STATUS_NOT_REQUESTED = 2; // 0x2
field public static final int RECOGNITION_STATUS_REQUESTED = 4; // 0x4
field public static final int STATUS_ERROR = -2147483648; // 0x80000000
- field public static final int STATUS_OK = 1; // 0x1
+ field public static final int STATUS_OK = 0; // 0x0
}
public static abstract interface AlwaysOnHotwordDetector.Callback {
diff --git a/core/java/android/hardware/soundtrigger/DspInfo.java b/core/java/android/hardware/soundtrigger/DspInfo.java
deleted file mode 100644
index 517159d..0000000
--- a/core/java/android/hardware/soundtrigger/DspInfo.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/**
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.soundtrigger;
-
-import java.util.UUID;
-
-/**
- * Properties of the DSP hardware on the device.
- *
- * @hide
- */
-public class DspInfo {
- /**
- * Unique voice engine Id (changes with each version).
- */
- public final UUID voiceEngineId;
-
- /**
- * Human readable voice detection engine implementor.
- */
- public final String voiceEngineImplementor;
- /**
- * Human readable voice detection engine description.
- */
- public final String voiceEngineDescription;
- /**
- * Human readable voice detection engine version
- */
- public final int voiceEngineVersion;
- /**
- * Rated power consumption when detection is active.
- */
- public final int powerConsumptionMw;
-
- public DspInfo(UUID voiceEngineId, String voiceEngineImplementor,
- String voiceEngineDescription, int version, int powerConsumptionMw) {
- this.voiceEngineId = voiceEngineId;
- this.voiceEngineImplementor = voiceEngineImplementor;
- this.voiceEngineDescription = voiceEngineDescription;
- this.voiceEngineVersion = version;
- this.powerConsumptionMw = powerConsumptionMw;
- }
-}
diff --git a/core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl b/core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl
new file mode 100644
index 0000000..5738909
--- /dev/null
+++ b/core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.soundtrigger;
+
+/**
+ * @hide
+ */
+oneway interface IRecognitionStatusCallback {
+ /**
+ * Called when the keyphrase is spoken.
+ *
+ * @param data Optional trigger audio data, if it was requested and is available.
+ * TODO: See if the data being passed in works well, if not use shared memory.
+ * This *MUST* not exceed 100K.
+ */
+ void onDetected(in byte[] data);
+ /**
+ * Called when the detection for the associated keyphrase starts.
+ */
+ void onDetectionStarted();
+ /**
+ * Called when the detection for the associated keyphrase stops.
+ */
+ void onDetectionStopped();
+} \ No newline at end of file
diff --git a/core/java/android/hardware/soundtrigger/Keyphrase.aidl b/core/java/android/hardware/soundtrigger/Keyphrase.aidl
deleted file mode 100644
index d9853a7..0000000
--- a/core/java/android/hardware/soundtrigger/Keyphrase.aidl
+++ /dev/null
@@ -1,4 +0,0 @@
-package android.hardware.soundtrigger;
-
-// @hide
-parcelable Keyphrase; \ No newline at end of file
diff --git a/core/java/android/hardware/soundtrigger/Keyphrase.java b/core/java/android/hardware/soundtrigger/Keyphrase.java
deleted file mode 100644
index 51311bb..0000000
--- a/core/java/android/hardware/soundtrigger/Keyphrase.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/**
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.soundtrigger;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * A Voice Keyphrase.
- *
- * @hide
- */
-public class Keyphrase implements Parcelable {
- /** A unique identifier for this keyphrase */
- public final int id;
- /** A hint text to display corresponding to this keyphrase, e.g. "Hello There". */
- public final String hintText;
- /** The locale of interest when using this Keyphrase. */
- public final String locale;
- /** The various recognition modes supported by this keyphrase */
- public final int recognitionModeFlags;
- /** The users associated with this keyphrase */
- public final int[] users;
-
- public static final Parcelable.Creator<Keyphrase> CREATOR
- = new Parcelable.Creator<Keyphrase>() {
- public Keyphrase createFromParcel(Parcel in) {
- return Keyphrase.fromParcel(in);
- }
-
- public Keyphrase[] newArray(int size) {
- return new Keyphrase[size];
- }
- };
-
- private static Keyphrase fromParcel(Parcel in) {
- int id = in.readInt();
- String hintText = in.readString();
- String locale = in.readString();
- int recognitionModeFlags = in.readInt();
- int numUsers = in.readInt();
- int[] users = null;
- if (numUsers > 0) {
- users = new int[numUsers];
- in.readIntArray(users);
- }
- return new Keyphrase(id, hintText, locale, recognitionModeFlags, users);
- }
-
- public Keyphrase(int id, String hintText, String locale, int recognitionModeFlags,
- int[] users) {
- this.id = id;
- this.hintText = hintText;
- this.locale = locale;
- this.recognitionModeFlags = recognitionModeFlags;
- this.users = users;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(id);
- dest.writeString(hintText);
- dest.writeString(locale);
- dest.writeInt(recognitionModeFlags);
- if (users != null) {
- dest.writeInt(users.length);
- dest.writeIntArray(users);
- } else {
- dest.writeInt(0);
- }
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + ((hintText == null) ? 0 : hintText.hashCode());
- result = prime * result + id;
- result = prime * result + ((locale == null) ? 0 : locale.hashCode());
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (getClass() != obj.getClass())
- return false;
- Keyphrase other = (Keyphrase) obj;
- if (hintText == null) {
- if (other.hintText != null)
- return false;
- } else if (!hintText.equals(other.hintText))
- return false;
- if (id != other.id)
- return false;
- if (locale == null) {
- if (other.locale != null)
- return false;
- } else if (!locale.equals(other.locale))
- return false;
- return true;
- }
-
- @Override
- public String toString() {
- return "Keyphrase[id=" + id + ", text=" + hintText + ", locale=" + locale
- + ", recognitionModes=" + recognitionModeFlags + "]";
- }
-
- protected SoundTrigger.Keyphrase convertToSoundTriggerKeyphrase() {
- return new SoundTrigger.Keyphrase(id, recognitionModeFlags, locale, hintText, users);
- }
-}
diff --git a/core/java/android/hardware/soundtrigger/KeyphraseSoundModel.aidl b/core/java/android/hardware/soundtrigger/KeyphraseSoundModel.aidl
deleted file mode 100644
index 39b33cc..0000000
--- a/core/java/android/hardware/soundtrigger/KeyphraseSoundModel.aidl
+++ /dev/null
@@ -1,4 +0,0 @@
-package android.hardware.soundtrigger;
-
-// @hide
-parcelable KeyphraseSoundModel; \ No newline at end of file
diff --git a/core/java/android/hardware/soundtrigger/KeyphraseSoundModel.java b/core/java/android/hardware/soundtrigger/KeyphraseSoundModel.java
deleted file mode 100644
index a5ab0d2..0000000
--- a/core/java/android/hardware/soundtrigger/KeyphraseSoundModel.java
+++ /dev/null
@@ -1,79 +0,0 @@
-package android.hardware.soundtrigger;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.UUID;
-
-/**
- * A KeyphraseSoundModel is a sound model capable of detecting voice keyphrases.
- * It contains data needed by the hardware to detect a given number of key phrases
- * and the list of corresponding {@link Keyphrase}s.
- *
- * @hide
- */
-public class KeyphraseSoundModel implements Parcelable {
-
- /** Key phrases in this sound model */
- public final Keyphrase[] keyphrases;
- public final byte[] data;
- public final UUID uuid;
-
- public static final Parcelable.Creator<KeyphraseSoundModel> CREATOR
- = new Parcelable.Creator<KeyphraseSoundModel>() {
- public KeyphraseSoundModel createFromParcel(Parcel in) {
- return KeyphraseSoundModel.fromParcel(in);
- }
-
- public KeyphraseSoundModel[] newArray(int size) {
- return new KeyphraseSoundModel[size];
- }
- };
-
- public KeyphraseSoundModel(UUID uuid, byte[] data,Keyphrase[] keyPhrases) {
- this.uuid = uuid;
- this.data = data;
- this.keyphrases = keyPhrases;
- }
-
- private static KeyphraseSoundModel fromParcel(Parcel in) {
- UUID uuid = UUID.fromString(in.readString());
- int dataLength = in.readInt();
- byte[] data = null;
- if (dataLength > 0) {
- data = new byte[in.readInt()];
- in.readByteArray(data);
- }
- Keyphrase[] keyphrases =
- (Keyphrase[]) in.readParcelableArray(Keyphrase.class.getClassLoader());
- return new KeyphraseSoundModel(uuid, data, keyphrases);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(uuid.toString());
- if (data != null) {
- dest.writeInt(data.length);
- dest.writeByteArray(data);
- } else {
- dest.writeInt(0);
- }
- dest.writeParcelableArray(keyphrases, 0);
- }
-
- public SoundTrigger.KeyphraseSoundModel convertToSoundTriggerKeyphraseSoundModel() {
- SoundTrigger.Keyphrase[] stKeyphrases = null;
- if (keyphrases != null) {
- stKeyphrases = new SoundTrigger.Keyphrase[keyphrases.length];
- for (int i = 0; i < keyphrases.length; i++) {
- stKeyphrases[i] = keyphrases[i].convertToSoundTriggerKeyphrase();
- }
- }
- return new SoundTrigger.KeyphraseSoundModel(uuid, data, stKeyphrases);
- }
-}
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.aidl b/core/java/android/hardware/soundtrigger/SoundTrigger.aidl
new file mode 100644
index 0000000..837691a
--- /dev/null
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.aidl
@@ -0,0 +1,24 @@
+/**
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.soundtrigger;
+
+parcelable SoundTrigger.ConfidenceLevel;
+parcelable SoundTrigger.Keyphrase;
+parcelable SoundTrigger.KeyphraseRecognitionExtra;
+parcelable SoundTrigger.KeyphraseSoundModel;
+parcelable SoundTrigger.ModuleProperties;
+parcelable SoundTrigger.RecognitionConfig; \ No newline at end of file
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index 1f48a92..7b0a678 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -17,8 +17,11 @@
package android.hardware.soundtrigger;
import android.os.Handler;
+import android.os.Parcel;
+import android.os.Parcelable;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.UUID;
/**
@@ -43,7 +46,7 @@ public class SoundTrigger {
* ID used to target any API call to this paricular module. Module
* properties are returned by listModules() method.
****************************************************************************/
- public static class ModuleProperties {
+ public static class ModuleProperties implements Parcelable {
/** Unique module ID provided by the native service */
public final int id;
@@ -102,6 +105,70 @@ public class SoundTrigger {
this.supportsConcurrentCapture = supportsConcurrentCapture;
this.powerConsumptionMw = powerConsumptionMw;
}
+
+ public static final Parcelable.Creator<ModuleProperties> CREATOR
+ = new Parcelable.Creator<ModuleProperties>() {
+ public ModuleProperties createFromParcel(Parcel in) {
+ return ModuleProperties.fromParcel(in);
+ }
+
+ public ModuleProperties[] newArray(int size) {
+ return new ModuleProperties[size];
+ }
+ };
+
+ private static ModuleProperties fromParcel(Parcel in) {
+ int id = in.readInt();
+ String implementor = in.readString();
+ String description = in.readString();
+ String uuid = in.readString();
+ int version = in.readInt();
+ int maxSoundModels = in.readInt();
+ int maxKeyphrases = in.readInt();
+ int maxUsers = in.readInt();
+ int recognitionModes = in.readInt();
+ boolean supportsCaptureTransition = in.readByte() == 1;
+ int maxBufferMs = in.readInt();
+ boolean supportsConcurrentCapture = in.readByte() == 1;
+ int powerConsumptionMw = in.readInt();
+ return new ModuleProperties(id, implementor, description, uuid, version,
+ maxSoundModels, maxKeyphrases, maxUsers, recognitionModes,
+ supportsCaptureTransition, maxBufferMs, supportsConcurrentCapture,
+ powerConsumptionMw);
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(id);
+ dest.writeString(implementor);
+ dest.writeString(description);
+ dest.writeString(uuid.toString());
+ dest.writeInt(version);
+ dest.writeInt(maxSoundModels);
+ dest.writeInt(maxKeyphrases);
+ dest.writeInt(maxUsers);
+ dest.writeInt(recognitionModes);
+ dest.writeByte((byte) (supportsCaptureTransition ? 1 : 0));
+ dest.writeInt(maxBufferMs);
+ dest.writeByte((byte) (supportsConcurrentCapture ? 1 : 0));
+ dest.writeInt(powerConsumptionMw);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return "ModuleProperties [id=" + id + ", implementor=" + implementor + ", description="
+ + description + ", uuid=" + uuid + ", version=" + version + ", maxSoundModels="
+ + maxSoundModels + ", maxKeyphrases=" + maxKeyphrases + ", maxUsers="
+ + maxUsers + ", recognitionModes=" + recognitionModes
+ + ", supportsCaptureTransition=" + supportsCaptureTransition + ", maxBufferMs="
+ + maxBufferMs + ", supportsConcurrentCapture=" + supportsConcurrentCapture
+ + ", powerConsumptionMw=" + powerConsumptionMw + "]";
+ }
}
/*****************************************************************************
@@ -137,7 +204,7 @@ public class SoundTrigger {
* A Keyphrase describes a key phrase that can be detected by a
* {@link KeyphraseSoundModel}
****************************************************************************/
- public static class Keyphrase {
+ public static class Keyphrase implements Parcelable {
/** Unique identifier for this keyphrase */
public final int id;
@@ -161,6 +228,96 @@ public class SoundTrigger {
this.text = text;
this.users = users;
}
+
+ public static final Parcelable.Creator<Keyphrase> CREATOR
+ = new Parcelable.Creator<Keyphrase>() {
+ public Keyphrase createFromParcel(Parcel in) {
+ return Keyphrase.fromParcel(in);
+ }
+
+ public Keyphrase[] newArray(int size) {
+ return new Keyphrase[size];
+ }
+ };
+
+ private static Keyphrase fromParcel(Parcel in) {
+ int id = in.readInt();
+ int recognitionModes = in.readInt();
+ String locale = in.readString();
+ String text = in.readString();
+ int[] users = null;
+ int numUsers = in.readInt();
+ if (numUsers > 0) {
+ users = new int[numUsers];
+ in.readIntArray(users);
+ }
+ return new Keyphrase(id, recognitionModes, locale, text, users);
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(id);
+ dest.writeInt(recognitionModes);
+ dest.writeString(locale);
+ dest.writeString(text);
+ if (users != null) {
+ dest.writeInt(users.length);
+ dest.writeIntArray(users);
+ } else {
+ dest.writeInt(0);
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((text == null) ? 0 : text.hashCode());
+ result = prime * result + id;
+ result = prime * result + ((locale == null) ? 0 : locale.hashCode());
+ result = prime * result + recognitionModes;
+ result = prime * result + Arrays.hashCode(users);
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Keyphrase other = (Keyphrase) obj;
+ if (text == null) {
+ if (other.text != null)
+ return false;
+ } else if (!text.equals(other.text))
+ return false;
+ if (id != other.id)
+ return false;
+ if (locale == null) {
+ if (other.locale != null)
+ return false;
+ } else if (!locale.equals(other.locale))
+ return false;
+ if (recognitionModes != other.recognitionModes)
+ return false;
+ if (!Arrays.equals(users, other.users))
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "Keyphrase [id=" + id + ", recognitionModes=" + recognitionModes + ", locale="
+ + locale + ", text=" + text + ", users=" + Arrays.toString(users) + "]";
+ }
}
/*****************************************************************************
@@ -168,7 +325,7 @@ public class SoundTrigger {
* It contains data needed by the hardware to detect a certain number of key phrases
* and the list of corresponding {@link Keyphrase} descriptors.
****************************************************************************/
- public static class KeyphraseSoundModel extends SoundModel {
+ public static class KeyphraseSoundModel extends SoundModel implements Parcelable {
/** Key phrases in this sound model */
public final Keyphrase[] keyphrases; // keyword phrases in model
@@ -176,6 +333,46 @@ public class SoundTrigger {
super(id, TYPE_KEYPHRASE, data);
this.keyphrases = keyphrases;
}
+
+ public static final Parcelable.Creator<KeyphraseSoundModel> CREATOR
+ = new Parcelable.Creator<KeyphraseSoundModel>() {
+ public KeyphraseSoundModel createFromParcel(Parcel in) {
+ return KeyphraseSoundModel.fromParcel(in);
+ }
+
+ public KeyphraseSoundModel[] newArray(int size) {
+ return new KeyphraseSoundModel[size];
+ }
+ };
+
+ private static KeyphraseSoundModel fromParcel(Parcel in) {
+ UUID uuid = UUID.fromString(in.readString());
+ byte[] data = null;
+ int dataLength = in.readInt();
+ if (dataLength > 0) {
+ data = new byte[dataLength];
+ in.readByteArray(data);
+ }
+ Keyphrase[] keyphrases = in.createTypedArray(Keyphrase.CREATOR);
+ return new KeyphraseSoundModel(uuid, data, keyphrases);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(uuid.toString());
+ if (data != null) {
+ dest.writeInt(data.length);
+ dest.writeByteArray(data);
+ } else {
+ dest.writeInt(0);
+ }
+ dest.writeTypedArray(keyphrases, 0);
+ }
}
/**
@@ -239,7 +436,7 @@ public class SoundTrigger {
* {@link SoundTriggerModule#startRecognition(int, RecognitionConfig)} to configure the
* recognition request.
*/
- public static class RecognitionConfig {
+ public static class RecognitionConfig implements Parcelable {
/** True if the DSP should capture the trigger sound and make it available for further
* capture. */
public final boolean captureRequested;
@@ -256,6 +453,47 @@ public class SoundTrigger {
this.keyphrases = keyphrases;
this.data = data;
}
+
+ public static final Parcelable.Creator<RecognitionConfig> CREATOR
+ = new Parcelable.Creator<RecognitionConfig>() {
+ public RecognitionConfig createFromParcel(Parcel in) {
+ return RecognitionConfig.fromParcel(in);
+ }
+
+ public RecognitionConfig[] newArray(int size) {
+ return new RecognitionConfig[size];
+ }
+ };
+
+ private static RecognitionConfig fromParcel(Parcel in) {
+ boolean captureRequested = in.readByte() == 1;
+ KeyphraseRecognitionExtra[] keyphrases =
+ in.createTypedArray(KeyphraseRecognitionExtra.CREATOR);
+ byte[] data = null;
+ int dataLength = in.readInt();
+ if (dataLength > 0) {
+ data = new byte[dataLength];
+ in.readByteArray(data);
+ }
+ return new RecognitionConfig(captureRequested, keyphrases, data);
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeByte((byte) (captureRequested ? 1 : 0));
+ dest.writeTypedArray(keyphrases, 0);
+ if (data != null) {
+ dest.writeInt(data.length);
+ dest.writeByteArray(data);
+ } else {
+ dest.writeInt(0);
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
}
/**
@@ -266,7 +504,7 @@ public class SoundTrigger {
* should trigger a recognition.
* - The user ID is derived from the system ID {@link android.os.UserHandle#getIdentifier()}.
*/
- public static class ConfidenceLevel {
+ public static class ConfidenceLevel implements Parcelable {
public final int userId;
public final int confidenceLevel;
@@ -274,14 +512,42 @@ public class SoundTrigger {
this.userId = userId;
this.confidenceLevel = confidenceLevel;
}
+
+ public static final Parcelable.Creator<ConfidenceLevel> CREATOR
+ = new Parcelable.Creator<ConfidenceLevel>() {
+ public ConfidenceLevel createFromParcel(Parcel in) {
+ return ConfidenceLevel.fromParcel(in);
+ }
+
+ public ConfidenceLevel[] newArray(int size) {
+ return new ConfidenceLevel[size];
+ }
+ };
+
+ private static ConfidenceLevel fromParcel(Parcel in) {
+ int userId = in.readInt();
+ int confidenceLevel = in.readInt();
+ return new ConfidenceLevel(userId, confidenceLevel);
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(userId);
+ dest.writeInt(confidenceLevel);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
}
/**
* Additional data conveyed by a {@link KeyphraseRecognitionEvent}
* for a key phrase detection.
*/
- public static class KeyphraseRecognitionExtra {
- /** The keyphrse ID */
+ public static class KeyphraseRecognitionExtra implements Parcelable {
+ /** The keyphrase ID */
public final int id;
/** Recognition modes matched for this event */
@@ -297,6 +563,36 @@ public class SoundTrigger {
this.recognitionModes = recognitionModes;
this.confidenceLevels = confidenceLevels;
}
+
+ public static final Parcelable.Creator<KeyphraseRecognitionExtra> CREATOR
+ = new Parcelable.Creator<KeyphraseRecognitionExtra>() {
+ public KeyphraseRecognitionExtra createFromParcel(Parcel in) {
+ return KeyphraseRecognitionExtra.fromParcel(in);
+ }
+
+ public KeyphraseRecognitionExtra[] newArray(int size) {
+ return new KeyphraseRecognitionExtra[size];
+ }
+ };
+
+ private static KeyphraseRecognitionExtra fromParcel(Parcel in) {
+ int id = in.readInt();
+ int recognitionModes = in.readInt();
+ ConfidenceLevel[] confidenceLevels = in.createTypedArray(ConfidenceLevel.CREATOR);
+ return new KeyphraseRecognitionExtra(id, recognitionModes, confidenceLevels);
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(id);
+ dest.writeInt(recognitionModes);
+ dest.writeTypedArray(confidenceLevels, 0);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
}
/**
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index 048fda1..c4ed8c5 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -17,14 +17,15 @@
package android.service.voice;
import android.content.Intent;
-import android.hardware.soundtrigger.Keyphrase;
+import android.hardware.soundtrigger.IRecognitionStatusCallback;
import android.hardware.soundtrigger.KeyphraseEnrollmentInfo;
import android.hardware.soundtrigger.KeyphraseMetadata;
-import android.hardware.soundtrigger.KeyphraseSoundModel;
import android.hardware.soundtrigger.SoundTrigger;
import android.hardware.soundtrigger.SoundTrigger.ConfidenceLevel;
+import android.hardware.soundtrigger.SoundTrigger.Keyphrase;
import android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra;
-import android.hardware.soundtrigger.SoundTriggerHelper;
+import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
+import android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
import android.os.RemoteException;
import android.util.Slog;
@@ -68,8 +69,8 @@ public class AlwaysOnHotwordDetector {
/**
* Return codes for {@link #startRecognition(int)}, {@link #stopRecognition()}
*/
- public static final int STATUS_ERROR = Integer.MIN_VALUE;
- public static final int STATUS_OK = 1;
+ public static final int STATUS_ERROR = SoundTrigger.STATUS_ERROR;
+ public static final int STATUS_OK = SoundTrigger.STATUS_OK;
//---- Keyphrase recognition status ----//
/** Indicates that recognition is not available. */
@@ -117,11 +118,10 @@ public class AlwaysOnHotwordDetector {
*/
private final KeyphraseSoundModel mEnrolledSoundModel;
private final KeyphraseEnrollmentInfo mKeyphraseEnrollmentInfo;
- private final SoundTriggerHelper mSoundTriggerHelper;
- private final SoundTriggerHelper.Listener mListener;
private final int mAvailability;
private final IVoiceInteractionService mVoiceInteractionService;
private final IVoiceInteractionManagerService mModelManagementService;
+ private final SoundTriggerListener mInternalCallback;
private int mRecognitionState;
@@ -157,15 +157,13 @@ public class AlwaysOnHotwordDetector {
*/
public AlwaysOnHotwordDetector(String text, String locale, Callback callback,
KeyphraseEnrollmentInfo keyphraseEnrollmentInfo,
- SoundTriggerHelper soundTriggerHelper,
IVoiceInteractionService voiceInteractionService,
IVoiceInteractionManagerService modelManagementService) {
mText = text;
mLocale = locale;
mKeyphraseEnrollmentInfo = keyphraseEnrollmentInfo;
mKeyphraseMetadata = mKeyphraseEnrollmentInfo.getKeyphraseMetadata(text, locale);
- mListener = new SoundTriggerListener(callback);
- mSoundTriggerHelper = soundTriggerHelper;
+ mInternalCallback = new SoundTriggerListener(callback);
mVoiceInteractionService = voiceInteractionService;
mModelManagementService = modelManagementService;
if (mKeyphraseMetadata != null) {
@@ -225,7 +223,7 @@ public class AlwaysOnHotwordDetector {
* @param recognitionFlags The flags to control the recognition properties.
* The allowed flags are {@link #RECOGNITION_FLAG_NONE} and
* {@link #RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO}.
- * @return One of {@link #STATUS_ERROR} or {@link #STATUS_OK}.
+ * @return {@link #STATUS_OK} if the call succeeds, an error code otherwise.
* @throws UnsupportedOperationException if the recognition isn't supported.
* Callers should check the availability by calling {@link #getAvailability()}
* before calling this method to avoid this exception.
@@ -245,22 +243,25 @@ public class AlwaysOnHotwordDetector {
mKeyphraseMetadata.recognitionModeFlags, new ConfidenceLevel[0]);
boolean captureTriggerAudio =
(recognitionFlags & RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO) != 0;
- int code = mSoundTriggerHelper.startRecognition(mKeyphraseMetadata.id,
- mEnrolledSoundModel.convertToSoundTriggerKeyphraseSoundModel(), mListener,
- new RecognitionConfig(
- captureTriggerAudio, recognitionExtra,null /* additional data */));
- if (code != SoundTriggerHelper.STATUS_OK) {
+ int code = STATUS_ERROR;
+ try {
+ code = mModelManagementService.startRecognition(mVoiceInteractionService,
+ mKeyphraseMetadata.id, mEnrolledSoundModel, mInternalCallback,
+ new RecognitionConfig(
+ captureTriggerAudio, recognitionExtra, null /* additional data */));
+ } catch (RemoteException e) {
+ Slog.w(TAG, "RemoteException in startRecognition!");
+ }
+ if (code != STATUS_OK) {
Slog.w(TAG, "startRecognition() failed with error code " + code);
- return STATUS_ERROR;
- } else {
- return STATUS_OK;
}
+ return code;
}
/**
* Stops recognition for the associated keyphrase.
*
- * @return One of {@link #STATUS_ERROR} or {@link #STATUS_OK}.
+ * @return {@link #STATUS_OK} if the call succeeds, an error code otherwise.
* @throws UnsupportedOperationException if the recognition isn't supported.
* Callers should check the availability by calling {@link #getAvailability()}
* before calling this method to avoid this exception.
@@ -272,14 +273,18 @@ public class AlwaysOnHotwordDetector {
}
mRecognitionState &= ~RECOGNITION_STATUS_NOT_REQUESTED;
- int code = mSoundTriggerHelper.stopRecognition(mKeyphraseMetadata.id, mListener);
+ int code = STATUS_ERROR;
+ try {
+ code = mModelManagementService.stopRecognition(
+ mVoiceInteractionService, mKeyphraseMetadata.id, mInternalCallback);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "RemoteException in stopRecognition!");
+ }
- if (code != SoundTriggerHelper.STATUS_OK) {
+ if (code != STATUS_OK) {
Slog.w(TAG, "stopRecognition() failed with error code " + code);
- return STATUS_ERROR;
- } else {
- return STATUS_OK;
}
+ return code;
}
/**
@@ -309,8 +314,15 @@ public class AlwaysOnHotwordDetector {
}
private int internalGetAvailability() {
+ ModuleProperties dspModuleProperties = null;
+ try {
+ dspModuleProperties =
+ mModelManagementService.getDspModuleProperties(mVoiceInteractionService);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "RemoteException in getDspProperties!");
+ }
// No DSP available
- if (mSoundTriggerHelper.dspInfo == null) {
+ if (dspModuleProperties == null) {
mRecognitionState = RECOGNITION_STATUS_NOT_AVAILABLE;
return KEYPHRASE_HARDWARE_UNAVAILABLE;
}
@@ -359,7 +371,7 @@ public class AlwaysOnHotwordDetector {
}
/** @hide */
- static final class SoundTriggerListener implements SoundTriggerHelper.Listener {
+ static final class SoundTriggerListener extends IRecognitionStatusCallback.Stub {
private final Callback mCallback;
public SoundTriggerListener(Callback callback) {
@@ -367,20 +379,21 @@ public class AlwaysOnHotwordDetector {
}
@Override
- public void onKeyphraseSpoken(byte[] data) {
+ public void onDetected(byte[] data) {
Slog.i(TAG, "onKeyphraseSpoken");
mCallback.onDetected(data);
}
@Override
- public void onListeningStateChanged(int state) {
- Slog.i(TAG, "onListeningStateChanged: state=" + state);
- // TODO: Set/unset the RECOGNITION_STATUS_ACTIVE flag here.
- if (state == SoundTriggerHelper.STATE_STARTED) {
- mCallback.onDetectionStarted();
- } else if (state == SoundTriggerHelper.STATE_STOPPED) {
- mCallback.onDetectionStopped();
- }
+ public void onDetectionStarted() {
+ // TODO: Set the RECOGNITION_STATUS_ACTIVE flag here.
+ mCallback.onDetectionStarted();
+ }
+
+ @Override
+ public void onDetectionStopped() {
+ // TODO: Unset the RECOGNITION_STATUS_ACTIVE flag here.
+ mCallback.onDetectionStopped();
}
}
}
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index 1f5d327..982f43d 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -22,7 +22,6 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.hardware.soundtrigger.KeyphraseEnrollmentInfo;
-import android.hardware.soundtrigger.SoundTriggerHelper;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -78,7 +77,6 @@ public class VoiceInteractionService extends Service {
IVoiceInteractionManagerService mSystemService;
private KeyphraseEnrollmentInfo mKeyphraseEnrollmentInfo;
- private SoundTriggerHelper mSoundTriggerHelper;
static final int MSG_READY = 1;
@@ -150,7 +148,6 @@ public class VoiceInteractionService extends Service {
mSystemService = IVoiceInteractionManagerService.Stub.asInterface(
ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE));
mKeyphraseEnrollmentInfo = new KeyphraseEnrollmentInfo(getPackageManager());
- mSoundTriggerHelper = new SoundTriggerHelper();
}
/**
@@ -168,7 +165,7 @@ public class VoiceInteractionService extends Service {
// TODO: Cache instances and return the same one instead of creating a new interactor
// for the same keyphrase/locale combination.
return new AlwaysOnHotwordDetector(keyphrase, locale, callback,
- mKeyphraseEnrollmentInfo, mSoundTriggerHelper, mInterface, mSystemService);
+ mKeyphraseEnrollmentInfo, mInterface, mSystemService);
}
/**
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index c78f770..7d5abd2 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -20,7 +20,8 @@ import android.content.Intent;
import android.os.Bundle;
import com.android.internal.app.IVoiceInteractor;
-import android.hardware.soundtrigger.KeyphraseSoundModel;
+import android.hardware.soundtrigger.IRecognitionStatusCallback;
+import android.hardware.soundtrigger.SoundTrigger;
import android.service.voice.IVoiceInteractionService;
import android.service.voice.IVoiceInteractionSession;
@@ -37,9 +38,26 @@ interface IVoiceInteractionManagerService {
*
* @param service The current voice interaction service.
*/
- List<KeyphraseSoundModel> listRegisteredKeyphraseSoundModels(in IVoiceInteractionService service);
+ List<SoundTrigger.KeyphraseSoundModel> listRegisteredKeyphraseSoundModels(
+ in IVoiceInteractionService service);
/**
* Updates the given keyphrase sound model. Adds the model if it doesn't exist currently.
*/
- int updateKeyphraseSoundModel(in KeyphraseSoundModel model);
+ int updateKeyphraseSoundModel(in SoundTrigger.KeyphraseSoundModel model);
+
+ /**
+ * Gets the properties of the DSP hardware on this device, null if not present.
+ */
+ SoundTrigger.ModuleProperties getDspModuleProperties(in IVoiceInteractionService service);
+ /**
+ * Starts a recognition for the given keyphrase.
+ */
+ int startRecognition(in IVoiceInteractionService service, int keyphraseId,
+ in SoundTrigger.KeyphraseSoundModel soundModel, in IRecognitionStatusCallback callback,
+ in SoundTrigger.RecognitionConfig recognitionConfig);
+ /**
+ * Stops a recognition for the given keyphrase.
+ */
+ int stopRecognition(in IVoiceInteractionService service, int keyphraseId,
+ in IRecognitionStatusCallback callback);
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
index 27bec9f..eed2d44 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
@@ -22,8 +22,9 @@ import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.hardware.soundtrigger.SoundTrigger;
-import android.hardware.soundtrigger.Keyphrase;
-import android.hardware.soundtrigger.KeyphraseSoundModel;
+import android.hardware.soundtrigger.SoundTrigger.Keyphrase;
+import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
+import android.text.TextUtils;
import android.util.Slog;
import java.util.ArrayList;
@@ -39,7 +40,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {
static final String TAG = "SoundModelDBHelper";
private static final String NAME = "sound_model.db";
- private static final int VERSION = 1;
+ private static final int VERSION = 2;
public static interface KeyphraseContract {
public static final String TABLE = "keyphrase";
@@ -63,7 +64,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {
+ KeyphraseContract.TABLE + "("
+ KeyphraseContract.KEY_ID + " INTEGER PRIMARY KEY,"
+ KeyphraseContract.KEY_RECOGNITION_MODES + " INTEGER,"
- + KeyphraseContract.KEY_USERS + " INTEGER,"
+ + KeyphraseContract.KEY_USERS + " TEXT,"
+ KeyphraseContract.KEY_SOUND_MODEL_ID + " TEXT,"
+ KeyphraseContract.KEY_LOCALE + " TEXT,"
+ KeyphraseContract.KEY_HINT_TEXT + " TEXT" + ")";
@@ -119,10 +120,11 @@ public class DatabaseHelper extends SQLiteOpenHelper {
private boolean addOrUpdateKeyphrase(SQLiteDatabase db, UUID modelId, Keyphrase keyphrase) {
ContentValues values = new ContentValues();
values.put(KeyphraseContract.KEY_ID, keyphrase.id);
- values.put(KeyphraseContract.KEY_RECOGNITION_MODES, keyphrase.recognitionModeFlags);
+ values.put(KeyphraseContract.KEY_RECOGNITION_MODES, keyphrase.recognitionModes);
values.put(KeyphraseContract.KEY_SOUND_MODEL_ID, modelId.toString());
- values.put(KeyphraseContract.KEY_HINT_TEXT, keyphrase.hintText);
+ values.put(KeyphraseContract.KEY_HINT_TEXT, keyphrase.text);
values.put(KeyphraseContract.KEY_LOCALE, keyphrase.locale);
+ values.put(KeyphraseContract.KEY_USERS, getCommaSeparatedString(keyphrase.users));
if (db.insertWithOnConflict(
KeyphraseContract.TABLE, null, values, SQLiteDatabase.CONFLICT_REPLACE) != -1) {
return true;
@@ -193,11 +195,12 @@ public class DatabaseHelper extends SQLiteOpenHelper {
do {
int id = c.getInt(c.getColumnIndex(KeyphraseContract.KEY_ID));
int modes = c.getInt(c.getColumnIndex(KeyphraseContract.KEY_RECOGNITION_MODES));
- int[] users = {c.getInt(c.getColumnIndex(KeyphraseContract.KEY_USERS))};
+ int[] users = getArrayForCommaSeparatedString(
+ c.getString(c.getColumnIndex(KeyphraseContract.KEY_USERS)));
String locale = c.getString(c.getColumnIndex(KeyphraseContract.KEY_LOCALE));
String hintText = c.getString(c.getColumnIndex(KeyphraseContract.KEY_HINT_TEXT));
- keyphrases.add(new Keyphrase(id, hintText, locale, modes, users));
+ keyphrases.add(new Keyphrase(id, modes, locale, hintText, users));
} while (c.moveToNext());
}
Keyphrase[] keyphraseArr = new Keyphrase[keyphrases.size()];
@@ -205,4 +208,29 @@ public class DatabaseHelper extends SQLiteOpenHelper {
c.close();
return keyphraseArr;
}
+
+
+ private String getCommaSeparatedString(int[] users) {
+ if (users == null || users.length == 0) {
+ return "";
+ }
+ String csv = "";
+ for (int user : users) {
+ csv += String.valueOf(user);
+ csv += ",";
+ }
+ return csv.substring(0, csv.length() - 1);
+ }
+
+ private int[] getArrayForCommaSeparatedString(String text) {
+ if (TextUtils.isEmpty(text)) {
+ return null;
+ }
+ String[] usersStr = text.split(",");
+ int[] users = new int[usersStr.length];
+ for (int i = 0; i < usersStr.length; i++) {
+ users[i] = Integer.valueOf(usersStr[i]);
+ }
+ return users;
+ }
}
diff --git a/core/java/android/hardware/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerHelper.java
index 431d550..6842f7d 100644
--- a/core/java/android/hardware/soundtrigger/SoundTriggerHelper.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerHelper.java
@@ -14,11 +14,17 @@
* limitations under the License.
*/
-package android.hardware.soundtrigger;
+package com.android.server.voiceinteraction;
+import android.hardware.soundtrigger.IRecognitionStatusCallback;
+import android.hardware.soundtrigger.SoundTrigger;
+import android.hardware.soundtrigger.SoundTrigger.Keyphrase;
+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.SoundTriggerModule;
+import android.os.RemoteException;
import android.util.Slog;
import android.util.SparseArray;
@@ -36,72 +42,45 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
static final int TEMP_KEYPHRASE_ID = 1;
/**
- * Return codes for {@link #startRecognition(Keyphrase)}, {@link #stopRecognition(Keyphrase)}
- * Note: Keep in sync with AlwaysOnKeyphraseInteractor.java
+ * Return codes for {@link #startRecognition(int, KeyphraseSoundModel,
+ * IRecognitionStatusCallback, RecognitionConfig)},
+ * {@link #stopRecognition(int, IRecognitionStatusCallback)}
*/
- public static final int STATUS_ERROR = Integer.MIN_VALUE;
- public static final int STATUS_OK = 1;
-
- /**
- * States for {@link Listener#onListeningStateChanged(int, int)}.
- */
- public static final int STATE_STOPPED = 0;
- public static final int STATE_STARTED = 1;
+ public static final int STATUS_ERROR = SoundTrigger.STATUS_ERROR;
+ public static final int STATUS_OK = SoundTrigger.STATUS_OK;
private static final int INVALID_SOUND_MODEL_HANDLE = -1;
/** The {@link DspInfo} for the system, or null if none exists. */
- public final DspInfo dspInfo;
+ final ModuleProperties moduleProperties;
/** The properties for the DSP module */
- private final ModuleProperties mModuleProperties;
private final SoundTriggerModule mModule;
- private final SparseArray<Listener> mActiveListeners;
+ // Use a RemoteCallbackList here?
+ private final SparseArray<IRecognitionStatusCallback> mActiveListeners;
private int mCurrentSoundModelHandle = INVALID_SOUND_MODEL_HANDLE;
- /**
- * The callback for sound trigger events.
- */
- public interface Listener {
- /**
- * Called when the given keyphrase is spoken.
- *
- * @param data The captured audio, may be null.
- */
- void onKeyphraseSpoken(byte[] data);
-
- /**
- * Called when the listening state for the given keyphrase changes.
- * @param state Indicates the current state.
- */
- void onListeningStateChanged(int state);
- }
-
- public SoundTriggerHelper() {
+ SoundTriggerHelper() {
ArrayList <ModuleProperties> modules = new ArrayList<>();
int status = SoundTrigger.listModules(modules);
mActiveListeners = new SparseArray<>(1);
if (status != SoundTrigger.STATUS_OK || modules.size() == 0) {
Slog.w(TAG, "listModules status=" + status + ", # of modules=" + modules.size());
- dspInfo = null;
- mModuleProperties = null;
+ moduleProperties = null;
mModule = null;
} else {
// TODO: Figure out how to determine which module corresponds to the DSP hardware.
- mModuleProperties = modules.get(0);
- dspInfo = new DspInfo(mModuleProperties.uuid, mModuleProperties.implementor,
- mModuleProperties.description, mModuleProperties.version,
- mModuleProperties.powerConsumptionMw);
- mModule = SoundTrigger.attachModule(mModuleProperties.id, this, null);
+ moduleProperties = modules.get(0);
+ mModule = SoundTrigger.attachModule(moduleProperties.id, this, null);
}
}
/**
* @return True, if a recognition for the given {@link Keyphrase} is active.
*/
- public synchronized boolean isKeyphraseActive(Keyphrase keyphrase) {
+ synchronized boolean isKeyphraseActive(Keyphrase keyphrase) {
if (keyphrase == null) {
Slog.w(TAG, "isKeyphraseActive requires a non-null keyphrase");
return false;
@@ -110,7 +89,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
}
/**
- * Starts recognition for the given {@link Keyphrase}.
+ * Starts recognition for the given keyphraseId.
*
* @param keyphraseId The identifier of the keyphrase for which
* the recognition is to be started.
@@ -118,22 +97,27 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
* @param listener The listener for the recognition events related to the given keyphrase.
* @return One of {@link #STATUS_ERROR} or {@link #STATUS_OK}.
*/
- public synchronized int startRecognition(int keyphraseId,
- SoundTrigger.KeyphraseSoundModel soundModel,
- Listener listener, RecognitionConfig recognitionConfig) {
- if (dspInfo == null || mModule == null) {
+ synchronized int startRecognition(int keyphraseId,
+ KeyphraseSoundModel soundModel,
+ IRecognitionStatusCallback listener,
+ RecognitionConfig recognitionConfig) {
+ if (moduleProperties == null || mModule == null) {
Slog.w(TAG, "Attempting startRecognition without the capability");
return STATUS_ERROR;
}
- Listener oldListener = mActiveListeners.get(keyphraseId);
+ IRecognitionStatusCallback oldListener = mActiveListeners.get(keyphraseId);
if (oldListener != null && oldListener != listener) {
if (mCurrentSoundModelHandle != INVALID_SOUND_MODEL_HANDLE) {
Slog.w(TAG, "Canceling previous recognition");
// TODO: Inspect the return codes here.
mModule.unloadSoundModel(mCurrentSoundModelHandle);
}
- mActiveListeners.get(keyphraseId).onListeningStateChanged(STATE_STOPPED);
+ try {
+ mActiveListeners.get(keyphraseId).onDetectionStopped();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "RemoteException in onDetectionStopped");
+ }
mActiveListeners.remove(keyphraseId);
}
@@ -165,7 +149,8 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
}
/**
- * Stops recognition for the given {@link Keyphrase} if a recognition is currently active.
+ * Stops recognition for the given {@link Keyphrase} if a recognition is
+ * currently active.
*
* @param keyphraseId The identifier of the keyphrase for which
* the recognition is to be stopped.
@@ -173,13 +158,13 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
*
* @return One of {@link #STATUS_ERROR} or {@link #STATUS_OK}.
*/
- public synchronized int stopRecognition(int keyphraseId, Listener listener) {
- if (dspInfo == null || mModule == null) {
+ synchronized int stopRecognition(int keyphraseId, IRecognitionStatusCallback listener) {
+ if (moduleProperties == null || mModule == null) {
Slog.w(TAG, "Attempting stopRecognition without the capability");
return STATUS_ERROR;
}
- Listener currentListener = mActiveListeners.get(keyphraseId);
+ IRecognitionStatusCallback currentListener = mActiveListeners.get(keyphraseId);
if (currentListener == null) {
// startRecognition hasn't been called or it failed.
Slog.w(TAG, "Attempting stopRecognition without a successful startRecognition");
@@ -217,7 +202,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
// TODO: Get the keyphrase out of the event and fire events on it.
// For now, as a nasty workaround, we fire all events to the listener for
// keyphrase with TEMP_KEYPHRASE_ID.
- Listener listener = null;
+ IRecognitionStatusCallback listener = null;
synchronized(this) {
// TODO: The keyphrase should come from the recognition event
// as it may be for a different keyphrase than the current one.
@@ -231,13 +216,25 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
switch (event.status) {
case SoundTrigger.RECOGNITION_STATUS_SUCCESS:
// TODO: Pass the captured audio back.
- listener.onKeyphraseSpoken(null);
+ try {
+ listener.onDetected(null);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "RemoteException in onDetected");
+ }
break;
case SoundTrigger.RECOGNITION_STATUS_ABORT:
- listener.onListeningStateChanged(STATE_STOPPED);
+ try {
+ listener.onDetectionStopped();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "RemoteException in onDetectionStopped");
+ }
break;
case SoundTrigger.RECOGNITION_STATUS_FAILURE:
- listener.onListeningStateChanged(STATE_STOPPED);
+ try {
+ listener.onDetectionStopped();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "RemoteException in onDetectionStopped");
+ }
break;
}
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 63702ba..2ce4971 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -24,7 +24,10 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.ContentObserver;
-import android.hardware.soundtrigger.KeyphraseSoundModel;
+import android.hardware.soundtrigger.IRecognitionStatusCallback;
+import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
+import android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
+import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -56,19 +59,17 @@ public class VoiceInteractionManagerService extends SystemService {
static final String TAG = "VoiceInteractionManagerService";
- // TODO: Add descriptive error codes.
- public static final int STATUS_ERROR = -1;
- public static final int STATUS_OK = 1;
-
final Context mContext;
final ContentResolver mResolver;
final DatabaseHelper mDbHelper;
+ final SoundTriggerHelper mSoundTriggerHelper;
public VoiceInteractionManagerService(Context context) {
super(context);
mContext = context;
mResolver = context.getContentResolver();
mDbHelper = new DatabaseHelper(context);
+ mSoundTriggerHelper = new SoundTriggerHelper();
}
@Override
@@ -239,6 +240,8 @@ public class VoiceInteractionManagerService extends SystemService {
}
}
+ //----------------- Model management APIs --------------------------------//
+
@Override
public List<KeyphraseSoundModel> listRegisteredKeyphraseSoundModels(
IVoiceInteractionService service) {
@@ -290,15 +293,15 @@ public class VoiceInteractionManagerService extends SystemService {
// If the keyphrases are not present in the model, delete the model.
if (model.keyphrases == null) {
if (mDbHelper.deleteKeyphraseSoundModel(model.uuid)) {
- return STATUS_OK;
+ return SoundTriggerHelper.STATUS_OK;
} else {
- return STATUS_ERROR;
+ return SoundTriggerHelper.STATUS_ERROR;
}
} else {
if (mDbHelper.addOrUpdateKeyphraseSoundModel(model)) {
- return STATUS_OK;
+ return SoundTriggerHelper.STATUS_OK;
} else {
- return STATUS_ERROR;
+ return SoundTriggerHelper.STATUS_ERROR;
}
}
} finally {
@@ -307,6 +310,68 @@ public class VoiceInteractionManagerService extends SystemService {
}
}
+ //----------------- SoundTrigger APIs --------------------------------//
+ @Override
+ public ModuleProperties getDspModuleProperties(IVoiceInteractionService service) {
+ // Allow the call if this is the current voice interaction service.
+ synchronized (this) {
+ if (mImpl == null || mImpl.mService == null
+ || service == null || service.asBinder() != mImpl.mService.asBinder()) {
+ throw new SecurityException(
+ "Caller is not the current voice interaction service");
+ }
+
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ return mSoundTriggerHelper.moduleProperties;
+ } finally {
+ Binder.restoreCallingIdentity(caller);
+ }
+ }
+ }
+
+ @Override
+ public int startRecognition(IVoiceInteractionService service, int keyphraseId,
+ KeyphraseSoundModel soundModel, IRecognitionStatusCallback callback,
+ RecognitionConfig recognitionConfig) {
+ // Allow the call if this is the current voice interaction service.
+ synchronized (this) {
+ if (mImpl == null || mImpl.mService == null
+ || service == null || service.asBinder() != mImpl.mService.asBinder()) {
+ throw new SecurityException(
+ "Caller is not the current voice interaction service");
+ }
+
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ return mSoundTriggerHelper.startRecognition(
+ keyphraseId, soundModel, callback, recognitionConfig);
+ } finally {
+ Binder.restoreCallingIdentity(caller);
+ }
+ }
+ }
+
+ @Override
+ public int stopRecognition(IVoiceInteractionService service, int keyphraseId,
+ IRecognitionStatusCallback callback) {
+ // Allow the call if this is the current voice interaction service.
+ synchronized (this) {
+ if (mImpl == null || mImpl.mService == null
+ || service == null || service.asBinder() != mImpl.mService.asBinder()) {
+ throw new SecurityException(
+ "Caller is not the current voice interaction service");
+ }
+
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ return mSoundTriggerHelper.stopRecognition(keyphraseId, callback);
+ } finally {
+ Binder.restoreCallingIdentity(caller);
+ }
+ }
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)