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