Session.java revision 013efe173e56612a910ebd8576480ce4ef005e3c
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(mWindowToken, id, width, height, 351 view.getVirtualBounds(), 352 presenter); 353 } catch (RemoteException e) { 354 Slog.e(TAG, "Error requesting to show fill UI", e); 355 } 356 } else { 357 if (DEBUG) { 358 Slog.d(TAG, "Do not show full UI on " + id + " as it is not the current view (" 359 + mCurrentViewId + ") anymore"); 360 } 361 } 362 } 363 } 364 365 // AutoFillUiCallback 366 @Override 367 public void requestHideFillUi(AutofillId id) { 368 synchronized (mLock) { 369 try { 370 mClient.requestHideFillUi(mWindowToken, id); 371 } catch (RemoteException e) { 372 Slog.e(TAG, "Error requesting to hide fill UI", e); 373 } 374 } 375 } 376 377 // AutoFillUiCallback 378 @Override 379 public void startIntentSender(IntentSender intentSender) { 380 synchronized (mLock) { 381 removeSelfLocked(); 382 } 383 mHandlerCaller.getHandler().post(() -> { 384 try { 385 synchronized (mLock) { 386 mClient.startIntentSender(intentSender); 387 } 388 } catch (RemoteException e) { 389 Slog.e(TAG, "Error launching auth intent", e); 390 } 391 }); 392 } 393 394 public void setAuthenticationResultLocked(Bundle data) { 395 if ((mResponseWaitingAuth == null && mDatasetWaitingAuth == null) || data == null) { 396 removeSelf(); 397 } else { 398 final Parcelable result = data.getParcelable( 399 AutofillManager.EXTRA_AUTHENTICATION_RESULT); 400 if (result instanceof FillResponse) { 401 mMetricsLogger.action(MetricsEvent.AUTOFILL_AUTHENTICATED, mPackageName); 402 final int requestIndex = mResponses.indexOfValue(mResponseWaitingAuth); 403 mResponseWaitingAuth = null; 404 if (requestIndex >= 0) { 405 final int requestId = mResponses.keyAt(requestIndex); 406 processResponseLocked((FillResponse) result, requestId); 407 } else { 408 Slog.e(TAG, "Error cannot find id for auth response"); 409 } 410 } else if (result instanceof Dataset) { 411 final Dataset dataset = (Dataset) result; 412 for (int i = 0; i < mResponses.size(); i++) { 413 final FillResponse response = mResponses.valueAt(i); 414 final int index = response.getDatasets().indexOf(mDatasetWaitingAuth); 415 if (index >= 0) { 416 response.getDatasets().set(index, dataset); 417 mDatasetWaitingAuth = null; 418 autoFill(dataset); 419 resetViewStatesLocked(dataset, ViewState.STATE_WAITING_DATASET_AUTH); 420 return; 421 } 422 } 423 } 424 } 425 } 426 427 public void setHasCallback(boolean hasIt) { 428 mHasCallback = hasIt; 429 } 430 431 public void setStructureLocked(AssistStructure structure) { 432 mStructure = structure; 433 } 434 435 /** 436 * Shows the save UI, when session can be saved. 437 * 438 * @return {@code true} if session is done, or {@code false} if it's pending user action. 439 */ 440 public boolean showSaveLocked() { 441 if (mStructure == null) { 442 Slog.wtf(TAG, "showSaveLocked(): no mStructure"); 443 return true; 444 } 445 if (mResponses == null) { 446 // Happens when the activity / session was finished before the service replied, or 447 // when the service cannot autofill it (and returned a null response). 448 if (DEBUG) { 449 Slog.d(TAG, "showSaveLocked(): no responses on session"); 450 } 451 return true; 452 } 453 454 final int lastResponseIdx = getLastResponseIndex(); 455 if (lastResponseIdx < 0) { 456 Slog.d(TAG, "showSaveLocked(): mResponses=" + mResponses 457 + ", mViewStates=" + mViewStates); 458 return true; 459 } 460 461 final FillResponse response = mResponses.valueAt(lastResponseIdx); 462 final SaveInfo saveInfo = response.getSaveInfo(); 463 if (DEBUG) { 464 Slog.d(TAG, "showSaveLocked(): mResponses=" + mResponses 465 + ", mViewStates=" + mViewStates); 466 } 467 468 /* 469 * The Save dialog is only shown if all conditions below are met: 470 * 471 * - saveInfo is not null 472 * - autofillValue of all required ids is not null 473 * - autofillValue of at least one id (required or optional) has changed. 474 */ 475 476 if (saveInfo == null) { 477 return true; 478 } 479 480 final AutofillId[] requiredIds = saveInfo.getRequiredIds(); 481 if (requiredIds == null || requiredIds.length == 0) { 482 Slog.w(TAG, "showSaveLocked(): no required ids on saveInfo"); 483 return true; 484 } 485 486 boolean allRequiredAreNotEmpty = true; 487 boolean atLeastOneChanged = false; 488 for (int i = 0; i < requiredIds.length; i++) { 489 final AutofillId id = requiredIds[i]; 490 final ViewState viewState = mViewStates.get(id); 491 if (viewState == null) { 492 Slog.w(TAG, "showSaveLocked(): no ViewState for required " + id); 493 allRequiredAreNotEmpty = false; 494 break; 495 } 496 497 final AutofillValue currentValue = viewState.getCurrentValue(); 498 if (currentValue == null || currentValue.isEmpty()) { 499 if (DEBUG) { 500 Slog.d(TAG, "showSaveLocked(): empty value for required " + id ); 501 } 502 allRequiredAreNotEmpty = false; 503 break; 504 } 505 final AutofillValue filledValue = viewState.getAutofilledValue(); 506 507 if (!currentValue.equals(filledValue)) { 508 if (DEBUG) { 509 Slog.d(TAG, "showSaveLocked(): found a change on required " + id + ": " 510 + filledValue + " => " + currentValue); 511 } 512 atLeastOneChanged = true; 513 } 514 } 515 516 final AutofillId[] optionalIds = saveInfo.getOptionalIds(); 517 if (allRequiredAreNotEmpty) { 518 if (!atLeastOneChanged && optionalIds != null) { 519 // No change on required ids yet, look for changes on optional ids. 520 for (int i = 0; i < optionalIds.length; i++) { 521 final AutofillId id = optionalIds[i]; 522 final ViewState viewState = mViewStates.get(id); 523 if (viewState == null) { 524 Slog.w(TAG, "showSaveLocked(): no ViewState for optional " + id); 525 continue; 526 } 527 if ((viewState.getState() & ViewState.STATE_CHANGED) != 0) { 528 final AutofillValue currentValue = viewState.getCurrentValue(); 529 final AutofillValue filledValue = viewState.getAutofilledValue(); 530 if (currentValue != null && !currentValue.equals(filledValue)) { 531 if (DEBUG) { 532 Slog.d(TAG, "finishSessionLocked(): found a change on optional " 533 + id + ": " + filledValue + " => " + currentValue); 534 } 535 atLeastOneChanged = true; 536 break; 537 } 538 } 539 } 540 } 541 if (atLeastOneChanged) { 542 getUiForShowing().showSaveUi(mService.getServiceLabel(), saveInfo, mPackageName); 543 return false; 544 } 545 } 546 // Nothing changed... 547 if (DEBUG) { 548 Slog.d(TAG, "showSaveLocked(): with no changes, comes no responsibilities." 549 + "allRequiredAreNotNull=" + allRequiredAreNotEmpty 550 + ", atLeastOneChanged=" + atLeastOneChanged); 551 } 552 return true; 553 } 554 555 /** 556 * Calls service when user requested save. 557 */ 558 void callSaveLocked() { 559 if (DEBUG) { 560 Slog.d(TAG, "callSaveLocked(): mViewStates=" + mViewStates); 561 } 562 563 for (Entry<AutofillId, ViewState> entry : mViewStates.entrySet()) { 564 final AutofillValue value = entry.getValue().getCurrentValue(); 565 if (value == null) { 566 if (VERBOSE) { 567 Slog.v(TAG, "callSaveLocked(): skipping " + entry.getKey()); 568 } 569 continue; 570 } 571 final AutofillId id = entry.getKey(); 572 final ViewNode node = findViewNodeByIdLocked(id); 573 if (node == null) { 574 Slog.w(TAG, "callSaveLocked(): did not find node with id " + id); 575 continue; 576 } 577 if (VERBOSE) { 578 Slog.v(TAG, "callSaveLocked(): updating " + id + " to " + value); 579 } 580 581 node.updateAutofillValue(value); 582 } 583 584 // Sanitize structure before it's sent to service. 585 mStructure.sanitizeForParceling(false); 586 587 if (VERBOSE) { 588 Slog.v(TAG, "Dumping " + mStructure + " before calling service.save()"); 589 mStructure.dump(); 590 } 591 592 // TODO(b/33197203): Implement partitioning properly 593 final int lastResponseIdx = getLastResponseIndex(); 594 final int requestId = mResponses.keyAt(lastResponseIdx); 595 final FillContext fillContext = new FillContext(requestId, mStructure); 596 final ArrayList fillContexts = new ArrayList(1); 597 fillContexts.add(fillContext); 598 599 final SaveRequest saveRequest = new SaveRequest(fillContexts, mClientState); 600 mRemoteFillService.onSaveRequest(saveRequest); 601 } 602 603 void updateLocked(AutofillId id, Rect virtualBounds, AutofillValue value, int flags) { 604 ViewState viewState = mViewStates.get(id); 605 606 if (viewState == null) { 607 if ((flags & (FLAG_START_SESSION | FLAG_VALUE_CHANGED)) != 0) { 608 if (DEBUG) { 609 Slog.d(TAG, "Creating viewState for " + id + " on " + getFlagAsString(flags)); 610 } 611 viewState = new ViewState(this, id, value, this, ViewState.STATE_INITIAL); 612 mViewStates.put(id, viewState); 613 } else if ((flags & FLAG_VIEW_ENTERED) != 0) { 614 viewState = startPartitionLocked(id, value); 615 } else { 616 if (VERBOSE) Slog.v(TAG, "Ignored " + getFlagAsString(flags) + " for " + id); 617 return; 618 } 619 } 620 621 if ((flags & FLAG_START_SESSION) != 0) { 622 // View is triggering autofill. 623 mCurrentViewId = viewState.id; 624 viewState.update(value, virtualBounds); 625 viewState.setState(ViewState.STATE_STARTED_SESSION); 626 return; 627 } 628 629 if ((flags & FLAG_VALUE_CHANGED) != 0) { 630 if (value != null && !value.equals(viewState.getCurrentValue())) { 631 // Always update the internal state. 632 viewState.setCurrentValue(value); 633 634 // Must check if this update was caused by autofilling the view, in which 635 // case we just update the value, but not the UI. 636 final AutofillValue filledValue = viewState.getAutofilledValue(); 637 if (value.equals(filledValue)) { 638 return; 639 } 640 // Update the internal state... 641 viewState.setState(ViewState.STATE_CHANGED); 642 643 //..and the UI 644 if (value.isText()) { 645 getUiForShowing().filterFillUi(value.getTextValue().toString()); 646 } else { 647 getUiForShowing().filterFillUi(null); 648 } 649 } 650 651 return; 652 } 653 654 if ((flags & FLAG_VIEW_ENTERED) != 0) { 655 // Remove the UI if the ViewState has changed. 656 if (mCurrentViewId != viewState.id) { 657 mUi.hideFillUi(mCurrentViewId != null ? mCurrentViewId : null); 658 mCurrentViewId = viewState.id; 659 } 660 661 // If the ViewState is ready to be displayed, onReady() will be called. 662 viewState.update(value, virtualBounds); 663 664 return; 665 } 666 667 if ((flags & FLAG_VIEW_EXITED) != 0) { 668 if (mCurrentViewId == viewState.id) { 669 mUi.hideFillUi(viewState.id); 670 mCurrentViewId = null; 671 } 672 return; 673 } 674 675 Slog.w(TAG, "updateLocked(): unknown flags " + flags + ": " + getFlagAsString(flags)); 676 } 677 678 private ViewState startPartitionLocked(AutofillId id, AutofillValue value) { 679 // TODO(b/33197203 , b/35707731): temporary workaround until partitioning supports auth 680 if (mResponseWaitingAuth != null) { 681 final ViewState viewState = 682 new ViewState(this, id, value, this, ViewState.STATE_WAITING_RESPONSE_AUTH); 683 mViewStates.put(id, viewState); 684 return viewState; 685 } 686 if (DEBUG) { 687 Slog.d(TAG, "Starting partition for view id " + id); 688 } 689 final ViewState newViewState = 690 new ViewState(this, id, value, this,ViewState.STATE_STARTED_PARTITION); 691 mViewStates.put(id, newViewState); 692 693 // Must update value of nodes so: 694 // - proper node is focused 695 // - autofillValue is sent back to service when it was previously autofilled 696 for (int i = 0; i < mViewStates.size(); i++) { 697 final ViewState viewState = mViewStates.valueAt(i); 698 699 final ViewNode node = findViewNodeByIdLocked(viewState.id); 700 if (node == null) { 701 Slog.w(TAG, "startPartitionLocked(): no node for " + viewState.id); 702 continue; 703 } 704 705 final AutofillValue initialValue = viewState.getInitialValue(); 706 final AutofillValue filledValue = viewState.getAutofilledValue(); 707 final AutofillOverlay overlay = new AutofillOverlay(); 708 if (filledValue != null && !filledValue.equals(initialValue)) { 709 overlay.value = filledValue; 710 } 711 overlay.focused = id.equals(viewState.id); 712 node.setAutofillOverlay(overlay); 713 } 714 715 FillRequest request = new FillRequest(mStructure, mClientState, 0); 716 mRemoteFillService.onFillRequest(request); 717 718 return newViewState; 719 } 720 721 @Override 722 public void onFillReady(FillResponse response, AutofillId filledId, 723 @Nullable AutofillValue value) { 724 String filterText = null; 725 if (value != null && value.isText()) { 726 filterText = value.getTextValue().toString(); 727 } 728 729 getUiForShowing().showFillUi(filledId, response, filterText, mPackageName); 730 } 731 732 String getFlagAsString(int flag) { 733 return DebugUtils.flagsToString(AutofillManager.class, "FLAG_", flag); 734 } 735 736 private void notifyUnavailableToClient() { 737 synchronized (mLock) { 738 if (mCurrentViewId == null) { 739 // TODO(b/33197203): temporary sanity check; should never happen 740 Slog.w(TAG, "notifyUnavailable(): mCurrentViewId is null"); 741 return; 742 } 743 if (!mHasCallback) return; 744 try { 745 mClient.notifyNoFillUi(mWindowToken, mCurrentViewId); 746 } catch (RemoteException e) { 747 Slog.e(TAG, "Error notifying client no fill UI: windowToken=" + mWindowToken 748 + " id=" + mCurrentViewId, e); 749 } 750 } 751 } 752 753 private void processResponseLocked(FillResponse response, int requestId) { 754 if (DEBUG) { 755 Slog.d(TAG, "processResponseLocked(mCurrentViewId=" + mCurrentViewId + "):" + response); 756 } 757 758 if (mResponses == null) { 759 mResponses = new SparseArray<>(4); 760 } 761 mResponses.put(requestId, response); 762 if (response != null) { 763 mClientState = response.getClientState(); 764 } 765 766 setViewStatesLocked(response, ViewState.STATE_FILLABLE); 767 768 if (mCurrentViewId == null) { 769 return; 770 } 771 772 if ((mFlags & FLAG_MANUAL_REQUEST) != 0 && response.getDatasets() != null 773 && response.getDatasets().size() == 1) { 774 Slog.d(TAG, "autofilling manual request directly"); 775 autoFill(response.getDatasets().get(0)); 776 return; 777 } 778 779 // Updates the UI, if necessary. 780 final ViewState currentView = mViewStates.get(mCurrentViewId); 781 currentView.maybeCallOnFillReady(); 782 } 783 784 /** 785 * Sets the state of all views in the given response. 786 */ 787 private void setViewStatesLocked(FillResponse response, int state) { 788 final ArrayList<Dataset> datasets = response.getDatasets(); 789 if (datasets != null) { 790 for (int i = 0; i < datasets.size(); i++) { 791 final Dataset dataset = datasets.get(i); 792 if (dataset == null) { 793 Slog.w(TAG, "Ignoring null dataset on " + datasets); 794 continue; 795 } 796 setViewStatesLocked(response, dataset, state); 797 } 798 } 799 } 800 801 /** 802 * Sets the state of all views in the given dataset and response. 803 */ 804 private void setViewStatesLocked(@Nullable FillResponse response, @NonNull Dataset dataset, 805 int state) { 806 final ArrayList<AutofillId> ids = dataset.getFieldIds(); 807 final ArrayList<AutofillValue> values = dataset.getFieldValues(); 808 for (int j = 0; j < ids.size(); j++) { 809 final AutofillId id = ids.get(j); 810 ViewState viewState = mViewStates.get(id); 811 if (viewState != null) { 812 viewState.setState(state); 813 } else { 814 viewState = new ViewState(this, id, null, this, state); 815 if (DEBUG) { // TODO(b/33197203): change to VERBOSE once stable 816 Slog.d(TAG, "Adding autofillable view with id " + id + " and state " + state); 817 } 818 mViewStates.put(id, viewState); 819 } 820 if ((state & ViewState.STATE_AUTOFILLED) != 0) { 821 viewState.setAutofilledValue(values.get(j)); 822 } 823 824 if (response != null) { 825 viewState.setResponse(response); 826 } 827 } 828 } 829 830 /** 831 * Resets the given state from all existing views in the given dataset. 832 */ 833 private void resetViewStatesLocked(@NonNull Dataset dataset, int state) { 834 final ArrayList<AutofillId> ids = dataset.getFieldIds(); 835 for (int j = 0; j < ids.size(); j++) { 836 final AutofillId id = ids.get(j); 837 final ViewState viewState = mViewStates.get(id); 838 if (viewState != null) { 839 viewState.resetState(state); 840 } 841 } 842 } 843 844 void autoFill(Dataset dataset) { 845 synchronized (mLock) { 846 // Autofill it directly... 847 if (dataset.getAuthentication() == null) { 848 autoFillApp(dataset); 849 return; 850 } 851 852 // ...or handle authentication. 853 // TODO(b/33197203 , b/35707731): make sure it's ignored if there is one already 854 mDatasetWaitingAuth = dataset; 855 setViewStatesLocked(null, dataset, ViewState.STATE_WAITING_DATASET_AUTH); 856 final Intent fillInIntent = createAuthFillInIntent(mStructure, null); 857 startAuthentication(dataset.getAuthentication(), fillInIntent); 858 } 859 } 860 861 CharSequence getServiceName() { 862 return mService.getServiceName(); 863 } 864 865 FillResponse getResponseWaitingAuth() { 866 return mResponseWaitingAuth; 867 } 868 869 private Intent createAuthFillInIntent(AssistStructure structure, Bundle extras) { 870 final Intent fillInIntent = new Intent(); 871 fillInIntent.putExtra(AutofillManager.EXTRA_ASSIST_STRUCTURE, structure); 872 if (extras != null) { 873 fillInIntent.putExtra(AutofillManager.EXTRA_DATA_EXTRAS, extras); 874 } 875 return fillInIntent; 876 } 877 878 private void startAuthentication(IntentSender intent, Intent fillInIntent) { 879 try { 880 synchronized (mLock) { 881 mClient.authenticate(intent, fillInIntent); 882 } 883 } catch (RemoteException e) { 884 Slog.e(TAG, "Error launching auth intent", e); 885 } 886 } 887 888 void dumpLocked(String prefix, PrintWriter pw) { 889 pw.print(prefix); pw.print("id: "); pw.println(id); 890 pw.print(prefix); pw.print("uid: "); pw.println(uid); 891 pw.print(prefix); pw.print("mActivityToken: "); pw.println(mActivityToken); 892 pw.print(prefix); pw.print("mFlags: "); pw.println(mFlags); 893 pw.print(prefix); pw.print("mResponses: "); pw.println(mResponses); 894 pw.print(prefix); pw.print("mResponseWaitingAuth: "); pw.println(mResponseWaitingAuth); 895 pw.print(prefix); pw.print("mDatasetWaitingAuth: "); pw.println(mDatasetWaitingAuth); 896 pw.print(prefix); pw.print("mCurrentViewId: "); pw.println(mCurrentViewId); 897 pw.print(prefix); pw.print("mViewStates size: "); pw.println(mViewStates.size()); 898 final String prefix2 = prefix + " "; 899 for (Map.Entry<AutofillId, ViewState> entry : mViewStates.entrySet()) { 900 pw.print(prefix); pw.print("State for id "); pw.println(entry.getKey()); 901 entry.getValue().dump(prefix2, pw); 902 } 903 if (VERBOSE) { 904 pw.print(prefix); pw.print("mStructure: " ); 905 // TODO(b/33197203): add method do dump AssistStructure on pw 906 if (mStructure != null) { 907 pw.println("look at logcat" ); 908 mStructure.dump(); // dumps to logcat 909 } else { 910 pw.println("null"); 911 } 912 } 913 pw.print(prefix); pw.print("mHasCallback: "); pw.println(mHasCallback); 914 pw.print(prefix); pw.print("mClientState: "); pw.println( 915 Helper.bundleToString(mClientState)); 916 mRemoteFillService.dump(prefix, pw); 917 } 918 919 void autoFillApp(Dataset dataset) { 920 synchronized (mLock) { 921 try { 922 if (DEBUG) { 923 Slog.d(TAG, "autoFillApp(): the buck is on the app: " + dataset); 924 } 925 mClient.autofill(mWindowToken, dataset.getFieldIds(), dataset.getFieldValues()); 926 setViewStatesLocked(null, dataset, ViewState.STATE_AUTOFILLED); 927 } catch (RemoteException e) { 928 Slog.w(TAG, "Error autofilling activity: " + e); 929 } 930 } 931 } 932 933 private AutoFillUI getUiForShowing() { 934 synchronized (mLock) { 935 mUi.setCallback(this); 936 return mUi; 937 } 938 } 939 940 private ViewNode findViewNodeByIdLocked(AutofillId id) { 941 final int size = mStructure.getWindowNodeCount(); 942 for (int i = 0; i < size; i++) { 943 final WindowNode window = mStructure.getWindowNodeAt(i); 944 final ViewNode root = window.getRootViewNode(); 945 if (id.equals(root.getAutofillId())) { 946 return root; 947 } 948 final ViewNode child = findViewNodeByIdLocked(root, id); 949 if (child != null) { 950 return child; 951 } 952 } 953 return null; 954 } 955 956 private ViewNode findViewNodeByIdLocked(ViewNode parent, AutofillId id) { 957 final int childrenSize = parent.getChildCount(); 958 if (childrenSize > 0) { 959 for (int i = 0; i < childrenSize; i++) { 960 final ViewNode child = parent.getChildAt(i); 961 if (id.equals(child.getAutofillId())) { 962 return child; 963 } 964 final ViewNode grandChild = findViewNodeByIdLocked(child, id); 965 if (grandChild != null && id.equals(grandChild.getAutofillId())) { 966 return grandChild; 967 } 968 } 969 } 970 return null; 971 } 972 973 void destroyLocked() { 974 mRemoteFillService.destroy(); 975 mUi.setCallback(null); 976 mMetricsLogger.action(MetricsEvent.AUTOFILL_SESSION_FINISHED, mPackageName); 977 } 978 979 void removeSelf() { 980 synchronized (mLock) { 981 removeSelfLocked(); 982 } 983 } 984 985 void removeSelfLocked() { 986 if (VERBOSE) { 987 Slog.v(TAG, "removeSelfLocked()"); 988 } 989 destroyLocked(); 990 mService.removeSessionLocked(id); 991 } 992 993 private int getLastResponseIndex() { 994 // The response ids are monotonically increasing so 995 // we just find the largest id which is the last. We 996 // do not rely on the internal ordering in sparse 997 // array to avoid - wow this stopped working!? 998 int lastResponseIdx = -1; 999 int lastResponseId = -1; 1000 final int responseCount = mResponses.size(); 1001 for (int i = 0; i < responseCount; i++) { 1002 if (mResponses.keyAt(i) > lastResponseId) { 1003 lastResponseIdx = i; 1004 } 1005 } 1006 return lastResponseIdx; 1007 } 1008} 1009