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 17 package com.android.server.autofill; 18 19 import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST; 20 import static android.view.autofill.AutofillManager.ACTION_START_SESSION; 21 import static android.view.autofill.AutofillManager.NO_SESSION; 22 23 import static com.android.server.autofill.Helper.sDebug; 24 import static com.android.server.autofill.Helper.sVerbose; 25 26 import android.annotation.NonNull; 27 import android.annotation.Nullable; 28 import android.app.ActivityManager; 29 import android.app.ActivityManagerInternal; 30 import android.app.AppGlobals; 31 import android.app.IActivityManager; 32 import android.content.ComponentName; 33 import android.content.Context; 34 import android.content.pm.ApplicationInfo; 35 import android.content.pm.PackageItemInfo; 36 import android.content.pm.PackageManager; 37 import android.content.pm.PackageManager.NameNotFoundException; 38 import android.content.pm.ServiceInfo; 39 import android.graphics.Rect; 40 import android.graphics.drawable.Drawable; 41 import android.metrics.LogMaker; 42 import android.os.AsyncTask; 43 import android.os.Binder; 44 import android.os.Bundle; 45 import android.os.Handler; 46 import android.os.IBinder; 47 import android.os.Looper; 48 import android.os.RemoteCallbackList; 49 import android.os.RemoteException; 50 import android.os.SystemClock; 51 import android.os.UserHandle; 52 import android.os.UserManager; 53 import android.provider.Settings; 54 import android.service.autofill.AutofillService; 55 import android.service.autofill.AutofillServiceInfo; 56 import android.service.autofill.FieldClassification; 57 import android.service.autofill.FieldClassification.Match; 58 import android.service.autofill.FillEventHistory; 59 import android.service.autofill.FillEventHistory.Event; 60 import android.service.autofill.FillResponse; 61 import android.service.autofill.IAutoFillService; 62 import android.service.autofill.UserData; 63 import android.text.TextUtils; 64 import android.util.ArrayMap; 65 import android.util.ArraySet; 66 import android.util.DebugUtils; 67 import android.util.LocalLog; 68 import android.util.Slog; 69 import android.util.SparseArray; 70 import android.util.TimeUtils; 71 import android.view.autofill.AutofillId; 72 import android.view.autofill.AutofillManager; 73 import android.view.autofill.AutofillValue; 74 import android.view.autofill.IAutoFillManagerClient; 75 76 import com.android.internal.R; 77 import com.android.internal.annotations.GuardedBy; 78 import com.android.internal.logging.MetricsLogger; 79 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 80 import com.android.server.LocalServices; 81 import com.android.server.autofill.AutofillManagerService.AutofillCompatState; 82 import com.android.server.autofill.ui.AutoFillUI; 83 84 import java.io.PrintWriter; 85 import java.util.ArrayList; 86 import java.util.List; 87 import 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 */ 94 final 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 541 // NOTE: not using Helper.newLogMaker() because we don't have the session id 542 final LogMaker log = new LogMaker(MetricsEvent.AUTOFILL_FORGED_COMPONENT_ATTEMPT) 543 .setPackageName(callingPackage) 544 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE, getServicePackageName()) 545 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_FORGED_COMPONENT_NAME, 546 componentName == null ? "null" : componentName.flattenToShortString()); 547 if (compatMode) { 548 log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_COMPAT_MODE, 1); 549 } 550 mMetricsLogger.write(log); 551 552 throw new SecurityException("Invalid component: " + componentName); 553 } 554 } 555 556 /** 557 * Restores a session after an activity was temporarily destroyed. 558 * 559 * @param sessionId The id of the session to restore 560 * @param uid UID of the process that tries to restore the session 561 * @param activityToken The new instance of the activity 562 * @param appCallback The callbacks to the activity 563 */ 564 boolean restoreSession(int sessionId, int uid, @NonNull IBinder activityToken, 565 @NonNull IBinder appCallback) { 566 final Session session = mSessions.get(sessionId); 567 568 if (session == null || uid != session.uid) { 569 return false; 570 } else { 571 session.switchActivity(activityToken, appCallback); 572 return true; 573 } 574 } 575 576 /** 577 * Updates a session and returns whether it should be restarted. 578 */ 579 @GuardedBy("mLock") 580 boolean updateSessionLocked(int sessionId, int uid, AutofillId autofillId, Rect virtualBounds, 581 AutofillValue value, int action, int flags) { 582 final Session session = mSessions.get(sessionId); 583 if (session == null || session.uid != uid) { 584 if ((flags & FLAG_MANUAL_REQUEST) != 0) { 585 if (sDebug) { 586 Slog.d(TAG, "restarting session " + sessionId + " due to manual request on " 587 + autofillId); 588 } 589 return true; 590 } 591 if (sVerbose) { 592 Slog.v(TAG, "updateSessionLocked(): session gone for " + sessionId 593 + "(" + uid + ")"); 594 } 595 return false; 596 } 597 598 session.updateLocked(autofillId, virtualBounds, value, action, flags); 599 return false; 600 } 601 602 @GuardedBy("mLock") 603 void removeSessionLocked(int sessionId) { 604 mSessions.remove(sessionId); 605 } 606 607 void handleSessionSave(Session session) { 608 synchronized (mLock) { 609 if (mSessions.get(session.id) == null) { 610 Slog.w(TAG, "handleSessionSave(): already gone: " + session.id); 611 612 return; 613 } 614 session.callSaveLocked(); 615 } 616 } 617 618 void onPendingSaveUi(int operation, @NonNull IBinder token) { 619 if (sVerbose) Slog.v(TAG, "onPendingSaveUi(" + operation + "): " + token); 620 synchronized (mLock) { 621 final int sessionCount = mSessions.size(); 622 for (int i = sessionCount - 1; i >= 0; i--) { 623 final Session session = mSessions.valueAt(i); 624 if (session.isSaveUiPendingForTokenLocked(token)) { 625 session.onPendingSaveUi(operation, token); 626 return; 627 } 628 } 629 } 630 if (sDebug) { 631 Slog.d(TAG, "No pending Save UI for token " + token + " and operation " 632 + DebugUtils.flagsToString(AutofillManager.class, "PENDING_UI_OPERATION_", 633 operation)); 634 } 635 } 636 637 @GuardedBy("mLock") 638 void handlePackageUpdateLocked(String packageName) { 639 final ServiceInfo serviceInfo = mFieldClassificationStrategy.getServiceInfo(); 640 if (serviceInfo != null && serviceInfo.packageName.equals(packageName)) { 641 resetExtServiceLocked(); 642 } 643 } 644 645 @GuardedBy("mLock") 646 void resetExtServiceLocked() { 647 if (sVerbose) Slog.v(TAG, "reset autofill service."); 648 mFieldClassificationStrategy.reset(); 649 } 650 651 @GuardedBy("mLock") 652 void destroyLocked() { 653 if (sVerbose) Slog.v(TAG, "destroyLocked()"); 654 655 resetExtServiceLocked(); 656 657 final int numSessions = mSessions.size(); 658 final ArraySet<RemoteFillService> remoteFillServices = new ArraySet<>(numSessions); 659 for (int i = 0; i < numSessions; i++) { 660 final RemoteFillService remoteFillService = mSessions.valueAt(i).destroyLocked(); 661 if (remoteFillService != null) { 662 remoteFillServices.add(remoteFillService); 663 } 664 } 665 mSessions.clear(); 666 for (int i = 0; i < remoteFillServices.size(); i++) { 667 remoteFillServices.valueAt(i).destroy(); 668 } 669 670 sendStateToClients(true); 671 if (mClients != null) { 672 mClients.kill(); 673 mClients = null; 674 } 675 } 676 677 @NonNull 678 CharSequence getServiceLabel() { 679 final CharSequence label = mInfo.getServiceInfo().loadSafeLabel( 680 mContext.getPackageManager(), 0 /* do not ellipsize */, 681 PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE | PackageItemInfo.SAFE_LABEL_FLAG_TRIM); 682 return label; 683 } 684 685 @NonNull 686 Drawable getServiceIcon() { 687 return mInfo.getServiceInfo().loadIcon(mContext.getPackageManager()); 688 } 689 690 /** 691 * Initializes the last fill selection after an autofill service returned a new 692 * {@link FillResponse}. 693 */ 694 void setLastResponse(int sessionId, @NonNull FillResponse response) { 695 synchronized (mLock) { 696 mEventHistory = new FillEventHistory(sessionId, response.getClientState()); 697 } 698 } 699 700 /** 701 * Resets the last fill selection. 702 */ 703 void resetLastResponse() { 704 synchronized (mLock) { 705 mEventHistory = null; 706 } 707 } 708 709 @GuardedBy("mLock") 710 private boolean isValidEventLocked(String method, int sessionId) { 711 if (mEventHistory == null) { 712 Slog.w(TAG, method + ": not logging event because history is null"); 713 return false; 714 } 715 if (sessionId != mEventHistory.getSessionId()) { 716 if (sDebug) { 717 Slog.d(TAG, method + ": not logging event for session " + sessionId 718 + " because tracked session is " + mEventHistory.getSessionId()); 719 } 720 return false; 721 } 722 return true; 723 } 724 725 /** 726 * Updates the last fill selection when an authentication was selected. 727 */ 728 void setAuthenticationSelected(int sessionId, @Nullable Bundle clientState) { 729 synchronized (mLock) { 730 if (isValidEventLocked("setAuthenticationSelected()", sessionId)) { 731 mEventHistory.addEvent( 732 new Event(Event.TYPE_AUTHENTICATION_SELECTED, null, clientState, null, null, 733 null, null, null, null, null, null)); 734 } 735 } 736 } 737 738 /** 739 * Updates the last fill selection when an dataset authentication was selected. 740 */ 741 void logDatasetAuthenticationSelected(@Nullable String selectedDataset, int sessionId, 742 @Nullable Bundle clientState) { 743 synchronized (mLock) { 744 if (isValidEventLocked("logDatasetAuthenticationSelected()", sessionId)) { 745 mEventHistory.addEvent( 746 new Event(Event.TYPE_DATASET_AUTHENTICATION_SELECTED, selectedDataset, 747 clientState, null, null, null, null, null, null, null, null)); 748 } 749 } 750 } 751 752 /** 753 * Updates the last fill selection when an save Ui is shown. 754 */ 755 void logSaveShown(int sessionId, @Nullable Bundle clientState) { 756 synchronized (mLock) { 757 if (isValidEventLocked("logSaveShown()", sessionId)) { 758 mEventHistory.addEvent(new Event(Event.TYPE_SAVE_SHOWN, null, clientState, null, 759 null, null, null, null, null, null, null)); 760 } 761 } 762 } 763 764 /** 765 * Updates the last fill response when a dataset was selected. 766 */ 767 void logDatasetSelected(@Nullable String selectedDataset, int sessionId, 768 @Nullable Bundle clientState) { 769 synchronized (mLock) { 770 if (isValidEventLocked("logDatasetSelected()", sessionId)) { 771 mEventHistory.addEvent( 772 new Event(Event.TYPE_DATASET_SELECTED, selectedDataset, clientState, null, 773 null, null, null, null, null, null, null)); 774 } 775 } 776 } 777 778 /** 779 * Updates the last fill response when an autofill context is committed. 780 */ 781 @GuardedBy("mLock") 782 void logContextCommittedLocked(int sessionId, @Nullable Bundle clientState, 783 @Nullable ArrayList<String> selectedDatasets, 784 @Nullable ArraySet<String> ignoredDatasets, 785 @Nullable ArrayList<AutofillId> changedFieldIds, 786 @Nullable ArrayList<String> changedDatasetIds, 787 @Nullable ArrayList<AutofillId> manuallyFilledFieldIds, 788 @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds, 789 @NonNull ComponentName appComponentName, boolean compatMode) { 790 logContextCommittedLocked(sessionId, clientState, selectedDatasets, ignoredDatasets, 791 changedFieldIds, changedDatasetIds, manuallyFilledFieldIds, 792 manuallyFilledDatasetIds, null, null, appComponentName, compatMode); 793 } 794 795 @GuardedBy("mLock") 796 void logContextCommittedLocked(int sessionId, @Nullable Bundle clientState, 797 @Nullable ArrayList<String> selectedDatasets, 798 @Nullable ArraySet<String> ignoredDatasets, 799 @Nullable ArrayList<AutofillId> changedFieldIds, 800 @Nullable ArrayList<String> changedDatasetIds, 801 @Nullable ArrayList<AutofillId> manuallyFilledFieldIds, 802 @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds, 803 @Nullable ArrayList<AutofillId> detectedFieldIdsList, 804 @Nullable ArrayList<FieldClassification> detectedFieldClassificationsList, 805 @NonNull ComponentName appComponentName, boolean compatMode) { 806 if (isValidEventLocked("logDatasetNotSelected()", sessionId)) { 807 if (sVerbose) { 808 Slog.v(TAG, "logContextCommitted() with FieldClassification: id=" + sessionId 809 + ", selectedDatasets=" + selectedDatasets 810 + ", ignoredDatasetIds=" + ignoredDatasets 811 + ", changedAutofillIds=" + changedFieldIds 812 + ", changedDatasetIds=" + changedDatasetIds 813 + ", manuallyFilledFieldIds=" + manuallyFilledFieldIds 814 + ", detectedFieldIds=" + detectedFieldIdsList 815 + ", detectedFieldClassifications=" + detectedFieldClassificationsList 816 + ", appComponentName=" + appComponentName.toShortString() 817 + ", compatMode=" + compatMode); 818 } 819 AutofillId[] detectedFieldsIds = null; 820 FieldClassification[] detectedFieldClassifications = null; 821 if (detectedFieldIdsList != null) { 822 detectedFieldsIds = new AutofillId[detectedFieldIdsList.size()]; 823 detectedFieldIdsList.toArray(detectedFieldsIds); 824 detectedFieldClassifications = 825 new FieldClassification[detectedFieldClassificationsList.size()]; 826 detectedFieldClassificationsList.toArray(detectedFieldClassifications); 827 828 final int numberFields = detectedFieldsIds.length; 829 int totalSize = 0; 830 float totalScore = 0; 831 for (int i = 0; i < numberFields; i++) { 832 final FieldClassification fc = detectedFieldClassifications[i]; 833 final List<Match> matches = fc.getMatches(); 834 final int size = matches.size(); 835 totalSize += size; 836 for (int j = 0; j < size; j++) { 837 totalScore += matches.get(j).getScore(); 838 } 839 } 840 841 final int averageScore = (int) ((totalScore * 100) / totalSize); 842 mMetricsLogger.write(Helper 843 .newLogMaker(MetricsEvent.AUTOFILL_FIELD_CLASSIFICATION_MATCHES, 844 appComponentName, getServicePackageName(), sessionId, compatMode) 845 .setCounterValue(numberFields) 846 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_MATCH_SCORE, 847 averageScore)); 848 } 849 mEventHistory.addEvent(new Event(Event.TYPE_CONTEXT_COMMITTED, null, 850 clientState, selectedDatasets, ignoredDatasets, 851 changedFieldIds, changedDatasetIds, 852 manuallyFilledFieldIds, manuallyFilledDatasetIds, 853 detectedFieldsIds, detectedFieldClassifications)); 854 } 855 } 856 857 /** 858 * Gets the fill event history. 859 * 860 * @param callingUid The calling uid 861 * 862 * @return The history or {@code null} if there is none. 863 */ 864 FillEventHistory getFillEventHistory(int callingUid) { 865 synchronized (mLock) { 866 if (mEventHistory != null 867 && isCalledByServiceLocked("getFillEventHistory", callingUid)) { 868 return mEventHistory; 869 } 870 } 871 return null; 872 } 873 874 // Called by Session - does not need to check uid 875 UserData getUserData() { 876 synchronized (mLock) { 877 return mUserData; 878 } 879 } 880 881 // Called by AutofillManager 882 UserData getUserData(int callingUid) { 883 synchronized (mLock) { 884 if (isCalledByServiceLocked("getUserData", callingUid)) { 885 return mUserData; 886 } 887 } 888 return null; 889 } 890 891 // Called by AutofillManager 892 void setUserData(int callingUid, UserData userData) { 893 synchronized (mLock) { 894 if (!isCalledByServiceLocked("setUserData", callingUid)) { 895 return; 896 } 897 mUserData = userData; 898 // Log it 899 final int numberFields = mUserData == null ? 0: mUserData.getCategoryIds().length; 900 // NOTE: contrary to most metrics, the service name is logged as the main package name 901 // here, not as MetricsEvent.FIELD_AUTOFILL_SERVICE 902 mMetricsLogger.write(new LogMaker(MetricsEvent.AUTOFILL_USERDATA_UPDATED) 903 .setPackageName(getServicePackageName()) 904 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, numberFields)); 905 } 906 } 907 908 @GuardedBy("mLock") 909 private boolean isCalledByServiceLocked(String methodName, int callingUid) { 910 if (getServiceUidLocked() != callingUid) { 911 Slog.w(TAG, methodName + "() called by UID " + callingUid 912 + ", but service UID is " + getServiceUidLocked()); 913 return false; 914 } 915 return true; 916 } 917 918 @GuardedBy("mLock") 919 void dumpLocked(String prefix, PrintWriter pw) { 920 final String prefix2 = prefix + " "; 921 922 pw.print(prefix); pw.print("User: "); pw.println(mUserId); 923 pw.print(prefix); pw.print("UID: "); pw.println(getServiceUidLocked()); 924 pw.print(prefix); pw.print("Autofill Service Info: "); 925 if (mInfo == null) { 926 pw.println("N/A"); 927 } else { 928 pw.println(); 929 mInfo.dump(prefix2, pw); 930 pw.print(prefix); pw.print("Service Label: "); pw.println(getServiceLabel()); 931 } 932 pw.print(prefix); pw.print("Component from settings: "); 933 pw.println(getComponentNameFromSettings()); 934 pw.print(prefix); pw.print("Default component: "); 935 pw.println(mContext.getString(R.string.config_defaultAutofillService)); 936 pw.print(prefix); pw.print("Disabled: "); pw.println(mDisabled); 937 pw.print(prefix); pw.print("Field classification enabled: "); 938 pw.println(isFieldClassificationEnabledLocked()); 939 pw.print(prefix); pw.print("Compat pkgs: "); 940 final ArrayMap<String, Long> compatPkgs = getCompatibilityPackagesLocked(); 941 if (compatPkgs == null) { 942 pw.println("N/A"); 943 } else { 944 pw.println(compatPkgs); 945 } 946 pw.print(prefix); pw.print("Setup complete: "); pw.println(mSetupComplete); 947 pw.print(prefix); pw.print("Last prune: "); pw.println(mLastPrune); 948 949 pw.print(prefix); pw.print("Disabled apps: "); 950 951 if (mDisabledApps == null) { 952 pw.println("N/A"); 953 } else { 954 final int size = mDisabledApps.size(); 955 pw.println(size); 956 final StringBuilder builder = new StringBuilder(); 957 final long now = SystemClock.elapsedRealtime(); 958 for (int i = 0; i < size; i++) { 959 final String packageName = mDisabledApps.keyAt(i); 960 final long expiration = mDisabledApps.valueAt(i); 961 builder.append(prefix).append(prefix) 962 .append(i).append(". ").append(packageName).append(": "); 963 TimeUtils.formatDuration((expiration - now), builder); 964 builder.append('\n'); 965 } 966 pw.println(builder); 967 } 968 969 pw.print(prefix); pw.print("Disabled activities: "); 970 971 if (mDisabledActivities == null) { 972 pw.println("N/A"); 973 } else { 974 final int size = mDisabledActivities.size(); 975 pw.println(size); 976 final StringBuilder builder = new StringBuilder(); 977 final long now = SystemClock.elapsedRealtime(); 978 for (int i = 0; i < size; i++) { 979 final ComponentName component = mDisabledActivities.keyAt(i); 980 final long expiration = mDisabledActivities.valueAt(i); 981 builder.append(prefix).append(prefix) 982 .append(i).append(". ").append(component).append(": "); 983 TimeUtils.formatDuration((expiration - now), builder); 984 builder.append('\n'); 985 } 986 pw.println(builder); 987 } 988 989 final int size = mSessions.size(); 990 if (size == 0) { 991 pw.print(prefix); pw.println("No sessions"); 992 } else { 993 pw.print(prefix); pw.print(size); pw.println(" sessions:"); 994 for (int i = 0; i < size; i++) { 995 pw.print(prefix); pw.print("#"); pw.println(i + 1); 996 mSessions.valueAt(i).dumpLocked(prefix2, pw); 997 } 998 } 999 1000 pw.print(prefix); pw.print("Clients: "); 1001 if (mClients == null) { 1002 pw.println("N/A"); 1003 } else { 1004 pw.println(); 1005 mClients.dump(pw, prefix2); 1006 } 1007 1008 if (mEventHistory == null || mEventHistory.getEvents() == null 1009 || mEventHistory.getEvents().size() == 0) { 1010 pw.print(prefix); pw.println("No event on last fill response"); 1011 } else { 1012 pw.print(prefix); pw.println("Events of last fill response:"); 1013 pw.print(prefix); 1014 1015 int numEvents = mEventHistory.getEvents().size(); 1016 for (int i = 0; i < numEvents; i++) { 1017 final Event event = mEventHistory.getEvents().get(i); 1018 pw.println(" " + i + ": eventType=" + event.getType() + " datasetId=" 1019 + event.getDatasetId()); 1020 } 1021 } 1022 1023 pw.print(prefix); pw.print("User data: "); 1024 if (mUserData == null) { 1025 pw.println("N/A"); 1026 } else { 1027 pw.println(); 1028 mUserData.dump(prefix2, pw); 1029 } 1030 1031 pw.print(prefix); pw.println("Field Classification strategy: "); 1032 mFieldClassificationStrategy.dump(prefix2, pw); 1033 } 1034 1035 @GuardedBy("mLock") 1036 void destroySessionsLocked() { 1037 if (mSessions.size() == 0) { 1038 mUi.destroyAll(null, null, false); 1039 return; 1040 } 1041 while (mSessions.size() > 0) { 1042 mSessions.valueAt(0).forceRemoveSelfLocked(); 1043 } 1044 } 1045 1046 // TODO(b/64940307): remove this method if SaveUI is refactored to be attached on activities 1047 @GuardedBy("mLock") 1048 void destroyFinishedSessionsLocked() { 1049 final int sessionCount = mSessions.size(); 1050 for (int i = sessionCount - 1; i >= 0; i--) { 1051 final Session session = mSessions.valueAt(i); 1052 if (session.isSavingLocked()) { 1053 if (sDebug) Slog.d(TAG, "destroyFinishedSessionsLocked(): " + session.id); 1054 session.forceRemoveSelfLocked(); 1055 } 1056 } 1057 } 1058 1059 @GuardedBy("mLock") 1060 void listSessionsLocked(ArrayList<String> output) { 1061 final int numSessions = mSessions.size(); 1062 for (int i = 0; i < numSessions; i++) { 1063 output.add((mInfo != null ? mInfo.getServiceInfo().getComponentName() 1064 : null) + ":" + mSessions.keyAt(i)); 1065 } 1066 } 1067 1068 @GuardedBy("mLock") 1069 @Nullable ArrayMap<String, Long> getCompatibilityPackagesLocked() { 1070 if (mInfo != null) { 1071 return mInfo.getCompatibilityPackages(); 1072 } 1073 return null; 1074 } 1075 1076 private void sendStateToClients(boolean resetClient) { 1077 final RemoteCallbackList<IAutoFillManagerClient> clients; 1078 final int userClientCount; 1079 synchronized (mLock) { 1080 if (mClients == null) { 1081 return; 1082 } 1083 clients = mClients; 1084 userClientCount = clients.beginBroadcast(); 1085 } 1086 try { 1087 for (int i = 0; i < userClientCount; i++) { 1088 final IAutoFillManagerClient client = clients.getBroadcastItem(i); 1089 try { 1090 final boolean resetSession; 1091 final boolean isEnabled; 1092 synchronized (mLock) { 1093 resetSession = resetClient || isClientSessionDestroyedLocked(client); 1094 isEnabled = isEnabledLocked(); 1095 } 1096 int flags = 0; 1097 if (isEnabled) { 1098 flags |= AutofillManager.SET_STATE_FLAG_ENABLED; 1099 } 1100 if (resetSession) { 1101 flags |= AutofillManager.SET_STATE_FLAG_RESET_SESSION; 1102 } 1103 if (resetClient) { 1104 flags |= AutofillManager.SET_STATE_FLAG_RESET_CLIENT; 1105 } 1106 if (sDebug) { 1107 flags |= AutofillManager.SET_STATE_FLAG_DEBUG; 1108 } 1109 if (sVerbose) { 1110 flags |= AutofillManager.SET_STATE_FLAG_VERBOSE; 1111 } 1112 client.setState(flags); 1113 } catch (RemoteException re) { 1114 /* ignore */ 1115 } 1116 } 1117 } finally { 1118 clients.finishBroadcast(); 1119 } 1120 } 1121 1122 @GuardedBy("mLock") 1123 private boolean isClientSessionDestroyedLocked(IAutoFillManagerClient client) { 1124 final int sessionCount = mSessions.size(); 1125 for (int i = 0; i < sessionCount; i++) { 1126 final Session session = mSessions.valueAt(i); 1127 if (session.getClient().equals(client)) { 1128 return session.isDestroyed(); 1129 } 1130 } 1131 return true; 1132 } 1133 1134 @GuardedBy("mLock") 1135 boolean isEnabledLocked() { 1136 return mSetupComplete && mInfo != null && !mDisabled; 1137 } 1138 1139 /** 1140 * Called by {@link Session} when service asked to disable autofill for an app. 1141 */ 1142 void disableAutofillForApp(@NonNull String packageName, long duration, int sessionId, 1143 boolean compatMode) { 1144 synchronized (mLock) { 1145 if (mDisabledApps == null) { 1146 mDisabledApps = new ArrayMap<>(1); 1147 } 1148 long expiration = SystemClock.elapsedRealtime() + duration; 1149 // Protect it against overflow 1150 if (expiration < 0) { 1151 expiration = Long.MAX_VALUE; 1152 } 1153 mDisabledApps.put(packageName, expiration); 1154 int intDuration = duration > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) duration; 1155 mMetricsLogger.write(Helper.newLogMaker(MetricsEvent.AUTOFILL_SERVICE_DISABLED_APP, 1156 packageName, getServicePackageName(), sessionId, compatMode) 1157 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_DURATION, intDuration)); 1158 } 1159 } 1160 1161 /** 1162 * Called by {@link Session} when service asked to disable autofill an app. 1163 */ 1164 void disableAutofillForActivity(@NonNull ComponentName componentName, long duration, 1165 int sessionId, boolean compatMode) { 1166 synchronized (mLock) { 1167 if (mDisabledActivities == null) { 1168 mDisabledActivities = new ArrayMap<>(1); 1169 } 1170 long expiration = SystemClock.elapsedRealtime() + duration; 1171 // Protect it against overflow 1172 if (expiration < 0) { 1173 expiration = Long.MAX_VALUE; 1174 } 1175 mDisabledActivities.put(componentName, expiration); 1176 final int intDuration = duration > Integer.MAX_VALUE 1177 ? Integer.MAX_VALUE 1178 : (int) duration; 1179 // NOTE: not using Helper.newLogMaker() because we're setting the componentName instead 1180 // of package name 1181 final LogMaker log = new LogMaker(MetricsEvent.AUTOFILL_SERVICE_DISABLED_ACTIVITY) 1182 .setComponentName(componentName) 1183 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE, getServicePackageName()) 1184 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_DURATION, intDuration) 1185 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SESSION_ID, sessionId); 1186 if (compatMode) { 1187 log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_COMPAT_MODE, 1); 1188 } 1189 mMetricsLogger.write(log); 1190 } 1191 } 1192 1193 /** 1194 * Checks if autofill is disabled by service to the given activity. 1195 */ 1196 @GuardedBy("mLock") 1197 private boolean isAutofillDisabledLocked(@NonNull ComponentName componentName) { 1198 // Check activities first. 1199 long elapsedTime = 0; 1200 if (mDisabledActivities != null) { 1201 elapsedTime = SystemClock.elapsedRealtime(); 1202 final Long expiration = mDisabledActivities.get(componentName); 1203 if (expiration != null) { 1204 if (expiration >= elapsedTime) return true; 1205 // Restriction expired - clean it up. 1206 if (sVerbose) { 1207 Slog.v(TAG, "Removing " + componentName.toShortString() 1208 + " from disabled list"); 1209 } 1210 mDisabledActivities.remove(componentName); 1211 } 1212 } 1213 1214 // Then check apps. 1215 final String packageName = componentName.getPackageName(); 1216 if (mDisabledApps == null) return false; 1217 1218 final Long expiration = mDisabledApps.get(packageName); 1219 if (expiration == null) return false; 1220 1221 if (elapsedTime == 0) { 1222 elapsedTime = SystemClock.elapsedRealtime(); 1223 } 1224 1225 if (expiration >= elapsedTime) return true; 1226 1227 // Restriction expired - clean it up. 1228 if (sVerbose) Slog.v(TAG, "Removing " + packageName + " from disabled list"); 1229 mDisabledApps.remove(packageName); 1230 return false; 1231 } 1232 1233 // Called by AutofillManager, checks UID. 1234 boolean isFieldClassificationEnabled(int callingUid) { 1235 synchronized (mLock) { 1236 if (!isCalledByServiceLocked("isFieldClassificationEnabled", callingUid)) { 1237 return false; 1238 } 1239 return isFieldClassificationEnabledLocked(); 1240 } 1241 } 1242 1243 // Called by internally, no need to check UID. 1244 boolean isFieldClassificationEnabledLocked() { 1245 return Settings.Secure.getIntForUser( 1246 mContext.getContentResolver(), 1247 Settings.Secure.AUTOFILL_FEATURE_FIELD_CLASSIFICATION, 1, 1248 mUserId) == 1; 1249 } 1250 1251 FieldClassificationStrategy getFieldClassificationStrategy() { 1252 return mFieldClassificationStrategy; 1253 } 1254 1255 String[] getAvailableFieldClassificationAlgorithms(int callingUid) { 1256 synchronized (mLock) { 1257 if (!isCalledByServiceLocked("getFCAlgorithms()", callingUid)) { 1258 return null; 1259 } 1260 } 1261 return mFieldClassificationStrategy.getAvailableAlgorithms(); 1262 } 1263 1264 String getDefaultFieldClassificationAlgorithm(int callingUid) { 1265 synchronized (mLock) { 1266 if (!isCalledByServiceLocked("getDefaultFCAlgorithm()", callingUid)) { 1267 return null; 1268 } 1269 } 1270 return mFieldClassificationStrategy.getDefaultAlgorithm(); 1271 } 1272 1273 @Override 1274 public String toString() { 1275 return "AutofillManagerServiceImpl: [userId=" + mUserId 1276 + ", component=" + (mInfo != null 1277 ? mInfo.getServiceInfo().getComponentName() : null) + "]"; 1278 } 1279 1280 /** Task used to prune abandoned session */ 1281 private class PruneTask extends AsyncTask<Void, Void, Void> { 1282 @Override 1283 protected Void doInBackground(Void... ignored) { 1284 int numSessionsToRemove; 1285 1286 SparseArray<IBinder> sessionsToRemove; 1287 1288 synchronized (mLock) { 1289 numSessionsToRemove = mSessions.size(); 1290 sessionsToRemove = new SparseArray<>(numSessionsToRemove); 1291 1292 for (int i = 0; i < numSessionsToRemove; i++) { 1293 Session session = mSessions.valueAt(i); 1294 1295 sessionsToRemove.put(session.id, session.getActivityTokenLocked()); 1296 } 1297 } 1298 1299 IActivityManager am = ActivityManager.getService(); 1300 1301 // Only remove sessions which's activities are not known to the activity manager anymore 1302 for (int i = 0; i < numSessionsToRemove; i++) { 1303 try { 1304 // The activity manager cannot resolve activities that have been removed 1305 if (am.getActivityClassForToken(sessionsToRemove.valueAt(i)) != null) { 1306 sessionsToRemove.removeAt(i); 1307 i--; 1308 numSessionsToRemove--; 1309 } 1310 } catch (RemoteException e) { 1311 Slog.w(TAG, "Cannot figure out if activity is finished", e); 1312 } 1313 } 1314 1315 synchronized (mLock) { 1316 for (int i = 0; i < numSessionsToRemove; i++) { 1317 Session sessionToRemove = mSessions.get(sessionsToRemove.keyAt(i)); 1318 1319 if (sessionToRemove != null && sessionsToRemove.valueAt(i) 1320 == sessionToRemove.getActivityTokenLocked()) { 1321 if (sessionToRemove.isSavingLocked()) { 1322 if (sVerbose) { 1323 Slog.v(TAG, "Session " + sessionToRemove.id + " is saving"); 1324 } 1325 } else { 1326 if (sDebug) { 1327 Slog.i(TAG, "Prune session " + sessionToRemove.id + " (" 1328 + sessionToRemove.getActivityTokenLocked() + ")"); 1329 } 1330 sessionToRemove.removeSelfLocked(); 1331 } 1332 } 1333 } 1334 } 1335 1336 return null; 1337 } 1338 } 1339 } 1340