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