Loading src/com/android/settings/applications/credentials/CombinedProviderInfo.java +4 −1 Original line number Diff line number Diff line Loading @@ -237,7 +237,10 @@ public final class CombinedProviderInfo { List<CredentialProviderInfo> cpi = credmanServices.get(packageName); // If there are multiple autofill services then pick the first one. AutofillServiceInfo selectedAsi = asi.isEmpty() ? null : asi.get(0); AutofillServiceInfo selectedAsi = null; if (asi != null && !asi.isEmpty()) { selectedAsi = asi.get(0); } // Check if we are the default autofill provider. boolean isDefaultAutofillProvider = false; Loading src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java +64 −98 Original line number Diff line number Diff line Loading @@ -41,8 +41,8 @@ import android.os.Bundle; import android.os.Handler; import android.os.OutcomeReceiver; import android.os.UserHandle; import android.provider.DeviceConfig; import android.provider.Settings; import android.service.autofill.AutofillServiceInfo; import android.text.TextUtils; import android.util.IconDrawableFactory; import android.util.Log; Loading Loading @@ -79,6 +79,15 @@ import java.util.concurrent.Executor; public class CredentialManagerPreferenceController extends BasePreferenceController implements LifecycleObserver { public static final String ADD_SERVICE_DEVICE_CONFIG = "credential_manager_service_search_uri"; /** * In the settings logic we should hide the list of additional credman providers if there is no * provider selected at the top. The current logic relies on checking whether the autofill * provider is set which won't work for cred-man only providers. Therefore when a CM only * provider is set we will set the autofill setting to be this placeholder. */ public static final String AUTOFILL_CREDMAN_ONLY_PROVIDER_PLACEHOLDER = "credential-provider"; private static final String TAG = "CredentialManagerPreferenceController"; private static final String ALTERNATE_INTENT = "android.settings.SYNC_SETTINGS"; private static final String PRIMARY_INTENT = "android.settings.CREDENTIAL_PROVIDER"; Loading @@ -99,6 +108,8 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl private @Nullable String mFlagOverrideForTest = null; private @Nullable PreferenceScreen mPreferenceScreen = null; private boolean mVisibility = false; public CredentialManagerPreferenceController(Context context, String preferenceKey) { super(context, preferenceKey); mPm = context.getPackageManager(); Loading Loading @@ -131,7 +142,7 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl return UNSUPPORTED_ON_DEVICE; } if (!isAutofillPrefSelected()) { if (!mVisibility) { return CONDITIONALLY_UNAVAILABLE; } Loading Loading @@ -271,7 +282,7 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl setAvailableServices( mCredentialManager.getCredentialProviderServices( getUser(), CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_ONLY), getUser(), CredentialManager.PROVIDER_FILTER_ALL_PROVIDERS), null); } Loading @@ -287,6 +298,17 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl } } private void setVisibility(boolean newVisibility) { if (newVisibility == mVisibility) { return; } mVisibility = newVisibility; if (mDelegate != null) { mDelegate.forceDelegateRefresh(); } } @VisibleForTesting void setAvailableServices( List<CredentialProviderInfo> availableServices, String flagOverrideForTest) { Loading Loading @@ -320,48 +342,8 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl PreferenceGroup group = screen.findPreference(getPreferenceKey()); group.removeAll(); // Hide/show based on autofill pref. boolean isVisible = isAutofillPrefSelected(); screen.setVisible(isVisible); group.setVisible(isVisible); if (!isVisible) { return; } Context context = screen.getContext(); mPrefs.putAll(buildPreferenceList(context, group)); // Add the "add service" button only when there are no providers. if (mPrefs.isEmpty()) { String searchUri = getAddServiceUri(context); if (!TextUtils.isEmpty(searchUri)) { group.addPreference(newAddServicePreference(searchUri, context)); } } } /** * Returns the "add service" URI to show the play store. It will first try and use the * credential manager specific search URI and if that is null it will fallback to the autofill * one. */ public @NonNull String getAddServiceUri(@NonNull Context context) { // Check the credential manager gflag for a link. String searchUri = DeviceConfig.getString( DeviceConfig.NAMESPACE_CREDENTIAL, ADD_SERVICE_DEVICE_CONFIG, mFlagOverrideForTest); if (!TextUtils.isEmpty(searchUri)) { return searchUri; } // If not fall back on autofill. return Settings.Secure.getStringForUser( context.getContentResolver(), Settings.Secure.AUTOFILL_SERVICE_SEARCH_URI, getUser()); } /** Loading Loading @@ -396,52 +378,59 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl @VisibleForTesting public Map<String, SwitchPreference> buildPreferenceList( Context context, PreferenceGroup group) { // Group the services by package name. Map<String, List<CredentialProviderInfo>> groupedInfos = new HashMap<>(); for (CredentialProviderInfo cpi : mServices) { String packageName = cpi.getServiceInfo().packageName; if (isProviderHiddenBecauseOfAutofill(packageName)) { continue; // Get the selected autofill provider. If it is the placeholder then replace it with an // empty string. String selectedAutofillProvider = DefaultCombinedPicker.getSelectedAutofillProvider(mContext, getUser()); if (TextUtils.equals( selectedAutofillProvider, AUTOFILL_CREDMAN_ONLY_PROVIDER_PLACEHOLDER)) { selectedAutofillProvider = ""; } // Get the list of combined providers. List<CombinedProviderInfo> providers = CombinedProviderInfo.buildMergedList( AutofillServiceInfo.getAvailableServices(context, getUser()), mServices, selectedAutofillProvider); // Get the provider that is displayed at the top. If there is none then hide // everything. CombinedProviderInfo topProvider = CombinedProviderInfo.getTopProvider(providers); if (topProvider == null) { setVisibility(false); return new HashMap<>(); } if (!groupedInfos.containsKey(packageName)) { groupedInfos.put(packageName, new ArrayList<>()); } Map<String, SwitchPreference> output = new HashMap<>(); for (CombinedProviderInfo combinedInfo : providers) { final String packageName = combinedInfo.getApplicationInfo().packageName; groupedInfos.get(packageName).add(cpi); // If this provider is displayed at the top then we should not show it. if (topProvider != null && topProvider.getApplicationInfo().packageName.equals(packageName)) { continue; } // Build the pref list. Map<String, SwitchPreference> output = new HashMap<>(); for (String packageName : groupedInfos.keySet()) { List<CredentialProviderInfo> infos = groupedInfos.get(packageName); CredentialProviderInfo firstInfo = infos.get(0); ServiceInfo firstServiceInfo = firstInfo.getServiceInfo(); CharSequence title = firstInfo.getLabel(context); Drawable icon = firstInfo.getServiceIcon(context); if (infos.size() > 1) { // If there is more than one then group them under the package. ApplicationInfo appInfo = firstServiceInfo.applicationInfo; if (appInfo.nonLocalizedLabel != null) { title = appInfo.loadLabel(mPm); } icon = mIconFactory.getBadgedIcon(appInfo, getUser()); // If this is an autofill provider then don't show it here. if (combinedInfo.getCredentialProviderInfos().isEmpty()) { continue; } // If there is no title then show the package manager. if (TextUtils.isEmpty(title)) { title = firstServiceInfo.packageName; } Drawable icon = combinedInfo.getAppIcon(context); CharSequence title = combinedInfo.getAppName(context); // Build the pref and add it to the output & group. SwitchPreference pref = addProviderPreference( context, title, icon, packageName, firstInfo.getSettingsSubtitle()); context, title, icon, packageName, combinedInfo.getSettingsSubtitle()); output.put(packageName, pref); group.addPreference(pref); } // Set the visibility if we have services. setVisibility(!output.isEmpty()); return output; } Loading Loading @@ -607,29 +596,6 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl return new NewProviderConfirmationDialogFragment(host, packageName, appName); } /** If the provider is also the autofill provider then hide it. */ @VisibleForTesting public boolean isProviderHiddenBecauseOfAutofill(String packageName) { final String autofillService = Settings.Secure.getStringForUser( mContext.getContentResolver(), Settings.Secure.AUTOFILL_SERVICE, getUser()); if (autofillService == null || TextUtils.isEmpty(autofillService)) { return false; } if (packageName == null || TextUtils.isEmpty(packageName)) { return false; } return autofillService.startsWith(packageName); } private boolean isAutofillPrefSelected() { final String autofillService = Settings.Secure.getStringForUser( mContext.getContentResolver(), Settings.Secure.AUTOFILL_SERVICE, getUser()); return !TextUtils.isEmpty(autofillService); } @VisibleForTesting void completeEnableProviderDialogBox( int whichButton, String packageName, boolean setActivityResult) { Loading src/com/android/settings/applications/credentials/DefaultCombinedPicker.java +140 −24 Original line number Diff line number Diff line Loading @@ -16,21 +16,27 @@ package com.android.settings.applications.credentials; import android.annotation.Nullable; import android.app.Activity; import android.app.settings.SettingsEnums; import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.ServiceInfo; import android.credentials.CredentialManager; import android.credentials.CredentialProviderInfo; import android.credentials.SetEnabledProvidersException; import android.net.Uri; import android.os.Bundle; import android.os.OutcomeReceiver; import android.os.UserHandle; import android.provider.Settings; import android.service.autofill.AutofillService; import android.service.autofill.AutofillServiceInfo; import android.text.Html; import android.text.TextUtils; import android.util.Log; import androidx.core.content.ContextCompat; import androidx.preference.Preference; import com.android.internal.content.PackageMonitor; Loading @@ -47,8 +53,8 @@ public class DefaultCombinedPicker extends DefaultAppPickerFragment { private static final String TAG = "DefaultCombinedPicker"; public static final String SETTING = Settings.Secure.AUTOFILL_SERVICE; public static final Intent AUTOFILL_PROBE = new Intent(AutofillService.SERVICE_INTERFACE); public static final String AUTOFILL_SETTING = Settings.Secure.AUTOFILL_SERVICE; public static final String CREDENTIAL_SETTING = Settings.Secure.CREDENTIAL_SERVICE; /** Extra set when the fragment is implementing ACTION_REQUEST_SET_AUTOFILL_SERVICE. */ public static final String EXTRA_PACKAGE_NAME = "package_name"; Loading @@ -56,6 +62,8 @@ public class DefaultCombinedPicker extends DefaultAppPickerFragment { /** Set when the fragment is implementing ACTION_REQUEST_SET_AUTOFILL_SERVICE. */ private DialogInterface.OnClickListener mCancelListener; private CredentialManager mCredentialManager; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Loading @@ -71,6 +79,7 @@ public class DefaultCombinedPicker extends DefaultAppPickerFragment { // ACTION_REQUEST_SET_AUTOFILL_SERVICE and we should always use the calling uid. mUserId = UserHandle.myUserId(); } mSettingsPackageMonitor.register(activity, activity.getMainLooper(), false); update(); } Loading Loading @@ -187,35 +196,76 @@ public class DefaultCombinedPicker extends DefaultAppPickerFragment { } } @Override /** * Get the Credential Manager service if we haven't already got it. We need to get the service * later because if we do it in onCreate it will fail. */ private @Nullable CredentialManager getCredentialProviderService() { if (mCredentialManager == null) { mCredentialManager = getContext().getSystemService(CredentialManager.class); } return mCredentialManager; } private List<CombinedProviderInfo> getAllProviders() { final Context context = getContext(); final List<AutofillServiceInfo> autofillProviders = AutofillServiceInfo.getAvailableServices(context, mUserId); final CredentialManager service = getCredentialProviderService(); final List<CredentialProviderInfo> credManProviders = new ArrayList<>(); if (service != null) { credManProviders.addAll( service.getCredentialProviderServices( mUserId, CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_ONLY)); } final String selectedAutofillProvider = getSelectedAutofillProvider(context, mUserId); return CombinedProviderInfo.buildMergedList( autofillProviders, credManProviders, selectedAutofillProvider); } public static String getSelectedAutofillProvider(Context context, int userId) { return Settings.Secure.getStringForUser( context.getContentResolver(), AUTOFILL_SETTING, userId); } protected List<DefaultAppInfo> getCandidates() { final Context context = getContext(); final List<CombinedProviderInfo> allProviders = getAllProviders(); final List<DefaultAppInfo> candidates = new ArrayList<>(); final List<AutofillServiceInfo> services = AutofillServiceInfo.getAvailableServices(getContext(), mUserId); for (AutofillServiceInfo asi : services) { for (CombinedProviderInfo cpi : allProviders) { ServiceInfo brandingService = cpi.getBrandingService(); if (brandingService == null) { candidates.add( new DefaultAppInfo( getContext(), mPm, mUserId, asi.getServiceInfo().getComponentName())); context, mPm, mUserId, cpi.getApplicationInfo(), cpi.getSettingsSubtitle(), true)); } else { candidates.add( new DefaultAppInfo( context, mPm, mUserId, brandingService, cpi.getSettingsSubtitle(), true)); } return candidates; } public static String getDefaultKey(Context context, int userId) { String setting = Settings.Secure.getStringForUser(context.getContentResolver(), SETTING, userId); if (setting != null) { ComponentName componentName = ComponentName.unflattenFromString(setting); if (componentName != null) { return componentName.flattenToString(); } } return null; return candidates; } @Override protected String getDefaultKey() { return getDefaultKey(getContext(), mUserId); final CombinedProviderInfo topProvider = CombinedProviderInfo.getTopProvider(getAllProviders()); return topProvider == null ? "" : topProvider.getApplicationInfo().packageName; } @Override Loading @@ -234,7 +284,39 @@ public class DefaultCombinedPicker extends DefaultAppPickerFragment { @Override protected boolean setDefaultKey(String key) { Settings.Secure.putStringForUser(getContext().getContentResolver(), SETTING, key, mUserId); // Get the list of providers and see if any match the key (package name). final List<CombinedProviderInfo> allProviders = getAllProviders(); CombinedProviderInfo matchedProvider = null; for (CombinedProviderInfo cpi : allProviders) { if (cpi.getApplicationInfo().packageName.equals(key)) { matchedProvider = cpi; break; } } // If there were none then clear the stored providers. if (matchedProvider == null) { setProviders(null, new ArrayList<>()); return true; } // Get the component names and save them. final List<String> credManComponents = new ArrayList<>(); for (CredentialProviderInfo pi : matchedProvider.getCredentialProviderInfos()) { credManComponents.add(pi.getServiceInfo().getComponentName().flattenToString()); } String autofillValue = null; if (matchedProvider.getAutofillServiceInfo() != null) { autofillValue = matchedProvider .getAutofillServiceInfo() .getServiceInfo() .getComponentName() .flattenToString(); } setProviders(autofillValue, credManComponents); // Check if activity was launched from Settings.ACTION_REQUEST_SET_AUTOFILL_SERVICE // intent, and set proper result if so... Loading @@ -255,4 +337,38 @@ public class DefaultCombinedPicker extends DefaultAppPickerFragment { return true; } private void setProviders(String autofillProvider, List<String> credManProviders) { if (TextUtils.isEmpty(autofillProvider)) { if (credManProviders.size() > 0) { autofillProvider = CredentialManagerPreferenceController .AUTOFILL_CREDMAN_ONLY_PROVIDER_PLACEHOLDER; } } Settings.Secure.putStringForUser( getContext().getContentResolver(), AUTOFILL_SETTING, autofillProvider, mUserId); CredentialManager service = getCredentialProviderService(); if (service == null) { return; } service.setEnabledProviders( credManProviders, mUserId, ContextCompat.getMainExecutor(getContext()), new OutcomeReceiver<Void, SetEnabledProvidersException>() { @Override public void onResult(Void result) { Log.i(TAG, "setEnabledProviders success"); } @Override public void onError(SetEnabledProvidersException e) { Log.e(TAG, "setEnabledProviders error: " + e.toString()); } }); } } src/com/android/settings/applications/credentials/DefaultCombinedPreferenceController.java +49 −17 Original line number Diff line number Diff line Loading @@ -23,7 +23,9 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.credentials.CredentialManager; import android.credentials.CredentialProviderInfo; import android.provider.Settings; import android.service.autofill.AutofillService; import android.service.autofill.AutofillServiceInfo; import android.text.TextUtils; import android.util.Log; Loading @@ -36,6 +38,9 @@ import java.util.List; public class DefaultCombinedPreferenceController extends DefaultAppPreferenceController { private static final Intent AUTOFILL_PROBE = new Intent(AutofillService.SERVICE_INTERFACE); private static final String TAG = "DefaultCombinedPreferenceController"; private final AutofillManager mAutofillManager; private final CredentialManager mCredentialManager; Loading Loading @@ -76,26 +81,57 @@ public class DefaultCombinedPreferenceController extends DefaultAppPreferenceCon @Override protected DefaultAppInfo getDefaultAppInfo() { final String flattenComponent = Settings.Secure.getString( mContext.getContentResolver(), DefaultCombinedPicker.SETTING); if (!TextUtils.isEmpty(flattenComponent)) { DefaultAppInfo appInfo = new DefaultAppInfo( List<CombinedProviderInfo> providers = getAllProviders(mUserId); CombinedProviderInfo topProvider = CombinedProviderInfo.getTopProvider(providers); if (topProvider != null) { ServiceInfo brandingService = topProvider.getBrandingService(); if (brandingService == null) { return new DefaultAppInfo( mContext, mPackageManager, mUserId, ComponentName.unflattenFromString(flattenComponent)); return appInfo; topProvider.getApplicationInfo(), topProvider.getSettingsSubtitle(), true); } else { return new DefaultAppInfo( mContext, mPackageManager, mUserId, brandingService, topProvider.getSettingsSubtitle(), true); } } return null; } private List<CombinedProviderInfo> getAllProviders(int userId) { final List<AutofillServiceInfo> autofillProviders = AutofillServiceInfo.getAvailableServices(mContext, userId); final List<CredentialProviderInfo> credManProviders = mCredentialManager.getCredentialProviderServices( userId, CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_ONLY); final String selectedAutofillProvider = Settings.Secure.getStringForUser( mContext.getContentResolver(), DefaultCombinedPicker.AUTOFILL_SETTING, userId); return CombinedProviderInfo.buildMergedList( autofillProviders, credManProviders, selectedAutofillProvider); } @Override protected boolean showLabelAsTitle() { return true; } @Override protected boolean showAppSummary() { return true; } /** Provides Intent to setting activity for the specified autofill service. */ static final class AutofillSettingIntentProvider { Loading @@ -113,9 +149,7 @@ public class DefaultCombinedPreferenceController extends DefaultAppPreferenceCon final List<ResolveInfo> resolveInfos = mContext.getPackageManager() .queryIntentServicesAsUser( DefaultCombinedPicker.AUTOFILL_PROBE, PackageManager.GET_META_DATA, mUserId); AUTOFILL_PROBE, PackageManager.GET_META_DATA, mUserId); for (ResolveInfo resolveInfo : resolveInfos) { final ServiceInfo serviceInfo = resolveInfo.serviceInfo; Loading @@ -130,9 +164,7 @@ public class DefaultCombinedPreferenceController extends DefaultAppPreferenceCon .getSettingsActivity(); } catch (SecurityException e) { // Service does not declare the proper permission, ignore it. Log.w( "AutofillSettingIntentProvider", "Error getting info for " + serviceInfo + ": " + e); Log.w(TAG, "Error getting info for " + serviceInfo + ": " + e); return null; } if (TextUtils.isEmpty(settingsActivity)) { Loading src/com/android/settings/applications/credentials/DefaultWorkCombinedPreferenceController.java +1 −1 Original line number Diff line number Diff line Loading @@ -52,7 +52,7 @@ public class DefaultWorkCombinedPreferenceController extends DefaultCombinedPref final String flattenComponent = Settings.Secure.getStringForUser( mContext.getContentResolver(), DefaultCombinedPicker.SETTING, DefaultCombinedPicker.AUTOFILL_SETTING, mUserHandle.getIdentifier()); if (!TextUtils.isEmpty(flattenComponent)) { DefaultAppInfo appInfo = Loading Loading
src/com/android/settings/applications/credentials/CombinedProviderInfo.java +4 −1 Original line number Diff line number Diff line Loading @@ -237,7 +237,10 @@ public final class CombinedProviderInfo { List<CredentialProviderInfo> cpi = credmanServices.get(packageName); // If there are multiple autofill services then pick the first one. AutofillServiceInfo selectedAsi = asi.isEmpty() ? null : asi.get(0); AutofillServiceInfo selectedAsi = null; if (asi != null && !asi.isEmpty()) { selectedAsi = asi.get(0); } // Check if we are the default autofill provider. boolean isDefaultAutofillProvider = false; Loading
src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java +64 −98 Original line number Diff line number Diff line Loading @@ -41,8 +41,8 @@ import android.os.Bundle; import android.os.Handler; import android.os.OutcomeReceiver; import android.os.UserHandle; import android.provider.DeviceConfig; import android.provider.Settings; import android.service.autofill.AutofillServiceInfo; import android.text.TextUtils; import android.util.IconDrawableFactory; import android.util.Log; Loading Loading @@ -79,6 +79,15 @@ import java.util.concurrent.Executor; public class CredentialManagerPreferenceController extends BasePreferenceController implements LifecycleObserver { public static final String ADD_SERVICE_DEVICE_CONFIG = "credential_manager_service_search_uri"; /** * In the settings logic we should hide the list of additional credman providers if there is no * provider selected at the top. The current logic relies on checking whether the autofill * provider is set which won't work for cred-man only providers. Therefore when a CM only * provider is set we will set the autofill setting to be this placeholder. */ public static final String AUTOFILL_CREDMAN_ONLY_PROVIDER_PLACEHOLDER = "credential-provider"; private static final String TAG = "CredentialManagerPreferenceController"; private static final String ALTERNATE_INTENT = "android.settings.SYNC_SETTINGS"; private static final String PRIMARY_INTENT = "android.settings.CREDENTIAL_PROVIDER"; Loading @@ -99,6 +108,8 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl private @Nullable String mFlagOverrideForTest = null; private @Nullable PreferenceScreen mPreferenceScreen = null; private boolean mVisibility = false; public CredentialManagerPreferenceController(Context context, String preferenceKey) { super(context, preferenceKey); mPm = context.getPackageManager(); Loading Loading @@ -131,7 +142,7 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl return UNSUPPORTED_ON_DEVICE; } if (!isAutofillPrefSelected()) { if (!mVisibility) { return CONDITIONALLY_UNAVAILABLE; } Loading Loading @@ -271,7 +282,7 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl setAvailableServices( mCredentialManager.getCredentialProviderServices( getUser(), CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_ONLY), getUser(), CredentialManager.PROVIDER_FILTER_ALL_PROVIDERS), null); } Loading @@ -287,6 +298,17 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl } } private void setVisibility(boolean newVisibility) { if (newVisibility == mVisibility) { return; } mVisibility = newVisibility; if (mDelegate != null) { mDelegate.forceDelegateRefresh(); } } @VisibleForTesting void setAvailableServices( List<CredentialProviderInfo> availableServices, String flagOverrideForTest) { Loading Loading @@ -320,48 +342,8 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl PreferenceGroup group = screen.findPreference(getPreferenceKey()); group.removeAll(); // Hide/show based on autofill pref. boolean isVisible = isAutofillPrefSelected(); screen.setVisible(isVisible); group.setVisible(isVisible); if (!isVisible) { return; } Context context = screen.getContext(); mPrefs.putAll(buildPreferenceList(context, group)); // Add the "add service" button only when there are no providers. if (mPrefs.isEmpty()) { String searchUri = getAddServiceUri(context); if (!TextUtils.isEmpty(searchUri)) { group.addPreference(newAddServicePreference(searchUri, context)); } } } /** * Returns the "add service" URI to show the play store. It will first try and use the * credential manager specific search URI and if that is null it will fallback to the autofill * one. */ public @NonNull String getAddServiceUri(@NonNull Context context) { // Check the credential manager gflag for a link. String searchUri = DeviceConfig.getString( DeviceConfig.NAMESPACE_CREDENTIAL, ADD_SERVICE_DEVICE_CONFIG, mFlagOverrideForTest); if (!TextUtils.isEmpty(searchUri)) { return searchUri; } // If not fall back on autofill. return Settings.Secure.getStringForUser( context.getContentResolver(), Settings.Secure.AUTOFILL_SERVICE_SEARCH_URI, getUser()); } /** Loading Loading @@ -396,52 +378,59 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl @VisibleForTesting public Map<String, SwitchPreference> buildPreferenceList( Context context, PreferenceGroup group) { // Group the services by package name. Map<String, List<CredentialProviderInfo>> groupedInfos = new HashMap<>(); for (CredentialProviderInfo cpi : mServices) { String packageName = cpi.getServiceInfo().packageName; if (isProviderHiddenBecauseOfAutofill(packageName)) { continue; // Get the selected autofill provider. If it is the placeholder then replace it with an // empty string. String selectedAutofillProvider = DefaultCombinedPicker.getSelectedAutofillProvider(mContext, getUser()); if (TextUtils.equals( selectedAutofillProvider, AUTOFILL_CREDMAN_ONLY_PROVIDER_PLACEHOLDER)) { selectedAutofillProvider = ""; } // Get the list of combined providers. List<CombinedProviderInfo> providers = CombinedProviderInfo.buildMergedList( AutofillServiceInfo.getAvailableServices(context, getUser()), mServices, selectedAutofillProvider); // Get the provider that is displayed at the top. If there is none then hide // everything. CombinedProviderInfo topProvider = CombinedProviderInfo.getTopProvider(providers); if (topProvider == null) { setVisibility(false); return new HashMap<>(); } if (!groupedInfos.containsKey(packageName)) { groupedInfos.put(packageName, new ArrayList<>()); } Map<String, SwitchPreference> output = new HashMap<>(); for (CombinedProviderInfo combinedInfo : providers) { final String packageName = combinedInfo.getApplicationInfo().packageName; groupedInfos.get(packageName).add(cpi); // If this provider is displayed at the top then we should not show it. if (topProvider != null && topProvider.getApplicationInfo().packageName.equals(packageName)) { continue; } // Build the pref list. Map<String, SwitchPreference> output = new HashMap<>(); for (String packageName : groupedInfos.keySet()) { List<CredentialProviderInfo> infos = groupedInfos.get(packageName); CredentialProviderInfo firstInfo = infos.get(0); ServiceInfo firstServiceInfo = firstInfo.getServiceInfo(); CharSequence title = firstInfo.getLabel(context); Drawable icon = firstInfo.getServiceIcon(context); if (infos.size() > 1) { // If there is more than one then group them under the package. ApplicationInfo appInfo = firstServiceInfo.applicationInfo; if (appInfo.nonLocalizedLabel != null) { title = appInfo.loadLabel(mPm); } icon = mIconFactory.getBadgedIcon(appInfo, getUser()); // If this is an autofill provider then don't show it here. if (combinedInfo.getCredentialProviderInfos().isEmpty()) { continue; } // If there is no title then show the package manager. if (TextUtils.isEmpty(title)) { title = firstServiceInfo.packageName; } Drawable icon = combinedInfo.getAppIcon(context); CharSequence title = combinedInfo.getAppName(context); // Build the pref and add it to the output & group. SwitchPreference pref = addProviderPreference( context, title, icon, packageName, firstInfo.getSettingsSubtitle()); context, title, icon, packageName, combinedInfo.getSettingsSubtitle()); output.put(packageName, pref); group.addPreference(pref); } // Set the visibility if we have services. setVisibility(!output.isEmpty()); return output; } Loading Loading @@ -607,29 +596,6 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl return new NewProviderConfirmationDialogFragment(host, packageName, appName); } /** If the provider is also the autofill provider then hide it. */ @VisibleForTesting public boolean isProviderHiddenBecauseOfAutofill(String packageName) { final String autofillService = Settings.Secure.getStringForUser( mContext.getContentResolver(), Settings.Secure.AUTOFILL_SERVICE, getUser()); if (autofillService == null || TextUtils.isEmpty(autofillService)) { return false; } if (packageName == null || TextUtils.isEmpty(packageName)) { return false; } return autofillService.startsWith(packageName); } private boolean isAutofillPrefSelected() { final String autofillService = Settings.Secure.getStringForUser( mContext.getContentResolver(), Settings.Secure.AUTOFILL_SERVICE, getUser()); return !TextUtils.isEmpty(autofillService); } @VisibleForTesting void completeEnableProviderDialogBox( int whichButton, String packageName, boolean setActivityResult) { Loading
src/com/android/settings/applications/credentials/DefaultCombinedPicker.java +140 −24 Original line number Diff line number Diff line Loading @@ -16,21 +16,27 @@ package com.android.settings.applications.credentials; import android.annotation.Nullable; import android.app.Activity; import android.app.settings.SettingsEnums; import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.ServiceInfo; import android.credentials.CredentialManager; import android.credentials.CredentialProviderInfo; import android.credentials.SetEnabledProvidersException; import android.net.Uri; import android.os.Bundle; import android.os.OutcomeReceiver; import android.os.UserHandle; import android.provider.Settings; import android.service.autofill.AutofillService; import android.service.autofill.AutofillServiceInfo; import android.text.Html; import android.text.TextUtils; import android.util.Log; import androidx.core.content.ContextCompat; import androidx.preference.Preference; import com.android.internal.content.PackageMonitor; Loading @@ -47,8 +53,8 @@ public class DefaultCombinedPicker extends DefaultAppPickerFragment { private static final String TAG = "DefaultCombinedPicker"; public static final String SETTING = Settings.Secure.AUTOFILL_SERVICE; public static final Intent AUTOFILL_PROBE = new Intent(AutofillService.SERVICE_INTERFACE); public static final String AUTOFILL_SETTING = Settings.Secure.AUTOFILL_SERVICE; public static final String CREDENTIAL_SETTING = Settings.Secure.CREDENTIAL_SERVICE; /** Extra set when the fragment is implementing ACTION_REQUEST_SET_AUTOFILL_SERVICE. */ public static final String EXTRA_PACKAGE_NAME = "package_name"; Loading @@ -56,6 +62,8 @@ public class DefaultCombinedPicker extends DefaultAppPickerFragment { /** Set when the fragment is implementing ACTION_REQUEST_SET_AUTOFILL_SERVICE. */ private DialogInterface.OnClickListener mCancelListener; private CredentialManager mCredentialManager; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Loading @@ -71,6 +79,7 @@ public class DefaultCombinedPicker extends DefaultAppPickerFragment { // ACTION_REQUEST_SET_AUTOFILL_SERVICE and we should always use the calling uid. mUserId = UserHandle.myUserId(); } mSettingsPackageMonitor.register(activity, activity.getMainLooper(), false); update(); } Loading Loading @@ -187,35 +196,76 @@ public class DefaultCombinedPicker extends DefaultAppPickerFragment { } } @Override /** * Get the Credential Manager service if we haven't already got it. We need to get the service * later because if we do it in onCreate it will fail. */ private @Nullable CredentialManager getCredentialProviderService() { if (mCredentialManager == null) { mCredentialManager = getContext().getSystemService(CredentialManager.class); } return mCredentialManager; } private List<CombinedProviderInfo> getAllProviders() { final Context context = getContext(); final List<AutofillServiceInfo> autofillProviders = AutofillServiceInfo.getAvailableServices(context, mUserId); final CredentialManager service = getCredentialProviderService(); final List<CredentialProviderInfo> credManProviders = new ArrayList<>(); if (service != null) { credManProviders.addAll( service.getCredentialProviderServices( mUserId, CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_ONLY)); } final String selectedAutofillProvider = getSelectedAutofillProvider(context, mUserId); return CombinedProviderInfo.buildMergedList( autofillProviders, credManProviders, selectedAutofillProvider); } public static String getSelectedAutofillProvider(Context context, int userId) { return Settings.Secure.getStringForUser( context.getContentResolver(), AUTOFILL_SETTING, userId); } protected List<DefaultAppInfo> getCandidates() { final Context context = getContext(); final List<CombinedProviderInfo> allProviders = getAllProviders(); final List<DefaultAppInfo> candidates = new ArrayList<>(); final List<AutofillServiceInfo> services = AutofillServiceInfo.getAvailableServices(getContext(), mUserId); for (AutofillServiceInfo asi : services) { for (CombinedProviderInfo cpi : allProviders) { ServiceInfo brandingService = cpi.getBrandingService(); if (brandingService == null) { candidates.add( new DefaultAppInfo( getContext(), mPm, mUserId, asi.getServiceInfo().getComponentName())); context, mPm, mUserId, cpi.getApplicationInfo(), cpi.getSettingsSubtitle(), true)); } else { candidates.add( new DefaultAppInfo( context, mPm, mUserId, brandingService, cpi.getSettingsSubtitle(), true)); } return candidates; } public static String getDefaultKey(Context context, int userId) { String setting = Settings.Secure.getStringForUser(context.getContentResolver(), SETTING, userId); if (setting != null) { ComponentName componentName = ComponentName.unflattenFromString(setting); if (componentName != null) { return componentName.flattenToString(); } } return null; return candidates; } @Override protected String getDefaultKey() { return getDefaultKey(getContext(), mUserId); final CombinedProviderInfo topProvider = CombinedProviderInfo.getTopProvider(getAllProviders()); return topProvider == null ? "" : topProvider.getApplicationInfo().packageName; } @Override Loading @@ -234,7 +284,39 @@ public class DefaultCombinedPicker extends DefaultAppPickerFragment { @Override protected boolean setDefaultKey(String key) { Settings.Secure.putStringForUser(getContext().getContentResolver(), SETTING, key, mUserId); // Get the list of providers and see if any match the key (package name). final List<CombinedProviderInfo> allProviders = getAllProviders(); CombinedProviderInfo matchedProvider = null; for (CombinedProviderInfo cpi : allProviders) { if (cpi.getApplicationInfo().packageName.equals(key)) { matchedProvider = cpi; break; } } // If there were none then clear the stored providers. if (matchedProvider == null) { setProviders(null, new ArrayList<>()); return true; } // Get the component names and save them. final List<String> credManComponents = new ArrayList<>(); for (CredentialProviderInfo pi : matchedProvider.getCredentialProviderInfos()) { credManComponents.add(pi.getServiceInfo().getComponentName().flattenToString()); } String autofillValue = null; if (matchedProvider.getAutofillServiceInfo() != null) { autofillValue = matchedProvider .getAutofillServiceInfo() .getServiceInfo() .getComponentName() .flattenToString(); } setProviders(autofillValue, credManComponents); // Check if activity was launched from Settings.ACTION_REQUEST_SET_AUTOFILL_SERVICE // intent, and set proper result if so... Loading @@ -255,4 +337,38 @@ public class DefaultCombinedPicker extends DefaultAppPickerFragment { return true; } private void setProviders(String autofillProvider, List<String> credManProviders) { if (TextUtils.isEmpty(autofillProvider)) { if (credManProviders.size() > 0) { autofillProvider = CredentialManagerPreferenceController .AUTOFILL_CREDMAN_ONLY_PROVIDER_PLACEHOLDER; } } Settings.Secure.putStringForUser( getContext().getContentResolver(), AUTOFILL_SETTING, autofillProvider, mUserId); CredentialManager service = getCredentialProviderService(); if (service == null) { return; } service.setEnabledProviders( credManProviders, mUserId, ContextCompat.getMainExecutor(getContext()), new OutcomeReceiver<Void, SetEnabledProvidersException>() { @Override public void onResult(Void result) { Log.i(TAG, "setEnabledProviders success"); } @Override public void onError(SetEnabledProvidersException e) { Log.e(TAG, "setEnabledProviders error: " + e.toString()); } }); } }
src/com/android/settings/applications/credentials/DefaultCombinedPreferenceController.java +49 −17 Original line number Diff line number Diff line Loading @@ -23,7 +23,9 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.credentials.CredentialManager; import android.credentials.CredentialProviderInfo; import android.provider.Settings; import android.service.autofill.AutofillService; import android.service.autofill.AutofillServiceInfo; import android.text.TextUtils; import android.util.Log; Loading @@ -36,6 +38,9 @@ import java.util.List; public class DefaultCombinedPreferenceController extends DefaultAppPreferenceController { private static final Intent AUTOFILL_PROBE = new Intent(AutofillService.SERVICE_INTERFACE); private static final String TAG = "DefaultCombinedPreferenceController"; private final AutofillManager mAutofillManager; private final CredentialManager mCredentialManager; Loading Loading @@ -76,26 +81,57 @@ public class DefaultCombinedPreferenceController extends DefaultAppPreferenceCon @Override protected DefaultAppInfo getDefaultAppInfo() { final String flattenComponent = Settings.Secure.getString( mContext.getContentResolver(), DefaultCombinedPicker.SETTING); if (!TextUtils.isEmpty(flattenComponent)) { DefaultAppInfo appInfo = new DefaultAppInfo( List<CombinedProviderInfo> providers = getAllProviders(mUserId); CombinedProviderInfo topProvider = CombinedProviderInfo.getTopProvider(providers); if (topProvider != null) { ServiceInfo brandingService = topProvider.getBrandingService(); if (brandingService == null) { return new DefaultAppInfo( mContext, mPackageManager, mUserId, ComponentName.unflattenFromString(flattenComponent)); return appInfo; topProvider.getApplicationInfo(), topProvider.getSettingsSubtitle(), true); } else { return new DefaultAppInfo( mContext, mPackageManager, mUserId, brandingService, topProvider.getSettingsSubtitle(), true); } } return null; } private List<CombinedProviderInfo> getAllProviders(int userId) { final List<AutofillServiceInfo> autofillProviders = AutofillServiceInfo.getAvailableServices(mContext, userId); final List<CredentialProviderInfo> credManProviders = mCredentialManager.getCredentialProviderServices( userId, CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_ONLY); final String selectedAutofillProvider = Settings.Secure.getStringForUser( mContext.getContentResolver(), DefaultCombinedPicker.AUTOFILL_SETTING, userId); return CombinedProviderInfo.buildMergedList( autofillProviders, credManProviders, selectedAutofillProvider); } @Override protected boolean showLabelAsTitle() { return true; } @Override protected boolean showAppSummary() { return true; } /** Provides Intent to setting activity for the specified autofill service. */ static final class AutofillSettingIntentProvider { Loading @@ -113,9 +149,7 @@ public class DefaultCombinedPreferenceController extends DefaultAppPreferenceCon final List<ResolveInfo> resolveInfos = mContext.getPackageManager() .queryIntentServicesAsUser( DefaultCombinedPicker.AUTOFILL_PROBE, PackageManager.GET_META_DATA, mUserId); AUTOFILL_PROBE, PackageManager.GET_META_DATA, mUserId); for (ResolveInfo resolveInfo : resolveInfos) { final ServiceInfo serviceInfo = resolveInfo.serviceInfo; Loading @@ -130,9 +164,7 @@ public class DefaultCombinedPreferenceController extends DefaultAppPreferenceCon .getSettingsActivity(); } catch (SecurityException e) { // Service does not declare the proper permission, ignore it. Log.w( "AutofillSettingIntentProvider", "Error getting info for " + serviceInfo + ": " + e); Log.w(TAG, "Error getting info for " + serviceInfo + ": " + e); return null; } if (TextUtils.isEmpty(settingsActivity)) { Loading
src/com/android/settings/applications/credentials/DefaultWorkCombinedPreferenceController.java +1 −1 Original line number Diff line number Diff line Loading @@ -52,7 +52,7 @@ public class DefaultWorkCombinedPreferenceController extends DefaultCombinedPref final String flattenComponent = Settings.Secure.getStringForUser( mContext.getContentResolver(), DefaultCombinedPicker.SETTING, DefaultCombinedPicker.AUTOFILL_SETTING, mUserHandle.getIdentifier()); if (!TextUtils.isEmpty(flattenComponent)) { DefaultAppInfo appInfo = Loading