Initial code for listing/storing sound models

- We use a DB to store and persist the sound models.
- This'll be used by SoundTriggerModelManager, a service that lists,
  deletes and registers new models. This'll be used by the enrollment
  client to enroll and unenroll users.

- This needs the unique identifiers for sound model & keyphrases to be
  present in the respective data structures

This is very early stage so please point out any concerns.

Change-Id: I82962895bf326167458f20e6ba995295551de025
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index 2d7af85..f7636a3 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -16,8 +16,6 @@
 
 package android.hardware.soundtrigger;
 
-import android.content.Context;
-import android.content.Intent;
 import android.os.Handler;
 
 import java.util.ArrayList;
@@ -65,7 +63,7 @@
         public final int maxSoundModels;
 
         /** Maximum number of key phrases */
-        public final int maxKeyPhrases;
+        public final int maxKeyphrases;
 
         /** Maximum number of users per key phrase */
         public final int maxUsers;
@@ -86,7 +84,7 @@
         public final int powerConsumptionMw;
 
         ModuleProperties(int id, String implementor, String description,
-                String uuid, int version, int maxSoundModels, int maxKeyPhrases,
+                String uuid, int version, int maxSoundModels, int maxKeyphrases,
                 int maxUsers, int recognitionModes, boolean supportsCaptureTransition,
                 int maxBufferMs, boolean supportsConcurrentCapture,
                 int powerConsumptionMw) {
@@ -96,7 +94,7 @@
             this.uuid = UUID.fromString(uuid);
             this.version = version;
             this.maxSoundModels = maxSoundModels;
-            this.maxKeyPhrases = maxKeyPhrases;
+            this.maxKeyphrases = maxKeyphrases;
             this.maxUsers = maxUsers;
             this.recognitionModes = recognitionModes;
             this.supportsCaptureTransition = supportsCaptureTransition;
@@ -109,7 +107,7 @@
     /*****************************************************************************
      * A SoundModel describes the attributes and contains the binary data used by the hardware
      * implementation to detect a particular sound pattern.
-     * A specialized version {@link KeyPhraseSoundModel} is defined for key phrase
+     * A specialized version {@link KeyphraseSoundModel} is defined for key phrase
      * sound models.
      ****************************************************************************/
     public static class SoundModel {
@@ -119,23 +117,30 @@
         /** Keyphrase sound model */
         public static final int TYPE_KEYPHRASE = 0;
 
+        /** Unique sound model identifier */
+        public final UUID uuid;
+
         /** Sound model type (e.g. TYPE_KEYPHRASE); */
         public final int type;
 
         /** Opaque data. For use by vendor implementation and enrollment application */
         public final byte[] data;
 
-        public SoundModel(int type, byte[] data) {
+        public SoundModel(UUID uuid, int type, byte[] data) {
+            this.uuid = uuid;
             this.type = type;
             this.data = data;
         }
     }
 
     /*****************************************************************************
-     * A KeyPhrase describes a key phrase that can be detected by a
-     * {@link KeyPhraseSoundModel}
+     * A Keyphrase describes a key phrase that can be detected by a
+     * {@link KeyphraseSoundModel}
      ****************************************************************************/
-    public static class KeyPhrase {
+    public static class Keyphrase {
+        /** Unique identifier for this keyphrase */
+        public final int id;
+
         /** Recognition modes supported for this key phrase in the model */
         public final int recognitionModes;
 
@@ -148,7 +153,8 @@
         /** Number of users this key phrase has been trained for */
         public final int numUsers;
 
-        public KeyPhrase(int recognitionModes, String locale, String text, int numUsers) {
+        public Keyphrase(int id, int recognitionModes, String locale, String text, int numUsers) {
+            this.id = id;
             this.recognitionModes = recognitionModes;
             this.locale = locale;
             this.text = text;
@@ -157,17 +163,17 @@
     }
 
     /*****************************************************************************
-     * A KeyPhraseSoundModel is a specialized {@link SoundModel} for key phrases.
+     * A KeyphraseSoundModel is a specialized {@link SoundModel} for key phrases.
      * It contains data needed by the hardware to detect a certain number of key phrases
-     * and the list of corresponding {@link KeyPhrase} descriptors.
+     * and the list of corresponding {@link Keyphrase} descriptors.
      ****************************************************************************/
-    public static class KeyPhraseSoundModel extends SoundModel {
+    public static class KeyphraseSoundModel extends SoundModel {
         /** Key phrases in this sound model */
-        public final KeyPhrase[] keyPhrases; // keyword phrases in model
+        public final Keyphrase[] keyphrases; // keyword phrases in model
 
-        public KeyPhraseSoundModel(byte[] data, KeyPhrase[] keyPhrases) {
-            super(TYPE_KEYPHRASE, data);
-            this.keyPhrases = keyPhrases;
+        public KeyphraseSoundModel(UUID id, byte[] data, Keyphrase[] keyphrases) {
+            super(id, TYPE_KEYPHRASE, data);
+            this.keyphrases = keyphrases;
         }
     }
 
@@ -225,10 +231,10 @@
     }
 
     /**
-     *  Additional data conveyed by a {@link KeyPhraseRecognitionEvent}
+     *  Additional data conveyed by a {@link KeyphraseRecognitionEvent}
      *  for a key phrase detection.
      */
-    public static class KeyPhraseRecognitionExtra {
+    public static class KeyphraseRecognitionExtra {
         /** Confidence level for each user defined in the key phrase in the same order as
          * users in the key phrase. The confidence level is expressed in percentage (0% -100%) */
         public final int[] confidenceLevels;
@@ -236,7 +242,7 @@
         /** Recognition modes matched for this event */
         public final int recognitionModes;
 
-        KeyPhraseRecognitionExtra(int[] confidenceLevels, int recognitionModes) {
+        KeyphraseRecognitionExtra(int[] confidenceLevels, int recognitionModes) {
             this.confidenceLevels = confidenceLevels;
             this.recognitionModes = recognitionModes;
         }
@@ -245,19 +251,19 @@
     /**
      *  Specialized {@link RecognitionEvent} for a key phrase detection.
      */
-    public static class KeyPhraseRecognitionEvent extends RecognitionEvent {
+    public static class KeyphraseRecognitionEvent extends RecognitionEvent {
         /** Indicates if the key phrase is present in the buffered audio available for capture */
-        public final KeyPhraseRecognitionExtra[] keyPhraseExtras;
+        public final KeyphraseRecognitionExtra[] keyphraseExtras;
 
         /** Additional data available for each recognized key phrases in the model */
-        public final boolean keyPhraseInCapture;
+        public final boolean keyphraseInCapture;
 
-        KeyPhraseRecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
+        KeyphraseRecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
                int captureSession, int captureDelayMs, byte[] data,
-               boolean keyPhraseInCapture, KeyPhraseRecognitionExtra[] keyPhraseExtras) {
+               boolean keyphraseInCapture, KeyphraseRecognitionExtra[] keyphraseExtras) {
             super(status, soundModelHandle, captureAvailable, captureSession, captureDelayMs, data);
-            this.keyPhraseInCapture = keyPhraseInCapture;
-            this.keyPhraseExtras = keyPhraseExtras;
+            this.keyphraseInCapture = keyphraseInCapture;
+            this.keyphraseExtras = keyphraseExtras;
         }
     }
 
diff --git a/core/jni/android_hardware_SoundTrigger.cpp b/core/jni/android_hardware_SoundTrigger.cpp
index 69e991d..afb92a1 100644
--- a/core/jni/android_hardware_SoundTrigger.cpp
+++ b/core/jni/android_hardware_SoundTrigger.cpp
@@ -37,6 +37,11 @@
     jmethodID    add;
 } gArrayListMethods;
 
+static jclass gUUIDClass;
+static struct {
+    jmethodID    toString;
+} gUUIDMethods;
+
 static const char* const kSoundTriggerClassPathName = "android/hardware/soundtrigger/SoundTrigger";
 static jclass gSoundTriggerClass;
 
@@ -57,25 +62,27 @@
                                      "android/hardware/soundtrigger/SoundTrigger$SoundModel";
 static jclass gSoundModelClass;
 static struct {
+    jfieldID    uuid;
     jfieldID    data;
 } gSoundModelFields;
 
-static const char* const kKeyPhraseClassPathName =
-                                     "android/hardware/soundtrigger/SoundTrigger$KeyPhrase";
-static jclass gKeyPhraseClass;
+static const char* const kKeyphraseClassPathName =
+                                     "android/hardware/soundtrigger/SoundTrigger$Keyphrase";
+static jclass gKeyphraseClass;
 static struct {
+    jfieldID id;
     jfieldID recognitionModes;
     jfieldID locale;
     jfieldID text;
     jfieldID numUsers;
-} gKeyPhraseFields;
+} gKeyphraseFields;
 
-static const char* const kKeyPhraseSoundModelClassPathName =
-                                 "android/hardware/soundtrigger/SoundTrigger$KeyPhraseSoundModel";
-static jclass gKeyPhraseSoundModelClass;
+static const char* const kKeyphraseSoundModelClassPathName =
+                                 "android/hardware/soundtrigger/SoundTrigger$KeyphraseSoundModel";
+static jclass gKeyphraseSoundModelClass;
 static struct {
-    jfieldID    keyPhrases;
-} gKeyPhraseSoundModelFields;
+    jfieldID    keyphrases;
+} gKeyphraseSoundModelFields;
 
 
 static const char* const kRecognitionEventClassPathName =
@@ -83,15 +90,15 @@
 static jclass gRecognitionEventClass;
 static jmethodID   gRecognitionEventCstor;
 
-static const char* const kKeyPhraseRecognitionEventClassPathName =
-                             "android/hardware/soundtrigger/SoundTrigger$KeyPhraseRecognitionEvent";
-static jclass gKeyPhraseRecognitionEventClass;
-static jmethodID   gKeyPhraseRecognitionEventCstor;
+static const char* const kKeyphraseRecognitionEventClassPathName =
+                             "android/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionEvent";
+static jclass gKeyphraseRecognitionEventClass;
+static jmethodID   gKeyphraseRecognitionEventCstor;
 
-static const char* const kKeyPhraseRecognitionExtraClassPathName =
-                             "android/hardware/soundtrigger/SoundTrigger$KeyPhraseRecognitionExtra";
-static jclass gKeyPhraseRecognitionExtraClass;
-static jmethodID   gKeyPhraseRecognitionExtraCstor;
+static const char* const kKeyphraseRecognitionExtraClassPathName =
+                             "android/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionExtra";
+static jclass gKeyphraseRecognitionExtraClass;
+static jmethodID   gKeyphraseRecognitionExtraCstor;
 
 static Mutex gLock;
 
@@ -170,7 +177,7 @@
                 (struct sound_trigger_phrase_recognition_event *)event;
 
         jobjectArray jExtras = env->NewObjectArray(phraseEvent->num_phrases,
-                                                  gKeyPhraseRecognitionExtraClass, NULL);
+                                                  gKeyphraseRecognitionExtraClass, NULL);
         if (jExtras == NULL) {
             return;
         }
@@ -185,8 +192,8 @@
                    phraseEvent->phrase_extras[i].confidence_levels,
                    phraseEvent->phrase_extras[i].num_users * sizeof(int));
             env->ReleaseIntArrayElements(jConfidenceLevels, nConfidenceLevels, 0);
-            jobject jNewExtra = env->NewObject(gKeyPhraseRecognitionExtraClass,
-                                               gKeyPhraseRecognitionExtraCstor,
+            jobject jNewExtra = env->NewObject(gKeyphraseRecognitionExtraClass,
+                                               gKeyphraseRecognitionExtraCstor,
                                                jConfidenceLevels,
                                                phraseEvent->phrase_extras[i].recognition_modes);
 
@@ -196,7 +203,7 @@
             env->SetObjectArrayElement(jExtras, i, jNewExtra);
 
         }
-        jEvent = env->NewObject(gKeyPhraseRecognitionEventClass, gKeyPhraseRecognitionEventCstor,
+        jEvent = env->NewObject(gKeyphraseRecognitionEventClass, gKeyphraseRecognitionEventCstor,
                                 event->status, event->model, event->capture_available,
                                event->capture_session, event->capture_delay_ms, jData,
                                phraseEvent->key_phrase_in_capture, jExtras);
@@ -381,6 +388,9 @@
     sp<IMemory> memory;
     size_t size;
     sound_model_handle_t handle;
+    jobject jUuid;
+    jstring jUuidString;
+    const char *nUuidString;
 
     ALOGV("loadSoundModel");
     sp<SoundTrigger> module = getSoundTrigger(env, thiz);
@@ -404,13 +414,22 @@
     }
     size_t offset;
     sound_trigger_sound_model_type_t type;
-    if (env->IsInstanceOf(jSoundModel, gKeyPhraseSoundModelClass)) {
+    if (env->IsInstanceOf(jSoundModel, gKeyphraseSoundModelClass)) {
         offset = sizeof(struct sound_trigger_phrase_sound_model);
         type = SOUND_MODEL_TYPE_KEYPHRASE;
     } else {
         offset = sizeof(struct sound_trigger_sound_model);
         type = SOUND_MODEL_TYPE_UNKNOWN;
     }
+
+    jUuid = env->GetObjectField(jSoundModel, gSoundModelFields.uuid);
+    jUuidString = (jstring)env->CallObjectMethod(jUuid, gUUIDMethods.toString);
+    nUuidString = env->GetStringUTFChars(jUuidString, NULL);
+    sound_trigger_uuid_t nUuid;
+    SoundTrigger::stringToGuid(nUuidString, &nUuid);
+    env->ReleaseStringUTFChars(jUuidString, nUuidString);
+    env->DeleteLocalRef(jUuidString);
+
     jData = (jbyteArray)env->GetObjectField(jSoundModel, gSoundModelFields.data);
     if (jData == NULL) {
         status = SOUNDTRIGGER_STATUS_BAD_VALUE;
@@ -438,6 +457,7 @@
     nSoundModel = (struct sound_trigger_sound_model *)memory->pointer();
 
     nSoundModel->type = type;
+    nSoundModel->uuid = nUuid;
     nSoundModel->data_size = size;
     nSoundModel->data_offset = offset;
     memcpy((char *)nSoundModel + offset, nData, size);
@@ -446,7 +466,7 @@
                 (struct sound_trigger_phrase_sound_model *)nSoundModel;
 
         jobjectArray jPhrases =
-            (jobjectArray)env->GetObjectField(jSoundModel, gKeyPhraseSoundModelFields.keyPhrases);
+            (jobjectArray)env->GetObjectField(jSoundModel, gKeyphraseSoundModelFields.keyphrases);
         if (jPhrases == NULL) {
             status = SOUNDTRIGGER_STATUS_BAD_VALUE;
             goto exit;
@@ -457,16 +477,18 @@
         ALOGV("loadSoundModel numPhrases %d", numPhrases);
         for (size_t i = 0; i < numPhrases; i++) {
             jobject jPhrase = env->GetObjectArrayElement(jPhrases, i);
+            phraseModel->phrases[i].id =
+                                    env->GetIntField(jPhrase,gKeyphraseFields.id);
             phraseModel->phrases[i].recognition_mode =
-                                    env->GetIntField(jPhrase,gKeyPhraseFields.recognitionModes);
+                                    env->GetIntField(jPhrase,gKeyphraseFields.recognitionModes);
             phraseModel->phrases[i].num_users =
-                                    env->GetIntField(jPhrase, gKeyPhraseFields.numUsers);
-            jstring jLocale = (jstring)env->GetObjectField(jPhrase, gKeyPhraseFields.locale);
+                                    env->GetIntField(jPhrase, gKeyphraseFields.numUsers);
+            jstring jLocale = (jstring)env->GetObjectField(jPhrase, gKeyphraseFields.locale);
             const char *nLocale = env->GetStringUTFChars(jLocale, NULL);
             strncpy(phraseModel->phrases[i].locale,
                     nLocale,
                     SOUND_TRIGGER_MAX_LOCALE_LEN);
-            jstring jText = (jstring)env->GetObjectField(jPhrase, gKeyPhraseFields.text);
+            jstring jText = (jstring)env->GetObjectField(jPhrase, gKeyphraseFields.text);
             const char *nText = env->GetStringUTFChars(jText, NULL);
             strncpy(phraseModel->phrases[i].text,
                     nText,
@@ -599,6 +621,10 @@
     gArrayListClass = (jclass) env->NewGlobalRef(arrayListClass);
     gArrayListMethods.add = env->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z");
 
+    jclass uuidClass = env->FindClass("java/util/UUID");
+    gUUIDClass = (jclass) env->NewGlobalRef(uuidClass);
+    gUUIDMethods.toString = env->GetMethodID(uuidClass, "toString", "()Ljava/lang/String;");
+
     jclass lClass = env->FindClass(kSoundTriggerClassPathName);
     gSoundTriggerClass = (jclass) env->NewGlobalRef(lClass);
 
@@ -617,20 +643,22 @@
 
     jclass soundModelClass = env->FindClass(kSoundModelClassPathName);
     gSoundModelClass = (jclass) env->NewGlobalRef(soundModelClass);
+    gSoundModelFields.uuid = env->GetFieldID(soundModelClass, "uuid", "Ljava/util/UUID;");
     gSoundModelFields.data = env->GetFieldID(soundModelClass, "data", "[B");
 
-    jclass keyPhraseClass = env->FindClass(kKeyPhraseClassPathName);
-    gKeyPhraseClass = (jclass) env->NewGlobalRef(keyPhraseClass);
-    gKeyPhraseFields.recognitionModes = env->GetFieldID(keyPhraseClass, "recognitionModes", "I");
-    gKeyPhraseFields.locale = env->GetFieldID(keyPhraseClass, "locale", "Ljava/lang/String;");
-    gKeyPhraseFields.text = env->GetFieldID(keyPhraseClass, "text", "Ljava/lang/String;");
-    gKeyPhraseFields.numUsers = env->GetFieldID(keyPhraseClass, "numUsers", "I");
+    jclass keyphraseClass = env->FindClass(kKeyphraseClassPathName);
+    gKeyphraseClass = (jclass) env->NewGlobalRef(keyphraseClass);
+    gKeyphraseFields.id = env->GetFieldID(keyphraseClass, "id", "I");
+    gKeyphraseFields.recognitionModes = env->GetFieldID(keyphraseClass, "recognitionModes", "I");
+    gKeyphraseFields.locale = env->GetFieldID(keyphraseClass, "locale", "Ljava/lang/String;");
+    gKeyphraseFields.text = env->GetFieldID(keyphraseClass, "text", "Ljava/lang/String;");
+    gKeyphraseFields.numUsers = env->GetFieldID(keyphraseClass, "numUsers", "I");
 
-    jclass keyPhraseSoundModelClass = env->FindClass(kKeyPhraseSoundModelClassPathName);
-    gKeyPhraseSoundModelClass = (jclass) env->NewGlobalRef(keyPhraseSoundModelClass);
-    gKeyPhraseSoundModelFields.keyPhrases = env->GetFieldID(keyPhraseSoundModelClass,
-                                         "keyPhrases",
-                                         "[Landroid/hardware/soundtrigger/SoundTrigger$KeyPhrase;");
+    jclass keyphraseSoundModelClass = env->FindClass(kKeyphraseSoundModelClassPathName);
+    gKeyphraseSoundModelClass = (jclass) env->NewGlobalRef(keyphraseSoundModelClass);
+    gKeyphraseSoundModelFields.keyphrases = env->GetFieldID(keyphraseSoundModelClass,
+                                         "keyphrases",
+                                         "[Landroid/hardware/soundtrigger/SoundTrigger$Keyphrase;");
 
 
     jclass recognitionEventClass = env->FindClass(kRecognitionEventClassPathName);
@@ -638,15 +666,15 @@
     gRecognitionEventCstor = env->GetMethodID(recognitionEventClass, "<init>",
                                               "(IIZII[B)V");
 
-    jclass keyPhraseRecognitionEventClass = env->FindClass(kKeyPhraseRecognitionEventClassPathName);
-    gKeyPhraseRecognitionEventClass = (jclass) env->NewGlobalRef(keyPhraseRecognitionEventClass);
-    gKeyPhraseRecognitionEventCstor = env->GetMethodID(keyPhraseRecognitionEventClass, "<init>",
-              "(IIZII[BZ[Landroid/hardware/soundtrigger/SoundTrigger$KeyPhraseRecognitionExtra;)V");
+    jclass keyphraseRecognitionEventClass = env->FindClass(kKeyphraseRecognitionEventClassPathName);
+    gKeyphraseRecognitionEventClass = (jclass) env->NewGlobalRef(keyphraseRecognitionEventClass);
+    gKeyphraseRecognitionEventCstor = env->GetMethodID(keyphraseRecognitionEventClass, "<init>",
+              "(IIZII[BZ[Landroid/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionExtra;)V");
 
 
-    jclass keyPhraseRecognitionExtraClass = env->FindClass(kKeyPhraseRecognitionExtraClassPathName);
-    gKeyPhraseRecognitionExtraClass = (jclass) env->NewGlobalRef(keyPhraseRecognitionExtraClass);
-    gKeyPhraseRecognitionExtraCstor = env->GetMethodID(keyPhraseRecognitionExtraClass, "<init>",
+    jclass keyphraseRecognitionExtraClass = env->FindClass(kKeyphraseRecognitionExtraClassPathName);
+    gKeyphraseRecognitionExtraClass = (jclass) env->NewGlobalRef(keyphraseRecognitionExtraClass);
+    gKeyphraseRecognitionExtraCstor = env->GetMethodID(keyphraseRecognitionExtraClass, "<init>",
                                               "([II)V");
 
     int status = AndroidRuntime::registerNativeMethods(env,
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
new file mode 100644
index 0000000..cb902b2
--- /dev/null
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
@@ -0,0 +1,181 @@
+/**
+ * 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 com.android.server.voiceinteraction;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteDatabase.CursorFactory;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.hardware.soundtrigger.SoundTrigger;
+import android.hardware.soundtrigger.SoundTrigger.Keyphrase;
+import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
+import android.util.Slog;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+/**
+ * @hide
+ */
+public class DatabaseHelper extends SQLiteOpenHelper {
+    static final String TAG = "SoundModelDBHelper";
+
+    private static final String NAME = "sound_model.db";
+    private static final int VERSION = 1;
+
+    public static interface KeyphraseContract {
+        public static final String TABLE = "keyphrase";
+        public static final String KEY_ID = "_id";
+        public static final String KEY_RECOGNITION_MODES = "modes";
+        public static final String KEY_LOCALE = "locale";
+        public static final String KEY_HINT_TEXT = "hint_text";
+        public static final String KEY_NUM_USERS = "num_users";
+        public static final String KEY_SOUND_MODEL_ID = "sound_model_id";
+    }
+
+    public static interface SoundModelContract {
+        public static final String TABLE = "sound_model";
+        public static final String KEY_ID = "_id";
+        public static final String KEY_TYPE = "type";
+        public static final String KEY_DATA = "data";
+    }
+
+    // Table Create Statements
+    private static final String CREATE_TABLE_KEYPRHASES = "CREATE TABLE "
+            + KeyphraseContract.TABLE + "("
+            + KeyphraseContract.KEY_ID + " INTEGER PRIMARY KEY,"
+            + KeyphraseContract.KEY_RECOGNITION_MODES + " INTEGER,"
+            + KeyphraseContract.KEY_NUM_USERS + " INTEGER,"
+            + KeyphraseContract.KEY_SOUND_MODEL_ID + " TEXT,"
+            + KeyphraseContract.KEY_LOCALE + " TEXT,"
+            + KeyphraseContract.KEY_HINT_TEXT + " TEXT" + ")";
+
+    private static final String CREATE_TABLE_SOUND_MODEL = "CREATE TABLE "
+            + SoundModelContract.TABLE + "("
+            + SoundModelContract.KEY_ID + " TEXT PRIMARY KEY,"
+            + SoundModelContract.KEY_TYPE + " INTEGER,"
+            + SoundModelContract.KEY_DATA + " BLOB" + ")";
+
+    public DatabaseHelper(Context context, CursorFactory factory) {
+        super(context, NAME, null, VERSION);
+    }
+
+    @Override
+    public void onCreate(SQLiteDatabase db) {
+        // creating required tables
+        db.execSQL(CREATE_TABLE_KEYPRHASES);
+        db.execSQL(CREATE_TABLE_SOUND_MODEL);
+    }
+
+    @Override
+    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+        // TODO(sansid): For now, drop older tables and recreate new ones.
+        db.execSQL("DROP TABLE IF EXISTS " + KeyphraseContract.TABLE);
+        db.execSQL("DROP TABLE IF EXISTS " + SoundModelContract.TABLE);
+        onCreate(db);
+    }
+
+    /**
+     * TODO(sansid): Change to addOrUpdate to handle changes here.
+     */
+    public void addKeyphraseSoundModel(KeyphraseSoundModel soundModel) {
+        SQLiteDatabase db = this.getWritableDatabase();
+        ContentValues values = new ContentValues();
+        // Generate a random ID for the model.
+        values.put(SoundModelContract.KEY_ID, soundModel.uuid.toString());
+        values.put(SoundModelContract.KEY_DATA, soundModel.data);
+        values.put(SoundModelContract.KEY_TYPE, SoundTrigger.SoundModel.TYPE_KEYPHRASE);
+
+        if (db.insert(SoundModelContract.TABLE, null, values) != -1) {
+            for (Keyphrase keyphrase : soundModel.keyphrases) {
+                addKeyphrase(soundModel.uuid, keyphrase);
+            }
+        } else {
+            Slog.w(TAG, "Failed to persist sound model to database");
+        }
+    }
+
+    /**
+     * TODO(sansid): Change to addOrUpdate to handle changes here.
+     */
+    private void addKeyphrase(UUID modelId, SoundTrigger.Keyphrase keyphrase) {
+        SQLiteDatabase db = this.getWritableDatabase();
+        ContentValues values = new ContentValues();
+        values.put(KeyphraseContract.KEY_ID, keyphrase.id);
+        values.put(KeyphraseContract.KEY_RECOGNITION_MODES, keyphrase.recognitionModes);
+        values.put(KeyphraseContract.KEY_SOUND_MODEL_ID, keyphrase.id);
+        values.put(KeyphraseContract.KEY_HINT_TEXT, keyphrase.text);
+        values.put(KeyphraseContract.KEY_LOCALE, keyphrase.locale);
+        if (db.insert(KeyphraseContract.TABLE, null, values) == -1) {
+            Slog.w(TAG, "Failed to persist keyphrase to database");
+        }
+    }
+
+    /**
+     * Lists all the keyphrase sound models currently registered with the system.
+     */
+    public List<KeyphraseSoundModel> getKephraseSoundModels() {
+        List<KeyphraseSoundModel> models = new ArrayList<>();
+        String selectQuery = "SELECT  * FROM " + SoundModelContract.TABLE;
+        SQLiteDatabase db = this.getReadableDatabase();
+        Cursor c = db.rawQuery(selectQuery, null);
+
+        // looping through all rows and adding to list
+        if (c.moveToFirst()) {
+            do {
+                int type = c.getInt(c.getColumnIndex(SoundModelContract.KEY_TYPE));
+                if (type != SoundTrigger.SoundModel.TYPE_KEYPHRASE) {
+                    // Ignore non-keyphrase sound models.
+                    continue;
+                }
+                String id = c.getString(c.getColumnIndex(SoundModelContract.KEY_ID));
+                byte[] data = c.getBlob(c.getColumnIndex(SoundModelContract.KEY_DATA));
+                // Get all the keyphrases for this this sound model.
+                models.add(new KeyphraseSoundModel(
+                        UUID.fromString(id), data, getKeyphrasesForSoundModel(id)));
+            } while (c.moveToNext());
+        }
+        return models;
+    }
+
+    private Keyphrase[] getKeyphrasesForSoundModel(String modelId) {
+        List<Keyphrase> keyphrases = new ArrayList<>();
+        String selectQuery = "SELECT  * FROM " + KeyphraseContract.TABLE
+                + " WHERE " + KeyphraseContract.KEY_SOUND_MODEL_ID + " = '" + modelId + "'";
+        SQLiteDatabase db = this.getReadableDatabase();
+        Cursor c = db.rawQuery(selectQuery, null);
+
+        // looping through all rows and adding to list
+        if (c.moveToFirst()) {
+            do {
+                int id = c.getInt(c.getColumnIndex(KeyphraseContract.KEY_ID));
+                int modes = c.getInt(c.getColumnIndex(KeyphraseContract.KEY_RECOGNITION_MODES));
+                int numUsers = c.getInt(c.getColumnIndex(KeyphraseContract.KEY_NUM_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, modes, locale, hintText, numUsers));
+            } while (c.moveToNext());
+        }
+        Keyphrase[] keyphraseArr = new Keyphrase[keyphrases.size()];
+        keyphrases.toArray(keyphraseArr);
+        return keyphraseArr;
+    }
+}