Merge "Support multiple Enrollment APKs" into nyc-dev
am: 9e928c1
* commit '9e928c1f715c53cc9cd6a85b3dcdcdc68cabc9c6':
Support multiple Enrollment APKs
Change-Id: I66fbc18f3291a4178fdfb613c6a4e905edf5715e
This commit is contained in:
@@ -35,9 +35,11 @@ import org.xmlpull.v1.XmlPullParser;
|
|||||||
import org.xmlpull.v1.XmlPullParserException;
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enrollment information about the different available keyphrases.
|
* Enrollment information about the different available keyphrases.
|
||||||
@@ -82,8 +84,16 @@ public class KeyphraseEnrollmentInfo {
|
|||||||
public static final String EXTRA_VOICE_KEYPHRASE_LOCALE =
|
public static final String EXTRA_VOICE_KEYPHRASE_LOCALE =
|
||||||
"com.android.intent.extra.VOICE_KEYPHRASE_LOCALE";
|
"com.android.intent.extra.VOICE_KEYPHRASE_LOCALE";
|
||||||
|
|
||||||
private KeyphraseMetadata[] mKeyphrases;
|
/**
|
||||||
private String mEnrollmentPackage;
|
* List of available keyphrases.
|
||||||
|
*/
|
||||||
|
final private KeyphraseMetadata[] mKeyphrases;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map between KeyphraseMetadata and the package name of the enrollment app that provides it.
|
||||||
|
*/
|
||||||
|
final private Map<KeyphraseMetadata, String> mKeyphrasePackageMap;
|
||||||
|
|
||||||
private String mParseError;
|
private String mParseError;
|
||||||
|
|
||||||
public KeyphraseEnrollmentInfo(PackageManager pm) {
|
public KeyphraseEnrollmentInfo(PackageManager pm) {
|
||||||
@@ -94,15 +104,17 @@ public class KeyphraseEnrollmentInfo {
|
|||||||
new Intent(ACTION_MANAGE_VOICE_KEYPHRASES), PackageManager.MATCH_DEFAULT_ONLY);
|
new Intent(ACTION_MANAGE_VOICE_KEYPHRASES), PackageManager.MATCH_DEFAULT_ONLY);
|
||||||
if (ris == null || ris.isEmpty()) {
|
if (ris == null || ris.isEmpty()) {
|
||||||
// No application capable of enrolling for voice keyphrases is present.
|
// No application capable of enrolling for voice keyphrases is present.
|
||||||
mParseError = "No enrollment application found";
|
mParseError = "No enrollment applications found";
|
||||||
|
mKeyphrasePackageMap = null;
|
||||||
|
mKeyphrases = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean found = false;
|
List<String> parseErrors = new LinkedList<String>();
|
||||||
ApplicationInfo ai = null;
|
mKeyphrasePackageMap = new HashMap<KeyphraseMetadata, String>();
|
||||||
for (ResolveInfo ri : ris) {
|
for (ResolveInfo ri : ris) {
|
||||||
try {
|
try {
|
||||||
ai = pm.getApplicationInfo(
|
ApplicationInfo ai = pm.getApplicationInfo(
|
||||||
ri.activityInfo.packageName, PackageManager.GET_META_DATA);
|
ri.activityInfo.packageName, PackageManager.GET_META_DATA);
|
||||||
if ((ai.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) == 0) {
|
if ((ai.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) == 0) {
|
||||||
// The application isn't privileged (/system/priv-app).
|
// The application isn't privileged (/system/priv-app).
|
||||||
@@ -116,27 +128,45 @@ public class KeyphraseEnrollmentInfo {
|
|||||||
Slog.w(TAG, ai.packageName + " does not require MANAGE_VOICE_KEYPHRASES");
|
Slog.w(TAG, ai.packageName + " does not require MANAGE_VOICE_KEYPHRASES");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
mEnrollmentPackage = ai.packageName;
|
|
||||||
found = true;
|
mKeyphrasePackageMap.put(
|
||||||
break;
|
getKeyphraseMetadataFromApplicationInfo(pm, ai, parseErrors),
|
||||||
|
ai.packageName);
|
||||||
} catch (PackageManager.NameNotFoundException e) {
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
Slog.w(TAG, "error parsing voice enrollment meta-data", e);
|
String error = "error parsing voice enrollment meta-data for "
|
||||||
|
+ ri.activityInfo.packageName;
|
||||||
|
parseErrors.add(error + ": " + e);
|
||||||
|
Slog.w(TAG, error, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!found) {
|
if (mKeyphrasePackageMap.isEmpty()) {
|
||||||
|
String error = "No suitable enrollment application found";
|
||||||
|
parseErrors.add(error);
|
||||||
|
Slog.w(TAG, error);
|
||||||
mKeyphrases = null;
|
mKeyphrases = null;
|
||||||
mParseError = "No suitable enrollment application found";
|
} else {
|
||||||
return;
|
mKeyphrases = mKeyphrasePackageMap.keySet().toArray(
|
||||||
|
new KeyphraseMetadata[mKeyphrasePackageMap.size()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!parseErrors.isEmpty()) {
|
||||||
|
mParseError = TextUtils.join("\n", parseErrors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private KeyphraseMetadata getKeyphraseMetadataFromApplicationInfo(PackageManager pm,
|
||||||
|
ApplicationInfo ai, List<String> parseErrors) {
|
||||||
XmlResourceParser parser = null;
|
XmlResourceParser parser = null;
|
||||||
|
String packageName = ai.packageName;
|
||||||
|
KeyphraseMetadata keyphraseMetadata = null;
|
||||||
try {
|
try {
|
||||||
parser = ai.loadXmlMetaData(pm, VOICE_KEYPHRASE_META_DATA);
|
parser = ai.loadXmlMetaData(pm, VOICE_KEYPHRASE_META_DATA);
|
||||||
if (parser == null) {
|
if (parser == null) {
|
||||||
mParseError = "No " + VOICE_KEYPHRASE_META_DATA + " meta-data for "
|
String error = "No " + VOICE_KEYPHRASE_META_DATA + " meta-data for " + packageName;
|
||||||
+ ai.packageName;
|
parseErrors.add(error);
|
||||||
return;
|
Slog.w(TAG, error);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Resources res = pm.getResourcesForApplication(ai);
|
Resources res = pm.getResourcesForApplication(ai);
|
||||||
@@ -149,48 +179,55 @@ public class KeyphraseEnrollmentInfo {
|
|||||||
|
|
||||||
String nodeName = parser.getName();
|
String nodeName = parser.getName();
|
||||||
if (!"voice-enrollment-application".equals(nodeName)) {
|
if (!"voice-enrollment-application".equals(nodeName)) {
|
||||||
mParseError = "Meta-data does not start with voice-enrollment-application tag";
|
String error = "Meta-data does not start with voice-enrollment-application tag for "
|
||||||
return;
|
+ packageName;
|
||||||
|
parseErrors.add(error);
|
||||||
|
Slog.w(TAG, error);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
TypedArray array = res.obtainAttributes(attrs,
|
TypedArray array = res.obtainAttributes(attrs,
|
||||||
com.android.internal.R.styleable.VoiceEnrollmentApplication);
|
com.android.internal.R.styleable.VoiceEnrollmentApplication);
|
||||||
initializeKeyphrasesFromTypedArray(array);
|
keyphraseMetadata = getKeyphraseFromTypedArray(array, packageName, parseErrors);
|
||||||
array.recycle();
|
array.recycle();
|
||||||
} catch (XmlPullParserException e) {
|
} catch (XmlPullParserException e) {
|
||||||
mParseError = "Error parsing keyphrase enrollment meta-data: " + e;
|
String error = "Error parsing keyphrase enrollment meta-data for " + packageName;
|
||||||
Slog.w(TAG, "error parsing keyphrase enrollment meta-data", e);
|
parseErrors.add(error + ": " + e);
|
||||||
return;
|
Slog.w(TAG, error, e);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
mParseError = "Error parsing keyphrase enrollment meta-data: " + e;
|
String error = "Error parsing keyphrase enrollment meta-data for " + packageName;
|
||||||
Slog.w(TAG, "error parsing keyphrase enrollment meta-data", e);
|
parseErrors.add(error + ": " + e);
|
||||||
return;
|
Slog.w(TAG, error, e);
|
||||||
} catch (PackageManager.NameNotFoundException e) {
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
mParseError = "Error parsing keyphrase enrollment meta-data: " + e;
|
String error = "Error parsing keyphrase enrollment meta-data for " + packageName;
|
||||||
Slog.w(TAG, "error parsing keyphrase enrollment meta-data", e);
|
parseErrors.add(error + ": " + e);
|
||||||
return;
|
Slog.w(TAG, error, e);
|
||||||
} finally {
|
} finally {
|
||||||
if (parser != null) parser.close();
|
if (parser != null) parser.close();
|
||||||
}
|
}
|
||||||
|
return keyphraseMetadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeKeyphrasesFromTypedArray(TypedArray array) {
|
private KeyphraseMetadata getKeyphraseFromTypedArray(TypedArray array, String packageName,
|
||||||
|
List<String> parseErrors) {
|
||||||
// Get the keyphrase ID.
|
// Get the keyphrase ID.
|
||||||
int searchKeyphraseId = array.getInt(
|
int searchKeyphraseId = array.getInt(
|
||||||
com.android.internal.R.styleable.VoiceEnrollmentApplication_searchKeyphraseId, -1);
|
com.android.internal.R.styleable.VoiceEnrollmentApplication_searchKeyphraseId, -1);
|
||||||
if (searchKeyphraseId <= 0) {
|
if (searchKeyphraseId <= 0) {
|
||||||
mParseError = "No valid searchKeyphraseId specified in meta-data";
|
String error = "No valid searchKeyphraseId specified in meta-data for " + packageName;
|
||||||
Slog.w(TAG, mParseError);
|
parseErrors.add(error);
|
||||||
return;
|
Slog.w(TAG, error);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the keyphrase text.
|
// Get the keyphrase text.
|
||||||
String searchKeyphrase = array.getString(
|
String searchKeyphrase = array.getString(
|
||||||
com.android.internal.R.styleable.VoiceEnrollmentApplication_searchKeyphrase);
|
com.android.internal.R.styleable.VoiceEnrollmentApplication_searchKeyphrase);
|
||||||
if (searchKeyphrase == null) {
|
if (searchKeyphrase == null) {
|
||||||
mParseError = "No valid searchKeyphrase specified in meta-data";
|
String error = "No valid searchKeyphrase specified in meta-data for " + packageName;
|
||||||
Slog.w(TAG, mParseError);
|
parseErrors.add(error);
|
||||||
return;
|
Slog.w(TAG, error);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the supported locales.
|
// Get the supported locales.
|
||||||
@@ -198,9 +235,11 @@ public class KeyphraseEnrollmentInfo {
|
|||||||
com.android.internal.R.styleable
|
com.android.internal.R.styleable
|
||||||
.VoiceEnrollmentApplication_searchKeyphraseSupportedLocales);
|
.VoiceEnrollmentApplication_searchKeyphraseSupportedLocales);
|
||||||
if (searchKeyphraseSupportedLocales == null) {
|
if (searchKeyphraseSupportedLocales == null) {
|
||||||
mParseError = "No valid searchKeyphraseSupportedLocales specified in meta-data";
|
String error = "No valid searchKeyphraseSupportedLocales specified in meta-data for "
|
||||||
Slog.w(TAG, mParseError);
|
+ packageName;
|
||||||
return;
|
parseErrors.add(error);
|
||||||
|
Slog.w(TAG, error);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
ArraySet<Locale> locales = new ArraySet<>();
|
ArraySet<Locale> locales = new ArraySet<>();
|
||||||
// Try adding locales if the locale string is non-empty.
|
// Try adding locales if the locale string is non-empty.
|
||||||
@@ -214,9 +253,11 @@ public class KeyphraseEnrollmentInfo {
|
|||||||
// We catch a generic exception here because we don't want the system service
|
// 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
|
// to be affected by a malformed metadata because invalid locales were specified
|
||||||
// by the system application.
|
// by the system application.
|
||||||
mParseError = "Error reading searchKeyphraseSupportedLocales from meta-data";
|
String error = "Error reading searchKeyphraseSupportedLocales from meta-data for "
|
||||||
Slog.w(TAG, mParseError, ex);
|
+ packageName;
|
||||||
return;
|
parseErrors.add(error);
|
||||||
|
Slog.w(TAG, error);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,13 +265,13 @@ public class KeyphraseEnrollmentInfo {
|
|||||||
int recognitionModes = array.getInt(com.android.internal.R.styleable
|
int recognitionModes = array.getInt(com.android.internal.R.styleable
|
||||||
.VoiceEnrollmentApplication_searchKeyphraseRecognitionFlags, -1);
|
.VoiceEnrollmentApplication_searchKeyphraseRecognitionFlags, -1);
|
||||||
if (recognitionModes < 0) {
|
if (recognitionModes < 0) {
|
||||||
mParseError = "No valid searchKeyphraseRecognitionFlags specified in meta-data";
|
String error = "No valid searchKeyphraseRecognitionFlags specified in meta-data for "
|
||||||
Slog.w(TAG, mParseError);
|
+ packageName;
|
||||||
return;
|
parseErrors.add(error);
|
||||||
|
Slog.w(TAG, error);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
mKeyphrases = new KeyphraseMetadata[1];
|
return new KeyphraseMetadata(searchKeyphraseId, searchKeyphrase, locales, recognitionModes);
|
||||||
mKeyphrases[0] = new KeyphraseMetadata(searchKeyphraseId, searchKeyphrase, locales,
|
|
||||||
recognitionModes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getParseError() {
|
public String getParseError() {
|
||||||
@@ -259,14 +300,15 @@ public class KeyphraseEnrollmentInfo {
|
|||||||
* given keyphrase/locale combination isn't possible.
|
* given keyphrase/locale combination isn't possible.
|
||||||
*/
|
*/
|
||||||
public Intent getManageKeyphraseIntent(int action, String keyphrase, Locale locale) {
|
public Intent getManageKeyphraseIntent(int action, String keyphrase, Locale locale) {
|
||||||
if (mEnrollmentPackage == null || mEnrollmentPackage.isEmpty()) {
|
if (mKeyphrasePackageMap == null || mKeyphrasePackageMap.isEmpty()) {
|
||||||
Slog.w(TAG, "No enrollment application exists");
|
Slog.w(TAG, "No enrollment application exists");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getKeyphraseMetadata(keyphrase, locale) != null) {
|
KeyphraseMetadata keyphraseMetadata = getKeyphraseMetadata(keyphrase, locale);
|
||||||
|
if (keyphraseMetadata != null) {
|
||||||
Intent intent = new Intent(ACTION_MANAGE_VOICE_KEYPHRASES)
|
Intent intent = new Intent(ACTION_MANAGE_VOICE_KEYPHRASES)
|
||||||
.setPackage(mEnrollmentPackage)
|
.setPackage(mKeyphrasePackageMap.get(keyphraseMetadata))
|
||||||
.putExtra(EXTRA_VOICE_KEYPHRASE_HINT_TEXT, keyphrase)
|
.putExtra(EXTRA_VOICE_KEYPHRASE_HINT_TEXT, keyphrase)
|
||||||
.putExtra(EXTRA_VOICE_KEYPHRASE_LOCALE, locale.toLanguageTag())
|
.putExtra(EXTRA_VOICE_KEYPHRASE_LOCALE, locale.toLanguageTag())
|
||||||
.putExtra(EXTRA_VOICE_KEYPHRASE_ACTION, action);
|
.putExtra(EXTRA_VOICE_KEYPHRASE_ACTION, action);
|
||||||
@@ -298,14 +340,13 @@ public class KeyphraseEnrollmentInfo {
|
|||||||
return keyphraseMetadata;
|
return keyphraseMetadata;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Slog.w(TAG, "Enrollment application doesn't support the given keyphrase/locale");
|
Slog.w(TAG, "No Enrollment application supports the given keyphrase/locale");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "KeyphraseEnrollmentInfo [Keyphrases=" + Arrays.toString(mKeyphrases)
|
return "KeyphraseEnrollmentInfo [Keyphrases=" + mKeyphrasePackageMap.toString()
|
||||||
+ ", EnrollmentPackage=" + mEnrollmentPackage + ", ParseError=" + mParseError
|
+ ", ParseError=" + mParseError + "]";
|
||||||
+ "]";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user