diff --git a/src/com/android/settings/applications/credentials/CombinedProviderInfo.java b/src/com/android/settings/applications/credentials/CombinedProviderInfo.java index 4f278709f6b..49d7bdc6f81 100644 --- a/src/com/android/settings/applications/credentials/CombinedProviderInfo.java +++ b/src/com/android/settings/applications/credentials/CombinedProviderInfo.java @@ -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 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; } diff --git a/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java b/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java index e16219b7402..47beeb66e21 100644 --- a/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java +++ b/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java @@ -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 newProviders = mCredentialManager.getCredentialProviderServices( - getUser(), CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_ONLY); + getUser(), + CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_INCLUDING_HIDDEN); Set newComponents = buildComponentNameSet(newProviders, false); Set 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(); diff --git a/src/com/android/settings/applications/credentials/DefaultCombinedPicker.java b/src/com/android/settings/applications/credentials/DefaultCombinedPicker.java index d6f5289e07d..ff3b5c74f2b 100644 --- a/src/com/android/settings/applications/credentials/DefaultCombinedPicker.java +++ b/src/com/android/settings/applications/credentials/DefaultCombinedPicker.java @@ -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 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()); diff --git a/src/com/android/settings/applications/credentials/DefaultCombinedPreferenceController.java b/src/com/android/settings/applications/credentials/DefaultCombinedPreferenceController.java index 032402fb157..e24e226db7a 100644 --- a/src/com/android/settings/applications/credentials/DefaultCombinedPreferenceController.java +++ b/src/com/android/settings/applications/credentials/DefaultCombinedPreferenceController.java @@ -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); diff --git a/src/com/android/settings/widget/RadioButtonPickerFragment.java b/src/com/android/settings/widget/RadioButtonPickerFragment.java index 6d94fc90723..d68a91afca8 100644 --- a/src/com/android/settings/widget/RadioButtonPickerFragment.java +++ b/src/com/android/settings/widget/RadioButtonPickerFragment.java @@ -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 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); }