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 com.android.server.credentials.CredentialManagerService.getPrimaryProvidersForUserId; 20 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.content.ComponentName; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.pm.PackageManager; 27 import android.content.pm.ResolveInfo; 28 import android.content.pm.ServiceInfo; 29 import android.credentials.CredentialManager; 30 import android.credentials.CredentialProviderInfo; 31 import android.credentials.flags.Flags; 32 import android.service.credentials.CredentialProviderInfoFactory; 33 import android.service.credentials.CredentialProviderService; 34 import android.util.Slog; 35 36 import com.android.internal.annotations.GuardedBy; 37 import com.android.server.infra.AbstractPerUserSystemService; 38 39 import java.util.List; 40 import java.util.Set; 41 42 43 /** 44 * Per-user, per remote service implementation of {@link CredentialManagerService} 45 */ 46 public final class CredentialManagerServiceImpl extends 47 AbstractPerUserSystemService<CredentialManagerServiceImpl, CredentialManagerService> { 48 private static final String TAG = CredentialManager.TAG; 49 50 @GuardedBy("mLock") 51 @NonNull 52 private CredentialProviderInfo mInfo; 53 CredentialManagerServiceImpl( @onNull CredentialManagerService master, @NonNull Object lock, int userId, String serviceName)54 CredentialManagerServiceImpl( 55 @NonNull CredentialManagerService master, 56 @NonNull Object lock, int userId, String serviceName) 57 throws PackageManager.NameNotFoundException { 58 super(master, lock, userId); 59 Slog.i(TAG, "CredentialManagerServiceImpl constructed for: " + serviceName); 60 synchronized (mLock) { 61 newServiceInfoLocked(ComponentName.unflattenFromString(serviceName)); 62 } 63 } 64 65 @Nullable 66 @Override 67 @GuardedBy("mLock") getServiceComponentName()68 public ComponentName getServiceComponentName() { 69 return getComponentName(); 70 } 71 72 @GuardedBy("mLock") getComponentName()73 public ComponentName getComponentName() { 74 return mInfo.getServiceInfo().getComponentName(); 75 } 76 CredentialManagerServiceImpl( @onNull CredentialManagerService master, @NonNull Object lock, int userId, CredentialProviderInfo providerInfo)77 CredentialManagerServiceImpl( 78 @NonNull CredentialManagerService master, 79 @NonNull Object lock, int userId, CredentialProviderInfo providerInfo) { 80 super(master, lock, userId); 81 Slog.i(TAG, "CredentialManagerServiceImpl constructed for: " 82 + providerInfo.getServiceInfo().getComponentName().flattenToString()); 83 mInfo = providerInfo; 84 } 85 86 @Override // from PerUserSystemService when a new service is to be created 87 @GuardedBy("mLock") newServiceInfoLocked(@onNull ComponentName serviceComponent)88 protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent) 89 throws PackageManager.NameNotFoundException, SecurityException, NullPointerException { 90 boolean isSystemProvider = false; 91 if (mInfo != null) { 92 Slog.i(TAG, "newServiceInfoLocked, mInfo not null : " 93 + mInfo.getServiceInfo().getComponentName().flattenToString() + " , " 94 + serviceComponent.flattenToString()); 95 isSystemProvider = mInfo.isSystemProvider(); 96 } else { 97 Slog.i(TAG, "newServiceInfoLocked, mInfo null, " 98 + serviceComponent.flattenToString()); 99 } 100 Set<ComponentName> primaryProviders = 101 getPrimaryProvidersForUserId(mMaster.getContext(), mUserId); 102 mInfo = CredentialProviderInfoFactory.create( 103 getContext(), serviceComponent, 104 mUserId, isSystemProvider, 105 primaryProviders.contains(serviceComponent)); 106 return mInfo.getServiceInfo(); 107 } 108 109 /** 110 * Starts a provider session and associates it with the given request session. 111 */ 112 @Nullable 113 @GuardedBy("mLock") initiateProviderSessionForRequestLocked( RequestSession requestSession, List<String> requestOptions)114 public ProviderSession initiateProviderSessionForRequestLocked( 115 RequestSession requestSession, List<String> requestOptions) { 116 if (!requestOptions.isEmpty() && !isServiceCapableLocked(requestOptions)) { 117 if (mInfo != null) { 118 Slog.i(TAG, "Service does not have the required capabilities: " 119 + mInfo.getComponentName()); 120 } 121 return null; 122 } 123 if (mInfo == null) { 124 Slog.w(TAG, "Initiating provider session for request " 125 + "but mInfo is null. This shouldn't happen"); 126 return null; 127 } 128 final RemoteCredentialService remoteService = new RemoteCredentialService( 129 getContext(), mInfo.getServiceInfo().getComponentName(), mUserId); 130 return requestSession.initiateProviderSession(mInfo, remoteService); 131 } 132 133 /** Return true if at least one capability found. */ 134 @GuardedBy("mLock") isServiceCapableLocked(List<String> requestedOptions)135 boolean isServiceCapableLocked(List<String> requestedOptions) { 136 if (mInfo == null) { 137 return false; 138 } 139 for (String capability : requestedOptions) { 140 if (mInfo.hasCapability(capability)) { 141 return true; 142 } 143 } 144 return false; 145 } 146 147 @GuardedBy("mLock") getCredentialProviderInfo()148 public CredentialProviderInfo getCredentialProviderInfo() { 149 return mInfo; 150 } 151 152 /** 153 * Callback called when an app has been updated. 154 * 155 * @param packageName package of the app being updated. 156 */ 157 @GuardedBy("mLock") 158 @SuppressWarnings("GuardedBy") // ErrorProne requires this.mMaster.mLock which is the case 159 // because this method is called by this.mMaster anyway handlePackageUpdateLocked(@onNull String packageName)160 protected void handlePackageUpdateLocked(@NonNull String packageName) { 161 if (mInfo != null && mInfo.getServiceInfo() != null 162 && mInfo.getServiceInfo().getComponentName() 163 .getPackageName().equals(packageName)) { 164 if (Flags.packageUpdateFixEnabled()) { 165 try { 166 updateCredentialProviderInfo(mInfo.getServiceInfo().getComponentName(), 167 mInfo.isSystemProvider()); 168 } catch (SecurityException | PackageManager.NameNotFoundException 169 | NullPointerException e) { 170 Slog.w(TAG, "Unable to update provider, must be removed: " + e.getMessage()); 171 mMaster.handleServiceRemovedMultiModeLocked(mInfo.getComponentName(), mUserId); 172 } 173 } else { 174 try { 175 newServiceInfoLocked(mInfo.getServiceInfo().getComponentName()); 176 } catch (PackageManager.NameNotFoundException e) { 177 Slog.e(TAG, "Issue while updating serviceInfo: " + e.getMessage()); 178 } 179 } 180 } 181 } 182 183 @GuardedBy("mLock") updateCredentialProviderInfo(ComponentName componentName, boolean isSystemProvider)184 private void updateCredentialProviderInfo(ComponentName componentName, boolean isSystemProvider) 185 throws SecurityException, PackageManager.NameNotFoundException { 186 Slog.d(TAG, "Updating credential provider: " + componentName.flattenToString()); 187 if (!isValidCredentialProviderInfo(componentName, mUserId, isSystemProvider)) { 188 throw new SecurityException("Service has not been set up correctly"); 189 } 190 newServiceInfoLocked(componentName); 191 } 192 isValidCredentialProviderInfo(ComponentName componentName, int userId, boolean isSystemProvider)193 private boolean isValidCredentialProviderInfo(ComponentName componentName, int userId, 194 boolean isSystemProvider) { 195 Context context = getContext(); 196 if (context == null) { 197 return false; 198 } 199 String serviceInterface = CredentialProviderService.SERVICE_INTERFACE; 200 if (isSystemProvider) { 201 serviceInterface = CredentialProviderService.SYSTEM_SERVICE_INTERFACE; 202 } 203 final List<ResolveInfo> resolveInfos = 204 context.getPackageManager() 205 .queryIntentServicesAsUser( 206 new Intent(serviceInterface), 207 PackageManager.ResolveInfoFlags.of(PackageManager.GET_META_DATA), 208 userId); 209 for (ResolveInfo resolveInfo : resolveInfos) { 210 final ServiceInfo serviceInfo = resolveInfo.serviceInfo; 211 if (serviceInfo.getComponentName().equals(componentName)) { 212 return true; 213 } 214 } 215 return false; 216 } 217 } 218