Merge "CamcorderProfiles: add high speed profile constants" into lmp-dev
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 95cb9f3..59cd97c 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -246,6 +246,7 @@
private static native void nativeRestoreAllowFds(long nativePtr, boolean lastValue);
private static native void nativeWriteByteArray(long nativePtr, byte[] b, int offset, int len);
+ private static native void nativeWriteBlob(long nativePtr, byte[] b, int offset, int len);
private static native void nativeWriteInt(long nativePtr, int val);
private static native void nativeWriteLong(long nativePtr, long val);
private static native void nativeWriteFloat(long nativePtr, float val);
@@ -255,6 +256,7 @@
private static native void nativeWriteFileDescriptor(long nativePtr, FileDescriptor val);
private static native byte[] nativeCreateByteArray(long nativePtr);
+ private static native byte[] nativeReadBlob(long nativePtr);
private static native int nativeReadInt(long nativePtr);
private static native long nativeReadLong(long nativePtr);
private static native float nativeReadFloat(long nativePtr);
@@ -479,6 +481,16 @@
}
/**
+ * Write a blob of data into the parcel at the current {@link #dataPosition},
+ * growing {@link #dataCapacity} if needed.
+ * @param b Bytes to place into the parcel.
+ * {@hide}
+ */
+ public final void writeBlob(byte[] b) {
+ nativeWriteBlob(mNativePtr, b, 0, (b != null) ? b.length : 0);
+ }
+
+ /**
* Write an integer value into the parcel at the current dataPosition(),
* growing dataCapacity() if needed.
*/
@@ -1700,6 +1712,14 @@
}
/**
+ * Read a blob of data from the parcel and return it as a byte array.
+ * {@hide}
+ */
+ public final byte[] readBlob() {
+ return nativeReadBlob(mNativePtr);
+ }
+
+ /**
* Read and return a String[] object from the parcel.
* {@hide}
*/
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index d685cc5..d077a17 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -133,10 +133,10 @@
private final Handler mHandler;
/**
- * The sound model for the keyphrase, derived from the model management service
- * (IVoiceInteractionManagerService). May be null if the keyphrase isn't enrolled yet.
+ * Indicates if there is a sound model enrolled for the keyphrase,
+ * derived from the model management service (IVoiceInteractionManagerService).
*/
- private KeyphraseSoundModel mEnrolledSoundModel;
+ private boolean mIsEnrolledForDetection;
private int mAvailability = STATE_NOT_READY;
/**
@@ -257,7 +257,7 @@
int code = STATUS_ERROR;
try {
code = mModelManagementService.startRecognition(mVoiceInteractionService,
- mKeyphraseMetadata.id, mEnrolledSoundModel, mInternalCallback,
+ mKeyphraseMetadata.id, mInternalCallback,
new RecognitionConfig(
captureTriggerAudio, recognitionExtra, null /* additional data */));
} catch (RemoteException e) {
@@ -417,14 +417,13 @@
@Override
public Void doInBackground(Void... params) {
int availability = internalGetInitialAvailability();
- KeyphraseSoundModel soundModel = null;
+ boolean enrolled = false;
// Fetch the sound model if the availability is one of the supported ones.
if (availability == STATE_NOT_READY
|| availability == STATE_KEYPHRASE_UNENROLLED
|| availability == STATE_KEYPHRASE_ENROLLED) {
- soundModel =
- internalGetKeyphraseSoundModel(mKeyphraseMetadata.id);
- if (soundModel == null) {
+ enrolled = internalGetIsEnrolled(mKeyphraseMetadata.id);
+ if (!enrolled) {
availability = STATE_KEYPHRASE_UNENROLLED;
} else {
availability = STATE_KEYPHRASE_ENROLLED;
@@ -436,8 +435,8 @@
Slog.d(TAG, "Hotword availability changed from " + mAvailability
+ " -> " + availability);
}
+ mIsEnrolledForDetection = enrolled;
mAvailability = availability;
- mEnrolledSoundModel = soundModel;
notifyStateChangedLocked();
}
return null;
@@ -475,31 +474,14 @@
/**
* @return The corresponding {@link KeyphraseSoundModel} or null if none is found.
*/
- private KeyphraseSoundModel internalGetKeyphraseSoundModel(int keyphraseId) {
- List<KeyphraseSoundModel> soundModels;
+ private boolean internalGetIsEnrolled(int keyphraseId) {
try {
- soundModels = mModelManagementService
- .listRegisteredKeyphraseSoundModels(mVoiceInteractionService);
- if (soundModels == null || soundModels.isEmpty()) {
- Slog.i(TAG, "No available sound models for keyphrase ID: " + keyphraseId);
- return null;
- }
- for (int i = 0; i < soundModels.size(); i++) {
- KeyphraseSoundModel soundModel = soundModels.get(i);
- if (soundModel.keyphrases == null || soundModel.keyphrases.length == 0) {
- continue;
- }
- for (int j = 0; i < soundModel.keyphrases.length; j++) {
- Keyphrase keyphrase = soundModel.keyphrases[j];
- if (keyphrase.id == keyphraseId) {
- return soundModel;
- }
- }
- }
+ return mModelManagementService.isEnrolledForKeyphrase(
+ mVoiceInteractionService, keyphraseId);
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException in listRegisteredKeyphraseSoundModels!");
}
- return null;
+ return false;
}
}
}
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index 7d5abd2..22ec4be 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -33,19 +33,24 @@
void finish(IBinder token);
/**
- * Lists the registered Sound models for keyphrase detection.
+ * Lists the registered Sound model for keyphrase detection.
* May be null if no matching sound models exist.
- *
- * @param service The current voice interaction service.
*/
- List<SoundTrigger.KeyphraseSoundModel> listRegisteredKeyphraseSoundModels(
- in IVoiceInteractionService service);
+ SoundTrigger.KeyphraseSoundModel getKeyphraseSoundModel(int keyphraseId);
/**
* Updates the given keyphrase sound model. Adds the model if it doesn't exist currently.
*/
int updateKeyphraseSoundModel(in SoundTrigger.KeyphraseSoundModel model);
+ /**
+ * Deletes the given keyphrase sound model.
+ */
+ int deleteKeyphraseSoundModel(int keyphraseId);
/**
+ * Indicates if there's a keyphrase sound model available for the given keyphrase ID.
+ */
+ boolean isEnrolledForKeyphrase(IVoiceInteractionService service, int keyphraseId);
+ /**
* Gets the properties of the DSP hardware on this device, null if not present.
*/
SoundTrigger.ModuleProperties getDspModuleProperties(in IVoiceInteractionService service);
@@ -53,7 +58,7 @@
* Starts a recognition for the given keyphrase.
*/
int startRecognition(in IVoiceInteractionService service, int keyphraseId,
- in SoundTrigger.KeyphraseSoundModel soundModel, in IRecognitionStatusCallback callback,
+ in IRecognitionStatusCallback callback,
in SoundTrigger.RecognitionConfig recognitionConfig);
/**
* Stops a recognition for the given keyphrase.
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index 50f6c73..3ba481e 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -187,6 +187,37 @@
}
}
+static void android_os_Parcel_writeBlob(JNIEnv* env, jclass clazz, jlong nativePtr, jobject data,
+ jint offset, jint length) {
+ Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
+ if (parcel == NULL) {
+ return;
+ }
+
+ const status_t err = parcel->writeInt32(length);
+ if (err != NO_ERROR) {
+ signalExceptionForError(env, clazz, err);
+ return;
+ }
+
+ android::Parcel::WritableBlob blob;
+ android::status_t err2 = parcel->writeBlob(length, &blob);
+ if (err2 != NO_ERROR) {
+ signalExceptionForError(env, clazz, err2);
+ return;
+ }
+
+ jbyte* ar = (jbyte*)env->GetPrimitiveArrayCritical((jarray)data, 0);
+ if (ar == NULL) {
+ memset(blob.data(), 0, length);
+ } else {
+ memcpy(blob.data(), ar + offset, length);
+ env->ReleasePrimitiveArrayCritical((jarray)data, ar, 0);
+ }
+
+ blob.release();
+}
+
static void android_os_Parcel_writeInt(JNIEnv* env, jclass clazz, jlong nativePtr, jint val) {
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
const status_t err = parcel->writeInt32(val);
@@ -297,6 +328,36 @@
return ret;
}
+static jbyteArray android_os_Parcel_readBlob(JNIEnv* env, jclass clazz, jlong nativePtr)
+{
+ jbyteArray ret = NULL;
+
+ Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
+ if (parcel != NULL) {
+ int32_t len = parcel->readInt32();
+ if (len >= 0) {
+ android::Parcel::ReadableBlob blob;
+ android::status_t err = parcel->readBlob(len, &blob);
+ if (err != NO_ERROR) {
+ signalExceptionForError(env, clazz, err);
+ return NULL;
+ }
+
+ ret = env->NewByteArray(len);
+ if (ret != NULL) {
+ jbyte* a2 = (jbyte*)env->GetPrimitiveArrayCritical(ret, 0);
+ if (a2) {
+ memcpy(a2, blob.data(), len);
+ env->ReleasePrimitiveArrayCritical(ret, a2, 0);
+ }
+ }
+ blob.release();
+ }
+ }
+
+ return ret;
+}
+
static jint android_os_Parcel_readInt(JNIEnv* env, jclass clazz, jlong nativePtr)
{
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
@@ -634,6 +695,7 @@
{"nativeRestoreAllowFds", "(JZ)V", (void*)android_os_Parcel_restoreAllowFds},
{"nativeWriteByteArray", "(J[BII)V", (void*)android_os_Parcel_writeNative},
+ {"nativeWriteBlob", "(J[BII)V", (void*)android_os_Parcel_writeBlob},
{"nativeWriteInt", "(JI)V", (void*)android_os_Parcel_writeInt},
{"nativeWriteLong", "(JJ)V", (void*)android_os_Parcel_writeLong},
{"nativeWriteFloat", "(JF)V", (void*)android_os_Parcel_writeFloat},
@@ -643,6 +705,7 @@
{"nativeWriteFileDescriptor", "(JLjava/io/FileDescriptor;)V", (void*)android_os_Parcel_writeFileDescriptor},
{"nativeCreateByteArray", "(J)[B", (void*)android_os_Parcel_createByteArray},
+ {"nativeReadBlob", "(J)[B", (void*)android_os_Parcel_readBlob},
{"nativeReadInt", "(J)I", (void*)android_os_Parcel_readInt},
{"nativeReadLong", "(J)J", (void*)android_os_Parcel_readLong},
{"nativeReadFloat", "(J)F", (void*)android_os_Parcel_readFloat},
diff --git a/core/res/res/layout/app_permission_item.xml b/core/res/res/layout/app_permission_item.xml
index e2ffffb..1eff3dc 100644
--- a/core/res/res/layout/app_permission_item.xml
+++ b/core/res/res/layout/app_permission_item.xml
@@ -27,8 +27,8 @@
<ImageView
android:id="@+id/perm_icon"
- android:layout_width="32dp"
- android:layout_height="32dp"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="8dp"
android:scaleType="fitCenter" />
diff --git a/core/res/res/layout/app_permission_item_money.xml b/core/res/res/layout/app_permission_item_money.xml
index 3fa4653..7e1aca1 100644
--- a/core/res/res/layout/app_permission_item_money.xml
+++ b/core/res/res/layout/app_permission_item_money.xml
@@ -27,8 +27,8 @@
<ImageView
android:id="@+id/perm_icon"
- android:layout_width="32dp"
- android:layout_height="32dp"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="8dp"
android:scaleType="fitCenter" />
diff --git a/core/res/res/layout/app_permission_item_old.xml b/core/res/res/layout/app_permission_item_old.xml
index ce0cd42..de6fc4f 100644
--- a/core/res/res/layout/app_permission_item_old.xml
+++ b/core/res/res/layout/app_permission_item_old.xml
@@ -26,8 +26,8 @@
<ImageView
android:id="@+id/perm_icon"
- android:layout_width="30dip"
- android:layout_height="30dip"
+ android:layout_width="24dip"
+ android:layout_height="24dip"
android:layout_alignParentStart="true"
android:scaleType="fitCenter" />
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
index 1e0d6de..8913eb9 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
@@ -28,8 +28,6 @@
import android.text.TextUtils;
import android.util.Slog;
-import java.util.ArrayList;
-import java.util.List;
import java.util.UUID;
/**
@@ -39,43 +37,35 @@
*/
public class DatabaseHelper extends SQLiteOpenHelper {
static final String TAG = "SoundModelDBHelper";
- static final boolean DBG = false;
+ // TODO: Set to false.
+ static final boolean DBG = true;
private static final String NAME = "sound_model.db";
- private static final int VERSION = 2;
-
- 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_USERS = "users";
- public static final String KEY_SOUND_MODEL_ID = "sound_model_id";
- }
+ private static final int VERSION = 3;
public static interface SoundModelContract {
public static final String TABLE = "sound_model";
- public static final String KEY_ID = "_id";
+ public static final String KEY_KEYPHRASE_ID = "keyphrase_id";
+ public static final String KEY_MODEL_UUID = "model_uuid";
public static final String KEY_TYPE = "type";
public static final String KEY_DATA = "data";
+ public static final String KEY_RECOGNITION_MODES = "recognition_modes";
+ public static final String KEY_LOCALE = "locale";
+ public static final String KEY_HINT_TEXT = "hint_text";
+ public static final String KEY_USERS = "users";
}
- // 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_USERS + " TEXT,"
- + KeyphraseContract.KEY_SOUND_MODEL_ID + " TEXT,"
- + KeyphraseContract.KEY_LOCALE + " TEXT,"
- + KeyphraseContract.KEY_HINT_TEXT + " TEXT" + ")";
-
+ // Table Create Statement
private static final String CREATE_TABLE_SOUND_MODEL = "CREATE TABLE "
+ SoundModelContract.TABLE + "("
- + SoundModelContract.KEY_ID + " TEXT PRIMARY KEY,"
+ + SoundModelContract.KEY_KEYPHRASE_ID + " INTEGER PRIMARY KEY,"
+ + SoundModelContract.KEY_MODEL_UUID + " TEXT,"
+ SoundModelContract.KEY_TYPE + " INTEGER,"
- + SoundModelContract.KEY_DATA + " BLOB" + ")";
+ + SoundModelContract.KEY_DATA + " BLOB,"
+ + SoundModelContract.KEY_RECOGNITION_MODES + " INTEGER,"
+ + SoundModelContract.KEY_LOCALE + " TEXT,"
+ + SoundModelContract.KEY_HINT_TEXT + " TEXT,"
+ + SoundModelContract.KEY_USERS + " TEXT" + ")";
private final UserManager mUserManager;
@@ -87,57 +77,44 @@
@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: 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);
}
- public boolean addOrUpdateKeyphraseSoundModel(KeyphraseSoundModel soundModel) {
+ /**
+ * Updates the given keyphrase model, adds it, if it doesn't already exist.
+ *
+ * TODO: We only support one keyphrase currently.
+ */
+ public boolean updateKeyphraseSoundModel(KeyphraseSoundModel soundModel) {
synchronized(this) {
SQLiteDatabase db = 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_MODEL_UUID, soundModel.uuid.toString());
values.put(SoundModelContract.KEY_TYPE, SoundTrigger.SoundModel.TYPE_KEYPHRASE);
-
- boolean status = true;
- if (db.insertWithOnConflict(SoundModelContract.TABLE, null, values,
- SQLiteDatabase.CONFLICT_REPLACE) != -1) {
- for (Keyphrase keyphrase : soundModel.keyphrases) {
- status &= addOrUpdateKeyphraseLocked(db, soundModel.uuid, keyphrase);
- }
- db.close();
- return status;
- } else {
- Slog.w(TAG, "Failed to persist sound model to database");
- db.close();
- return false;
- }
- }
- }
+ values.put(SoundModelContract.KEY_DATA, soundModel.data);
- private boolean addOrUpdateKeyphraseLocked(
- SQLiteDatabase db, UUID modelId, Keyphrase keyphrase) {
- 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, modelId.toString());
- 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;
- } else {
- Slog.w(TAG, "Failed to persist keyphrase to database");
+ if (soundModel.keyphrases != null && soundModel.keyphrases.length == 1) {
+ values.put(SoundModelContract.KEY_KEYPHRASE_ID, soundModel.keyphrases[0].id);
+ values.put(SoundModelContract.KEY_RECOGNITION_MODES,
+ soundModel.keyphrases[0].recognitionModes);
+ values.put(SoundModelContract.KEY_USERS,
+ getCommaSeparatedString(soundModel.keyphrases[0].users));
+ values.put(SoundModelContract.KEY_LOCALE, soundModel.keyphrases[0].locale);
+ values.put(SoundModelContract.KEY_HINT_TEXT, soundModel.keyphrases[0].text);
+ try {
+ return db.insertWithOnConflict(SoundModelContract.TABLE, null, values,
+ SQLiteDatabase.CONFLICT_REPLACE) != -1;
+ } finally {
+ db.close();
+ }
+ }
return false;
}
}
@@ -145,111 +122,90 @@
/**
* Deletes the sound model and associated keyphrases.
*/
- public boolean deleteKeyphraseSoundModel(UUID uuid) {
+ public boolean deleteKeyphraseSoundModel(int keyphraseId) {
synchronized(this) {
SQLiteDatabase db = getWritableDatabase();
- String modelId = uuid.toString();
- String soundModelClause = SoundModelContract.KEY_ID + "=" + modelId;
- boolean status = true;
- if (db.delete(SoundModelContract.TABLE, soundModelClause, null) == 0) {
- Slog.w(TAG, "No sound models deleted from the database");
- status = false;
+ String soundModelClause = SoundModelContract.KEY_KEYPHRASE_ID + "=" + keyphraseId;
+
+ try {
+ return db.delete(SoundModelContract.TABLE, soundModelClause, null) != 0;
+ } finally {
+ db.close();
}
- String keyphraseClause = KeyphraseContract.KEY_SOUND_MODEL_ID + "=" + modelId;
- if (db.delete(KeyphraseContract.TABLE, keyphraseClause, null) == 0) {
- Slog.w(TAG, "No keyphrases deleted from the database");
- status = false;
- }
- db.close();
- return status;
}
}
/**
- * Lists all the keyphrase sound models currently registered with the system.
+ * Returns a matching {@link KeyphraseSoundModel} for the keyphrase ID.
+ * Returns null if a match isn't found.
+ *
+ * TODO: We only support one keyphrase currently.
*/
- public List<KeyphraseSoundModel> getKephraseSoundModels() {
+ public KeyphraseSoundModel getKeyphraseSoundModel(int keyphraseId) {
synchronized(this) {
- List<KeyphraseSoundModel> models = new ArrayList<>();
- String selectQuery = "SELECT * FROM " + SoundModelContract.TABLE;
+ // Find the corresponding sound model ID for the keyphrase.
+ String selectQuery = "SELECT * FROM " + SoundModelContract.TABLE
+ + " WHERE " + SoundModelContract.KEY_KEYPHRASE_ID + " = '" + keyphraseId + "'";
SQLiteDatabase db = getReadableDatabase();
Cursor c = db.rawQuery(selectQuery, null);
-
- // looping through all rows and adding to list
- if (c.moveToFirst()) {
- do {
+
+ try {
+ if (c.moveToFirst()) {
int type = c.getInt(c.getColumnIndex(SoundModelContract.KEY_TYPE));
if (type != SoundTrigger.SoundModel.TYPE_KEYPHRASE) {
- // Ignore non-keyphrase sound models.
- continue;
+ Slog.w(TAG, "No KeyphraseSoundModel available for the given keyphrase");
+ return null;
}
- 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.
- // Validate the sound model.
- if (id == null) {
+
+ String modelUuid = c.getString(
+ c.getColumnIndex(SoundModelContract.KEY_MODEL_UUID));
+ if (modelUuid == null) {
Slog.w(TAG, "Ignoring sound model since it doesn't specify an ID");
- continue;
+ return null;
}
- KeyphraseSoundModel model = new KeyphraseSoundModel(
- UUID.fromString(id), data, getKeyphrasesForSoundModelLocked(db, id));
- if (DBG) {
- Slog.d(TAG, "Adding model: " + model);
+
+ byte[] data = c.getBlob(c.getColumnIndex(SoundModelContract.KEY_DATA));
+ int recognitionModes = c.getInt(
+ c.getColumnIndex(SoundModelContract.KEY_RECOGNITION_MODES));
+ int[] users = getArrayForCommaSeparatedString(
+ c.getString(c.getColumnIndex(SoundModelContract.KEY_USERS)));
+ String locale = c.getString(c.getColumnIndex(SoundModelContract.KEY_LOCALE));
+ String text = c.getString(
+ c.getColumnIndex(SoundModelContract.KEY_HINT_TEXT));
+
+ // Only add keyphrases meant for the current user.
+ if (users == null) {
+ // No users present in the keyphrase.
+ Slog.w(TAG, "Ignoring keyphrase since it doesn't specify users");
+ return null;
}
- models.add(model);
- } while (c.moveToNext());
+ boolean isAvailableForCurrentUser = false;
+ int currentUser = mUserManager.getUserHandle();
+ for (int user : users) {
+ if (currentUser == user) {
+ isAvailableForCurrentUser = true;
+ break;
+ }
+ }
+ if (!isAvailableForCurrentUser) {
+ Slog.w(TAG, "Ignoring keyphrase since it's not for the current user");
+ return null;
+ }
+
+ Keyphrase[] keyphrases = new Keyphrase[1];
+ keyphrases[0] = new Keyphrase(
+ keyphraseId, recognitionModes, locale, text, users);
+ return new KeyphraseSoundModel(UUID.fromString(modelUuid), data, keyphrases);
+ }
+ Slog.w(TAG, "No SoundModel available for the given keyphrase");
+ } finally {
+ c.close();
+ db.close();
}
- c.close();
- db.close();
- return models;
+ return null;
}
}
- private Keyphrase[] getKeyphrasesForSoundModelLocked(SQLiteDatabase db, String modelId) {
- List<Keyphrase> keyphrases = new ArrayList<>();
- String selectQuery = "SELECT * FROM " + KeyphraseContract.TABLE
- + " WHERE " + KeyphraseContract.KEY_SOUND_MODEL_ID + " = '" + modelId + "'";
- 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[] 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));
-
- // Only add keyphrases meant for the current user.
- if (users == null) {
- // No users present in the keyphrase.
- Slog.w(TAG, "Ignoring keyphrase since it doesn't specify users");
- continue;
- }
- boolean isAvailableForCurrentUser = false;
- int currentUser = mUserManager.getUserHandle();
- for (int user : users) {
- if (currentUser == user) {
- isAvailableForCurrentUser = true;
- break;
- }
- }
- if (!isAvailableForCurrentUser) {
- Slog.w(TAG, "Ignoring keyphrase since it's not for the current user");
- continue;
- }
-
- keyphrases.add(new Keyphrase(id, modes, locale, hintText, users));
- } while (c.moveToNext());
- }
- Keyphrase[] keyphraseArr = new Keyphrase[keyphrases.size()];
- keyphrases.toArray(keyphraseArr);
- c.close();
- return keyphraseArr;
- }
-
-
private static String getCommaSeparatedString(int[] users) {
if (users == null || users.length == 0) {
return "";
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerHelper.java
index 4430586..86dca79 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerHelper.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerHelper.java
@@ -38,7 +38,8 @@
*/
public class SoundTriggerHelper implements SoundTrigger.StatusListener {
static final String TAG = "SoundTriggerHelper";
- static final boolean DBG = false;
+ // TODO: Set to false.
+ static final boolean DBG = true;
// TODO: Remove this.
static final int TEMP_KEYPHRASE_ID = 100;
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 5d9e107..a3d578a 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -50,6 +50,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.List;
+import java.util.UUID;
/**
@@ -243,34 +244,18 @@
//----------------- Model management APIs --------------------------------//
@Override
- public List<KeyphraseSoundModel> listRegisteredKeyphraseSoundModels(
- IVoiceInteractionService service) {
- // Allow the call if this is the current voice interaction service
- // or the caller holds the MANAGE_VOICE_KEYPHRASES permission.
+ public KeyphraseSoundModel getKeyphraseSoundModel(int keyphraseId) {
synchronized (this) {
- boolean permissionGranted =
- mContext.checkCallingPermission(Manifest.permission.MANAGE_VOICE_KEYPHRASES)
- == PackageManager.PERMISSION_GRANTED;
- boolean currentVoiceInteractionService = service != null
- && mImpl != null
- && mImpl.mService != null
- && service.asBinder() == mImpl.mService.asBinder();
-
- if (!permissionGranted && !currentVoiceInteractionService) {
- if (!currentVoiceInteractionService) {
- throw new SecurityException(
- "Caller is not the current voice interaction service");
- }
- if (!permissionGranted) {
- throw new SecurityException("Caller does not hold the permission "
- + Manifest.permission.MANAGE_VOICE_KEYPHRASES);
- }
+ if (mContext.checkCallingPermission(Manifest.permission.MANAGE_VOICE_KEYPHRASES)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Caller does not hold the permission "
+ + Manifest.permission.MANAGE_VOICE_KEYPHRASES);
}
}
final long caller = Binder.clearCallingIdentity();
try {
- return mDbHelper.getKephraseSoundModels();
+ return mDbHelper.getKeyphraseSoundModel(keyphraseId);
} finally {
Binder.restoreCallingIdentity(caller);
}
@@ -291,15 +276,35 @@
final long caller = Binder.clearCallingIdentity();
try {
- boolean success = false;
- if (model.keyphrases == null) {
- // If the keyphrases are not present in the model, delete the model.
- success = mDbHelper.deleteKeyphraseSoundModel(model.uuid);
+ if (mDbHelper.updateKeyphraseSoundModel(model)) {
+ synchronized (this) {
+ // Notify the voice interaction service of a change in sound models.
+ if (mImpl != null && mImpl.mService != null) {
+ mImpl.notifySoundModelsChangedLocked();
+ }
+ }
+ return SoundTriggerHelper.STATUS_OK;
} else {
- // Else update the model.
- success = mDbHelper.addOrUpdateKeyphraseSoundModel(model);
+ return SoundTriggerHelper.STATUS_ERROR;
}
- if (success) {
+ } finally {
+ Binder.restoreCallingIdentity(caller);
+ }
+ }
+
+ @Override
+ public int deleteKeyphraseSoundModel(int keyphraseId) {
+ synchronized (this) {
+ if (mContext.checkCallingPermission(Manifest.permission.MANAGE_VOICE_KEYPHRASES)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Caller does not hold the permission "
+ + Manifest.permission.MANAGE_VOICE_KEYPHRASES);
+ }
+ }
+
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ if (mDbHelper.deleteKeyphraseSoundModel(keyphraseId)) {
synchronized (this) {
// Notify the voice interaction service of a change in sound models.
if (mImpl != null && mImpl.mService != null) {
@@ -317,6 +322,25 @@
//----------------- SoundTrigger APIs --------------------------------//
@Override
+ public boolean isEnrolledForKeyphrase(IVoiceInteractionService service, int keyphraseId) {
+ synchronized (this) {
+ if (mImpl == null || mImpl.mService == null
+ || service.asBinder() != mImpl.mService.asBinder()) {
+ throw new SecurityException(
+ "Caller is not the current voice interaction service");
+ }
+ }
+
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ KeyphraseSoundModel model = mDbHelper.getKeyphraseSoundModel(keyphraseId);
+ return model != null;
+ } finally {
+ Binder.restoreCallingIdentity(caller);
+ }
+ }
+
+ @Override
public ModuleProperties getDspModuleProperties(IVoiceInteractionService service) {
// Allow the call if this is the current voice interaction service.
synchronized (this) {
@@ -337,8 +361,7 @@
@Override
public int startRecognition(IVoiceInteractionService service, int keyphraseId,
- KeyphraseSoundModel soundModel, IRecognitionStatusCallback callback,
- RecognitionConfig recognitionConfig) {
+ IRecognitionStatusCallback callback, RecognitionConfig recognitionConfig) {
// Allow the call if this is the current voice interaction service.
synchronized (this) {
if (mImpl == null || mImpl.mService == null
@@ -347,13 +370,25 @@
"Caller is not the current voice interaction service");
}
- final long caller = Binder.clearCallingIdentity();
- try {
+ if (callback == null || recognitionConfig == null) {
+ throw new IllegalArgumentException("Illegal argument(s) in startRecognition");
+ }
+ }
+
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ KeyphraseSoundModel soundModel = mDbHelper.getKeyphraseSoundModel(keyphraseId);
+ if (soundModel == null
+ || soundModel.uuid == null
+ || soundModel.keyphrases == null) {
+ Slog.w(TAG, "No matching sound model found in startRecognition");
+ return SoundTriggerHelper.STATUS_ERROR;
+ } else {
return mSoundTriggerHelper.startRecognition(
keyphraseId, soundModel, callback, recognitionConfig);
- } finally {
- Binder.restoreCallingIdentity(caller);
}
+ } finally {
+ Binder.restoreCallingIdentity(caller);
}
}
@@ -367,13 +402,13 @@
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);
- }
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ return mSoundTriggerHelper.stopRecognition(keyphraseId, callback);
+ } finally {
+ Binder.restoreCallingIdentity(caller);
}
}