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