diff options
author | Sandeep Siddhartha <sansid@google.com> | 2014-05-29 17:54:06 -0700 |
---|---|---|
committer | Sandeep Siddhartha <sansid@google.com> | 2014-06-11 15:33:58 -0700 |
commit | e5706787599f8397e19e66afa70753ba350cc91b (patch) | |
tree | 09cc5d27deef27d61f4a9b797f4c913c109c2553 | |
parent | 6123315ad41b268bb2de52b2008885bf00408b5e (diff) | |
download | frameworks_base-e5706787599f8397e19e66afa70753ba350cc91b.zip frameworks_base-e5706787599f8397e19e66afa70753ba350cc91b.tar.gz frameworks_base-e5706787599f8397e19e66afa70753ba350cc91b.tar.bz2 |
Initial support for reading the enrollment metadata
- Defines schema for enrollment apps to publish metadata for the search
keyphrase. This metadata consists of an ID, a user visible keyphrase,
and the target package to handle the keyphrase, when it triggers.
- Reads the metadata and populates the KeyphraseInfo object for access via
the VoiceInteractionService
- Adds permission and intent action for the enrollment app.
The enrollment app needs to be a system app protected with the
MANAGE_VOICE_KEYPHRASES permission and the activity performing the
enrollment needs to handle the ACTION_MANAGE_VOICE_KEYPHRASES
- The keyphrase info currently stores an ID - that's for internal
bookkeeping only, a keyphrase text - that's used by the public APIs,
and a list of supported locales, which isn't exposed but is used while
indicating if hardware hotword is available for a particular keyphrase
in a language or not.
Change-Id: Ibe6c52a5a3eecfd74c4a8382713a35eb88d38df9
-rw-r--r-- | core/java/android/service/voice/KeyphraseEnrollmentInfo.java | 167 | ||||
-rw-r--r-- | core/java/android/service/voice/KeyphraseInfo.java | 19 | ||||
-rw-r--r-- | core/java/android/service/voice/VoiceInteractionService.java | 1 | ||||
-rw-r--r-- | core/res/AndroidManifest.xml | 8 | ||||
-rw-r--r-- | core/res/res/values/attrs.xml | 10 | ||||
-rw-r--r-- | core/res/res/values/strings.xml | 6 |
6 files changed, 211 insertions, 0 deletions
diff --git a/core/java/android/service/voice/KeyphraseEnrollmentInfo.java b/core/java/android/service/voice/KeyphraseEnrollmentInfo.java new file mode 100644 index 0000000..41b8613 --- /dev/null +++ b/core/java/android/service/voice/KeyphraseEnrollmentInfo.java @@ -0,0 +1,167 @@ +/* + * 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 android.service.voice; + +import android.Manifest; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.content.res.XmlResourceParser; +import android.util.AttributeSet; +import android.util.Log; +import android.util.Xml; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.util.List; + +/** @hide */ +public class KeyphraseEnrollmentInfo { + private static final String TAG = "KeyphraseEnrollmentInfo"; + /** + * Name under which a Hotword enrollment component publishes information about itself. + * This meta-data should reference an XML resource containing a + * <code><{@link + * android.R.styleable#VoiceEnrollmentApplication + * voice-enrollment-application}></code> tag. + */ + private static final String VOICE_KEYPHRASE_META_DATA = "android.voice_enrollment"; + + /** + * Activity Action: Show activity for managing the keyphrases for hotword detection. + * This needs to be defined by an activity that supports enrolling users for hotword/keyphrase + * detection. + */ + public static final String ACTION_MANAGE_VOICE_KEYPHRASES = + "com.android.intent.action.MANAGE_VOICE_KEYPHRASES"; + + private KeyphraseInfo[] mKeyphrases; + private String mParseError; + + public KeyphraseEnrollmentInfo(PackageManager pm) { + // Find the apps that supports enrollment for hotword keyhphrases, + // Pick a privileged app and obtain the information about the supported keyphrases + // from its metadata. + List<ResolveInfo> ris = pm.queryIntentActivities( + new Intent(ACTION_MANAGE_VOICE_KEYPHRASES), PackageManager.MATCH_DEFAULT_ONLY); + if (ris == null || ris.isEmpty()) { + // No application capable of enrolling for voice keyphrases is present. + mParseError = "No enrollment application found"; + return; + } + + boolean found = false; + ApplicationInfo ai = null; + for (ResolveInfo ri : ris) { + try { + ai = pm.getApplicationInfo( + ri.activityInfo.packageName, PackageManager.GET_META_DATA); + if ((ai.flags & ApplicationInfo.FLAG_PRIVILEGED) == 0) { + // The application isn't privileged (/system/priv-app). + // The enrollment application needs to be a privileged system app. + continue; + } + if (!Manifest.permission.MANAGE_VOICE_KEYPHRASES.equals(ai.permission)) { + // The application trying to manage keyphrases doesn't + // require the MANAGE_VOICE_KEYPHRASES permission. + continue; + } + found = true; + break; + } catch (PackageManager.NameNotFoundException e) { + Log.w(TAG, "error parsing voice enrollment meta-data", e); + } + } + + if (!found) { + mParseError = "No suitable enrollment application found"; + return; + } + + XmlResourceParser parser = null; + try { + parser = ai.loadXmlMetaData(pm, VOICE_KEYPHRASE_META_DATA); + if (parser == null) { + mParseError = "No " + VOICE_KEYPHRASE_META_DATA + " meta-data for " + + ai.packageName; + return; + } + + Resources res = pm.getResourcesForApplication(ai); + AttributeSet attrs = Xml.asAttributeSet(parser); + + int type; + while ((type=parser.next()) != XmlPullParser.END_DOCUMENT + && type != XmlPullParser.START_TAG) { + } + + String nodeName = parser.getName(); + if (!"voice-enrollment-application".equals(nodeName)) { + mParseError = "Meta-data does not start with voice-enrollment-application tag"; + return; + } + + 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); + 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(","); + } + mKeyphrases = new KeyphraseInfo[1]; + mKeyphrases[0] = new KeyphraseInfo( + searchKeyphraseId, searchKeyphrase, supportedLocales); + } else { + mParseError = "searchKeyphraseId not specified in meta-data"; + return; + } + } catch (XmlPullParserException e) { + mParseError = "Error parsing keyphrase enrollment meta-data: " + e; + Log.w(TAG, "error parsing keyphrase enrollment meta-data", e); + return; + } catch (IOException e) { + mParseError = "Error parsing keyphrase enrollment meta-data: " + e; + Log.w(TAG, "error parsing keyphrase enrollment meta-data", e); + return; + } catch (PackageManager.NameNotFoundException e) { + mParseError = "Error parsing keyphrase enrollment meta-data: " + e; + Log.w(TAG, "error parsing keyphrase enrollment meta-data", e); + return; + } finally { + if (parser != null) parser.close(); + } + } + + public String getParseError() { + return mParseError; + } +} diff --git a/core/java/android/service/voice/KeyphraseInfo.java b/core/java/android/service/voice/KeyphraseInfo.java new file mode 100644 index 0000000..55734e8 --- /dev/null +++ b/core/java/android/service/voice/KeyphraseInfo.java @@ -0,0 +1,19 @@ +package android.service.voice; + +import android.util.ArraySet; + +/** @hide */ +public class KeyphraseInfo { + public final int id; + public final String keyphrase; + public final ArraySet<String> supportedLocales; + + public KeyphraseInfo(int id, String keyphrase, String[] supportedLocales) { + this.id = id; + this.keyphrase = keyphrase; + this.supportedLocales = new ArraySet<String>(supportedLocales.length); + for (String locale : supportedLocales) { + this.supportedLocales.add(locale); + } + } +} diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java index e15489b..2154719 100644 --- a/core/java/android/service/voice/VoiceInteractionService.java +++ b/core/java/android/service/voice/VoiceInteractionService.java @@ -25,6 +25,7 @@ import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; + import com.android.internal.app.IVoiceInteractionManagerService; /** diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 9e5068f..44c41b8 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2070,6 +2070,14 @@ android:description="@string/permdesc_bindVoiceInteraction" android:protectionLevel="signature" /> + <!-- Must be required by hotword enrollment application, + to ensure that only the system can interact with it. + @hide <p>Not for use by third-party applications.</p> --> + <permission android:name="android.permission.MANAGE_VOICE_KEYPHRASES" + android:label="@string/permlab_manageVoiceKeyphrases" + android:description="@string/permdesc_manageVoiceKeyphrases" + android:protectionLevel="signature|system" /> + <!-- Must be required by a {@link com.android.media.remotedisplay.RemoteDisplayProvider}, to ensure that only the system can bind to it. @hide --> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index a751906..1f6cd91 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -6394,6 +6394,16 @@ <attr name="settingsActivity" /> </declare-styleable> + <!-- Use <code>voice-enrollment-application</code> + as the root tag of the XML resource that escribes the supported keyphrases (hotwords) + by the enrollment application. + Described here are the attributes that can be included in that tag. --> + <declare-styleable name="VoiceEnrollmentApplication"> + <attr name="searchKeyphraseId" format="integer" /> + <attr name="searchKeyphrase" format="string" /> + <attr name="searchKeyphraseSupportedLocales" format="string" /> + </declare-styleable> + <!-- Attributes used to style the Action Bar. --> <declare-styleable name="ActionBar"> <!-- The type of navigation to use. --> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 391a32c..8a422be 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1122,6 +1122,12 @@ interface of a voice interaction service. Should never be needed for normal apps.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permlab_manageVoiceKeyphrases">manage voice keyphrases</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_manageVoiceKeyphrases">Allows the holder to manage the keyphrases for voice hotword detection. + Should never be needed for normal apps.</string> + + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_bindRemoteDisplay">bind to a remote display</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permdesc_bindRemoteDisplay">Allows the holder to bind to the top-level |