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