AutofillManagerServiceImpl.java revision 24d5893b25ce62b7bc9ed9f35fa72b9d47f23cdd
1/* 2 * Copyright (C) 2016 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 17package com.android.server.autofill; 18 19import static android.service.autofill.AutofillService.EXTRA_ACTIVITY_TOKEN; 20import static android.service.voice.VoiceInteractionSession.KEY_RECEIVER_EXTRAS; 21import static android.service.voice.VoiceInteractionSession.KEY_STRUCTURE; 22import static android.view.autofill.AutofillManager.FLAG_VIEW_ENTERED; 23import static android.view.autofill.AutofillManager.FLAG_VIEW_EXITED; 24import static android.view.autofill.AutofillManager.FLAG_START_SESSION; 25import static android.view.autofill.AutofillManager.FLAG_VALUE_CHANGED; 26import static android.view.autofill.AutofillManager.FLAG_MANUAL_REQUEST; 27 28import static com.android.server.autofill.Helper.DEBUG; 29import static com.android.server.autofill.Helper.VERBOSE; 30import static com.android.server.autofill.Helper.findValue; 31 32import android.annotation.NonNull; 33import android.annotation.Nullable; 34import android.app.Activity; 35import android.app.ActivityManager; 36import android.app.AppGlobals; 37import android.app.assist.AssistStructure; 38import android.app.assist.AssistStructure.ViewNode; 39import android.app.assist.AssistStructure.WindowNode; 40import android.content.ComponentName; 41import android.content.Context; 42import android.content.Intent; 43import android.content.IntentSender; 44import android.content.pm.ApplicationInfo; 45import android.content.pm.PackageManager; 46import android.content.pm.ServiceInfo; 47import android.graphics.Rect; 48import android.metrics.LogMaker; 49import android.os.Binder; 50import android.os.Bundle; 51import android.os.IBinder; 52import android.os.Looper; 53import android.os.Parcelable; 54import android.os.RemoteCallbackList; 55import android.os.RemoteException; 56import android.os.UserManager; 57import android.provider.Settings; 58import android.service.autofill.AutofillService; 59import android.service.autofill.AutofillServiceInfo; 60import android.service.autofill.Dataset; 61import android.service.autofill.FillResponse; 62import android.service.autofill.IAutoFillService; 63import android.service.autofill.SaveInfo; 64import android.text.TextUtils; 65import android.util.ArrayMap; 66import android.util.LocalLog; 67import android.util.Log; 68import android.util.PrintWriterPrinter; 69import android.util.Slog; 70import android.view.autofill.AutofillId; 71import android.view.autofill.AutofillManager; 72import android.view.autofill.AutofillValue; 73import android.view.autofill.IAutoFillManagerClient; 74import android.view.autofill.AutofillManager.AutofillCallback; 75 76import com.android.internal.annotations.GuardedBy; 77import com.android.internal.logging.MetricsLogger; 78import com.android.internal.logging.nano.MetricsProto; 79import com.android.internal.os.HandlerCaller; 80import com.android.internal.os.IResultReceiver; 81import com.android.server.autofill.ui.AutoFillUI; 82 83import java.io.PrintWriter; 84import java.util.ArrayList; 85import java.util.Map; 86import java.util.Map.Entry; 87 88/** 89 * Bridge between the {@code system_server}'s {@link AutofillManagerService} and the 90 * app's {@link IAutoFillService} implementation. 91 * 92 */ 93final class AutofillManagerServiceImpl { 94 95 private static final String TAG = "AutofillManagerServiceImpl"; 96 97 private static final int MSG_SERVICE_SAVE = 1; 98 99 private final int mUserId; 100 private final Context mContext; 101 private final Object mLock; 102 private final AutoFillUI mUi; 103 private final MetricsLogger mMetricsLogger = new MetricsLogger(); 104 105 private RemoteCallbackList<IAutoFillManagerClient> mClients; 106 private AutofillServiceInfo mInfo; 107 108 private final LocalLog mRequestsHistory; 109 /** 110 * Whether service was disabled for user due to {@link UserManager} restrictions. 111 */ 112 private boolean mDisabled; 113 114 private final HandlerCaller.Callback mHandlerCallback = (msg) -> { 115 switch (msg.what) { 116 case MSG_SERVICE_SAVE: 117 handleSessionSave((IBinder) msg.obj); 118 break; 119 default: 120 Slog.w(TAG, "invalid msg on handler: " + msg); 121 } 122 }; 123 124 private final HandlerCaller mHandlerCaller = new HandlerCaller(null, Looper.getMainLooper(), 125 mHandlerCallback, true); 126 127 /** 128 * Cache of pending {@link Session}s, keyed by {@code activityToken}. 129 * 130 * <p>They're kept until the {@link AutofillService} finished handling a request, an error 131 * occurs, or the session times out. 132 */ 133 // TODO(b/33197203): need to make sure service is bound while callback is pending and/or 134 // use WeakReference 135 @GuardedBy("mLock") 136 private final ArrayMap<IBinder, Session> mSessions = new ArrayMap<>(); 137 138 /** 139 * Receiver of assist data from the app's {@link Activity}. 140 */ 141 private final IResultReceiver mAssistReceiver = new IResultReceiver.Stub() { 142 @Override 143 public void send(int resultCode, Bundle resultData) throws RemoteException { 144 if (VERBOSE) { 145 Slog.v(TAG, "resultCode on mAssistReceiver: " + resultCode); 146 } 147 148 final AssistStructure structure = resultData.getParcelable(KEY_STRUCTURE); 149 if (structure == null) { 150 Slog.wtf(TAG, "no assist structure for id " + resultCode); 151 return; 152 } 153 154 final Bundle receiverExtras = resultData.getBundle(KEY_RECEIVER_EXTRAS); 155 if (receiverExtras == null) { 156 Slog.wtf(TAG, "No " + KEY_RECEIVER_EXTRAS + " on receiver"); 157 return; 158 } 159 160 final IBinder activityToken = receiverExtras.getBinder(EXTRA_ACTIVITY_TOKEN); 161 final Session session; 162 synchronized (mLock) { 163 session = mSessions.get(activityToken); 164 if (session == null) { 165 Slog.w(TAG, "no server session for activityToken " + activityToken); 166 return; 167 } 168 // TODO(b/33197203): since service is fetching the data (to use for save later), 169 // we should optimize what's sent (for example, remove layout containers, 170 // color / font info, etc...) 171 session.mStructure = structure; 172 } 173 174 175 // TODO(b/33197203, b/33269702): Must fetch the data so it's available later on 176 // handleSave(), even if if the activity is gone by then, but structure.ensureData() 177 // gives a ONE_WAY warning because system_service could block on app calls. 178 // We need to change AssistStructure so it provides a "one-way" writeToParcel() 179 // method that sends all the data 180 structure.ensureData(); 181 182 // Sanitize structure before it's sent to service. 183 structure.sanitizeForParceling(true); 184 185 // TODO(b/33197203): Need to pipe the bundle 186 session.mRemoteFillService.onFillRequest(structure, null, session.mFlags); 187 } 188 }; 189 190 AutofillManagerServiceImpl(Context context, Object lock, LocalLog requestsHistory, 191 int userId, AutoFillUI ui, boolean disabled) { 192 mContext = context; 193 mLock = lock; 194 mRequestsHistory = requestsHistory; 195 mUserId = userId; 196 mUi = ui; 197 updateLocked(disabled); 198 } 199 200 CharSequence getServiceName() { 201 if (mInfo == null) { 202 return null; 203 } 204 final ComponentName serviceComponent = mInfo.getServiceInfo().getComponentName(); 205 final String packageName = serviceComponent.getPackageName(); 206 207 try { 208 final PackageManager pm = mContext.getPackageManager(); 209 final ApplicationInfo info = pm.getApplicationInfo(packageName, 0); 210 return pm.getApplicationLabel(info); 211 } catch (Exception e) { 212 Slog.e(TAG, "Could not get label for " + packageName + ": " + e); 213 return packageName; 214 } 215 } 216 217 void updateLocked(boolean disabled) { 218 final boolean wasEnabled = isEnabled(); 219 mDisabled = disabled; 220 ComponentName serviceComponent = null; 221 ServiceInfo serviceInfo = null; 222 final String componentName = Settings.Secure.getStringForUser( 223 mContext.getContentResolver(), Settings.Secure.AUTOFILL_SERVICE, mUserId); 224 if (!TextUtils.isEmpty(componentName)) { 225 try { 226 serviceComponent = ComponentName.unflattenFromString(componentName); 227 serviceInfo = AppGlobals.getPackageManager().getServiceInfo(serviceComponent, 228 0, mUserId); 229 } catch (RuntimeException | RemoteException e) { 230 Slog.e(TAG, "Bad autofill service name " + componentName + ": " + e); 231 return; 232 } 233 } 234 try { 235 if (serviceInfo != null) { 236 mInfo = new AutofillServiceInfo(mContext.getPackageManager(), 237 serviceComponent, mUserId); 238 } else { 239 mInfo = null; 240 } 241 if (wasEnabled != isEnabled()) { 242 if (!isEnabled()) { 243 final int sessionCount = mSessions.size(); 244 for (int i = sessionCount - 1; i >= 0; i--) { 245 final Session session = mSessions.valueAt(i); 246 session.removeSelfLocked(); 247 } 248 } 249 sendStateToClients(); 250 } 251 } catch (PackageManager.NameNotFoundException e) { 252 Slog.e(TAG, "Bad autofill service name " + componentName + ": " + e); 253 } 254 } 255 256 /** 257 * Used by {@link AutofillManagerServiceShellCommand} to request save for the current top app. 258 */ 259 void requestSaveForUserLocked(IBinder activityToken) { 260 if (!isEnabled()) { 261 return; 262 } 263 final Session session = mSessions.get(activityToken); 264 if (session == null) { 265 Slog.w(TAG, "requestSaveForUserLocked(): no session for " + activityToken); 266 return; 267 } 268 269 session.callSaveLocked(); 270 } 271 272 boolean addClientLocked(IAutoFillManagerClient client) { 273 if (mClients == null) { 274 mClients = new RemoteCallbackList<>(); 275 } 276 mClients.register(client); 277 return isEnabled(); 278 } 279 280 void setAuthenticationResultLocked(Bundle data, IBinder activityToken) { 281 if (!isEnabled()) { 282 return; 283 } 284 final Session session = mSessions.get(activityToken); 285 if (session != null) { 286 session.setAuthenticationResultLocked(data); 287 } 288 } 289 290 void setHasCallback(IBinder activityToken, boolean hasIt) { 291 if (!isEnabled()) { 292 return; 293 } 294 final Session session = mSessions.get(activityToken); 295 if (session != null) { 296 session.setHasCallback(hasIt); 297 } 298 } 299 300 void startSessionLocked(@NonNull IBinder activityToken, @Nullable IBinder windowToken, 301 @NonNull IBinder appCallbackToken, @NonNull AutofillId autofillId, @NonNull Rect bounds, 302 @Nullable AutofillValue value, boolean hasCallback, int flags, 303 @NonNull String packageName) { 304 if (!isEnabled()) { 305 return; 306 } 307 308 final String historyItem = "s=" + mInfo.getServiceInfo().packageName 309 + " u=" + mUserId + " a=" + activityToken 310 311 + " i=" + autofillId + " b=" + bounds + " hc=" + hasCallback + " f=" + flags; 312 mRequestsHistory.log(historyItem); 313 314 // TODO(b/33197203): Handle partitioning 315 final Session session = mSessions.get(activityToken); 316 if (session != null) { 317 // Already started... 318 return; 319 } 320 321 final Session newSession = createSessionByTokenLocked(activityToken, 322 windowToken, appCallbackToken, hasCallback, flags, packageName); 323 newSession.updateLocked(autofillId, bounds, value, FLAG_START_SESSION); 324 } 325 326 void finishSessionLocked(IBinder activityToken) { 327 if (!isEnabled()) { 328 return; 329 } 330 331 final Session session = mSessions.get(activityToken); 332 if (session == null) { 333 Slog.w(TAG, "finishSessionLocked(): no session for " + activityToken); 334 return; 335 } 336 337 final boolean finished = session.showSaveLocked(); 338 if (DEBUG) { 339 Log.d(TAG, "finishSessionLocked(): session finished on save? " + finished); 340 } 341 if (finished) { 342 session.removeSelf(); 343 } 344 } 345 346 void cancelSessionLocked(IBinder activityToken) { 347 if (!isEnabled()) { 348 return; 349 } 350 351 final Session session = mSessions.get(activityToken); 352 if (session == null) { 353 Slog.w(TAG, "cancelSessionLocked(): no session for " + activityToken); 354 return; 355 } 356 session.removeSelfLocked(); 357 } 358 359 private Session createSessionByTokenLocked(@NonNull IBinder activityToken, 360 @Nullable IBinder windowToken, @NonNull IBinder appCallbackToken, boolean hasCallback, 361 int flags, @NonNull String packageName) { 362 final Session newSession = new Session(mContext, activityToken, 363 windowToken, appCallbackToken, hasCallback, flags, packageName); 364 mSessions.put(activityToken, newSession); 365 366 /* 367 * TODO(b/33197203): apply security checks below: 368 * - checks if disabled by secure settings / device policy 369 * - log operation using noteOp() 370 * - check flags 371 * - display disclosure if needed 372 */ 373 try { 374 final Bundle receiverExtras = new Bundle(); 375 receiverExtras.putBinder(EXTRA_ACTIVITY_TOKEN, activityToken); 376 final long identity = Binder.clearCallingIdentity(); 377 try { 378 if (!ActivityManager.getService().requestAutofillData(mAssistReceiver, 379 receiverExtras, activityToken)) { 380 Slog.w(TAG, "failed to request autofill data for " + activityToken); 381 } 382 } finally { 383 Binder.restoreCallingIdentity(identity); 384 } 385 } catch (RemoteException e) { 386 // Should not happen, it's a local call. 387 } 388 return newSession; 389 } 390 391 void updateSessionLocked(IBinder activityToken, AutofillId autofillId, Rect bounds, 392 AutofillValue value, int flags) { 393 final Session session = mSessions.get(activityToken); 394 if (session == null) { 395 if (VERBOSE) { 396 Slog.v(TAG, "updateSessionLocked(): session gone for " + activityToken); 397 } 398 return; 399 } 400 401 session.updateLocked(autofillId, bounds, value, flags); 402 } 403 404 private void handleSessionSave(IBinder activityToken) { 405 synchronized (mLock) { 406 final Session session = mSessions.get(activityToken); 407 if (session == null) { 408 Slog.w(TAG, "handleSessionSave(): already gone: " + activityToken); 409 410 return; 411 } 412 session.callSaveLocked(); 413 } 414 } 415 416 void destroyLocked() { 417 if (VERBOSE) { 418 Slog.v(TAG, "destroyLocked()"); 419 } 420 421 for (Session session : mSessions.values()) { 422 session.destroyLocked(); 423 } 424 mSessions.clear(); 425 } 426 427 void dumpLocked(String prefix, PrintWriter pw) { 428 final String prefix2 = prefix + " "; 429 430 pw.print(prefix); pw.print("Component:"); pw.println(mInfo != null 431 ? mInfo.getServiceInfo().getComponentName() : null); 432 pw.print(prefix); pw.print("Disabled:"); pw.println(mDisabled); 433 434 if (VERBOSE && mInfo != null) { 435 // ServiceInfo dump is too noisy and redundant (it can be obtained through other dumps) 436 pw.print(prefix); pw.println("ServiceInfo:"); 437 mInfo.getServiceInfo().dump(new PrintWriterPrinter(pw), prefix + prefix); 438 } 439 440 final int size = mSessions.size(); 441 if (size == 0) { 442 pw.print(prefix); pw.println("No sessions"); 443 } else { 444 pw.print(prefix); pw.print(size); pw.println(" sessions:"); 445 for (int i = 0; i < size; i++) { 446 pw.print(prefix); pw.print("#"); pw.println(i + 1); 447 mSessions.valueAt(i).dumpLocked(prefix2, pw); 448 } 449 } 450 } 451 452 void destroySessionsLocked() { 453 for (Session session : mSessions.values()) { 454 session.removeSelf(); 455 } 456 } 457 458 void listSessionsLocked(ArrayList<String> output) { 459 for (IBinder activityToken : mSessions.keySet()) { 460 output.add((mInfo != null ? mInfo.getServiceInfo().getComponentName() 461 : null) + ":" + activityToken); 462 } 463 } 464 465 private void sendStateToClients() { 466 final RemoteCallbackList<IAutoFillManagerClient> clients; 467 final int userClientCount; 468 synchronized (mLock) { 469 if (mClients == null) { 470 return; 471 } 472 clients = mClients; 473 userClientCount = clients.beginBroadcast(); 474 } 475 try { 476 for (int i = 0; i < userClientCount; i++) { 477 final IAutoFillManagerClient client = clients.getBroadcastItem(i); 478 try { 479 client.setState(isEnabled()); 480 } catch (RemoteException re) { 481 /* ignore */ 482 } 483 } 484 } finally { 485 clients.finishBroadcast(); 486 } 487 } 488 489 private boolean isEnabled() { 490 return mInfo != null && !mDisabled; 491 } 492 493 @Override 494 public String toString() { 495 return "AutofillManagerServiceImpl: [userId=" + mUserId 496 + ", component=" + (mInfo != null 497 ? mInfo.getServiceInfo().getComponentName() : null) + "]"; 498 } 499 500 /** 501 * State for a given view with a AutofillId. 502 * 503 * <p>This class holds state about a view and calls its listener when the fill UI is ready to 504 * be displayed for the view. 505 */ 506 static final class ViewState { 507 interface Listener { 508 /** 509 * Called when the fill UI is ready to be shown for this view. 510 */ 511 void onFillReady(ViewState viewState, FillResponse fillResponse, Rect bounds, 512 AutofillId focusedId, @Nullable AutofillValue value); 513 } 514 515 final AutofillId mId; 516 private final Listener mListener; 517 // TODO(b/33197203): would not need a reference to response and session if it was an inner 518 // class of Session... 519 private final Session mSession; 520 // TODO(b/33197203): encapsulate access so it's not called by UI 521 FillResponse mResponse; 522 Intent mAuthIntent; 523 524 private AutofillValue mAutofillValue; 525 private Rect mBounds; 526 527 private boolean mValueUpdated; 528 529 ViewState(Session session, AutofillId id, Listener listener) { 530 mSession = session; 531 mId = id; 532 mListener = listener; 533 } 534 535 /** 536 * Response should only be set once. 537 */ 538 void setResponse(FillResponse response) { 539 mResponse = response; 540 maybeCallOnFillReady(); 541 } 542 543 /** 544 * Used when a {@link FillResponse} requires authentication to be unlocked. 545 */ 546 void setResponse(FillResponse response, Intent authIntent) { 547 mAuthIntent = authIntent; 548 setResponse(response); 549 } 550 551 CharSequence getServiceName() { 552 return mSession.getServiceName(); 553 } 554 555 // TODO(b/33197203): need to refactor / rename / document this method to make it clear that 556 // it can change the value and update the UI; similarly, should replace code that 557 // directly sets mAutoFilLValue to use encapsulation. 558 void update(@Nullable AutofillValue autofillValue, @Nullable Rect bounds) { 559 if (autofillValue != null) { 560 mAutofillValue = autofillValue; 561 } 562 if (bounds != null) { 563 mBounds = bounds; 564 } 565 566 maybeCallOnFillReady(); 567 } 568 569 /** 570 * Calls {@link 571 * Listener#onFillReady(ViewState, FillResponse, Rect, AutofillId, AutofillValue)} if the 572 * fill UI is ready to be displayed (i.e. when response and bounds are set). 573 */ 574 void maybeCallOnFillReady() { 575 if (mResponse != null && (mResponse.getAuthentication() != null 576 || mResponse.getDatasets() != null) && mBounds != null) { 577 mListener.onFillReady(this, mResponse, mBounds, mId, mAutofillValue); 578 } 579 } 580 581 @Override 582 public String toString() { 583 return "ViewState: [id=" + mId + ", value=" + mAutofillValue + ", bounds=" + mBounds 584 + ", updated = " + mValueUpdated + "]"; 585 } 586 587 void dump(String prefix, PrintWriter pw) { 588 pw.print(prefix); pw.print("id:" ); pw.println(mId); 589 pw.print(prefix); pw.print("value:" ); pw.println(mAutofillValue); 590 pw.print(prefix); pw.print("updated:" ); pw.println(mValueUpdated); 591 pw.print(prefix); pw.print("bounds:" ); pw.println(mBounds); 592 pw.print(prefix); pw.print("authIntent:" ); pw.println(mAuthIntent); 593 } 594 } 595 596 /** 597 * A session for a given activity. 598 * 599 * <p>This class manages the multiple {@link ViewState}s for each view it has, and keeps track 600 * of the current {@link ViewState} to display the appropriate UI. 601 * 602 * <p>Although the autofill requests and callbacks are stateless from the service's point of 603 * view, we need to keep state in the framework side for cases such as authentication. For 604 * example, when service return a {@link FillResponse} that contains all the fields needed 605 * to fill the activity but it requires authentication first, that response need to be held 606 * until the user authenticates or it times out. 607 */ 608 // TODO(b/33197203): make sure sessions are removed (and tested by CTS): 609 // - On all authentication scenarios. 610 // - When user does not interact back after a while. 611 // - When service is unbound. 612 final class Session implements RemoteFillService.FillServiceCallbacks, ViewState.Listener, 613 AutoFillUI.AutoFillUiCallback { 614 private final IBinder mActivityToken; 615 private final IBinder mWindowToken; 616 617 /** Package name of the app that is auto-filled */ 618 @NonNull private final String mPackageName; 619 620 @GuardedBy("mLock") 621 private final Map<AutofillId, ViewState> mViewStates = new ArrayMap<>(); 622 623 @GuardedBy("mLock") 624 @Nullable 625 private ViewState mCurrentViewState; 626 627 private final IAutoFillManagerClient mClient; 628 629 @GuardedBy("mLock") 630 RemoteFillService mRemoteFillService; 631 632 // TODO(b/33197203): Get a response per view instead of per activity. 633 @GuardedBy("mLock") 634 private FillResponse mCurrentResponse; 635 636 /** 637 * Used to remember which {@link Dataset} filled the session. 638 */ 639 // TODO(b/33197203): might need more than one once we support partitions 640 @GuardedBy("mLock") 641 private Dataset mAutoFilledDataset; 642 643 /** 644 * Assist structure sent by the app; it will be updated (sanitized, change values for save) 645 * before sent to {@link AutofillService}. 646 */ 647 @GuardedBy("mLock") 648 private AssistStructure mStructure; 649 650 /** 651 * Whether the client has an {@link android.view.autofill.AutofillManager.AutofillCallback}. 652 */ 653 private boolean mHasCallback; 654 655 /** 656 * Flags used to start the session. 657 */ 658 private int mFlags; 659 private Session(@NonNull Context context, @NonNull IBinder activityToken, 660 @Nullable IBinder windowToken, @NonNull IBinder client, boolean hasCallback, 661 int flags, @NonNull String packageName) { 662 mRemoteFillService = new RemoteFillService(context, 663 mInfo.getServiceInfo().getComponentName(), mUserId, this); 664 mActivityToken = activityToken; 665 mWindowToken = windowToken; 666 mHasCallback = hasCallback; 667 mFlags = flags; 668 mPackageName = packageName; 669 670 mClient = IAutoFillManagerClient.Stub.asInterface(client); 671 try { 672 client.linkToDeath(() -> { 673 if (DEBUG) { 674 Slog.d(TAG, "app binder died"); 675 } 676 677 removeSelf(); 678 }, 0); 679 } catch (RemoteException e) { 680 Slog.w(TAG, "linkToDeath() on mClient failed: " + e); 681 } 682 683 mMetricsLogger.action(MetricsProto.MetricsEvent.AUTOFILL_SESSION_STARTED, mPackageName); 684 } 685 686 // FillServiceCallbacks 687 @Override 688 public void onFillRequestSuccess(@Nullable FillResponse response, 689 @NonNull String servicePackageName) { 690 if (response == null) { 691 // Nothing to be done, but need to notify client. 692 notifyUnavailableToClient(); 693 removeSelf(); 694 return; 695 } 696 697 if ((response.getDatasets() == null || response.getDatasets().isEmpty()) 698 && response.getAuthentication() == null) { 699 // Response is "empty" from an UI point of view, need to notify client. 700 notifyUnavailableToClient(); 701 } 702 synchronized (mLock) { 703 processResponseLocked(response); 704 } 705 706 LogMaker log = (new LogMaker(MetricsProto.MetricsEvent.AUTOFILL_REQUEST)) 707 .setType(MetricsProto.MetricsEvent.TYPE_SUCCESS) 708 .setPackageName(mPackageName) 709 .addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS, 710 response.getDatasets() == null ? 0 : response.getDatasets().size()) 711 .addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_SERVICE, 712 servicePackageName); 713 mMetricsLogger.write(log); 714 } 715 716 // FillServiceCallbacks 717 @Override 718 public void onFillRequestFailure(@Nullable CharSequence message, 719 @NonNull String servicePackageName) { 720 LogMaker log = (new LogMaker(MetricsProto.MetricsEvent.AUTOFILL_REQUEST)) 721 .setType(MetricsProto.MetricsEvent.TYPE_FAILURE) 722 .setPackageName(mPackageName) 723 .addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_SERVICE, 724 servicePackageName); 725 mMetricsLogger.write(log); 726 727 getUiForShowing().showError(message); 728 removeSelf(); 729 } 730 731 // FillServiceCallbacks 732 @Override 733 public void onSaveRequestSuccess(@NonNull String servicePackageName) { 734 LogMaker log = (new LogMaker( 735 MetricsProto.MetricsEvent.AUTOFILL_DATA_SAVE_REQUEST)) 736 .setType(MetricsProto.MetricsEvent.TYPE_SUCCESS) 737 .setPackageName(mPackageName) 738 .addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_SERVICE, 739 servicePackageName); 740 mMetricsLogger.write(log); 741 742 // Nothing left to do... 743 removeSelf(); 744 } 745 746 // FillServiceCallbacks 747 @Override 748 public void onSaveRequestFailure(@Nullable CharSequence message, 749 @NonNull String servicePackageName) { 750 LogMaker log = (new LogMaker( 751 MetricsProto.MetricsEvent.AUTOFILL_DATA_SAVE_REQUEST)) 752 .setType(MetricsProto.MetricsEvent.TYPE_FAILURE) 753 .setPackageName(mPackageName) 754 .addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_SERVICE, 755 servicePackageName); 756 mMetricsLogger.write(log); 757 758 getUiForShowing().showError(message); 759 removeSelf(); 760 } 761 762 // FillServiceCallbacks 763 @Override 764 public void authenticate(IntentSender intent) { 765 final Intent fillInIntent; 766 synchronized (mLock) { 767 fillInIntent = createAuthFillInIntent(mStructure); 768 } 769 mHandlerCaller.getHandler().post(() -> startAuthentication(intent, fillInIntent)); 770 } 771 772 // FillServiceCallbacks 773 @Override 774 public void onDisableSelf() { 775 final long identity = Binder.clearCallingIdentity(); 776 try { 777 final String autoFillService = Settings.Secure.getStringForUser( 778 mContext.getContentResolver(), 779 Settings.Secure.AUTOFILL_SERVICE, mUserId); 780 if (mInfo.getServiceInfo().getComponentName().equals( 781 ComponentName.unflattenFromString(autoFillService))) { 782 Settings.Secure.putStringForUser(mContext.getContentResolver(), 783 Settings.Secure.AUTOFILL_SERVICE, null, mUserId); 784 } 785 } finally { 786 Binder.restoreCallingIdentity(identity); 787 } 788 synchronized (mLock) { 789 removeSelfLocked(); 790 } 791 } 792 793 // FillServiceCallbacks 794 @Override 795 public void onServiceDied(RemoteFillService service) { 796 // TODO(b/33197203): implement 797 } 798 799 // AutoFillUiCallback 800 @Override 801 public void fill(Dataset dataset) { 802 mHandlerCaller.getHandler().post(() -> autoFill(dataset)); 803 } 804 805 // AutoFillUiCallback 806 @Override 807 public void save() { 808 mHandlerCaller.getHandler().obtainMessage(MSG_SERVICE_SAVE, mActivityToken) 809 .sendToTarget(); 810 } 811 812 // AutoFillUiCallback 813 @Override 814 public void cancelSave() { 815 mHandlerCaller.getHandler().post(() -> removeSelf()); 816 } 817 818 // AutoFillUiCallback 819 @Override 820 public void onEvent(AutofillId id, int event) { 821 mHandlerCaller.getHandler().post(() -> notifyChangeToClient(id, event)); 822 } 823 824 public void setAuthenticationResultLocked(Bundle data) { 825 if (mCurrentResponse == null || data == null) { 826 removeSelf(); 827 } else { 828 Parcelable result = data.getParcelable( 829 AutofillManager.EXTRA_AUTHENTICATION_RESULT); 830 if (result instanceof FillResponse) { 831 mMetricsLogger.action(MetricsProto.MetricsEvent.AUTOFILL_AUTHENTICATED, 832 mPackageName); 833 834 mCurrentResponse = (FillResponse) result; 835 processResponseLocked(mCurrentResponse); 836 } else if (result instanceof Dataset) { 837 Dataset dataset = (Dataset) result; 838 mCurrentResponse.getDatasets().remove(mAutoFilledDataset); 839 mCurrentResponse.getDatasets().add(dataset); 840 mAutoFilledDataset = dataset; 841 processResponseLocked(mCurrentResponse); 842 } 843 } 844 } 845 846 public void setHasCallback(boolean hasIt) { 847 mHasCallback = hasIt; 848 } 849 850 /** 851 * Shows the save UI, when session can be saved. 852 * 853 * @return {@code true} if session is done, or {@code false} if it's pending user action. 854 */ 855 public boolean showSaveLocked() { 856 if (mStructure == null) { 857 Slog.wtf(TAG, "showSaveLocked(): no mStructure"); 858 return true; 859 } 860 if (mCurrentResponse == null) { 861 // Happens when the activity / session was finished before the service replied, or 862 // when the service cannot autofill it (and returned a null response). 863 if (DEBUG) { 864 Slog.d(TAG, "showSaveLocked(): no mCurrentResponse"); 865 } 866 return true; 867 } 868 final SaveInfo saveInfo = mCurrentResponse.getSaveInfo(); 869 if (DEBUG) { 870 Slog.d(TAG, "showSaveLocked(): saveInfo=" + saveInfo); 871 } 872 873 /* 874 * The Save dialog is only shown if all conditions below are met: 875 * 876 * - saveInfo is not null 877 * - autofillValue of all required ids is not null 878 * - autofillValue of at least one id (required or optional) has changed. 879 */ 880 881 if (saveInfo == null) { 882 return true; 883 } 884 885 final AutofillId[] requiredIds = saveInfo.getRequiredIds(); 886 if (requiredIds == null || requiredIds.length == 0) { 887 Slog.w(TAG, "showSaveLocked(): no required ids on saveInfo"); 888 return true; 889 } 890 891 boolean allRequiredAreNotEmpty = true; 892 boolean atLeastOneChanged = false; 893 for (int i = 0; i < requiredIds.length; i++) { 894 final AutofillId id = requiredIds[i]; 895 final ViewState state = mViewStates.get(id); 896 if (state == null || state.mAutofillValue == null 897 || state.mAutofillValue.isEmpty()) { 898 final ViewNode node = findViewNodeByIdLocked(id); 899 if (node == null) { 900 Slog.w(TAG, "Service passed invalid id on SavableInfo: " + id); 901 allRequiredAreNotEmpty = false; 902 break; 903 } 904 final AutofillValue initialValue = node.getAutofillValue(); 905 if (initialValue == null || initialValue.isEmpty()) { 906 if (DEBUG) { 907 Slog.d(TAG, "finishSessionLocked(): empty initial value for " + id ); 908 } 909 allRequiredAreNotEmpty = false; 910 break; 911 } 912 } 913 if (state.mValueUpdated) { 914 final AutofillValue filledValue = findValue(mAutoFilledDataset, id); 915 if (!state.mAutofillValue.equals(filledValue)) { 916 if (DEBUG) { 917 Slog.d(TAG, "finishSessionLocked(): found a change on " + id + ": " 918 + filledValue + " => " + state.mAutofillValue); 919 } 920 atLeastOneChanged = true; 921 } 922 } else { 923 if (state.mAutofillValue == null || state.mAutofillValue.isEmpty()) { 924 if (DEBUG) { 925 Slog.d(TAG, "finishSessionLocked(): empty value for " + id + ": " 926 + state.mAutofillValue); 927 } 928 allRequiredAreNotEmpty = false; 929 break; 930 931 } 932 } 933 } 934 935 if (allRequiredAreNotEmpty) { 936 if (!atLeastOneChanged && saveInfo.getOptionalIds() != null) { 937 for (int i = 0; i < saveInfo.getOptionalIds().length; i++) { 938 final AutofillId id = saveInfo.getOptionalIds()[i]; 939 final ViewState state = mViewStates.get(id); 940 if (state != null && state.mAutofillValue != null && state.mValueUpdated) { 941 final AutofillValue filledValue = findValue(mAutoFilledDataset, id); 942 if (!state.mAutofillValue.equals(filledValue)) { 943 if (DEBUG) { 944 Slog.d(TAG, "finishSessionLocked(): found a change on optional " 945 + id + ": " + filledValue + " => " 946 + state.mAutofillValue); 947 } 948 atLeastOneChanged = true; 949 break; 950 } 951 } 952 } 953 } 954 if (atLeastOneChanged) { 955 getUiForShowing().showSaveUi( 956 mInfo.getServiceInfo().loadLabel(mContext.getPackageManager()), 957 saveInfo, mPackageName); 958 return false; 959 } 960 } 961 // Nothing changed... 962 if (DEBUG) { 963 Slog.d(TAG, "showSaveLocked(): with no changes, comes no responsibilities." 964 + "allRequiredAreNotNull=" + allRequiredAreNotEmpty 965 + ", atLeastOneChanged=" + atLeastOneChanged); 966 } 967 return true; 968 } 969 970 /** 971 * Calls service when user requested save. 972 */ 973 private void callSaveLocked() { 974 if (DEBUG) { 975 Slog.d(TAG, "callSaveLocked(): mViewStates=" + mViewStates); 976 } 977 978 final Bundle extras = this.mCurrentResponse.getExtras(); 979 980 for (Entry<AutofillId, ViewState> entry : mViewStates.entrySet()) { 981 final AutofillValue value = entry.getValue().mAutofillValue; 982 if (value == null) { 983 if (VERBOSE) { 984 Slog.v(TAG, "callSaveLocked(): skipping " + entry.getKey()); 985 } 986 continue; 987 } 988 final AutofillId id = entry.getKey(); 989 final ViewNode node = findViewNodeByIdLocked(id); 990 if (node == null) { 991 Slog.w(TAG, "callSaveLocked(): did not find node with id " + id); 992 continue; 993 } 994 if (VERBOSE) { 995 Slog.v(TAG, "callSaveLocked(): updating " + id + " to " + value); 996 } 997 998 node.updateAutofillValue(value); 999 } 1000 1001 // Sanitize structure before it's sent to service. 1002 mStructure.sanitizeForParceling(false); 1003 1004 if (VERBOSE) { 1005 Slog.v(TAG, "Dumping " + mStructure + " before calling service.save()"); 1006 mStructure.dump(); 1007 } 1008 1009 mRemoteFillService.onSaveRequest(mStructure, extras); 1010 } 1011 1012 void updateLocked(AutofillId id, Rect bounds, AutofillValue value, int flags) { 1013 if (mAutoFilledDataset != null && (flags & FLAG_VALUE_CHANGED) == 0) { 1014 // TODO(b/33197203): ignoring because we don't support partitions yet 1015 Slog.d(TAG, "updateLocked(): ignoring " + flags + " after app was autofilled"); 1016 return; 1017 } 1018 1019 ViewState viewState = mViewStates.get(id); 1020 if (viewState == null) { 1021 viewState = new ViewState(this, id, this); 1022 mViewStates.put(id, viewState); 1023 } 1024 1025 if ((flags & FLAG_START_SESSION) != 0) { 1026 // View is triggering autofill. 1027 mCurrentViewState = viewState; 1028 viewState.update(value, bounds); 1029 return; 1030 } 1031 1032 if ((flags & FLAG_VALUE_CHANGED) != 0) { 1033 if (value != null && !value.equals(viewState.mAutofillValue)) { 1034 viewState.mValueUpdated = true; 1035 1036 // Must check if this update was caused by autofilling the view, in which 1037 // case we just update the value, but not the UI. 1038 if (mAutoFilledDataset != null) { 1039 final AutofillValue filledValue = findValue(mAutoFilledDataset, id); 1040 if (value.equals(filledValue)) { 1041 viewState.mAutofillValue = value; 1042 return; 1043 } 1044 } 1045 1046 // Change value 1047 viewState.mAutofillValue = value; 1048 1049 // Update the chooser UI 1050 if (value.isText()) { 1051 getUiForShowing().filterFillUi(value.getTextValue().toString()); 1052 } else { 1053 getUiForShowing().filterFillUi(null); 1054 } 1055 } 1056 1057 return; 1058 } 1059 1060 if ((flags & FLAG_VIEW_ENTERED) != 0) { 1061 // Remove the UI if the ViewState has changed. 1062 if (mCurrentViewState != viewState) { 1063 mUi.hideFillUi(mCurrentViewState != null ? mCurrentViewState.mId : null); 1064 mCurrentViewState = viewState; 1065 } 1066 1067 // If the ViewState is ready to be displayed, onReady() will be called. 1068 viewState.update(value, bounds); 1069 1070 // TODO(b/33197203): Remove when there is a response per activity. 1071 if (mCurrentResponse != null) { 1072 viewState.setResponse(mCurrentResponse); 1073 } 1074 1075 return; 1076 } 1077 1078 if ((flags & FLAG_VIEW_EXITED) != 0) { 1079 if (mCurrentViewState == viewState) { 1080 mUi.hideFillUi(viewState.mId); 1081 mCurrentViewState = null; 1082 } 1083 return; 1084 } 1085 1086 Slog.w(TAG, "updateLocked(): unknown flags " + flags); 1087 } 1088 1089 @Override 1090 public void onFillReady(ViewState viewState, FillResponse response, Rect bounds, 1091 AutofillId filledId, @Nullable AutofillValue value) { 1092 String filterText = null; 1093 if (value != null && value.isText()) { 1094 filterText = value.getTextValue().toString(); 1095 } 1096 1097 getUiForShowing().showFillUi(filledId, response, bounds, filterText, mPackageName); 1098 } 1099 1100 private void notifyChangeToClient(AutofillId id, int event) { 1101 if (!mHasCallback) return; 1102 try { 1103 mClient.onAutofillEvent(mWindowToken, id, event); 1104 } catch (RemoteException e) { 1105 Slog.e(TAG, "Error notifying client on change: id=" + id + ", event=" + event, e); 1106 } 1107 } 1108 1109 private void notifyUnavailableToClient() { 1110 if (mCurrentViewState == null) { 1111 // TODO(b/33197203): temporary sanity check; should never happen 1112 Slog.w(TAG, "notifyUnavailable(): mCurrentViewState is null"); 1113 return; 1114 } 1115 notifyChangeToClient(mCurrentViewState.mId, AutofillCallback.EVENT_INPUT_UNAVAILABLE); 1116 } 1117 1118 private void processResponseLocked(FillResponse response) { 1119 if (DEBUG) { 1120 Slog.d(TAG, "processResponseLocked(auth=" + response.getAuthentication() 1121 + "):" + response); 1122 } 1123 1124 if (mCurrentViewState == null) { 1125 // TODO(b/33197203): temporary sanity check; should never happen 1126 Slog.w(TAG, "processResponseLocked(): mCurrentViewState is null"); 1127 return; 1128 } 1129 1130 mCurrentResponse = response; 1131 1132 if (mCurrentResponse.getAuthentication() != null) { 1133 // Handle authentication. 1134 final Intent fillInIntent = createAuthFillInIntent(mStructure); 1135 mCurrentViewState.setResponse(mCurrentResponse, fillInIntent); 1136 return; 1137 } 1138 1139 if ((mFlags & FLAG_MANUAL_REQUEST) != 0 && response.getDatasets() != null 1140 && response.getDatasets().size() == 1) { 1141 Slog.d(TAG, "autofilling manual request directly"); 1142 autoFill(response.getDatasets().get(0)); 1143 return; 1144 } 1145 1146 mCurrentViewState.setResponse(mCurrentResponse); 1147 } 1148 1149 void autoFill(Dataset dataset) { 1150 synchronized (mLock) { 1151 mAutoFilledDataset = dataset; 1152 1153 // Autofill it directly... 1154 if (dataset.getAuthentication() == null) { 1155 autoFillApp(dataset); 1156 return; 1157 } 1158 1159 // ...or handle authentication. 1160 Intent fillInIntent = createAuthFillInIntent(mStructure); 1161 startAuthentication(dataset.getAuthentication(), fillInIntent); 1162 } 1163 } 1164 1165 CharSequence getServiceName() { 1166 return AutofillManagerServiceImpl.this.getServiceName(); 1167 } 1168 1169 private Intent createAuthFillInIntent(AssistStructure structure) { 1170 Intent fillInIntent = new Intent(); 1171 fillInIntent.putExtra(AutofillManager.EXTRA_ASSIST_STRUCTURE, structure); 1172 return fillInIntent; 1173 } 1174 1175 private void startAuthentication(IntentSender intent, Intent fillInIntent) { 1176 try { 1177 mClient.authenticate(intent, fillInIntent); 1178 } catch (RemoteException e) { 1179 Slog.e(TAG, "Error launching auth intent", e); 1180 } 1181 } 1182 1183 void dumpLocked(String prefix, PrintWriter pw) { 1184 pw.print(prefix); pw.print("mActivityToken: "); pw.println(mActivityToken); 1185 pw.print(prefix); pw.print("mFlags: "); pw.println(mFlags); 1186 pw.print(prefix); pw.print("mCurrentResponse: "); pw.println(mCurrentResponse); 1187 pw.print(prefix); pw.print("mAutoFilledDataset: "); pw.println(mAutoFilledDataset); 1188 pw.print(prefix); pw.print("mCurrentViewStates: "); pw.println(mCurrentViewState); 1189 pw.print(prefix); pw.print("mViewStates: "); pw.println(mViewStates.size()); 1190 final String prefix2 = prefix + " "; 1191 for (Map.Entry<AutofillId, ViewState> entry : mViewStates.entrySet()) { 1192 pw.print(prefix); pw.print("State for id "); pw.println(entry.getKey()); 1193 entry.getValue().dump(prefix2, pw); 1194 } 1195 if (VERBOSE) { 1196 pw.print(prefix); pw.print("mStructure: " ); 1197 // TODO(b/33197203): add method do dump AssistStructure on pw 1198 if (mStructure != null) { 1199 pw.println("look at logcat" ); 1200 mStructure.dump(); // dumps to logcat 1201 } else { 1202 pw.println("null"); 1203 } 1204 } 1205 pw.print(prefix); pw.print("mHasCallback: "); pw.println(mHasCallback); 1206 mRemoteFillService.dump(prefix, pw); 1207 } 1208 1209 void autoFillApp(Dataset dataset) { 1210 synchronized (mLock) { 1211 try { 1212 if (DEBUG) { 1213 Slog.d(TAG, "autoFillApp(): the buck is on the app: " + dataset); 1214 } 1215 mClient.autofill(dataset.getFieldIds(), dataset.getFieldValues()); 1216 } catch (RemoteException e) { 1217 Slog.w(TAG, "Error autofilling activity: " + e); 1218 } 1219 } 1220 } 1221 1222 private AutoFillUI getUiForShowing() { 1223 synchronized (mLock) { 1224 mUi.setCallback(this, mWindowToken); 1225 return mUi; 1226 } 1227 } 1228 1229 private ViewNode findViewNodeByIdLocked(AutofillId id) { 1230 final int size = mStructure.getWindowNodeCount(); 1231 for (int i = 0; i < size; i++) { 1232 final WindowNode window = mStructure.getWindowNodeAt(i); 1233 final ViewNode root = window.getRootViewNode(); 1234 if (id.equals(root.getAutofillId())) { 1235 return root; 1236 } 1237 final ViewNode child = findViewNodeByIdLocked(root, id); 1238 if (child != null) { 1239 return child; 1240 } 1241 } 1242 return null; 1243 } 1244 1245 private ViewNode findViewNodeByIdLocked(ViewNode parent, AutofillId id) { 1246 final int childrenSize = parent.getChildCount(); 1247 if (childrenSize > 0) { 1248 for (int i = 0; i < childrenSize; i++) { 1249 final ViewNode child = parent.getChildAt(i); 1250 if (id.equals(child.getAutofillId())) { 1251 return child; 1252 } 1253 final ViewNode grandChild = findViewNodeByIdLocked(child, id); 1254 if (grandChild != null && id.equals(grandChild.getAutofillId())) { 1255 return grandChild; 1256 } 1257 } 1258 } 1259 return null; 1260 } 1261 1262 private void destroyLocked() { 1263 mRemoteFillService.destroy(); 1264 mUi.setCallback(null, null); 1265 1266 mMetricsLogger.action(MetricsProto.MetricsEvent.AUTOFILL_SESSION_FINISHED, 1267 mPackageName); 1268 } 1269 1270 void removeSelf() { 1271 synchronized (mLock) { 1272 removeSelfLocked(); 1273 } 1274 } 1275 1276 private void removeSelfLocked() { 1277 if (VERBOSE) { 1278 Slog.v(TAG, "removeSelfLocked()"); 1279 } 1280 destroyLocked(); 1281 mSessions.remove(mActivityToken); 1282 } 1283 } 1284} 1285