1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.contentcapture; 18 19 import static android.service.contentcapture.ContentCaptureService.setClientState; 20 import static android.view.contentcapture.ContentCaptureManager.NO_SESSION_ID; 21 import static android.view.contentcapture.ContentCaptureSession.STATE_DISABLED; 22 import static android.view.contentcapture.ContentCaptureSession.STATE_DUPLICATED_ID; 23 import static android.view.contentcapture.ContentCaptureSession.STATE_INTERNAL_ERROR; 24 import static android.view.contentcapture.ContentCaptureSession.STATE_NOT_WHITELISTED; 25 import static android.view.contentcapture.ContentCaptureSession.STATE_NO_SERVICE; 26 27 import static com.android.server.contentcapture.ContentCaptureMetricsLogger.writeServiceEvent; 28 import static com.android.server.contentcapture.ContentCaptureMetricsLogger.writeSessionEvent; 29 import static com.android.server.contentcapture.ContentCaptureMetricsLogger.writeSetWhitelistEvent; 30 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT; 31 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_DATA; 32 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE; 33 34 import android.annotation.NonNull; 35 import android.annotation.Nullable; 36 import android.annotation.UserIdInt; 37 import android.app.ActivityManagerInternal; 38 import android.app.assist.ActivityId; 39 import android.app.assist.AssistContent; 40 import android.app.assist.AssistStructure; 41 import android.content.ComponentName; 42 import android.content.ContentCaptureOptions; 43 import android.content.pm.ActivityPresentationInfo; 44 import android.content.pm.PackageManager; 45 import android.content.pm.PackageManager.NameNotFoundException; 46 import android.content.pm.ServiceInfo; 47 import android.os.Binder; 48 import android.os.Bundle; 49 import android.os.Handler; 50 import android.os.IBinder; 51 import android.os.UserHandle; 52 import android.provider.Settings; 53 import android.service.contentcapture.ActivityEvent; 54 import android.service.contentcapture.ActivityEvent.ActivityEventType; 55 import android.service.contentcapture.ContentCaptureService; 56 import android.service.contentcapture.ContentCaptureServiceInfo; 57 import android.service.contentcapture.FlushMetrics; 58 import android.service.contentcapture.IContentCaptureServiceCallback; 59 import android.service.contentcapture.IDataShareCallback; 60 import android.service.contentcapture.SnapshotData; 61 import android.service.voice.VoiceInteractionManagerInternal; 62 import android.util.ArrayMap; 63 import android.util.ArraySet; 64 import android.util.Slog; 65 import android.util.SparseArray; 66 import android.util.SparseBooleanArray; 67 import android.view.contentcapture.ContentCaptureCondition; 68 import android.view.contentcapture.DataRemovalRequest; 69 import android.view.contentcapture.DataShareRequest; 70 71 import com.android.internal.annotations.GuardedBy; 72 import com.android.internal.os.IResultReceiver; 73 import com.android.internal.util.FrameworkStatsLog; 74 import com.android.server.LocalServices; 75 import com.android.server.contentcapture.RemoteContentCaptureService.ContentCaptureServiceCallbacks; 76 import com.android.server.infra.AbstractPerUserSystemService; 77 78 import java.io.PrintWriter; 79 import java.time.Instant; 80 import java.util.ArrayList; 81 import java.util.List; 82 83 /** 84 * Per-user instance of {@link ContentCaptureManagerService}. 85 */ 86 final class ContentCapturePerUserService 87 extends 88 AbstractPerUserSystemService<ContentCapturePerUserService, ContentCaptureManagerService> 89 implements ContentCaptureServiceCallbacks { 90 91 private static final String TAG = ContentCapturePerUserService.class.getSimpleName(); 92 93 private static final int MAX_REBIND_COUNTS = 5; 94 // 5 minutes 95 private static final long REBIND_DURATION_MS = 5 * 60 * 1_000; 96 97 @GuardedBy("mLock") 98 private final SparseArray<ContentCaptureServerSession> mSessions = new SparseArray<>(); 99 100 /** 101 * Reference to the remote service. 102 * 103 * <p>It's set in the constructor, but it's also updated when the service's updated in the 104 * main service's cache (for example, because a temporary service was set). 105 */ 106 @GuardedBy("mLock") 107 @Nullable 108 RemoteContentCaptureService mRemoteService; 109 110 private final ContentCaptureServiceRemoteCallback mRemoteServiceCallback = 111 new ContentCaptureServiceRemoteCallback(); 112 113 /** 114 * List of conditions keyed by package. 115 */ 116 @GuardedBy("mLock") 117 private final ArrayMap<String, ArraySet<ContentCaptureCondition>> mConditionsByPkg = 118 new ArrayMap<>(); 119 120 /** 121 * When {@code true}, remote service died but service state is kept so it's restored after 122 * the system re-binds to it. 123 */ 124 @GuardedBy("mLock") 125 private boolean mZombie; 126 127 @GuardedBy("mLock") 128 private ContentCaptureServiceInfo mInfo; 129 130 private Instant mLastRebindTime; 131 private int mRebindCount; 132 private final Handler mHandler; 133 134 private final Runnable mReBindServiceRunnable = new RebindServiceRunnable(); 135 136 // TODO(b/111276913): add mechanism to prune stale sessions, similar to Autofill's 137 ContentCapturePerUserService(@onNull ContentCaptureManagerService master, @NonNull Object lock, boolean disabled, @UserIdInt int userId, Handler handler)138 ContentCapturePerUserService(@NonNull ContentCaptureManagerService master, 139 @NonNull Object lock, boolean disabled, @UserIdInt int userId, Handler handler) { 140 super(master, lock, userId); 141 mHandler = handler; 142 updateRemoteServiceLocked(disabled); 143 } 144 145 /** 146 * Updates the reference to the remote service. 147 */ updateRemoteServiceLocked(boolean disabled)148 private void updateRemoteServiceLocked(boolean disabled) { 149 if (mMaster.verbose) Slog.v(TAG, "updateRemoteService(disabled=" + disabled + ")"); 150 if (mRemoteService != null) { 151 if (mMaster.debug) Slog.d(TAG, "updateRemoteService(): destroying old remote service"); 152 mRemoteService.destroy(); 153 mRemoteService = null; 154 resetContentCaptureWhitelistLocked(); 155 } 156 157 // Updates the component name 158 final ComponentName serviceComponentName = updateServiceInfoLocked(); 159 160 if (serviceComponentName == null) { 161 if (mMaster.debug) Slog.d(TAG, "updateRemoteService(): no service component name"); 162 return; 163 } 164 165 if (!disabled) { 166 if (mMaster.debug) { 167 Slog.d(TAG, "updateRemoteService(): creating new remote service for " 168 + serviceComponentName); 169 } 170 mRemoteService = new RemoteContentCaptureService(mMaster.getContext(), 171 ContentCaptureService.SERVICE_INTERFACE, serviceComponentName, 172 mRemoteServiceCallback, mUserId, this, mMaster.isBindInstantServiceAllowed(), 173 mMaster.verbose, mMaster.mDevCfgIdleUnbindTimeoutMs); 174 } 175 } 176 177 @Override // from PerUserSystemService newServiceInfoLocked(@onNull ComponentName serviceComponent)178 protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent) 179 throws NameNotFoundException { 180 mInfo = new ContentCaptureServiceInfo(getContext(), serviceComponent, 181 isTemporaryServiceSetLocked(), mUserId); 182 return mInfo.getServiceInfo(); 183 } 184 185 @Override // from PerUserSystemService 186 @GuardedBy("mLock") updateLocked(boolean disabled)187 protected boolean updateLocked(boolean disabled) { 188 final boolean disabledStateChanged = super.updateLocked(disabled); 189 if (disabledStateChanged) { 190 // update session content capture enabled state. 191 for (int i = 0; i < mSessions.size(); i++) { 192 mSessions.valueAt(i).setContentCaptureEnabledLocked(!disabled); 193 } 194 } 195 destroyLocked(); 196 updateRemoteServiceLocked(disabled); 197 return disabledStateChanged; 198 } 199 200 @Override // from ContentCaptureServiceCallbacks onServiceDied(@onNull RemoteContentCaptureService service)201 public void onServiceDied(@NonNull RemoteContentCaptureService service) { 202 // Don't do anything; eventually the system will bind to it again... 203 Slog.w(TAG, "remote service died: " + service); 204 synchronized (mLock) { 205 mZombie = true; 206 // Reset rebindCount if over 12 hours mLastRebindTime 207 if (mLastRebindTime != null && Instant.now().isAfter( 208 mLastRebindTime.plusMillis(12 * 60 * 60 * 1000))) { 209 if (mMaster.debug) { 210 Slog.i(TAG, "The current rebind count " + mRebindCount + " is reset."); 211 } 212 mRebindCount = 0; 213 } 214 if (mRebindCount >= MAX_REBIND_COUNTS) { 215 writeServiceEvent( 216 FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ON_REMOTE_SERVICE_DIED, 217 getServiceComponentName()); 218 } 219 if (mRebindCount < MAX_REBIND_COUNTS) { 220 mHandler.removeCallbacks(mReBindServiceRunnable); 221 mHandler.postDelayed(mReBindServiceRunnable, REBIND_DURATION_MS); 222 } 223 } 224 } 225 updateRemoteServiceAndResurrectSessionsLocked()226 private void updateRemoteServiceAndResurrectSessionsLocked() { 227 boolean disabled = !isEnabledLocked(); 228 updateRemoteServiceLocked(disabled); 229 resurrectSessionsLocked(); 230 } 231 232 private final class RebindServiceRunnable implements Runnable{ 233 234 @Override run()235 public void run() { 236 synchronized (mLock) { 237 if (mZombie) { 238 mLastRebindTime = Instant.now(); 239 mRebindCount++; 240 updateRemoteServiceAndResurrectSessionsLocked(); 241 } 242 } 243 } 244 } 245 246 /** 247 * Called after the remote service connected, it's used to restore state from a 'zombie' 248 * service (i.e., after it died). 249 */ onConnected()250 void onConnected() { 251 synchronized (mLock) { 252 if (mZombie) { 253 // Validity check - shouldn't happen 254 if (mRemoteService == null) { 255 Slog.w(TAG, "Cannot ressurect sessions because remote service is null"); 256 return; 257 } 258 259 mZombie = false; 260 resurrectSessionsLocked(); 261 } 262 } 263 } 264 resurrectSessionsLocked()265 private void resurrectSessionsLocked() { 266 final int numSessions = mSessions.size(); 267 if (mMaster.debug) { 268 Slog.d(TAG, "Ressurrecting remote service (" + mRemoteService + ") on " 269 + numSessions + " sessions"); 270 } 271 272 for (int i = 0; i < numSessions; i++) { 273 final ContentCaptureServerSession session = mSessions.valueAt(i); 274 session.resurrectLocked(); 275 } 276 } 277 onPackageUpdatingLocked()278 void onPackageUpdatingLocked() { 279 final int numSessions = mSessions.size(); 280 if (mMaster.debug) { 281 Slog.d(TAG, "Pausing " + numSessions + " sessions while package is updating"); 282 } 283 for (int i = 0; i < numSessions; i++) { 284 final ContentCaptureServerSession session = mSessions.valueAt(i); 285 session.pauseLocked(); 286 } 287 } 288 onPackageUpdatedLocked()289 void onPackageUpdatedLocked() { 290 mRebindCount = 0; 291 updateRemoteServiceAndResurrectSessionsLocked(); 292 } 293 294 @GuardedBy("mLock") startSessionLocked(@onNull IBinder activityToken, @NonNull IBinder shareableActivityToken, @NonNull ActivityPresentationInfo activityPresentationInfo, int sessionId, int uid, int flags, @NonNull IResultReceiver clientReceiver)295 public void startSessionLocked(@NonNull IBinder activityToken, 296 @NonNull IBinder shareableActivityToken, 297 @NonNull ActivityPresentationInfo activityPresentationInfo, int sessionId, int uid, 298 int flags, @NonNull IResultReceiver clientReceiver) { 299 if (activityPresentationInfo == null) { 300 Slog.w(TAG, "basic activity info is null"); 301 setClientState(clientReceiver, STATE_DISABLED | STATE_INTERNAL_ERROR, 302 /* binder= */ null); 303 return; 304 } 305 final int taskId = activityPresentationInfo.taskId; 306 final int displayId = activityPresentationInfo.displayId; 307 final ComponentName componentName = activityPresentationInfo.componentName; 308 final boolean whiteListed = mMaster.mGlobalContentCaptureOptions.isWhitelisted(mUserId, 309 componentName) || mMaster.mGlobalContentCaptureOptions.isWhitelisted(mUserId, 310 componentName.getPackageName()); 311 final ComponentName serviceComponentName = getServiceComponentName(); 312 final boolean enabled = isEnabledLocked(); 313 if (mMaster.mRequestsHistory != null) { 314 final String historyItem = 315 "id=" + sessionId + " uid=" + uid 316 + " a=" + ComponentName.flattenToShortString(componentName) 317 + " t=" + taskId + " d=" + displayId 318 + " s=" + ComponentName.flattenToShortString(serviceComponentName) 319 + " u=" + mUserId + " f=" + flags + (enabled ? "" : " (disabled)") 320 + " w=" + whiteListed; 321 mMaster.mRequestsHistory.log(historyItem); 322 } 323 324 if (!enabled) { 325 // TODO: it would be better to split in differet reasons, like 326 // STATE_DISABLED_NO and STATE_DISABLED_BY_DEVICE_POLICY 327 setClientState(clientReceiver, STATE_DISABLED | STATE_NO_SERVICE, 328 /* binder= */ null); 329 // Log metrics. 330 writeSessionEvent(sessionId, 331 FrameworkStatsLog.CONTENT_CAPTURE_SESSION_EVENTS__EVENT__SESSION_NOT_CREATED, 332 STATE_DISABLED | STATE_NO_SERVICE, serviceComponentName, 333 componentName, /* isChildSession= */ false); 334 return; 335 } 336 if (serviceComponentName == null) { 337 // TODO(b/111276913): this happens when the system service is starting, we should 338 // probably handle it in a more elegant way (like waiting for boot_complete or 339 // something like that 340 if (mMaster.debug) { 341 Slog.d(TAG, "startSession(" + activityToken + "): hold your horses"); 342 } 343 return; 344 } 345 346 if (!whiteListed) { 347 if (mMaster.debug) { 348 Slog.d(TAG, "startSession(" + componentName + "): package or component " 349 + "not whitelisted"); 350 } 351 setClientState(clientReceiver, STATE_DISABLED | STATE_NOT_WHITELISTED, 352 /* binder= */ null); 353 // Log metrics. 354 writeSessionEvent(sessionId, 355 FrameworkStatsLog.CONTENT_CAPTURE_SESSION_EVENTS__EVENT__SESSION_NOT_CREATED, 356 STATE_DISABLED | STATE_NOT_WHITELISTED, serviceComponentName, 357 componentName, /* isChildSession= */ false); 358 return; 359 } 360 361 final ContentCaptureServerSession existingSession = mSessions.get(sessionId); 362 if (existingSession != null) { 363 Slog.w(TAG, "startSession(id=" + existingSession + ", token=" + activityToken 364 + ": ignoring because it already exists for " + existingSession.mActivityToken); 365 setClientState(clientReceiver, STATE_DISABLED | STATE_DUPLICATED_ID, 366 /* binder=*/ null); 367 // Log metrics. 368 writeSessionEvent(sessionId, 369 FrameworkStatsLog.CONTENT_CAPTURE_SESSION_EVENTS__EVENT__SESSION_NOT_CREATED, 370 STATE_DISABLED | STATE_DUPLICATED_ID, 371 serviceComponentName, componentName, /* isChildSession= */ false); 372 return; 373 } 374 375 if (mRemoteService == null) { 376 updateRemoteServiceLocked(/* disabled= */ false); // already checked for isEnabled 377 } 378 379 if (mRemoteService == null) { 380 Slog.w(TAG, "startSession(id=" + existingSession + ", token=" + activityToken 381 + ": ignoring because service is not set"); 382 setClientState(clientReceiver, STATE_DISABLED | STATE_NO_SERVICE, 383 /* binder= */ null); 384 // Log metrics. 385 writeSessionEvent(sessionId, 386 FrameworkStatsLog.CONTENT_CAPTURE_SESSION_EVENTS__EVENT__SESSION_NOT_CREATED, 387 STATE_DISABLED | STATE_NO_SERVICE, serviceComponentName, 388 componentName, /* isChildSession= */ false); 389 return; 390 } 391 392 // Make sure service is bound, just in case the initial connection failed somehow 393 mRemoteService.ensureBoundLocked(); 394 395 final ContentCaptureServerSession newSession = new ContentCaptureServerSession(mLock, 396 activityToken, new ActivityId(taskId, shareableActivityToken), this, componentName, 397 clientReceiver, taskId, displayId, sessionId, uid, flags); 398 if (mMaster.verbose) { 399 Slog.v(TAG, "startSession(): new session for " 400 + ComponentName.flattenToShortString(componentName) + " and id " + sessionId); 401 } 402 mSessions.put(sessionId, newSession); 403 newSession.notifySessionStartedLocked(clientReceiver); 404 } 405 406 @GuardedBy("mLock") finishSessionLocked(int sessionId)407 public void finishSessionLocked(int sessionId) { 408 if (!isEnabledLocked()) { 409 return; 410 } 411 412 final ContentCaptureServerSession session = mSessions.get(sessionId); 413 if (session == null) { 414 if (mMaster.debug) { 415 Slog.d(TAG, "finishSession(): no session with id" + sessionId); 416 } 417 return; 418 } 419 if (mMaster.verbose) Slog.v(TAG, "finishSession(): id=" + sessionId); 420 session.removeSelfLocked(/* notifyRemoteService= */ true); 421 } 422 423 @GuardedBy("mLock") removeDataLocked(@onNull DataRemovalRequest request)424 public void removeDataLocked(@NonNull DataRemovalRequest request) { 425 if (!isEnabledLocked()) { 426 return; 427 } 428 assertCallerLocked(request.getPackageName()); 429 mRemoteService.onDataRemovalRequest(request); 430 } 431 432 @GuardedBy("mLock") onDataSharedLocked(@onNull DataShareRequest request, IDataShareCallback.Stub dataShareCallback)433 public void onDataSharedLocked(@NonNull DataShareRequest request, 434 IDataShareCallback.Stub dataShareCallback) { 435 if (!isEnabledLocked()) { 436 return; 437 } 438 assertCallerLocked(request.getPackageName()); 439 mRemoteService.onDataShareRequest(request, dataShareCallback); 440 } 441 442 @GuardedBy("mLock") 443 @Nullable getServiceSettingsActivityLocked()444 public ComponentName getServiceSettingsActivityLocked() { 445 if (mInfo == null) return null; 446 447 final String activityName = mInfo.getSettingsActivity(); 448 if (activityName == null) return null; 449 450 final String packageName = mInfo.getServiceInfo().packageName; 451 return new ComponentName(packageName, activityName); 452 } 453 454 /** 455 * Asserts the component is owned by the caller. 456 */ 457 @GuardedBy("mLock") assertCallerLocked(@onNull String packageName)458 private void assertCallerLocked(@NonNull String packageName) { 459 final PackageManager pm = getContext().getPackageManager(); 460 final int callingUid = Binder.getCallingUid(); 461 final int packageUid; 462 try { 463 packageUid = pm.getPackageUidAsUser(packageName, UserHandle.getCallingUserId()); 464 } catch (NameNotFoundException e) { 465 throw new SecurityException("Could not verify UID for " + packageName); 466 } 467 if (callingUid != packageUid && !LocalServices.getService(ActivityManagerInternal.class) 468 .hasRunningActivity(callingUid, packageName)) { 469 470 VoiceInteractionManagerInternal.HotwordDetectionServiceIdentity 471 hotwordDetectionServiceIdentity = 472 LocalServices.getService(VoiceInteractionManagerInternal.class) 473 .getHotwordDetectionServiceIdentity(); 474 475 boolean isHotwordDetectionServiceCall = 476 hotwordDetectionServiceIdentity != null 477 && callingUid == hotwordDetectionServiceIdentity.getIsolatedUid() 478 && packageUid == hotwordDetectionServiceIdentity.getOwnerUid(); 479 480 if (!isHotwordDetectionServiceCall) { 481 final String[] packages = pm.getPackagesForUid(callingUid); 482 final String callingPackage = packages != null ? packages[0] : "uid-" + callingUid; 483 Slog.w(TAG, "App (package=" + callingPackage + ", UID=" + callingUid 484 + ") passed package (" + packageName + ") owned by UID " + packageUid); 485 486 throw new SecurityException("Invalid package: " + packageName); 487 } 488 } 489 } 490 491 @GuardedBy("mLock") sendActivityAssistDataLocked(@onNull IBinder activityToken, @NonNull Bundle data)492 public boolean sendActivityAssistDataLocked(@NonNull IBinder activityToken, 493 @NonNull Bundle data) { 494 final int id = getSessionId(activityToken); 495 final Bundle assistData = data.getBundle(ASSIST_KEY_DATA); 496 final AssistStructure assistStructure = data.getParcelable(ASSIST_KEY_STRUCTURE); 497 final AssistContent assistContent = data.getParcelable(ASSIST_KEY_CONTENT); 498 final SnapshotData snapshotData = new SnapshotData(assistData, 499 assistStructure, assistContent); 500 if (id != NO_SESSION_ID) { 501 final ContentCaptureServerSession session = mSessions.get(id); 502 session.sendActivitySnapshotLocked(snapshotData); 503 return true; 504 } 505 506 // We want to send an activity snapshot regardless of whether a content capture session is 507 // present or not since a content capture session is not required for this functionality 508 if (mRemoteService != null) { 509 mRemoteService.onActivitySnapshotRequest(NO_SESSION_ID, snapshotData); 510 Slog.d(TAG, "Notified activity assist data for activity: " 511 + activityToken + " without a session Id"); 512 return true; 513 } 514 515 return false; 516 } 517 518 @GuardedBy("mLock") removeSessionLocked(int sessionId)519 public void removeSessionLocked(int sessionId) { 520 mSessions.remove(sessionId); 521 } 522 523 @GuardedBy("mLock") isContentCaptureServiceForUserLocked(int uid)524 public boolean isContentCaptureServiceForUserLocked(int uid) { 525 return uid == getServiceUidLocked(); 526 } 527 528 @GuardedBy("mLock") getSession(@onNull IBinder activityToken)529 private ContentCaptureServerSession getSession(@NonNull IBinder activityToken) { 530 for (int i = 0; i < mSessions.size(); i++) { 531 final ContentCaptureServerSession session = mSessions.valueAt(i); 532 if (session.mActivityToken.equals(activityToken)) { 533 return session; 534 } 535 } 536 return null; 537 } 538 539 /** 540 * Destroys the service and all state associated with it. 541 * 542 * <p>Called when the service was disabled (for example, if the settings change). 543 */ 544 @GuardedBy("mLock") destroyLocked()545 public void destroyLocked() { 546 if (mMaster.debug) Slog.d(TAG, "destroyLocked()"); 547 if (mRemoteService != null) { 548 mRemoteService.destroy(); 549 } 550 destroySessionsLocked(); 551 } 552 553 @GuardedBy("mLock") destroySessionsLocked()554 void destroySessionsLocked() { 555 final int numSessions = mSessions.size(); 556 for (int i = 0; i < numSessions; i++) { 557 final ContentCaptureServerSession session = mSessions.valueAt(i); 558 session.destroyLocked(/* notifyRemoteService= */ true); 559 } 560 mSessions.clear(); 561 } 562 563 @GuardedBy("mLock") listSessionsLocked(ArrayList<String> output)564 void listSessionsLocked(ArrayList<String> output) { 565 final int numSessions = mSessions.size(); 566 for (int i = 0; i < numSessions; i++) { 567 final ContentCaptureServerSession session = mSessions.valueAt(i); 568 output.add(session.toShortString()); 569 } 570 } 571 572 @GuardedBy("mLock") 573 @Nullable getContentCaptureConditionsLocked( @onNull String packageName)574 ArraySet<ContentCaptureCondition> getContentCaptureConditionsLocked( 575 @NonNull String packageName) { 576 return mConditionsByPkg.get(packageName); 577 } 578 579 @GuardedBy("mLock") onActivityEventLocked(@onNull ComponentName componentName, @ActivityEventType int type)580 void onActivityEventLocked(@NonNull ComponentName componentName, @ActivityEventType int type) { 581 if (mRemoteService == null) { 582 if (mMaster.debug) Slog.d(mTag, "onActivityEvent(): no remote service"); 583 return; 584 } 585 final ActivityEvent event = new ActivityEvent(componentName, type); 586 587 if (mMaster.verbose) Slog.v(mTag, "onActivityEvent(): " + event); 588 589 mRemoteService.onActivityLifecycleEvent(event); 590 } 591 592 @Override dumpLocked(String prefix, PrintWriter pw)593 protected void dumpLocked(String prefix, PrintWriter pw) { 594 super.dumpLocked(prefix, pw); 595 596 final String prefix2 = prefix + " "; 597 pw.print(prefix); pw.print("Service Info: "); 598 if (mInfo == null) { 599 pw.println("N/A"); 600 } else { 601 pw.println(); 602 mInfo.dump(prefix2, pw); 603 } 604 pw.print(prefix); pw.print("Zombie: "); pw.println(mZombie); 605 pw.print(prefix); pw.print("Rebind count: "); pw.println(mRebindCount); 606 pw.print(prefix); pw.print("Last rebind: "); pw.println(mLastRebindTime); 607 608 if (mRemoteService != null) { 609 pw.print(prefix); pw.println("remote service:"); 610 mRemoteService.dump(prefix2, pw); 611 } 612 613 if (mSessions.size() == 0) { 614 pw.print(prefix); pw.println("no sessions"); 615 } else { 616 final int sessionsSize = mSessions.size(); 617 pw.print(prefix); pw.print("number sessions: "); pw.println(sessionsSize); 618 for (int i = 0; i < sessionsSize; i++) { 619 pw.print(prefix); pw.print("#"); pw.println(i); 620 final ContentCaptureServerSession session = mSessions.valueAt(i); 621 session.dumpLocked(prefix2, pw); 622 pw.println(); 623 } 624 } 625 } 626 627 /** 628 * Returns the session id associated with the given activity. 629 */ 630 @GuardedBy("mLock") getSessionId(@onNull IBinder activityToken)631 private int getSessionId(@NonNull IBinder activityToken) { 632 for (int i = 0; i < mSessions.size(); i++) { 633 ContentCaptureServerSession session = mSessions.valueAt(i); 634 if (session.isActivitySession(activityToken)) { 635 return mSessions.keyAt(i); 636 } 637 } 638 return NO_SESSION_ID; 639 } 640 641 /** 642 * Resets the content capture allowlist. 643 */ 644 @GuardedBy("mLock") resetContentCaptureWhitelistLocked()645 private void resetContentCaptureWhitelistLocked() { 646 if (mMaster.verbose) { 647 Slog.v(TAG, "resetting content capture whitelist"); 648 } 649 mMaster.mGlobalContentCaptureOptions.resetWhitelist(mUserId); 650 } 651 652 private final class ContentCaptureServiceRemoteCallback extends 653 IContentCaptureServiceCallback.Stub { 654 655 @Override setContentCaptureWhitelist(List<String> packages, List<ComponentName> activities)656 public void setContentCaptureWhitelist(List<String> packages, 657 List<ComponentName> activities) { 658 // TODO(b/122595322): add CTS test for when it's null 659 if (mMaster.verbose) { 660 Slog.v(TAG, "setContentCaptureWhitelist(" + (packages == null 661 ? "null_packages" : packages.size() + " packages") 662 + ", " + (activities == null 663 ? "null_activities" : activities.size() + " activities") + ")" 664 + " for user " + mUserId); 665 } 666 667 ArraySet<String> oldList = 668 mMaster.mGlobalContentCaptureOptions.getWhitelistedPackages(mUserId); 669 670 mMaster.mGlobalContentCaptureOptions.setWhitelist(mUserId, packages, activities); 671 writeSetWhitelistEvent(getServiceComponentName(), packages, activities); 672 673 updateContentCaptureOptions(oldList); 674 675 // Must disable session that are not the allowlist anymore... 676 final int numSessions = mSessions.size(); 677 if (numSessions <= 0) return; 678 679 // ...but without holding the lock on mGlobalContentCaptureOptions 680 final SparseBooleanArray blacklistedSessions = new SparseBooleanArray(numSessions); 681 682 for (int i = 0; i < numSessions; i++) { 683 final ContentCaptureServerSession session = mSessions.valueAt(i); 684 final boolean whitelisted = mMaster.mGlobalContentCaptureOptions 685 .isWhitelisted(mUserId, session.appComponentName); 686 if (!whitelisted) { 687 final int sessionId = mSessions.keyAt(i); 688 if (mMaster.debug) { 689 Slog.d(TAG, "marking session " + sessionId + " (" + session.appComponentName 690 + ") for un-whitelisting"); 691 } 692 blacklistedSessions.append(sessionId, true); 693 } 694 } 695 final int numBlacklisted = blacklistedSessions.size(); 696 697 if (numBlacklisted <= 0) return; 698 699 synchronized (mLock) { 700 for (int i = 0; i < numBlacklisted; i++) { 701 final int sessionId = blacklistedSessions.keyAt(i); 702 if (mMaster.debug) Slog.d(TAG, "un-whitelisting " + sessionId); 703 final ContentCaptureServerSession session = mSessions.get(sessionId); 704 session.setContentCaptureEnabledLocked(false); 705 } 706 } 707 } 708 709 @Override setContentCaptureConditions(String packageName, List<ContentCaptureCondition> conditions)710 public void setContentCaptureConditions(String packageName, 711 List<ContentCaptureCondition> conditions) { 712 if (mMaster.verbose) { 713 Slog.v(TAG, "setContentCaptureConditions(" + packageName + "): " 714 + (conditions == null ? "null" : conditions.size() + " conditions")); 715 } 716 synchronized (mLock) { 717 if (conditions == null) { 718 mConditionsByPkg.remove(packageName); 719 } else { 720 mConditionsByPkg.put(packageName, new ArraySet<>(conditions)); 721 } 722 } 723 } 724 725 @Override disableSelf()726 public void disableSelf() { 727 if (mMaster.verbose) Slog.v(TAG, "disableSelf()"); 728 729 final long token = Binder.clearCallingIdentity(); 730 try { 731 Settings.Secure.putStringForUser(getContext().getContentResolver(), 732 Settings.Secure.CONTENT_CAPTURE_ENABLED, "0", mUserId); 733 } finally { 734 Binder.restoreCallingIdentity(token); 735 } 736 writeServiceEvent(FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__SET_DISABLED, 737 getServiceComponentName()); 738 } 739 740 @Override writeSessionFlush(int sessionId, ComponentName app, FlushMetrics flushMetrics, ContentCaptureOptions options, int flushReason)741 public void writeSessionFlush(int sessionId, ComponentName app, FlushMetrics flushMetrics, 742 ContentCaptureOptions options, int flushReason) { 743 ContentCaptureMetricsLogger.writeSessionFlush(sessionId, getServiceComponentName(), app, 744 flushMetrics, options, flushReason); 745 } 746 747 /** Updates {@link ContentCaptureOptions} for all newly added packages on allowlist. */ updateContentCaptureOptions(@ullable ArraySet<String> oldList)748 private void updateContentCaptureOptions(@Nullable ArraySet<String> oldList) { 749 ArraySet<String> adding = mMaster.mGlobalContentCaptureOptions 750 .getWhitelistedPackages(mUserId); 751 752 if (oldList != null && adding != null) { 753 adding.removeAll(oldList); 754 } 755 756 int N = adding != null ? adding.size() : 0; 757 for (int i = 0; i < N; i++) { 758 String packageName = adding.valueAt(i); 759 ContentCaptureOptions options = mMaster.mGlobalContentCaptureOptions 760 .getOptions(mUserId, packageName); 761 mMaster.updateOptions(packageName, options); 762 } 763 } 764 } 765 } 766