AutofillManagerServiceImpl.java revision 6ef61b82a07d9c6bc7d19a95a22bad438457291d
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.ActivityManagerInternal; 30import android.app.AppGlobals; 31import android.app.IActivityManager; 32import android.content.ComponentName; 33import android.content.Context; 34import android.content.pm.ApplicationInfo; 35import android.content.pm.PackageManager; 36import android.content.pm.PackageManager.NameNotFoundException; 37import android.content.pm.ServiceInfo; 38import android.graphics.Rect; 39import android.graphics.drawable.Drawable; 40import android.metrics.LogMaker; 41import android.os.AsyncTask; 42import android.os.Binder; 43import android.os.Build; 44import android.os.Bundle; 45import android.os.IBinder; 46import android.os.Looper; 47import android.os.RemoteCallbackList; 48import android.os.RemoteException; 49import android.os.SystemClock; 50import android.os.UserHandle; 51import android.os.UserManager; 52import android.provider.Settings; 53import android.service.autofill.AutofillService; 54import android.service.autofill.AutofillServiceInfo; 55import android.service.autofill.FieldClassification; 56import android.service.autofill.FieldClassification.Match; 57import android.service.autofill.FillEventHistory; 58import android.service.autofill.FillEventHistory.Event; 59import android.service.autofill.FillResponse; 60import android.service.autofill.IAutoFillService; 61import android.service.autofill.UserData; 62import android.text.TextUtils; 63import android.util.ArrayMap; 64import android.util.ArraySet; 65import android.util.DebugUtils; 66import android.util.LocalLog; 67import android.util.Pair; 68import android.util.Slog; 69import android.util.SparseArray; 70import android.util.TimeUtils; 71import android.view.autofill.AutofillId; 72import android.view.autofill.AutofillManager; 73import android.view.autofill.AutofillValue; 74import android.view.autofill.IAutoFillManagerClient; 75 76import com.android.internal.R; 77import com.android.internal.annotations.GuardedBy; 78import com.android.internal.logging.MetricsLogger; 79import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 80import com.android.internal.os.HandlerCaller; 81import com.android.server.LocalServices; 82import com.android.server.autofill.ui.AutoFillUI; 83 84import java.io.PrintWriter; 85import java.util.ArrayList; 86import java.util.List; 87import java.util.Map; 88import java.util.Random; 89 90/** 91 * Bridge between the {@code system_server}'s {@link AutofillManagerService} and the 92 * app's {@link IAutoFillService} implementation. 93 * 94 */ 95final class AutofillManagerServiceImpl { 96 97 private static final String TAG = "AutofillManagerServiceImpl"; 98 private static final int MAX_SESSION_ID_CREATE_TRIES = 2048; 99 100 /** Minimum interval to prune abandoned sessions */ 101 private static final int MAX_ABANDONED_SESSION_MILLIS = 30000; 102 103 static final int MSG_SERVICE_SAVE = 1; 104 105 private final int mUserId; 106 private final Context mContext; 107 private final Object mLock; 108 private final AutoFillUI mUi; 109 private final MetricsLogger mMetricsLogger = new MetricsLogger(); 110 111 @GuardedBy("mLock") 112 private RemoteCallbackList<IAutoFillManagerClient> mClients; 113 114 @GuardedBy("mLock") 115 private AutofillServiceInfo mInfo; 116 117 private static final Random sRandom = new Random(); 118 119 private final LocalLog mRequestsHistory; 120 private final LocalLog mUiLatencyHistory; 121 private final LocalLog mWtfHistory; 122 private final FieldClassificationStrategy mFieldClassificationStrategy; 123 124 /** 125 * Apps disabled by the service; key is package name, value is when they will be enabled again. 126 */ 127 @GuardedBy("mLock") 128 private ArrayMap<String, Long> mDisabledApps; 129 130 /** 131 * Activities disabled by the service; key is component name, value is when they will be enabled 132 * again. 133 */ 134 @GuardedBy("mLock") 135 private ArrayMap<ComponentName, Long> mDisabledActivities; 136 137 /** 138 * Whether service was disabled for user due to {@link UserManager} restrictions. 139 */ 140 @GuardedBy("mLock") 141 private boolean mDisabled; 142 143 /** 144 * Data used for field classification. 145 */ 146 @GuardedBy("mLock") 147 private UserData mUserData; 148 149 /** 150 * Caches whether the setup completed for the current user. 151 */ 152 @GuardedBy("mLock") 153 private boolean mSetupComplete; 154 155 private final HandlerCaller.Callback mHandlerCallback = (msg) -> { 156 switch (msg.what) { 157 case MSG_SERVICE_SAVE: 158 handleSessionSave(msg.arg1); 159 break; 160 default: 161 Slog.w(TAG, "invalid msg on handler: " + msg); 162 } 163 }; 164 165 private final HandlerCaller mHandlerCaller = new HandlerCaller(null, Looper.getMainLooper(), 166 mHandlerCallback, true); 167 168 /** 169 * Cache of pending {@link Session}s, keyed by sessionId. 170 * 171 * <p>They're kept until the {@link AutofillService} finished handling a request, an error 172 * occurs, or the session is abandoned. 173 */ 174 @GuardedBy("mLock") 175 private final SparseArray<Session> mSessions = new SparseArray<>(); 176 177 /** The last selection */ 178 @GuardedBy("mLock") 179 private FillEventHistory mEventHistory; 180 181 /** When was {@link PruneTask} last executed? */ 182 private long mLastPrune = 0; 183 184 AutofillManagerServiceImpl(Context context, Object lock, LocalLog requestsHistory, 185 LocalLog uiLatencyHistory, LocalLog wtfHistory, int userId, AutoFillUI ui, 186 boolean disabled) { 187 mContext = context; 188 mLock = lock; 189 mRequestsHistory = requestsHistory; 190 mUiLatencyHistory = uiLatencyHistory; 191 mWtfHistory = wtfHistory; 192 mUserId = userId; 193 mUi = ui; 194 mFieldClassificationStrategy = new FieldClassificationStrategy(context, userId); 195 updateLocked(disabled); 196 } 197 198 @Nullable 199 CharSequence getServiceName() { 200 final String packageName = getServicePackageName(); 201 if (packageName == null) { 202 return null; 203 } 204 205 try { 206 final PackageManager pm = mContext.getPackageManager(); 207 final ApplicationInfo info = pm.getApplicationInfo(packageName, 0); 208 return pm.getApplicationLabel(info); 209 } catch (Exception e) { 210 Slog.e(TAG, "Could not get label for " + packageName + ": " + e); 211 return packageName; 212 } 213 } 214 215 @GuardedBy("mLock") 216 private int getServiceUidLocked() { 217 if (mInfo == null) { 218 Slog.w(TAG, "getServiceUidLocked(): no mInfo"); 219 return -1; 220 } 221 return mInfo.getServiceInfo().applicationInfo.uid; 222 } 223 224 225 @GuardedBy("mLock") 226 @Nullable 227 String getUrlBarResourceIdForCompatModeLocked(@NonNull String packageName) { 228 if (mInfo == null) { 229 Slog.w(TAG, "getUrlBarResourceIdForCompatModeLocked(): no mInfo"); 230 return null; 231 } 232 return mInfo.getUrlBarResourceId(packageName); 233 } 234 235 @Nullable 236 String getServicePackageName() { 237 final ComponentName serviceComponent = getServiceComponentName(); 238 if (serviceComponent != null) { 239 return serviceComponent.getPackageName(); 240 } 241 return null; 242 } 243 244 ComponentName getServiceComponentName() { 245 synchronized (mLock) { 246 if (mInfo == null) { 247 return null; 248 } 249 return mInfo.getServiceInfo().getComponentName(); 250 } 251 } 252 253 private boolean isSetupCompletedLocked() { 254 final String setupComplete = Settings.Secure.getStringForUser( 255 mContext.getContentResolver(), Settings.Secure.USER_SETUP_COMPLETE, mUserId); 256 return "1".equals(setupComplete); 257 } 258 259 private String getComponentNameFromSettings() { 260 return Settings.Secure.getStringForUser( 261 mContext.getContentResolver(), Settings.Secure.AUTOFILL_SERVICE, mUserId); 262 } 263 264 @GuardedBy("mLock") 265 void updateLocked(boolean disabled) { 266 final boolean wasEnabled = isEnabledLocked(); 267 if (sVerbose) { 268 Slog.v(TAG, "updateLocked(u=" + mUserId + "): wasEnabled=" + wasEnabled 269 + ", mSetupComplete= " + mSetupComplete 270 + ", disabled=" + disabled + ", mDisabled=" + mDisabled); 271 } 272 mSetupComplete = isSetupCompletedLocked(); 273 mDisabled = disabled; 274 ComponentName serviceComponent = null; 275 ServiceInfo serviceInfo = null; 276 final String componentName = getComponentNameFromSettings(); 277 if (!TextUtils.isEmpty(componentName)) { 278 try { 279 serviceComponent = ComponentName.unflattenFromString(componentName); 280 serviceInfo = AppGlobals.getPackageManager().getServiceInfo(serviceComponent, 281 0, mUserId); 282 if (serviceInfo == null) { 283 Slog.e(TAG, "Bad AutofillService name: " + componentName); 284 } 285 } catch (RuntimeException | RemoteException e) { 286 Slog.e(TAG, "Error getting service info for '" + componentName + "': " + e); 287 serviceInfo = null; 288 } 289 } 290 try { 291 if (serviceInfo != null) { 292 mInfo = new AutofillServiceInfo(mContext, serviceComponent, mUserId); 293 if (sDebug) Slog.d(TAG, "Set component for user " + mUserId + " as " + mInfo); 294 } else { 295 mInfo = null; 296 if (sDebug) { 297 Slog.d(TAG, "Reset component for user " + mUserId + " (" + componentName + ")"); 298 } 299 } 300 } catch (Exception e) { 301 Slog.e(TAG, "Bad AutofillServiceInfo for '" + componentName + "': " + e); 302 mInfo = null; 303 } 304 final boolean isEnabled = isEnabledLocked(); 305 if (wasEnabled != isEnabled) { 306 if (!isEnabled) { 307 final int sessionCount = mSessions.size(); 308 for (int i = sessionCount - 1; i >= 0; i--) { 309 final Session session = mSessions.valueAt(i); 310 session.removeSelfLocked(); 311 } 312 } 313 sendStateToClients(false); 314 } 315 } 316 317 @GuardedBy("mLock") 318 boolean addClientLocked(IAutoFillManagerClient client) { 319 if (mClients == null) { 320 mClients = new RemoteCallbackList<>(); 321 } 322 mClients.register(client); 323 return isEnabledLocked(); 324 } 325 326 @GuardedBy("mLock") 327 void removeClientLocked(IAutoFillManagerClient client) { 328 if (mClients != null) { 329 mClients.unregister(client); 330 } 331 } 332 333 @GuardedBy("mLock") 334 void setAuthenticationResultLocked(Bundle data, int sessionId, int authenticationId, int uid) { 335 if (!isEnabledLocked()) { 336 return; 337 } 338 final Session session = mSessions.get(sessionId); 339 if (session != null && uid == session.uid) { 340 session.setAuthenticationResultLocked(data, authenticationId); 341 } 342 } 343 344 void setHasCallback(int sessionId, int uid, boolean hasIt) { 345 if (!isEnabledLocked()) { 346 return; 347 } 348 final Session session = mSessions.get(sessionId); 349 if (session != null && uid == session.uid) { 350 synchronized (mLock) { 351 session.setHasCallbackLocked(hasIt); 352 } 353 } 354 } 355 356 @GuardedBy("mLock") 357 int startSessionLocked(@NonNull IBinder activityToken, int uid, 358 @NonNull IBinder appCallbackToken, @NonNull AutofillId autofillId, 359 @NonNull Rect virtualBounds, @Nullable AutofillValue value, boolean hasCallback, 360 int flags, @NonNull ComponentName componentName, boolean compatMode) { 361 if (!isEnabledLocked()) { 362 return 0; 363 } 364 365 final String shortComponentName = componentName.toShortString(); 366 367 if (isAutofillDisabledLocked(componentName)) { 368 if (sDebug) { 369 Slog.d(TAG, "startSession(" + shortComponentName 370 + "): ignored because disabled by service"); 371 } 372 373 final IAutoFillManagerClient client = IAutoFillManagerClient.Stub 374 .asInterface(appCallbackToken); 375 try { 376 client.setSessionFinished(AutofillManager.STATE_DISABLED_BY_SERVICE); 377 } catch (RemoteException e) { 378 Slog.w(TAG, "Could not notify " + shortComponentName + " that it's disabled: " + e); 379 } 380 381 return NO_SESSION; 382 } 383 384 if (sVerbose) Slog.v(TAG, "startSession(): token=" + activityToken + ", flags=" + flags); 385 386 // Occasionally clean up abandoned sessions 387 pruneAbandonedSessionsLocked(); 388 389 final Session newSession = createSessionByTokenLocked(activityToken, uid, appCallbackToken, 390 hasCallback, componentName, compatMode, flags); 391 if (newSession == null) { 392 return NO_SESSION; 393 } 394 395 final String historyItem = 396 "id=" + newSession.id + " uid=" + uid + " a=" + shortComponentName 397 + " s=" + mInfo.getServiceInfo().packageName 398 + " u=" + mUserId + " i=" + autofillId + " b=" + virtualBounds 399 + " hc=" + hasCallback + " f=" + flags; 400 mRequestsHistory.log(historyItem); 401 402 newSession.updateLocked(autofillId, virtualBounds, value, ACTION_START_SESSION, flags); 403 404 return newSession.id; 405 } 406 407 /** 408 * Remove abandoned sessions if needed. 409 */ 410 @GuardedBy("mLock") 411 private void pruneAbandonedSessionsLocked() { 412 long now = System.currentTimeMillis(); 413 if (mLastPrune < now - MAX_ABANDONED_SESSION_MILLIS) { 414 mLastPrune = now; 415 416 if (mSessions.size() > 0) { 417 (new PruneTask()).execute(); 418 } 419 } 420 } 421 422 @GuardedBy("mLock") 423 void finishSessionLocked(int sessionId, int uid) { 424 if (!isEnabledLocked()) { 425 return; 426 } 427 428 final Session session = mSessions.get(sessionId); 429 if (session == null || uid != session.uid) { 430 if (sVerbose) { 431 Slog.v(TAG, "finishSessionLocked(): no session for " + sessionId + "(" + uid + ")"); 432 } 433 return; 434 } 435 436 session.logContextCommitted(); 437 438 final boolean finished = session.showSaveLocked(); 439 if (sVerbose) Slog.v(TAG, "finishSessionLocked(): session finished on save? " + finished); 440 441 if (finished) { 442 session.removeSelfLocked(); 443 } 444 } 445 446 @GuardedBy("mLock") 447 void cancelSessionLocked(int sessionId, int uid) { 448 if (!isEnabledLocked()) { 449 return; 450 } 451 452 final Session session = mSessions.get(sessionId); 453 if (session == null || uid != session.uid) { 454 Slog.w(TAG, "cancelSessionLocked(): no session for " + sessionId + "(" + uid + ")"); 455 return; 456 } 457 session.removeSelfLocked(); 458 } 459 460 @GuardedBy("mLock") 461 void disableOwnedAutofillServicesLocked(int uid) { 462 Slog.i(TAG, "disableOwnedServices(" + uid + "): " + mInfo); 463 if (mInfo == null) return; 464 465 final ServiceInfo serviceInfo = mInfo.getServiceInfo(); 466 if (serviceInfo.applicationInfo.uid != uid) { 467 Slog.w(TAG, "disableOwnedServices(): ignored when called by UID " + uid 468 + " instead of " + serviceInfo.applicationInfo.uid 469 + " for service " + mInfo); 470 return; 471 } 472 473 474 final long identity = Binder.clearCallingIdentity(); 475 try { 476 final String autoFillService = getComponentNameFromSettings(); 477 final ComponentName componentName = serviceInfo.getComponentName(); 478 if (componentName.equals(ComponentName.unflattenFromString(autoFillService))) { 479 mMetricsLogger.action(MetricsEvent.AUTOFILL_SERVICE_DISABLED_SELF, 480 componentName.getPackageName()); 481 Settings.Secure.putStringForUser(mContext.getContentResolver(), 482 Settings.Secure.AUTOFILL_SERVICE, null, mUserId); 483 destroySessionsLocked(); 484 } else { 485 Slog.w(TAG, "disableOwnedServices(): ignored because current service (" 486 + serviceInfo + ") does not match Settings (" + autoFillService + ")"); 487 } 488 } finally { 489 Binder.restoreCallingIdentity(identity); 490 } 491 } 492 493 @GuardedBy("mLock") 494 private Session createSessionByTokenLocked(@NonNull IBinder activityToken, int uid, 495 @NonNull IBinder appCallbackToken, boolean hasCallback, 496 @NonNull ComponentName componentName, boolean compatMode, int flags) { 497 // use random ids so that one app cannot know that another app creates sessions 498 int sessionId; 499 int tries = 0; 500 do { 501 tries++; 502 if (tries > MAX_SESSION_ID_CREATE_TRIES) { 503 Slog.w(TAG, "Cannot create session in " + MAX_SESSION_ID_CREATE_TRIES + " tries"); 504 return null; 505 } 506 507 sessionId = sRandom.nextInt(); 508 } while (sessionId == NO_SESSION || mSessions.indexOfKey(sessionId) >= 0); 509 510 assertCallerLocked(componentName); 511 512 final Session newSession = new Session(this, mUi, mContext, mHandlerCaller, mUserId, mLock, 513 sessionId, uid, activityToken, appCallbackToken, hasCallback, mUiLatencyHistory, 514 mWtfHistory, mInfo.getServiceInfo().getComponentName(), componentName, compatMode, 515 flags); 516 mSessions.put(newSession.id, newSession); 517 518 return newSession; 519 } 520 521 /** 522 * Asserts the component is owned by the caller. 523 */ 524 private void assertCallerLocked(@NonNull ComponentName componentName) { 525 final String packageName = componentName.getPackageName(); 526 final PackageManager pm = mContext.getPackageManager(); 527 final int callingUid = Binder.getCallingUid(); 528 final int packageUid; 529 try { 530 packageUid = pm.getPackageUidAsUser(packageName, UserHandle.getCallingUserId()); 531 } catch (NameNotFoundException e) { 532 throw new SecurityException("Could not verify UID for " + componentName); 533 } 534 if (callingUid != packageUid && !LocalServices.getService(ActivityManagerInternal.class) 535 .hasRunningActivity(callingUid, packageName)) { 536 final String[] packages = pm.getPackagesForUid(callingUid); 537 final String callingPackage = packages != null ? packages[0] : "uid-" + callingUid; 538 Slog.w(TAG, "App (package=" + callingPackage + ", UID=" + callingUid 539 + ") passed component (" + componentName + ") owned by UID " + packageUid); 540 mMetricsLogger.write( 541 Helper.newLogMaker(MetricsEvent.AUTOFILL_FORGED_COMPONENT_ATTEMPT, 542 callingPackage, getServicePackageName()) 543 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_FORGED_COMPONENT_NAME, 544 componentName == null ? "null" : componentName.flattenToShortString())); 545 546 throw new SecurityException("Invalid component: " + componentName); 547 } 548 } 549 550 /** 551 * Restores a session after an activity was temporarily destroyed. 552 * 553 * @param sessionId The id of the session to restore 554 * @param uid UID of the process that tries to restore the session 555 * @param activityToken The new instance of the activity 556 * @param appCallback The callbacks to the activity 557 */ 558 boolean restoreSession(int sessionId, int uid, @NonNull IBinder activityToken, 559 @NonNull IBinder appCallback) { 560 final Session session = mSessions.get(sessionId); 561 562 if (session == null || uid != session.uid) { 563 return false; 564 } else { 565 session.switchActivity(activityToken, appCallback); 566 return true; 567 } 568 } 569 570 /** 571 * Updates a session and returns whether it should be restarted. 572 */ 573 @GuardedBy("mLock") 574 boolean updateSessionLocked(int sessionId, int uid, AutofillId autofillId, Rect virtualBounds, 575 AutofillValue value, int action, int flags) { 576 final Session session = mSessions.get(sessionId); 577 if (session == null || session.uid != uid) { 578 if ((flags & FLAG_MANUAL_REQUEST) != 0) { 579 if (sDebug) { 580 Slog.d(TAG, "restarting session " + sessionId + " due to manual request on " 581 + autofillId); 582 } 583 return true; 584 } 585 if (sVerbose) { 586 Slog.v(TAG, "updateSessionLocked(): session gone for " + sessionId 587 + "(" + uid + ")"); 588 } 589 return false; 590 } 591 592 session.updateLocked(autofillId, virtualBounds, value, action, flags); 593 return false; 594 } 595 596 @GuardedBy("mLock") 597 void removeSessionLocked(int sessionId) { 598 mSessions.remove(sessionId); 599 } 600 601 private void handleSessionSave(int sessionId) { 602 synchronized (mLock) { 603 final Session session = mSessions.get(sessionId); 604 if (session == null) { 605 Slog.w(TAG, "handleSessionSave(): already gone: " + sessionId); 606 607 return; 608 } 609 session.callSaveLocked(); 610 } 611 } 612 613 void onPendingSaveUi(int operation, @NonNull IBinder token) { 614 if (sVerbose) Slog.v(TAG, "onPendingSaveUi(" + operation + "): " + token); 615 synchronized (mLock) { 616 final int sessionCount = mSessions.size(); 617 for (int i = sessionCount - 1; i >= 0; i--) { 618 final Session session = mSessions.valueAt(i); 619 if (session.isSaveUiPendingForTokenLocked(token)) { 620 session.onPendingSaveUi(operation, token); 621 return; 622 } 623 } 624 } 625 if (sDebug) { 626 Slog.d(TAG, "No pending Save UI for token " + token + " and operation " 627 + DebugUtils.flagsToString(AutofillManager.class, "PENDING_UI_OPERATION_", 628 operation)); 629 } 630 } 631 632 @GuardedBy("mLock") 633 void destroyLocked() { 634 if (sVerbose) Slog.v(TAG, "destroyLocked()"); 635 636 final int numSessions = mSessions.size(); 637 final ArraySet<RemoteFillService> remoteFillServices = new ArraySet<>(numSessions); 638 for (int i = 0; i < numSessions; i++) { 639 final RemoteFillService remoteFillService = mSessions.valueAt(i).destroyLocked(); 640 if (remoteFillService != null) { 641 remoteFillServices.add(remoteFillService); 642 } 643 } 644 mSessions.clear(); 645 for (int i = 0; i < remoteFillServices.size(); i++) { 646 remoteFillServices.valueAt(i).destroy(); 647 } 648 649 sendStateToClients(true); 650 if (mClients != null) { 651 mClients.kill(); 652 mClients = null; 653 } 654 } 655 656 @NonNull 657 CharSequence getServiceLabel() { 658 return mInfo.getServiceInfo().loadLabel(mContext.getPackageManager()); 659 } 660 661 @NonNull 662 Drawable getServiceIcon() { 663 return mInfo.getServiceInfo().loadIcon(mContext.getPackageManager()); 664 } 665 666 /** 667 * Initializes the last fill selection after an autofill service returned a new 668 * {@link FillResponse}. 669 */ 670 void setLastResponse(int sessionId, @NonNull FillResponse response) { 671 synchronized (mLock) { 672 mEventHistory = new FillEventHistory(sessionId, response.getClientState()); 673 } 674 } 675 676 /** 677 * Resets the last fill selection. 678 */ 679 void resetLastResponse() { 680 synchronized (mLock) { 681 mEventHistory = null; 682 } 683 } 684 685 @GuardedBy("mLock") 686 private boolean isValidEventLocked(String method, int sessionId) { 687 if (mEventHistory == null) { 688 Slog.w(TAG, method + ": not logging event because history is null"); 689 return false; 690 } 691 if (sessionId != mEventHistory.getSessionId()) { 692 if (sDebug) { 693 Slog.d(TAG, method + ": not logging event for session " + sessionId 694 + " because tracked session is " + mEventHistory.getSessionId()); 695 } 696 return false; 697 } 698 return true; 699 } 700 701 /** 702 * Updates the last fill selection when an authentication was selected. 703 */ 704 void setAuthenticationSelected(int sessionId, @Nullable Bundle clientState) { 705 synchronized (mLock) { 706 if (isValidEventLocked("setAuthenticationSelected()", sessionId)) { 707 mEventHistory.addEvent( 708 new Event(Event.TYPE_AUTHENTICATION_SELECTED, null, clientState, null, null, 709 null, null, null, null, null, null)); 710 } 711 } 712 } 713 714 /** 715 * Updates the last fill selection when an dataset authentication was selected. 716 */ 717 void logDatasetAuthenticationSelected(@Nullable String selectedDataset, int sessionId, 718 @Nullable Bundle clientState) { 719 synchronized (mLock) { 720 if (isValidEventLocked("logDatasetAuthenticationSelected()", sessionId)) { 721 mEventHistory.addEvent( 722 new Event(Event.TYPE_DATASET_AUTHENTICATION_SELECTED, selectedDataset, 723 clientState, null, null, null, null, null, null, null, null)); 724 } 725 } 726 } 727 728 /** 729 * Updates the last fill selection when an save Ui is shown. 730 */ 731 void logSaveShown(int sessionId, @Nullable Bundle clientState) { 732 synchronized (mLock) { 733 if (isValidEventLocked("logSaveShown()", sessionId)) { 734 mEventHistory.addEvent(new Event(Event.TYPE_SAVE_SHOWN, null, clientState, null, 735 null, null, null, null, null, null, null)); 736 } 737 } 738 } 739 740 /** 741 * Updates the last fill response when a dataset was selected. 742 */ 743 void logDatasetSelected(@Nullable String selectedDataset, int sessionId, 744 @Nullable Bundle clientState) { 745 synchronized (mLock) { 746 if (isValidEventLocked("logDatasetSelected()", sessionId)) { 747 mEventHistory.addEvent( 748 new Event(Event.TYPE_DATASET_SELECTED, selectedDataset, clientState, null, 749 null, null, null, null, null, null, null)); 750 } 751 } 752 } 753 754 /** 755 * Updates the last fill response when an autofill context is committed. 756 */ 757 @GuardedBy("mLock") 758 void logContextCommittedLocked(int sessionId, @Nullable Bundle clientState, 759 @Nullable ArrayList<String> selectedDatasets, 760 @Nullable ArraySet<String> ignoredDatasets, 761 @Nullable ArrayList<AutofillId> changedFieldIds, 762 @Nullable ArrayList<String> changedDatasetIds, 763 @Nullable ArrayList<AutofillId> manuallyFilledFieldIds, 764 @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds, 765 @NonNull String appPackageName) { 766 logContextCommittedLocked(sessionId, clientState, selectedDatasets, ignoredDatasets, 767 changedFieldIds, changedDatasetIds, manuallyFilledFieldIds, 768 manuallyFilledDatasetIds, null, null, appPackageName); 769 } 770 771 @GuardedBy("mLock") 772 void logContextCommittedLocked(int sessionId, @Nullable Bundle clientState, 773 @Nullable ArrayList<String> selectedDatasets, 774 @Nullable ArraySet<String> ignoredDatasets, 775 @Nullable ArrayList<AutofillId> changedFieldIds, 776 @Nullable ArrayList<String> changedDatasetIds, 777 @Nullable ArrayList<AutofillId> manuallyFilledFieldIds, 778 @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds, 779 @Nullable ArrayList<AutofillId> detectedFieldIdsList, 780 @Nullable ArrayList<FieldClassification> detectedFieldClassificationsList, 781 @NonNull String appPackageName) { 782 if (isValidEventLocked("logDatasetNotSelected()", sessionId)) { 783 if (sVerbose) { 784 Slog.v(TAG, "logContextCommitted() with FieldClassification: id=" + sessionId 785 + ", selectedDatasets=" + selectedDatasets 786 + ", ignoredDatasetIds=" + ignoredDatasets 787 + ", changedAutofillIds=" + changedFieldIds 788 + ", changedDatasetIds=" + changedDatasetIds 789 + ", manuallyFilledFieldIds=" + manuallyFilledFieldIds 790 + ", detectedFieldIds=" + detectedFieldIdsList 791 + ", detectedFieldClassifications=" + detectedFieldClassificationsList); 792 } 793 AutofillId[] detectedFieldsIds = null; 794 FieldClassification[] detectedFieldClassifications = null; 795 if (detectedFieldIdsList != null) { 796 detectedFieldsIds = new AutofillId[detectedFieldIdsList.size()]; 797 detectedFieldIdsList.toArray(detectedFieldsIds); 798 detectedFieldClassifications = 799 new FieldClassification[detectedFieldClassificationsList.size()]; 800 detectedFieldClassificationsList.toArray(detectedFieldClassifications); 801 802 final int numberFields = detectedFieldsIds.length; 803 int totalSize = 0; 804 float totalScore = 0; 805 for (int i = 0; i < numberFields; i++) { 806 final FieldClassification fc = detectedFieldClassifications[i]; 807 final List<Match> matches = fc.getMatches(); 808 final int size = matches.size(); 809 totalSize += size; 810 for (int j = 0; j < size; j++) { 811 totalScore += matches.get(j).getScore(); 812 } 813 } 814 815 final int averageScore = (int) ((totalScore * 100) / totalSize); 816 mMetricsLogger.write(Helper 817 .newLogMaker(MetricsEvent.AUTOFILL_FIELD_CLASSIFICATION_MATCHES, 818 appPackageName, getServicePackageName()) 819 .setCounterValue(numberFields) 820 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_MATCH_SCORE, 821 averageScore)); 822 } 823 mEventHistory.addEvent(new Event(Event.TYPE_CONTEXT_COMMITTED, null, 824 clientState, selectedDatasets, ignoredDatasets, 825 changedFieldIds, changedDatasetIds, 826 manuallyFilledFieldIds, manuallyFilledDatasetIds, 827 detectedFieldsIds, detectedFieldClassifications)); 828 } 829 } 830 831 /** 832 * Gets the fill event history. 833 * 834 * @param callingUid The calling uid 835 * 836 * @return The history or {@code null} if there is none. 837 */ 838 FillEventHistory getFillEventHistory(int callingUid) { 839 synchronized (mLock) { 840 if (mEventHistory != null 841 && isCalledByServiceLocked("getFillEventHistory", callingUid)) { 842 return mEventHistory; 843 } 844 } 845 return null; 846 } 847 848 // Called by Session - does not need to check uid 849 UserData getUserData() { 850 synchronized (mLock) { 851 return mUserData; 852 } 853 } 854 855 // Called by AutofillManager 856 UserData getUserData(int callingUid) { 857 synchronized (mLock) { 858 if (isCalledByServiceLocked("getUserData", callingUid)) { 859 return mUserData; 860 } 861 } 862 return null; 863 } 864 865 // Called by AutofillManager 866 void setUserData(int callingUid, UserData userData) { 867 synchronized (mLock) { 868 if (!isCalledByServiceLocked("setUserData", callingUid)) { 869 return; 870 } 871 mUserData = userData; 872 // Log it 873 int numberFields = mUserData == null ? 0: mUserData.getCategoryIds().length; 874 mMetricsLogger.write(Helper.newLogMaker(MetricsEvent.AUTOFILL_USERDATA_UPDATED, 875 getServicePackageName(), null) 876 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, numberFields)); 877 } 878 } 879 880 @GuardedBy("mLock") 881 private boolean isCalledByServiceLocked(String methodName, int callingUid) { 882 if (getServiceUidLocked() != callingUid) { 883 Slog.w(TAG, methodName + "() called by UID " + callingUid 884 + ", but service UID is " + getServiceUidLocked()); 885 return false; 886 } 887 return true; 888 } 889 890 @GuardedBy("mLock") 891 void dumpLocked(String prefix, PrintWriter pw) { 892 final String prefix2 = prefix + " "; 893 894 pw.print(prefix); pw.print("User: "); pw.println(mUserId); 895 pw.print(prefix); pw.print("UID: "); pw.println(getServiceUidLocked()); 896 pw.print(prefix); pw.print("Autofill Service Info: "); 897 if (mInfo == null) { 898 pw.println("N/A"); 899 } else { 900 pw.println(); 901 mInfo.dump(prefix2, pw); 902 } 903 pw.print(prefix); pw.print("Component from settings: "); 904 pw.println(getComponentNameFromSettings()); 905 pw.print(prefix); pw.print("Default component: "); 906 pw.println(mContext.getString(R.string.config_defaultAutofillService)); 907 pw.print(prefix); pw.print("Disabled: "); pw.println(mDisabled); 908 pw.print(prefix); pw.print("Field classification enabled: "); 909 pw.println(isFieldClassificationEnabledLocked()); 910 final ArrayMap<String, Pair<Long, String>> compatPkgs = getCompatibilityPackagesLocked(); 911 if (compatPkgs != null) { 912 pw.print(prefix); pw.print("Compat pkgs: "); pw.println(compatPkgs.keySet()); 913 } 914 pw.print(prefix); pw.print("Setup complete: "); pw.println(mSetupComplete); 915 pw.print(prefix); pw.print("Last prune: "); pw.println(mLastPrune); 916 917 pw.print(prefix); pw.print("Disabled apps: "); 918 919 if (mDisabledApps == null) { 920 pw.println("N/A"); 921 } else { 922 final int size = mDisabledApps.size(); 923 pw.println(size); 924 final StringBuilder builder = new StringBuilder(); 925 final long now = SystemClock.elapsedRealtime(); 926 for (int i = 0; i < size; i++) { 927 final String packageName = mDisabledApps.keyAt(i); 928 final long expiration = mDisabledApps.valueAt(i); 929 builder.append(prefix).append(prefix) 930 .append(i).append(". ").append(packageName).append(": "); 931 TimeUtils.formatDuration((expiration - now), builder); 932 builder.append('\n'); 933 } 934 pw.println(builder); 935 } 936 937 pw.print(prefix); pw.print("Disabled activities: "); 938 939 if (mDisabledActivities == null) { 940 pw.println("N/A"); 941 } else { 942 final int size = mDisabledActivities.size(); 943 pw.println(size); 944 final StringBuilder builder = new StringBuilder(); 945 final long now = SystemClock.elapsedRealtime(); 946 for (int i = 0; i < size; i++) { 947 final ComponentName component = mDisabledActivities.keyAt(i); 948 final long expiration = mDisabledActivities.valueAt(i); 949 builder.append(prefix).append(prefix) 950 .append(i).append(". ").append(component).append(": "); 951 TimeUtils.formatDuration((expiration - now), builder); 952 builder.append('\n'); 953 } 954 pw.println(builder); 955 } 956 957 final int size = mSessions.size(); 958 if (size == 0) { 959 pw.print(prefix); pw.println("No sessions"); 960 } else { 961 pw.print(prefix); pw.print(size); pw.println(" sessions:"); 962 for (int i = 0; i < size; i++) { 963 pw.print(prefix); pw.print("#"); pw.println(i + 1); 964 mSessions.valueAt(i).dumpLocked(prefix2, pw); 965 } 966 } 967 968 pw.print(prefix); pw.print("Clients: "); 969 if (mClients == null) { 970 pw.println("N/A"); 971 } else { 972 pw.println(); 973 mClients.dump(pw, prefix2); 974 } 975 976 if (mEventHistory == null || mEventHistory.getEvents() == null 977 || mEventHistory.getEvents().size() == 0) { 978 pw.print(prefix); pw.println("No event on last fill response"); 979 } else { 980 pw.print(prefix); pw.println("Events of last fill response:"); 981 pw.print(prefix); 982 983 int numEvents = mEventHistory.getEvents().size(); 984 for (int i = 0; i < numEvents; i++) { 985 final Event event = mEventHistory.getEvents().get(i); 986 pw.println(" " + i + ": eventType=" + event.getType() + " datasetId=" 987 + event.getDatasetId()); 988 } 989 } 990 991 pw.print(prefix); pw.print("User data: "); 992 if (mUserData == null) { 993 pw.println("N/A"); 994 } else { 995 pw.println(); 996 mUserData.dump(prefix2, pw); 997 } 998 999 pw.print(prefix); pw.println("Field Classification strategy: "); 1000 mFieldClassificationStrategy.dump(prefix2, pw); 1001 } 1002 1003 @GuardedBy("mLock") 1004 void destroySessionsLocked() { 1005 if (mSessions.size() == 0) { 1006 mUi.destroyAll(null, null, false); 1007 return; 1008 } 1009 while (mSessions.size() > 0) { 1010 mSessions.valueAt(0).forceRemoveSelfLocked(); 1011 } 1012 } 1013 1014 // TODO(b/64940307): remove this method if SaveUI is refactored to be attached on activities 1015 @GuardedBy("mLock") 1016 void destroyFinishedSessionsLocked() { 1017 final int sessionCount = mSessions.size(); 1018 for (int i = sessionCount - 1; i >= 0; i--) { 1019 final Session session = mSessions.valueAt(i); 1020 if (session.isSavingLocked()) { 1021 if (sDebug) Slog.d(TAG, "destroyFinishedSessionsLocked(): " + session.id); 1022 session.forceRemoveSelfLocked(); 1023 } 1024 } 1025 } 1026 1027 @GuardedBy("mLock") 1028 void listSessionsLocked(ArrayList<String> output) { 1029 final int numSessions = mSessions.size(); 1030 for (int i = 0; i < numSessions; i++) { 1031 output.add((mInfo != null ? mInfo.getServiceInfo().getComponentName() 1032 : null) + ":" + mSessions.keyAt(i)); 1033 } 1034 } 1035 1036 @GuardedBy("mLock") 1037 @Nullable ArrayMap<String, Pair<Long, String>> getCompatibilityPackagesLocked() { 1038 if (mInfo != null) { 1039 return mInfo.getCompatibilityPackages(); 1040 } 1041 return null; 1042 } 1043 1044 private void sendStateToClients(boolean resetClient) { 1045 final RemoteCallbackList<IAutoFillManagerClient> clients; 1046 final int userClientCount; 1047 synchronized (mLock) { 1048 if (mClients == null) { 1049 return; 1050 } 1051 clients = mClients; 1052 userClientCount = clients.beginBroadcast(); 1053 } 1054 try { 1055 for (int i = 0; i < userClientCount; i++) { 1056 final IAutoFillManagerClient client = clients.getBroadcastItem(i); 1057 try { 1058 final boolean resetSession; 1059 final boolean isEnabled; 1060 synchronized (mLock) { 1061 resetSession = resetClient || isClientSessionDestroyedLocked(client); 1062 isEnabled = isEnabledLocked(); 1063 } 1064 int flags = 0; 1065 if (isEnabled) { 1066 flags |= AutofillManager.SET_STATE_FLAG_ENABLED; 1067 } 1068 if (resetSession) { 1069 flags |= AutofillManager.SET_STATE_FLAG_RESET_SESSION; 1070 } 1071 if (resetClient) { 1072 flags |= AutofillManager.SET_STATE_FLAG_RESET_CLIENT; 1073 } 1074 if (sDebug) { 1075 flags |= AutofillManager.SET_STATE_FLAG_DEBUG; 1076 } 1077 if (sVerbose) { 1078 flags |= AutofillManager.SET_STATE_FLAG_VERBOSE; 1079 } 1080 client.setState(flags); 1081 } catch (RemoteException re) { 1082 /* ignore */ 1083 } 1084 } 1085 } finally { 1086 clients.finishBroadcast(); 1087 } 1088 } 1089 1090 @GuardedBy("mLock") 1091 private boolean isClientSessionDestroyedLocked(IAutoFillManagerClient client) { 1092 final int sessionCount = mSessions.size(); 1093 for (int i = 0; i < sessionCount; i++) { 1094 final Session session = mSessions.valueAt(i); 1095 if (session.getClient().equals(client)) { 1096 return session.isDestroyed(); 1097 } 1098 } 1099 return true; 1100 } 1101 1102 @GuardedBy("mLock") 1103 boolean isEnabledLocked() { 1104 return mSetupComplete && mInfo != null && !mDisabled; 1105 } 1106 1107 /** 1108 * Called by {@link Session} when service asked to disable autofill for an app. 1109 */ 1110 void disableAutofillForApp(@NonNull String packageName, long duration) { 1111 synchronized (mLock) { 1112 if (mDisabledApps == null) { 1113 mDisabledApps = new ArrayMap<>(1); 1114 } 1115 long expiration = SystemClock.elapsedRealtime() + duration; 1116 // Protect it against overflow 1117 if (expiration < 0) { 1118 expiration = Long.MAX_VALUE; 1119 } 1120 mDisabledApps.put(packageName, expiration); 1121 int intDuration = duration > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) duration; 1122 mMetricsLogger.write(Helper.newLogMaker(MetricsEvent.AUTOFILL_SERVICE_DISABLED_APP, 1123 packageName, getServicePackageName()) 1124 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_DURATION, intDuration)); 1125 } 1126 } 1127 1128 /** 1129 * Called by {@link Session} when service asked to disable autofill an app. 1130 */ 1131 void disableAutofillForActivity(@NonNull ComponentName componentName, long duration) { 1132 synchronized (mLock) { 1133 if (mDisabledActivities == null) { 1134 mDisabledActivities = new ArrayMap<>(1); 1135 } 1136 long expiration = SystemClock.elapsedRealtime() + duration; 1137 // Protect it against overflow 1138 if (expiration < 0) { 1139 expiration = Long.MAX_VALUE; 1140 } 1141 mDisabledActivities.put(componentName, expiration); 1142 final int intDuration = duration > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) duration; 1143 // NOTE: not using Helper.newLogMaker() because we're setting the componentName instead 1144 // of package name 1145 mMetricsLogger.write(new LogMaker(MetricsEvent.AUTOFILL_SERVICE_DISABLED_ACTIVITY) 1146 .setComponentName(componentName) 1147 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE, getServicePackageName()) 1148 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_DURATION, intDuration)); 1149 } 1150 } 1151 1152 /** 1153 * Checks if autofill is disabled by service to the given activity. 1154 */ 1155 @GuardedBy("mLock") 1156 private boolean isAutofillDisabledLocked(@NonNull ComponentName componentName) { 1157 // Check activities first. 1158 long elapsedTime = 0; 1159 if (mDisabledActivities != null) { 1160 elapsedTime = SystemClock.elapsedRealtime(); 1161 final Long expiration = mDisabledActivities.get(componentName); 1162 if (expiration != null) { 1163 if (expiration >= elapsedTime) return true; 1164 // Restriction expired - clean it up. 1165 if (sVerbose) { 1166 Slog.v(TAG, "Removing " + componentName.toShortString() 1167 + " from disabled list"); 1168 } 1169 mDisabledActivities.remove(componentName); 1170 } 1171 } 1172 1173 // Then check apps. 1174 final String packageName = componentName.getPackageName(); 1175 if (mDisabledApps == null) return false; 1176 1177 final Long expiration = mDisabledApps.get(packageName); 1178 if (expiration == null) return false; 1179 1180 if (elapsedTime == 0) { 1181 elapsedTime = SystemClock.elapsedRealtime(); 1182 } 1183 1184 if (expiration >= elapsedTime) return true; 1185 1186 // Restriction expired - clean it up. 1187 if (sVerbose) Slog.v(TAG, "Removing " + packageName + " from disabled list"); 1188 mDisabledApps.remove(packageName); 1189 return false; 1190 } 1191 1192 // Called by AutofillManager, checks UID. 1193 boolean isFieldClassificationEnabled(int callingUid) { 1194 synchronized (mLock) { 1195 if (!isCalledByServiceLocked("isFieldClassificationEnabled", callingUid)) { 1196 return false; 1197 } 1198 return isFieldClassificationEnabledLocked(); 1199 } 1200 } 1201 1202 // Called by internally, no need to check UID. 1203 boolean isFieldClassificationEnabledLocked() { 1204 return Settings.Secure.getIntForUser( 1205 mContext.getContentResolver(), 1206 Settings.Secure.AUTOFILL_FEATURE_FIELD_CLASSIFICATION, 0, 1207 mUserId) == 1; 1208 } 1209 1210 FieldClassificationStrategy getFieldClassificationStrategy() { 1211 return mFieldClassificationStrategy; 1212 } 1213 1214 String[] getAvailableFieldClassificationAlgorithms(int callingUid) { 1215 synchronized (mLock) { 1216 if (!isCalledByServiceLocked("getFCAlgorithms()", callingUid)) { 1217 return null; 1218 } 1219 } 1220 return mFieldClassificationStrategy.getAvailableAlgorithms(); 1221 } 1222 1223 String getDefaultFieldClassificationAlgorithm(int callingUid) { 1224 synchronized (mLock) { 1225 if (!isCalledByServiceLocked("getDefaultFCAlgorithm()", callingUid)) { 1226 return null; 1227 } 1228 } 1229 return mFieldClassificationStrategy.getDefaultAlgorithm(); 1230 } 1231 1232 @Override 1233 public String toString() { 1234 return "AutofillManagerServiceImpl: [userId=" + mUserId 1235 + ", component=" + (mInfo != null 1236 ? mInfo.getServiceInfo().getComponentName() : null) + "]"; 1237 } 1238 1239 /** Task used to prune abandoned session */ 1240 private class PruneTask extends AsyncTask<Void, Void, Void> { 1241 @Override 1242 protected Void doInBackground(Void... ignored) { 1243 int numSessionsToRemove; 1244 1245 SparseArray<IBinder> sessionsToRemove; 1246 1247 synchronized (mLock) { 1248 numSessionsToRemove = mSessions.size(); 1249 sessionsToRemove = new SparseArray<>(numSessionsToRemove); 1250 1251 for (int i = 0; i < numSessionsToRemove; i++) { 1252 Session session = mSessions.valueAt(i); 1253 1254 sessionsToRemove.put(session.id, session.getActivityTokenLocked()); 1255 } 1256 } 1257 1258 IActivityManager am = ActivityManager.getService(); 1259 1260 // Only remove sessions which's activities are not known to the activity manager anymore 1261 for (int i = 0; i < numSessionsToRemove; i++) { 1262 try { 1263 // The activity manager cannot resolve activities that have been removed 1264 if (am.getActivityClassForToken(sessionsToRemove.valueAt(i)) != null) { 1265 sessionsToRemove.removeAt(i); 1266 i--; 1267 numSessionsToRemove--; 1268 } 1269 } catch (RemoteException e) { 1270 Slog.w(TAG, "Cannot figure out if activity is finished", e); 1271 } 1272 } 1273 1274 synchronized (mLock) { 1275 for (int i = 0; i < numSessionsToRemove; i++) { 1276 Session sessionToRemove = mSessions.get(sessionsToRemove.keyAt(i)); 1277 1278 if (sessionToRemove != null && sessionsToRemove.valueAt(i) 1279 == sessionToRemove.getActivityTokenLocked()) { 1280 if (sessionToRemove.isSavingLocked()) { 1281 if (sVerbose) { 1282 Slog.v(TAG, "Session " + sessionToRemove.id + " is saving"); 1283 } 1284 } else { 1285 if (sDebug) { 1286 Slog.i(TAG, "Prune session " + sessionToRemove.id + " (" 1287 + sessionToRemove.getActivityTokenLocked() + ")"); 1288 } 1289 sessionToRemove.removeSelfLocked(); 1290 } 1291 } 1292 } 1293 } 1294 1295 return null; 1296 } 1297 } 1298} 1299