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