AutofillManagerServiceImpl.java revision 1116652087c5889a98ff6d736e127fc0e97dbe37
1/* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server.autofill; 18 19import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST; 20import static android.view.autofill.AutofillManager.ACTION_START_SESSION; 21import static android.view.autofill.AutofillManager.NO_SESSION; 22 23import static com.android.server.autofill.Helper.sDebug; 24import static com.android.server.autofill.Helper.sVerbose; 25 26import android.annotation.NonNull; 27import android.annotation.Nullable; 28import android.app.ActivityManager; 29import android.app.ActivityManagerInternal; 30import android.app.AppGlobals; 31import android.app.IActivityManager; 32import android.content.ComponentName; 33import android.content.Context; 34import android.content.pm.ApplicationInfo; 35import android.content.pm.PackageManager; 36import android.content.pm.PackageManager.NameNotFoundException; 37import android.content.pm.ServiceInfo; 38import android.graphics.Rect; 39import android.graphics.drawable.Drawable; 40import android.metrics.LogMaker; 41import android.os.AsyncTask; 42import android.os.Binder; 43import android.os.Bundle; 44import android.os.Handler; 45import android.os.IBinder; 46import android.os.Looper; 47import android.os.RemoteCallbackList; 48import android.os.RemoteException; 49import android.os.SystemClock; 50import android.os.UserHandle; 51import android.os.UserManager; 52import android.provider.Settings; 53import android.service.autofill.AutofillService; 54import android.service.autofill.AutofillServiceInfo; 55import android.service.autofill.FieldClassification; 56import android.service.autofill.FieldClassification.Match; 57import android.service.autofill.FillEventHistory; 58import android.service.autofill.FillEventHistory.Event; 59import android.service.autofill.FillResponse; 60import android.service.autofill.IAutoFillService; 61import android.service.autofill.UserData; 62import android.text.TextUtils; 63import android.util.ArrayMap; 64import android.util.ArraySet; 65import android.util.DebugUtils; 66import android.util.LocalLog; 67import android.util.Slog; 68import android.util.SparseArray; 69import android.util.TimeUtils; 70import android.view.autofill.AutofillId; 71import android.view.autofill.AutofillManager; 72import android.view.autofill.AutofillValue; 73import android.view.autofill.IAutoFillManagerClient; 74 75import com.android.internal.R; 76import com.android.internal.annotations.GuardedBy; 77import com.android.internal.logging.MetricsLogger; 78import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 79import com.android.server.LocalServices; 80import com.android.server.autofill.AutofillManagerService.AutofillCompatState; 81import com.android.server.autofill.ui.AutoFillUI; 82 83import java.io.PrintWriter; 84import java.util.ArrayList; 85import java.util.List; 86import java.util.Random; 87 88/** 89 * Bridge between the {@code system_server}'s {@link AutofillManagerService} and the 90 * app's {@link IAutoFillService} implementation. 91 * 92 */ 93final class AutofillManagerServiceImpl { 94 95 private static final String TAG = "AutofillManagerServiceImpl"; 96 private static final int MAX_SESSION_ID_CREATE_TRIES = 2048; 97 98 /** Minimum interval to prune abandoned sessions */ 99 private static final int MAX_ABANDONED_SESSION_MILLIS = 30000; 100 101 private final int mUserId; 102 private final Context mContext; 103 private final Object mLock; 104 private final AutoFillUI mUi; 105 private final MetricsLogger mMetricsLogger = new MetricsLogger(); 106 107 @GuardedBy("mLock") 108 private RemoteCallbackList<IAutoFillManagerClient> mClients; 109 110 @GuardedBy("mLock") 111 private AutofillServiceInfo mInfo; 112 113 private static final Random sRandom = new Random(); 114 115 private final LocalLog mRequestsHistory; 116 private final LocalLog mUiLatencyHistory; 117 private final LocalLog mWtfHistory; 118 private final FieldClassificationStrategy mFieldClassificationStrategy; 119 120 /** 121 * Apps disabled by the service; key is package name, value is when they will be enabled again. 122 */ 123 @GuardedBy("mLock") 124 private ArrayMap<String, Long> mDisabledApps; 125 126 /** 127 * Activities disabled by the service; key is component name, value is when they will be enabled 128 * again. 129 */ 130 @GuardedBy("mLock") 131 private ArrayMap<ComponentName, Long> mDisabledActivities; 132 133 /** 134 * Whether service was disabled for user due to {@link UserManager} restrictions. 135 */ 136 @GuardedBy("mLock") 137 private boolean mDisabled; 138 139 /** 140 * Data used for field classification. 141 */ 142 @GuardedBy("mLock") 143 private UserData mUserData; 144 145 /** 146 * Caches whether the setup completed for the current user. 147 */ 148 @GuardedBy("mLock") 149 private boolean mSetupComplete; 150 151 private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true); 152 153 /** 154 * Cache of pending {@link Session}s, keyed by sessionId. 155 * 156 * <p>They're kept until the {@link AutofillService} finished handling a request, an error 157 * occurs, or the session is abandoned. 158 */ 159 @GuardedBy("mLock") 160 private final SparseArray<Session> mSessions = new SparseArray<>(); 161 162 /** The last selection */ 163 @GuardedBy("mLock") 164 private FillEventHistory mEventHistory; 165 166 /** Shared instance, doesn't need to be logged */ 167 private final AutofillCompatState mAutofillCompatState; 168 169 /** When was {@link PruneTask} last executed? */ 170 private long mLastPrune = 0; 171 172 AutofillManagerServiceImpl(Context context, Object lock, LocalLog requestsHistory, 173 LocalLog uiLatencyHistory, LocalLog wtfHistory, int userId, AutoFillUI ui, 174 AutofillCompatState autofillCompatState, boolean disabled) { 175 mContext = context; 176 mLock = lock; 177 mRequestsHistory = requestsHistory; 178 mUiLatencyHistory = uiLatencyHistory; 179 mWtfHistory = wtfHistory; 180 mUserId = userId; 181 mUi = ui; 182 mFieldClassificationStrategy = new FieldClassificationStrategy(context, userId); 183 mAutofillCompatState = autofillCompatState; 184 updateLocked(disabled); 185 } 186 187 @Nullable 188 CharSequence getServiceName() { 189 final String packageName = getServicePackageName(); 190 if (packageName == null) { 191 return null; 192 } 193 194 try { 195 final PackageManager pm = mContext.getPackageManager(); 196 final ApplicationInfo info = pm.getApplicationInfo(packageName, 0); 197 return pm.getApplicationLabel(info); 198 } catch (Exception e) { 199 Slog.e(TAG, "Could not get label for " + packageName + ": " + e); 200 return packageName; 201 } 202 } 203 204 @GuardedBy("mLock") 205 private int getServiceUidLocked() { 206 if (mInfo == null) { 207 Slog.w(TAG, "getServiceUidLocked(): no mInfo"); 208 return -1; 209 } 210 return mInfo.getServiceInfo().applicationInfo.uid; 211 } 212 213 214 @Nullable 215 String[] getUrlBarResourceIdsForCompatMode(@NonNull String packageName) { 216 return mAutofillCompatState.getUrlBarResourceIds(packageName, mUserId); 217 } 218 219 @Nullable 220 String getServicePackageName() { 221 final ComponentName serviceComponent = getServiceComponentName(); 222 if (serviceComponent != null) { 223 return serviceComponent.getPackageName(); 224 } 225 return null; 226 } 227 228 ComponentName getServiceComponentName() { 229 synchronized (mLock) { 230 if (mInfo == null) { 231 return null; 232 } 233 return mInfo.getServiceInfo().getComponentName(); 234 } 235 } 236 237 private boolean isSetupCompletedLocked() { 238 final String setupComplete = Settings.Secure.getStringForUser( 239 mContext.getContentResolver(), Settings.Secure.USER_SETUP_COMPLETE, mUserId); 240 return "1".equals(setupComplete); 241 } 242 243 private String getComponentNameFromSettings() { 244 return Settings.Secure.getStringForUser( 245 mContext.getContentResolver(), Settings.Secure.AUTOFILL_SERVICE, mUserId); 246 } 247 248 @GuardedBy("mLock") 249 void updateLocked(boolean disabled) { 250 final boolean wasEnabled = isEnabledLocked(); 251 if (sVerbose) { 252 Slog.v(TAG, "updateLocked(u=" + mUserId + "): wasEnabled=" + wasEnabled 253 + ", mSetupComplete= " + mSetupComplete 254 + ", disabled=" + disabled + ", mDisabled=" + mDisabled); 255 } 256 mSetupComplete = isSetupCompletedLocked(); 257 mDisabled = disabled; 258 ComponentName serviceComponent = null; 259 ServiceInfo serviceInfo = null; 260 final String componentName = getComponentNameFromSettings(); 261 if (!TextUtils.isEmpty(componentName)) { 262 try { 263 serviceComponent = ComponentName.unflattenFromString(componentName); 264 serviceInfo = AppGlobals.getPackageManager().getServiceInfo(serviceComponent, 265 0, mUserId); 266 if (serviceInfo == null) { 267 Slog.e(TAG, "Bad AutofillService name: " + componentName); 268 } 269 } catch (RuntimeException | RemoteException e) { 270 Slog.e(TAG, "Error getting service info for '" + componentName + "': " + e); 271 serviceInfo = null; 272 } 273 } 274 try { 275 if (serviceInfo != null) { 276 mInfo = new AutofillServiceInfo(mContext, serviceComponent, mUserId); 277 if (sDebug) Slog.d(TAG, "Set component for user " + mUserId + " as " + mInfo); 278 } else { 279 mInfo = null; 280 if (sDebug) { 281 Slog.d(TAG, "Reset component for user " + mUserId + " (" + componentName + ")"); 282 } 283 } 284 } catch (Exception e) { 285 Slog.e(TAG, "Bad AutofillServiceInfo for '" + componentName + "': " + e); 286 mInfo = null; 287 } 288 final boolean isEnabled = isEnabledLocked(); 289 if (wasEnabled != isEnabled) { 290 if (!isEnabled) { 291 final int sessionCount = mSessions.size(); 292 for (int i = sessionCount - 1; i >= 0; i--) { 293 final Session session = mSessions.valueAt(i); 294 session.removeSelfLocked(); 295 } 296 } 297 sendStateToClients(false); 298 } 299 } 300 301 @GuardedBy("mLock") 302 boolean addClientLocked(IAutoFillManagerClient client) { 303 if (mClients == null) { 304 mClients = new RemoteCallbackList<>(); 305 } 306 mClients.register(client); 307 return isEnabledLocked(); 308 } 309 310 @GuardedBy("mLock") 311 void removeClientLocked(IAutoFillManagerClient client) { 312 if (mClients != null) { 313 mClients.unregister(client); 314 } 315 } 316 317 @GuardedBy("mLock") 318 void setAuthenticationResultLocked(Bundle data, int sessionId, int authenticationId, int uid) { 319 if (!isEnabledLocked()) { 320 return; 321 } 322 final Session session = mSessions.get(sessionId); 323 if (session != null && uid == session.uid) { 324 session.setAuthenticationResultLocked(data, authenticationId); 325 } 326 } 327 328 void setHasCallback(int sessionId, int uid, boolean hasIt) { 329 if (!isEnabledLocked()) { 330 return; 331 } 332 final Session session = mSessions.get(sessionId); 333 if (session != null && uid == session.uid) { 334 synchronized (mLock) { 335 session.setHasCallbackLocked(hasIt); 336 } 337 } 338 } 339 340 @GuardedBy("mLock") 341 int startSessionLocked(@NonNull IBinder activityToken, int uid, 342 @NonNull IBinder appCallbackToken, @NonNull AutofillId autofillId, 343 @NonNull Rect virtualBounds, @Nullable AutofillValue value, boolean hasCallback, 344 int flags, @NonNull ComponentName componentName, boolean compatMode) { 345 if (!isEnabledLocked()) { 346 return 0; 347 } 348 349 final String shortComponentName = componentName.toShortString(); 350 351 if (isAutofillDisabledLocked(componentName)) { 352 if (sDebug) { 353 Slog.d(TAG, "startSession(" + shortComponentName 354 + "): ignored because disabled by service"); 355 } 356 357 final IAutoFillManagerClient client = IAutoFillManagerClient.Stub 358 .asInterface(appCallbackToken); 359 try { 360 client.setSessionFinished(AutofillManager.STATE_DISABLED_BY_SERVICE); 361 } catch (RemoteException e) { 362 Slog.w(TAG, "Could not notify " + shortComponentName + " that it's disabled: " + e); 363 } 364 365 return NO_SESSION; 366 } 367 368 if (sVerbose) Slog.v(TAG, "startSession(): token=" + activityToken + ", flags=" + flags); 369 370 // Occasionally clean up abandoned sessions 371 pruneAbandonedSessionsLocked(); 372 373 final Session newSession = createSessionByTokenLocked(activityToken, uid, appCallbackToken, 374 hasCallback, componentName, compatMode, flags); 375 if (newSession == null) { 376 return NO_SESSION; 377 } 378 379 final String historyItem = 380 "id=" + newSession.id + " uid=" + uid + " a=" + shortComponentName 381 + " s=" + mInfo.getServiceInfo().packageName 382 + " u=" + mUserId + " i=" + autofillId + " b=" + virtualBounds 383 + " hc=" + hasCallback + " f=" + flags; 384 mRequestsHistory.log(historyItem); 385 386 newSession.updateLocked(autofillId, virtualBounds, value, ACTION_START_SESSION, flags); 387 388 return newSession.id; 389 } 390 391 /** 392 * Remove abandoned sessions if needed. 393 */ 394 @GuardedBy("mLock") 395 private void pruneAbandonedSessionsLocked() { 396 long now = System.currentTimeMillis(); 397 if (mLastPrune < now - MAX_ABANDONED_SESSION_MILLIS) { 398 mLastPrune = now; 399 400 if (mSessions.size() > 0) { 401 (new PruneTask()).execute(); 402 } 403 } 404 } 405 406 @GuardedBy("mLock") 407 void setAutofillFailureLocked(int sessionId, int uid, @NonNull List<AutofillId> ids) { 408 if (!isEnabledLocked()) { 409 return; 410 } 411 final Session session = mSessions.get(sessionId); 412 if (session == null || uid != session.uid) { 413 Slog.v(TAG, "setAutofillFailure(): no session for " + sessionId + "(" + uid + ")"); 414 return; 415 } 416 session.setAutofillFailureLocked(ids); 417 } 418 419 @GuardedBy("mLock") 420 void finishSessionLocked(int sessionId, int uid) { 421 if (!isEnabledLocked()) { 422 return; 423 } 424 425 final Session session = mSessions.get(sessionId); 426 if (session == null || uid != session.uid) { 427 if (sVerbose) { 428 Slog.v(TAG, "finishSessionLocked(): no session for " + sessionId + "(" + uid + ")"); 429 } 430 return; 431 } 432 433 session.logContextCommitted(); 434 435 final boolean finished = session.showSaveLocked(); 436 if (sVerbose) Slog.v(TAG, "finishSessionLocked(): session finished on save? " + finished); 437 438 if (finished) { 439 session.removeSelfLocked(); 440 } 441 } 442 443 @GuardedBy("mLock") 444 void cancelSessionLocked(int sessionId, int uid) { 445 if (!isEnabledLocked()) { 446 return; 447 } 448 449 final Session session = mSessions.get(sessionId); 450 if (session == null || uid != session.uid) { 451 Slog.w(TAG, "cancelSessionLocked(): no session for " + sessionId + "(" + uid + ")"); 452 return; 453 } 454 session.removeSelfLocked(); 455 } 456 457 @GuardedBy("mLock") 458 void disableOwnedAutofillServicesLocked(int uid) { 459 Slog.i(TAG, "disableOwnedServices(" + uid + "): " + mInfo); 460 if (mInfo == null) return; 461 462 final ServiceInfo serviceInfo = mInfo.getServiceInfo(); 463 if (serviceInfo.applicationInfo.uid != uid) { 464 Slog.w(TAG, "disableOwnedServices(): ignored when called by UID " + uid 465 + " instead of " + serviceInfo.applicationInfo.uid 466 + " for service " + mInfo); 467 return; 468 } 469 470 471 final long identity = Binder.clearCallingIdentity(); 472 try { 473 final String autoFillService = getComponentNameFromSettings(); 474 final ComponentName componentName = serviceInfo.getComponentName(); 475 if (componentName.equals(ComponentName.unflattenFromString(autoFillService))) { 476 mMetricsLogger.action(MetricsEvent.AUTOFILL_SERVICE_DISABLED_SELF, 477 componentName.getPackageName()); 478 Settings.Secure.putStringForUser(mContext.getContentResolver(), 479 Settings.Secure.AUTOFILL_SERVICE, null, mUserId); 480 destroySessionsLocked(); 481 } else { 482 Slog.w(TAG, "disableOwnedServices(): ignored because current service (" 483 + serviceInfo + ") does not match Settings (" + autoFillService + ")"); 484 } 485 } finally { 486 Binder.restoreCallingIdentity(identity); 487 } 488 } 489 490 @GuardedBy("mLock") 491 private Session createSessionByTokenLocked(@NonNull IBinder activityToken, int uid, 492 @NonNull IBinder appCallbackToken, boolean hasCallback, 493 @NonNull ComponentName componentName, boolean compatMode, int flags) { 494 // use random ids so that one app cannot know that another app creates sessions 495 int sessionId; 496 int tries = 0; 497 do { 498 tries++; 499 if (tries > MAX_SESSION_ID_CREATE_TRIES) { 500 Slog.w(TAG, "Cannot create session in " + MAX_SESSION_ID_CREATE_TRIES + " tries"); 501 return null; 502 } 503 504 sessionId = sRandom.nextInt(); 505 } while (sessionId == NO_SESSION || mSessions.indexOfKey(sessionId) >= 0); 506 507 assertCallerLocked(componentName, compatMode); 508 509 final Session newSession = new Session(this, mUi, mContext, mHandler, mUserId, mLock, 510 sessionId, uid, activityToken, appCallbackToken, hasCallback, mUiLatencyHistory, 511 mWtfHistory, mInfo.getServiceInfo().getComponentName(), componentName, compatMode, 512 flags); 513 mSessions.put(newSession.id, newSession); 514 515 return newSession; 516 } 517 518 /** 519 * Asserts the component is owned by the caller. 520 */ 521 private void assertCallerLocked(@NonNull ComponentName componentName, boolean compatMode) { 522 final String packageName = componentName.getPackageName(); 523 final PackageManager pm = mContext.getPackageManager(); 524 final int callingUid = Binder.getCallingUid(); 525 final int packageUid; 526 try { 527 packageUid = pm.getPackageUidAsUser(packageName, UserHandle.getCallingUserId()); 528 } catch (NameNotFoundException e) { 529 throw new SecurityException("Could not verify UID for " + componentName); 530 } 531 if (callingUid != packageUid && !LocalServices.getService(ActivityManagerInternal.class) 532 .hasRunningActivity(callingUid, packageName)) { 533 final String[] packages = pm.getPackagesForUid(callingUid); 534 final String callingPackage = packages != null ? packages[0] : "uid-" + callingUid; 535 Slog.w(TAG, "App (package=" + callingPackage + ", UID=" + callingUid 536 + ") passed component (" + componentName + ") owned by UID " + packageUid); 537 mMetricsLogger.write( 538 Helper.newLogMaker(MetricsEvent.AUTOFILL_FORGED_COMPONENT_ATTEMPT, 539 callingPackage, getServicePackageName(), compatMode) 540 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_FORGED_COMPONENT_NAME, 541 componentName == null ? "null" : componentName.flattenToShortString())); 542 543 throw new SecurityException("Invalid component: " + componentName); 544 } 545 } 546 547 /** 548 * Restores a session after an activity was temporarily destroyed. 549 * 550 * @param sessionId The id of the session to restore 551 * @param uid UID of the process that tries to restore the session 552 * @param activityToken The new instance of the activity 553 * @param appCallback The callbacks to the activity 554 */ 555 boolean restoreSession(int sessionId, int uid, @NonNull IBinder activityToken, 556 @NonNull IBinder appCallback) { 557 final Session session = mSessions.get(sessionId); 558 559 if (session == null || uid != session.uid) { 560 return false; 561 } else { 562 session.switchActivity(activityToken, appCallback); 563 return true; 564 } 565 } 566 567 /** 568 * Updates a session and returns whether it should be restarted. 569 */ 570 @GuardedBy("mLock") 571 boolean updateSessionLocked(int sessionId, int uid, AutofillId autofillId, Rect virtualBounds, 572 AutofillValue value, int action, int flags) { 573 final Session session = mSessions.get(sessionId); 574 if (session == null || session.uid != uid) { 575 if ((flags & FLAG_MANUAL_REQUEST) != 0) { 576 if (sDebug) { 577 Slog.d(TAG, "restarting session " + sessionId + " due to manual request on " 578 + autofillId); 579 } 580 return true; 581 } 582 if (sVerbose) { 583 Slog.v(TAG, "updateSessionLocked(): session gone for " + sessionId 584 + "(" + uid + ")"); 585 } 586 return false; 587 } 588 589 session.updateLocked(autofillId, virtualBounds, value, action, flags); 590 return false; 591 } 592 593 @GuardedBy("mLock") 594 void removeSessionLocked(int sessionId) { 595 mSessions.remove(sessionId); 596 } 597 598 void handleSessionSave(Session session) { 599 synchronized (mLock) { 600 if (mSessions.get(session.id) == null) { 601 Slog.w(TAG, "handleSessionSave(): already gone: " + session.id); 602 603 return; 604 } 605 session.callSaveLocked(); 606 } 607 } 608 609 void onPendingSaveUi(int operation, @NonNull IBinder token) { 610 if (sVerbose) Slog.v(TAG, "onPendingSaveUi(" + operation + "): " + token); 611 synchronized (mLock) { 612 final int sessionCount = mSessions.size(); 613 for (int i = sessionCount - 1; i >= 0; i--) { 614 final Session session = mSessions.valueAt(i); 615 if (session.isSaveUiPendingForTokenLocked(token)) { 616 session.onPendingSaveUi(operation, token); 617 return; 618 } 619 } 620 } 621 if (sDebug) { 622 Slog.d(TAG, "No pending Save UI for token " + token + " and operation " 623 + DebugUtils.flagsToString(AutofillManager.class, "PENDING_UI_OPERATION_", 624 operation)); 625 } 626 } 627 628 @GuardedBy("mLock") 629 void handlePackageUpdateLocked(String packageName) { 630 final ServiceInfo serviceInfo = mFieldClassificationStrategy.getServiceInfo(); 631 if (serviceInfo != null && serviceInfo.packageName.equals(packageName)) { 632 resetExtServiceLocked(); 633 } 634 } 635 636 @GuardedBy("mLock") 637 void resetExtServiceLocked() { 638 if (sVerbose) Slog.v(TAG, "reset autofill service."); 639 mFieldClassificationStrategy.reset(); 640 } 641 642 @GuardedBy("mLock") 643 void destroyLocked() { 644 if (sVerbose) Slog.v(TAG, "destroyLocked()"); 645 646 resetExtServiceLocked(); 647 648 final int numSessions = mSessions.size(); 649 final ArraySet<RemoteFillService> remoteFillServices = new ArraySet<>(numSessions); 650 for (int i = 0; i < numSessions; i++) { 651 final RemoteFillService remoteFillService = mSessions.valueAt(i).destroyLocked(); 652 if (remoteFillService != null) { 653 remoteFillServices.add(remoteFillService); 654 } 655 } 656 mSessions.clear(); 657 for (int i = 0; i < remoteFillServices.size(); i++) { 658 remoteFillServices.valueAt(i).destroy(); 659 } 660 661 sendStateToClients(true); 662 if (mClients != null) { 663 mClients.kill(); 664 mClients = null; 665 } 666 } 667 668 @NonNull 669 CharSequence getServiceLabel() { 670 return mInfo.getServiceInfo().loadLabel(mContext.getPackageManager()); 671 } 672 673 @NonNull 674 Drawable getServiceIcon() { 675 return mInfo.getServiceInfo().loadIcon(mContext.getPackageManager()); 676 } 677 678 /** 679 * Initializes the last fill selection after an autofill service returned a new 680 * {@link FillResponse}. 681 */ 682 void setLastResponse(int sessionId, @NonNull FillResponse response) { 683 synchronized (mLock) { 684 mEventHistory = new FillEventHistory(sessionId, response.getClientState()); 685 } 686 } 687 688 /** 689 * Resets the last fill selection. 690 */ 691 void resetLastResponse() { 692 synchronized (mLock) { 693 mEventHistory = null; 694 } 695 } 696 697 @GuardedBy("mLock") 698 private boolean isValidEventLocked(String method, int sessionId) { 699 if (mEventHistory == null) { 700 Slog.w(TAG, method + ": not logging event because history is null"); 701 return false; 702 } 703 if (sessionId != mEventHistory.getSessionId()) { 704 if (sDebug) { 705 Slog.d(TAG, method + ": not logging event for session " + sessionId 706 + " because tracked session is " + mEventHistory.getSessionId()); 707 } 708 return false; 709 } 710 return true; 711 } 712 713 /** 714 * Updates the last fill selection when an authentication was selected. 715 */ 716 void setAuthenticationSelected(int sessionId, @Nullable Bundle clientState) { 717 synchronized (mLock) { 718 if (isValidEventLocked("setAuthenticationSelected()", sessionId)) { 719 mEventHistory.addEvent( 720 new Event(Event.TYPE_AUTHENTICATION_SELECTED, null, clientState, null, null, 721 null, null, null, null, null, null)); 722 } 723 } 724 } 725 726 /** 727 * Updates the last fill selection when an dataset authentication was selected. 728 */ 729 void logDatasetAuthenticationSelected(@Nullable String selectedDataset, int sessionId, 730 @Nullable Bundle clientState) { 731 synchronized (mLock) { 732 if (isValidEventLocked("logDatasetAuthenticationSelected()", sessionId)) { 733 mEventHistory.addEvent( 734 new Event(Event.TYPE_DATASET_AUTHENTICATION_SELECTED, selectedDataset, 735 clientState, null, null, null, null, null, null, null, null)); 736 } 737 } 738 } 739 740 /** 741 * Updates the last fill selection when an save Ui is shown. 742 */ 743 void logSaveShown(int sessionId, @Nullable Bundle clientState) { 744 synchronized (mLock) { 745 if (isValidEventLocked("logSaveShown()", sessionId)) { 746 mEventHistory.addEvent(new Event(Event.TYPE_SAVE_SHOWN, null, clientState, null, 747 null, null, null, null, null, null, null)); 748 } 749 } 750 } 751 752 /** 753 * Updates the last fill response when a dataset was selected. 754 */ 755 void logDatasetSelected(@Nullable String selectedDataset, int sessionId, 756 @Nullable Bundle clientState) { 757 synchronized (mLock) { 758 if (isValidEventLocked("logDatasetSelected()", sessionId)) { 759 mEventHistory.addEvent( 760 new Event(Event.TYPE_DATASET_SELECTED, selectedDataset, clientState, null, 761 null, null, null, null, null, null, null)); 762 } 763 } 764 } 765 766 /** 767 * Updates the last fill response when an autofill context is committed. 768 */ 769 @GuardedBy("mLock") 770 void logContextCommittedLocked(int sessionId, @Nullable Bundle clientState, 771 @Nullable ArrayList<String> selectedDatasets, 772 @Nullable ArraySet<String> ignoredDatasets, 773 @Nullable ArrayList<AutofillId> changedFieldIds, 774 @Nullable ArrayList<String> changedDatasetIds, 775 @Nullable ArrayList<AutofillId> manuallyFilledFieldIds, 776 @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds, 777 @NonNull String appPackageName, boolean compatMode) { 778 logContextCommittedLocked(sessionId, clientState, selectedDatasets, ignoredDatasets, 779 changedFieldIds, changedDatasetIds, manuallyFilledFieldIds, 780 manuallyFilledDatasetIds, null, null, appPackageName, compatMode); 781 } 782 783 @GuardedBy("mLock") 784 void logContextCommittedLocked(int sessionId, @Nullable Bundle clientState, 785 @Nullable ArrayList<String> selectedDatasets, 786 @Nullable ArraySet<String> ignoredDatasets, 787 @Nullable ArrayList<AutofillId> changedFieldIds, 788 @Nullable ArrayList<String> changedDatasetIds, 789 @Nullable ArrayList<AutofillId> manuallyFilledFieldIds, 790 @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds, 791 @Nullable ArrayList<AutofillId> detectedFieldIdsList, 792 @Nullable ArrayList<FieldClassification> detectedFieldClassificationsList, 793 @NonNull String appPackageName, boolean compatMode) { 794 if (isValidEventLocked("logDatasetNotSelected()", sessionId)) { 795 if (sVerbose) { 796 Slog.v(TAG, "logContextCommitted() with FieldClassification: id=" + sessionId 797 + ", selectedDatasets=" + selectedDatasets 798 + ", ignoredDatasetIds=" + ignoredDatasets 799 + ", changedAutofillIds=" + changedFieldIds 800 + ", changedDatasetIds=" + changedDatasetIds 801 + ", manuallyFilledFieldIds=" + manuallyFilledFieldIds 802 + ", detectedFieldIds=" + detectedFieldIdsList 803 + ", detectedFieldClassifications=" + detectedFieldClassificationsList 804 + ", compatMode=" + compatMode); 805 } 806 AutofillId[] detectedFieldsIds = null; 807 FieldClassification[] detectedFieldClassifications = null; 808 if (detectedFieldIdsList != null) { 809 detectedFieldsIds = new AutofillId[detectedFieldIdsList.size()]; 810 detectedFieldIdsList.toArray(detectedFieldsIds); 811 detectedFieldClassifications = 812 new FieldClassification[detectedFieldClassificationsList.size()]; 813 detectedFieldClassificationsList.toArray(detectedFieldClassifications); 814 815 final int numberFields = detectedFieldsIds.length; 816 int totalSize = 0; 817 float totalScore = 0; 818 for (int i = 0; i < numberFields; i++) { 819 final FieldClassification fc = detectedFieldClassifications[i]; 820 final List<Match> matches = fc.getMatches(); 821 final int size = matches.size(); 822 totalSize += size; 823 for (int j = 0; j < size; j++) { 824 totalScore += matches.get(j).getScore(); 825 } 826 } 827 828 final int averageScore = (int) ((totalScore * 100) / totalSize); 829 mMetricsLogger.write(Helper 830 .newLogMaker(MetricsEvent.AUTOFILL_FIELD_CLASSIFICATION_MATCHES, 831 appPackageName, getServicePackageName(), compatMode) 832 .setCounterValue(numberFields) 833 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_MATCH_SCORE, 834 averageScore)); 835 } 836 mEventHistory.addEvent(new Event(Event.TYPE_CONTEXT_COMMITTED, null, 837 clientState, selectedDatasets, ignoredDatasets, 838 changedFieldIds, changedDatasetIds, 839 manuallyFilledFieldIds, manuallyFilledDatasetIds, 840 detectedFieldsIds, detectedFieldClassifications)); 841 } 842 } 843 844 /** 845 * Gets the fill event history. 846 * 847 * @param callingUid The calling uid 848 * 849 * @return The history or {@code null} if there is none. 850 */ 851 FillEventHistory getFillEventHistory(int callingUid) { 852 synchronized (mLock) { 853 if (mEventHistory != null 854 && isCalledByServiceLocked("getFillEventHistory", callingUid)) { 855 return mEventHistory; 856 } 857 } 858 return null; 859 } 860 861 // Called by Session - does not need to check uid 862 UserData getUserData() { 863 synchronized (mLock) { 864 return mUserData; 865 } 866 } 867 868 // Called by AutofillManager 869 UserData getUserData(int callingUid) { 870 synchronized (mLock) { 871 if (isCalledByServiceLocked("getUserData", callingUid)) { 872 return mUserData; 873 } 874 } 875 return null; 876 } 877 878 // Called by AutofillManager 879 void setUserData(int callingUid, UserData userData) { 880 synchronized (mLock) { 881 if (!isCalledByServiceLocked("setUserData", callingUid)) { 882 return; 883 } 884 mUserData = userData; 885 // Log it 886 int numberFields = mUserData == null ? 0: mUserData.getCategoryIds().length; 887 mMetricsLogger.write(Helper.newLogMaker(MetricsEvent.AUTOFILL_USERDATA_UPDATED, 888 getServicePackageName(), null) 889 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, numberFields)); 890 } 891 } 892 893 @GuardedBy("mLock") 894 private boolean isCalledByServiceLocked(String methodName, int callingUid) { 895 if (getServiceUidLocked() != callingUid) { 896 Slog.w(TAG, methodName + "() called by UID " + callingUid 897 + ", but service UID is " + getServiceUidLocked()); 898 return false; 899 } 900 return true; 901 } 902 903 @GuardedBy("mLock") 904 void dumpLocked(String prefix, PrintWriter pw) { 905 final String prefix2 = prefix + " "; 906 907 pw.print(prefix); pw.print("User: "); pw.println(mUserId); 908 pw.print(prefix); pw.print("UID: "); pw.println(getServiceUidLocked()); 909 pw.print(prefix); pw.print("Autofill Service Info: "); 910 if (mInfo == null) { 911 pw.println("N/A"); 912 } else { 913 pw.println(); 914 mInfo.dump(prefix2, pw); 915 } 916 pw.print(prefix); pw.print("Component from settings: "); 917 pw.println(getComponentNameFromSettings()); 918 pw.print(prefix); pw.print("Default component: "); 919 pw.println(mContext.getString(R.string.config_defaultAutofillService)); 920 pw.print(prefix); pw.print("Disabled: "); pw.println(mDisabled); 921 pw.print(prefix); pw.print("Field classification enabled: "); 922 pw.println(isFieldClassificationEnabledLocked()); 923 pw.print(prefix); pw.print("Compat pkgs: "); 924 final ArrayMap<String, Long> compatPkgs = getCompatibilityPackagesLocked(); 925 if (compatPkgs == null) { 926 pw.println("N/A"); 927 } else { 928 pw.println(compatPkgs); 929 } 930 pw.print(prefix); pw.print("Setup complete: "); pw.println(mSetupComplete); 931 pw.print(prefix); pw.print("Last prune: "); pw.println(mLastPrune); 932 933 pw.print(prefix); pw.print("Disabled apps: "); 934 935 if (mDisabledApps == null) { 936 pw.println("N/A"); 937 } else { 938 final int size = mDisabledApps.size(); 939 pw.println(size); 940 final StringBuilder builder = new StringBuilder(); 941 final long now = SystemClock.elapsedRealtime(); 942 for (int i = 0; i < size; i++) { 943 final String packageName = mDisabledApps.keyAt(i); 944 final long expiration = mDisabledApps.valueAt(i); 945 builder.append(prefix).append(prefix) 946 .append(i).append(". ").append(packageName).append(": "); 947 TimeUtils.formatDuration((expiration - now), builder); 948 builder.append('\n'); 949 } 950 pw.println(builder); 951 } 952 953 pw.print(prefix); pw.print("Disabled activities: "); 954 955 if (mDisabledActivities == null) { 956 pw.println("N/A"); 957 } else { 958 final int size = mDisabledActivities.size(); 959 pw.println(size); 960 final StringBuilder builder = new StringBuilder(); 961 final long now = SystemClock.elapsedRealtime(); 962 for (int i = 0; i < size; i++) { 963 final ComponentName component = mDisabledActivities.keyAt(i); 964 final long expiration = mDisabledActivities.valueAt(i); 965 builder.append(prefix).append(prefix) 966 .append(i).append(". ").append(component).append(": "); 967 TimeUtils.formatDuration((expiration - now), builder); 968 builder.append('\n'); 969 } 970 pw.println(builder); 971 } 972 973 final int size = mSessions.size(); 974 if (size == 0) { 975 pw.print(prefix); pw.println("No sessions"); 976 } else { 977 pw.print(prefix); pw.print(size); pw.println(" sessions:"); 978 for (int i = 0; i < size; i++) { 979 pw.print(prefix); pw.print("#"); pw.println(i + 1); 980 mSessions.valueAt(i).dumpLocked(prefix2, pw); 981 } 982 } 983 984 pw.print(prefix); pw.print("Clients: "); 985 if (mClients == null) { 986 pw.println("N/A"); 987 } else { 988 pw.println(); 989 mClients.dump(pw, prefix2); 990 } 991 992 if (mEventHistory == null || mEventHistory.getEvents() == null 993 || mEventHistory.getEvents().size() == 0) { 994 pw.print(prefix); pw.println("No event on last fill response"); 995 } else { 996 pw.print(prefix); pw.println("Events of last fill response:"); 997 pw.print(prefix); 998 999 int numEvents = mEventHistory.getEvents().size(); 1000 for (int i = 0; i < numEvents; i++) { 1001 final Event event = mEventHistory.getEvents().get(i); 1002 pw.println(" " + i + ": eventType=" + event.getType() + " datasetId=" 1003 + event.getDatasetId()); 1004 } 1005 } 1006 1007 pw.print(prefix); pw.print("User data: "); 1008 if (mUserData == null) { 1009 pw.println("N/A"); 1010 } else { 1011 pw.println(); 1012 mUserData.dump(prefix2, pw); 1013 } 1014 1015 pw.print(prefix); pw.println("Field Classification strategy: "); 1016 mFieldClassificationStrategy.dump(prefix2, pw); 1017 } 1018 1019 @GuardedBy("mLock") 1020 void destroySessionsLocked() { 1021 if (mSessions.size() == 0) { 1022 mUi.destroyAll(null, null, false); 1023 return; 1024 } 1025 while (mSessions.size() > 0) { 1026 mSessions.valueAt(0).forceRemoveSelfLocked(); 1027 } 1028 } 1029 1030 // TODO(b/64940307): remove this method if SaveUI is refactored to be attached on activities 1031 @GuardedBy("mLock") 1032 void destroyFinishedSessionsLocked() { 1033 final int sessionCount = mSessions.size(); 1034 for (int i = sessionCount - 1; i >= 0; i--) { 1035 final Session session = mSessions.valueAt(i); 1036 if (session.isSavingLocked()) { 1037 if (sDebug) Slog.d(TAG, "destroyFinishedSessionsLocked(): " + session.id); 1038 session.forceRemoveSelfLocked(); 1039 } 1040 } 1041 } 1042 1043 @GuardedBy("mLock") 1044 void listSessionsLocked(ArrayList<String> output) { 1045 final int numSessions = mSessions.size(); 1046 for (int i = 0; i < numSessions; i++) { 1047 output.add((mInfo != null ? mInfo.getServiceInfo().getComponentName() 1048 : null) + ":" + mSessions.keyAt(i)); 1049 } 1050 } 1051 1052 @GuardedBy("mLock") 1053 @Nullable ArrayMap<String, Long> getCompatibilityPackagesLocked() { 1054 if (mInfo != null) { 1055 return mInfo.getCompatibilityPackages(); 1056 } 1057 return null; 1058 } 1059 1060 private void sendStateToClients(boolean resetClient) { 1061 final RemoteCallbackList<IAutoFillManagerClient> clients; 1062 final int userClientCount; 1063 synchronized (mLock) { 1064 if (mClients == null) { 1065 return; 1066 } 1067 clients = mClients; 1068 userClientCount = clients.beginBroadcast(); 1069 } 1070 try { 1071 for (int i = 0; i < userClientCount; i++) { 1072 final IAutoFillManagerClient client = clients.getBroadcastItem(i); 1073 try { 1074 final boolean resetSession; 1075 final boolean isEnabled; 1076 synchronized (mLock) { 1077 resetSession = resetClient || isClientSessionDestroyedLocked(client); 1078 isEnabled = isEnabledLocked(); 1079 } 1080 int flags = 0; 1081 if (isEnabled) { 1082 flags |= AutofillManager.SET_STATE_FLAG_ENABLED; 1083 } 1084 if (resetSession) { 1085 flags |= AutofillManager.SET_STATE_FLAG_RESET_SESSION; 1086 } 1087 if (resetClient) { 1088 flags |= AutofillManager.SET_STATE_FLAG_RESET_CLIENT; 1089 } 1090 if (sDebug) { 1091 flags |= AutofillManager.SET_STATE_FLAG_DEBUG; 1092 } 1093 if (sVerbose) { 1094 flags |= AutofillManager.SET_STATE_FLAG_VERBOSE; 1095 } 1096 client.setState(flags); 1097 } catch (RemoteException re) { 1098 /* ignore */ 1099 } 1100 } 1101 } finally { 1102 clients.finishBroadcast(); 1103 } 1104 } 1105 1106 @GuardedBy("mLock") 1107 private boolean isClientSessionDestroyedLocked(IAutoFillManagerClient client) { 1108 final int sessionCount = mSessions.size(); 1109 for (int i = 0; i < sessionCount; i++) { 1110 final Session session = mSessions.valueAt(i); 1111 if (session.getClient().equals(client)) { 1112 return session.isDestroyed(); 1113 } 1114 } 1115 return true; 1116 } 1117 1118 @GuardedBy("mLock") 1119 boolean isEnabledLocked() { 1120 return mSetupComplete && mInfo != null && !mDisabled; 1121 } 1122 1123 /** 1124 * Called by {@link Session} when service asked to disable autofill for an app. 1125 */ 1126 void disableAutofillForApp(@NonNull String packageName, long duration, boolean compatMode) { 1127 synchronized (mLock) { 1128 if (mDisabledApps == null) { 1129 mDisabledApps = new ArrayMap<>(1); 1130 } 1131 long expiration = SystemClock.elapsedRealtime() + duration; 1132 // Protect it against overflow 1133 if (expiration < 0) { 1134 expiration = Long.MAX_VALUE; 1135 } 1136 mDisabledApps.put(packageName, expiration); 1137 int intDuration = duration > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) duration; 1138 mMetricsLogger.write(Helper.newLogMaker(MetricsEvent.AUTOFILL_SERVICE_DISABLED_APP, 1139 packageName, getServicePackageName(), compatMode) 1140 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_DURATION, intDuration)); 1141 } 1142 } 1143 1144 /** 1145 * Called by {@link Session} when service asked to disable autofill an app. 1146 */ 1147 void disableAutofillForActivity(@NonNull ComponentName componentName, long duration, 1148 boolean compatMode) { 1149 synchronized (mLock) { 1150 if (mDisabledActivities == null) { 1151 mDisabledActivities = new ArrayMap<>(1); 1152 } 1153 long expiration = SystemClock.elapsedRealtime() + duration; 1154 // Protect it against overflow 1155 if (expiration < 0) { 1156 expiration = Long.MAX_VALUE; 1157 } 1158 mDisabledActivities.put(componentName, expiration); 1159 final int intDuration = duration > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) duration; 1160 // NOTE: not using Helper.newLogMaker() because we're setting the componentName instead 1161 // of package name 1162 mMetricsLogger.write(new LogMaker(MetricsEvent.AUTOFILL_SERVICE_DISABLED_ACTIVITY) 1163 .setComponentName(componentName) 1164 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE, getServicePackageName()) 1165 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_DURATION, intDuration) 1166 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_COMPAT_MODE, compatMode ? 1 : 0)); 1167 } 1168 } 1169 1170 /** 1171 * Checks if autofill is disabled by service to the given activity. 1172 */ 1173 @GuardedBy("mLock") 1174 private boolean isAutofillDisabledLocked(@NonNull ComponentName componentName) { 1175 // Check activities first. 1176 long elapsedTime = 0; 1177 if (mDisabledActivities != null) { 1178 elapsedTime = SystemClock.elapsedRealtime(); 1179 final Long expiration = mDisabledActivities.get(componentName); 1180 if (expiration != null) { 1181 if (expiration >= elapsedTime) return true; 1182 // Restriction expired - clean it up. 1183 if (sVerbose) { 1184 Slog.v(TAG, "Removing " + componentName.toShortString() 1185 + " from disabled list"); 1186 } 1187 mDisabledActivities.remove(componentName); 1188 } 1189 } 1190 1191 // Then check apps. 1192 final String packageName = componentName.getPackageName(); 1193 if (mDisabledApps == null) return false; 1194 1195 final Long expiration = mDisabledApps.get(packageName); 1196 if (expiration == null) return false; 1197 1198 if (elapsedTime == 0) { 1199 elapsedTime = SystemClock.elapsedRealtime(); 1200 } 1201 1202 if (expiration >= elapsedTime) return true; 1203 1204 // Restriction expired - clean it up. 1205 if (sVerbose) Slog.v(TAG, "Removing " + packageName + " from disabled list"); 1206 mDisabledApps.remove(packageName); 1207 return false; 1208 } 1209 1210 // Called by AutofillManager, checks UID. 1211 boolean isFieldClassificationEnabled(int callingUid) { 1212 synchronized (mLock) { 1213 if (!isCalledByServiceLocked("isFieldClassificationEnabled", callingUid)) { 1214 return false; 1215 } 1216 return isFieldClassificationEnabledLocked(); 1217 } 1218 } 1219 1220 // Called by internally, no need to check UID. 1221 boolean isFieldClassificationEnabledLocked() { 1222 return Settings.Secure.getIntForUser( 1223 mContext.getContentResolver(), 1224 Settings.Secure.AUTOFILL_FEATURE_FIELD_CLASSIFICATION, 1, 1225 mUserId) == 1; 1226 } 1227 1228 FieldClassificationStrategy getFieldClassificationStrategy() { 1229 return mFieldClassificationStrategy; 1230 } 1231 1232 String[] getAvailableFieldClassificationAlgorithms(int callingUid) { 1233 synchronized (mLock) { 1234 if (!isCalledByServiceLocked("getFCAlgorithms()", callingUid)) { 1235 return null; 1236 } 1237 } 1238 return mFieldClassificationStrategy.getAvailableAlgorithms(); 1239 } 1240 1241 String getDefaultFieldClassificationAlgorithm(int callingUid) { 1242 synchronized (mLock) { 1243 if (!isCalledByServiceLocked("getDefaultFCAlgorithm()", callingUid)) { 1244 return null; 1245 } 1246 } 1247 return mFieldClassificationStrategy.getDefaultAlgorithm(); 1248 } 1249 1250 @Override 1251 public String toString() { 1252 return "AutofillManagerServiceImpl: [userId=" + mUserId 1253 + ", component=" + (mInfo != null 1254 ? mInfo.getServiceInfo().getComponentName() : null) + "]"; 1255 } 1256 1257 /** Task used to prune abandoned session */ 1258 private class PruneTask extends AsyncTask<Void, Void, Void> { 1259 @Override 1260 protected Void doInBackground(Void... ignored) { 1261 int numSessionsToRemove; 1262 1263 SparseArray<IBinder> sessionsToRemove; 1264 1265 synchronized (mLock) { 1266 numSessionsToRemove = mSessions.size(); 1267 sessionsToRemove = new SparseArray<>(numSessionsToRemove); 1268 1269 for (int i = 0; i < numSessionsToRemove; i++) { 1270 Session session = mSessions.valueAt(i); 1271 1272 sessionsToRemove.put(session.id, session.getActivityTokenLocked()); 1273 } 1274 } 1275 1276 IActivityManager am = ActivityManager.getService(); 1277 1278 // Only remove sessions which's activities are not known to the activity manager anymore 1279 for (int i = 0; i < numSessionsToRemove; i++) { 1280 try { 1281 // The activity manager cannot resolve activities that have been removed 1282 if (am.getActivityClassForToken(sessionsToRemove.valueAt(i)) != null) { 1283 sessionsToRemove.removeAt(i); 1284 i--; 1285 numSessionsToRemove--; 1286 } 1287 } catch (RemoteException e) { 1288 Slog.w(TAG, "Cannot figure out if activity is finished", e); 1289 } 1290 } 1291 1292 synchronized (mLock) { 1293 for (int i = 0; i < numSessionsToRemove; i++) { 1294 Session sessionToRemove = mSessions.get(sessionsToRemove.keyAt(i)); 1295 1296 if (sessionToRemove != null && sessionsToRemove.valueAt(i) 1297 == sessionToRemove.getActivityTokenLocked()) { 1298 if (sessionToRemove.isSavingLocked()) { 1299 if (sVerbose) { 1300 Slog.v(TAG, "Session " + sessionToRemove.id + " is saving"); 1301 } 1302 } else { 1303 if (sDebug) { 1304 Slog.i(TAG, "Prune session " + sessionToRemove.id + " (" 1305 + sessionToRemove.getActivityTokenLocked() + ")"); 1306 } 1307 sessionToRemove.removeSelfLocked(); 1308 } 1309 } 1310 } 1311 } 1312 1313 return null; 1314 } 1315 } 1316} 1317