1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.service.credentials;
18 
19 import static java.util.Objects.requireNonNull;
20 
21 import android.Manifest;
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.annotation.UserIdInt;
25 import android.app.AppGlobals;
26 import android.app.admin.DevicePolicyManager;
27 import android.app.admin.PackagePolicy;
28 import android.content.ComponentName;
29 import android.content.Context;
30 import android.content.Intent;
31 import android.content.pm.ApplicationInfo;
32 import android.content.pm.PackageManager;
33 import android.content.pm.ResolveInfo;
34 import android.content.pm.ServiceInfo;
35 import android.content.res.Resources;
36 import android.content.res.TypedArray;
37 import android.content.res.XmlResourceParser;
38 import android.credentials.CredentialManager;
39 import android.credentials.CredentialProviderInfo;
40 import android.os.Bundle;
41 import android.os.RemoteException;
42 import android.os.UserHandle;
43 import android.text.TextUtils;
44 import android.util.AttributeSet;
45 import android.util.Slog;
46 import android.util.Xml;
47 
48 import com.android.internal.R;
49 import com.android.internal.annotations.VisibleForTesting;
50 
51 import org.xmlpull.v1.XmlPullParser;
52 import org.xmlpull.v1.XmlPullParserException;
53 
54 import java.io.IOException;
55 import java.util.ArrayList;
56 import java.util.HashMap;
57 import java.util.HashSet;
58 import java.util.List;
59 import java.util.Map;
60 import java.util.Set;
61 
62 /**
63  * {@link CredentialProviderInfo} generator.
64  *
65  * @hide
66  */
67 public final class CredentialProviderInfoFactory {
68     private static final String TAG = CredentialManager.TAG;
69 
70     private static final String TAG_CREDENTIAL_PROVIDER = "credential-provider";
71     private static final String TAG_CAPABILITIES = "capabilities";
72     private static final String TAG_CAPABILITY = "capability";
73     private static final String ATTR_NAME = "name";
74 
75     /**
76      * Constructs an information instance of the credential provider.
77      *
78      * @param context the context object
79      * @param serviceComponent the serviceComponent of the provider service
80      * @param userId the android userId for which the current process is running
81      * @param isSystemProvider whether this provider is a system provider
82      * @throws PackageManager.NameNotFoundException If provider service is not found
83      * @throws SecurityException If provider does not require the relevant permission
84      */
create( @onNull Context context, @NonNull ComponentName serviceComponent, int userId, boolean isSystemProvider, boolean isPrimary)85     public static CredentialProviderInfo create(
86             @NonNull Context context,
87             @NonNull ComponentName serviceComponent,
88             int userId,
89             boolean isSystemProvider,
90             boolean isPrimary)
91             throws PackageManager.NameNotFoundException {
92         return create(
93                 context,
94                 getServiceInfoOrThrow(serviceComponent, userId),
95                 isSystemProvider,
96                 /* disableSystemAppVerificationForTests= */ false,
97                 /* isEnabled= */ false,
98                 isPrimary);
99     }
100 
101     /**
102      * Constructs an information instance of the credential provider.
103      *
104      * @param context the context object
105      * @param serviceInfo the service info for the provider app. This must be retrieved from the
106      *     {@code PackageManager}
107      * @param isSystemProvider whether the provider app is a system provider
108      * @param disableSystemAppVerificationForTests whether to disable system app permission
109      *     verification so that tests can install system providers
110      * @param isEnabled whether the user enabled this provider
111      * @throws SecurityException If provider does not require the relevant permission
112      */
create( @onNull Context context, @NonNull ServiceInfo serviceInfo, boolean isSystemProvider, boolean disableSystemAppVerificationForTests, boolean isEnabled, boolean isPrimary)113     public static CredentialProviderInfo create(
114             @NonNull Context context,
115             @NonNull ServiceInfo serviceInfo,
116             boolean isSystemProvider,
117             boolean disableSystemAppVerificationForTests,
118             boolean isEnabled,
119             boolean isPrimary)
120             throws SecurityException {
121         verifyProviderPermission(serviceInfo);
122         if (isSystemProvider) {
123             if (!isValidSystemProvider(
124                     context, serviceInfo, disableSystemAppVerificationForTests)) {
125                 Slog.e(TAG, "Provider is not a valid system provider: " + serviceInfo);
126                 throw new SecurityException(
127                         "Provider is not a valid system provider: " + serviceInfo);
128             }
129         }
130 
131         return populateMetadata(context, serviceInfo)
132                 .setSystemProvider(isSystemProvider)
133                 .setEnabled(isEnabled)
134                 .setPrimary(isPrimary)
135                 .build();
136     }
137 
138     /**
139      * Constructs an information instance of the credential provider for testing purposes. Does not
140      * run any verifications and passes parameters as is.
141      */
142     @VisibleForTesting
createForTests( @onNull ServiceInfo serviceInfo, @NonNull CharSequence overrideLabel, boolean isSystemProvider, boolean isEnabled, @NonNull List<String> capabilities)143     public static CredentialProviderInfo createForTests(
144             @NonNull ServiceInfo serviceInfo,
145             @NonNull CharSequence overrideLabel,
146             boolean isSystemProvider,
147             boolean isEnabled,
148             @NonNull List<String> capabilities) {
149         return new CredentialProviderInfo.Builder(serviceInfo)
150                 .setEnabled(isEnabled)
151                 .setOverrideLabel(overrideLabel)
152                 .setSystemProvider(isSystemProvider)
153                 .addCapabilities(capabilities)
154                 .build();
155     }
156 
verifyProviderPermission(ServiceInfo serviceInfo)157     private static void verifyProviderPermission(ServiceInfo serviceInfo) throws SecurityException {
158         final String permission = Manifest.permission.BIND_CREDENTIAL_PROVIDER_SERVICE;
159         if (permission.equals(serviceInfo.permission)) {
160             return;
161         }
162         throw new SecurityException(
163                 "Service does not require the expected permission : " + permission);
164     }
165 
isSystemProviderWithValidPermission( ServiceInfo serviceInfo, Context context)166     private static boolean isSystemProviderWithValidPermission(
167             ServiceInfo serviceInfo, Context context) {
168         if (context == null) {
169             Slog.w(TAG, "Context is null in isSystemProviderWithValidPermission");
170             return false;
171         }
172         return PermissionUtils.hasPermission(
173                 context,
174                 serviceInfo.packageName,
175                 Manifest.permission.PROVIDE_DEFAULT_ENABLED_CREDENTIAL_SERVICE);
176     }
177 
isValidSystemProvider( Context context, ServiceInfo serviceInfo, boolean disableSystemAppVerificationForTests)178     private static boolean isValidSystemProvider(
179             Context context,
180             ServiceInfo serviceInfo,
181             boolean disableSystemAppVerificationForTests) {
182         requireNonNull(context, "context must not be null");
183 
184         if (disableSystemAppVerificationForTests) {
185             Bundle metadata = serviceInfo.metaData;
186             if (metadata == null) {
187                 Slog.w(
188                         TAG,
189                         "metadata is null while reading "
190                                 + "TEST_SYSTEM_PROVIDER_META_DATA_KEY: "
191                                 + serviceInfo);
192                 return false;
193             }
194             return metadata.getBoolean(
195                     CredentialProviderService.TEST_SYSTEM_PROVIDER_META_DATA_KEY);
196         }
197 
198         return isSystemProviderWithValidPermission(serviceInfo, context);
199     }
200 
populateMetadata( @onNull Context context, ServiceInfo serviceInfo)201     private static CredentialProviderInfo.Builder populateMetadata(
202             @NonNull Context context, ServiceInfo serviceInfo) {
203         requireNonNull(context, "context must not be null");
204         final PackageManager pm = context.getPackageManager();
205         CredentialProviderInfo.Builder builder = new CredentialProviderInfo.Builder(serviceInfo);
206 
207         // 1. Get the metadata for the service.
208         final Bundle metadata = serviceInfo.metaData;
209         if (metadata == null) {
210             Slog.w(TAG, "Metadata is null for provider: " + serviceInfo.getComponentName());
211             return builder;
212         }
213 
214         // 2. Get the resources for the application.
215         Resources resources = null;
216         try {
217             resources = pm.getResourcesForApplication(serviceInfo.applicationInfo);
218         } catch (PackageManager.NameNotFoundException e) {
219             Slog.e(TAG, "Failed to get app resources", e);
220         }
221 
222         // 3. Stop if we are missing data.
223         if (resources == null) {
224             Slog.w(
225                     TAG,
226                     "Resources are null for the serviceInfo being processed: "
227                             + serviceInfo.getComponentName());
228             return builder;
229         }
230 
231         // 4. Extract the XML metadata.
232         try {
233             builder = extractXmlMetadata(context, serviceInfo, pm, resources);
234         } catch (Exception e) {
235             Slog.e(TAG, "Failed to get XML metadata", e);
236         }
237 
238         return builder;
239     }
240 
extractXmlMetadata( @onNull Context context, @NonNull ServiceInfo serviceInfo, @NonNull PackageManager pm, @NonNull Resources resources)241     private static CredentialProviderInfo.Builder extractXmlMetadata(
242             @NonNull Context context,
243             @NonNull ServiceInfo serviceInfo,
244             @NonNull PackageManager pm,
245             @NonNull Resources resources) {
246         final CredentialProviderInfo.Builder builder =
247                 new CredentialProviderInfo.Builder(serviceInfo);
248         final XmlResourceParser parser =
249                 serviceInfo.loadXmlMetaData(pm, CredentialProviderService.SERVICE_META_DATA);
250         if (parser == null) {
251             return builder;
252         }
253 
254         try {
255             int type = 0;
256             while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) {
257                 type = parser.next();
258             }
259 
260             // This is matching a <credential-provider /> tag in the XML.
261             if (TAG_CREDENTIAL_PROVIDER.equals(parser.getName())) {
262                 final AttributeSet allAttributes = Xml.asAttributeSet(parser);
263                 TypedArray afsAttributes = null;
264                 try {
265                     afsAttributes =
266                             resources.obtainAttributes(
267                                     allAttributes,
268                                     com.android.internal.R.styleable.CredentialProvider);
269                     builder.setSettingsSubtitle(
270                             getAfsAttributeSafe(
271                                     afsAttributes,
272                                     R.styleable.CredentialProvider_settingsSubtitle));
273                     builder.setSettingsActivity(
274                             getAfsAttributeSafe(
275                                     afsAttributes,
276                                     R.styleable.CredentialProvider_settingsActivity));
277                 } catch (Exception e) {
278                     Slog.w(TAG, "Failed to get XML attr for metadata", e);
279                 } finally {
280                     if (afsAttributes != null) {
281                         afsAttributes.recycle();
282                     }
283                 }
284 
285                 builder.addCapabilities(parseXmlProviderOuterCapabilities(parser, resources));
286             } else {
287                 Slog.w(TAG, "Meta-data does not start with credential-provider-service tag");
288             }
289         } catch (IOException | XmlPullParserException e) {
290             Slog.e(TAG, "Error parsing credential provider service meta-data", e);
291         }
292 
293         return builder;
294     }
295 
getAfsAttributeSafe( @ullable TypedArray afsAttributes, int resId)296     private static @Nullable String getAfsAttributeSafe(
297             @Nullable TypedArray afsAttributes, int resId) {
298         if (afsAttributes == null) {
299             return null;
300         }
301 
302         try {
303             return afsAttributes.getString(resId);
304         } catch (Exception e) {
305             Slog.w(TAG, "Failed to get XML attr from afs attributes", e);
306         }
307 
308         return null;
309     }
310 
parseXmlProviderOuterCapabilities( XmlPullParser parser, Resources resources)311     private static List<String> parseXmlProviderOuterCapabilities(
312             XmlPullParser parser, Resources resources) throws IOException, XmlPullParserException {
313         final List<String> capabilities = new ArrayList<>();
314         final int outerDepth = parser.getDepth();
315         int type;
316         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
317                 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
318             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
319                 continue;
320             }
321 
322             if (TAG_CAPABILITIES.equals(parser.getName())) {
323                 capabilities.addAll(parseXmlProviderInnerCapabilities(parser, resources));
324             }
325         }
326 
327         return capabilities;
328     }
329 
parseXmlProviderInnerCapabilities( XmlPullParser parser, Resources resources)330     private static List<String> parseXmlProviderInnerCapabilities(
331             XmlPullParser parser, Resources resources) throws IOException, XmlPullParserException {
332         List<String> capabilities = new ArrayList<>();
333 
334         final int outerDepth = parser.getDepth();
335         int type;
336         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
337                 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
338             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
339                 continue;
340             }
341 
342             if (TAG_CAPABILITY.equals(parser.getName())) {
343                 String name = parser.getAttributeValue(null, ATTR_NAME);
344                 if (name != null && !TextUtils.isEmpty(name)) {
345                     capabilities.add(name);
346                 }
347             }
348         }
349 
350         return capabilities;
351     }
352 
getServiceInfoOrThrow( @onNull ComponentName serviceComponent, int userId)353     private static ServiceInfo getServiceInfoOrThrow(
354             @NonNull ComponentName serviceComponent, int userId)
355             throws PackageManager.NameNotFoundException {
356         try {
357             ServiceInfo si =
358                     AppGlobals.getPackageManager()
359                             .getServiceInfo(serviceComponent, PackageManager.GET_META_DATA, userId);
360             if (si != null) {
361                 return si;
362             }
363         } catch (RemoteException e) {
364             Slog.e(TAG, "Unable to get serviceInfo", e);
365         }
366         throw new PackageManager.NameNotFoundException(serviceComponent.toString());
367     }
368 
369     /**
370      * Returns the valid credential provider services available for the user with the given {@code
371      * userId}.
372      */
373     @NonNull
getAvailableSystemServiceInfos( @onNull Context context, @UserIdInt int userId, boolean disableSystemAppVerificationForTests)374     private static List<ServiceInfo> getAvailableSystemServiceInfos(
375             @NonNull Context context,
376             @UserIdInt int userId,
377             boolean disableSystemAppVerificationForTests) {
378         requireNonNull(context, "context must not be null");
379 
380         final List<ServiceInfo> services = new ArrayList<>();
381         final List<ResolveInfo> resolveInfos = new ArrayList<>();
382 
383         resolveInfos.addAll(
384                 context.getPackageManager()
385                         .queryIntentServicesAsUser(
386                                 new Intent(CredentialProviderService.SYSTEM_SERVICE_INTERFACE),
387                                 PackageManager.ResolveInfoFlags.of(PackageManager.GET_META_DATA),
388                                 userId));
389 
390         for (ResolveInfo resolveInfo : resolveInfos) {
391             final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
392             if (disableSystemAppVerificationForTests) {
393                 if (serviceInfo != null) {
394                     services.add(serviceInfo);
395                 }
396                 continue;
397             }
398 
399             try {
400                 ApplicationInfo appInfo =
401                         context.getPackageManager()
402                                 .getApplicationInfo(
403                                         serviceInfo.packageName,
404                                         PackageManager.ApplicationInfoFlags.of(
405                                                 PackageManager.MATCH_SYSTEM_ONLY));
406 
407                 if (appInfo == null || serviceInfo == null) {
408                     continue;
409                 }
410                 services.add(serviceInfo);
411             } catch (SecurityException | PackageManager.NameNotFoundException e) {
412                 Slog.e(TAG, "Error getting info for " + serviceInfo, e);
413             }
414         }
415         return services;
416     }
417 
418     /**
419      * Returns the valid credential provider services available for the user with the given {@code
420      * userId}.
421      */
422     @NonNull
getAvailableSystemServices( @onNull Context context, @UserIdInt int userId, boolean disableSystemAppVerificationForTests, Set<ComponentName> enabledServices)423     public static List<CredentialProviderInfo> getAvailableSystemServices(
424             @NonNull Context context,
425             @UserIdInt int userId,
426             boolean disableSystemAppVerificationForTests,
427             Set<ComponentName> enabledServices) {
428         requireNonNull(context, "context must not be null");
429 
430         final List<CredentialProviderInfo> providerInfos = new ArrayList<>();
431         for (ServiceInfo si :
432                 getAvailableSystemServiceInfos(
433                         context, userId, disableSystemAppVerificationForTests)) {
434             try {
435                 CredentialProviderInfo cpi =
436                         CredentialProviderInfoFactory.create(
437                                 context,
438                                 si,
439                                 /* isSystemProvider= */ true,
440                                 disableSystemAppVerificationForTests,
441                                 enabledServices.contains(si.getComponentName()),
442                                 false);
443                 if (cpi.isSystemProvider()) {
444                     providerInfos.add(cpi);
445                 } else {
446                     Slog.e(TAG, "Non system provider was in system provider list.");
447                 }
448             } catch (SecurityException e) {
449                 Slog.e(TAG, "Failed to create CredentialProviderInfo: " + e);
450             }
451         }
452         return providerInfos;
453     }
454 
getDeviceManagerPolicy( @onNull Context context, int userId)455     private static @Nullable PackagePolicy getDeviceManagerPolicy(
456             @NonNull Context context, int userId) {
457         Context newContext = context.createContextAsUser(UserHandle.of(userId), 0);
458 
459         try {
460             DevicePolicyManager dpm = newContext.getSystemService(DevicePolicyManager.class);
461             PackagePolicy pp = dpm.getCredentialManagerPolicy();
462             return pp;
463         } catch (SecurityException e) {
464             // If the current user is not enrolled in DPM then this can throw a security error.
465             Slog.e(TAG, "Failed to get device policy: " + e);
466         }
467 
468         return null;
469     }
470 
471     /**
472      * Returns the valid credential provider services available for the user with the given {@code
473      * userId}.
474      */
475     @NonNull
getCredentialProviderServices( @onNull Context context, int userId, int providerFilter, Set<ComponentName> enabledServices, Set<ComponentName> primaryServices)476     public static List<CredentialProviderInfo> getCredentialProviderServices(
477             @NonNull Context context,
478             int userId,
479             int providerFilter,
480             Set<ComponentName> enabledServices,
481             Set<ComponentName> primaryServices) {
482         requireNonNull(context, "context must not be null");
483 
484         // Get the device policy. If the client has asked for all providers then we
485         // should ignore the device policy.
486         PackagePolicy pp =
487                 providerFilter != CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_INCLUDING_HIDDEN
488                         ? getDeviceManagerPolicy(context, userId)
489                         : null;
490 
491         // Generate the provider list.
492         final boolean disableSystemAppVerificationForTests = false;
493         ProviderGenerator generator =
494                 new ProviderGenerator(
495                         context, pp, disableSystemAppVerificationForTests, providerFilter);
496         generator.addUserProviders(
497                 getUserProviders(
498                         context,
499                         userId,
500                         disableSystemAppVerificationForTests,
501                         enabledServices,
502                         primaryServices));
503         generator.addSystemProviders(
504                 getAvailableSystemServices(
505                         context, userId, disableSystemAppVerificationForTests, enabledServices));
506         return generator.getProviders();
507     }
508 
509     /**
510      * Returns the valid credential provider services available for the user with the given {@code
511      * userId}. Includes test providers.
512      */
513     @NonNull
getCredentialProviderServicesForTesting( @onNull Context context, int userId, int providerFilter, Set<ComponentName> enabledServices, Set<ComponentName> primaryServices)514     public static List<CredentialProviderInfo> getCredentialProviderServicesForTesting(
515             @NonNull Context context,
516             int userId,
517             int providerFilter,
518             Set<ComponentName> enabledServices,
519             Set<ComponentName> primaryServices) {
520         requireNonNull(context, "context must not be null");
521 
522         // Get the device policy. If the client has asked for all providers then we
523         // should ignore the device policy.
524         PackagePolicy pp =
525                 providerFilter != CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_INCLUDING_HIDDEN
526                         ? getDeviceManagerPolicy(context, userId)
527                         : null;
528 
529         // Generate the provider list.
530         final boolean disableSystemAppVerificationForTests = true;
531         ProviderGenerator generator =
532                 new ProviderGenerator(
533                         context, pp, disableSystemAppVerificationForTests, providerFilter);
534         generator.addUserProviders(
535                 getUserProviders(
536                         context,
537                         userId,
538                         disableSystemAppVerificationForTests,
539                         enabledServices,
540                         primaryServices));
541         generator.addSystemProviders(
542                 getAvailableSystemServices(
543                         context, userId, disableSystemAppVerificationForTests, enabledServices));
544         return generator.getProviders();
545     }
546 
547     private static class ProviderGenerator {
548         private final Context mContext;
549         private final PackagePolicy mPp;
550         private final boolean mDisableSystemAppVerificationForTests;
551         private final Map<String, CredentialProviderInfo> mServices = new HashMap();
552         private final int mProviderFilter;
553 
ProviderGenerator( Context context, PackagePolicy pp, boolean disableSystemAppVerificationForTests, int providerFilter)554         ProviderGenerator(
555                 Context context,
556                 PackagePolicy pp,
557                 boolean disableSystemAppVerificationForTests,
558                 int providerFilter) {
559             this.mContext = context;
560             this.mPp = pp;
561             this.mDisableSystemAppVerificationForTests = disableSystemAppVerificationForTests;
562             this.mProviderFilter = providerFilter;
563         }
564 
isPackageAllowed(boolean isSystemProvider, String packageName)565         private boolean isPackageAllowed(boolean isSystemProvider, String packageName) {
566             if (mPp == null) {
567                 return true;
568             }
569 
570             if (isSystemProvider) {
571                 return mPp.getPolicyType() == PackagePolicy.PACKAGE_POLICY_ALLOWLIST_AND_SYSTEM;
572             }
573 
574             return mPp.isPackageAllowed(packageName, new HashSet<>());
575         }
576 
getProviders()577         public List<CredentialProviderInfo> getProviders() {
578             return new ArrayList<>(mServices.values());
579         }
580 
addUserProviders(List<CredentialProviderInfo> providers)581         public void addUserProviders(List<CredentialProviderInfo> providers) {
582             for (CredentialProviderInfo cpi : providers) {
583                 if (!cpi.isSystemProvider()) {
584                     addProvider(cpi);
585                 }
586             }
587         }
588 
addSystemProviders(List<CredentialProviderInfo> providers)589         public void addSystemProviders(List<CredentialProviderInfo> providers) {
590             for (CredentialProviderInfo cpi : providers) {
591                 if (cpi.isSystemProvider()) {
592                     addProvider(cpi);
593                 }
594             }
595         }
596 
isProviderAllowedWithFilter(CredentialProviderInfo cpi)597         private boolean isProviderAllowedWithFilter(CredentialProviderInfo cpi) {
598             if (mProviderFilter == CredentialManager.PROVIDER_FILTER_ALL_PROVIDERS) {
599                 return true;
600             }
601 
602             if (cpi.isSystemProvider()) {
603                 return mProviderFilter == CredentialManager.PROVIDER_FILTER_SYSTEM_PROVIDERS_ONLY;
604             } else {
605                 return mProviderFilter == CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_ONLY
606                         || mProviderFilter
607                                 == CredentialManager
608                                         .PROVIDER_FILTER_USER_PROVIDERS_INCLUDING_HIDDEN;
609             }
610         }
611 
addProvider(CredentialProviderInfo cpi)612         private void addProvider(CredentialProviderInfo cpi) {
613             final String componentNameString =
614                     cpi.getServiceInfo().getComponentName().flattenToString();
615             if (!isProviderAllowedWithFilter(cpi)) {
616                 return;
617             }
618 
619             if (!isPackageAllowed(cpi.isSystemProvider(), cpi.getServiceInfo().packageName)) {
620                 return;
621             }
622 
623             mServices.put(componentNameString, cpi);
624         }
625     }
626 
627     /**
628      * Returns the valid credential provider services available for the user with the given {@code
629      * userId}.
630      */
631     @NonNull
getUserProviders( @onNull Context context, @UserIdInt int userId, boolean disableSystemAppVerificationForTests, Set<ComponentName> enabledServices, Set<ComponentName> primaryServices)632     private static List<CredentialProviderInfo> getUserProviders(
633             @NonNull Context context,
634             @UserIdInt int userId,
635             boolean disableSystemAppVerificationForTests,
636             Set<ComponentName> enabledServices,
637             Set<ComponentName> primaryServices) {
638         final List<CredentialProviderInfo> services = new ArrayList<>();
639         final List<ResolveInfo> resolveInfos =
640                 context.getPackageManager()
641                         .queryIntentServicesAsUser(
642                                 new Intent(CredentialProviderService.SERVICE_INTERFACE),
643                                 PackageManager.ResolveInfoFlags.of(PackageManager.GET_META_DATA),
644                                 userId);
645         for (ResolveInfo resolveInfo : resolveInfos) {
646             final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
647             if (serviceInfo == null) {
648                 Slog.d(TAG, "No serviceInfo found for resolveInfo, so skipping provider");
649                 continue;
650             }
651 
652             try {
653                 CredentialProviderInfo cpi =
654                         CredentialProviderInfoFactory.create(
655                                 context,
656                                 serviceInfo,
657                                 /* isSystemProvider= */ false,
658                                 disableSystemAppVerificationForTests,
659                                 enabledServices.contains(serviceInfo.getComponentName()),
660                                 primaryServices.contains(serviceInfo.getComponentName()));
661                 if (!cpi.isSystemProvider()) {
662                     services.add(cpi);
663                 }
664             } catch (Exception e) {
665                 Slog.e(TAG, "Error getting info for " + serviceInfo, e);
666             }
667         }
668         return services;
669     }
670 }
671