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