AutofillManagerServiceImpl.java revision e11139c73d234efdd672a91830b09c92f6120d5e
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.AppGlobals; 30import android.app.IActivityManager; 31import android.content.ComponentName; 32import android.content.Context; 33import android.content.pm.ApplicationInfo; 34import android.content.pm.PackageManager; 35import android.content.pm.ServiceInfo; 36import android.graphics.Rect; 37import android.graphics.drawable.Drawable; 38import android.os.AsyncTask; 39import android.os.Binder; 40import android.os.Bundle; 41import android.os.IBinder; 42import android.os.Looper; 43import android.os.RemoteCallbackList; 44import android.os.RemoteException; 45import android.os.UserManager; 46import android.provider.Settings; 47import android.service.autofill.AutofillService; 48import android.service.autofill.AutofillServiceInfo; 49import android.service.autofill.FillEventHistory; 50import android.service.autofill.FillEventHistory.Event; 51import android.service.autofill.FillResponse; 52import android.service.autofill.IAutoFillService; 53import android.text.TextUtils; 54import android.util.ArraySet; 55import android.util.DebugUtils; 56import android.util.LocalLog; 57import android.util.Slog; 58import android.util.SparseArray; 59import android.view.autofill.AutofillId; 60import android.view.autofill.AutofillManager; 61import android.view.autofill.AutofillValue; 62import android.view.autofill.IAutoFillManagerClient; 63 64import com.android.internal.R; 65import com.android.internal.annotations.GuardedBy; 66import com.android.internal.os.HandlerCaller; 67import com.android.server.autofill.ui.AutoFillUI; 68 69import java.io.PrintWriter; 70import java.util.ArrayList; 71import java.util.Random; 72 73/** 74 * Bridge between the {@code system_server}'s {@link AutofillManagerService} and the 75 * app's {@link IAutoFillService} implementation. 76 * 77 */ 78final class AutofillManagerServiceImpl { 79 80 private static final String TAG = "AutofillManagerServiceImpl"; 81 private static final int MAX_SESSION_ID_CREATE_TRIES = 2048; 82 83 /** Minimum interval to prune abandoned sessions */ 84 private static final int MAX_ABANDONED_SESSION_MILLIS = 30000; 85 86 static final int MSG_SERVICE_SAVE = 1; 87 88 private final int mUserId; 89 private final Context mContext; 90 private final Object mLock; 91 private final AutoFillUI mUi; 92 93 private RemoteCallbackList<IAutoFillManagerClient> mClients; 94 private AutofillServiceInfo mInfo; 95 96 private static final Random sRandom = new Random(); 97 98 private final LocalLog mRequestsHistory; 99 /** 100 * Whether service was disabled for user due to {@link UserManager} restrictions. 101 */ 102 private boolean mDisabled; 103 104 /** 105 * Caches whether the setup completed for the current user. 106 */ 107 @GuardedBy("mLock") 108 private boolean mSetupComplete; 109 110 private final HandlerCaller.Callback mHandlerCallback = (msg) -> { 111 switch (msg.what) { 112 case MSG_SERVICE_SAVE: 113 handleSessionSave(msg.arg1); 114 break; 115 default: 116 Slog.w(TAG, "invalid msg on handler: " + msg); 117 } 118 }; 119 120 private final HandlerCaller mHandlerCaller = new HandlerCaller(null, Looper.getMainLooper(), 121 mHandlerCallback, true); 122 123 /** 124 * Cache of pending {@link Session}s, keyed by sessionId. 125 * 126 * <p>They're kept until the {@link AutofillService} finished handling a request, an error 127 * occurs, or the session is abandoned. 128 */ 129 @GuardedBy("mLock") 130 private final SparseArray<Session> mSessions = new SparseArray<>(); 131 132 /** The last selection */ 133 @GuardedBy("mLock") 134 private FillEventHistory mEventHistory; 135 136 /** When was {@link PruneTask} last executed? */ 137 private long mLastPrune = 0; 138 139 AutofillManagerServiceImpl(Context context, Object lock, LocalLog requestsHistory, 140 int userId, AutoFillUI ui, boolean disabled) { 141 mContext = context; 142 mLock = lock; 143 mRequestsHistory = requestsHistory; 144 mUserId = userId; 145 mUi = ui; 146 updateLocked(disabled); 147 } 148 149 CharSequence getServiceName() { 150 final String packageName = getPackageName(); 151 if (packageName == null) { 152 return null; 153 } 154 155 try { 156 final PackageManager pm = mContext.getPackageManager(); 157 final ApplicationInfo info = pm.getApplicationInfo(packageName, 0); 158 return pm.getApplicationLabel(info); 159 } catch (Exception e) { 160 Slog.e(TAG, "Could not get label for " + packageName + ": " + e); 161 return packageName; 162 } 163 } 164 165 String getPackageName() { 166 final ComponentName serviceComponent = getServiceComponentName(); 167 if (serviceComponent != null) { 168 return serviceComponent.getPackageName(); 169 } 170 return null; 171 } 172 173 ComponentName getServiceComponentName() { 174 synchronized (mLock) { 175 if (mInfo == null) { 176 return null; 177 } 178 return mInfo.getServiceInfo().getComponentName(); 179 } 180 } 181 182 private boolean isSetupCompletedLocked() { 183 final String setupComplete = Settings.Secure.getStringForUser( 184 mContext.getContentResolver(), Settings.Secure.USER_SETUP_COMPLETE, mUserId); 185 return "1".equals(setupComplete); 186 } 187 188 private String getComponentNameFromSettings() { 189 return Settings.Secure.getStringForUser( 190 mContext.getContentResolver(), Settings.Secure.AUTOFILL_SERVICE, mUserId); 191 } 192 193 void updateLocked(boolean disabled) { 194 final boolean wasEnabled = isEnabled(); 195 if (sVerbose) { 196 Slog.v(TAG, "updateLocked(u=" + mUserId + "): wasEnabled=" + wasEnabled 197 + ", mSetupComplete= " + mSetupComplete 198 + ", disabled=" + disabled + ", mDisabled=" + mDisabled); 199 } 200 mSetupComplete = isSetupCompletedLocked(); 201 mDisabled = disabled; 202 ComponentName serviceComponent = null; 203 ServiceInfo serviceInfo = null; 204 final String componentName = getComponentNameFromSettings(); 205 if (!TextUtils.isEmpty(componentName)) { 206 try { 207 serviceComponent = ComponentName.unflattenFromString(componentName); 208 serviceInfo = AppGlobals.getPackageManager().getServiceInfo(serviceComponent, 209 0, mUserId); 210 } catch (RuntimeException | RemoteException e) { 211 Slog.e(TAG, "Bad autofill service name " + componentName + ": " + e); 212 return; 213 } 214 } 215 try { 216 if (serviceInfo != null) { 217 mInfo = new AutofillServiceInfo(mContext.getPackageManager(), 218 serviceComponent, mUserId); 219 } else { 220 mInfo = null; 221 } 222 final boolean isEnabled = isEnabled(); 223 if (wasEnabled != isEnabled) { 224 if (!isEnabled) { 225 final int sessionCount = mSessions.size(); 226 for (int i = sessionCount - 1; i >= 0; i--) { 227 final Session session = mSessions.valueAt(i); 228 session.removeSelfLocked(); 229 } 230 } 231 sendStateToClients(false); 232 } 233 } catch (Exception e) { 234 Slog.e(TAG, "Bad AutofillService '" + componentName + "': " + e); 235 } 236 } 237 238 boolean addClientLocked(IAutoFillManagerClient client) { 239 if (mClients == null) { 240 mClients = new RemoteCallbackList<>(); 241 } 242 mClients.register(client); 243 return isEnabled(); 244 } 245 246 void setAuthenticationResultLocked(Bundle data, int sessionId, int authenticationId, int uid) { 247 if (!isEnabled()) { 248 return; 249 } 250 final Session session = mSessions.get(sessionId); 251 if (session != null && uid == session.uid) { 252 session.setAuthenticationResultLocked(data, authenticationId); 253 } 254 } 255 256 void setHasCallback(int sessionId, int uid, boolean hasIt) { 257 if (!isEnabled()) { 258 return; 259 } 260 final Session session = mSessions.get(sessionId); 261 if (session != null && uid == session.uid) { 262 synchronized (mLock) { 263 session.setHasCallbackLocked(hasIt); 264 } 265 } 266 } 267 268 int startSessionLocked(@NonNull IBinder activityToken, int uid, 269 @NonNull IBinder appCallbackToken, @NonNull AutofillId autofillId, 270 @NonNull Rect virtualBounds, @Nullable AutofillValue value, boolean hasCallback, 271 int flags, @NonNull String packageName) { 272 if (!isEnabled()) { 273 return 0; 274 } 275 if (sVerbose) Slog.v(TAG, "startSession(): token=" + activityToken + ", flags=" + flags); 276 277 // Occasionally clean up abandoned sessions 278 pruneAbandonedSessionsLocked(); 279 280 final Session newSession = createSessionByTokenLocked(activityToken, uid, appCallbackToken, 281 hasCallback, packageName); 282 if (newSession == null) { 283 return NO_SESSION; 284 } 285 286 final String historyItem = 287 "id=" + newSession.id + " uid=" + uid + " s=" + mInfo.getServiceInfo().packageName 288 + " u=" + mUserId + " i=" + autofillId + " b=" + virtualBounds + " hc=" + 289 hasCallback + " f=" + flags; 290 mRequestsHistory.log(historyItem); 291 292 newSession.updateLocked(autofillId, virtualBounds, value, ACTION_START_SESSION, flags); 293 294 return newSession.id; 295 } 296 297 /** 298 * Remove abandoned sessions if needed. 299 */ 300 private void pruneAbandonedSessionsLocked() { 301 long now = System.currentTimeMillis(); 302 if (mLastPrune < now - MAX_ABANDONED_SESSION_MILLIS) { 303 mLastPrune = now; 304 305 if (mSessions.size() > 0) { 306 (new PruneTask()).execute(); 307 } 308 } 309 } 310 311 void finishSessionLocked(int sessionId, int uid) { 312 if (!isEnabled()) { 313 return; 314 } 315 316 final Session session = mSessions.get(sessionId); 317 if (session == null || uid != session.uid) { 318 if (sVerbose) { 319 Slog.v(TAG, "finishSessionLocked(): no session for " + sessionId + "(" + uid + ")"); 320 } 321 return; 322 } 323 324 final boolean finished = session.showSaveLocked(); 325 if (sVerbose) Slog.v(TAG, "finishSessionLocked(): session finished on save? " + finished); 326 327 if (finished) { 328 session.removeSelfLocked(); 329 } 330 } 331 332 void cancelSessionLocked(int sessionId, int uid) { 333 if (!isEnabled()) { 334 return; 335 } 336 337 final Session session = mSessions.get(sessionId); 338 if (session == null || uid != session.uid) { 339 Slog.w(TAG, "cancelSessionLocked(): no session for " + sessionId + "(" + uid + ")"); 340 return; 341 } 342 session.removeSelfLocked(); 343 } 344 345 void disableOwnedAutofillServicesLocked(int uid) { 346 if (mInfo == null || mInfo.getServiceInfo().applicationInfo.uid != uid) { 347 return; 348 } 349 final long identity = Binder.clearCallingIdentity(); 350 try { 351 final String autoFillService = getComponentNameFromSettings(); 352 if (mInfo.getServiceInfo().getComponentName().equals( 353 ComponentName.unflattenFromString(autoFillService))) { 354 Settings.Secure.putStringForUser(mContext.getContentResolver(), 355 Settings.Secure.AUTOFILL_SERVICE, null, mUserId); 356 destroySessionsLocked(); 357 } 358 } finally { 359 Binder.restoreCallingIdentity(identity); 360 } 361 } 362 363 private Session createSessionByTokenLocked(@NonNull IBinder activityToken, int uid, 364 @NonNull IBinder appCallbackToken, boolean hasCallback, @NonNull String packageName) { 365 // use random ids so that one app cannot know that another app creates sessions 366 int sessionId; 367 int tries = 0; 368 do { 369 tries++; 370 if (tries > MAX_SESSION_ID_CREATE_TRIES) { 371 Slog.w(TAG, "Cannot create session in " + MAX_SESSION_ID_CREATE_TRIES + " tries"); 372 return null; 373 } 374 375 sessionId = sRandom.nextInt(); 376 } while (sessionId == NO_SESSION || mSessions.indexOfKey(sessionId) >= 0); 377 378 final Session newSession = new Session(this, mUi, mContext, mHandlerCaller, mUserId, mLock, 379 sessionId, uid, activityToken, appCallbackToken, hasCallback, 380 mInfo.getServiceInfo().getComponentName(), packageName); 381 mSessions.put(newSession.id, newSession); 382 383 return newSession; 384 } 385 386 /** 387 * Restores a session after an activity was temporarily destroyed. 388 * 389 * @param sessionId The id of the session to restore 390 * @param uid UID of the process that tries to restore the session 391 * @param activityToken The new instance of the activity 392 * @param appCallback The callbacks to the activity 393 */ 394 boolean restoreSession(int sessionId, int uid, @NonNull IBinder activityToken, 395 @NonNull IBinder appCallback) { 396 final Session session = mSessions.get(sessionId); 397 398 if (session == null || uid != session.uid) { 399 return false; 400 } else { 401 session.switchActivity(activityToken, appCallback); 402 return true; 403 } 404 } 405 406 /** 407 * Updates a session and returns whether it should be restarted. 408 */ 409 boolean updateSessionLocked(int sessionId, int uid, AutofillId autofillId, Rect virtualBounds, 410 AutofillValue value, int action, int flags) { 411 final Session session = mSessions.get(sessionId); 412 if (session == null || session.uid != uid) { 413 if ((flags & FLAG_MANUAL_REQUEST) != 0) { 414 if (sDebug) { 415 Slog.d(TAG, "restarting session " + sessionId + " due to manual request on " 416 + autofillId); 417 } 418 return true; 419 } 420 if (sVerbose) { 421 Slog.v(TAG, "updateSessionLocked(): session gone for " + sessionId 422 + "(" + uid + ")"); 423 } 424 return false; 425 } 426 427 session.updateLocked(autofillId, virtualBounds, value, action, flags); 428 return false; 429 } 430 431 void removeSessionLocked(int sessionId) { 432 mSessions.remove(sessionId); 433 } 434 435 private void handleSessionSave(int sessionId) { 436 synchronized (mLock) { 437 final Session session = mSessions.get(sessionId); 438 if (session == null) { 439 Slog.w(TAG, "handleSessionSave(): already gone: " + sessionId); 440 441 return; 442 } 443 session.callSaveLocked(); 444 } 445 } 446 447 void onPendingSaveUi(int operation, @NonNull IBinder token) { 448 if (sVerbose) Slog.v(TAG, "onPendingSaveUi(" + operation + "): " + token); 449 synchronized (mLock) { 450 final int sessionCount = mSessions.size(); 451 for (int i = sessionCount - 1; i >= 0; i--) { 452 final Session session = mSessions.valueAt(i); 453 if (session.isSaveUiPendingForToken(token)) { 454 session.onPendingSaveUi(operation, token); 455 return; 456 } 457 } 458 } 459 if (sDebug) { 460 Slog.d(TAG, "No pending Save UI for token " + token + " and operation " 461 + DebugUtils.flagsToString(AutofillManager.class, "PENDING_UI_OPERATION_", 462 operation)); 463 } 464 } 465 466 void destroyLocked() { 467 if (sVerbose) Slog.v(TAG, "destroyLocked()"); 468 469 final int numSessions = mSessions.size(); 470 final ArraySet<RemoteFillService> remoteFillServices = new ArraySet<>(numSessions); 471 for (int i = 0; i < numSessions; i++) { 472 final RemoteFillService remoteFillService = mSessions.valueAt(i).destroyLocked(); 473 if (remoteFillService != null) { 474 remoteFillServices.add(remoteFillService); 475 } 476 } 477 mSessions.clear(); 478 for (int i = 0; i < remoteFillServices.size(); i++) { 479 remoteFillServices.valueAt(i).destroy(); 480 } 481 482 sendStateToClients(true); 483 } 484 485 @NonNull 486 CharSequence getServiceLabel() { 487 return mInfo.getServiceInfo().loadLabel(mContext.getPackageManager()); 488 } 489 490 @NonNull 491 Drawable getServiceIcon() { 492 return mInfo.getServiceInfo().loadIcon(mContext.getPackageManager()); 493 } 494 495 /** 496 * Initializes the last fill selection after an autofill service returned a new 497 * {@link FillResponse}. 498 */ 499 void setLastResponse(int serviceUid, int sessionId, @NonNull FillResponse response) { 500 synchronized (mLock) { 501 mEventHistory = new FillEventHistory(serviceUid, sessionId, response.getClientState()); 502 } 503 } 504 505 /** 506 * Resets the last fill selection. 507 */ 508 void resetLastResponse() { 509 synchronized (mLock) { 510 mEventHistory = null; 511 } 512 } 513 514 private boolean isValidEventLocked(String method, int sessionId) { 515 if (mEventHistory == null) { 516 Slog.w(TAG, method + ": not logging event because history is null"); 517 return false; 518 } 519 if (sessionId != mEventHistory.getSessionId()) { 520 if (sDebug) { 521 Slog.d(TAG, method + ": not logging event for session " + sessionId 522 + " because tracked session is " + mEventHistory.getSessionId()); 523 } 524 return false; 525 } 526 return true; 527 } 528 529 /** 530 * Updates the last fill selection when an authentication was selected. 531 */ 532 void setAuthenticationSelected(int sessionId) { 533 synchronized (mLock) { 534 if (isValidEventLocked("setAuthenticationSelected()", sessionId)) { 535 mEventHistory.addEvent(new Event(Event.TYPE_AUTHENTICATION_SELECTED, null)); 536 } 537 } 538 } 539 540 /** 541 * Updates the last fill selection when an dataset authentication was selected. 542 */ 543 void logDatasetAuthenticationSelected(@Nullable String selectedDataset, int sessionId) { 544 synchronized (mLock) { 545 if (isValidEventLocked("logDatasetAuthenticationSelected()", sessionId)) { 546 mEventHistory.addEvent( 547 new Event(Event.TYPE_DATASET_AUTHENTICATION_SELECTED, selectedDataset)); 548 } 549 } 550 } 551 552 /** 553 * Updates the last fill selection when an save Ui is shown. 554 */ 555 void logSaveShown(int sessionId) { 556 synchronized (mLock) { 557 if (isValidEventLocked("logSaveShown()", sessionId)) { 558 mEventHistory.addEvent(new Event(Event.TYPE_SAVE_SHOWN, null)); 559 } 560 } 561 } 562 563 /** 564 * Updates the last fill response when a dataset was selected. 565 */ 566 void logDatasetSelected(@Nullable String selectedDataset, int sessionId) { 567 synchronized (mLock) { 568 if (isValidEventLocked("setDatasetSelected()", sessionId)) { 569 mEventHistory.addEvent(new Event(Event.TYPE_DATASET_SELECTED, selectedDataset)); 570 } 571 } 572 } 573 574 /** 575 * Gets the fill event history. 576 * 577 * @param callingUid The calling uid 578 * 579 * @return The history or {@code null} if there is none. 580 */ 581 FillEventHistory getFillEventHistory(int callingUid) { 582 synchronized (mLock) { 583 if (mEventHistory != null && mEventHistory.getServiceUid() == callingUid) { 584 return mEventHistory; 585 } 586 } 587 588 return null; 589 } 590 591 void dumpLocked(String prefix, PrintWriter pw) { 592 final String prefix2 = prefix + " "; 593 594 pw.print(prefix); pw.print("User: "); pw.println(mUserId); 595 pw.print(prefix); pw.print("Component: "); pw.println(mInfo != null 596 ? mInfo.getServiceInfo().getComponentName() : null); 597 pw.print(prefix); pw.print("Component from settings: "); 598 pw.println(getComponentNameFromSettings()); 599 pw.print(prefix); pw.print("Default component: "); 600 pw.println(mContext.getString(R.string.config_defaultAutofillService)); 601 pw.print(prefix); pw.print("Disabled: "); pw.println(mDisabled); 602 pw.print(prefix); pw.print("Setup complete: "); pw.println(mSetupComplete); 603 pw.print(prefix); pw.print("Last prune: "); pw.println(mLastPrune); 604 605 final int size = mSessions.size(); 606 if (size == 0) { 607 pw.print(prefix); pw.println("No sessions"); 608 } else { 609 pw.print(prefix); pw.print(size); pw.println(" sessions:"); 610 for (int i = 0; i < size; i++) { 611 pw.print(prefix); pw.print("#"); pw.println(i + 1); 612 mSessions.valueAt(i).dumpLocked(prefix2, pw); 613 } 614 } 615 616 if (mEventHistory == null || mEventHistory.getEvents() == null 617 || mEventHistory.getEvents().size() == 0) { 618 pw.print(prefix); pw.println("No event on last fill response"); 619 } else { 620 pw.print(prefix); pw.println("Events of last fill response:"); 621 pw.print(prefix); 622 623 int numEvents = mEventHistory.getEvents().size(); 624 for (int i = 0; i < numEvents; i++) { 625 final Event event = mEventHistory.getEvents().get(i); 626 pw.println(" " + i + ": eventType=" + event.getType() + " datasetId=" 627 + event.getDatasetId()); 628 } 629 } 630 } 631 632 void destroySessionsLocked() { 633 if (mSessions.size() == 0) { 634 mUi.destroyAll(null, null); 635 return; 636 } 637 while (mSessions.size() > 0) { 638 mSessions.valueAt(0).forceRemoveSelfLocked(); 639 } 640 } 641 642 void listSessionsLocked(ArrayList<String> output) { 643 final int numSessions = mSessions.size(); 644 for (int i = 0; i < numSessions; i++) { 645 output.add((mInfo != null ? mInfo.getServiceInfo().getComponentName() 646 : null) + ":" + mSessions.keyAt(i)); 647 } 648 } 649 650 private void sendStateToClients(boolean resetClient) { 651 final RemoteCallbackList<IAutoFillManagerClient> clients; 652 final int userClientCount; 653 synchronized (mLock) { 654 if (mClients == null) { 655 return; 656 } 657 clients = mClients; 658 userClientCount = clients.beginBroadcast(); 659 } 660 try { 661 for (int i = 0; i < userClientCount; i++) { 662 final IAutoFillManagerClient client = clients.getBroadcastItem(i); 663 try { 664 final boolean resetSession; 665 synchronized (mLock) { 666 resetSession = resetClient || isClientSessionDestroyedLocked(client); 667 } 668 client.setState(isEnabled(), resetSession, resetClient); 669 } catch (RemoteException re) { 670 /* ignore */ 671 } 672 } 673 } finally { 674 clients.finishBroadcast(); 675 } 676 } 677 678 private boolean isClientSessionDestroyedLocked(IAutoFillManagerClient client) { 679 final int sessionCount = mSessions.size(); 680 for (int i = 0; i < sessionCount; i++) { 681 final Session session = mSessions.valueAt(i); 682 if (session.getClient().equals(client)) { 683 return session.isDestroyed(); 684 } 685 } 686 return true; 687 } 688 689 boolean isEnabled() { 690 return mSetupComplete && mInfo != null && !mDisabled; 691 } 692 693 @Override 694 public String toString() { 695 return "AutofillManagerServiceImpl: [userId=" + mUserId 696 + ", component=" + (mInfo != null 697 ? mInfo.getServiceInfo().getComponentName() : null) + "]"; 698 } 699 700 /** Task used to prune abandoned session */ 701 private class PruneTask extends AsyncTask<Void, Void, Void> { 702 @Override 703 protected Void doInBackground(Void... ignored) { 704 int numSessionsToRemove; 705 706 SparseArray<IBinder> sessionsToRemove; 707 708 synchronized (mLock) { 709 numSessionsToRemove = mSessions.size(); 710 sessionsToRemove = new SparseArray<>(numSessionsToRemove); 711 712 for (int i = 0; i < numSessionsToRemove; i++) { 713 Session session = mSessions.valueAt(i); 714 715 sessionsToRemove.put(session.id, session.getActivityTokenLocked()); 716 } 717 } 718 719 IActivityManager am = ActivityManager.getService(); 720 721 // Only remove sessions which's activities are not known to the activity manager anymore 722 for (int i = 0; i < numSessionsToRemove; i++) { 723 try { 724 // The activity manager cannot resolve activities that have been removed 725 if (am.getActivityClassForToken(sessionsToRemove.valueAt(i)) != null) { 726 sessionsToRemove.removeAt(i); 727 i--; 728 numSessionsToRemove--; 729 } 730 } catch (RemoteException e) { 731 Slog.w(TAG, "Cannot figure out if activity is finished", e); 732 } 733 } 734 735 synchronized (mLock) { 736 for (int i = 0; i < numSessionsToRemove; i++) { 737 Session sessionToRemove = mSessions.get(sessionsToRemove.keyAt(i)); 738 739 if (sessionToRemove != null && sessionsToRemove.valueAt(i) 740 == sessionToRemove.getActivityTokenLocked()) { 741 if (sessionToRemove.isSavingLocked()) { 742 if (sVerbose) { 743 Slog.v(TAG, "Session " + sessionToRemove.id + " is saving"); 744 } 745 } else { 746 if (sDebug) { 747 Slog.i(TAG, "Prune session " + sessionToRemove.id + " (" 748 + sessionToRemove.getActivityTokenLocked() + ")"); 749 } 750 sessionToRemove.removeSelfLocked(); 751 } 752 } 753 } 754 } 755 756 return null; 757 } 758 } 759} 760