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