Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b2e70aeb authored by Becca Hughes's avatar Becca Hughes Committed by Android (Google) Code Review
Browse files

Merge changes from topic "udc-settings-subtitle" into udc-dev

* changes:
  Add subtitle to settings (framework)
  Add framework change for manifest API change
parents 8386a3c2 51d9ad43
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
@@ -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
@@ -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
@@ -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";
@@ -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 {
+2 −0
Original line number Diff line number Diff line
@@ -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();
@@ -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);
  }

+46 −5
Original line number Diff line number Diff line
@@ -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.
@@ -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;

@@ -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;
    }
@@ -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. */
@@ -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() {
@@ -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
@@ -135,6 +152,9 @@ public final class CredentialProviderInfo implements Parcelable {
                + "overrideLabel="
                + mOverrideLabel
                + ", "
                + "settingsSubtitle="
                + mSettingsSubtitle
                + ", "
                + "capabilities="
                + String.join(",", mCapabilities)
                + "}";
@@ -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 =
@@ -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;

@@ -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;
+159 −26
Original line number Diff line number Diff line
@@ -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;
@@ -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.
     *
@@ -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(
@@ -134,7 +150,6 @@ public final class CredentialProviderInfoFactory {
                .setSystemProvider(isSystemProvider)
                .addCapabilities(capabilities)
                .build();

    }

    private static void verifyProviderPermission(ServiceInfo serviceInfo) throws SecurityException {
@@ -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;
@@ -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;
@@ -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);
+35 −0
Original line number Diff line number Diff line
@@ -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>&lt;{@link android.R.styleable#CredentialProvider credential-provider}&gt;</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