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