Move sound trigger calls to VoiceInteractionManagerService

- This ensures that any data being loaded on the DSP comes from the framework

Change-Id: Ie15f0994850ba8f298ca07c49fe0b89e066d9e2b
diff --git a/Android.mk b/Android.mk
index d4dd6fd..4321f8b 100644
--- a/Android.mk
+++ b/Android.mk
@@ -167,6 +167,7 @@
 	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 @@
     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 @@
      * 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 @@
             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 @@
      * 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 @@
             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 @@
      * 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 @@
             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 @@
      *  {@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 @@
             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 @@
      * 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 @@
             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 @@
             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 @@
     /**
      * 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 @@
      */
     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 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 @@
      * @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 @@
                 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) {
-            Slog.w(TAG, "startRecognition() failed with error code " + code);
-            return STATUS_ERROR;
-        } else {
-            return 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 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 @@
         }
 
         mRecognitionState &= ~RECOGNITION_STATUS_NOT_REQUESTED;
-        int code = mSoundTriggerHelper.stopRecognition(mKeyphraseMetadata.id, mListener);
-
-        if (code != SoundTriggerHelper.STATUS_OK) {
-            Slog.w(TAG, "stopRecognition() failed with error code " + code);
-            return STATUS_ERROR;
-        } else {
-            return STATUS_OK;
+        int code = STATUS_ERROR;
+        try {
+            code = mModelManagementService.stopRecognition(
+                    mVoiceInteractionService, mKeyphraseMetadata.id, mInternalCallback);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "RemoteException in stopRecognition!");
         }
+
+        if (code != STATUS_OK) {
+            Slog.w(TAG, "stopRecognition() failed with error code " + code);
+        }
+        return code;
     }
 
     /**
@@ -309,8 +314,15 @@
     }
 
     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 @@
     }
 
     /** @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 @@
         }
 
         @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.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 @@
     IVoiceInteractionManagerService mSystemService;
 
     private KeyphraseEnrollmentInfo mKeyphraseEnrollmentInfo;
-    private SoundTriggerHelper mSoundTriggerHelper;
 
     static final int MSG_READY = 1;
 
@@ -150,7 +148,6 @@
         mSystemService = IVoiceInteractionManagerService.Stub.asInterface(
                 ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE));
         mKeyphraseEnrollmentInfo = new KeyphraseEnrollmentInfo(getPackageManager());
-        mSoundTriggerHelper = new SoundTriggerHelper();
     }
 
     /**
@@ -168,7 +165,7 @@
         // 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.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 @@
      *
      * @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.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 @@
     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 @@
             + 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 @@
     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 @@
             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 @@
         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
similarity index 73%
rename from core/java/android/hardware/soundtrigger/SoundTriggerHelper.java
rename to 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 @@
     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 @@
     }
 
     /**
-     * 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 @@
      * @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 @@
     }
 
     /**
-     * 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 @@
      *
      * @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 @@
         // 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 @@
         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.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 @@
 
     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 @@
             }
         }
 
+        //----------------- Model management APIs --------------------------------//
+
         @Override
         public List<KeyphraseSoundModel> listRegisteredKeyphraseSoundModels(
                 IVoiceInteractionService service) {
@@ -290,15 +293,15 @@
                     // 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 @@
             }
         }
 
+        //----------------- 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)