1 /* 2 * Copyright (C) 2017 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.autofill; 18 19 import static android.service.autofill.FillRequest.INVALID_REQUEST_ID; 20 import static android.service.autofill.Flags.remoteFillServiceUseWeakReference; 21 22 import static com.android.server.autofill.Helper.sVerbose; 23 24 import android.annotation.NonNull; 25 import android.annotation.Nullable; 26 import android.content.ComponentName; 27 import android.content.Context; 28 import android.content.Intent; 29 import android.content.IntentSender; 30 import android.os.Handler; 31 import android.os.IBinder; 32 import android.os.ICancellationSignal; 33 import android.os.RemoteException; 34 import android.service.autofill.AutofillService; 35 import android.service.autofill.ConvertCredentialRequest; 36 import android.service.autofill.ConvertCredentialResponse; 37 import android.service.autofill.FillEventHistory; 38 import android.service.autofill.FillRequest; 39 import android.service.autofill.FillResponse; 40 import android.service.autofill.IAutoFillService; 41 import android.service.autofill.IConvertCredentialCallback; 42 import android.service.autofill.IFillCallback; 43 import android.service.autofill.ISaveCallback; 44 import android.service.autofill.SaveRequest; 45 import android.text.format.DateUtils; 46 import android.util.Slog; 47 48 import com.android.internal.infra.AbstractRemoteService; 49 import com.android.internal.infra.ServiceConnector; 50 import com.android.internal.os.IResultReceiver; 51 52 import java.lang.ref.WeakReference; 53 import java.util.concurrent.CancellationException; 54 import java.util.concurrent.CompletableFuture; 55 import java.util.concurrent.TimeUnit; 56 import java.util.concurrent.TimeoutException; 57 import java.util.concurrent.atomic.AtomicReference; 58 59 final class RemoteFillService extends ServiceConnector.Impl<IAutoFillService> { 60 61 private static final String TAG = "RemoteFillService"; 62 63 private static final long TIMEOUT_IDLE_BIND_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS; 64 private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS; 65 66 private final FillServiceCallbacks mCallbacks; 67 private final Object mLock = new Object(); 68 private CompletableFuture<FillResponse> mPendingFillRequest; 69 private int mPendingFillRequestId = INVALID_REQUEST_ID; 70 private AtomicReference<IFillCallback> mFillCallback; 71 private AtomicReference<ISaveCallback> mSaveCallback; 72 private AtomicReference<IConvertCredentialCallback> mConvertCredentialCallback; 73 private final ComponentName mComponentName; 74 75 private final boolean mIsCredentialAutofillService; 76 isCredentialAutofillService()77 public boolean isCredentialAutofillService() { 78 return mIsCredentialAutofillService; 79 } 80 81 public interface FillServiceCallbacks 82 extends AbstractRemoteService.VultureCallback<RemoteFillService> { onFillRequestSuccess(int requestId, @Nullable FillResponse response, @NonNull String servicePackageName, int requestFlags)83 void onFillRequestSuccess(int requestId, @Nullable FillResponse response, 84 @NonNull String servicePackageName, int requestFlags); 85 onFillRequestFailure(int requestId, Throwable t)86 void onFillRequestFailure(int requestId, Throwable t); 87 onSaveRequestSuccess(@onNull String servicePackageName, @Nullable IntentSender intentSender)88 void onSaveRequestSuccess(@NonNull String servicePackageName, 89 @Nullable IntentSender intentSender); 90 91 // TODO(b/80093094): add timeout here too? onSaveRequestFailure(@ullable CharSequence message, @NonNull String servicePackageName)92 void onSaveRequestFailure(@Nullable CharSequence message, 93 @NonNull String servicePackageName); 94 onConvertCredentialRequestSuccess(@onNull ConvertCredentialResponse convertCredentialResponse)95 void onConvertCredentialRequestSuccess(@NonNull ConvertCredentialResponse 96 convertCredentialResponse); 97 } 98 RemoteFillService(Context context, ComponentName componentName, int userId, FillServiceCallbacks callbacks, boolean bindInstantServiceAllowed, @Nullable ComponentName credentialAutofillService)99 RemoteFillService(Context context, ComponentName componentName, int userId, 100 FillServiceCallbacks callbacks, boolean bindInstantServiceAllowed, 101 @Nullable ComponentName credentialAutofillService) { 102 super(context, new Intent(AutofillService.SERVICE_INTERFACE).setComponent(componentName), 103 Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS 104 | (bindInstantServiceAllowed ? Context.BIND_ALLOW_INSTANT : 0), 105 userId, IAutoFillService.Stub::asInterface); 106 mCallbacks = callbacks; 107 mComponentName = componentName; 108 mIsCredentialAutofillService = mComponentName.equals(credentialAutofillService); 109 } 110 111 @Override // from ServiceConnector.Impl onServiceConnectionStatusChanged(IAutoFillService service, boolean connected)112 protected void onServiceConnectionStatusChanged(IAutoFillService service, boolean connected) { 113 try { 114 service.onConnectedStateChanged(connected); 115 } catch (Exception e) { 116 Slog.w(TAG, "Exception calling onConnectedStateChanged(" + connected + "): " + e); 117 } 118 } 119 dispatchCancellationSignal(@ullable ICancellationSignal signal)120 private void dispatchCancellationSignal(@Nullable ICancellationSignal signal) { 121 if (signal == null) { 122 return; 123 } 124 try { 125 signal.cancel(); 126 } catch (RemoteException e) { 127 Slog.e(TAG, "Error requesting a cancellation", e); 128 } 129 } 130 131 @Override // from ServiceConnector.Impl getAutoDisconnectTimeoutMs()132 protected long getAutoDisconnectTimeoutMs() { 133 return TIMEOUT_IDLE_BIND_MILLIS; 134 } 135 136 @Override // from ServiceConnector.Impl addLast(Job<IAutoFillService, ?> iAutoFillServiceJob)137 public void addLast(Job<IAutoFillService, ?> iAutoFillServiceJob) { 138 // Only maintain single request at a time 139 cancelPendingJobs(); 140 super.addLast(iAutoFillServiceJob); 141 } 142 getComponentName()143 public ComponentName getComponentName() { 144 return mComponentName; 145 } 146 147 /** 148 * Cancel the currently pending request. 149 * 150 * <p>This can be used when the request is unnecessary or will be superceeded by a request that 151 * will soon be queued. 152 * 153 * @return the id of the canceled request, or {@link FillRequest#INVALID_REQUEST_ID} if no 154 * {@link FillRequest} was canceled. 155 */ cancelCurrentRequest()156 public int cancelCurrentRequest() { 157 synchronized (mLock) { 158 return mPendingFillRequest != null && mPendingFillRequest.cancel(false) 159 ? mPendingFillRequestId 160 : INVALID_REQUEST_ID; 161 } 162 } 163 164 static class IFillCallbackDelegate extends IFillCallback.Stub { 165 private WeakReference<IFillCallback> mCallbackWeakRef; 166 IFillCallbackDelegate(IFillCallback callback)167 IFillCallbackDelegate(IFillCallback callback) { 168 mCallbackWeakRef = new WeakReference(callback); 169 } 170 171 @Override onCancellable(ICancellationSignal cancellation)172 public void onCancellable(ICancellationSignal cancellation) throws RemoteException { 173 IFillCallback callback = mCallbackWeakRef.get(); 174 if (callback != null) { 175 callback.onCancellable(cancellation); 176 } 177 } 178 179 @Override onSuccess(FillResponse response)180 public void onSuccess(FillResponse response) throws RemoteException { 181 IFillCallback callback = mCallbackWeakRef.get(); 182 if (callback != null) { 183 callback.onSuccess(response); 184 } 185 } 186 187 @Override onFailure(int requestId, CharSequence message)188 public void onFailure(int requestId, CharSequence message) throws RemoteException { 189 IFillCallback callback = mCallbackWeakRef.get(); 190 if (callback != null) { 191 callback.onFailure(requestId, message); 192 } 193 } 194 } 195 196 static class ISaveCallbackDelegate extends ISaveCallback.Stub { 197 198 private WeakReference<ISaveCallback> mCallbackWeakRef; 199 ISaveCallbackDelegate(ISaveCallback callback)200 ISaveCallbackDelegate(ISaveCallback callback) { 201 mCallbackWeakRef = new WeakReference(callback); 202 } 203 204 @Override onSuccess(IntentSender intentSender)205 public void onSuccess(IntentSender intentSender) throws RemoteException { 206 ISaveCallback callback = mCallbackWeakRef.get(); 207 if (callback != null) { 208 callback.onSuccess(intentSender); 209 } 210 } 211 212 @Override onFailure(CharSequence message)213 public void onFailure(CharSequence message) throws RemoteException { 214 ISaveCallback callback = mCallbackWeakRef.get(); 215 if (callback != null) { 216 callback.onFailure(message); 217 } 218 } 219 } 220 221 static class IConvertCredentialCallbackDelegate extends IConvertCredentialCallback.Stub { 222 223 private WeakReference<IConvertCredentialCallback> mCallbackWeakRef; 224 IConvertCredentialCallbackDelegate(IConvertCredentialCallback callback)225 IConvertCredentialCallbackDelegate(IConvertCredentialCallback callback) { 226 mCallbackWeakRef = new WeakReference(callback); 227 } 228 229 @Override onSuccess(ConvertCredentialResponse convertCredentialResponse)230 public void onSuccess(ConvertCredentialResponse convertCredentialResponse) 231 throws RemoteException { 232 IConvertCredentialCallback callback = mCallbackWeakRef.get(); 233 if (callback != null) { 234 callback.onSuccess(convertCredentialResponse); 235 } 236 } 237 238 @Override onFailure(CharSequence message)239 public void onFailure(CharSequence message) throws RemoteException { 240 IConvertCredentialCallback callback = mCallbackWeakRef.get(); 241 if (callback != null) { 242 callback.onFailure(message); 243 } 244 } 245 } 246 247 /** 248 * Wraps an {@link IFillCallback} object using weak reference. 249 * 250 * This prevents lingering allocation issue by breaking the chain of strong references from 251 * Binder to {@link callback}. Since {@link callback} is not held by Binder anymore, it needs 252 * to be held by {@link mFillCallback} so it's not deallocated prematurely. 253 */ maybeWrapWithWeakReference(IFillCallback callback)254 private IFillCallback maybeWrapWithWeakReference(IFillCallback callback) { 255 if (remoteFillServiceUseWeakReference()) { 256 mFillCallback = new AtomicReference<>(callback); 257 return new IFillCallbackDelegate(callback); 258 } 259 return callback; 260 } 261 262 /** 263 * Wraps an {@link ISaveCallback} object using weak reference. 264 */ maybeWrapWithWeakReference(ISaveCallback callback)265 private ISaveCallback maybeWrapWithWeakReference(ISaveCallback callback) { 266 if (remoteFillServiceUseWeakReference()) { 267 mSaveCallback = new AtomicReference<>(callback); 268 return new ISaveCallbackDelegate(callback); 269 } 270 return callback; 271 } 272 273 /** 274 * Wraps an {@link IConvertCredentialCallback} object using weak reference 275 */ maybeWrapWithWeakReference( IConvertCredentialCallback callback)276 private IConvertCredentialCallback maybeWrapWithWeakReference( 277 IConvertCredentialCallback callback) { 278 if (remoteFillServiceUseWeakReference()) { 279 mConvertCredentialCallback = new AtomicReference<>(callback); 280 return new IConvertCredentialCallbackDelegate(callback); 281 } 282 return callback; 283 } 284 onFillCredentialRequest(@onNull FillRequest request, IBinder autofillCallback)285 public void onFillCredentialRequest(@NonNull FillRequest request, IBinder autofillCallback) { 286 if (sVerbose) { 287 Slog.v(TAG, "onFillRequest:" + request); 288 } 289 AtomicReference<ICancellationSignal> cancellationSink = new AtomicReference<>(); 290 AtomicReference<CompletableFuture<FillResponse>> futureRef = new AtomicReference<>(); 291 292 CompletableFuture<FillResponse> connectThenFillRequest = postAsync(remoteService -> { 293 if (sVerbose) { 294 Slog.v(TAG, "calling onFillRequest() for id=" + request.getId()); 295 } 296 297 CompletableFuture<FillResponse> fillRequest = new CompletableFuture<>(); 298 remoteService.onFillCredentialRequest( 299 request, maybeWrapWithWeakReference(new IFillCallback.Stub() { 300 @Override 301 public void onCancellable(ICancellationSignal cancellation) { 302 CompletableFuture<FillResponse> future = futureRef.get(); 303 if (future != null && future.isCancelled()) { 304 dispatchCancellationSignal(cancellation); 305 } else { 306 cancellationSink.set(cancellation); 307 } 308 } 309 310 @Override 311 public void onSuccess(FillResponse response) { 312 fillRequest.complete(response); 313 } 314 315 @Override 316 public void onFailure(int requestId, CharSequence message) { 317 String errorMessage = message == null ? "" : String.valueOf(message); 318 fillRequest.completeExceptionally( 319 new RuntimeException(errorMessage)); 320 } 321 }), autofillCallback); 322 return fillRequest; 323 }).orTimeout(TIMEOUT_REMOTE_REQUEST_MILLIS, TimeUnit.MILLISECONDS); 324 futureRef.set(connectThenFillRequest); 325 326 synchronized (mLock) { 327 mPendingFillRequest = connectThenFillRequest; 328 mPendingFillRequestId = request.getId(); 329 } 330 331 connectThenFillRequest.whenComplete((res, err) -> Handler.getMain().post(() -> { 332 synchronized (mLock) { 333 mPendingFillRequest = null; 334 mPendingFillRequestId = INVALID_REQUEST_ID; 335 } 336 if (mCallbacks == null) { 337 Slog.w(TAG, "Error calling RemoteFillService - service already unbound"); 338 return; 339 } 340 if (err == null) { 341 mCallbacks.onFillRequestSuccess(request.getId(), res, 342 mComponentName.getPackageName(), request.getFlags()); 343 } else { 344 Slog.e(TAG, "Error calling on fill request", err); 345 if (err instanceof TimeoutException) { 346 dispatchCancellationSignal(cancellationSink.get()); 347 mCallbacks.onFillRequestFailure(request.getId(), err); 348 } else if (err instanceof CancellationException) { 349 // Cancellation is a part of the user flow - don't mark as failure 350 dispatchCancellationSignal(cancellationSink.get()); 351 } else { 352 mCallbacks.onFillRequestFailure(request.getId(), err); 353 } 354 } 355 })); 356 } 357 onFillRequest(@onNull FillRequest request)358 public void onFillRequest(@NonNull FillRequest request) { 359 if (sVerbose) { 360 Slog.v(TAG, "onFillRequest:" + request); 361 } 362 AtomicReference<ICancellationSignal> cancellationSink = new AtomicReference<>(); 363 AtomicReference<CompletableFuture<FillResponse>> futureRef = new AtomicReference<>(); 364 365 CompletableFuture<FillResponse> connectThenFillRequest = postAsync(remoteService -> { 366 if (sVerbose) { 367 Slog.v(TAG, "calling onFillRequest() for id=" + request.getId()); 368 } 369 370 CompletableFuture<FillResponse> fillRequest = new CompletableFuture<>(); 371 remoteService.onFillRequest( 372 request, maybeWrapWithWeakReference(new IFillCallback.Stub() { 373 @Override 374 public void onCancellable(ICancellationSignal cancellation) { 375 CompletableFuture<FillResponse> future = futureRef.get(); 376 if (future != null && future.isCancelled()) { 377 dispatchCancellationSignal(cancellation); 378 } else { 379 cancellationSink.set(cancellation); 380 } 381 } 382 383 @Override 384 public void onSuccess(FillResponse response) { 385 fillRequest.complete(response); 386 } 387 388 @Override 389 public void onFailure(int requestId, CharSequence message) { 390 String errorMessage = message == null ? "" : String.valueOf(message); 391 fillRequest.completeExceptionally( 392 new RuntimeException(errorMessage)); 393 } 394 })); 395 return fillRequest; 396 }).orTimeout(TIMEOUT_REMOTE_REQUEST_MILLIS, TimeUnit.MILLISECONDS); 397 futureRef.set(connectThenFillRequest); 398 399 synchronized (mLock) { 400 mPendingFillRequest = connectThenFillRequest; 401 mPendingFillRequestId = request.getId(); 402 } 403 404 connectThenFillRequest.whenComplete((res, err) -> Handler.getMain().post(() -> { 405 synchronized (mLock) { 406 mPendingFillRequest = null; 407 mPendingFillRequestId = INVALID_REQUEST_ID; 408 } 409 if (err == null) { 410 mCallbacks.onFillRequestSuccess(request.getId(), res, 411 mComponentName.getPackageName(), request.getFlags()); 412 } else { 413 Slog.e(TAG, "Error calling on fill request", err); 414 if (err instanceof TimeoutException) { 415 dispatchCancellationSignal(cancellationSink.get()); 416 mCallbacks.onFillRequestFailure(request.getId(), err); 417 } else if (err instanceof CancellationException) { 418 // Cancellation is a part of the user flow - don't mark as failure 419 dispatchCancellationSignal(cancellationSink.get()); 420 } else { 421 mCallbacks.onFillRequestFailure(request.getId(), err); 422 } 423 } 424 })); 425 } 426 onConvertCredentialRequest( @onNull ConvertCredentialRequest convertCredentialRequest)427 public void onConvertCredentialRequest( 428 @NonNull ConvertCredentialRequest convertCredentialRequest) { 429 if (sVerbose) Slog.v(TAG, "calling onConvertCredentialRequest()"); 430 CompletableFuture<ConvertCredentialResponse> 431 connectThenConvertCredentialRequest = postAsync( 432 remoteService -> { 433 if (sVerbose) { 434 Slog.v(TAG, "calling onConvertCredentialRequest()"); 435 } 436 CompletableFuture<ConvertCredentialResponse> 437 convertCredentialCompletableFuture = new CompletableFuture<>(); 438 remoteService.onConvertCredentialRequest(convertCredentialRequest, 439 maybeWrapWithWeakReference( 440 new IConvertCredentialCallback.Stub() { 441 @Override 442 public void onSuccess(ConvertCredentialResponse 443 convertCredentialResponse) { 444 convertCredentialCompletableFuture 445 .complete(convertCredentialResponse); 446 } 447 448 @Override 449 public void onFailure(CharSequence message) { 450 String errorMessage = 451 message == null ? "" : 452 String.valueOf(message); 453 convertCredentialCompletableFuture 454 .completeExceptionally( 455 new RuntimeException(errorMessage)); 456 } 457 }) 458 ); 459 return convertCredentialCompletableFuture; 460 }).orTimeout(TIMEOUT_REMOTE_REQUEST_MILLIS, TimeUnit.MILLISECONDS); 461 462 connectThenConvertCredentialRequest.whenComplete( 463 (res, err) -> Handler.getMain().post(() -> { 464 if (err == null) { 465 mCallbacks.onConvertCredentialRequestSuccess(res); 466 } else { 467 // TODO: Add a callback function to log this failure 468 Slog.e(TAG, "Error calling on convert credential request", err); 469 } 470 })); 471 } 472 onSaveRequest(@onNull SaveRequest request)473 public void onSaveRequest(@NonNull SaveRequest request) { 474 postAsync(service -> { 475 if (sVerbose) Slog.v(TAG, "calling onSaveRequest()"); 476 477 CompletableFuture<IntentSender> save = new CompletableFuture<>(); 478 service.onSaveRequest(request, maybeWrapWithWeakReference(new ISaveCallback.Stub() { 479 @Override 480 public void onSuccess(IntentSender intentSender) { 481 save.complete(intentSender); 482 } 483 484 @Override 485 public void onFailure(CharSequence message) { 486 save.completeExceptionally(new RuntimeException(String.valueOf(message))); 487 } 488 })); 489 return save; 490 }).orTimeout(TIMEOUT_REMOTE_REQUEST_MILLIS, TimeUnit.MILLISECONDS) 491 .whenComplete((res, err) -> Handler.getMain().post(() -> { 492 if (err == null) { 493 mCallbacks.onSaveRequestSuccess(mComponentName.getPackageName(), res); 494 } else { 495 mCallbacks.onSaveRequestFailure( 496 mComponentName.getPackageName(), err.getMessage()); 497 } 498 })); 499 } 500 onSessionDestroyed(@ullable FillEventHistory history)501 public void onSessionDestroyed(@Nullable FillEventHistory history) { 502 boolean success = run(service -> { 503 service.onSessionDestroyed(history); 504 }); 505 506 if (sVerbose) Slog.v(TAG, "called onSessionDestroyed(): " + success); 507 } 508 onSavedPasswordCountRequest(IResultReceiver receiver)509 void onSavedPasswordCountRequest(IResultReceiver receiver) { 510 run(service -> service.onSavedPasswordCountRequest(receiver)); 511 } 512 destroy()513 public void destroy() { 514 unbind(); 515 } 516 } 517