blob: 8ab8f8dd474130226e9b316e059c28b5c8d0d967 [file] [log] [blame]
Felipe Lemee348dc32018-11-05 12:35:29 -08001/*
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
Felipe Leme749b8892018-12-03 16:30:30 -080017package com.android.server.contentcapture;
Felipe Lemee348dc32018-11-05 12:35:29 -080018
Felipe Lemeb96878492018-12-17 12:22:29 -080019import static android.service.contentcapture.ContentCaptureService.setClientState;
Felipe Leme01b87492019-01-15 13:26:52 -080020import static android.view.contentcapture.ContentCaptureSession.STATE_DISABLED;
21import static android.view.contentcapture.ContentCaptureSession.STATE_DUPLICATED_ID;
Adam Hebb82c6a2019-01-17 14:59:02 -080022import static android.view.contentcapture.ContentCaptureSession.STATE_INTERNAL_ERROR;
Adam He6240eab2019-02-25 13:34:45 -080023import static android.view.contentcapture.ContentCaptureSession.STATE_NOT_WHITELISTED;
Felipe Leme01b87492019-01-15 13:26:52 -080024import static android.view.contentcapture.ContentCaptureSession.STATE_NO_SERVICE;
Felipe Lemeb96878492018-12-17 12:22:29 -080025
Winson Chungfbbb1582018-11-13 16:09:01 -080026import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT;
27import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_DATA;
28import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
29
Felipe Lemee348dc32018-11-05 12:35:29 -080030import android.Manifest;
31import android.annotation.NonNull;
Adam He6240eab2019-02-25 13:34:45 -080032import android.annotation.Nullable;
Felipe Leme284ad1c2018-11-15 18:16:12 -080033import android.annotation.UserIdInt;
Adam He3d0409b2019-01-15 14:22:04 -080034import android.app.ActivityManagerInternal;
Felipe Lemee348dc32018-11-05 12:35:29 -080035import android.app.AppGlobals;
Winson Chungfbbb1582018-11-13 16:09:01 -080036import android.app.assist.AssistContent;
37import android.app.assist.AssistStructure;
Felipe Lemee348dc32018-11-05 12:35:29 -080038import android.content.ComponentName;
Felipe Leme326f15a2019-02-19 09:42:24 -080039import android.content.ContentCaptureOptions;
Adam Hebb82c6a2019-01-17 14:59:02 -080040import android.content.pm.ActivityPresentationInfo;
Felipe Lemee348dc32018-11-05 12:35:29 -080041import android.content.pm.PackageManager;
42import android.content.pm.PackageManager.NameNotFoundException;
43import android.content.pm.ServiceInfo;
Adam He3d0409b2019-01-15 14:22:04 -080044import android.os.Binder;
Winson Chungfbbb1582018-11-13 16:09:01 -080045import android.os.Bundle;
Felipe Lemee348dc32018-11-05 12:35:29 -080046import android.os.IBinder;
47import android.os.RemoteException;
Adam He3d0409b2019-01-15 14:22:04 -080048import android.os.UserHandle;
Felipe Leme72e83d82019-02-13 14:51:17 -080049import android.provider.Settings;
Felipe Leme50b33dc2018-12-20 16:01:39 -080050import android.service.contentcapture.ContentCaptureService;
Felipe Lemeaf4e4fd2019-01-15 08:32:42 -080051import android.service.contentcapture.IContentCaptureServiceCallback;
Felipe Leme749b8892018-12-03 16:30:30 -080052import android.service.contentcapture.SnapshotData;
Felipe Lemee348dc32018-11-05 12:35:29 -080053import android.util.ArrayMap;
Felipe Lemee8eae9d42019-02-13 15:41:55 -080054import android.util.ArraySet;
Felipe Lemee348dc32018-11-05 12:35:29 -080055import android.util.Slog;
Adam He3d0409b2019-01-15 14:22:04 -080056import android.view.contentcapture.UserDataRemovalRequest;
Felipe Lemee348dc32018-11-05 12:35:29 -080057
58import com.android.internal.annotations.GuardedBy;
Adam He6240eab2019-02-25 13:34:45 -080059import com.android.internal.infra.WhitelistHelper;
Felipe Lemee348dc32018-11-05 12:35:29 -080060import com.android.internal.os.IResultReceiver;
Adam He3d0409b2019-01-15 14:22:04 -080061import com.android.server.LocalServices;
Felipe Leme50b33dc2018-12-20 16:01:39 -080062import com.android.server.contentcapture.RemoteContentCaptureService.ContentCaptureServiceCallbacks;
Felipe Leme1e3de572018-12-05 15:44:17 -080063import com.android.server.infra.AbstractPerUserSystemService;
Felipe Lemee348dc32018-11-05 12:35:29 -080064
65import java.io.PrintWriter;
Felipe Leme5933efb2018-11-29 16:08:49 -080066import java.util.ArrayList;
Felipe Lemeaf4e4fd2019-01-15 08:32:42 -080067import java.util.List;
Felipe Lemee348dc32018-11-05 12:35:29 -080068
69/**
Felipe Leme749b8892018-12-03 16:30:30 -080070 * Per-user instance of {@link ContentCaptureManagerService}.
Felipe Lemee348dc32018-11-05 12:35:29 -080071 */
Felipe Leme749b8892018-12-03 16:30:30 -080072final class ContentCapturePerUserService
73 extends
Felipe Leme50b33dc2018-12-20 16:01:39 -080074 AbstractPerUserSystemService<ContentCapturePerUserService, ContentCaptureManagerService>
75 implements ContentCaptureServiceCallbacks {
Felipe Lemee348dc32018-11-05 12:35:29 -080076
Felipe Leme749b8892018-12-03 16:30:30 -080077 private static final String TAG = ContentCaptureManagerService.class.getSimpleName();
Felipe Lemee348dc32018-11-05 12:35:29 -080078
Felipe Lemee348dc32018-11-05 12:35:29 -080079 @GuardedBy("mLock")
Felipe Lemeaa5088e2018-12-10 14:53:58 -080080 private final ArrayMap<String, ContentCaptureServerSession> mSessions =
Felipe Lemea7bdb142018-11-05 16:29:29 -080081 new ArrayMap<>();
Felipe Lemee348dc32018-11-05 12:35:29 -080082
Felipe Leme50b33dc2018-12-20 16:01:39 -080083 /**
84 * Reference to the remote service.
85 *
86 * <p>It's set in the constructor, but it's also updated when the service's updated in the
87 * master's cache (for example, because a temporary service was set).
88 */
89 @GuardedBy("mLock")
90 private RemoteContentCaptureService mRemoteService;
91
Felipe Lemeaf4e4fd2019-01-15 08:32:42 -080092 private final ContentCaptureServiceRemoteCallback mRemoteServiceCallback =
93 new ContentCaptureServiceRemoteCallback();
94
Felipe Lemee8eae9d42019-02-13 15:41:55 -080095 /**
96 * List of packages that are whitelisted to be content captured.
97 */
98 @GuardedBy("mLock")
Adam He6240eab2019-02-25 13:34:45 -080099 private final WhitelistHelper mWhitelistHelper = new WhitelistHelper();
Felipe Lemee8eae9d42019-02-13 15:41:55 -0800100
Felipe Lemee348dc32018-11-05 12:35:29 -0800101 // TODO(b/111276913): add mechanism to prune stale sessions, similar to Autofill's
102
Felipe Leme50b33dc2018-12-20 16:01:39 -0800103 ContentCapturePerUserService(@NonNull ContentCaptureManagerService master,
104 @NonNull Object lock, boolean disabled, @UserIdInt int userId) {
Felipe Lemec28711c2018-12-20 11:17:02 -0800105 super(master, lock, userId);
Felipe Leme50b33dc2018-12-20 16:01:39 -0800106 updateRemoteServiceLocked(disabled);
107 }
108
109 /**
110 * Updates the reference to the remote service.
111 */
112 private void updateRemoteServiceLocked(boolean disabled) {
113 if (mRemoteService != null) {
114 if (mMaster.debug) Slog.d(TAG, "updateRemoteService(): destroying old remote service");
115 mRemoteService.destroy();
116 mRemoteService = null;
117 }
118
119 // Updates the component name
120 final ComponentName serviceComponentName = updateServiceInfoLocked();
121
122 if (serviceComponentName == null) {
Felipe Lemee127c9a2019-01-04 14:56:38 -0800123 if (mMaster.debug) Slog.d(TAG, "updateRemoteService(): no service component name");
Felipe Leme50b33dc2018-12-20 16:01:39 -0800124 return;
125 }
126
127 if (!disabled) {
Felipe Lemeaf4e4fd2019-01-15 08:32:42 -0800128 mRemoteService = new RemoteContentCaptureService(mMaster.getContext(),
129 ContentCaptureService.SERVICE_INTERFACE, serviceComponentName,
130 mRemoteServiceCallback, mUserId, this, mMaster.isBindInstantServiceAllowed(),
Felipe Lemee764fa22019-02-21 16:45:35 -0800131 mMaster.verbose, mMaster.mDevCfgIdleUnbindTimeoutMs);
Felipe Leme50b33dc2018-12-20 16:01:39 -0800132 }
Felipe Lemee348dc32018-11-05 12:35:29 -0800133 }
134
135 @Override // from PerUserSystemService
Felipe Leme50b33dc2018-12-20 16:01:39 -0800136 protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent)
Felipe Lemee348dc32018-11-05 12:35:29 -0800137 throws NameNotFoundException {
138
Felipe Leme658c8e42018-11-30 12:49:45 -0800139 int flags = PackageManager.GET_META_DATA;
140 final boolean isTemp = isTemporaryServiceSetLocked();
141 if (!isTemp) {
142 flags |= PackageManager.MATCH_SYSTEM_ONLY;
143 }
144
Felipe Lemee348dc32018-11-05 12:35:29 -0800145 ServiceInfo si;
146 try {
Felipe Leme658c8e42018-11-30 12:49:45 -0800147 si = AppGlobals.getPackageManager().getServiceInfo(serviceComponent, flags, mUserId);
Felipe Lemee348dc32018-11-05 12:35:29 -0800148 } catch (RemoteException e) {
149 Slog.w(TAG, "Could not get service for " + serviceComponent + ": " + e);
150 return null;
151 }
Felipe Leme658c8e42018-11-30 12:49:45 -0800152 if (si == null) {
153 Slog.w(TAG, "Could not get serviceInfo for " + (isTemp ? " (temp)" : "(default system)")
154 + " " + serviceComponent.flattenToShortString());
155 return null;
156 }
Felipe Leme749b8892018-12-03 16:30:30 -0800157 if (!Manifest.permission.BIND_CONTENT_CAPTURE_SERVICE.equals(si.permission)) {
158 Slog.w(TAG, "ContentCaptureService from '" + si.packageName
Felipe Lemee348dc32018-11-05 12:35:29 -0800159 + "' does not require permission "
Felipe Leme749b8892018-12-03 16:30:30 -0800160 + Manifest.permission.BIND_CONTENT_CAPTURE_SERVICE);
Felipe Lemee348dc32018-11-05 12:35:29 -0800161 throw new SecurityException("Service does not require permission "
Felipe Leme749b8892018-12-03 16:30:30 -0800162 + Manifest.permission.BIND_CONTENT_CAPTURE_SERVICE);
Felipe Lemee348dc32018-11-05 12:35:29 -0800163 }
164 return si;
165 }
166
Felipe Lemeecb08be2018-11-27 15:48:47 -0800167 @Override // from PerUserSystemService
168 @GuardedBy("mLock")
169 protected boolean updateLocked(boolean disabled) {
170 destroyLocked();
Felipe Leme50b33dc2018-12-20 16:01:39 -0800171 final boolean disabledStateChanged = super.updateLocked(disabled);
172 updateRemoteServiceLocked(disabled);
173 return disabledStateChanged;
Felipe Lemeecb08be2018-11-27 15:48:47 -0800174 }
175
Felipe Leme50b33dc2018-12-20 16:01:39 -0800176 @Override // from ContentCaptureServiceCallbacks
177 public void onServiceDied(@NonNull RemoteContentCaptureService service) {
178 if (mMaster.debug) Slog.d(TAG, "remote service died: " + service);
179 synchronized (mLock) {
180 removeSelfFromCacheLocked();
181 }
182 }
183
184 // TODO(b/119613670): log metrics
Felipe Lemee348dc32018-11-05 12:35:29 -0800185 @GuardedBy("mLock")
186 public void startSessionLocked(@NonNull IBinder activityToken,
Adam Hebb82c6a2019-01-17 14:59:02 -0800187 @NonNull ActivityPresentationInfo activityPresentationInfo,
Felipe Leme50b33dc2018-12-20 16:01:39 -0800188 @NonNull String sessionId, int uid, int flags,
Felipe Leme87a9dc92018-12-18 14:28:07 -0800189 @NonNull IResultReceiver clientReceiver) {
Adam Hebb82c6a2019-01-17 14:59:02 -0800190 if (activityPresentationInfo == null) {
191 Slog.w(TAG, "basic activity info is null");
192 setClientState(clientReceiver, STATE_DISABLED | STATE_INTERNAL_ERROR,
193 /* binder= */ null);
194 return;
195 }
196 final int taskId = activityPresentationInfo.taskId;
197 final int displayId = activityPresentationInfo.displayId;
198 final ComponentName componentName = activityPresentationInfo.componentName;
Adam He6240eab2019-02-25 13:34:45 -0800199 final boolean whiteListed = isWhitelistedLocked(componentName);
Felipe Leme930f3242019-01-15 16:47:09 -0800200 final ComponentName serviceComponentName = getServiceComponentName();
201 final boolean enabled = isEnabledLocked();
Felipe Lemed32d8f6f2019-02-15 10:25:33 -0800202 if (mMaster.mRequestsHistory != null) {
203 final String historyItem =
204 "id=" + sessionId + " uid=" + uid
205 + " a=" + ComponentName.flattenToShortString(componentName)
206 + " t=" + taskId + " d=" + displayId
207 + " s=" + ComponentName.flattenToShortString(serviceComponentName)
Felipe Lemee8eae9d42019-02-13 15:41:55 -0800208 + " u=" + mUserId + " f=" + flags + (enabled ? "" : " (disabled)")
Adam He6240eab2019-02-25 13:34:45 -0800209 + " w=" + whiteListed;
Felipe Lemed32d8f6f2019-02-15 10:25:33 -0800210 mMaster.mRequestsHistory.log(historyItem);
211 }
Felipe Leme930f3242019-01-15 16:47:09 -0800212
213 if (!enabled) {
Felipe Lemee127c9a2019-01-04 14:56:38 -0800214 // TODO: it would be better to split in differet reasons, like
215 // STATE_DISABLED_NO_SERVICE and STATE_DISABLED_BY_DEVICE_POLICY
Felipe Leme01b87492019-01-15 13:26:52 -0800216 setClientState(clientReceiver, STATE_DISABLED | STATE_NO_SERVICE,
Felipe Lemee127c9a2019-01-04 14:56:38 -0800217 /* binder= */ null);
Felipe Leme8e2e3412018-11-14 10:39:29 -0800218 return;
219 }
Felipe Lemee348dc32018-11-05 12:35:29 -0800220 if (serviceComponentName == null) {
221 // TODO(b/111276913): this happens when the system service is starting, we should
222 // probably handle it in a more elegant way (like waiting for boot_complete or
223 // something like that
Felipe Leme8e2e3412018-11-14 10:39:29 -0800224 if (mMaster.debug) {
225 Slog.d(TAG, "startSession(" + activityToken + "): hold your horses");
226 }
Felipe Lemee348dc32018-11-05 12:35:29 -0800227 return;
228 }
229
Adam He6240eab2019-02-25 13:34:45 -0800230 if (!whiteListed) {
Felipe Lemee8eae9d42019-02-13 15:41:55 -0800231 if (mMaster.debug) {
Adam He6240eab2019-02-25 13:34:45 -0800232 Slog.d(TAG, "startSession(" + componentName + "): package or component "
233 + "not whitelisted");
Felipe Lemee8eae9d42019-02-13 15:41:55 -0800234 }
Adam He6240eab2019-02-25 13:34:45 -0800235 setClientState(clientReceiver, STATE_DISABLED | STATE_NOT_WHITELISTED,
Felipe Lemee8eae9d42019-02-13 15:41:55 -0800236 /* binder= */ null);
237 return;
238 }
239
Felipe Lemeb96878492018-12-17 12:22:29 -0800240 final ContentCaptureServerSession existingSession = mSessions.get(sessionId);
241 if (existingSession != null) {
242 Slog.w(TAG, "startSession(id=" + existingSession + ", token=" + activityToken
243 + ": ignoring because it already exists for " + existingSession.mActivityToken);
Felipe Leme01b87492019-01-15 13:26:52 -0800244 setClientState(clientReceiver, STATE_DISABLED | STATE_DUPLICATED_ID,
Felipe Lemeb96878492018-12-17 12:22:29 -0800245 /* binder=*/ null);
Felipe Lemee348dc32018-11-05 12:35:29 -0800246 return;
247 }
248
Felipe Leme50b33dc2018-12-20 16:01:39 -0800249 if (mRemoteService == null) {
250 updateRemoteServiceLocked(/* disabled= */ false); // already checked for isEnabled
251 }
252
253 if (mRemoteService == null) {
254 // TODO(b/119613670): log metrics
255 Slog.w(TAG, "startSession(id=" + existingSession + ", token=" + activityToken
256 + ": ignoring because service is not set");
Felipe Leme01b87492019-01-15 13:26:52 -0800257 setClientState(clientReceiver, STATE_DISABLED | STATE_NO_SERVICE,
Felipe Lemee127c9a2019-01-04 14:56:38 -0800258 /* binder= */ null);
Felipe Leme50b33dc2018-12-20 16:01:39 -0800259 return;
260 }
261
262 final ContentCaptureServerSession newSession = new ContentCaptureServerSession(
263 activityToken, this, mRemoteService, componentName, taskId,
264 displayId, sessionId, uid, flags);
Felipe Lemee348dc32018-11-05 12:35:29 -0800265 if (mMaster.verbose) {
Felipe Lemeb96878492018-12-17 12:22:29 -0800266 Slog.v(TAG, "startSession(): new session for "
267 + ComponentName.flattenToShortString(componentName) + " and id " + sessionId);
Felipe Lemee348dc32018-11-05 12:35:29 -0800268 }
Felipe Lemeb96878492018-12-17 12:22:29 -0800269 mSessions.put(sessionId, newSession);
270 newSession.notifySessionStartedLocked(clientReceiver);
Felipe Lemee348dc32018-11-05 12:35:29 -0800271 }
272
Felipe Lemee8eae9d42019-02-13 15:41:55 -0800273 @GuardedBy("mLock")
274 private boolean isWhitelistedLocked(@NonNull ComponentName componentName) {
Adam He6240eab2019-02-25 13:34:45 -0800275 return mWhitelistHelper.isWhitelisted(componentName);
Felipe Lemee8eae9d42019-02-13 15:41:55 -0800276 }
277
Adam He6240eab2019-02-25 13:34:45 -0800278 private void setWhitelist(@Nullable List<String> packages,
279 @Nullable List<ComponentName> components) {
Felipe Lemee8eae9d42019-02-13 15:41:55 -0800280 // TODO(b/122595322): add CTS test for when it's null
281 synchronized (mLock) {
Adam He6240eab2019-02-25 13:34:45 -0800282 if (mMaster.verbose) {
283 Slog.v(TAG, "whitelisting packages: " + packages + "and activities: " + components);
Felipe Lemee8eae9d42019-02-13 15:41:55 -0800284 }
Adam He6240eab2019-02-25 13:34:45 -0800285 mWhitelistHelper.setWhitelist(new ArraySet<>(packages), new ArraySet<>(components));
Felipe Lemee8eae9d42019-02-13 15:41:55 -0800286 }
287 }
288
Felipe Leme50b33dc2018-12-20 16:01:39 -0800289 // TODO(b/119613670): log metrics
Felipe Lemee348dc32018-11-05 12:35:29 -0800290 @GuardedBy("mLock")
Felipe Lemeb96878492018-12-17 12:22:29 -0800291 public void finishSessionLocked(@NonNull String sessionId) {
Felipe Leme8e2e3412018-11-14 10:39:29 -0800292 if (!isEnabledLocked()) {
293 return;
294 }
295
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800296 final ContentCaptureServerSession session = mSessions.get(sessionId);
Felipe Lemee348dc32018-11-05 12:35:29 -0800297 if (session == null) {
Felipe Leme8e2e3412018-11-14 10:39:29 -0800298 if (mMaster.debug) {
299 Slog.d(TAG, "finishSession(): no session with id" + sessionId);
300 }
Felipe Lemee348dc32018-11-05 12:35:29 -0800301 return;
302 }
Felipe Lemeb96878492018-12-17 12:22:29 -0800303 if (mMaster.verbose) Slog.v(TAG, "finishSession(): id=" + sessionId);
304 session.removeSelfLocked(/* notifyRemoteService= */ true);
Felipe Leme7a534082018-11-05 15:03:04 -0800305 }
306
307 @GuardedBy("mLock")
Adam He3d0409b2019-01-15 14:22:04 -0800308 public void removeUserDataLocked(@NonNull UserDataRemovalRequest request) {
309 if (!isEnabledLocked()) {
310 return;
311 }
312 assertCallerLocked(request.getPackageName());
313 mRemoteService.onUserDataRemovalRequest(request);
314 }
315
316 /**
317 * Asserts the component is owned by the caller.
318 */
319 @GuardedBy("mLock")
320 private void assertCallerLocked(@NonNull String packageName) {
321 final PackageManager pm = getContext().getPackageManager();
322 final int callingUid = Binder.getCallingUid();
323 final int packageUid;
324 try {
325 packageUid = pm.getPackageUidAsUser(packageName, UserHandle.getCallingUserId());
326 } catch (NameNotFoundException e) {
327 throw new SecurityException("Could not verify UID for " + packageName);
328 }
329 if (callingUid != packageUid && !LocalServices.getService(ActivityManagerInternal.class)
330 .hasRunningActivity(callingUid, packageName)) {
331 final String[] packages = pm.getPackagesForUid(callingUid);
332 final String callingPackage = packages != null ? packages[0] : "uid-" + callingUid;
333 Slog.w(TAG, "App (package=" + callingPackage + ", UID=" + callingUid
334 + ") passed package (" + packageName + ") owned by UID " + packageUid);
335
336 throw new SecurityException("Invalid package: " + packageName);
337 }
338 }
339
340 @GuardedBy("mLock")
Winson Chungfbbb1582018-11-13 16:09:01 -0800341 public boolean sendActivityAssistDataLocked(@NonNull IBinder activityToken,
342 @NonNull Bundle data) {
Felipe Leme749b8892018-12-03 16:30:30 -0800343 final String id = getSessionId(activityToken);
Winson Chungfbbb1582018-11-13 16:09:01 -0800344 if (id != null) {
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800345 final ContentCaptureServerSession session = mSessions.get(id);
Winson Chungfbbb1582018-11-13 16:09:01 -0800346 final Bundle assistData = data.getBundle(ASSIST_KEY_DATA);
347 final AssistStructure assistStructure = data.getParcelable(ASSIST_KEY_STRUCTURE);
348 final AssistContent assistContent = data.getParcelable(ASSIST_KEY_CONTENT);
349 final SnapshotData snapshotData = new SnapshotData(assistData,
350 assistStructure, assistContent);
351 session.sendActivitySnapshotLocked(snapshotData);
352 return true;
353 } else {
354 Slog.e(TAG, "Failed to notify activity assist data for activity: " + activityToken);
355 }
356 return false;
357 }
358
359 @GuardedBy("mLock")
Felipe Leme749b8892018-12-03 16:30:30 -0800360 public void removeSessionLocked(@NonNull String sessionId) {
Felipe Lemea7bdb142018-11-05 16:29:29 -0800361 mSessions.remove(sessionId);
Felipe Lemee348dc32018-11-05 12:35:29 -0800362 }
363
Felipe Leme927fbf02018-11-12 17:27:23 -0800364 @GuardedBy("mLock")
Felipe Leme749b8892018-12-03 16:30:30 -0800365 public boolean isContentCaptureServiceForUserLocked(int uid) {
Felipe Leme927fbf02018-11-12 17:27:23 -0800366 return uid == getServiceUidLocked();
367 }
368
Felipe Leme284ad1c2018-11-15 18:16:12 -0800369 @GuardedBy("mLock")
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800370 private ContentCaptureServerSession getSession(@NonNull IBinder activityToken) {
Felipe Leme284ad1c2018-11-15 18:16:12 -0800371 for (int i = 0; i < mSessions.size(); i++) {
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800372 final ContentCaptureServerSession session = mSessions.valueAt(i);
Felipe Leme284ad1c2018-11-15 18:16:12 -0800373 if (session.mActivityToken.equals(activityToken)) {
374 return session;
375 }
376 }
377 return null;
378 }
379
Felipe Leme8e2e3412018-11-14 10:39:29 -0800380 /**
381 * Destroys the service and all state associated with it.
382 *
383 * <p>Called when the service was disabled (for example, if the settings change).
384 */
385 @GuardedBy("mLock")
386 public void destroyLocked() {
387 if (mMaster.debug) Slog.d(TAG, "destroyLocked()");
Felipe Leme5933efb2018-11-29 16:08:49 -0800388 destroySessionsLocked();
389 }
390
391 @GuardedBy("mLock")
392 void destroySessionsLocked() {
Felipe Leme8e2e3412018-11-14 10:39:29 -0800393 final int numSessions = mSessions.size();
394 for (int i = 0; i < numSessions; i++) {
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800395 final ContentCaptureServerSession session = mSessions.valueAt(i);
Felipe Lemee127c9a2019-01-04 14:56:38 -0800396 session.destroyLocked(/* notifyRemoteService= */ true);
Felipe Leme8e2e3412018-11-14 10:39:29 -0800397 }
398 mSessions.clear();
399 }
400
Felipe Leme5933efb2018-11-29 16:08:49 -0800401 @GuardedBy("mLock")
402 void listSessionsLocked(ArrayList<String> output) {
403 final int numSessions = mSessions.size();
404 for (int i = 0; i < numSessions; i++) {
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800405 final ContentCaptureServerSession session = mSessions.valueAt(i);
Felipe Leme5933efb2018-11-29 16:08:49 -0800406 output.add(session.toShortString());
407 }
408 }
409
Felipe Leme326f15a2019-02-19 09:42:24 -0800410 @GuardedBy("mLock")
411 ContentCaptureOptions getOptionsForPackageLocked(@NonNull String packageName) {
Adam He6240eab2019-02-25 13:34:45 -0800412 if (!mWhitelistHelper.isWhitelisted(packageName)) {
Felipe Leme326f15a2019-02-19 09:42:24 -0800413 if (mMaster.verbose) {
414 Slog.v(mTag, "getOptionsForPackage(" + packageName + "): not whitelisted");
415 }
416 return null;
417 }
418
Adam He6240eab2019-02-25 13:34:45 -0800419 final ArraySet<ComponentName> whitelistedComponents = mWhitelistHelper
420 .getWhitelistedComponents(packageName);
Felipe Leme326f15a2019-02-19 09:42:24 -0800421 ContentCaptureOptions options = new ContentCaptureOptions(mMaster.mDevCfgLoggingLevel,
422 mMaster.mDevCfgMaxBufferSize, mMaster.mDevCfgIdleFlushingFrequencyMs,
423 mMaster.mDevCfgTextChangeFlushingFrequencyMs, mMaster.mDevCfgLogHistorySize,
424 whitelistedComponents);
425 if (mMaster.verbose) {
426 Slog.v(mTag, "getOptionsForPackage(" + packageName + "): " + options);
427 }
428 return options;
429 }
430
Felipe Lemee348dc32018-11-05 12:35:29 -0800431 @Override
432 protected void dumpLocked(String prefix, PrintWriter pw) {
433 super.dumpLocked(prefix, pw);
Felipe Leme50b33dc2018-12-20 16:01:39 -0800434
435 final String prefix2 = prefix + " ";
436 if (mRemoteService != null) {
437 pw.print(prefix); pw.println("remote service:");
438 mRemoteService.dump(prefix2, pw);
439 }
440
Adam He6240eab2019-02-25 13:34:45 -0800441 pw.print(prefix); pw.print("Whitelist: "); pw.println(mWhitelistHelper);
Felipe Lemee8eae9d42019-02-13 15:41:55 -0800442
Felipe Lemee348dc32018-11-05 12:35:29 -0800443 if (mSessions.isEmpty()) {
444 pw.print(prefix); pw.println("no sessions");
445 } else {
Felipe Lemee8eae9d42019-02-13 15:41:55 -0800446 final int sessionsSize = mSessions.size();
447 pw.print(prefix); pw.print("number sessions: "); pw.println(sessionsSize);
448 for (int i = 0; i < sessionsSize; i++) {
449 pw.print(prefix); pw.print("#"); pw.println(i);
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800450 final ContentCaptureServerSession session = mSessions.valueAt(i);
Felipe Lemee348dc32018-11-05 12:35:29 -0800451 session.dumpLocked(prefix2, pw);
Felipe Lemee8eae9d42019-02-13 15:41:55 -0800452 pw.println();
Felipe Lemee348dc32018-11-05 12:35:29 -0800453 }
454 }
455 }
456
Winson Chungfbbb1582018-11-13 16:09:01 -0800457 /**
Felipe Leme749b8892018-12-03 16:30:30 -0800458 * Returns the session id associated with the given activity.
Winson Chungfbbb1582018-11-13 16:09:01 -0800459 */
460 @GuardedBy("mLock")
Felipe Leme749b8892018-12-03 16:30:30 -0800461 private String getSessionId(@NonNull IBinder activityToken) {
Winson Chungfbbb1582018-11-13 16:09:01 -0800462 for (int i = 0; i < mSessions.size(); i++) {
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800463 ContentCaptureServerSession session = mSessions.valueAt(i);
Winson Chungfbbb1582018-11-13 16:09:01 -0800464 if (session.isActivitySession(activityToken)) {
465 return mSessions.keyAt(i);
466 }
467 }
468 return null;
469 }
Felipe Lemeaf4e4fd2019-01-15 08:32:42 -0800470
471 private final class ContentCaptureServiceRemoteCallback extends
472 IContentCaptureServiceCallback.Stub {
473
474 @Override
475 public void setContentCaptureWhitelist(List<String> packages,
476 List<ComponentName> activities) {
477 if (mMaster.verbose) {
Felipe Lemee8eae9d42019-02-13 15:41:55 -0800478 Slog.v(TAG, "setContentCaptureWhitelist(packages=" + packages + ", activities="
Felipe Lemeaf4e4fd2019-01-15 08:32:42 -0800479 + activities + ")");
480 }
Adam He6240eab2019-02-25 13:34:45 -0800481 setWhitelist(packages, activities);
Felipe Lemee8eae9d42019-02-13 15:41:55 -0800482
Felipe Lemeaf4e4fd2019-01-15 08:32:42 -0800483 // TODO(b/119613670): log metrics
484 }
Felipe Leme72e83d82019-02-13 14:51:17 -0800485
486 @Override
487 public void disableSelf() {
488 if (mMaster.verbose) Slog.v(TAG, "disableSelf()");
489
490 final long token = Binder.clearCallingIdentity();
491 try {
492 Settings.Secure.putStringForUser(getContext().getContentResolver(),
493 Settings.Secure.CONTENT_CAPTURE_ENABLED, "false", mUserId);
494 } finally {
495 Binder.restoreCallingIdentity(token);
496 }
497 }
Felipe Lemeaf4e4fd2019-01-15 08:32:42 -0800498 }
Felipe Lemee348dc32018-11-05 12:35:29 -0800499}