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