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