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