Session.java revision 41200eac711f8a2a50c0e87ad8b5bae509589c61
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 18package com.android.server.autofill; 19 20import static android.view.autofill.AutofillManager.FLAG_MANUAL_REQUEST; 21import static android.view.autofill.AutofillManager.FLAG_START_SESSION; 22import static android.view.autofill.AutofillManager.FLAG_VALUE_CHANGED; 23import static android.view.autofill.AutofillManager.FLAG_VIEW_ENTERED; 24import static android.view.autofill.AutofillManager.FLAG_VIEW_EXITED; 25 26import static com.android.server.autofill.Helper.DEBUG; 27import static com.android.server.autofill.Helper.VERBOSE; 28 29import android.annotation.NonNull; 30import android.annotation.Nullable; 31import android.app.assist.AssistStructure; 32import android.app.assist.AssistStructure.AutofillOverlay; 33import android.app.assist.AssistStructure.ViewNode; 34import android.app.assist.AssistStructure.WindowNode; 35import android.content.ComponentName; 36import android.content.Context; 37import android.content.Intent; 38import android.content.IntentSender; 39import android.graphics.Rect; 40import android.metrics.LogMaker; 41import android.os.Bundle; 42import android.os.IBinder; 43import android.os.Parcelable; 44import android.os.RemoteException; 45import android.service.autofill.AutofillService; 46import android.service.autofill.Dataset; 47import android.service.autofill.FillContext; 48import android.service.autofill.FillRequest; 49import android.service.autofill.FillResponse; 50import android.service.autofill.SaveInfo; 51import android.service.autofill.SaveRequest; 52import android.util.ArrayMap; 53import android.util.DebugUtils; 54import android.util.Slog; 55import android.util.SparseArray; 56import android.view.autofill.AutofillId; 57import android.view.autofill.AutofillManager; 58import android.view.autofill.AutofillValue; 59import android.view.autofill.IAutoFillManagerClient; 60import android.view.autofill.IAutofillWindowPresenter; 61 62import com.android.internal.R; 63import com.android.internal.annotations.GuardedBy; 64import com.android.internal.logging.MetricsLogger; 65import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 66import com.android.internal.os.HandlerCaller; 67import com.android.server.autofill.ui.AutoFillUI; 68 69import java.io.PrintWriter; 70import java.util.ArrayList; 71import java.util.Map; 72import java.util.Map.Entry; 73 74/** 75 * A session for a given activity. 76 * 77 * <p>This class manages the multiple {@link ViewState}s for each view it has, and keeps track 78 * of the current {@link ViewState} to display the appropriate UI. 79 * 80 * <p>Although the autofill requests and callbacks are stateless from the service's point of 81 * view, we need to keep state in the framework side for cases such as authentication. For 82 * example, when service return a {@link FillResponse} that contains all the fields needed 83 * to fill the activity but it requires authentication first, that response need to be held 84 * until the user authenticates or it times out. 85 */ 86// TODO(b/33197203): make sure sessions are removed (and tested by CTS): 87// - On all authentication scenarios. 88// - When user does not interact back after a while. 89// - When service is unbound. 90final class Session implements RemoteFillService.FillServiceCallbacks, ViewState.Listener, 91 AutoFillUI.AutoFillUiCallback { 92 private static final String TAG = "AutofillSession"; 93 94 private final AutofillManagerServiceImpl mService; 95 private final HandlerCaller mHandlerCaller; 96 private final Object mLock; 97 private final AutoFillUI mUi; 98 99 private final MetricsLogger mMetricsLogger = new MetricsLogger(); 100 101 /** Id of the session */ 102 public final int id; 103 104 /** uid the session is for */ 105 public final int uid; 106 107 @GuardedBy("mLock") 108 @NonNull private IBinder mActivityToken; 109 110 @GuardedBy("mLock") 111 @NonNull private IBinder mWindowToken; 112 113 /** Package name of the app that is auto-filled */ 114 @NonNull private final String mPackageName; 115 116 @GuardedBy("mLock") 117 private final ArrayMap<AutofillId, ViewState> mViewStates = new ArrayMap<>(); 118 119 /** 120 * Id of the View currently being displayed. 121 */ 122 @GuardedBy("mLock") 123 @Nullable private AutofillId mCurrentViewId; 124 125 @GuardedBy("mLock") 126 private IAutoFillManagerClient mClient; 127 128 @GuardedBy("mLock") 129 RemoteFillService mRemoteFillService; 130 131 @GuardedBy("mLock") 132 private SparseArray<FillResponse> mResponses; 133 134 /** 135 * Response that requires a service authentitcation request. 136 */ 137 @GuardedBy("mLock") 138 private FillResponse mResponseWaitingAuth; 139 140 /** 141 * Dataset that when tapped launched a service authentication request. 142 */ 143 @GuardedBy("mLock") 144 private Dataset mDatasetWaitingAuth; 145 146 /** 147 * Assist structure sent by the app; it will be updated (sanitized, change values for save) 148 * before sent to {@link AutofillService}. 149 */ 150 @GuardedBy("mLock") 151 private AssistStructure mStructure; 152 153 /** 154 * Whether the client has an {@link android.view.autofill.AutofillManager.AutofillCallback}. 155 */ 156 private boolean mHasCallback; 157 158 /** 159 * Extras sent by service on {@code onFillRequest()} calls; the first non-null extra is saved 160 * and used on subsequent {@code onFillRequest()} and {@code onSaveRequest()} calls. 161 */ 162 @GuardedBy("mLock") 163 private Bundle mClientState; 164 165 /** 166 * Flags used to start the session. 167 */ 168 int mFlags; 169 170 Session(@NonNull AutofillManagerServiceImpl service, @NonNull AutoFillUI ui, 171 @NonNull Context context, @NonNull HandlerCaller handlerCaller, int userId, 172 @NonNull Object lock, int sessionId, int uid, @NonNull IBinder activityToken, 173 @Nullable IBinder windowToken, @NonNull IBinder client, boolean hasCallback, 174 int flags, @NonNull ComponentName componentName, @NonNull String packageName) { 175 id = sessionId; 176 this.uid = uid; 177 mService = service; 178 mLock = lock; 179 mUi = ui; 180 mHandlerCaller = handlerCaller; 181 mRemoteFillService = new RemoteFillService(context, componentName, userId, this); 182 mActivityToken = activityToken; 183 mWindowToken = windowToken; 184 mHasCallback = hasCallback; 185 mPackageName = packageName; 186 mFlags = flags; 187 mClient = IAutoFillManagerClient.Stub.asInterface(client); 188 189 mMetricsLogger.action(MetricsEvent.AUTOFILL_SESSION_STARTED, mPackageName); 190 } 191 192 /** 193 * Gets the currently registered activity token 194 * 195 * @return The activity token 196 */ 197 public IBinder getActivityTokenLocked() { 198 return mActivityToken; 199 } 200 201 /** 202 * Sets new window for this session. 203 * 204 * @param newWindow The window the Ui should be attached to. Can be {@code null} if no 205 * further UI is needed. 206 */ 207 void switchWindow(@NonNull IBinder newWindow) { 208 synchronized (mLock) { 209 mWindowToken = newWindow; 210 } 211 } 212 213 /** 214 * Sets new activity and client for this session. 215 * 216 * @param newActivity The token of the new activity 217 * @param newClient The client receiving autofill callbacks 218 */ 219 void switchActivity(@NonNull IBinder newActivity, @NonNull IBinder newClient) { 220 synchronized (mLock) { 221 mActivityToken = newActivity; 222 mClient = IAutoFillManagerClient.Stub.asInterface(newClient); 223 } 224 } 225 226 // FillServiceCallbacks 227 @Override 228 public void onFillRequestSuccess(@Nullable FillResponse response, 229 @NonNull String servicePackageName, int requestId) { 230 if (response == null) { 231 if ((mFlags & FLAG_MANUAL_REQUEST) != 0) { 232 getUiForShowing().showError(R.string.autofill_error_cannot_autofill); 233 } 234 // Nothing to be done, but need to notify client. 235 notifyUnavailableToClient(); 236 removeSelf(); 237 return; 238 } 239 240 if ((response.getDatasets() == null || response.getDatasets().isEmpty()) 241 && response.getAuthentication() == null) { 242 // Response is "empty" from an UI point of view, need to notify client. 243 notifyUnavailableToClient(); 244 } 245 synchronized (mLock) { 246 if (response.getAuthentication() != null) { 247 // TODO(b/33197203 , b/35707731): make sure it's ignored if there is one already 248 mResponseWaitingAuth = response; 249 } 250 processResponseLocked(response, requestId); 251 } 252 253 final LogMaker log = (new LogMaker(MetricsEvent.AUTOFILL_REQUEST)) 254 .setType(MetricsEvent.TYPE_SUCCESS) 255 .setPackageName(mPackageName) 256 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS, 257 response.getDatasets() == null ? 0 : response.getDatasets().size()) 258 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE, 259 servicePackageName); 260 mMetricsLogger.write(log); 261 } 262 263 // FillServiceCallbacks 264 @Override 265 public void onFillRequestFailure(@Nullable CharSequence message, 266 @NonNull String servicePackageName) { 267 LogMaker log = (new LogMaker(MetricsEvent.AUTOFILL_REQUEST)) 268 .setType(MetricsEvent.TYPE_FAILURE) 269 .setPackageName(mPackageName) 270 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE, servicePackageName); 271 mMetricsLogger.write(log); 272 273 getUiForShowing().showError(message); 274 removeSelf(); 275 } 276 277 // FillServiceCallbacks 278 @Override 279 public void onSaveRequestSuccess(@NonNull String servicePackageName) { 280 LogMaker log = (new LogMaker( 281 MetricsEvent.AUTOFILL_DATA_SAVE_REQUEST)) 282 .setType(MetricsEvent.TYPE_SUCCESS) 283 .setPackageName(mPackageName) 284 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE, servicePackageName); 285 mMetricsLogger.write(log); 286 287 // Nothing left to do... 288 removeSelf(); 289 } 290 291 // FillServiceCallbacks 292 @Override 293 public void onSaveRequestFailure(@Nullable CharSequence message, 294 @NonNull String servicePackageName) { 295 LogMaker log = (new LogMaker( 296 MetricsEvent.AUTOFILL_DATA_SAVE_REQUEST)) 297 .setType(MetricsEvent.TYPE_FAILURE) 298 .setPackageName(mPackageName) 299 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE, servicePackageName); 300 mMetricsLogger.write(log); 301 302 getUiForShowing().showError(message); 303 removeSelf(); 304 } 305 306 // FillServiceCallbacks 307 @Override 308 public void authenticate(IntentSender intent, Bundle extras) { 309 final Intent fillInIntent; 310 synchronized (mLock) { 311 fillInIntent = createAuthFillInIntent(mStructure, extras); 312 } 313 mHandlerCaller.getHandler().post(() -> startAuthentication(intent, fillInIntent)); 314 } 315 316 // FillServiceCallbacks 317 @Override 318 public void onServiceDied(RemoteFillService service) { 319 // TODO(b/33197203): implement 320 } 321 322 // AutoFillUiCallback 323 @Override 324 public void fill(Dataset dataset) { 325 mHandlerCaller.getHandler().post(() -> autoFill(dataset)); 326 } 327 328 // AutoFillUiCallback 329 @Override 330 public void save() { 331 mHandlerCaller.getHandler() 332 .obtainMessage(AutofillManagerServiceImpl.MSG_SERVICE_SAVE, id, 0) 333 .sendToTarget(); 334 } 335 336 // AutoFillUiCallback 337 @Override 338 public void cancelSave() { 339 mHandlerCaller.getHandler().post(() -> removeSelf()); 340 } 341 342 // AutoFillUiCallback 343 @Override 344 public void requestShowFillUi(AutofillId id, int width, int height, 345 IAutofillWindowPresenter presenter) { 346 synchronized (mLock) { 347 if (id.equals(mCurrentViewId)) { 348 try { 349 final ViewState view = mViewStates.get(id); 350 mClient.requestShowFillUi(this.id, mWindowToken, id, width, height, 351 view.getVirtualBounds(), presenter); 352 } catch (RemoteException e) { 353 Slog.e(TAG, "Error requesting to show fill UI", e); 354 } 355 } else { 356 if (DEBUG) { 357 Slog.d(TAG, "Do not show full UI on " + id + " as it is not the current view (" 358 + mCurrentViewId + ") anymore"); 359 } 360 } 361 } 362 } 363 364 // AutoFillUiCallback 365 @Override 366 public void requestHideFillUi(AutofillId id) { 367 synchronized (mLock) { 368 try { 369 mClient.requestHideFillUi(this.id, mWindowToken, id); 370 } catch (RemoteException e) { 371 Slog.e(TAG, "Error requesting to hide fill UI", e); 372 } 373 } 374 } 375 376 // AutoFillUiCallback 377 @Override 378 public void startIntentSender(IntentSender intentSender) { 379 synchronized (mLock) { 380 removeSelfLocked(); 381 } 382 mHandlerCaller.getHandler().post(() -> { 383 try { 384 synchronized (mLock) { 385 mClient.startIntentSender(intentSender); 386 } 387 } catch (RemoteException e) { 388 Slog.e(TAG, "Error launching auth intent", e); 389 } 390 }); 391 } 392 393 public void setAuthenticationResultLocked(Bundle data) { 394 if ((mResponseWaitingAuth == null && mDatasetWaitingAuth == null) || data == null) { 395 removeSelf(); 396 } else { 397 final Parcelable result = data.getParcelable( 398 AutofillManager.EXTRA_AUTHENTICATION_RESULT); 399 if (result instanceof FillResponse) { 400 mMetricsLogger.action(MetricsEvent.AUTOFILL_AUTHENTICATED, mPackageName); 401 final int requestIndex = mResponses.indexOfValue(mResponseWaitingAuth); 402 mResponseWaitingAuth = null; 403 if (requestIndex >= 0) { 404 final int requestId = mResponses.keyAt(requestIndex); 405 processResponseLocked((FillResponse) result, requestId); 406 } else { 407 Slog.e(TAG, "Error cannot find id for auth response"); 408 } 409 } else if (result instanceof Dataset) { 410 final Dataset dataset = (Dataset) result; 411 for (int i = 0; i < mResponses.size(); i++) { 412 final FillResponse response = mResponses.valueAt(i); 413 final int index = response.getDatasets().indexOf(mDatasetWaitingAuth); 414 if (index >= 0) { 415 response.getDatasets().set(index, dataset); 416 mDatasetWaitingAuth = null; 417 autoFill(dataset); 418 resetViewStatesLocked(dataset, ViewState.STATE_WAITING_DATASET_AUTH); 419 return; 420 } 421 } 422 } 423 } 424 } 425 426 public void setHasCallback(boolean hasIt) { 427 mHasCallback = hasIt; 428 } 429 430 public void setStructureLocked(AssistStructure structure) { 431 mStructure = structure; 432 } 433 434 /** 435 * Shows the save UI, when session can be saved. 436 * 437 * @return {@code true} if session is done, or {@code false} if it's pending user action. 438 */ 439 public boolean showSaveLocked() { 440 if (mStructure == null) { 441 Slog.wtf(TAG, "showSaveLocked(): no mStructure"); 442 return true; 443 } 444 if (mResponses == null) { 445 // Happens when the activity / session was finished before the service replied, or 446 // when the service cannot autofill it (and returned a null response). 447 if (DEBUG) { 448 Slog.d(TAG, "showSaveLocked(): no responses on session"); 449 } 450 return true; 451 } 452 453 final int lastResponseIdx = getLastResponseIndex(); 454 if (lastResponseIdx < 0) { 455 Slog.d(TAG, "showSaveLocked(): mResponses=" + mResponses 456 + ", mViewStates=" + mViewStates); 457 return true; 458 } 459 460 final FillResponse response = mResponses.valueAt(lastResponseIdx); 461 final SaveInfo saveInfo = response.getSaveInfo(); 462 if (DEBUG) { 463 Slog.d(TAG, "showSaveLocked(): mResponses=" + mResponses 464 + ", mViewStates=" + mViewStates); 465 } 466 467 /* 468 * The Save dialog is only shown if all conditions below are met: 469 * 470 * - saveInfo is not null 471 * - autofillValue of all required ids is not null 472 * - autofillValue of at least one id (required or optional) has changed. 473 */ 474 475 if (saveInfo == null) { 476 return true; 477 } 478 479 final AutofillId[] requiredIds = saveInfo.getRequiredIds(); 480 if (requiredIds == null || requiredIds.length == 0) { 481 Slog.w(TAG, "showSaveLocked(): no required ids on saveInfo"); 482 return true; 483 } 484 485 boolean allRequiredAreNotEmpty = true; 486 boolean atLeastOneChanged = false; 487 for (int i = 0; i < requiredIds.length; i++) { 488 final AutofillId id = requiredIds[i]; 489 final ViewState viewState = mViewStates.get(id); 490 if (viewState == null) { 491 Slog.w(TAG, "showSaveLocked(): no ViewState for required " + id); 492 allRequiredAreNotEmpty = false; 493 break; 494 } 495 496 final AutofillValue currentValue = viewState.getCurrentValue(); 497 if (currentValue == null || currentValue.isEmpty()) { 498 if (DEBUG) { 499 Slog.d(TAG, "showSaveLocked(): empty value for required " + id ); 500 } 501 allRequiredAreNotEmpty = false; 502 break; 503 } 504 final AutofillValue filledValue = viewState.getAutofilledValue(); 505 506 if (!currentValue.equals(filledValue)) { 507 if (DEBUG) { 508 Slog.d(TAG, "showSaveLocked(): found a change on required " + id + ": " 509 + filledValue + " => " + currentValue); 510 } 511 atLeastOneChanged = true; 512 } 513 } 514 515 final AutofillId[] optionalIds = saveInfo.getOptionalIds(); 516 if (allRequiredAreNotEmpty) { 517 if (!atLeastOneChanged && optionalIds != null) { 518 // No change on required ids yet, look for changes on optional ids. 519 for (int i = 0; i < optionalIds.length; i++) { 520 final AutofillId id = optionalIds[i]; 521 final ViewState viewState = mViewStates.get(id); 522 if (viewState == null) { 523 Slog.w(TAG, "showSaveLocked(): no ViewState for optional " + id); 524 continue; 525 } 526 if ((viewState.getState() & ViewState.STATE_CHANGED) != 0) { 527 final AutofillValue currentValue = viewState.getCurrentValue(); 528 final AutofillValue filledValue = viewState.getAutofilledValue(); 529 if (currentValue != null && !currentValue.equals(filledValue)) { 530 if (DEBUG) { 531 Slog.d(TAG, "finishSessionLocked(): found a change on optional " 532 + id + ": " + filledValue + " => " + currentValue); 533 } 534 atLeastOneChanged = true; 535 break; 536 } 537 } 538 } 539 } 540 if (atLeastOneChanged) { 541 getUiForShowing().showSaveUi(mService.getServiceLabel(), saveInfo, mPackageName); 542 return false; 543 } 544 } 545 // Nothing changed... 546 if (DEBUG) { 547 Slog.d(TAG, "showSaveLocked(): with no changes, comes no responsibilities." 548 + "allRequiredAreNotNull=" + allRequiredAreNotEmpty 549 + ", atLeastOneChanged=" + atLeastOneChanged); 550 } 551 return true; 552 } 553 554 /** 555 * Calls service when user requested save. 556 */ 557 void callSaveLocked() { 558 if (DEBUG) { 559 Slog.d(TAG, "callSaveLocked(): mViewStates=" + mViewStates); 560 } 561 562 for (Entry<AutofillId, ViewState> entry : mViewStates.entrySet()) { 563 final AutofillValue value = entry.getValue().getCurrentValue(); 564 if (value == null) { 565 if (VERBOSE) { 566 Slog.v(TAG, "callSaveLocked(): skipping " + entry.getKey()); 567 } 568 continue; 569 } 570 final AutofillId id = entry.getKey(); 571 final ViewNode node = findViewNodeByIdLocked(id); 572 if (node == null) { 573 Slog.w(TAG, "callSaveLocked(): did not find node with id " + id); 574 continue; 575 } 576 if (VERBOSE) { 577 Slog.v(TAG, "callSaveLocked(): updating " + id + " to " + value); 578 } 579 580 node.updateAutofillValue(value); 581 } 582 583 // Sanitize structure before it's sent to service. 584 mStructure.sanitizeForParceling(false); 585 586 if (VERBOSE) { 587 Slog.v(TAG, "Dumping " + mStructure + " before calling service.save()"); 588 mStructure.dump(); 589 } 590 591 // TODO(b/33197203): Implement partitioning properly 592 final int lastResponseIdx = getLastResponseIndex(); 593 final int requestId = mResponses.keyAt(lastResponseIdx); 594 final FillContext fillContext = new FillContext(requestId, mStructure); 595 final ArrayList fillContexts = new ArrayList(1); 596 fillContexts.add(fillContext); 597 598 final SaveRequest saveRequest = new SaveRequest(fillContexts, mClientState); 599 mRemoteFillService.onSaveRequest(saveRequest); 600 } 601 602 void updateLocked(AutofillId id, Rect virtualBounds, AutofillValue value, int flags) { 603 ViewState viewState = mViewStates.get(id); 604 605 if (viewState == null) { 606 if ((flags & (FLAG_START_SESSION | FLAG_VALUE_CHANGED)) != 0) { 607 if (DEBUG) { 608 Slog.d(TAG, "Creating viewState for " + id + " on " + getFlagAsString(flags)); 609 } 610 viewState = new ViewState(this, id, value, this, ViewState.STATE_INITIAL); 611 mViewStates.put(id, viewState); 612 } else if (mStructure != null && (flags & FLAG_VIEW_ENTERED) != 0) { 613 viewState = startPartitionLocked(id, value); 614 } else { 615 if (VERBOSE) Slog.v(TAG, "Ignored " + getFlagAsString(flags) + " for " + id); 616 return; 617 } 618 } 619 620 if ((flags & FLAG_START_SESSION) != 0) { 621 // View is triggering autofill. 622 mCurrentViewId = viewState.id; 623 viewState.update(value, virtualBounds); 624 viewState.setState(ViewState.STATE_STARTED_SESSION); 625 return; 626 } 627 628 if ((flags & FLAG_VALUE_CHANGED) != 0) { 629 if (value != null && !value.equals(viewState.getCurrentValue())) { 630 // Always update the internal state. 631 viewState.setCurrentValue(value); 632 633 // Must check if this update was caused by autofilling the view, in which 634 // case we just update the value, but not the UI. 635 final AutofillValue filledValue = viewState.getAutofilledValue(); 636 if (value.equals(filledValue)) { 637 return; 638 } 639 // Update the internal state... 640 viewState.setState(ViewState.STATE_CHANGED); 641 642 //..and the UI 643 if (value.isText()) { 644 getUiForShowing().filterFillUi(value.getTextValue().toString()); 645 } else { 646 getUiForShowing().filterFillUi(null); 647 } 648 } 649 650 return; 651 } 652 653 if ((flags & FLAG_VIEW_ENTERED) != 0) { 654 // Remove the UI if the ViewState has changed. 655 if (mCurrentViewId != viewState.id) { 656 mUi.hideFillUi(mCurrentViewId != null ? mCurrentViewId : null); 657 mCurrentViewId = viewState.id; 658 } 659 660 // If the ViewState is ready to be displayed, onReady() will be called. 661 viewState.update(value, virtualBounds); 662 663 return; 664 } 665 666 if ((flags & FLAG_VIEW_EXITED) != 0) { 667 if (mCurrentViewId == viewState.id) { 668 mUi.hideFillUi(viewState.id); 669 mCurrentViewId = null; 670 } 671 return; 672 } 673 674 Slog.w(TAG, "updateLocked(): unknown flags " + flags + ": " + getFlagAsString(flags)); 675 } 676 677 private ViewState startPartitionLocked(AutofillId id, AutofillValue value) { 678 // TODO(b/33197203 , b/35707731): temporary workaround until partitioning supports auth 679 if (mResponseWaitingAuth != null) { 680 final ViewState viewState = 681 new ViewState(this, id, value, this, ViewState.STATE_WAITING_RESPONSE_AUTH); 682 mViewStates.put(id, viewState); 683 return viewState; 684 } 685 if (DEBUG) { 686 Slog.d(TAG, "Starting partition for view id " + id); 687 } 688 final ViewState newViewState = 689 new ViewState(this, id, value, this,ViewState.STATE_STARTED_PARTITION); 690 mViewStates.put(id, newViewState); 691 692 // Must update value of nodes so: 693 // - proper node is focused 694 // - autofillValue is sent back to service when it was previously autofilled 695 for (int i = 0; i < mViewStates.size(); i++) { 696 final ViewState viewState = mViewStates.valueAt(i); 697 698 final ViewNode node = findViewNodeByIdLocked(viewState.id); 699 if (node == null) { 700 Slog.w(TAG, "startPartitionLocked(): no node for " + viewState.id); 701 continue; 702 } 703 704 final AutofillValue initialValue = viewState.getInitialValue(); 705 final AutofillValue filledValue = viewState.getAutofilledValue(); 706 final AutofillOverlay overlay = new AutofillOverlay(); 707 if (filledValue != null && !filledValue.equals(initialValue)) { 708 overlay.value = filledValue; 709 } 710 overlay.focused = id.equals(viewState.id); 711 node.setAutofillOverlay(overlay); 712 } 713 714 FillRequest request = new FillRequest(mStructure, mClientState, 0); 715 mRemoteFillService.onFillRequest(request); 716 717 return newViewState; 718 } 719 720 @Override 721 public void onFillReady(FillResponse response, AutofillId filledId, 722 @Nullable AutofillValue value) { 723 String filterText = null; 724 if (value != null && value.isText()) { 725 filterText = value.getTextValue().toString(); 726 } 727 728 getUiForShowing().showFillUi(filledId, response, filterText, mPackageName); 729 } 730 731 String getFlagAsString(int flag) { 732 return DebugUtils.flagsToString(AutofillManager.class, "FLAG_", flag); 733 } 734 735 private void notifyUnavailableToClient() { 736 synchronized (mLock) { 737 if (mCurrentViewId == null) { 738 // TODO(b/33197203): temporary sanity check; should never happen 739 Slog.w(TAG, "notifyUnavailable(): mCurrentViewId is null"); 740 return; 741 } 742 if (!mHasCallback) return; 743 try { 744 mClient.notifyNoFillUi(id, mWindowToken, mCurrentViewId); 745 } catch (RemoteException e) { 746 Slog.e(TAG, "Error notifying client no fill UI: windowToken=" + mWindowToken 747 + " id=" + mCurrentViewId, e); 748 } 749 } 750 } 751 752 private void processResponseLocked(FillResponse response, int requestId) { 753 if (DEBUG) { 754 Slog.d(TAG, "processResponseLocked(mCurrentViewId=" + mCurrentViewId + "):" + response); 755 } 756 757 if (mResponses == null) { 758 mResponses = new SparseArray<>(4); 759 } 760 mResponses.put(requestId, response); 761 if (response != null) { 762 mClientState = response.getClientState(); 763 } 764 765 setViewStatesLocked(response, ViewState.STATE_FILLABLE); 766 767 if (mCurrentViewId == null) { 768 return; 769 } 770 771 if ((mFlags & FLAG_MANUAL_REQUEST) != 0 && response.getDatasets() != null 772 && response.getDatasets().size() == 1) { 773 Slog.d(TAG, "autofilling manual request directly"); 774 autoFill(response.getDatasets().get(0)); 775 return; 776 } 777 778 // Updates the UI, if necessary. 779 final ViewState currentView = mViewStates.get(mCurrentViewId); 780 currentView.maybeCallOnFillReady(); 781 } 782 783 /** 784 * Sets the state of all views in the given response. 785 */ 786 private void setViewStatesLocked(FillResponse response, int state) { 787 final ArrayList<Dataset> datasets = response.getDatasets(); 788 if (datasets != null) { 789 for (int i = 0; i < datasets.size(); i++) { 790 final Dataset dataset = datasets.get(i); 791 if (dataset == null) { 792 Slog.w(TAG, "Ignoring null dataset on " + datasets); 793 continue; 794 } 795 setViewStatesLocked(response, dataset, state); 796 } 797 } 798 } 799 800 /** 801 * Sets the state of all views in the given dataset and response. 802 */ 803 private void setViewStatesLocked(@Nullable FillResponse response, @NonNull Dataset dataset, 804 int state) { 805 final ArrayList<AutofillId> ids = dataset.getFieldIds(); 806 final ArrayList<AutofillValue> values = dataset.getFieldValues(); 807 for (int j = 0; j < ids.size(); j++) { 808 final AutofillId id = ids.get(j); 809 ViewState viewState = mViewStates.get(id); 810 if (viewState != null) { 811 viewState.setState(state); 812 } else { 813 viewState = new ViewState(this, id, null, this, state); 814 if (DEBUG) { // TODO(b/33197203): change to VERBOSE once stable 815 Slog.d(TAG, "Adding autofillable view with id " + id + " and state " + state); 816 } 817 mViewStates.put(id, viewState); 818 } 819 if ((state & ViewState.STATE_AUTOFILLED) != 0) { 820 viewState.setAutofilledValue(values.get(j)); 821 } 822 823 if (response != null) { 824 viewState.setResponse(response); 825 } 826 } 827 } 828 829 /** 830 * Resets the given state from all existing views in the given dataset. 831 */ 832 private void resetViewStatesLocked(@NonNull Dataset dataset, int state) { 833 final ArrayList<AutofillId> ids = dataset.getFieldIds(); 834 for (int j = 0; j < ids.size(); j++) { 835 final AutofillId id = ids.get(j); 836 final ViewState viewState = mViewStates.get(id); 837 if (viewState != null) { 838 viewState.resetState(state); 839 } 840 } 841 } 842 843 void autoFill(Dataset dataset) { 844 synchronized (mLock) { 845 // Autofill it directly... 846 if (dataset.getAuthentication() == null) { 847 autoFillApp(dataset); 848 return; 849 } 850 851 // ...or handle authentication. 852 // TODO(b/33197203 , b/35707731): make sure it's ignored if there is one already 853 mDatasetWaitingAuth = dataset; 854 setViewStatesLocked(null, dataset, ViewState.STATE_WAITING_DATASET_AUTH); 855 final Intent fillInIntent = createAuthFillInIntent(mStructure, null); 856 startAuthentication(dataset.getAuthentication(), fillInIntent); 857 } 858 } 859 860 CharSequence getServiceName() { 861 return mService.getServiceName(); 862 } 863 864 FillResponse getResponseWaitingAuth() { 865 return mResponseWaitingAuth; 866 } 867 868 private Intent createAuthFillInIntent(AssistStructure structure, Bundle extras) { 869 final Intent fillInIntent = new Intent(); 870 fillInIntent.putExtra(AutofillManager.EXTRA_ASSIST_STRUCTURE, structure); 871 if (extras != null) { 872 fillInIntent.putExtra(AutofillManager.EXTRA_DATA_EXTRAS, extras); 873 } 874 return fillInIntent; 875 } 876 877 private void startAuthentication(IntentSender intent, Intent fillInIntent) { 878 try { 879 synchronized (mLock) { 880 mClient.authenticate(id, intent, fillInIntent); 881 } 882 } catch (RemoteException e) { 883 Slog.e(TAG, "Error launching auth intent", e); 884 } 885 } 886 887 void dumpLocked(String prefix, PrintWriter pw) { 888 pw.print(prefix); pw.print("id: "); pw.println(id); 889 pw.print(prefix); pw.print("uid: "); pw.println(uid); 890 pw.print(prefix); pw.print("mActivityToken: "); pw.println(mActivityToken); 891 pw.print(prefix); pw.print("mFlags: "); pw.println(mFlags); 892 pw.print(prefix); pw.print("mResponses: "); pw.println(mResponses); 893 pw.print(prefix); pw.print("mResponseWaitingAuth: "); pw.println(mResponseWaitingAuth); 894 pw.print(prefix); pw.print("mDatasetWaitingAuth: "); pw.println(mDatasetWaitingAuth); 895 pw.print(prefix); pw.print("mCurrentViewId: "); pw.println(mCurrentViewId); 896 pw.print(prefix); pw.print("mViewStates size: "); pw.println(mViewStates.size()); 897 final String prefix2 = prefix + " "; 898 for (Map.Entry<AutofillId, ViewState> entry : mViewStates.entrySet()) { 899 pw.print(prefix); pw.print("State for id "); pw.println(entry.getKey()); 900 entry.getValue().dump(prefix2, pw); 901 } 902 if (VERBOSE) { 903 pw.print(prefix); pw.print("mStructure: " ); 904 // TODO(b/33197203): add method do dump AssistStructure on pw 905 if (mStructure != null) { 906 pw.println("look at logcat" ); 907 mStructure.dump(); // dumps to logcat 908 } else { 909 pw.println("null"); 910 } 911 } 912 pw.print(prefix); pw.print("mHasCallback: "); pw.println(mHasCallback); 913 pw.print(prefix); pw.print("mClientState: "); pw.println( 914 Helper.bundleToString(mClientState)); 915 mRemoteFillService.dump(prefix, pw); 916 } 917 918 void autoFillApp(Dataset dataset) { 919 synchronized (mLock) { 920 try { 921 if (DEBUG) { 922 Slog.d(TAG, "autoFillApp(): the buck is on the app: " + dataset); 923 } 924 mClient.autofill(id, mWindowToken, dataset.getFieldIds(), dataset.getFieldValues()); 925 setViewStatesLocked(null, dataset, ViewState.STATE_AUTOFILLED); 926 } catch (RemoteException e) { 927 Slog.w(TAG, "Error autofilling activity: " + e); 928 } 929 } 930 } 931 932 private AutoFillUI getUiForShowing() { 933 synchronized (mLock) { 934 mUi.setCallback(this); 935 return mUi; 936 } 937 } 938 939 private ViewNode findViewNodeByIdLocked(AutofillId id) { 940 final int size = mStructure.getWindowNodeCount(); 941 for (int i = 0; i < size; i++) { 942 final WindowNode window = mStructure.getWindowNodeAt(i); 943 final ViewNode root = window.getRootViewNode(); 944 if (id.equals(root.getAutofillId())) { 945 return root; 946 } 947 final ViewNode child = findViewNodeByIdLocked(root, id); 948 if (child != null) { 949 return child; 950 } 951 } 952 return null; 953 } 954 955 private ViewNode findViewNodeByIdLocked(ViewNode parent, AutofillId id) { 956 final int childrenSize = parent.getChildCount(); 957 if (childrenSize > 0) { 958 for (int i = 0; i < childrenSize; i++) { 959 final ViewNode child = parent.getChildAt(i); 960 if (id.equals(child.getAutofillId())) { 961 return child; 962 } 963 final ViewNode grandChild = findViewNodeByIdLocked(child, id); 964 if (grandChild != null && id.equals(grandChild.getAutofillId())) { 965 return grandChild; 966 } 967 } 968 } 969 return null; 970 } 971 972 void destroyLocked() { 973 mRemoteFillService.destroy(); 974 mUi.setCallback(null); 975 mMetricsLogger.action(MetricsEvent.AUTOFILL_SESSION_FINISHED, mPackageName); 976 } 977 978 void removeSelf() { 979 synchronized (mLock) { 980 removeSelfLocked(); 981 } 982 } 983 984 void removeSelfLocked() { 985 if (VERBOSE) { 986 Slog.v(TAG, "removeSelfLocked()"); 987 } 988 destroyLocked(); 989 mService.removeSessionLocked(id); 990 } 991 992 private int getLastResponseIndex() { 993 // The response ids are monotonically increasing so 994 // we just find the largest id which is the last. We 995 // do not rely on the internal ordering in sparse 996 // array to avoid - wow this stopped working!? 997 int lastResponseIdx = -1; 998 int lastResponseId = -1; 999 final int responseCount = mResponses.size(); 1000 for (int i = 0; i < responseCount; i++) { 1001 if (mResponses.keyAt(i) > lastResponseId) { 1002 lastResponseIdx = i; 1003 } 1004 } 1005 return lastResponseIdx; 1006 } 1007} 1008