/* * Copyright (C) 2023 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.credentials; import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; import android.content.ComponentName; import android.content.Context; import android.content.pm.ServiceInfo; import android.credentials.flags.Flags; import android.graphics.drawable.Drawable; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * {@link ServiceInfo} and meta-data about a credential provider. * * @hide */ @TestApi public final class CredentialProviderInfo implements Parcelable { @NonNull private final ServiceInfo mServiceInfo; @NonNull private final List mCapabilities = new ArrayList<>(); @Nullable private final CharSequence mOverrideLabel; @Nullable private CharSequence mSettingsSubtitle = null; @Nullable private CharSequence mSettingsActivity = null; private final boolean mIsSystemProvider; private final boolean mIsEnabled; private final boolean mIsPrimary; /** * Constructs an information instance of the credential provider. * * @param builder the builder object. */ private CredentialProviderInfo(@NonNull Builder builder) { mServiceInfo = builder.mServiceInfo; mCapabilities.addAll(builder.mCapabilities); mIsSystemProvider = builder.mIsSystemProvider; mSettingsSubtitle = builder.mSettingsSubtitle; mIsEnabled = builder.mIsEnabled; mIsPrimary = builder.mIsPrimary; mOverrideLabel = builder.mOverrideLabel; mSettingsActivity = builder.mSettingsActivity; } /** Returns true if the service supports the given {@code credentialType}, false otherwise. */ @NonNull public boolean hasCapability(@NonNull String credentialType) { return mCapabilities.contains(credentialType); } /** Returns the service info. */ @NonNull public ServiceInfo getServiceInfo() { return mServiceInfo; } /** Returns whether it is a system provider. */ public boolean isSystemProvider() { return mIsSystemProvider; } /** Returns the service icon. */ @Nullable public Drawable getServiceIcon(@NonNull Context context) { return mServiceInfo.loadIcon(context.getPackageManager()); } /** Returns the service label. */ @Nullable public CharSequence getLabel(@NonNull Context context) { if (mOverrideLabel != null) { return mOverrideLabel; } return mServiceInfo.loadSafeLabel(context.getPackageManager()); } /** Returns a list of capabilities this provider service can support. */ @NonNull public List getCapabilities() { return Collections.unmodifiableList(mCapabilities); } /** Returns whether the provider is enabled by the user. */ public boolean isEnabled() { return mIsEnabled; } /** Returns whether the provider is set as primary by the user. */ public boolean isPrimary() { return mIsPrimary; } /** Returns the settings subtitle. */ @Nullable public CharSequence getSettingsSubtitle() { return mSettingsSubtitle; } /** * Returns the settings activity. * * @hide */ @Nullable @TestApi @FlaggedApi(Flags.FLAG_SETTINGS_ACTIVITY_ENABLED) public CharSequence getSettingsActivity() { // Add a manual check to make sure this returns null if // the flag is not enabled. if (!Flags.settingsActivityEnabled()) { return null; } return mSettingsActivity; } /** Returns the component name for the service. */ @NonNull public ComponentName getComponentName() { return mServiceInfo.getComponentName(); } @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeTypedObject(mServiceInfo, flags); dest.writeBoolean(mIsSystemProvider); dest.writeStringList(mCapabilities); dest.writeBoolean(mIsEnabled); dest.writeBoolean(mIsPrimary); TextUtils.writeToParcel(mOverrideLabel, dest, flags); TextUtils.writeToParcel(mSettingsSubtitle, dest, flags); TextUtils.writeToParcel(mSettingsActivity, dest, flags); } @Override public int describeContents() { return 0; } @Override public String toString() { return "CredentialProviderInfo {" + "serviceInfo=" + mServiceInfo + ", " + "isSystemProvider=" + mIsSystemProvider + ", " + "isEnabled=" + mIsEnabled + ", " + "isPrimary=" + mIsPrimary + ", " + "overrideLabel=" + mOverrideLabel + ", " + "settingsSubtitle=" + mSettingsSubtitle + ", " + "settingsActivity=" + mSettingsActivity + ", " + "capabilities=" + String.join(",", mCapabilities) + "}"; } private CredentialProviderInfo(@NonNull Parcel in) { mServiceInfo = in.readTypedObject(ServiceInfo.CREATOR); mIsSystemProvider = in.readBoolean(); in.readStringList(mCapabilities); mIsEnabled = in.readBoolean(); mIsPrimary = in.readBoolean(); mOverrideLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); mSettingsSubtitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); mSettingsActivity = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); } public static final @NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { @Override public CredentialProviderInfo[] newArray(int size) { return new CredentialProviderInfo[size]; } @Override public CredentialProviderInfo createFromParcel(@NonNull Parcel in) { return new CredentialProviderInfo(in); } }; /** A builder for {@link CredentialProviderInfo} objects. */ public static final class Builder { @NonNull private ServiceInfo mServiceInfo; @NonNull private List mCapabilities = new ArrayList<>(); private boolean mIsSystemProvider = false; @Nullable private CharSequence mSettingsSubtitle = null; @Nullable private CharSequence mSettingsActivity = null; private boolean mIsEnabled = false; private boolean mIsPrimary = false; @Nullable private CharSequence mOverrideLabel = null; /** * Creates a new builder. * * @param serviceInfo the service info of the credential provider service. */ public Builder(@NonNull ServiceInfo serviceInfo) { mServiceInfo = serviceInfo; } /** Sets whether it is a system provider. */ public @NonNull Builder setSystemProvider(boolean isSystemProvider) { mIsSystemProvider = isSystemProvider; return this; } /** * Sets the label to be used instead of getting from the system (for unit tests). * * @hide */ public @NonNull Builder setOverrideLabel(@NonNull CharSequence overrideLabel) { mOverrideLabel = overrideLabel; return this; } /** Sets the settings subtitle. */ public @NonNull Builder setSettingsSubtitle(@Nullable CharSequence settingsSubtitle) { mSettingsSubtitle = settingsSubtitle; return this; } /** * Sets the settings activity. * * @hide */ public @NonNull Builder setSettingsActivity(@Nullable CharSequence settingsActivity) { mSettingsActivity = settingsActivity; return this; } /** Sets a list of capabilities this provider service can support. */ public @NonNull Builder addCapabilities(@NonNull List capabilities) { mCapabilities.addAll(capabilities); return this; } /** Sets whether it is enabled by the user. */ public @NonNull Builder setEnabled(boolean isEnabled) { mIsEnabled = isEnabled; return this; } /** * Sets whether it is set as primary by the user. * *

Primary provider will be used for saving credentials by default. In most cases, there * should only one primary provider exist. However, if there are multiple credential * providers exist in the same package, all of them will be marked as primary. * * @hide */ public @NonNull Builder setPrimary(boolean isPrimary) { mIsPrimary = isPrimary; return this; } /** Builds a new {@link CredentialProviderInfo} instance. */ public @NonNull CredentialProviderInfo build() { return new CredentialProviderInfo(this); } } }