diff options
author | Nicholas Ambur <nambur@google.com> | 2020-01-14 20:35:04 -0800 |
---|---|---|
committer | Nicholas Ambur <nambur@google.com> | 2020-01-22 16:40:18 -0800 |
commit | f771e6c9f694c772dd261aacbc0c566e587c345f (patch) | |
tree | 49c85da089db57e2b0aa547d1f3326a4fcbbfb5d /services/voiceinteraction | |
parent | ef84fc48433d47ea9c91dcb3273ae3d74ca6d32a (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')
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. |