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.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.content.ComponentName; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.credentials.ClearCredentialStateException; 25 import android.credentials.CreateCredentialException; 26 import android.credentials.CredentialManager; 27 import android.credentials.GetCredentialException; 28 import android.os.Binder; 29 import android.os.Handler; 30 import android.os.ICancellationSignal; 31 import android.os.RemoteException; 32 import android.service.credentials.BeginCreateCredentialRequest; 33 import android.service.credentials.BeginCreateCredentialResponse; 34 import android.service.credentials.BeginGetCredentialRequest; 35 import android.service.credentials.BeginGetCredentialResponse; 36 import android.service.credentials.ClearCredentialStateRequest; 37 import android.service.credentials.CredentialProviderErrors; 38 import android.service.credentials.CredentialProviderService; 39 import android.service.credentials.IBeginCreateCredentialCallback; 40 import android.service.credentials.IBeginGetCredentialCallback; 41 import android.service.credentials.IClearCredentialStateCallback; 42 import android.service.credentials.ICredentialProviderService; 43 import android.text.format.DateUtils; 44 import android.util.Slog; 45 46 import com.android.internal.infra.ServiceConnector; 47 48 import java.util.concurrent.CancellationException; 49 import java.util.concurrent.CompletableFuture; 50 import java.util.concurrent.TimeUnit; 51 import java.util.concurrent.TimeoutException; 52 import java.util.concurrent.atomic.AtomicBoolean; 53 import java.util.concurrent.atomic.AtomicReference; 54 55 /** 56 * Handles connections with the remote credential provider 57 * 58 * @hide 59 */ 60 public class RemoteCredentialService extends ServiceConnector.Impl<ICredentialProviderService> { 61 62 private static final String TAG = CredentialManager.TAG; 63 /** Timeout for a single request. */ 64 private static final long TIMEOUT_REQUEST_MILLIS = 3 * DateUtils.SECOND_IN_MILLIS; 65 /** Timeout to unbind after the task queue is empty. */ 66 private static final long TIMEOUT_IDLE_SERVICE_CONNECTION_MILLIS = 67 5 * DateUtils.SECOND_IN_MILLIS; 68 69 private final ComponentName mComponentName; 70 71 private AtomicBoolean mOngoingRequest = new AtomicBoolean(false); 72 73 @Nullable private ProviderCallbacks mCallback; 74 75 /** 76 * Callbacks to be invoked when the provider remote service responds with a 77 * success or failure. 78 * 79 * @param <T> the type of response expected from the provider 80 */ 81 public interface ProviderCallbacks<T> { 82 /** Called when a successful response is received from the remote provider. */ onProviderResponseSuccess(@ullable T response)83 void onProviderResponseSuccess(@Nullable T response); 84 85 /** Called when a failure response is received from the remote provider. */ onProviderResponseFailure(int internalErrorCode, @Nullable Exception e)86 void onProviderResponseFailure(int internalErrorCode, @Nullable Exception e); 87 88 /** Called when the remote provider service dies. */ onProviderServiceDied(RemoteCredentialService service)89 void onProviderServiceDied(RemoteCredentialService service); 90 91 /** Called to set the cancellation transport from the remote provider service. */ onProviderCancellable(ICancellationSignal cancellation)92 void onProviderCancellable(ICancellationSignal cancellation); 93 } 94 RemoteCredentialService(@onNull Context context, @NonNull ComponentName componentName, int userId)95 public RemoteCredentialService(@NonNull Context context, 96 @NonNull ComponentName componentName, int userId) { 97 super(context, new Intent(CredentialProviderService.SERVICE_INTERFACE) 98 .setComponent(componentName), /*bindingFlags=*/0, 99 userId, ICredentialProviderService.Stub::asInterface); 100 mComponentName = componentName; 101 } 102 setCallback(ProviderCallbacks callback)103 public void setCallback(ProviderCallbacks callback) { 104 mCallback = callback; 105 } 106 107 /** Unbinds automatically after this amount of time. */ 108 @Override getAutoDisconnectTimeoutMs()109 protected long getAutoDisconnectTimeoutMs() { 110 return TIMEOUT_IDLE_SERVICE_CONNECTION_MILLIS; 111 } 112 113 @Override onBindingDied(ComponentName name)114 public void onBindingDied(ComponentName name) { 115 super.onBindingDied(name); 116 117 Slog.w(TAG, "binding died for: " + name); 118 } 119 120 @Override binderDied()121 public void binderDied() { 122 super.binderDied(); 123 Slog.w(TAG, "binderDied"); 124 125 if (mCallback != null) { 126 mOngoingRequest.set(false); 127 mCallback.onProviderServiceDied(this); 128 } 129 130 } 131 132 /** Return the componentName of the service to be connected. */ 133 @NonNull getComponentName()134 public ComponentName getComponentName() { 135 return mComponentName; 136 } 137 138 /** Destroys this remote service by unbinding the connection. */ destroy()139 public void destroy() { 140 unbind(); 141 } 142 143 /** 144 * Main entry point to be called for executing a getCredential call on the remote 145 * provider service. 146 * 147 * @param request the request to be sent to the provider 148 */ onBeginGetCredential(@onNull BeginGetCredentialRequest request)149 public void onBeginGetCredential(@NonNull BeginGetCredentialRequest request) { 150 if (mCallback == null) { 151 Slog.w(TAG, "Callback is not set"); 152 return; 153 } 154 mOngoingRequest.set(true); 155 156 AtomicReference<ICancellationSignal> cancellationSink = new AtomicReference<>(); 157 AtomicReference<CompletableFuture<BeginGetCredentialResponse>> futureRef = 158 new AtomicReference<>(); 159 160 161 CompletableFuture<BeginGetCredentialResponse> connectThenExecute = postAsync(service -> { 162 CompletableFuture<BeginGetCredentialResponse> getCredentials = 163 new CompletableFuture<>(); 164 final long originalCallingUidToken = Binder.clearCallingIdentity(); 165 try { 166 service.onBeginGetCredential(request, 167 new IBeginGetCredentialCallback.Stub() { 168 @Override 169 public void onSuccess(BeginGetCredentialResponse response) { 170 getCredentials.complete(response); 171 } 172 173 @Override 174 public void onFailure(String errorType, CharSequence message) { 175 String errorMsg = message == null ? "" : String.valueOf( 176 message); 177 getCredentials.completeExceptionally( 178 new GetCredentialException(errorType, errorMsg)); 179 } 180 181 @Override 182 public void onCancellable(ICancellationSignal cancellation) { 183 CompletableFuture<BeginGetCredentialResponse> future = 184 futureRef.get(); 185 if (future != null && future.isCancelled()) { 186 dispatchCancellationSignal(cancellation); 187 } else { 188 cancellationSink.set(cancellation); 189 if (mCallback != null) { 190 mCallback.onProviderCancellable(cancellation); 191 } 192 } 193 } 194 }); 195 return getCredentials; 196 } finally { 197 Binder.restoreCallingIdentity(originalCallingUidToken); 198 } 199 }).orTimeout(TIMEOUT_REQUEST_MILLIS, TimeUnit.MILLISECONDS); 200 futureRef.set(connectThenExecute); 201 202 connectThenExecute.whenComplete((result, error) -> Handler.getMain().post(() -> 203 handleExecutionResponse(result, error, cancellationSink))); 204 } 205 206 /** 207 * Main entry point to be called for executing a beginCreateCredential call on the remote 208 * provider service. 209 * 210 * @param request the request to be sent to the provider 211 */ onBeginCreateCredential(@onNull BeginCreateCredentialRequest request)212 public void onBeginCreateCredential(@NonNull BeginCreateCredentialRequest request) { 213 if (mCallback == null) { 214 Slog.w(TAG, "Callback is not set"); 215 return; 216 } 217 mOngoingRequest.set(true); 218 219 AtomicReference<ICancellationSignal> cancellationSink = new AtomicReference<>(); 220 AtomicReference<CompletableFuture<BeginCreateCredentialResponse>> futureRef = 221 new AtomicReference<>(); 222 223 CompletableFuture<BeginCreateCredentialResponse> connectThenExecute = 224 postAsync(service -> { 225 CompletableFuture<BeginCreateCredentialResponse> createCredentialFuture = 226 new CompletableFuture<>(); 227 final long originalCallingUidToken = Binder.clearCallingIdentity(); 228 try { 229 service.onBeginCreateCredential( 230 request, new IBeginCreateCredentialCallback.Stub() { 231 @Override 232 public void onSuccess(BeginCreateCredentialResponse response) { 233 createCredentialFuture.complete(response); 234 } 235 236 @Override 237 public void onFailure(String errorType, CharSequence message) { 238 String errorMsg = message == null ? "" : String.valueOf( 239 message); 240 createCredentialFuture.completeExceptionally( 241 new CreateCredentialException(errorType, errorMsg)); 242 } 243 244 @Override 245 public void onCancellable(ICancellationSignal cancellation) { 246 CompletableFuture<BeginCreateCredentialResponse> future = 247 futureRef.get(); 248 if (future != null && future.isCancelled()) { 249 dispatchCancellationSignal(cancellation); 250 } else { 251 cancellationSink.set(cancellation); 252 if (mCallback != null) { 253 mCallback.onProviderCancellable(cancellation); 254 } 255 } 256 } 257 }); 258 return createCredentialFuture; 259 } finally { 260 Binder.restoreCallingIdentity(originalCallingUidToken); 261 } 262 }).orTimeout(TIMEOUT_REQUEST_MILLIS, TimeUnit.MILLISECONDS); 263 futureRef.set(connectThenExecute); 264 265 connectThenExecute.whenComplete((result, error) -> Handler.getMain().post(() -> 266 handleExecutionResponse(result, error, cancellationSink))); 267 } 268 269 /** 270 * Main entry point to be called for executing a clearCredentialState call on the remote 271 * provider service. 272 * 273 * @param request the request to be sent to the provider 274 */ onClearCredentialState(@onNull ClearCredentialStateRequest request)275 public void onClearCredentialState(@NonNull ClearCredentialStateRequest request) { 276 if (mCallback == null) { 277 Slog.w(TAG, "Callback is not set"); 278 return; 279 } 280 mOngoingRequest.set(true); 281 282 AtomicReference<ICancellationSignal> cancellationSink = new AtomicReference<>(); 283 AtomicReference<CompletableFuture<Void>> futureRef = new AtomicReference<>(); 284 285 CompletableFuture<Void> connectThenExecute = 286 postAsync(service -> { 287 CompletableFuture<Void> clearCredentialFuture = 288 new CompletableFuture<>(); 289 final long originalCallingUidToken = Binder.clearCallingIdentity(); 290 try { 291 service.onClearCredentialState( 292 request, new IClearCredentialStateCallback.Stub() { 293 @Override 294 public void onSuccess() { 295 clearCredentialFuture.complete(null); 296 } 297 298 @Override 299 public void onFailure(String errorType, CharSequence message) { 300 String errorMsg = message == null ? "" : 301 String.valueOf(message); 302 clearCredentialFuture.completeExceptionally( 303 new ClearCredentialStateException(errorType, 304 errorMsg)); 305 } 306 307 @Override 308 public void onCancellable(ICancellationSignal cancellation) { 309 CompletableFuture<Void> future = futureRef.get(); 310 if (future != null && future.isCancelled()) { 311 dispatchCancellationSignal(cancellation); 312 } else { 313 cancellationSink.set(cancellation); 314 if (mCallback != null) { 315 mCallback.onProviderCancellable(cancellation); 316 } 317 } 318 } 319 }); 320 return clearCredentialFuture; 321 } finally { 322 Binder.restoreCallingIdentity(originalCallingUidToken); 323 } 324 }).orTimeout(TIMEOUT_REQUEST_MILLIS, TimeUnit.MILLISECONDS); 325 futureRef.set(connectThenExecute); 326 327 connectThenExecute.whenComplete((result, error) -> Handler.getMain().post(() -> 328 handleExecutionResponse(result, error, cancellationSink))); 329 } 330 handleExecutionResponse(T result, Throwable error, AtomicReference<ICancellationSignal> cancellationSink)331 private <T> void handleExecutionResponse(T result, 332 Throwable error, 333 AtomicReference<ICancellationSignal> cancellationSink) { 334 if (error == null) { 335 if (mCallback != null) { 336 mCallback.onProviderResponseSuccess(result); 337 } 338 } else { 339 if (error instanceof TimeoutException) { 340 Slog.i(TAG, "Remote provider response timed tuo for: " + mComponentName); 341 if (!mOngoingRequest.get()) { 342 return; 343 } 344 dispatchCancellationSignal(cancellationSink.get()); 345 if (mCallback != null) { 346 mOngoingRequest.set(false); 347 mCallback.onProviderResponseFailure( 348 CredentialProviderErrors.ERROR_TIMEOUT, null); 349 } 350 } else if (error instanceof CancellationException) { 351 Slog.i(TAG, "Cancellation exception for remote provider: " + mComponentName); 352 if (!mOngoingRequest.get()) { 353 return; 354 } 355 dispatchCancellationSignal(cancellationSink.get()); 356 if (mCallback != null) { 357 mOngoingRequest.set(false); 358 mCallback.onProviderResponseFailure( 359 CredentialProviderErrors.ERROR_TASK_CANCELED, 360 null); 361 } 362 } else if (error instanceof GetCredentialException) { 363 if (mCallback != null) { 364 mCallback.onProviderResponseFailure( 365 CredentialProviderErrors.ERROR_PROVIDER_FAILURE, 366 (GetCredentialException) error); 367 } 368 } else if (error instanceof CreateCredentialException) { 369 if (mCallback != null) { 370 mCallback.onProviderResponseFailure( 371 CredentialProviderErrors.ERROR_PROVIDER_FAILURE, 372 (CreateCredentialException) error); 373 } 374 } else { 375 if (mCallback != null) { 376 mCallback.onProviderResponseFailure( 377 CredentialProviderErrors.ERROR_UNKNOWN, 378 (Exception) error); 379 } 380 } 381 } 382 } 383 dispatchCancellationSignal(@ullable ICancellationSignal signal)384 private void dispatchCancellationSignal(@Nullable ICancellationSignal signal) { 385 if (signal == null) { 386 Slog.e(TAG, "Error dispatching a cancellation - Signal is null"); 387 return; 388 } 389 try { 390 signal.cancel(); 391 } catch (RemoteException e) { 392 Slog.e(TAG, "Error dispatching a cancellation", e); 393 } 394 } 395 } 396