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