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