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 android.Manifest; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.content.ComponentName; 23 import android.content.Context; 24 import android.content.pm.ApplicationInfo; 25 import android.content.pm.PackageManager; 26 import android.credentials.Credential; 27 import android.credentials.CredentialManager; 28 import android.credentials.CredentialProviderInfo; 29 import android.credentials.selection.ProviderData; 30 import android.credentials.selection.ProviderPendingIntentResponse; 31 import android.os.ICancellationSignal; 32 import android.os.RemoteException; 33 import android.util.Slog; 34 35 import com.android.server.credentials.metrics.ProviderSessionMetric; 36 37 import java.util.UUID; 38 39 /** 40 * Provider session storing the state of provider response and ui entries. 41 * 42 * @param <T> The request to be sent to the provider 43 * @param <R> The response to be expected from the provider 44 */ 45 public abstract class ProviderSession<T, R> 46 implements RemoteCredentialService.ProviderCallbacks<R> { 47 48 private static final String TAG = CredentialManager.TAG; 49 50 @NonNull 51 protected final Context mContext; 52 @NonNull 53 protected final ComponentName mComponentName; 54 @Nullable 55 protected final CredentialProviderInfo mProviderInfo; 56 @Nullable 57 protected final RemoteCredentialService mRemoteCredentialService; 58 @NonNull 59 protected final int mUserId; 60 @NonNull 61 protected Status mStatus = Status.NOT_STARTED; 62 @Nullable 63 protected final ProviderInternalCallback mCallbacks; 64 @Nullable 65 protected Credential mFinalCredentialResponse; 66 @Nullable 67 protected ICancellationSignal mProviderCancellationSignal; 68 @NonNull 69 protected final T mProviderRequest; 70 @Nullable 71 protected R mProviderResponse; 72 @NonNull 73 protected Boolean mProviderResponseSet = false; 74 @NonNull 75 protected final ProviderSessionMetric mProviderSessionMetric; 76 @NonNull 77 private int mProviderSessionUid; 78 79 enum CredentialsSource { 80 REMOTE_PROVIDER, 81 REGISTRY, 82 AUTH_ENTRY 83 } 84 85 /** 86 * Returns true if the given status reflects that the provider state is ready to be shown 87 * on the credMan UI. 88 */ isUiInvokingStatus(Status status)89 public static boolean isUiInvokingStatus(Status status) { 90 return status == Status.CREDENTIALS_RECEIVED || status == Status.SAVE_ENTRIES_RECEIVED 91 || status == Status.NO_CREDENTIALS_FROM_AUTH_ENTRY; 92 } 93 94 /** 95 * Returns true if the given status reflects that the provider is waiting for a remote 96 * response. 97 */ isStatusWaitingForRemoteResponse(Status status)98 public static boolean isStatusWaitingForRemoteResponse(Status status) { 99 return status == Status.PENDING; 100 } 101 102 /** 103 * Returns true if the given status means that the provider session must be terminated. 104 */ isTerminatingStatus(Status status)105 public static boolean isTerminatingStatus(Status status) { 106 return status == Status.CANCELED || status == Status.SERVICE_DEAD; 107 } 108 109 /** 110 * Returns true if the given status reflects that the provider is done getting the response, 111 * and is ready to return the final credential back to the user. 112 */ isCompletionStatus(Status status)113 public static boolean isCompletionStatus(Status status) { 114 return status == Status.COMPLETE || status == Status.EMPTY_RESPONSE; 115 } 116 117 /** 118 * Gives access to the objects metric collectors. 119 */ getProviderSessionMetric()120 public ProviderSessionMetric getProviderSessionMetric() { 121 return this.mProviderSessionMetric; 122 } 123 124 /** 125 * Interface to be implemented by any class that wishes to get a callback when a particular 126 * provider session's status changes. Typically, implemented by the {@link RequestSession} 127 * class. 128 * 129 * @param <V> the type of the final response expected 130 */ 131 public interface ProviderInternalCallback<V> { 132 /** Called when status changes. */ onProviderStatusChanged(Status status, ComponentName componentName, CredentialsSource source)133 void onProviderStatusChanged(Status status, ComponentName componentName, 134 CredentialsSource source); 135 136 /** Called when the final credential is received through an entry selection. */ onFinalResponseReceived(ComponentName componentName, V response)137 void onFinalResponseReceived(ComponentName componentName, V response); 138 139 /** Called when an error is received through an entry selection. */ onFinalErrorReceived(ComponentName componentName, String errorType, @Nullable String message)140 void onFinalErrorReceived(ComponentName componentName, String errorType, 141 @Nullable String message); 142 } 143 ProviderSession(@onNull Context context, @NonNull T providerRequest, @Nullable ProviderInternalCallback callbacks, @NonNull ComponentName componentName, @NonNull int userId, @Nullable RemoteCredentialService remoteCredentialService)144 protected ProviderSession(@NonNull Context context, 145 @NonNull T providerRequest, 146 @Nullable ProviderInternalCallback callbacks, 147 @NonNull ComponentName componentName, 148 @NonNull int userId, 149 @Nullable RemoteCredentialService remoteCredentialService) { 150 mContext = context; 151 mProviderInfo = null; 152 mProviderRequest = providerRequest; 153 mCallbacks = callbacks; 154 mUserId = userId; 155 mComponentName = componentName; 156 mRemoteCredentialService = remoteCredentialService; 157 mProviderSessionUid = MetricUtilities.getPackageUid(mContext, mComponentName, userId); 158 mProviderSessionMetric = new ProviderSessionMetric( 159 ((RequestSession) mCallbacks).mRequestSessionMetric.getSessionIdTrackTwo()); 160 } 161 162 /** Provider status at various states of the provider session. */ 163 enum Status { 164 NOT_STARTED, 165 PENDING, 166 CREDENTIALS_RECEIVED, 167 SERVICE_DEAD, 168 SAVE_ENTRIES_RECEIVED, 169 CANCELED, 170 EMPTY_RESPONSE, 171 NO_CREDENTIALS_FROM_AUTH_ENTRY, 172 COMPLETE 173 } 174 generateUniqueId()175 protected static String generateUniqueId() { 176 return UUID.randomUUID().toString(); 177 } 178 getFinalCredentialResponse()179 public Credential getFinalCredentialResponse() { 180 return mFinalCredentialResponse; 181 } 182 183 /** Propagates cancellation signal to the remote provider service. */ cancelProviderRemoteSession()184 public void cancelProviderRemoteSession() { 185 try { 186 if (mProviderCancellationSignal != null) { 187 mProviderCancellationSignal.cancel(); 188 } 189 setStatus(Status.CANCELED); 190 } catch (RemoteException e) { 191 Slog.e(TAG, "Issue while cancelling provider session: ", e); 192 } 193 } 194 setStatus(@onNull Status status)195 protected void setStatus(@NonNull Status status) { 196 mStatus = status; 197 } 198 199 @NonNull getStatus()200 protected Status getStatus() { 201 return mStatus; 202 } 203 204 @NonNull getComponentName()205 protected ComponentName getComponentName() { 206 return mComponentName; 207 } 208 209 @Nullable getRemoteCredentialService()210 protected RemoteCredentialService getRemoteCredentialService() { 211 return mRemoteCredentialService; 212 } 213 214 /** Updates the status . */ updateStatusAndInvokeCallback(@onNull Status status, CredentialsSource source)215 protected void updateStatusAndInvokeCallback(@NonNull Status status, 216 CredentialsSource source) { 217 setStatus(status); 218 boolean isPrimary = mProviderInfo != null && mProviderInfo.isPrimary(); 219 mProviderSessionMetric.collectCandidateMetricUpdate(isTerminatingStatus(status) 220 || isStatusWaitingForRemoteResponse(status), 221 isCompletionStatus(status) || isUiInvokingStatus(status), 222 mProviderSessionUid, 223 /*isAuthEntry*/source == CredentialsSource.AUTH_ENTRY, 224 /*isPrimary*/isPrimary); 225 mCallbacks.onProviderStatusChanged(status, mComponentName, source); 226 } 227 /** Common method that transfers metrics from the init phase to candidates */ startCandidateMetrics()228 protected void startCandidateMetrics() { 229 mProviderSessionMetric.collectCandidateMetricSetupViaInitialMetric( 230 ((RequestSession) mCallbacks).mRequestSessionMetric.getInitialPhaseMetric()); 231 } 232 233 /** Get the request to be sent to the provider. */ getProviderRequest()234 protected T getProviderRequest() { 235 return mProviderRequest; 236 } 237 238 /** Returns whether the provider response is set. */ isProviderResponseSet()239 protected Boolean isProviderResponseSet() { 240 return mProviderResponse != null || mProviderResponseSet; 241 } 242 invokeCallbackWithError(String errorType, @Nullable String errorMessage)243 protected void invokeCallbackWithError(String errorType, @Nullable String errorMessage) { 244 // TODO: Determine what the error message should be 245 mCallbacks.onFinalErrorReceived(mComponentName, errorType, errorMessage); 246 } 247 248 /** Update the response state stored with the provider session. */ 249 @Nullable getProviderResponse()250 protected R getProviderResponse() { 251 return mProviderResponse; 252 } 253 enforceRemoteEntryRestrictions( @ullable ComponentName expectedRemoteEntryProviderService)254 protected boolean enforceRemoteEntryRestrictions( 255 @Nullable ComponentName expectedRemoteEntryProviderService) { 256 // Check if the service is the one set by the OEM. If not silently reject this entry 257 if (!mComponentName.equals(expectedRemoteEntryProviderService)) { 258 Slog.w(TAG, "Remote entry being dropped as it is not from the service " 259 + "configured by the OEM."); 260 return false; 261 } 262 // Check if the service has the hybrid permission .If not, silently reject this entry. 263 // This check is in addition to the permission check happening in the provider's process. 264 try { 265 ApplicationInfo appInfo = mContext.getPackageManager().getApplicationInfo( 266 mComponentName.getPackageName(), 267 PackageManager.ApplicationInfoFlags.of(PackageManager.MATCH_SYSTEM_ONLY)); 268 if (appInfo != null 269 && mContext.checkPermission( 270 Manifest.permission.PROVIDE_REMOTE_CREDENTIALS, 271 /*pId=*/-1, appInfo.uid) == PackageManager.PERMISSION_GRANTED) { 272 return true; 273 } 274 } catch (SecurityException | PackageManager.NameNotFoundException e) { 275 Slog.e(TAG, "Error getting info for " + mComponentName.flattenToString(), e); 276 return false; 277 } 278 return false; 279 } 280 281 /** 282 * Should be overridden to prepare, and stores state for {@link ProviderData} to be 283 * shown on the UI. 284 */ 285 @Nullable prepareUiData()286 protected abstract ProviderData prepareUiData(); 287 288 /** Should be overridden to handle the selected entry from the UI. */ onUiEntrySelected(String entryType, String entryId, ProviderPendingIntentResponse providerPendingIntentResponse)289 protected abstract void onUiEntrySelected(String entryType, String entryId, 290 ProviderPendingIntentResponse providerPendingIntentResponse); 291 292 /** 293 * Should be overridden to invoke the provider at a defined location. Helpful for 294 * situations such as metric generation. 295 */ invokeSession()296 protected abstract void invokeSession(); 297 } 298