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