summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSandeep Siddhartha <sansid@google.com>2014-08-23 06:19:01 +0000
committerAndroid Git Automerger <android-git-automerger@android.com>2014-08-23 06:19:01 +0000
commit173db08cce9ea4f8c654f67a7c6394dce33a2bcd (patch)
treee9a38c43798dff5f5a5b9b83edbdcc81f284dcf5
parentc52b5b1fc1d0c09883dc6886bc03548ba5bea2be (diff)
parent960c98e2aa83f51032e30fdae9b5c1afb5dcbe02 (diff)
downloadframeworks_base-173db08cce9ea4f8c654f67a7c6394dce33a2bcd.zip
frameworks_base-173db08cce9ea4f8c654f67a7c6394dce33a2bcd.tar.gz
frameworks_base-173db08cce9ea4f8c654f67a7c6394dce33a2bcd.tar.bz2
am a8d8e4c4: am e829259d: Merge "Fix the Locale story in the hotword API" into lmp-dev
* commit 'a8d8e4c439da6a3b9292335d631c6ec65ff45b01': Fix the Locale story in the hotword API
-rw-r--r--api/current.txt2
-rw-r--r--core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java96
-rw-r--r--core/java/android/hardware/soundtrigger/KeyphraseMetadata.java13
-rw-r--r--core/java/android/service/voice/AlwaysOnHotwordDetector.java15
-rw-r--r--core/java/android/service/voice/VoiceInteractionService.java17
-rw-r--r--core/res/res/values/attrs.xml4
-rw-r--r--tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java4
7 files changed, 102 insertions, 49 deletions
diff --git a/api/current.txt b/api/current.txt
index fc39cba..a592b0e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -27464,7 +27464,7 @@ package android.service.voice {
public class VoiceInteractionService extends android.app.Service {
ctor public VoiceInteractionService();
- method public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(java.lang.String, java.lang.String, android.service.voice.AlwaysOnHotwordDetector.Callback);
+ method public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(java.lang.String, java.util.Locale, android.service.voice.AlwaysOnHotwordDetector.Callback);
method public static boolean isActiveService(android.content.Context, android.content.ComponentName);
method public android.os.IBinder onBind(android.content.Intent);
method public void onReady();
diff --git a/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java b/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java
index 0dbde6b..2d5a271 100644
--- a/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java
+++ b/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java
@@ -25,6 +25,8 @@ import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.service.voice.AlwaysOnHotwordDetector;
+import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.Slog;
import android.util.Xml;
@@ -35,6 +37,7 @@ import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
+import java.util.Locale;
/**
* Enrollment information about the different available keyphrases.
@@ -151,33 +154,8 @@ public class KeyphraseEnrollmentInfo {
TypedArray array = res.obtainAttributes(attrs,
com.android.internal.R.styleable.VoiceEnrollmentApplication);
- int searchKeyphraseId = array.getInt(
- com.android.internal.R.styleable.VoiceEnrollmentApplication_searchKeyphraseId,
- -1);
- if (searchKeyphraseId != -1) {
- String searchKeyphrase = array.getString(com.android.internal.R.styleable
- .VoiceEnrollmentApplication_searchKeyphrase);
- if (searchKeyphrase == null) {
- searchKeyphrase = "";
- }
- String searchKeyphraseSupportedLocales =
- array.getString(com.android.internal.R.styleable
- .VoiceEnrollmentApplication_searchKeyphraseSupportedLocales);
- String[] supportedLocales = new String[0];
- // Get all the supported locales from the comma-delimted string.
- if (searchKeyphraseSupportedLocales != null
- && !searchKeyphraseSupportedLocales.isEmpty()) {
- supportedLocales = searchKeyphraseSupportedLocales.split(",");
- }
- int recognitionModes = array.getInt(com.android.internal.R.styleable
- .VoiceEnrollmentApplication_searchKeyphraseRecognitionFlags, 0);
- mKeyphrases = new KeyphraseMetadata[1];
- mKeyphrases[0] = new KeyphraseMetadata(
- searchKeyphraseId, searchKeyphrase, supportedLocales, recognitionModes);
- } else {
- mParseError = "searchKeyphraseId not specified in meta-data";
- return;
- }
+ initializeKeyphrasesFromTypedArray(array);
+ array.recycle();
} catch (XmlPullParserException e) {
mParseError = "Error parsing keyphrase enrollment meta-data: " + e;
Slog.w(TAG, "error parsing keyphrase enrollment meta-data", e);
@@ -195,6 +173,65 @@ public class KeyphraseEnrollmentInfo {
}
}
+ private void initializeKeyphrasesFromTypedArray(TypedArray array) {
+ // Get the keyphrase ID.
+ int searchKeyphraseId = array.getInt(
+ com.android.internal.R.styleable.VoiceEnrollmentApplication_searchKeyphraseId, -1);
+ if (searchKeyphraseId <= 0) {
+ mParseError = "No valid searchKeyphraseId specified in meta-data";
+ Slog.w(TAG, mParseError);
+ return;
+ }
+
+ // Get the keyphrase text.
+ String searchKeyphrase = array.getString(
+ com.android.internal.R.styleable.VoiceEnrollmentApplication_searchKeyphrase);
+ if (searchKeyphrase == null) {
+ mParseError = "No valid searchKeyphrase specified in meta-data";
+ Slog.w(TAG, mParseError);
+ return;
+ }
+
+ // Get the supported locales.
+ String searchKeyphraseSupportedLocales = array.getString(
+ com.android.internal.R.styleable
+ .VoiceEnrollmentApplication_searchKeyphraseSupportedLocales);
+ if (searchKeyphraseSupportedLocales == null) {
+ mParseError = "No valid searchKeyphraseSupportedLocales specified in meta-data";
+ Slog.w(TAG, mParseError);
+ return;
+ }
+ ArraySet<Locale> locales = new ArraySet<>();
+ // Try adding locales if the locale string is non-empty.
+ if (!TextUtils.isEmpty(searchKeyphraseSupportedLocales)) {
+ try {
+ String[] supportedLocalesDelimited = searchKeyphraseSupportedLocales.split(",");
+ for (int i = 0; i < supportedLocalesDelimited.length; i++) {
+ locales.add(Locale.forLanguageTag(supportedLocalesDelimited[i]));
+ }
+ } catch (Exception ex) {
+ // We catch a generic exception here because we don't want the system service
+ // to be affected by a malformed metadata because invalid locales were specified
+ // by the system application.
+ mParseError = "Error reading searchKeyphraseSupportedLocales from meta-data";
+ Slog.w(TAG, mParseError, ex);
+ return;
+ }
+ }
+
+ // Get the supported recognition modes.
+ int recognitionModes = array.getInt(com.android.internal.R.styleable
+ .VoiceEnrollmentApplication_searchKeyphraseRecognitionFlags, -1);
+ if (recognitionModes < 0) {
+ mParseError = "No valid searchKeyphraseRecognitionFlags specified in meta-data";
+ Slog.w(TAG, mParseError);
+ return;
+ }
+ mKeyphrases = new KeyphraseMetadata[1];
+ mKeyphrases[0] = new KeyphraseMetadata(searchKeyphraseId, searchKeyphrase, locales,
+ recognitionModes);
+ }
+
public String getParseError() {
return mParseError;
}
@@ -217,11 +254,10 @@ public class KeyphraseEnrollmentInfo {
* or {@link AlwaysOnHotwordDetector#MANAGE_ACTION_UN_ENROLL}
* @param keyphrase The keyphrase that the user needs to be enrolled to.
* @param locale The locale for which the enrollment needs to be performed.
- * This is a Java locale, for example "en_US".
* @return An {@link Intent} to manage the keyphrase. This can be null if managing the
* given keyphrase/locale combination isn't possible.
*/
- public Intent getManageKeyphraseIntent(int action, String keyphrase, String locale) {
+ public Intent getManageKeyphraseIntent(int action, String keyphrase, Locale locale) {
if (mEnrollmentPackage == null || mEnrollmentPackage.isEmpty()) {
Slog.w(TAG, "No enrollment application exists");
return null;
@@ -248,7 +284,7 @@ public class KeyphraseEnrollmentInfo {
* @return The metadata, if the enrollment client supports the given keyphrase
* and locale, null otherwise.
*/
- public KeyphraseMetadata getKeyphraseMetadata(String keyphrase, String locale) {
+ public KeyphraseMetadata getKeyphraseMetadata(String keyphrase, Locale locale) {
if (mKeyphrases == null || mKeyphrases.length == 0) {
Slog.w(TAG, "Enrollment application doesn't support keyphrases");
return null;
diff --git a/core/java/android/hardware/soundtrigger/KeyphraseMetadata.java b/core/java/android/hardware/soundtrigger/KeyphraseMetadata.java
index 38305f9..ed8c296 100644
--- a/core/java/android/hardware/soundtrigger/KeyphraseMetadata.java
+++ b/core/java/android/hardware/soundtrigger/KeyphraseMetadata.java
@@ -18,6 +18,8 @@ package android.hardware.soundtrigger;
import android.util.ArraySet;
+import java.util.Locale;
+
/**
* A Voice Keyphrase metadata read from the enrollment application.
*
@@ -26,17 +28,14 @@ import android.util.ArraySet;
public class KeyphraseMetadata {
public final int id;
public final String keyphrase;
- public final ArraySet<String> supportedLocales;
+ public final ArraySet<Locale> supportedLocales;
public final int recognitionModeFlags;
- public KeyphraseMetadata(int id, String keyphrase, String[] supportedLocales,
+ public KeyphraseMetadata(int id, String keyphrase, ArraySet<Locale> supportedLocales,
int recognitionModeFlags) {
this.id = id;
this.keyphrase = keyphrase;
- this.supportedLocales = new ArraySet<String>(supportedLocales.length);
- for (String locale : supportedLocales) {
- this.supportedLocales.add(locale);
- }
+ this.supportedLocales = supportedLocales;
this.recognitionModeFlags = recognitionModeFlags;
}
@@ -56,7 +55,7 @@ public class KeyphraseMetadata {
/**
* @return Indicates if we support the given locale.
*/
- public boolean supportsLocale(String locale) {
+ public boolean supportsLocale(Locale locale) {
return supportedLocales.isEmpty() || supportedLocales.contains(locale);
}
}
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index 15e66a0..2095773 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -39,10 +39,10 @@ import android.util.Slog;
import com.android.internal.app.IVoiceInteractionManagerService;
-import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Locale;
/**
* A class that lets a VoiceInteractionService implementation interact with
@@ -167,7 +167,7 @@ public class AlwaysOnHotwordDetector {
private static final int MSG_DETECTION_RESUME = 5;
private final String mText;
- private final String mLocale;
+ private final Locale mLocale;
/**
* The metadata of the Keyphrase, derived from the enrollment application.
* This may be null if this keyphrase isn't supported by the enrollment application.
@@ -317,7 +317,7 @@ public class AlwaysOnHotwordDetector {
*
* @hide
*/
- public AlwaysOnHotwordDetector(String text, String locale, Callback callback,
+ public AlwaysOnHotwordDetector(String text, Locale locale, Callback callback,
KeyphraseEnrollmentInfo keyphraseEnrollmentInfo,
IVoiceInteractionService voiceInteractionService,
IVoiceInteractionManagerService modelManagementService) {
@@ -491,8 +491,6 @@ public class AlwaysOnHotwordDetector {
*/
void onSoundModelsChanged() {
synchronized (mLock) {
- // FIXME: This should stop the recognition if it was using an enrolled sound model
- // that's no longer available.
if (mAvailability == STATE_INVALID
|| mAvailability == STATE_HARDWARE_UNAVAILABLE
|| mAvailability == STATE_KEYPHRASE_UNSUPPORTED) {
@@ -500,6 +498,13 @@ public class AlwaysOnHotwordDetector {
return;
}
+ // Stop the recognition before proceeding.
+ // This is done because we want to stop the recognition on an older model if it changed
+ // or was deleted.
+ // The availability change callback should ensure that the client starts recognition
+ // again if needed.
+ stopRecognitionLocked();
+
// Execute a refresh availability task - which should then notify of a change.
new RefreshAvailabiltyTask().execute();
}
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index b200356..884fa9f 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -35,6 +35,7 @@ import com.android.internal.app.IVoiceInteractionManagerService;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.Locale;
/**
@@ -163,7 +164,7 @@ public class VoiceInteractionService extends Service {
* Called during service initialization to tell you when the system is ready
* to receive interaction from it. You should generally do initialization here
* rather than in {@link #onCreate()}. Methods such as {@link #startSession(Bundle)} and
- * {@link #createAlwaysOnHotwordDetector(String, String, android.service.voice.AlwaysOnHotwordDetector.Callback)}
+ * {@link #createAlwaysOnHotwordDetector(String, Locale, android.service.voice.AlwaysOnHotwordDetector.Callback)}
* will not be operational until this point.
*/
public void onReady() {
@@ -200,6 +201,17 @@ public class VoiceInteractionService extends Service {
}
/**
+ * FIXME: Remove once the prebuilts are updated.
+ *
+ * @hide
+ */
+ @Deprecated
+ public final AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(
+ String keyphrase, String locale, AlwaysOnHotwordDetector.Callback callback) {
+ return createAlwaysOnHotwordDetector(keyphrase, new Locale(locale), callback);
+ }
+
+ /**
* Creates an {@link AlwaysOnHotwordDetector} for the given keyphrase and locale.
* This instance must be retained and used by the client.
* Calling this a second time invalidates the previously created hotword detector
@@ -207,12 +219,11 @@ public class VoiceInteractionService extends Service {
*
* @param keyphrase The keyphrase that's being used, for example "Hello Android".
* @param locale The locale for which the enrollment needs to be performed.
- * This is a Java locale, for example "en_US".
* @param callback The callback to notify of detection events.
* @return An always-on hotword detector for the given keyphrase and locale.
*/
public final AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(
- String keyphrase, String locale, AlwaysOnHotwordDetector.Callback callback) {
+ String keyphrase, Locale locale, AlwaysOnHotwordDetector.Callback callback) {
if (mSystemService == null) {
throw new IllegalStateException("Not available until onReady() is called");
}
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 3524636..cc8d7cf 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -6930,8 +6930,8 @@
<attr name="searchKeyphraseId" format="integer" />
<!-- The actual keyphrase/hint text, or empty if not keyphrase dependent. @hide @SystemApi -->
<attr name="searchKeyphrase" format="string" />
- <!-- A comma separated list of java locales that are supported for this keyphrase,
- or empty if not locale dependent. @hide @SystemApi -->
+ <!-- A comma separated list of BCP-47 language tag for locales that are supported
+ for this keyphrase, or empty if not locale dependent. @hide @SystemApi -->
<attr name="searchKeyphraseSupportedLocales" format="string" />
<!-- Flags for supported recognition modes. @hide @SystemApi -->
<attr name="searchKeyphraseRecognitionFlags">
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java
index 77c0c32..1233c0c 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java
@@ -25,6 +25,7 @@ import android.service.voice.VoiceInteractionService;
import android.util.Log;
import java.util.Arrays;
+import java.util.Locale;
public class MainInteractionService extends VoiceInteractionService {
static final String TAG = "MainInteractionService";
@@ -67,7 +68,8 @@ public class MainInteractionService extends VoiceInteractionService {
Log.i(TAG, "Keyphrase enrollment meta-data: "
+ Arrays.toString(getKeyphraseEnrollmentInfo().listKeyphraseMetadata()));
- mHotwordDetector = createAlwaysOnHotwordDetector("Hello There", "en-US", mHotwordCallback);
+ mHotwordDetector = createAlwaysOnHotwordDetector(
+ "Hello There", new Locale("en-US"), mHotwordCallback);
}
@Override