summaryrefslogtreecommitdiff
path: root/services/voiceinteraction
diff options
context:
space:
mode:
authorNicholas Ambur <nambur@google.com>2020-01-14 20:35:04 -0800
committerNicholas Ambur <nambur@google.com>2020-01-22 16:40:18 -0800
commitf771e6c9f694c772dd261aacbc0c566e587c345f (patch)
tree49c85da089db57e2b0aa547d1f3326a4fcbbfb5d /services/voiceinteraction
parentef84fc48433d47ea9c91dcb3273ae3d74ca6d32a (diff)
async enrollment support AlwaysOnHotwordDetector
Added ability for AlwaysOnHotwordDetector to support async enrollment performed outside of support detected through KeyphraseEnrollmentInfo. Bug: 147159435 Test: tested enrollment and availability is updated when enrolling outside of KeyphraseEnrollmentInfo Change-Id: Ia5d71e90c062ac100d4c6df760acf0d41920853e
Diffstat (limited to 'services/voiceinteraction')
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java175
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java38
2 files changed, 139 insertions, 74 deletions
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
index 195a9e49d70d..af81ab6339f3 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
@@ -194,7 +194,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {
* Deletes the sound model and associated keyphrases.
*/
public boolean deleteKeyphraseSoundModel(int keyphraseId, int userHandle, String bcp47Locale) {
- // Sanitize the locale to guard against SQL injection.
+ // Normalize the locale to guard against SQL injection.
bcp47Locale = Locale.forLanguageTag(bcp47Locale).toLanguageTag();
synchronized(this) {
KeyphraseSoundModel soundModel = getKeyphraseSoundModel(keyphraseId, userHandle,
@@ -230,90 +230,117 @@ public class DatabaseHelper extends SQLiteOpenHelper {
String selectQuery = "SELECT * FROM " + SoundModelContract.TABLE
+ " WHERE " + SoundModelContract.KEY_KEYPHRASE_ID + "= '" + keyphraseId
+ "' AND " + SoundModelContract.KEY_LOCALE + "='" + bcp47Locale + "'";
- SQLiteDatabase db = getReadableDatabase();
- Cursor c = db.rawQuery(selectQuery, null);
+ return getValidKeyphraseSoundModelForUser(selectQuery, userHandle);
+ }
+ }
- try {
- if (c.moveToFirst()) {
- do {
- int type = c.getInt(c.getColumnIndex(SoundModelContract.KEY_TYPE));
- if (type != SoundTrigger.SoundModel.TYPE_KEYPHRASE) {
- if (DBG) {
- Slog.w(TAG, "Ignoring SoundModel since it's type is incorrect");
- }
- continue;
- }
+ /**
+ * Returns a matching {@link KeyphraseSoundModel} for the keyphrase string.
+ * Returns null if a match isn't found.
+ *
+ * TODO: We only support one keyphrase currently.
+ */
+ public KeyphraseSoundModel getKeyphraseSoundModel(String keyphrase, int userHandle,
+ String bcp47Locale) {
+ // Sanitize the locale to guard against SQL injection.
+ bcp47Locale = Locale.forLanguageTag(bcp47Locale).toLanguageTag();
+ synchronized (this) {
+ // Find the corresponding sound model ID for the keyphrase.
+ String selectQuery = "SELECT * FROM " + SoundModelContract.TABLE
+ + " WHERE " + SoundModelContract.KEY_HINT_TEXT + "= '" + keyphrase
+ + "' AND " + SoundModelContract.KEY_LOCALE + "='" + bcp47Locale + "'";
+ return getValidKeyphraseSoundModelForUser(selectQuery, userHandle);
+ }
+ }
- String modelUuid = c.getString(
- c.getColumnIndex(SoundModelContract.KEY_MODEL_UUID));
- if (modelUuid == null) {
- Slog.w(TAG, "Ignoring SoundModel since it doesn't specify an ID");
- continue;
- }
+ private KeyphraseSoundModel getValidKeyphraseSoundModelForUser(String selectQuery,
+ int userHandle) {
+ SQLiteDatabase db = getReadableDatabase();
+ Cursor c = db.rawQuery(selectQuery, null);
- String vendorUuidString = null;
- int vendorUuidColumn = c.getColumnIndex(SoundModelContract.KEY_VENDOR_UUID);
- if (vendorUuidColumn != -1) {
- vendorUuidString = c.getString(vendorUuidColumn);
- }
- 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)));
- Locale modelLocale = Locale.forLanguageTag(c.getString(
- c.getColumnIndex(SoundModelContract.KEY_LOCALE)));
- String text = c.getString(
- c.getColumnIndex(SoundModelContract.KEY_HINT_TEXT));
- int version = c.getInt(
- c.getColumnIndex(SoundModelContract.KEY_MODEL_VERSION));
-
- // Only add keyphrases meant for the current user.
- if (users == null) {
- // No users present in the keyphrase.
- Slog.w(TAG, "Ignoring SoundModel since it doesn't specify users");
- continue;
+ try {
+ if (c.moveToFirst()) {
+ do {
+ int type = c.getInt(c.getColumnIndex(SoundModelContract.KEY_TYPE));
+ if (type != SoundTrigger.SoundModel.TYPE_KEYPHRASE) {
+ if (DBG) {
+ Slog.w(TAG, "Ignoring SoundModel since its type is incorrect");
}
+ continue;
+ }
- boolean isAvailableForCurrentUser = false;
- for (int user : users) {
- if (userHandle == user) {
- isAvailableForCurrentUser = true;
- break;
- }
- }
- if (!isAvailableForCurrentUser) {
- if (DBG) {
- Slog.w(TAG, "Ignoring SoundModel since user handles don't match");
- }
- continue;
- } else {
- if (DBG) Slog.d(TAG, "Found a SoundModel for user: " + userHandle);
- }
+ String modelUuid = c.getString(
+ c.getColumnIndex(SoundModelContract.KEY_MODEL_UUID));
+ if (modelUuid == null) {
+ Slog.w(TAG, "Ignoring SoundModel since it doesn't specify an ID");
+ continue;
+ }
+
+ String vendorUuidString = null;
+ int vendorUuidColumn = c.getColumnIndex(SoundModelContract.KEY_VENDOR_UUID);
+ if (vendorUuidColumn != -1) {
+ vendorUuidString = c.getString(vendorUuidColumn);
+ }
+ int keyphraseId = c.getInt(
+ c.getColumnIndex(SoundModelContract.KEY_KEYPHRASE_ID));
+ 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)));
+ Locale modelLocale = Locale.forLanguageTag(c.getString(
+ c.getColumnIndex(SoundModelContract.KEY_LOCALE)));
+ String text = c.getString(
+ c.getColumnIndex(SoundModelContract.KEY_HINT_TEXT));
+ int version = c.getInt(
+ c.getColumnIndex(SoundModelContract.KEY_MODEL_VERSION));
+
+ // Only add keyphrases meant for the current user.
+ if (users == null) {
+ // No users present in the keyphrase.
+ Slog.w(TAG, "Ignoring SoundModel since it doesn't specify users");
+ continue;
+ }
- Keyphrase[] keyphrases = new Keyphrase[1];
- keyphrases[0] = new Keyphrase(
- keyphraseId, recognitionModes, modelLocale, text, users);
- UUID vendorUuid = null;
- if (vendorUuidString != null) {
- vendorUuid = UUID.fromString(vendorUuidString);
+ boolean isAvailableForCurrentUser = false;
+ for (int user : users) {
+ if (userHandle == user) {
+ isAvailableForCurrentUser = true;
+ break;
}
- KeyphraseSoundModel model = new KeyphraseSoundModel(
- UUID.fromString(modelUuid), vendorUuid, data, keyphrases, version);
+ }
+ if (!isAvailableForCurrentUser) {
if (DBG) {
- Slog.d(TAG, "Found SoundModel for the given keyphrase/locale/user: "
- + model);
+ Slog.w(TAG, "Ignoring SoundModel since user handles don't match");
}
- return model;
- } while (c.moveToNext());
- }
- Slog.w(TAG, "No SoundModel available for the given keyphrase");
- } finally {
- c.close();
- db.close();
+ continue;
+ } else {
+ if (DBG) Slog.d(TAG, "Found a SoundModel for user: " + userHandle);
+ }
+
+ Keyphrase[] keyphrases = new Keyphrase[1];
+ keyphrases[0] = new Keyphrase(
+ keyphraseId, recognitionModes, modelLocale, text, users);
+ UUID vendorUuid = null;
+ if (vendorUuidString != null) {
+ vendorUuid = UUID.fromString(vendorUuidString);
+ }
+ KeyphraseSoundModel model = new KeyphraseSoundModel(
+ UUID.fromString(modelUuid), vendorUuid, data, keyphrases, version);
+ if (DBG) {
+ Slog.d(TAG, "Found SoundModel for the given keyphrase/locale/user: "
+ + model);
+ }
+ return model;
+ } while (c.moveToNext());
}
- return null;
+ Slog.w(TAG, "No SoundModel available for the given keyphrase");
+ } finally {
+ c.close();
+ db.close();
}
+
+ return null;
}
private static String getCommaSeparatedString(int[] users) {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index ec0a1bacf094..d5eec332cda0 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -41,7 +41,9 @@ import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.hardware.soundtrigger.IRecognitionStatusCallback;
+import android.hardware.soundtrigger.KeyphraseMetadata;
import android.hardware.soundtrigger.ModelParams;
+import android.hardware.soundtrigger.SoundTrigger;
import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
import android.hardware.soundtrigger.SoundTrigger.ModelParamRange;
import android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
@@ -90,6 +92,7 @@ import com.android.server.wm.ActivityTaskManagerInternal;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.List;
+import java.util.Locale;
import java.util.Objects;
import java.util.concurrent.Executor;
@@ -1024,6 +1027,41 @@ public class VoiceInteractionManagerService extends SystemService {
}
}
+ @Nullable
+ public KeyphraseMetadata getEnrolledKeyphraseMetadata(IVoiceInteractionService service,
+ String keyphrase, String bcp47Locale) {
+ synchronized (this) {
+ enforceIsCurrentVoiceInteractionService(service);
+ }
+
+ if (bcp47Locale == null) {
+ throw new IllegalArgumentException("Illegal argument(s) in isEnrolledForKeyphrase");
+ }
+
+ final int callingUid = UserHandle.getCallingUserId();
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ KeyphraseSoundModel model =
+ mDbHelper.getKeyphraseSoundModel(keyphrase, callingUid, bcp47Locale);
+ if (model == null) {
+ return null;
+ }
+
+ for (SoundTrigger.Keyphrase phrase : model.keyphrases) {
+ if (keyphrase.equals(phrase.text)) {
+ ArraySet<Locale> locales = new ArraySet<>();
+ locales.add(phrase.locale);
+ return new KeyphraseMetadata(phrase.id, phrase.text, locales,
+ phrase.recognitionModes);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(caller);
+ }
+
+ return null;
+ }
+
@Override
public ModuleProperties getDspModuleProperties(IVoiceInteractionService service) {
// Allow the call if this is the current voice interaction service.