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