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