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