Add settingsActivity API to Credential Manager
This adds a flag guarded API to allow apps
to specify an app to be opened when the
app is tapped on in settings.
Test: CTS
Bug: 300014059
Change-Id: Id3760778e11ba11da2111e145ab60d7e39343418
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 40c6fa8..b65065c 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1181,6 +1181,7 @@
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 @FlaggedApi("android.credentials.flags.settings_activity_enabled") @Nullable public CharSequence getSettingsActivity();
method @Nullable public CharSequence getSettingsSubtitle();
method @NonNull public boolean hasCapability(@NonNull String);
method public boolean isEnabled();
diff --git a/core/java/android/credentials/CredentialProviderInfo.java b/core/java/android/credentials/CredentialProviderInfo.java
index 95ca011..8503072 100644
--- a/core/java/android/credentials/CredentialProviderInfo.java
+++ b/core/java/android/credentials/CredentialProviderInfo.java
@@ -16,12 +16,14 @@
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;
@@ -42,6 +44,7 @@
@NonNull private final List<String> 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;
@@ -59,6 +62,7 @@
mIsEnabled = builder.mIsEnabled;
mIsPrimary = builder.mIsPrimary;
mOverrideLabel = builder.mOverrideLabel;
+ mSettingsActivity = builder.mSettingsActivity;
}
/** Returns true if the service supports the given {@code credentialType}, false otherwise. */
@@ -104,10 +108,7 @@
return mIsEnabled;
}
- /**
- * Returns whether the provider is set as primary by the user.
- *
- */
+ /** Returns whether the provider is set as primary by the user. */
public boolean isPrimary() {
return mIsPrimary;
}
@@ -118,6 +119,18 @@
return mSettingsSubtitle;
}
+ /**
+ * Returns the settings activity.
+ *
+ * @hide
+ */
+ @Nullable
+ @TestApi
+ @FlaggedApi(Flags.FLAG_SETTINGS_ACTIVITY_ENABLED)
+ public CharSequence getSettingsActivity() {
+ return mSettingsActivity;
+ }
+
/** Returns the component name for the service. */
@NonNull
public ComponentName getComponentName() {
@@ -133,6 +146,7 @@
dest.writeBoolean(mIsPrimary);
TextUtils.writeToParcel(mOverrideLabel, dest, flags);
TextUtils.writeToParcel(mSettingsSubtitle, dest, flags);
+ TextUtils.writeToParcel(mSettingsActivity, dest, flags);
}
@Override
@@ -161,6 +175,9 @@
+ "settingsSubtitle="
+ mSettingsSubtitle
+ ", "
+ + "settingsActivity="
+ + mSettingsActivity
+ + ", "
+ "capabilities="
+ String.join(",", mCapabilities)
+ "}";
@@ -174,6 +191,7 @@
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<CredentialProviderInfo> CREATOR =
@@ -196,6 +214,7 @@
@NonNull private List<String> 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;
@@ -231,6 +250,16 @@
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<String> capabilities) {
mCapabilities.addAll(capabilities);
diff --git a/core/java/android/service/credentials/CredentialProviderInfoFactory.java b/core/java/android/service/credentials/CredentialProviderInfoFactory.java
index 0aa6a23..514d722 100644
--- a/core/java/android/service/credentials/CredentialProviderInfoFactory.java
+++ b/core/java/android/service/credentials/CredentialProviderInfoFactory.java
@@ -45,8 +45,8 @@
import android.util.Slog;
import android.util.Xml;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -135,8 +135,8 @@
}
/**
- * 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(
@@ -151,7 +151,6 @@
.setSystemProvider(isSystemProvider)
.addCapabilities(capabilities)
.build();
-
}
private static void verifyProviderPermission(ServiceInfo serviceInfo) throws SecurityException {
@@ -267,15 +266,21 @@
allAttributes,
com.android.internal.R.styleable.CredentialProvider);
builder.setSettingsSubtitle(
- afsAttributes.getString(
+ getAfsAttributeSafe(
+ afsAttributes,
R.styleable.CredentialProvider_settingsSubtitle));
+ builder.setSettingsActivity(
+ getAfsAttributeSafe(
+ afsAttributes,
+ R.styleable.CredentialProvider_settingsActivity));
} catch (Exception e) {
- Slog.e(TAG, "Failed to get XML attr", e);
+ Slog.w(TAG, "Failed to get XML attr for metadata", e);
} finally {
if (afsAttributes != null) {
afsAttributes.recycle();
}
}
+
builder.addCapabilities(parseXmlProviderOuterCapabilities(parser, resources));
} else {
Slog.w(TAG, "Meta-data does not start with credential-provider-service tag");
@@ -287,6 +292,21 @@
return builder;
}
+ private static @Nullable String getAfsAttributeSafe(
+ @Nullable TypedArray afsAttributes, int resId) {
+ if (afsAttributes == null) {
+ return null;
+ }
+
+ try {
+ return afsAttributes.getString(resId);
+ } catch (Exception e) {
+ Slog.w(TAG, "Failed to get XML attr from afs attributes", e);
+ }
+
+ return null;
+ }
+
private static List<String> parseXmlProviderOuterCapabilities(
XmlPullParser parser, Resources resources) throws IOException, XmlPullParserException {
final List<String> capabilities = new ArrayList<>();
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index e54347f..04fd70a 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -10118,6 +10118,9 @@
screen that can be used to provide more information about a provider. For
longer strings it will be truncated. -->
<attr name="settingsSubtitle" format="string" />
+ <!-- Fully qualified class name of an activity that allows the user to modify
+ the settings for this service. -->
+ <attr name="settingsActivity" />
</declare-styleable>
<!-- A list of capabilities that indicates to the OS what kinds of credentials