Loading core/api/current.txt +4 −1 Original line number Diff line number Diff line Loading @@ -556,6 +556,7 @@ package android { field public static final int canTakeScreenshot = 16844303; // 0x101060f field public static final int candidatesTextStyleSpans = 16843312; // 0x1010230 field public static final int cantSaveState = 16844142; // 0x101056e field public static final int capability; field @Deprecated public static final int capitalize = 16843113; // 0x1010169 field public static final int category = 16843752; // 0x10103e8 field public static final int centerBright = 16842956; // 0x10100cc Loading Loading @@ -1448,6 +1449,7 @@ package android { field public static final int sessionService = 16843837; // 0x101043d field public static final int settingsActivity = 16843301; // 0x1010225 field public static final int settingsSliceUri = 16844179; // 0x1010593 field public static final int settingsSubtitle; field public static final int setupActivity = 16843766; // 0x10103f6 field public static final int shadowColor = 16843105; // 0x1010161 field public static final int shadowDx = 16843106; // 0x1010162 Loading Loading @@ -40707,7 +40709,7 @@ package android.service.credentials { method public abstract void onBeginGetCredential(@NonNull android.service.credentials.BeginGetCredentialRequest, @NonNull android.os.CancellationSignal, @NonNull android.os.OutcomeReceiver<android.service.credentials.BeginGetCredentialResponse,android.credentials.GetCredentialException>); method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent); method public abstract void onClearCredentialState(@NonNull android.service.credentials.ClearCredentialStateRequest, @NonNull android.os.CancellationSignal, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.credentials.ClearCredentialStateException>); field public static final String CAPABILITY_META_DATA_KEY = "android.credentials.capabilities"; field @Deprecated public static final String CAPABILITY_META_DATA_KEY = "android.credentials.capabilities"; field public static final String EXTRA_BEGIN_GET_CREDENTIAL_REQUEST = "android.service.credentials.extra.BEGIN_GET_CREDENTIAL_REQUEST"; field public static final String EXTRA_BEGIN_GET_CREDENTIAL_RESPONSE = "android.service.credentials.extra.BEGIN_GET_CREDENTIAL_RESPONSE"; field public static final String EXTRA_CREATE_CREDENTIAL_EXCEPTION = "android.service.credentials.extra.CREATE_CREDENTIAL_EXCEPTION"; Loading @@ -40717,6 +40719,7 @@ package android.service.credentials { field public static final String EXTRA_GET_CREDENTIAL_REQUEST = "android.service.credentials.extra.GET_CREDENTIAL_REQUEST"; field public static final String EXTRA_GET_CREDENTIAL_RESPONSE = "android.service.credentials.extra.GET_CREDENTIAL_RESPONSE"; field public static final String SERVICE_INTERFACE = "android.service.credentials.CredentialProviderService"; field public static final String SERVICE_META_DATA = "android.credentials.provider"; } public final class GetCredentialRequest implements android.os.Parcelable { core/api/test-current.txt +2 −0 Original line number Diff line number Diff line Loading @@ -1112,6 +1112,7 @@ package android.credentials { method @Nullable public CharSequence getLabel(@NonNull android.content.Context); method @Nullable public android.graphics.drawable.Drawable getServiceIcon(@NonNull android.content.Context); method @NonNull public android.content.pm.ServiceInfo getServiceInfo(); method @Nullable public CharSequence getSettingsSubtitle(); method @NonNull public boolean hasCapability(@NonNull String); method public boolean isEnabled(); method public boolean isSystemProvider(); Loading @@ -1124,6 +1125,7 @@ package android.credentials { method @NonNull public android.credentials.CredentialProviderInfo.Builder addCapabilities(@NonNull java.util.List<java.lang.String>); method @NonNull public android.credentials.CredentialProviderInfo build(); method @NonNull public android.credentials.CredentialProviderInfo.Builder setEnabled(boolean); method @NonNull public android.credentials.CredentialProviderInfo.Builder setSettingsSubtitle(@Nullable CharSequence); method @NonNull public android.credentials.CredentialProviderInfo.Builder setSystemProvider(boolean); } Loading core/java/android/credentials/CredentialProviderInfo.java +46 −5 Original line number Diff line number Diff line Loading @@ -29,7 +29,9 @@ import android.text.TextUtils; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; /** * {@link ServiceInfo} and meta-data about a credential provider. Loading @@ -39,8 +41,9 @@ import java.util.List; @TestApi public final class CredentialProviderInfo implements Parcelable { @NonNull private final ServiceInfo mServiceInfo; @NonNull private final List<String> mCapabilities = new ArrayList<>(); @NonNull private final Set<String> mCapabilities = new HashSet<>(); @Nullable private final CharSequence mOverrideLabel; @Nullable private CharSequence mSettingsSubtitle = null; private final boolean mIsSystemProvider; private final boolean mIsEnabled; Loading @@ -53,6 +56,7 @@ public final class CredentialProviderInfo implements Parcelable { mServiceInfo = builder.mServiceInfo; mCapabilities.addAll(builder.mCapabilities); mIsSystemProvider = builder.mIsSystemProvider; mSettingsSubtitle = builder.mSettingsSubtitle; mIsEnabled = builder.mIsEnabled; mOverrideLabel = builder.mOverrideLabel; } Loading Loading @@ -92,7 +96,11 @@ public final class CredentialProviderInfo implements Parcelable { /** Returns a list of capabilities this provider service can support. */ @NonNull public List<String> getCapabilities() { return Collections.unmodifiableList(mCapabilities); List<String> capabilities = new ArrayList<>(); for (String capability : mCapabilities) { capabilities.add(capability); } return Collections.unmodifiableList(capabilities); } /** Returns whether the provider is enabled by the user. */ Loading @@ -100,6 +108,12 @@ public final class CredentialProviderInfo implements Parcelable { return mIsEnabled; } /** Returns the settings subtitle. */ @Nullable public CharSequence getSettingsSubtitle() { return mSettingsSubtitle; } /** Returns the component name for the service. */ @NonNull public ComponentName getComponentName() { Loading @@ -110,9 +124,12 @@ public final class CredentialProviderInfo implements Parcelable { public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeTypedObject(mServiceInfo, flags); dest.writeBoolean(mIsSystemProvider); dest.writeStringList(mCapabilities); dest.writeBoolean(mIsEnabled); TextUtils.writeToParcel(mOverrideLabel, dest, flags); TextUtils.writeToParcel(mSettingsSubtitle, dest, flags); List<String> capabilities = getCapabilities(); dest.writeStringList(capabilities); } @Override Loading @@ -135,6 +152,9 @@ public final class CredentialProviderInfo implements Parcelable { + "overrideLabel=" + mOverrideLabel + ", " + "settingsSubtitle=" + mSettingsSubtitle + ", " + "capabilities=" + String.join(",", mCapabilities) + "}"; Loading @@ -143,9 +163,13 @@ public final class CredentialProviderInfo implements Parcelable { private CredentialProviderInfo(@NonNull Parcel in) { mServiceInfo = in.readTypedObject(ServiceInfo.CREATOR); mIsSystemProvider = in.readBoolean(); in.readStringList(mCapabilities); mIsEnabled = in.readBoolean(); mOverrideLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); mSettingsSubtitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); List<String> capabilities = new ArrayList<>(); in.readStringList(capabilities); mCapabilities.addAll(capabilities); } public static final @NonNull Parcelable.Creator<CredentialProviderInfo> CREATOR = Loading @@ -165,8 +189,9 @@ public final class CredentialProviderInfo implements Parcelable { public static final class Builder { @NonNull private ServiceInfo mServiceInfo; @NonNull private List<String> mCapabilities = new ArrayList<>(); @NonNull private Set<String> mCapabilities = new HashSet<>(); private boolean mIsSystemProvider = false; @Nullable private CharSequence mSettingsSubtitle = null; private boolean mIsEnabled = false; @Nullable private CharSequence mOverrideLabel = null; Loading Loading @@ -195,12 +220,28 @@ public final class CredentialProviderInfo implements Parcelable { return this; } /** Sets the settings subtitle. */ public @NonNull Builder setSettingsSubtitle(@Nullable CharSequence settingsSubtitle) { mSettingsSubtitle = settingsSubtitle; return this; } /** Sets a list of capabilities this provider service can support. */ public @NonNull Builder addCapabilities(@NonNull List<String> capabilities) { mCapabilities.addAll(capabilities); return this; } /** * Sets a list of capabilities this provider service can support. * * @hide */ public @NonNull Builder addCapabilities(@NonNull Set<String> capabilities) { mCapabilities.addAll(capabilities); return this; } /** Sets whether it is enabled by the user. */ public @NonNull Builder setEnabled(boolean isEnabled) { mIsEnabled = isEnabled; Loading core/java/android/service/credentials/CredentialProviderInfoFactory.java +159 −26 Original line number Diff line number Diff line Loading @@ -33,17 +33,28 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.credentials.CredentialManager; import android.credentials.CredentialProviderInfo; import android.os.Bundle; import android.os.RemoteException; import android.os.UserHandle; import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; import android.util.Slog; import android.util.Xml; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; Loading @@ -58,6 +69,11 @@ import java.util.Set; public final class CredentialProviderInfoFactory { private static final String TAG = "CredentialProviderInfoFactory"; private static final String TAG_CREDENTIAL_PROVIDER = "credential-provider"; private static final String TAG_CAPABILITIES = "capabilities"; private static final String TAG_CAPABILITY = "capability"; private static final String ATTR_NAME = "name"; /** * Constructs an information instance of the credential provider. * Loading Loading @@ -118,8 +134,8 @@ public final class CredentialProviderInfoFactory { } /** * Constructs an information instance of the credential provider for testing purposes. Does * not run any verifications and passes parameters as is. * Constructs an information instance of the credential provider for testing purposes. Does not * run any verifications and passes parameters as is. */ @VisibleForTesting public static CredentialProviderInfo createForTests( Loading @@ -134,7 +150,6 @@ public final class CredentialProviderInfoFactory { .setSystemProvider(isSystemProvider) .addCapabilities(capabilities) .build(); } private static void verifyProviderPermission(ServiceInfo serviceInfo) throws SecurityException { Loading Loading @@ -194,10 +209,8 @@ public final class CredentialProviderInfoFactory { private static CredentialProviderInfo.Builder populateMetadata( @NonNull Context context, ServiceInfo serviceInfo) { requireNonNull(context, "context must not be null"); final CredentialProviderInfo.Builder builder = new CredentialProviderInfo.Builder(serviceInfo); final PackageManager pm = context.getPackageManager(); CredentialProviderInfo.Builder builder = new CredentialProviderInfo.Builder(serviceInfo); // 1. Get the metadata for the service. final Bundle metadata = serviceInfo.metaData; Loading @@ -206,46 +219,165 @@ public final class CredentialProviderInfoFactory { return builder; } // 2. Extract the capabilities from the bundle. // 2. Get the resources for the application. Resources resources = null; try { Resources resources = pm.getResourcesForApplication(serviceInfo.applicationInfo); resources = pm.getResourcesForApplication(serviceInfo.applicationInfo); } catch (PackageManager.NameNotFoundException e) { Log.e(TAG, "Failed to get app resources", e); } // 3. Stop if we are missing data. if (metadata == null || resources == null) { Log.i(TAG, "populateMetadata - resources is null"); return builder; } builder.addCapabilities(populateProviderCapabilities(resources, metadata, serviceInfo)); } catch (PackageManager.NameNotFoundException e) { Slog.e(TAG, e.getMessage()); // 4. Extract the XML metadata. try { builder = extractXmlMetadata(context, builder, serviceInfo, pm, resources); } catch (Exception e) { Log.e(TAG, "Failed to get XML metadata", e); } // 5. Extract the legacy metadata. try { builder.addCapabilities( populateLegacyProviderCapabilities(resources, metadata, serviceInfo)); } catch (Exception e) { Log.e(TAG, "Failed to get legacy metadata ", e); } return builder; } private static CredentialProviderInfo.Builder extractXmlMetadata( @NonNull Context context, @NonNull CredentialProviderInfo.Builder builder, @NonNull ServiceInfo serviceInfo, @NonNull PackageManager pm, @NonNull Resources resources) { final XmlResourceParser parser = serviceInfo.loadXmlMetaData(pm, CredentialProviderService.SERVICE_META_DATA); if (parser == null) { return builder; } try { int type = 0; while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) { type = parser.next(); } // This is matching a <credential-provider /> tag in the XML. if (TAG_CREDENTIAL_PROVIDER.equals(parser.getName())) { final AttributeSet allAttributes = Xml.asAttributeSet(parser); TypedArray afsAttributes = null; try { afsAttributes = resources.obtainAttributes( allAttributes, com.android.internal.R.styleable.CredentialProvider); builder.setSettingsSubtitle( afsAttributes.getString( R.styleable.CredentialProvider_settingsSubtitle)); } catch (Exception e) { Log.e(TAG, "Failed to get XML attr", e); } finally { if (afsAttributes != null) { afsAttributes.recycle(); } } builder.addCapabilities(parseXmlProviderOuterCapabilities(parser, resources)); } else { Log.e(TAG, "Meta-data does not start with credential-provider-service tag"); } } catch (IOException | XmlPullParserException e) { Log.e(TAG, "Error parsing credential provider service meta-data", e); } return builder; } private static List<String> populateProviderCapabilities( private static Set<String> parseXmlProviderOuterCapabilities( XmlPullParser parser, Resources resources) throws IOException, XmlPullParserException { final Set<String> capabilities = new HashSet<>(); final int outerDepth = parser.getDepth(); int type; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { continue; } if (TAG_CAPABILITIES.equals(parser.getName())) { capabilities.addAll(parseXmlProviderInnerCapabilities(parser, resources)); } } return capabilities; } private static List<String> parseXmlProviderInnerCapabilities( XmlPullParser parser, Resources resources) throws IOException, XmlPullParserException { List<String> capabilities = new ArrayList<>(); final int outerDepth = parser.getDepth(); int type; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { continue; } if (TAG_CAPABILITY.equals(parser.getName())) { String name = parser.getAttributeValue(null, ATTR_NAME); if (name != null && !TextUtils.isEmpty(name)) { capabilities.add(name); } } } return capabilities; } private static Set<String> populateLegacyProviderCapabilities( Resources resources, Bundle metadata, ServiceInfo serviceInfo) { List<String> output = new ArrayList<>(); String[] capabilities = new String[0]; Set<String> output = new HashSet<>(); Set<String> capabilities = new HashSet<>(); try { capabilities = String[] discovered = resources.getStringArray( metadata.getInt(CredentialProviderService.CAPABILITY_META_DATA_KEY)); } catch (Resources.NotFoundException e) { Slog.e(TAG, "Failed to get capabilities: " + e.getMessage()); if (discovered != null) { capabilities.addAll(Arrays.asList(discovered)); } } catch (Resources.NotFoundException | NullPointerException e) { Log.e(TAG, "Failed to get capabilities: ", e); } try { String[] discovered = metadata.getStringArray(CredentialProviderService.CAPABILITY_META_DATA_KEY); if (discovered != null) { capabilities.addAll(Arrays.asList(discovered)); } } catch (Resources.NotFoundException | NullPointerException e) { Log.e(TAG, "Failed to get capabilities: ", e); } if (capabilities == null || capabilities.length == 0) { Slog.e(TAG, "No capabilities found for provider:" + serviceInfo.packageName); if (capabilities.size() == 0) { Log.e(TAG, "No capabilities found for provider:" + serviceInfo); return output; } for (String capability : capabilities) { if (capability.isEmpty()) { Slog.e(TAG, "Skipping empty capability"); if (capability == null || capability.isEmpty()) { Log.w(TAG, "Skipping empty/null capability"); continue; } Slog.e(TAG, "Capabilities found for provider: " + capability); Log.i(TAG, "Capabilities found for provider: " + capability); output.add(capability); } return output; Loading Loading @@ -361,7 +493,8 @@ public final class CredentialProviderInfoFactory { try { DevicePolicyManager dpm = newContext.getSystemService(DevicePolicyManager.class); return dpm.getCredentialManagerPolicy(); PackagePolicy pp = dpm.getCredentialManagerPolicy(); return pp; } catch (SecurityException e) { // If the current user is not enrolled in DPM then this can throw a security error. Log.e(TAG, "Failed to get device policy: " + e); Loading core/java/android/service/credentials/CredentialProviderService.java +35 −0 Original line number Diff line number Diff line Loading @@ -156,8 +156,43 @@ public abstract class CredentialProviderService extends Service { private static final String TAG = "CredProviderService"; /** * The list of capabilities exposed by a credential provider. * * @deprecated Replaced with {@link android.service.credentials#SERVICE_META_DATA} */ @Deprecated public static final String CAPABILITY_META_DATA_KEY = "android.credentials.capabilities"; /** * Name under which a Credential Provider service component publishes information * about itself. This meta-data must reference an XML resource containing * an * <code><{@link android.R.styleable#CredentialProvider credential-provider}></code> * tag. * * For example (AndroidManifest.xml): * <code> * <meta-data * android:name="android.credentials.provider" * android:resource="@xml/provider"/> * </code> * * For example (xml/provider.xml): * <code> * <credential-provider xmlns:android="http://schemas.android.com/apk/res/android" * android:settingsSubtitle="@string/providerSubtitle"> * <capabilities> * <capability>@string/passwords</capability> * <capability>@string/passkeys</capability> * </capabilities> * <string name="passwords">android.credentials.TYPE_PASSWORD_CREDENTIAL</string> * <string name="passkeys">android.credentials.TYPE_PUBLIC_KEY_CREDENTIAL</string> * </credential-provider> * </code> */ public static final String SERVICE_META_DATA = "android.credentials.provider"; /** @hide */ public static final String TEST_SYSTEM_PROVIDER_META_DATA_KEY = "android.credentials.testsystemprovider"; Loading Loading
core/api/current.txt +4 −1 Original line number Diff line number Diff line Loading @@ -556,6 +556,7 @@ package android { field public static final int canTakeScreenshot = 16844303; // 0x101060f field public static final int candidatesTextStyleSpans = 16843312; // 0x1010230 field public static final int cantSaveState = 16844142; // 0x101056e field public static final int capability; field @Deprecated public static final int capitalize = 16843113; // 0x1010169 field public static final int category = 16843752; // 0x10103e8 field public static final int centerBright = 16842956; // 0x10100cc Loading Loading @@ -1448,6 +1449,7 @@ package android { field public static final int sessionService = 16843837; // 0x101043d field public static final int settingsActivity = 16843301; // 0x1010225 field public static final int settingsSliceUri = 16844179; // 0x1010593 field public static final int settingsSubtitle; field public static final int setupActivity = 16843766; // 0x10103f6 field public static final int shadowColor = 16843105; // 0x1010161 field public static final int shadowDx = 16843106; // 0x1010162 Loading Loading @@ -40707,7 +40709,7 @@ package android.service.credentials { method public abstract void onBeginGetCredential(@NonNull android.service.credentials.BeginGetCredentialRequest, @NonNull android.os.CancellationSignal, @NonNull android.os.OutcomeReceiver<android.service.credentials.BeginGetCredentialResponse,android.credentials.GetCredentialException>); method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent); method public abstract void onClearCredentialState(@NonNull android.service.credentials.ClearCredentialStateRequest, @NonNull android.os.CancellationSignal, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.credentials.ClearCredentialStateException>); field public static final String CAPABILITY_META_DATA_KEY = "android.credentials.capabilities"; field @Deprecated public static final String CAPABILITY_META_DATA_KEY = "android.credentials.capabilities"; field public static final String EXTRA_BEGIN_GET_CREDENTIAL_REQUEST = "android.service.credentials.extra.BEGIN_GET_CREDENTIAL_REQUEST"; field public static final String EXTRA_BEGIN_GET_CREDENTIAL_RESPONSE = "android.service.credentials.extra.BEGIN_GET_CREDENTIAL_RESPONSE"; field public static final String EXTRA_CREATE_CREDENTIAL_EXCEPTION = "android.service.credentials.extra.CREATE_CREDENTIAL_EXCEPTION"; Loading @@ -40717,6 +40719,7 @@ package android.service.credentials { field public static final String EXTRA_GET_CREDENTIAL_REQUEST = "android.service.credentials.extra.GET_CREDENTIAL_REQUEST"; field public static final String EXTRA_GET_CREDENTIAL_RESPONSE = "android.service.credentials.extra.GET_CREDENTIAL_RESPONSE"; field public static final String SERVICE_INTERFACE = "android.service.credentials.CredentialProviderService"; field public static final String SERVICE_META_DATA = "android.credentials.provider"; } public final class GetCredentialRequest implements android.os.Parcelable {
core/api/test-current.txt +2 −0 Original line number Diff line number Diff line Loading @@ -1112,6 +1112,7 @@ package android.credentials { method @Nullable public CharSequence getLabel(@NonNull android.content.Context); method @Nullable public android.graphics.drawable.Drawable getServiceIcon(@NonNull android.content.Context); method @NonNull public android.content.pm.ServiceInfo getServiceInfo(); method @Nullable public CharSequence getSettingsSubtitle(); method @NonNull public boolean hasCapability(@NonNull String); method public boolean isEnabled(); method public boolean isSystemProvider(); Loading @@ -1124,6 +1125,7 @@ package android.credentials { method @NonNull public android.credentials.CredentialProviderInfo.Builder addCapabilities(@NonNull java.util.List<java.lang.String>); method @NonNull public android.credentials.CredentialProviderInfo build(); method @NonNull public android.credentials.CredentialProviderInfo.Builder setEnabled(boolean); method @NonNull public android.credentials.CredentialProviderInfo.Builder setSettingsSubtitle(@Nullable CharSequence); method @NonNull public android.credentials.CredentialProviderInfo.Builder setSystemProvider(boolean); } Loading
core/java/android/credentials/CredentialProviderInfo.java +46 −5 Original line number Diff line number Diff line Loading @@ -29,7 +29,9 @@ import android.text.TextUtils; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; /** * {@link ServiceInfo} and meta-data about a credential provider. Loading @@ -39,8 +41,9 @@ import java.util.List; @TestApi public final class CredentialProviderInfo implements Parcelable { @NonNull private final ServiceInfo mServiceInfo; @NonNull private final List<String> mCapabilities = new ArrayList<>(); @NonNull private final Set<String> mCapabilities = new HashSet<>(); @Nullable private final CharSequence mOverrideLabel; @Nullable private CharSequence mSettingsSubtitle = null; private final boolean mIsSystemProvider; private final boolean mIsEnabled; Loading @@ -53,6 +56,7 @@ public final class CredentialProviderInfo implements Parcelable { mServiceInfo = builder.mServiceInfo; mCapabilities.addAll(builder.mCapabilities); mIsSystemProvider = builder.mIsSystemProvider; mSettingsSubtitle = builder.mSettingsSubtitle; mIsEnabled = builder.mIsEnabled; mOverrideLabel = builder.mOverrideLabel; } Loading Loading @@ -92,7 +96,11 @@ public final class CredentialProviderInfo implements Parcelable { /** Returns a list of capabilities this provider service can support. */ @NonNull public List<String> getCapabilities() { return Collections.unmodifiableList(mCapabilities); List<String> capabilities = new ArrayList<>(); for (String capability : mCapabilities) { capabilities.add(capability); } return Collections.unmodifiableList(capabilities); } /** Returns whether the provider is enabled by the user. */ Loading @@ -100,6 +108,12 @@ public final class CredentialProviderInfo implements Parcelable { return mIsEnabled; } /** Returns the settings subtitle. */ @Nullable public CharSequence getSettingsSubtitle() { return mSettingsSubtitle; } /** Returns the component name for the service. */ @NonNull public ComponentName getComponentName() { Loading @@ -110,9 +124,12 @@ public final class CredentialProviderInfo implements Parcelable { public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeTypedObject(mServiceInfo, flags); dest.writeBoolean(mIsSystemProvider); dest.writeStringList(mCapabilities); dest.writeBoolean(mIsEnabled); TextUtils.writeToParcel(mOverrideLabel, dest, flags); TextUtils.writeToParcel(mSettingsSubtitle, dest, flags); List<String> capabilities = getCapabilities(); dest.writeStringList(capabilities); } @Override Loading @@ -135,6 +152,9 @@ public final class CredentialProviderInfo implements Parcelable { + "overrideLabel=" + mOverrideLabel + ", " + "settingsSubtitle=" + mSettingsSubtitle + ", " + "capabilities=" + String.join(",", mCapabilities) + "}"; Loading @@ -143,9 +163,13 @@ public final class CredentialProviderInfo implements Parcelable { private CredentialProviderInfo(@NonNull Parcel in) { mServiceInfo = in.readTypedObject(ServiceInfo.CREATOR); mIsSystemProvider = in.readBoolean(); in.readStringList(mCapabilities); mIsEnabled = in.readBoolean(); mOverrideLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); mSettingsSubtitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); List<String> capabilities = new ArrayList<>(); in.readStringList(capabilities); mCapabilities.addAll(capabilities); } public static final @NonNull Parcelable.Creator<CredentialProviderInfo> CREATOR = Loading @@ -165,8 +189,9 @@ public final class CredentialProviderInfo implements Parcelable { public static final class Builder { @NonNull private ServiceInfo mServiceInfo; @NonNull private List<String> mCapabilities = new ArrayList<>(); @NonNull private Set<String> mCapabilities = new HashSet<>(); private boolean mIsSystemProvider = false; @Nullable private CharSequence mSettingsSubtitle = null; private boolean mIsEnabled = false; @Nullable private CharSequence mOverrideLabel = null; Loading Loading @@ -195,12 +220,28 @@ public final class CredentialProviderInfo implements Parcelable { return this; } /** Sets the settings subtitle. */ public @NonNull Builder setSettingsSubtitle(@Nullable CharSequence settingsSubtitle) { mSettingsSubtitle = settingsSubtitle; return this; } /** Sets a list of capabilities this provider service can support. */ public @NonNull Builder addCapabilities(@NonNull List<String> capabilities) { mCapabilities.addAll(capabilities); return this; } /** * Sets a list of capabilities this provider service can support. * * @hide */ public @NonNull Builder addCapabilities(@NonNull Set<String> capabilities) { mCapabilities.addAll(capabilities); return this; } /** Sets whether it is enabled by the user. */ public @NonNull Builder setEnabled(boolean isEnabled) { mIsEnabled = isEnabled; Loading
core/java/android/service/credentials/CredentialProviderInfoFactory.java +159 −26 Original line number Diff line number Diff line Loading @@ -33,17 +33,28 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.credentials.CredentialManager; import android.credentials.CredentialProviderInfo; import android.os.Bundle; import android.os.RemoteException; import android.os.UserHandle; import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; import android.util.Slog; import android.util.Xml; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; Loading @@ -58,6 +69,11 @@ import java.util.Set; public final class CredentialProviderInfoFactory { private static final String TAG = "CredentialProviderInfoFactory"; private static final String TAG_CREDENTIAL_PROVIDER = "credential-provider"; private static final String TAG_CAPABILITIES = "capabilities"; private static final String TAG_CAPABILITY = "capability"; private static final String ATTR_NAME = "name"; /** * Constructs an information instance of the credential provider. * Loading Loading @@ -118,8 +134,8 @@ public final class CredentialProviderInfoFactory { } /** * Constructs an information instance of the credential provider for testing purposes. Does * not run any verifications and passes parameters as is. * Constructs an information instance of the credential provider for testing purposes. Does not * run any verifications and passes parameters as is. */ @VisibleForTesting public static CredentialProviderInfo createForTests( Loading @@ -134,7 +150,6 @@ public final class CredentialProviderInfoFactory { .setSystemProvider(isSystemProvider) .addCapabilities(capabilities) .build(); } private static void verifyProviderPermission(ServiceInfo serviceInfo) throws SecurityException { Loading Loading @@ -194,10 +209,8 @@ public final class CredentialProviderInfoFactory { private static CredentialProviderInfo.Builder populateMetadata( @NonNull Context context, ServiceInfo serviceInfo) { requireNonNull(context, "context must not be null"); final CredentialProviderInfo.Builder builder = new CredentialProviderInfo.Builder(serviceInfo); final PackageManager pm = context.getPackageManager(); CredentialProviderInfo.Builder builder = new CredentialProviderInfo.Builder(serviceInfo); // 1. Get the metadata for the service. final Bundle metadata = serviceInfo.metaData; Loading @@ -206,46 +219,165 @@ public final class CredentialProviderInfoFactory { return builder; } // 2. Extract the capabilities from the bundle. // 2. Get the resources for the application. Resources resources = null; try { Resources resources = pm.getResourcesForApplication(serviceInfo.applicationInfo); resources = pm.getResourcesForApplication(serviceInfo.applicationInfo); } catch (PackageManager.NameNotFoundException e) { Log.e(TAG, "Failed to get app resources", e); } // 3. Stop if we are missing data. if (metadata == null || resources == null) { Log.i(TAG, "populateMetadata - resources is null"); return builder; } builder.addCapabilities(populateProviderCapabilities(resources, metadata, serviceInfo)); } catch (PackageManager.NameNotFoundException e) { Slog.e(TAG, e.getMessage()); // 4. Extract the XML metadata. try { builder = extractXmlMetadata(context, builder, serviceInfo, pm, resources); } catch (Exception e) { Log.e(TAG, "Failed to get XML metadata", e); } // 5. Extract the legacy metadata. try { builder.addCapabilities( populateLegacyProviderCapabilities(resources, metadata, serviceInfo)); } catch (Exception e) { Log.e(TAG, "Failed to get legacy metadata ", e); } return builder; } private static CredentialProviderInfo.Builder extractXmlMetadata( @NonNull Context context, @NonNull CredentialProviderInfo.Builder builder, @NonNull ServiceInfo serviceInfo, @NonNull PackageManager pm, @NonNull Resources resources) { final XmlResourceParser parser = serviceInfo.loadXmlMetaData(pm, CredentialProviderService.SERVICE_META_DATA); if (parser == null) { return builder; } try { int type = 0; while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) { type = parser.next(); } // This is matching a <credential-provider /> tag in the XML. if (TAG_CREDENTIAL_PROVIDER.equals(parser.getName())) { final AttributeSet allAttributes = Xml.asAttributeSet(parser); TypedArray afsAttributes = null; try { afsAttributes = resources.obtainAttributes( allAttributes, com.android.internal.R.styleable.CredentialProvider); builder.setSettingsSubtitle( afsAttributes.getString( R.styleable.CredentialProvider_settingsSubtitle)); } catch (Exception e) { Log.e(TAG, "Failed to get XML attr", e); } finally { if (afsAttributes != null) { afsAttributes.recycle(); } } builder.addCapabilities(parseXmlProviderOuterCapabilities(parser, resources)); } else { Log.e(TAG, "Meta-data does not start with credential-provider-service tag"); } } catch (IOException | XmlPullParserException e) { Log.e(TAG, "Error parsing credential provider service meta-data", e); } return builder; } private static List<String> populateProviderCapabilities( private static Set<String> parseXmlProviderOuterCapabilities( XmlPullParser parser, Resources resources) throws IOException, XmlPullParserException { final Set<String> capabilities = new HashSet<>(); final int outerDepth = parser.getDepth(); int type; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { continue; } if (TAG_CAPABILITIES.equals(parser.getName())) { capabilities.addAll(parseXmlProviderInnerCapabilities(parser, resources)); } } return capabilities; } private static List<String> parseXmlProviderInnerCapabilities( XmlPullParser parser, Resources resources) throws IOException, XmlPullParserException { List<String> capabilities = new ArrayList<>(); final int outerDepth = parser.getDepth(); int type; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { continue; } if (TAG_CAPABILITY.equals(parser.getName())) { String name = parser.getAttributeValue(null, ATTR_NAME); if (name != null && !TextUtils.isEmpty(name)) { capabilities.add(name); } } } return capabilities; } private static Set<String> populateLegacyProviderCapabilities( Resources resources, Bundle metadata, ServiceInfo serviceInfo) { List<String> output = new ArrayList<>(); String[] capabilities = new String[0]; Set<String> output = new HashSet<>(); Set<String> capabilities = new HashSet<>(); try { capabilities = String[] discovered = resources.getStringArray( metadata.getInt(CredentialProviderService.CAPABILITY_META_DATA_KEY)); } catch (Resources.NotFoundException e) { Slog.e(TAG, "Failed to get capabilities: " + e.getMessage()); if (discovered != null) { capabilities.addAll(Arrays.asList(discovered)); } } catch (Resources.NotFoundException | NullPointerException e) { Log.e(TAG, "Failed to get capabilities: ", e); } try { String[] discovered = metadata.getStringArray(CredentialProviderService.CAPABILITY_META_DATA_KEY); if (discovered != null) { capabilities.addAll(Arrays.asList(discovered)); } } catch (Resources.NotFoundException | NullPointerException e) { Log.e(TAG, "Failed to get capabilities: ", e); } if (capabilities == null || capabilities.length == 0) { Slog.e(TAG, "No capabilities found for provider:" + serviceInfo.packageName); if (capabilities.size() == 0) { Log.e(TAG, "No capabilities found for provider:" + serviceInfo); return output; } for (String capability : capabilities) { if (capability.isEmpty()) { Slog.e(TAG, "Skipping empty capability"); if (capability == null || capability.isEmpty()) { Log.w(TAG, "Skipping empty/null capability"); continue; } Slog.e(TAG, "Capabilities found for provider: " + capability); Log.i(TAG, "Capabilities found for provider: " + capability); output.add(capability); } return output; Loading Loading @@ -361,7 +493,8 @@ public final class CredentialProviderInfoFactory { try { DevicePolicyManager dpm = newContext.getSystemService(DevicePolicyManager.class); return dpm.getCredentialManagerPolicy(); PackagePolicy pp = dpm.getCredentialManagerPolicy(); return pp; } catch (SecurityException e) { // If the current user is not enrolled in DPM then this can throw a security error. Log.e(TAG, "Failed to get device policy: " + e); Loading
core/java/android/service/credentials/CredentialProviderService.java +35 −0 Original line number Diff line number Diff line Loading @@ -156,8 +156,43 @@ public abstract class CredentialProviderService extends Service { private static final String TAG = "CredProviderService"; /** * The list of capabilities exposed by a credential provider. * * @deprecated Replaced with {@link android.service.credentials#SERVICE_META_DATA} */ @Deprecated public static final String CAPABILITY_META_DATA_KEY = "android.credentials.capabilities"; /** * Name under which a Credential Provider service component publishes information * about itself. This meta-data must reference an XML resource containing * an * <code><{@link android.R.styleable#CredentialProvider credential-provider}></code> * tag. * * For example (AndroidManifest.xml): * <code> * <meta-data * android:name="android.credentials.provider" * android:resource="@xml/provider"/> * </code> * * For example (xml/provider.xml): * <code> * <credential-provider xmlns:android="http://schemas.android.com/apk/res/android" * android:settingsSubtitle="@string/providerSubtitle"> * <capabilities> * <capability>@string/passwords</capability> * <capability>@string/passkeys</capability> * </capabilities> * <string name="passwords">android.credentials.TYPE_PASSWORD_CREDENTIAL</string> * <string name="passkeys">android.credentials.TYPE_PUBLIC_KEY_CREDENTIAL</string> * </credential-provider> * </code> */ public static final String SERVICE_META_DATA = "android.credentials.provider"; /** @hide */ public static final String TEST_SYSTEM_PROVIDER_META_DATA_KEY = "android.credentials.testsystemprovider"; Loading