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.Nullable;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedLockUtilsInternal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -236,6 +239,31 @@ public final class CombinedProviderInfo {
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. */
public static @Nullable CombinedProviderInfo getTopProvider(
List<CombinedProviderInfo> providers) {
@@ -327,8 +355,7 @@ public final class CombinedProviderInfo {
}
public static @Nullable Intent createSettingsActivityIntent(
@Nullable CharSequence packageName,
@Nullable CharSequence settingsActivity) {
@Nullable CharSequence packageName, @Nullable CharSequence settingsActivity) {
if (TextUtils.isEmpty(packageName) || TextUtils.isEmpty(settingsActivity)) {
return null;
}

View File

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

View File

@@ -21,6 +21,9 @@ import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.DialogInterface;
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.credentials.CredentialManager;
import android.credentials.CredentialProviderInfo;
@@ -39,14 +42,17 @@ import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.preference.Preference;
import com.android.internal.content.PackageMonitor;
import com.android.settings.R;
import com.android.settings.applications.defaultapps.DefaultAppPickerFragment;
import com.android.settingslib.RestrictedSelectorWithWidgetPreference;
import com.android.settingslib.applications.DefaultAppInfo;
import com.android.settingslib.widget.CandidateInfo;
import com.android.settingslib.widget.SelectorWithWidgetPreference;
import java.util.ArrayList;
import java.util.List;
@@ -263,7 +269,8 @@ public class DefaultCombinedPicker extends DefaultAppPickerFragment {
if (service != null) {
credManProviders.addAll(
service.getCredentialProviderServices(
getUser(), CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_ONLY));
getUser(),
CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_INCLUDING_HIDDEN));
}
final String selectedAutofillProvider = getSelectedAutofillProvider(context, getUser());
@@ -283,30 +290,75 @@ public class DefaultCombinedPicker extends DefaultAppPickerFragment {
for (CombinedProviderInfo cpi : allProviders) {
ServiceInfo brandingService = cpi.getBrandingService();
if (brandingService == null) {
ApplicationInfo appInfo = cpi.getApplicationInfo();
if (brandingService != null) {
candidates.add(
new DefaultAppInfo(
context,
mPm,
getUser(),
cpi.getApplicationInfo(),
cpi.getSettingsSubtitle(),
true));
} else {
new CredentialManagerDefaultAppInfo(
context, mPm, getUser(), brandingService, cpi));
} else if (appInfo != null) {
candidates.add(
new DefaultAppInfo(
context,
mPm,
getUser(),
brandingService,
cpi.getSettingsSubtitle(),
true));
new CredentialManagerDefaultAppInfo(context, mPm, getUser(), appInfo, cpi));
}
}
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
protected String getDefaultKey() {
final CombinedProviderInfo topProvider =
@@ -415,7 +467,8 @@ public class DefaultCombinedPicker extends DefaultAppPickerFragment {
final List<String> credManProviders = new ArrayList<>();
for (CredentialProviderInfo cpi :
service.getCredentialProviderServices(
getUser(), CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_ONLY)) {
getUser(),
CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_INCLUDING_HIDDEN)) {
if (cpi.isEnabled() && !cpi.isPrimary()) {
credManProviders.add(cpi.getServiceInfo().getComponentName().flattenToString());

View File

@@ -111,7 +111,7 @@ public class DefaultCombinedPreferenceController extends DefaultAppPreferenceCon
@Nullable CharSequence appName,
@Nullable String appSubtitle,
@Nullable Drawable appIcon,
@Nullable CharSequence packageName,
@Nullable String packageName,
@Nullable CharSequence settingsActivity) {
if (appName == null) {
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) {
}
/**
* A chance for subclasses to create a custom preference instance.
*/
protected SelectorWithWidgetPreference createPreference() {
return new SelectorWithWidgetPreference(getPrefContext());
}
public void updateCandidates() {
mCandidates.clear();
final List<? extends CandidateInfo> candidateList = getCandidates();
@@ -193,8 +200,7 @@ public abstract class RadioButtonPickerFragment extends SettingsPreferenceFragme
}
if (candidateList != null) {
for (CandidateInfo info : candidateList) {
SelectorWithWidgetPreference pref =
new SelectorWithWidgetPreference(getPrefContext());
SelectorWithWidgetPreference pref = createPreference();
if (customLayoutResId > 0) {
pref.setLayoutResource(customLayoutResId);
}