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