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 com.android.server.credentials;
18 
19 import static android.Manifest.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS;
20 import static android.Manifest.permission.CREDENTIAL_MANAGER_SET_ORIGIN;
21 import static android.content.Context.CREDENTIAL_SERVICE;
22 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
23 
24 import android.Manifest;
25 import android.annotation.NonNull;
26 import android.annotation.Nullable;
27 import android.annotation.UserIdInt;
28 import android.app.ActivityManager;
29 import android.content.ComponentName;
30 import android.content.ContentResolver;
31 import android.content.Context;
32 import android.content.pm.PackageInfo;
33 import android.content.pm.PackageManager;
34 import android.credentials.ClearCredentialStateRequest;
35 import android.credentials.CreateCredentialException;
36 import android.credentials.CreateCredentialRequest;
37 import android.credentials.CredentialManager;
38 import android.credentials.CredentialOption;
39 import android.credentials.CredentialProviderInfo;
40 import android.credentials.GetCandidateCredentialsException;
41 import android.credentials.GetCredentialException;
42 import android.credentials.GetCredentialRequest;
43 import android.credentials.IClearCredentialStateCallback;
44 import android.credentials.ICreateCredentialCallback;
45 import android.credentials.ICredentialManager;
46 import android.credentials.IGetCandidateCredentialsCallback;
47 import android.credentials.IGetCredentialCallback;
48 import android.credentials.IPrepareGetCredentialCallback;
49 import android.credentials.ISetEnabledProvidersCallback;
50 import android.credentials.PrepareGetCredentialResponseInternal;
51 import android.credentials.RegisterCredentialDescriptionRequest;
52 import android.credentials.UnregisterCredentialDescriptionRequest;
53 import android.os.Binder;
54 import android.os.CancellationSignal;
55 import android.os.IBinder;
56 import android.os.ICancellationSignal;
57 import android.os.RemoteException;
58 import android.os.UserHandle;
59 import android.provider.DeviceConfig;
60 import android.provider.Settings;
61 import android.service.credentials.CallingAppInfo;
62 import android.service.credentials.CredentialProviderInfoFactory;
63 import android.service.credentials.PermissionUtils;
64 import android.text.TextUtils;
65 import android.util.Pair;
66 import android.util.Slog;
67 import android.util.SparseArray;
68 
69 import com.android.internal.R;
70 import com.android.internal.annotations.GuardedBy;
71 import com.android.server.credentials.metrics.ApiName;
72 import com.android.server.credentials.metrics.ApiStatus;
73 import com.android.server.infra.AbstractMasterSystemService;
74 import com.android.server.infra.SecureSettingsServiceNameResolver;
75 
76 import java.util.ArrayList;
77 import java.util.HashMap;
78 import java.util.HashSet;
79 import java.util.LinkedHashSet;
80 import java.util.List;
81 import java.util.Map;
82 import java.util.Set;
83 import java.util.function.Consumer;
84 import java.util.stream.Collectors;
85 
86 /**
87  * Entry point service for credential management.
88  *
89  * <p>This service provides the {@link ICredentialManager} implementation and keeps a list of {@link
90  * CredentialManagerServiceImpl} per user; the real work is done by {@link
91  * CredentialManagerServiceImpl} itself.
92  */
93 public final class CredentialManagerService
94         extends AbstractMasterSystemService<
95         CredentialManagerService, CredentialManagerServiceImpl> {
96 
97     private static final String TAG = CredentialManager.TAG;
98     private static final String PERMISSION_DENIED_ERROR = "permission_denied";
99     private static final String PERMISSION_DENIED_WRITE_SECURE_SETTINGS_ERROR =
100             "Caller is missing WRITE_SECURE_SETTINGS permission";
101     private static final String DEVICE_CONFIG_ENABLE_CREDENTIAL_MANAGER =
102             "enable_credential_manager";
103 
104     private static final String DEVICE_CONFIG_ENABLE_CREDENTIAL_DESC_API =
105             "enable_credential_description_api";
106 
107     /**
108      * Value stored in autofill pref when credential provider is primary. This is
109      * used as a placeholder since a credman only provider will not have an
110      * autofill service.
111      */
112     public static final String AUTOFILL_PLACEHOLDER_VALUE = "credential-provider";
113 
114     private final Context mContext;
115 
116     /** Cache of system service list per user id. */
117     @GuardedBy("mLock")
118     private final SparseArray<List<CredentialManagerServiceImpl>> mSystemServicesCacheList =
119             new SparseArray<>();
120 
121     /** Cache of all ongoing request sessions per user id. */
122     @GuardedBy("mLock")
123     private final SparseArray<Map<IBinder, RequestSession>> mRequestSessions =
124             new SparseArray<>();
125 
126     private final SessionManager mSessionManager = new SessionManager();
127 
CredentialManagerService(@onNull Context context)128     public CredentialManagerService(@NonNull Context context) {
129         super(
130                 context,
131                 new SecureSettingsServiceNameResolver(
132                         context, Settings.Secure.CREDENTIAL_SERVICE, /* isMultipleMode= */ true),
133                 null,
134                 PACKAGE_UPDATE_POLICY_REFRESH_EAGER);
135         mContext = context;
136     }
137 
138     @NonNull
139     @GuardedBy("mLock")
constructSystemServiceListLocked( int resolvedUserId)140     private List<CredentialManagerServiceImpl> constructSystemServiceListLocked(
141             int resolvedUserId) {
142         List<CredentialManagerServiceImpl> services = new ArrayList<>();
143         List<CredentialProviderInfo> serviceInfos =
144                 CredentialProviderInfoFactory.getAvailableSystemServices(
145                         mContext,
146                         resolvedUserId,
147                         /* disableSystemAppVerificationForTests= */ false,
148                         new HashSet<>());
149         serviceInfos.forEach(
150                 info -> {
151                     services.add(
152                             new CredentialManagerServiceImpl(this, mLock, resolvedUserId,
153                                     info));
154                 });
155         return services;
156     }
157 
158     @Override
getServiceSettingsProperty()159     protected String getServiceSettingsProperty() {
160         return Settings.Secure.CREDENTIAL_SERVICE;
161     }
162 
163     @Override // from AbstractMasterSystemService
newServiceLocked( @serIdInt int resolvedUserId, boolean disabled)164     protected CredentialManagerServiceImpl newServiceLocked(
165             @UserIdInt int resolvedUserId, boolean disabled) {
166         // This method should not be called for CredentialManagerService as it is configured to use
167         // multiple services.
168         Slog.w(
169                 TAG,
170                 "Should not be here - CredentialManagerService is configured to use "
171                         + "multiple services");
172         return null;
173     }
174 
175     @Override // from SystemService
onStart()176     public void onStart() {
177         publishBinderService(CREDENTIAL_SERVICE, new CredentialManagerServiceStub());
178     }
179 
180     @Override // from AbstractMasterSystemService
181     @GuardedBy("mLock")
newServiceListLocked( int resolvedUserId, boolean disabled, String[] serviceNames)182     protected List<CredentialManagerServiceImpl> newServiceListLocked(
183             int resolvedUserId, boolean disabled, String[] serviceNames) {
184         getOrConstructSystemServiceListLock(resolvedUserId);
185         if (serviceNames == null || serviceNames.length == 0) {
186             return new ArrayList<>();
187         }
188         List<CredentialManagerServiceImpl> serviceList = new ArrayList<>(serviceNames.length);
189         for (String serviceName : serviceNames) {
190             if (TextUtils.isEmpty(serviceName)) {
191                 continue;
192             }
193             try {
194                 serviceList.add(
195                         new CredentialManagerServiceImpl(this, mLock, resolvedUserId, serviceName));
196             } catch (PackageManager.NameNotFoundException | SecurityException e) {
197                 Slog.e(TAG, "Unable to add serviceInfo : ", e);
198             }
199         }
200         return serviceList;
201     }
202 
203     @GuardedBy("mLock")
204     @SuppressWarnings("GuardedBy") // ErrorProne requires service.mLock which is the same
205     // this.mLock
handlePackageRemovedMultiModeLocked(String packageName, int userId)206     protected void handlePackageRemovedMultiModeLocked(String packageName, int userId) {
207         updateProvidersWhenPackageRemoved(new SettingsWrapper(mContext), packageName);
208 
209         List<CredentialManagerServiceImpl> services = peekServiceListForUserLocked(userId);
210         if (services == null) {
211             return;
212         }
213 
214         List<CredentialManagerServiceImpl> servicesToBeRemoved = new ArrayList<>();
215         for (CredentialManagerServiceImpl service : services) {
216             if (service != null) {
217                 CredentialProviderInfo credentialProviderInfo = service.getCredentialProviderInfo();
218                 ComponentName componentName =
219                         credentialProviderInfo.getServiceInfo().getComponentName();
220                 if (packageName.equals(componentName.getPackageName())) {
221                     servicesToBeRemoved.add(service);
222                 }
223             }
224         }
225 
226         // Iterate over all the services to be removed, and remove them from the user configurable
227         // services cache, the system services cache as well as the setting key-value pair.
228         for (CredentialManagerServiceImpl serviceToBeRemoved : servicesToBeRemoved) {
229             removeServiceFromCache(serviceToBeRemoved, userId);
230             removeServiceFromSystemServicesCache(serviceToBeRemoved, userId);
231             CredentialDescriptionRegistry.forUser(userId)
232                     .evictProviderWithPackageName(serviceToBeRemoved.getServicePackageName());
233         }
234     }
235 
236     @GuardedBy("mLock")
removeServiceFromSystemServicesCache( CredentialManagerServiceImpl serviceToBeRemoved, int userId)237     private void removeServiceFromSystemServicesCache(
238             CredentialManagerServiceImpl serviceToBeRemoved, int userId) {
239         if (mSystemServicesCacheList.get(userId) != null) {
240             mSystemServicesCacheList.get(userId).remove(serviceToBeRemoved);
241         }
242     }
243 
244     @GuardedBy("mLock")
getOrConstructSystemServiceListLock( int resolvedUserId)245     private List<CredentialManagerServiceImpl> getOrConstructSystemServiceListLock(
246             int resolvedUserId) {
247         List<CredentialManagerServiceImpl> services = mSystemServicesCacheList.get(resolvedUserId);
248         if (services == null || services.size() == 0) {
249             services = constructSystemServiceListLocked(resolvedUserId);
250             mSystemServicesCacheList.put(resolvedUserId, services);
251         }
252         return services;
253     }
254 
hasWriteSecureSettingsPermission()255     private boolean hasWriteSecureSettingsPermission() {
256         return hasPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS);
257     }
258 
verifyGetProvidersPermission()259     private void verifyGetProvidersPermission() throws SecurityException {
260         if (hasPermission(android.Manifest.permission.QUERY_ALL_PACKAGES)) {
261             return;
262         }
263 
264         if (hasPermission(android.Manifest.permission.LIST_ENABLED_CREDENTIAL_PROVIDERS)) {
265             return;
266         }
267 
268         throw new SecurityException(
269                 "Caller is missing permission: QUERY_ALL_PACKAGES or "
270                         + "LIST_ENABLED_CREDENTIAL_PROVIDERS");
271     }
272 
hasPermission(String permission)273     private boolean hasPermission(String permission) {
274         final boolean result =
275                 mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED;
276         if (!result) {
277             Slog.e(TAG, "Caller does not have permission: " + permission);
278         }
279         return result;
280     }
281 
runForUser(@onNull final Consumer<CredentialManagerServiceImpl> c)282     private void runForUser(@NonNull final Consumer<CredentialManagerServiceImpl> c) {
283         final int userId = UserHandle.getCallingUserId();
284         final long origId = Binder.clearCallingIdentity();
285         try {
286             synchronized (mLock) {
287                 final List<CredentialManagerServiceImpl> services =
288                         getCredentialProviderServicesLocked(userId);
289                 for (CredentialManagerServiceImpl s : services) {
290                     c.accept(s);
291                 }
292             }
293         } finally {
294             Binder.restoreCallingIdentity(origId);
295         }
296     }
297 
getPrimaryProvidersForUserId(Context context, int userId)298     static Set<ComponentName> getPrimaryProvidersForUserId(Context context, int userId) {
299         final int resolvedUserId = ActivityManager.handleIncomingUser(
300                 Binder.getCallingPid(), Binder.getCallingUid(),
301                 userId, false, false,
302                 "getPrimaryProvidersForUserId", null);
303         SecureSettingsServiceNameResolver resolver = new SecureSettingsServiceNameResolver(
304                 context, Settings.Secure.CREDENTIAL_SERVICE_PRIMARY,
305                 /* isMultipleMode= */ true);
306         String[] serviceNames = resolver.readServiceNameList(resolvedUserId);
307         if (serviceNames == null) {
308             return new HashSet<ComponentName>();
309         }
310 
311         Set<ComponentName> services = new HashSet<>();
312         for (String serviceName : serviceNames) {
313             ComponentName compName = ComponentName.unflattenFromString(serviceName);
314             if (compName == null) {
315                 Slog.w(
316                         TAG,
317                         "Primary provider component name unflatten from string error: "
318                                 + serviceName);
319                 continue;
320             }
321             services.add(compName);
322         }
323         return services;
324     }
325 
326     @GuardedBy("mLock")
getCredentialProviderServicesLocked(int userId)327     private List<CredentialManagerServiceImpl> getCredentialProviderServicesLocked(int userId) {
328         List<CredentialManagerServiceImpl> concatenatedServices = new ArrayList<>();
329         List<CredentialManagerServiceImpl> userConfigurableServices =
330                 getServiceListForUserLocked(userId);
331         if (userConfigurableServices != null && !userConfigurableServices.isEmpty()) {
332             concatenatedServices.addAll(userConfigurableServices);
333         }
334         concatenatedServices.addAll(getOrConstructSystemServiceListLock(userId));
335         return concatenatedServices;
336     }
337 
isCredentialDescriptionApiEnabled()338     public static boolean isCredentialDescriptionApiEnabled() {
339         final long origId = Binder.clearCallingIdentity();
340         try {
341             return DeviceConfig.getBoolean(
342                     DeviceConfig.NAMESPACE_CREDENTIAL, DEVICE_CONFIG_ENABLE_CREDENTIAL_DESC_API,
343                     false);
344         } finally {
345             Binder.restoreCallingIdentity(origId);
346         }
347     }
348 
349     @SuppressWarnings("GuardedBy") // ErrorProne requires initiateProviderSessionForRequestLocked
350     // to be guarded by 'service.mLock', which is the same as mLock.
initiateProviderSessionsWithActiveContainers( GetRequestSession session, Set<Pair<CredentialOption, CredentialDescriptionRegistry.FilterResult>> activeCredentialContainers)351     private List<ProviderSession> initiateProviderSessionsWithActiveContainers(
352             GetRequestSession session,
353             Set<Pair<CredentialOption, CredentialDescriptionRegistry.FilterResult>>
354                     activeCredentialContainers) {
355         List<ProviderSession> providerSessions = new ArrayList<>();
356         for (Pair<CredentialOption, CredentialDescriptionRegistry.FilterResult> result :
357                 activeCredentialContainers) {
358             ProviderSession providerSession = ProviderRegistryGetSession.createNewSession(
359                     mContext,
360                     UserHandle.getCallingUserId(),
361                     session,
362                     session.mClientAppInfo,
363                     result.second.mPackageName,
364                     result.first);
365             providerSessions.add(providerSession);
366             session.addProviderSession(providerSession.getComponentName(), providerSession);
367         }
368         return providerSessions;
369     }
370 
371     @SuppressWarnings("GuardedBy") // ErrorProne requires initiateProviderSessionForRequestLocked
372     // to be guarded by 'service.mLock', which is the same as mLock.
initiateProviderSessionsWithActiveContainers( PrepareGetRequestSession session, Set<Pair<CredentialOption, CredentialDescriptionRegistry.FilterResult>> activeCredentialContainers)373     private List<ProviderSession> initiateProviderSessionsWithActiveContainers(
374             PrepareGetRequestSession session,
375             Set<Pair<CredentialOption, CredentialDescriptionRegistry.FilterResult>>
376                     activeCredentialContainers) {
377         List<ProviderSession> providerSessions = new ArrayList<>();
378         for (Pair<CredentialOption, CredentialDescriptionRegistry.FilterResult> result :
379                 activeCredentialContainers) {
380             ProviderSession providerSession = ProviderRegistryGetSession.createNewSession(
381                     mContext,
382                     UserHandle.getCallingUserId(),
383                     session,
384                     session.mClientAppInfo,
385                     result.second.mPackageName,
386                     result.first);
387             providerSessions.add(providerSession);
388             session.addProviderSession(providerSession.getComponentName(), providerSession);
389         }
390         return providerSessions;
391     }
392 
393 
394     @NonNull
395     private Set<Pair<CredentialOption, CredentialDescriptionRegistry.FilterResult>>
getFilteredResultFromRegistry(List<CredentialOption> options)396     getFilteredResultFromRegistry(List<CredentialOption> options) {
397         // Session for active/provisioned credential descriptions;
398         CredentialDescriptionRegistry registry =
399                 CredentialDescriptionRegistry.forUser(UserHandle.getCallingUserId());
400 
401         // All requested credential descriptions based on the given request.
402         Set<Set<String>> requestedCredentialDescriptions =
403                 options.stream()
404                         .map(
405                                 getCredentialOption ->
406                                         new HashSet<>(getCredentialOption
407                                                 .getCredentialRetrievalData()
408                                                 .getStringArrayList(
409                                                         CredentialOption.SUPPORTED_ELEMENT_KEYS)))
410                         .collect(Collectors.toSet());
411 
412         // All requested credential descriptions based on the given request.
413         Set<CredentialDescriptionRegistry.FilterResult> filterResults =
414                 registry.getMatchingProviders(requestedCredentialDescriptions);
415 
416         Set<Pair<CredentialOption, CredentialDescriptionRegistry.FilterResult>> result =
417                 new HashSet<>();
418 
419         for (CredentialDescriptionRegistry.FilterResult filterResult : filterResults) {
420             for (CredentialOption credentialOption : options) {
421                 Set<String> requestedElementKeys = new HashSet<>(
422                         credentialOption
423                                 .getCredentialRetrievalData()
424                                 .getStringArrayList(CredentialOption.SUPPORTED_ELEMENT_KEYS));
425                 if (CredentialDescriptionRegistry.checkForMatch(filterResult.mElementKeys,
426                         requestedElementKeys)) {
427                     result.add(new Pair<>(credentialOption, filterResult));
428                 }
429             }
430         }
431         return result;
432     }
433 
434     @SuppressWarnings("GuardedBy") // ErrorProne requires initiateProviderSessionForRequestLocked
435     // to be guarded by 'service.mLock', which is the same as mLock.
initiateProviderSessions( RequestSession session, List<String> requestOptions)436     private List<ProviderSession> initiateProviderSessions(
437             RequestSession session, List<String> requestOptions) {
438         List<ProviderSession> providerSessions = new ArrayList<>();
439         // Invoke all services of a user to initiate a provider session
440         runForUser(
441                 (service) -> {
442                     synchronized (mLock) {
443                         ProviderSession providerSession =
444                                 service.initiateProviderSessionForRequestLocked(
445                                         session, requestOptions);
446                         if (providerSession != null) {
447                             providerSessions.add(providerSession);
448                         }
449                     }
450                 });
451         return providerSessions;
452     }
453 
454     @Override
455     @GuardedBy("CredentialDescriptionRegistry.sLock")
onUserStopped(@onNull TargetUser user)456     public void onUserStopped(@NonNull TargetUser user) {
457         super.onUserStopped(user);
458         CredentialDescriptionRegistry.clearUserSession(user.getUserIdentifier());
459     }
460 
constructCallingAppInfo( String realPackageName, int userId, @Nullable String origin)461     private CallingAppInfo constructCallingAppInfo(
462             String realPackageName,
463             int userId,
464             @Nullable String origin) {
465         final PackageInfo packageInfo;
466         CallingAppInfo callingAppInfo;
467         try {
468             packageInfo =
469                     getContext()
470                             .getPackageManager()
471                             .getPackageInfoAsUser(
472                                     realPackageName,
473                                     PackageManager.PackageInfoFlags.of(
474                                             PackageManager.GET_SIGNING_CERTIFICATES),
475                                     userId);
476             callingAppInfo = new CallingAppInfo(realPackageName, packageInfo.signingInfo, origin);
477         } catch (PackageManager.NameNotFoundException e) {
478             Slog.e(TAG, "Issue while retrieving signatureInfo : ", e);
479             callingAppInfo = new CallingAppInfo(realPackageName, null, origin);
480         }
481         return callingAppInfo;
482     }
483 
484     final class CredentialManagerServiceStub extends ICredentialManager.Stub {
485         @Override
getCandidateCredentials( GetCredentialRequest request, IGetCandidateCredentialsCallback callback, IBinder clientBinder, final String callingPackage)486         public ICancellationSignal getCandidateCredentials(
487                 GetCredentialRequest request,
488                 IGetCandidateCredentialsCallback callback,
489                 IBinder clientBinder,
490                 final String callingPackage) {
491             Slog.i(TAG, "starting getCandidateCredentials with callingPackage: "
492                     + callingPackage);
493             ICancellationSignal cancelTransport = CancellationSignal.createTransport();
494 
495             final int userId = UserHandle.getCallingUserId();
496             final int callingUid = Binder.getCallingUid();
497 
498             // New request session, scoped for this request only.
499             final GetCandidateRequestSession session =
500                     new GetCandidateRequestSession(
501                             getContext(),
502                             mSessionManager,
503                             mLock,
504                             userId,
505                             callingUid,
506                             callback,
507                             request,
508                             constructCallingAppInfo(callingPackage, userId, request.getOrigin()),
509                             getEnabledProvidersForUser(userId),
510                             CancellationSignal.fromTransport(cancelTransport),
511                             clientBinder
512                     );
513             addSessionLocked(userId, session);
514 
515             List<ProviderSession> providerSessions =
516                     initiateProviderSessions(
517                             session,
518                             request.getCredentialOptions().stream()
519                                     .map(CredentialOption::getType)
520                                     .collect(Collectors.toList()));
521 
522             finalizeAndEmitInitialPhaseMetric(session);
523 
524             if (providerSessions.isEmpty()) {
525                 try {
526                     callback.onError(
527                             GetCandidateCredentialsException.TYPE_NO_CREDENTIAL,
528                             "No credentials available on this device.");
529                 } catch (RemoteException e) {
530                     Slog.i(
531                             TAG,
532                             "Issue invoking onError on IGetCredentialCallback "
533                                     + "callback: "
534                                     + e.getMessage());
535                 }
536             }
537 
538             invokeProviderSessions(providerSessions);
539             return cancelTransport;
540         }
541 
542         @Override
executeGetCredential( GetCredentialRequest request, IGetCredentialCallback callback, final String callingPackage)543         public ICancellationSignal executeGetCredential(
544                 GetCredentialRequest request,
545                 IGetCredentialCallback callback,
546                 final String callingPackage) {
547             final long timestampBegan = System.nanoTime();
548             Slog.i(TAG, "starting executeGetCredential with callingPackage: "
549                     + callingPackage);
550             ICancellationSignal cancelTransport = CancellationSignal.createTransport();
551 
552             final int userId = UserHandle.getCallingUserId();
553             final int callingUid = Binder.getCallingUid();
554             enforceCallingPackage(callingPackage, callingUid);
555 
556             validateGetCredentialRequest(request);
557 
558             // New request session, scoped for this request only.
559             final GetRequestSession session =
560                     new GetRequestSession(
561                             getContext(),
562                             mSessionManager,
563                             mLock,
564                             userId,
565                             callingUid,
566                             callback,
567                             request,
568                             constructCallingAppInfo(callingPackage, userId, request.getOrigin()),
569                             getEnabledProvidersForUser(userId),
570                             CancellationSignal.fromTransport(cancelTransport),
571                             timestampBegan);
572             addSessionLocked(userId, session);
573 
574             List<ProviderSession> providerSessions =
575                     prepareProviderSessions(request, session);
576 
577             if (providerSessions.isEmpty()) {
578                 try {
579                     callback.onError(
580                             GetCredentialException.TYPE_NO_CREDENTIAL,
581                             "No credentials available on this device.");
582                 } catch (RemoteException e) {
583                     Slog.e(
584                             TAG,
585                             "Issue invoking onError on IGetCredentialCallback "
586                                     + "callback: "
587                                     + e.getMessage());
588                 }
589             }
590 
591             invokeProviderSessions(providerSessions);
592             return cancelTransport;
593         }
594 
595         @Override
executePrepareGetCredential( GetCredentialRequest request, IPrepareGetCredentialCallback prepareGetCredentialCallback, IGetCredentialCallback getCredentialCallback, final String callingPackage)596         public ICancellationSignal executePrepareGetCredential(
597                 GetCredentialRequest request,
598                 IPrepareGetCredentialCallback prepareGetCredentialCallback,
599                 IGetCredentialCallback getCredentialCallback,
600                 final String callingPackage) {
601             final long timestampBegan = System.nanoTime();
602             ICancellationSignal cancelTransport = CancellationSignal.createTransport();
603 
604             if (request.getOrigin() != null) {
605                 // Check privileged permissions
606                 mContext.enforceCallingPermission(CREDENTIAL_MANAGER_SET_ORIGIN, null);
607             }
608             enforcePermissionForAllowedProviders(request);
609 
610             final int userId = UserHandle.getCallingUserId();
611             final int callingUid = Binder.getCallingUid();
612             enforceCallingPackage(callingPackage, callingUid);
613 
614             final PrepareGetRequestSession session =
615                     new PrepareGetRequestSession(
616                             getContext(),
617                             mSessionManager,
618                             mLock,
619                             userId,
620                             callingUid,
621                             getCredentialCallback,
622                             request,
623                             constructCallingAppInfo(callingPackage, userId, request.getOrigin()),
624                             getEnabledProvidersForUser(userId),
625                             CancellationSignal.fromTransport(cancelTransport),
626                             timestampBegan,
627                             prepareGetCredentialCallback);
628 
629             List<ProviderSession> providerSessions = prepareProviderSessions(request, session);
630 
631             if (providerSessions.isEmpty()) {
632                 try {
633                     prepareGetCredentialCallback.onResponse(
634                             new PrepareGetCredentialResponseInternal(PermissionUtils.hasPermission(
635                                     mContext,
636                                     callingPackage,
637                                     Manifest.permission
638                                             .CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS),
639                                     /*credentialResultTypes=*/null,
640                                     /*hasAuthenticationResults=*/false,
641                                     /*hasRemoteResults=*/false,
642                                     /*pendingIntent=*/null));
643                 } catch (RemoteException e) {
644                     Slog.e(
645                             TAG,
646                             "Issue invoking onError on IGetCredentialCallback "
647                                     + "callback: "
648                                     + e.getMessage());
649                 }
650             }
651 
652             invokeProviderSessions(providerSessions);
653 
654             return cancelTransport;
655         }
656 
prepareProviderSessions( GetCredentialRequest request, GetRequestSession session)657         private List<ProviderSession> prepareProviderSessions(
658                 GetCredentialRequest request,
659                 GetRequestSession session) {
660             List<ProviderSession> providerSessions;
661 
662             if (isCredentialDescriptionApiEnabled()) {
663                 List<CredentialOption> optionsThatRequireActiveCredentials =
664                         request.getCredentialOptions().stream()
665                                 .filter(credentialOption -> credentialOption
666                                         .getCredentialRetrievalData()
667                                         .getStringArrayList(
668                                                 CredentialOption
669                                                         .SUPPORTED_ELEMENT_KEYS) != null)
670                                 .toList();
671 
672                 List<CredentialOption> optionsThatDoNotRequireActiveCredentials =
673                         request.getCredentialOptions().stream()
674                                 .filter(credentialOption -> credentialOption
675                                         .getCredentialRetrievalData()
676                                         .getStringArrayList(
677                                                 CredentialOption
678                                                         .SUPPORTED_ELEMENT_KEYS) == null)
679                                 .toList();
680 
681                 List<ProviderSession> sessionsWithoutRemoteService =
682                         initiateProviderSessionsWithActiveContainers(
683                                 session,
684                                 getFilteredResultFromRegistry(optionsThatRequireActiveCredentials));
685 
686                 List<ProviderSession> sessionsWithRemoteService =
687                         initiateProviderSessions(
688                                 session,
689                                 optionsThatDoNotRequireActiveCredentials.stream()
690                                         .map(CredentialOption::getType)
691                                         .collect(Collectors.toList()));
692 
693                 Set<ProviderSession> all = new LinkedHashSet<>();
694                 all.addAll(sessionsWithRemoteService);
695                 all.addAll(sessionsWithoutRemoteService);
696 
697                 providerSessions = new ArrayList<>(all);
698             } else {
699                 // Initiate all provider sessions
700                 providerSessions =
701                         initiateProviderSessions(
702                                 session,
703                                 request.getCredentialOptions().stream()
704                                         .map(CredentialOption::getType)
705                                         .collect(Collectors.toList()));
706             }
707 
708             finalizeAndEmitInitialPhaseMetric(session);
709             // TODO(b/271135048) - May still be worth emitting in the empty cases above.
710             return providerSessions;
711         }
712 
invokeProviderSessions(List<ProviderSession> providerSessions)713         private void invokeProviderSessions(List<ProviderSession> providerSessions) {
714             providerSessions.forEach(ProviderSession::invokeSession);
715         }
716 
717         @Override
executeCreateCredential( CreateCredentialRequest request, ICreateCredentialCallback callback, String callingPackage)718         public ICancellationSignal executeCreateCredential(
719                 CreateCredentialRequest request,
720                 ICreateCredentialCallback callback,
721                 String callingPackage) {
722             final long timestampBegan = System.nanoTime();
723             Slog.i(TAG, "starting executeCreateCredential with callingPackage: "
724                     + callingPackage);
725             ICancellationSignal cancelTransport = CancellationSignal.createTransport();
726 
727             if (request.getOrigin() != null) {
728                 // Check privileged permissions
729                 mContext.enforceCallingPermission(CREDENTIAL_MANAGER_SET_ORIGIN, null);
730             }
731 
732             final int userId = UserHandle.getCallingUserId();
733             final int callingUid = Binder.getCallingUid();
734             enforceCallingPackage(callingPackage, callingUid);
735 
736             // New request session, scoped for this request only.
737             final CreateRequestSession session =
738                     new CreateRequestSession(
739                             getContext(),
740                             mSessionManager,
741                             mLock,
742                             userId,
743                             callingUid,
744                             request,
745                             callback,
746                             constructCallingAppInfo(callingPackage, userId, request.getOrigin()),
747                             getEnabledProvidersForUser(userId),
748                             getPrimaryProvidersForUserId(getContext(), userId),
749                             CancellationSignal.fromTransport(cancelTransport),
750                             timestampBegan);
751             addSessionLocked(userId, session);
752 
753             processCreateCredential(request, callback, session);
754             return cancelTransport;
755         }
756 
processCreateCredential( CreateCredentialRequest request, ICreateCredentialCallback callback, CreateRequestSession session)757         private void processCreateCredential(
758                 CreateCredentialRequest request,
759                 ICreateCredentialCallback callback,
760                 CreateRequestSession session) {
761             // Initiate all provider sessions
762             List<ProviderSession> providerSessions =
763                     initiateProviderSessions(session, List.of(request.getType()));
764 
765             if (providerSessions.isEmpty()) {
766                 try {
767                     callback.onError(
768                             CreateCredentialException.TYPE_NO_CREATE_OPTIONS,
769                             "No create options available.");
770                 } catch (RemoteException e) {
771                     Slog.e(
772                             TAG,
773                             "Issue invoking onError on ICreateCredentialCallback "
774                                     + "callback: ", e);
775                 }
776             }
777 
778             finalizeAndEmitInitialPhaseMetric(session);
779             // Iterate over all provider sessions and invoke the request
780             providerSessions.forEach(ProviderSession::invokeSession);
781         }
782 
finalizeAndEmitInitialPhaseMetric(GetCandidateRequestSession session)783         private void finalizeAndEmitInitialPhaseMetric(GetCandidateRequestSession session) {
784             var initMetric = session.mRequestSessionMetric.getInitialPhaseMetric();
785             initMetric.setAutofillSessionId(session.getAutofillSessionId());
786             initMetric.setAutofillRequestId(session.getAutofillRequestId());
787             finalizeAndEmitInitialPhaseMetric((RequestSession) session);
788         }
789 
finalizeAndEmitInitialPhaseMetric(RequestSession session)790         private void finalizeAndEmitInitialPhaseMetric(RequestSession session) {
791             try {
792                 var initMetric = session.mRequestSessionMetric.getInitialPhaseMetric();
793                 initMetric.setCredentialServiceBeginQueryTimeNanoseconds(System.nanoTime());
794                 MetricUtilities.logApiCalledInitialPhase(initMetric,
795                         session.mRequestSessionMetric.returnIncrementSequence());
796             } catch (Exception e) {
797                 Slog.i(TAG, "Unexpected error during metric logging: ", e);
798             }
799         }
800 
801         @Override
setEnabledProviders( List<String> primaryProviders, List<String> providers, int userId, ISetEnabledProvidersCallback callback)802         public void setEnabledProviders(
803                 List<String> primaryProviders, List<String> providers, int userId,
804                 ISetEnabledProvidersCallback callback) {
805             final int callingUid = Binder.getCallingUid();
806             if (!hasWriteSecureSettingsPermission()) {
807                 try {
808                     MetricUtilities.logApiCalledSimpleV2(
809                             ApiName.SET_ENABLED_PROVIDERS,
810                             ApiStatus.FAILURE, callingUid);
811                     callback.onError(
812                             PERMISSION_DENIED_ERROR, PERMISSION_DENIED_WRITE_SECURE_SETTINGS_ERROR);
813                 } catch (RemoteException e) {
814                     MetricUtilities.logApiCalledSimpleV2(
815                             ApiName.SET_ENABLED_PROVIDERS,
816                             ApiStatus.FAILURE, callingUid);
817                     Slog.e(TAG, "Issue with invoking response: ", e);
818                 }
819                 return;
820             }
821 
822             userId =
823                     ActivityManager.handleIncomingUser(
824                             Binder.getCallingPid(),
825                             Binder.getCallingUid(),
826                             userId,
827                             false,
828                             false,
829                             "setEnabledProviders",
830                             null);
831 
832             Set<String> enableProvider = new HashSet<>(providers);
833             enableProvider.addAll(primaryProviders);
834 
835             boolean writeEnabledStatus =
836                     Settings.Secure.putStringForUser(getContext().getContentResolver(),
837                             Settings.Secure.CREDENTIAL_SERVICE,
838                             String.join(":", enableProvider),
839                             userId);
840 
841             boolean writePrimaryStatus =
842                     Settings.Secure.putStringForUser(getContext().getContentResolver(),
843                             Settings.Secure.CREDENTIAL_SERVICE_PRIMARY,
844                             String.join(":", primaryProviders),
845                             userId);
846 
847             if (!writeEnabledStatus || !writePrimaryStatus) {
848                 Slog.e(TAG, "Failed to store setting containing enabled or primary providers");
849                 try {
850                     MetricUtilities.logApiCalledSimpleV2(
851                             ApiName.SET_ENABLED_PROVIDERS,
852                             ApiStatus.FAILURE, callingUid);
853                     callback.onError(
854                             "failed_setting_store",
855                             "Failed to store setting containing enabled or primary providers");
856                 } catch (RemoteException e) {
857                     MetricUtilities.logApiCalledSimpleV2(
858                             ApiName.SET_ENABLED_PROVIDERS,
859                             ApiStatus.FAILURE, callingUid);
860                     Slog.e(TAG, "Issue with invoking error response: ", e);
861                     return;
862                 }
863             }
864 
865             // Call the callback.
866             try {
867                 MetricUtilities.logApiCalledSimpleV2(
868                         ApiName.SET_ENABLED_PROVIDERS,
869                         ApiStatus.SUCCESS, callingUid);
870                 callback.onResponse();
871             } catch (RemoteException e) {
872                 MetricUtilities.logApiCalledSimpleV2(
873                         ApiName.SET_ENABLED_PROVIDERS,
874                         ApiStatus.FAILURE, callingUid);
875                 Slog.e(TAG, "Issue with invoking response: ", e);
876                 // TODO: Propagate failure
877             }
878         }
879 
880         @Override
isEnabledCredentialProviderService( ComponentName componentName, String callingPackage)881         public boolean isEnabledCredentialProviderService(
882                 ComponentName componentName, String callingPackage) {
883             Slog.i(TAG, "isEnabledCredentialProviderService with componentName: "
884                     + componentName.flattenToString());
885 
886             final int userId = UserHandle.getCallingUserId();
887             final int callingUid = Binder.getCallingUid();
888             enforceCallingPackage(callingPackage, callingUid);
889 
890             if (componentName == null) {
891                 Slog.w(TAG, "isEnabledCredentialProviderService componentName is null");
892                 // If the component name was not specified then throw an error and
893                 // record a failure because the request failed due to invalid input.
894                 MetricUtilities.logApiCalledSimpleV2(
895                       ApiName.IS_ENABLED_CREDENTIAL_PROVIDER_SERVICE,
896                       ApiStatus.FAILURE, callingUid);
897                 return false;
898             }
899 
900             if (!componentName.getPackageName().equals(callingPackage)) {
901                 Slog.w(TAG, "isEnabledCredentialProviderService component name"
902                         + " does not match requested component");
903                 // If the requested component name package name does not match
904                 // the calling package then throw an error and record a failure
905                 // metric (because the request failed due to invalid input).
906                 MetricUtilities.logApiCalledSimpleV2(
907                       ApiName.IS_ENABLED_CREDENTIAL_PROVIDER_SERVICE,
908                       ApiStatus.FAILURE, callingUid);
909                 throw new IllegalArgumentException("provided component name does not match"
910                         + " does not match requesting component");
911             }
912 
913             final Set<ComponentName> enabledProviders = getEnabledProvidersForUser(userId);
914             MetricUtilities.logApiCalledSimpleV2(
915                 ApiName.IS_ENABLED_CREDENTIAL_PROVIDER_SERVICE,
916                 ApiStatus.SUCCESS, callingUid);
917             if (enabledProviders == null) {
918                 return false;
919             }
920             return enabledProviders.contains(componentName);
921         }
922 
923         @Override
getCredentialProviderServices( int userId, int providerFilter)924         public List<CredentialProviderInfo> getCredentialProviderServices(
925                 int userId, int providerFilter) {
926             verifyGetProvidersPermission();
927             final int callingUid = Binder.getCallingUid();
928             MetricUtilities.logApiCalledSimpleV2(
929                     ApiName.GET_CREDENTIAL_PROVIDER_SERVICES,
930                     ApiStatus.SUCCESS, callingUid);
931             return CredentialProviderInfoFactory
932                     .getCredentialProviderServices(
933                             mContext, userId, providerFilter, getEnabledProvidersForUser(userId),
934                             getPrimaryProvidersForUserId(mContext, userId));
935 
936         }
937 
938         @Override
getCredentialProviderServicesForTesting( int providerFilter)939         public List<CredentialProviderInfo> getCredentialProviderServicesForTesting(
940                 int providerFilter) {
941             verifyGetProvidersPermission();
942 
943             final int userId = UserHandle.getCallingUserId();
944             return CredentialProviderInfoFactory.getCredentialProviderServicesForTesting(
945                     mContext, userId, providerFilter, getEnabledProvidersForUser(userId),
946                     getPrimaryProvidersForUserId(mContext, userId));
947         }
948 
949         @Override
isServiceEnabled()950         public boolean isServiceEnabled() {
951             final long origId = Binder.clearCallingIdentity();
952             try {
953                 return DeviceConfig.getBoolean(
954                         DeviceConfig.NAMESPACE_CREDENTIAL,
955                         DEVICE_CONFIG_ENABLE_CREDENTIAL_MANAGER,
956                         true);
957             } finally {
958                 Binder.restoreCallingIdentity(origId);
959             }
960         }
961 
getEnabledProvidersForUser(int userId)962         private Set<ComponentName> getEnabledProvidersForUser(int userId) {
963             final int resolvedUserId = ActivityManager.handleIncomingUser(
964                     Binder.getCallingPid(), Binder.getCallingUid(),
965                     userId, false, false,
966                     "getEnabledProvidersForUser", null);
967 
968             Set<ComponentName> enabledProviders = new HashSet<>();
969             String directValue = Settings.Secure.getStringForUser(
970                     mContext.getContentResolver(), Settings.Secure.CREDENTIAL_SERVICE,
971                     resolvedUserId);
972 
973             if (!TextUtils.isEmpty(directValue)) {
974                 String[] components = directValue.split(":");
975                 for (String componentString : components) {
976                     ComponentName component = ComponentName.unflattenFromString(componentString);
977                     if (component != null) {
978                         enabledProviders.add(component);
979                     }
980                 }
981             }
982 
983             return enabledProviders;
984         }
985 
986         @Override
clearCredentialState( ClearCredentialStateRequest request, IClearCredentialStateCallback callback, String callingPackage)987         public ICancellationSignal clearCredentialState(
988                 ClearCredentialStateRequest request,
989                 IClearCredentialStateCallback callback,
990                 String callingPackage) {
991             final long timestampBegan = System.nanoTime();
992             Slog.i(TAG, "starting clearCredentialState with callingPackage: "
993                     + callingPackage);
994             final int userId = UserHandle.getCallingUserId();
995             int callingUid = Binder.getCallingUid();
996             enforceCallingPackage(callingPackage, callingUid);
997 
998             // TODO : Implement cancellation
999             ICancellationSignal cancelTransport = CancellationSignal.createTransport();
1000 
1001             // New request session, scoped for this request only.
1002             final ClearRequestSession session =
1003                     new ClearRequestSession(
1004                             getContext(),
1005                             mSessionManager,
1006                             mLock,
1007                             userId,
1008                             callingUid,
1009                             callback,
1010                             request,
1011                             constructCallingAppInfo(callingPackage, userId, null),
1012                             getEnabledProvidersForUser(userId),
1013                             CancellationSignal.fromTransport(cancelTransport),
1014                             timestampBegan);
1015             addSessionLocked(userId, session);
1016 
1017             // Initiate all provider sessions
1018             // TODO: Determine if provider needs to have clear capability in their manifest
1019             List<ProviderSession> providerSessions = initiateProviderSessions(session, List.of());
1020 
1021             if (providerSessions.isEmpty()) {
1022                 try {
1023                     // TODO("Replace with properly defined error type")
1024                     callback.onError("UNKNOWN", "No credentials available on "
1025                             + "this device");
1026                 } catch (RemoteException e) {
1027                     Slog.e(
1028                             TAG,
1029                             "Issue invoking onError on IClearCredentialStateCallback "
1030                                     + "callback: ", e);
1031                 }
1032             }
1033 
1034             finalizeAndEmitInitialPhaseMetric(session);
1035 
1036             // Iterate over all provider sessions and invoke the request
1037             providerSessions.forEach(ProviderSession::invokeSession);
1038             return cancelTransport;
1039         }
1040 
1041         @Override
registerCredentialDescription( RegisterCredentialDescriptionRequest request, String callingPackage)1042         public void registerCredentialDescription(
1043                 RegisterCredentialDescriptionRequest request, String callingPackage)
1044                 throws IllegalArgumentException, NonCredentialProviderCallerException {
1045             Slog.i(TAG, "registerCredentialDescription with callingPackage: " + callingPackage);
1046 
1047             if (!isCredentialDescriptionApiEnabled()) {
1048                 throw new UnsupportedOperationException("Feature not supported");
1049             }
1050 
1051             enforceCallingPackage(callingPackage, Binder.getCallingUid());
1052 
1053             CredentialDescriptionRegistry session =
1054                     CredentialDescriptionRegistry.forUser(UserHandle.getCallingUserId());
1055 
1056             session.executeRegisterRequest(request, callingPackage);
1057         }
1058 
1059         @Override
unregisterCredentialDescription( UnregisterCredentialDescriptionRequest request, String callingPackage)1060         public void unregisterCredentialDescription(
1061                 UnregisterCredentialDescriptionRequest request, String callingPackage)
1062                 throws IllegalArgumentException {
1063             Slog.i(TAG, "unregisterCredentialDescription with callingPackage: "
1064                     + callingPackage);
1065 
1066 
1067             if (!isCredentialDescriptionApiEnabled()) {
1068                 throw new UnsupportedOperationException("Feature not supported");
1069             }
1070 
1071             enforceCallingPackage(callingPackage, Binder.getCallingUid());
1072 
1073             CredentialDescriptionRegistry session =
1074                     CredentialDescriptionRegistry.forUser(UserHandle.getCallingUserId());
1075 
1076             session.executeUnregisterRequest(request, callingPackage);
1077         }
1078     }
1079 
validateGetCredentialRequest(GetCredentialRequest request)1080     private void validateGetCredentialRequest(GetCredentialRequest request) {
1081         if (request.getOrigin() != null) {
1082             // Check privileged permissions
1083             mContext.enforceCallingPermission(CREDENTIAL_MANAGER_SET_ORIGIN, null);
1084         }
1085         enforcePermissionForAllowedProviders(request);
1086     }
1087 
enforcePermissionForAllowedProviders(GetCredentialRequest request)1088     private void enforcePermissionForAllowedProviders(GetCredentialRequest request) {
1089         boolean containsAllowedProviders = request.getCredentialOptions()
1090                 .stream()
1091                 .anyMatch(option -> option.getAllowedProviders() != null
1092                         && !option.getAllowedProviders().isEmpty());
1093         if (containsAllowedProviders) {
1094             mContext.enforceCallingPermission(CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS,
1095                     null);
1096         }
1097     }
1098 
addSessionLocked(@serIdInt int userId, RequestSession requestSession)1099     private void addSessionLocked(@UserIdInt int userId,
1100             RequestSession requestSession) {
1101         synchronized (mLock) {
1102             mSessionManager.addSession(userId, requestSession.mRequestId, requestSession);
1103         }
1104     }
1105 
enforceCallingPackage(String callingPackage, int callingUid)1106     private void enforceCallingPackage(String callingPackage, int callingUid) {
1107         int packageUid;
1108         PackageManager pm = mContext.createContextAsUser(
1109                 UserHandle.getUserHandleForUid(callingUid), 0).getPackageManager();
1110         try {
1111             packageUid = pm.getPackageUid(callingPackage,
1112                     PackageManager.PackageInfoFlags.of(0));
1113         } catch (PackageManager.NameNotFoundException e) {
1114             throw new SecurityException(callingPackage + " not found");
1115         }
1116         if (packageUid != callingUid) {
1117             throw new SecurityException(callingPackage + " does not belong to uid " + callingUid);
1118         }
1119     }
1120 
1121     private class SessionManager implements RequestSession.SessionLifetime {
1122         @Override
1123         @GuardedBy("mLock")
onFinishRequestSession(@serIdInt int userId, IBinder token)1124         public void onFinishRequestSession(@UserIdInt int userId, IBinder token) {
1125             if (mRequestSessions.get(userId) != null) {
1126                 mRequestSessions.get(userId).remove(token);
1127             }
1128         }
1129 
1130         @GuardedBy("mLock")
addSession(int userId, IBinder token, RequestSession requestSession)1131         public void addSession(int userId, IBinder token, RequestSession requestSession) {
1132             if (mRequestSessions.get(userId) == null) {
1133                 mRequestSessions.put(userId, new HashMap<>());
1134             }
1135             mRequestSessions.get(userId).put(token, requestSession);
1136         }
1137     }
1138 
1139     /** Updates the list of providers when an app is uninstalled. */
updateProvidersWhenPackageRemoved( SettingsWrapper settingsWrapper, String packageName)1140     public static void updateProvidersWhenPackageRemoved(
1141             SettingsWrapper settingsWrapper, String packageName) {
1142         Slog.i(TAG, "updateProvidersWhenPackageRemoved");
1143 
1144         // Get the current providers.
1145         String rawProviders =
1146                 settingsWrapper.getStringForUser(
1147                         Settings.Secure.CREDENTIAL_SERVICE_PRIMARY, UserHandle.myUserId());
1148         if (rawProviders == null) {
1149             Slog.w(TAG, "settings key is null");
1150             return;
1151         }
1152 
1153         // Remove any providers from the primary setting that contain the package name
1154         // being removed.
1155         Set<String> primaryProviders = getStoredProviders(rawProviders, packageName);
1156         if (!settingsWrapper.putStringForUser(
1157                 Settings.Secure.CREDENTIAL_SERVICE_PRIMARY,
1158                 String.join(":", primaryProviders),
1159                 UserHandle.myUserId(),
1160                 /* overrideableByRestore= */ true)) {
1161             Slog.e(TAG, "Failed to remove primary package: " + packageName);
1162             return;
1163         }
1164 
1165         // Read the autofill provider so we don't accidentally erase it.
1166         String autofillProvider =
1167                 settingsWrapper.getStringForUser(
1168                         Settings.Secure.AUTOFILL_SERVICE, UserHandle.myUserId());
1169 
1170         // If there is an autofill provider and it is the credential autofill service indicating
1171         // that the currently selected primary provider does not support autofill
1172         // then we should keep as is
1173         String credentialAutofillService = settingsWrapper.mContext.getResources().getString(
1174                 R.string.config_defaultCredentialManagerAutofillService);
1175         if (autofillProvider != null && primaryProviders.isEmpty() && !TextUtils.equals(
1176                 autofillProvider, credentialAutofillService)) {
1177             // If the existing autofill provider is from the app being removed
1178             // then erase the autofill service setting.
1179             ComponentName cn = ComponentName.unflattenFromString(autofillProvider);
1180             if (cn != null && cn.getPackageName().equals(packageName)) {
1181                 if (!settingsWrapper.putStringForUser(
1182                         Settings.Secure.AUTOFILL_SERVICE,
1183                         "",
1184                         UserHandle.myUserId(),
1185                         /* overrideableByRestore= */ true)) {
1186                     Slog.e(TAG, "Failed to remove autofill package: " + packageName);
1187                 }
1188             }
1189         }
1190 
1191         // Read the credential providers to remove any reference of the removed app.
1192         String rawCredentialProviders =
1193                 settingsWrapper.getStringForUser(
1194                         Settings.Secure.CREDENTIAL_SERVICE, UserHandle.myUserId());
1195 
1196         // Remove any providers that belong to the removed app.
1197         Set<String> credentialProviders = getStoredProviders(rawCredentialProviders, packageName);
1198         if (!settingsWrapper.putStringForUser(
1199                 Settings.Secure.CREDENTIAL_SERVICE,
1200                 String.join(":", credentialProviders),
1201                 UserHandle.myUserId(),
1202                 /* overrideableByRestore= */ true)) {
1203             Slog.e(TAG, "Failed to remove secondary package: " + packageName);
1204         }
1205     }
1206 
1207     /** Gets the list of stored providers from a string removing any mention of package name. */
getStoredProviders(String rawProviders, String packageName)1208     public static Set<String> getStoredProviders(String rawProviders, String packageName) {
1209         // If the app being removed matches any of the package names from
1210         // this list then don't add it in the output.
1211         Set<String> providers = new HashSet<>();
1212         if (rawProviders == null || packageName == null) {
1213             return providers;
1214         }
1215         for (String rawComponentName : rawProviders.split(":")) {
1216             if (TextUtils.isEmpty(rawComponentName)
1217                     || rawComponentName.equals("null")) {
1218                 Slog.d(TAG, "provider component name is empty or null");
1219                 continue;
1220             }
1221 
1222             ComponentName cn = ComponentName.unflattenFromString(rawComponentName);
1223             if (cn != null && !cn.getPackageName().equals(packageName)) {
1224                 providers.add(cn.flattenToString());
1225             }
1226         }
1227 
1228         return providers;
1229     }
1230 
1231     /** A wrapper class that can be used by tests for intercepting reads/writes. */
1232     public static class SettingsWrapper {
1233         private final Context mContext;
1234 
SettingsWrapper(@onNull Context context)1235         public SettingsWrapper(@NonNull Context context) {
1236             this.mContext = context;
1237         }
1238 
getContentResolver()1239         ContentResolver getContentResolver() {
1240             return mContext.getContentResolver();
1241         }
1242 
1243         /** Retrieves the string value of a system setting */
getStringForUser(String name, int userHandle)1244         public String getStringForUser(String name, int userHandle) {
1245             return Settings.Secure.getStringForUser(getContentResolver(), name, userHandle);
1246         }
1247 
1248         /** Updates the string value of a system setting */
putStringForUser( String name, String value, int userHandle, boolean overrideableByRestore)1249         public boolean putStringForUser(
1250                 String name,
1251                 String value,
1252                 int userHandle,
1253                 boolean overrideableByRestore) {
1254             return Settings.Secure.putStringForUser(
1255                     getContentResolver(),
1256                     name,
1257                     value,
1258                     null,
1259                     false,
1260                     userHandle,
1261                     overrideableByRestore);
1262         }
1263     }
1264 }
1265