Implement policy transparency for CredMan

This CL implements policy transparency for
Credential Manager settings.

Screenshots:
https://hsv.googleplex.com/5502938857340928
https://hsv.googleplex.com/4999933426925568

Test: on device w/ test dpc
Bug: 318552220
Change-Id: I78e1fa47541c81e6e2d2bf2c13159be01928e77c
This commit is contained in:
Becca Hughes
2024-02-06 08:23:18 -08:00
parent 0c85e4bca9
commit 9931d03f6f
5 changed files with 125 additions and 31 deletions

View File

@@ -33,6 +33,9 @@ import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedLockUtilsInternal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
@@ -236,6 +239,31 @@ public final class CombinedProviderInfo {
return null; return null;
} }
/** Returns whether this entry contains a system provider. */
public boolean isCredentialManagerSystemProvider() {
for (CredentialProviderInfo cpi : mCredentialProviderInfos) {
if (cpi.isSystemProvider()) {
return true;
}
}
return false;
}
/** Returns whether this entry has device admin restrictions. */
@Nullable
public RestrictedLockUtils.EnforcedAdmin getDeviceAdminRestrictions(
Context context, int userId) {
final String packageName = getPackageName();
if (TextUtils.isEmpty(packageName)) {
return null;
}
return RestrictedLockUtilsInternal.checkIfApplicationCanBeCredentialManagerProvider(
context.createContextAsUser(UserHandle.of(userId), /* flags= */ 0),
packageName);
}
/** Returns the provider that gets the top spot. */ /** Returns the provider that gets the top spot. */
public static @Nullable CombinedProviderInfo getTopProvider( public static @Nullable CombinedProviderInfo getTopProvider(
List<CombinedProviderInfo> providers) { List<CombinedProviderInfo> providers) {
@@ -327,8 +355,7 @@ public final class CombinedProviderInfo {
} }
public static @Nullable Intent createSettingsActivityIntent( public static @Nullable Intent createSettingsActivityIntent(
@Nullable CharSequence packageName, @Nullable CharSequence packageName, @Nullable CharSequence settingsActivity) {
@Nullable CharSequence settingsActivity) {
if (TextUtils.isEmpty(packageName) || TextUtils.isEmpty(settingsActivity)) { if (TextUtils.isEmpty(packageName) || TextUtils.isEmpty(settingsActivity)) {
return null; return null;
} }

View File

@@ -69,8 +69,9 @@ import com.android.settings.R;
import com.android.settings.Utils; import com.android.settings.Utils;
import com.android.settings.core.BasePreferenceController; import com.android.settings.core.BasePreferenceController;
import com.android.settings.dashboard.DashboardFragment; import com.android.settings.dashboard.DashboardFragment;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedPreference;
import com.android.settingslib.utils.ThreadUtils; import com.android.settingslib.utils.ThreadUtils;
import com.android.settingslib.widget.TwoTargetPreference;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@@ -328,7 +329,8 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
setAvailableServices( setAvailableServices(
mCredentialManager.getCredentialProviderServices( mCredentialManager.getCredentialProviderServices(
getUser(), CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_ONLY), getUser(),
CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_INCLUDING_HIDDEN),
null); null);
} }
@@ -355,7 +357,8 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
// Get the list of new providers and components. // Get the list of new providers and components.
List<CredentialProviderInfo> newProviders = List<CredentialProviderInfo> newProviders =
mCredentialManager.getCredentialProviderServices( mCredentialManager.getCredentialProviderServices(
getUser(), CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_ONLY); getUser(),
CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_INCLUDING_HIDDEN);
Set<ComponentName> newComponents = buildComponentNameSet(newProviders, false); Set<ComponentName> newComponents = buildComponentNameSet(newProviders, false);
Set<ComponentName> newPrimaryComponents = buildComponentNameSet(newProviders, true); Set<ComponentName> newPrimaryComponents = buildComponentNameSet(newProviders, true);
@@ -548,7 +551,8 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
icon, icon,
packageName, packageName,
combinedInfo.getSettingsSubtitle(), combinedInfo.getSettingsSubtitle(),
combinedInfo.getSettingsActivity()); combinedInfo.getSettingsActivity(),
combinedInfo.getDeviceAdminRestrictions(context, getUser()));
output.put(packageName, pref); output.put(packageName, pref);
group.addPreference(pref); group.addPreference(pref);
} }
@@ -569,7 +573,8 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
service.getServiceIcon(mContext), service.getServiceIcon(mContext),
service.getServiceInfo().packageName, service.getServiceInfo().packageName,
service.getSettingsSubtitle(), service.getSettingsSubtitle(),
service.getSettingsActivity()); service.getSettingsActivity(),
/* enforcedCredManAdmin= */ null);
} }
/** /**
@@ -653,7 +658,8 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
@Nullable Drawable icon, @Nullable Drawable icon,
@NonNull String packageName, @NonNull String packageName,
@Nullable CharSequence subtitle, @Nullable CharSequence subtitle,
@Nullable CharSequence settingsActivity) { @Nullable CharSequence settingsActivity,
@Nullable RestrictedLockUtils.EnforcedAdmin enforcedCredManAdmin) {
final CombiPreference pref = final CombiPreference pref =
new CombiPreference(prefContext, mEnabledPackageNames.contains(packageName)); new CombiPreference(prefContext, mEnabledPackageNames.contains(packageName));
pref.setTitle(title); pref.setTitle(title);
@@ -669,6 +675,8 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
pref.setSummary(subtitle); pref.setSummary(subtitle);
} }
pref.setDisabledByAdmin(enforcedCredManAdmin);
pref.setPreferenceListener( pref.setPreferenceListener(
new CombiPreference.OnCombiPreferenceClickListener() { new CombiPreference.OnCombiPreferenceClickListener() {
@Override @Override
@@ -1005,8 +1013,8 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
} }
} }
/** CombiPreference is a combination of TwoTargetPreference and SwitchPreference. */ /** CombiPreference is a combination of RestrictedPreference and SwitchPreference. */
public static class CombiPreference extends TwoTargetPreference { public static class CombiPreference extends RestrictedPreference {
private final Listener mListener = new Listener(); private final Listener mListener = new Listener();

View File

@@ -21,6 +21,9 @@ import android.app.settings.SettingsEnums;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo; import android.content.pm.ServiceInfo;
import android.credentials.CredentialManager; import android.credentials.CredentialManager;
import android.credentials.CredentialProviderInfo; import android.credentials.CredentialProviderInfo;
@@ -39,14 +42,17 @@ import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import androidx.preference.Preference; import androidx.preference.Preference;
import com.android.internal.content.PackageMonitor; import com.android.internal.content.PackageMonitor;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.applications.defaultapps.DefaultAppPickerFragment; import com.android.settings.applications.defaultapps.DefaultAppPickerFragment;
import com.android.settingslib.RestrictedSelectorWithWidgetPreference;
import com.android.settingslib.applications.DefaultAppInfo; import com.android.settingslib.applications.DefaultAppInfo;
import com.android.settingslib.widget.CandidateInfo; import com.android.settingslib.widget.CandidateInfo;
import com.android.settingslib.widget.SelectorWithWidgetPreference;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -263,7 +269,8 @@ public class DefaultCombinedPicker extends DefaultAppPickerFragment {
if (service != null) { if (service != null) {
credManProviders.addAll( credManProviders.addAll(
service.getCredentialProviderServices( service.getCredentialProviderServices(
getUser(), CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_ONLY)); getUser(),
CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_INCLUDING_HIDDEN));
} }
final String selectedAutofillProvider = getSelectedAutofillProvider(context, getUser()); final String selectedAutofillProvider = getSelectedAutofillProvider(context, getUser());
@@ -283,30 +290,75 @@ public class DefaultCombinedPicker extends DefaultAppPickerFragment {
for (CombinedProviderInfo cpi : allProviders) { for (CombinedProviderInfo cpi : allProviders) {
ServiceInfo brandingService = cpi.getBrandingService(); ServiceInfo brandingService = cpi.getBrandingService();
if (brandingService == null) { ApplicationInfo appInfo = cpi.getApplicationInfo();
if (brandingService != null) {
candidates.add( candidates.add(
new DefaultAppInfo( new CredentialManagerDefaultAppInfo(
context, context, mPm, getUser(), brandingService, cpi));
mPm, } else if (appInfo != null) {
getUser(),
cpi.getApplicationInfo(),
cpi.getSettingsSubtitle(),
true));
} else {
candidates.add( candidates.add(
new DefaultAppInfo( new CredentialManagerDefaultAppInfo(context, mPm, getUser(), appInfo, cpi));
context,
mPm,
getUser(),
brandingService,
cpi.getSettingsSubtitle(),
true));
} }
} }
return candidates; return candidates;
} }
@Override
public void bindPreferenceExtra(
SelectorWithWidgetPreference pref,
String key,
CandidateInfo info,
String defaultKey,
String systemDefaultKey) {
super.bindPreferenceExtra(pref, key, info, defaultKey, systemDefaultKey);
if (!(info instanceof CredentialManagerDefaultAppInfo)) {
Log.e(TAG, "Candidate info should be a subclass of CredentialManagerDefaultAppInfo");
return;
}
if (!(pref instanceof RestrictedSelectorWithWidgetPreference)) {
Log.e(TAG, "Preference should be a subclass of RestrictedSelectorWithWidgetPreference");
return;
}
CredentialManagerDefaultAppInfo credmanAppInfo = (CredentialManagerDefaultAppInfo) info;
RestrictedSelectorWithWidgetPreference rp = (RestrictedSelectorWithWidgetPreference) pref;
// Apply policy transparency.
rp.setDisabledByAdmin(
credmanAppInfo
.getCombinedProviderInfo()
.getDeviceAdminRestrictions(getContext(), getUser()));
}
@Override
protected SelectorWithWidgetPreference createPreference() {
return new RestrictedSelectorWithWidgetPreference(getPrefContext());
}
/** This extends DefaultAppInfo with custom CredMan app info. */
public static class CredentialManagerDefaultAppInfo extends DefaultAppInfo {
private final CombinedProviderInfo mCombinedProviderInfo;
CredentialManagerDefaultAppInfo(
Context context,
PackageManager pm,
int uid,
PackageItemInfo info,
CombinedProviderInfo cpi) {
super(context, pm, uid, info, cpi.getSettingsSubtitle(), /* enabled= */ true);
mCombinedProviderInfo = cpi;
}
public @NonNull CombinedProviderInfo getCombinedProviderInfo() {
return mCombinedProviderInfo;
}
}
@Override @Override
protected String getDefaultKey() { protected String getDefaultKey() {
final CombinedProviderInfo topProvider = final CombinedProviderInfo topProvider =
@@ -415,7 +467,8 @@ public class DefaultCombinedPicker extends DefaultAppPickerFragment {
final List<String> credManProviders = new ArrayList<>(); final List<String> credManProviders = new ArrayList<>();
for (CredentialProviderInfo cpi : for (CredentialProviderInfo cpi :
service.getCredentialProviderServices( service.getCredentialProviderServices(
getUser(), CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_ONLY)) { getUser(),
CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_INCLUDING_HIDDEN)) {
if (cpi.isEnabled() && !cpi.isPrimary()) { if (cpi.isEnabled() && !cpi.isPrimary()) {
credManProviders.add(cpi.getServiceInfo().getComponentName().flattenToString()); credManProviders.add(cpi.getServiceInfo().getComponentName().flattenToString());

View File

@@ -111,7 +111,7 @@ public class DefaultCombinedPreferenceController extends DefaultAppPreferenceCon
@Nullable CharSequence appName, @Nullable CharSequence appName,
@Nullable String appSubtitle, @Nullable String appSubtitle,
@Nullable Drawable appIcon, @Nullable Drawable appIcon,
@Nullable CharSequence packageName, @Nullable String packageName,
@Nullable CharSequence settingsActivity) { @Nullable CharSequence settingsActivity) {
if (appName == null) { if (appName == null) {
preference.setTitle(R.string.credman_app_list_preference_none); preference.setTitle(R.string.credman_app_list_preference_none);

View File

@@ -159,6 +159,13 @@ public abstract class RadioButtonPickerFragment extends SettingsPreferenceFragme
String key, CandidateInfo info, String defaultKey, String systemDefaultKey) { String key, CandidateInfo info, String defaultKey, String systemDefaultKey) {
} }
/**
* A chance for subclasses to create a custom preference instance.
*/
protected SelectorWithWidgetPreference createPreference() {
return new SelectorWithWidgetPreference(getPrefContext());
}
public void updateCandidates() { public void updateCandidates() {
mCandidates.clear(); mCandidates.clear();
final List<? extends CandidateInfo> candidateList = getCandidates(); final List<? extends CandidateInfo> candidateList = getCandidates();
@@ -193,8 +200,7 @@ public abstract class RadioButtonPickerFragment extends SettingsPreferenceFragme
} }
if (candidateList != null) { if (candidateList != null) {
for (CandidateInfo info : candidateList) { for (CandidateInfo info : candidateList) {
SelectorWithWidgetPreference pref = SelectorWithWidgetPreference pref = createPreference();
new SelectorWithWidgetPreference(getPrefContext());
if (customLayoutResId > 0) { if (customLayoutResId > 0) {
pref.setLayoutResource(customLayoutResId); pref.setLayoutResource(customLayoutResId);
} }