• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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