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