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