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