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;
+ }
+}