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