blob: 583c5b593b8861fd4e4d3ce14795a9eaa213813c [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 Leme08054202019-03-28 11:29:25 -070020import static android.view.contentcapture.ContentCaptureSession.NO_SESSION_ID;
Felipe Leme01b87492019-01-15 13:26:52 -080021import static android.view.contentcapture.ContentCaptureSession.STATE_DISABLED;
22import static android.view.contentcapture.ContentCaptureSession.STATE_DUPLICATED_ID;
Adam Hebb82c6a2019-01-17 14:59:02 -080023import static android.view.contentcapture.ContentCaptureSession.STATE_INTERNAL_ERROR;
Adam He6240eab2019-02-25 13:34:45 -080024import static android.view.contentcapture.ContentCaptureSession.STATE_NOT_WHITELISTED;
Felipe Leme01b87492019-01-15 13:26:52 -080025import static android.view.contentcapture.ContentCaptureSession.STATE_NO_SERVICE;
Felipe Lemeb96878492018-12-17 12:22:29 -080026
Adam He420947c2019-01-23 15:59:09 -080027import static com.android.server.contentcapture.ContentCaptureMetricsLogger.writeServiceEvent;
28import static com.android.server.contentcapture.ContentCaptureMetricsLogger.writeSessionEvent;
29import static com.android.server.contentcapture.ContentCaptureMetricsLogger.writeSetWhitelistEvent;
Winson Chungfbbb1582018-11-13 16:09:01 -080030import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT;
31import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_DATA;
32import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
33
Felipe Lemee348dc32018-11-05 12:35:29 -080034import android.annotation.NonNull;
Adam He6240eab2019-02-25 13:34:45 -080035import android.annotation.Nullable;
Felipe Leme284ad1c2018-11-15 18:16:12 -080036import android.annotation.UserIdInt;
Adam He3d0409b2019-01-15 14:22:04 -080037import android.app.ActivityManagerInternal;
Winson Chungfbbb1582018-11-13 16:09:01 -080038import android.app.assist.AssistContent;
39import android.app.assist.AssistStructure;
Felipe Lemee348dc32018-11-05 12:35:29 -080040import android.content.ComponentName;
Adam He420947c2019-01-23 15:59:09 -080041import android.content.ContentCaptureOptions;
Adam Hebb82c6a2019-01-17 14:59:02 -080042import android.content.pm.ActivityPresentationInfo;
Felipe Lemee348dc32018-11-05 12:35:29 -080043import android.content.pm.PackageManager;
44import android.content.pm.PackageManager.NameNotFoundException;
45import android.content.pm.ServiceInfo;
Adam He3d0409b2019-01-15 14:22:04 -080046import android.os.Binder;
Winson Chungfbbb1582018-11-13 16:09:01 -080047import android.os.Bundle;
Felipe Lemee348dc32018-11-05 12:35:29 -080048import android.os.IBinder;
Adam He3d0409b2019-01-15 14:22:04 -080049import android.os.UserHandle;
Felipe Leme72e83d82019-02-13 14:51:17 -080050import android.provider.Settings;
Felipe Leme141864d2019-02-27 17:01:51 -080051import android.service.contentcapture.ActivityEvent;
52import android.service.contentcapture.ActivityEvent.ActivityEventType;
Felipe Leme50b33dc2018-12-20 16:01:39 -080053import android.service.contentcapture.ContentCaptureService;
Felipe Lemea5d5e2d2019-03-19 16:54:55 -070054import android.service.contentcapture.ContentCaptureServiceInfo;
Adam He420947c2019-01-23 15:59:09 -080055import android.service.contentcapture.FlushMetrics;
Felipe Lemeaf4e4fd2019-01-15 08:32:42 -080056import android.service.contentcapture.IContentCaptureServiceCallback;
Sergey Volnov6e049012020-01-10 18:10:48 +000057import android.service.contentcapture.IDataShareCallback;
Felipe Leme749b8892018-12-03 16:30:30 -080058import android.service.contentcapture.SnapshotData;
Felipe Lemea8d33c22019-03-25 16:36:09 -070059import android.util.ArrayMap;
60import android.util.ArraySet;
Felipe Lemee348dc32018-11-05 12:35:29 -080061import android.util.Slog;
Felipe Leme08054202019-03-28 11:29:25 -070062import android.util.SparseArray;
Felipe Leme0a3bec52019-05-06 15:57:30 -070063import android.util.SparseBooleanArray;
Felipe Lemea8d33c22019-03-25 16:36:09 -070064import android.view.contentcapture.ContentCaptureCondition;
Felipe Leme4439ee62019-05-08 10:27:49 -070065import android.view.contentcapture.DataRemovalRequest;
Sergey Volnov6e049012020-01-10 18:10:48 +000066import android.view.contentcapture.DataShareRequest;
Felipe Lemee348dc32018-11-05 12:35:29 -080067
68import com.android.internal.annotations.GuardedBy;
69import com.android.internal.os.IResultReceiver;
Muhammad Qureshie2b24322020-01-28 10:54:17 -080070import com.android.internal.util.FrameworkStatsLog;
Adam He3d0409b2019-01-15 14:22:04 -080071import com.android.server.LocalServices;
Felipe Leme50b33dc2018-12-20 16:01:39 -080072import com.android.server.contentcapture.RemoteContentCaptureService.ContentCaptureServiceCallbacks;
Felipe Leme1e3de572018-12-05 15:44:17 -080073import com.android.server.infra.AbstractPerUserSystemService;
Felipe Lemee348dc32018-11-05 12:35:29 -080074
75import java.io.PrintWriter;
Felipe Leme5933efb2018-11-29 16:08:49 -080076import java.util.ArrayList;
Felipe Lemeaf4e4fd2019-01-15 08:32:42 -080077import java.util.List;
Felipe Lemee348dc32018-11-05 12:35:29 -080078
79/**
Felipe Leme749b8892018-12-03 16:30:30 -080080 * Per-user instance of {@link ContentCaptureManagerService}.
Felipe Lemee348dc32018-11-05 12:35:29 -080081 */
Felipe Leme749b8892018-12-03 16:30:30 -080082final class ContentCapturePerUserService
83 extends
Felipe Leme50b33dc2018-12-20 16:01:39 -080084 AbstractPerUserSystemService<ContentCapturePerUserService, ContentCaptureManagerService>
85 implements ContentCaptureServiceCallbacks {
Felipe Lemee348dc32018-11-05 12:35:29 -080086
Felipe Leme2f288432019-03-21 13:57:45 -070087 private static final String TAG = ContentCapturePerUserService.class.getSimpleName();
Felipe Lemee348dc32018-11-05 12:35:29 -080088
Felipe Lemee348dc32018-11-05 12:35:29 -080089 @GuardedBy("mLock")
Felipe Leme08054202019-03-28 11:29:25 -070090 private final SparseArray<ContentCaptureServerSession> mSessions = new SparseArray<>();
Felipe Lemee348dc32018-11-05 12:35:29 -080091
Felipe Leme50b33dc2018-12-20 16:01:39 -080092 /**
93 * Reference to the remote service.
94 *
95 * <p>It's set in the constructor, but it's also updated when the service's updated in the
96 * master's cache (for example, because a temporary service was set).
97 */
98 @GuardedBy("mLock")
Felipe Leme2f288432019-03-21 13:57:45 -070099 @Nullable
100 RemoteContentCaptureService mRemoteService;
Felipe Leme50b33dc2018-12-20 16:01:39 -0800101
Felipe Lemeaf4e4fd2019-01-15 08:32:42 -0800102 private final ContentCaptureServiceRemoteCallback mRemoteServiceCallback =
103 new ContentCaptureServiceRemoteCallback();
104
Felipe Lemee8eae9d42019-02-13 15:41:55 -0800105 /**
Felipe Lemea8d33c22019-03-25 16:36:09 -0700106 * List of conditions keyed by package.
107 */
108 @GuardedBy("mLock")
109 private final ArrayMap<String, ArraySet<ContentCaptureCondition>> mConditionsByPkg =
110 new ArrayMap<>();
111
112 /**
Felipe Leme518fb622019-03-08 15:35:04 -0800113 * When {@code true}, remote service died but service state is kept so it's restored after
114 * the system re-binds to it.
115 */
116 @GuardedBy("mLock")
117 private boolean mZombie;
118
Felipe Lemea5d5e2d2019-03-19 16:54:55 -0700119 @GuardedBy("mLock")
120 private ContentCaptureServiceInfo mInfo;
121
Felipe Lemee348dc32018-11-05 12:35:29 -0800122 // TODO(b/111276913): add mechanism to prune stale sessions, similar to Autofill's
123
Felipe Leme50b33dc2018-12-20 16:01:39 -0800124 ContentCapturePerUserService(@NonNull ContentCaptureManagerService master,
125 @NonNull Object lock, boolean disabled, @UserIdInt int userId) {
Felipe Lemec28711c2018-12-20 11:17:02 -0800126 super(master, lock, userId);
Felipe Leme50b33dc2018-12-20 16:01:39 -0800127 updateRemoteServiceLocked(disabled);
128 }
129
130 /**
131 * Updates the reference to the remote service.
132 */
133 private void updateRemoteServiceLocked(boolean disabled) {
Felipe Leme41e9eb02019-04-17 13:57:59 -0700134 if (mMaster.verbose) Slog.v(TAG, "updateRemoteService(disabled=" + disabled + ")");
Felipe Leme50b33dc2018-12-20 16:01:39 -0800135 if (mRemoteService != null) {
136 if (mMaster.debug) Slog.d(TAG, "updateRemoteService(): destroying old remote service");
137 mRemoteService.destroy();
138 mRemoteService = null;
Adam He59e96e12019-03-25 15:49:58 -0700139 resetContentCaptureWhitelistLocked();
Felipe Leme50b33dc2018-12-20 16:01:39 -0800140 }
141
142 // Updates the component name
143 final ComponentName serviceComponentName = updateServiceInfoLocked();
144
145 if (serviceComponentName == null) {
Felipe Lemee127c9a2019-01-04 14:56:38 -0800146 if (mMaster.debug) Slog.d(TAG, "updateRemoteService(): no service component name");
Felipe Leme50b33dc2018-12-20 16:01:39 -0800147 return;
148 }
149
150 if (!disabled) {
Felipe Leme2f288432019-03-21 13:57:45 -0700151 if (mMaster.debug) {
152 Slog.d(TAG, "updateRemoteService(): creating new remote service for "
153 + serviceComponentName);
154 }
Felipe Lemeaf4e4fd2019-01-15 08:32:42 -0800155 mRemoteService = new RemoteContentCaptureService(mMaster.getContext(),
156 ContentCaptureService.SERVICE_INTERFACE, serviceComponentName,
157 mRemoteServiceCallback, mUserId, this, mMaster.isBindInstantServiceAllowed(),
Felipe Lemee764fa22019-02-21 16:45:35 -0800158 mMaster.verbose, mMaster.mDevCfgIdleUnbindTimeoutMs);
Felipe Leme50b33dc2018-12-20 16:01:39 -0800159 }
Felipe Lemee348dc32018-11-05 12:35:29 -0800160 }
161
162 @Override // from PerUserSystemService
Felipe Leme50b33dc2018-12-20 16:01:39 -0800163 protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent)
Felipe Lemee348dc32018-11-05 12:35:29 -0800164 throws NameNotFoundException {
Felipe Lemea5d5e2d2019-03-19 16:54:55 -0700165 mInfo = new ContentCaptureServiceInfo(getContext(), serviceComponent,
166 isTemporaryServiceSetLocked(), mUserId);
167 return mInfo.getServiceInfo();
Felipe Lemee348dc32018-11-05 12:35:29 -0800168 }
169
Felipe Lemeecb08be2018-11-27 15:48:47 -0800170 @Override // from PerUserSystemService
171 @GuardedBy("mLock")
172 protected boolean updateLocked(boolean disabled) {
Felipe Leme50b33dc2018-12-20 16:01:39 -0800173 final boolean disabledStateChanged = super.updateLocked(disabled);
Adam He6c0afca2019-04-15 13:54:11 -0700174 if (disabledStateChanged) {
175 // update session content capture enabled state.
176 for (int i = 0; i < mSessions.size(); i++) {
177 mSessions.valueAt(i).setContentCaptureEnabledLocked(!disabled);
178 }
179 }
180 destroyLocked();
Felipe Leme50b33dc2018-12-20 16:01:39 -0800181 updateRemoteServiceLocked(disabled);
182 return disabledStateChanged;
Felipe Lemeecb08be2018-11-27 15:48:47 -0800183 }
184
Felipe Leme50b33dc2018-12-20 16:01:39 -0800185 @Override // from ContentCaptureServiceCallbacks
186 public void onServiceDied(@NonNull RemoteContentCaptureService service) {
Felipe Leme817b5e12019-03-07 14:32:56 -0800187 // Don't do anything; eventually the system will bind to it again...
188 Slog.w(TAG, "remote service died: " + service);
Felipe Leme518fb622019-03-08 15:35:04 -0800189 synchronized (mLock) {
190 mZombie = true;
191 }
192 }
193
194 /**
195 * Called after the remote service connected, it's used to restore state from a 'zombie'
196 * service (i.e., after it died).
197 */
198 void onConnected() {
199 synchronized (mLock) {
200 if (mZombie) {
201 // Sanity check - shouldn't happen
202 if (mRemoteService == null) {
203 Slog.w(TAG, "Cannot ressurect sessions because remote service is null");
204 return;
205 }
206
207 mZombie = false;
Felipe Leme2f288432019-03-21 13:57:45 -0700208 resurrectSessionsLocked();
Felipe Leme518fb622019-03-08 15:35:04 -0800209 }
210 }
Felipe Leme50b33dc2018-12-20 16:01:39 -0800211 }
212
Felipe Leme2f288432019-03-21 13:57:45 -0700213 private void resurrectSessionsLocked() {
214 final int numSessions = mSessions.size();
215 if (mMaster.debug) {
216 Slog.d(TAG, "Ressurrecting remote service (" + mRemoteService + ") on "
217 + numSessions + " sessions");
218 }
219
220 for (int i = 0; i < numSessions; i++) {
221 final ContentCaptureServerSession session = mSessions.valueAt(i);
222 session.resurrectLocked();
223 }
224 }
225
226 void onPackageUpdatingLocked() {
227 final int numSessions = mSessions.size();
228 if (mMaster.debug) {
229 Slog.d(TAG, "Pausing " + numSessions + " sessions while package is updating");
230 }
231 for (int i = 0; i < numSessions; i++) {
232 final ContentCaptureServerSession session = mSessions.valueAt(i);
233 session.pauseLocked();
234 }
235 }
236
237 void onPackageUpdatedLocked() {
238 updateRemoteServiceLocked(!isEnabledLocked());
239 resurrectSessionsLocked();
240 }
241
Felipe Lemee348dc32018-11-05 12:35:29 -0800242 @GuardedBy("mLock")
243 public void startSessionLocked(@NonNull IBinder activityToken,
Felipe Leme08054202019-03-28 11:29:25 -0700244 @NonNull ActivityPresentationInfo activityPresentationInfo, int sessionId, int uid,
245 int flags, @NonNull IResultReceiver clientReceiver) {
Adam Hebb82c6a2019-01-17 14:59:02 -0800246 if (activityPresentationInfo == null) {
247 Slog.w(TAG, "basic activity info is null");
248 setClientState(clientReceiver, STATE_DISABLED | STATE_INTERNAL_ERROR,
249 /* binder= */ null);
250 return;
251 }
252 final int taskId = activityPresentationInfo.taskId;
253 final int displayId = activityPresentationInfo.displayId;
254 final ComponentName componentName = activityPresentationInfo.componentName;
Felipe Leme9bee9442019-03-27 13:40:40 -0700255 final boolean whiteListed = mMaster.mGlobalContentCaptureOptions.isWhitelisted(mUserId,
Felipe Leme41e9eb02019-04-17 13:57:59 -0700256 componentName) || mMaster.mGlobalContentCaptureOptions.isWhitelisted(mUserId,
257 componentName.getPackageName());
Felipe Leme930f3242019-01-15 16:47:09 -0800258 final ComponentName serviceComponentName = getServiceComponentName();
259 final boolean enabled = isEnabledLocked();
Felipe Lemed32d8f6f2019-02-15 10:25:33 -0800260 if (mMaster.mRequestsHistory != null) {
261 final String historyItem =
262 "id=" + sessionId + " uid=" + uid
263 + " a=" + ComponentName.flattenToShortString(componentName)
264 + " t=" + taskId + " d=" + displayId
265 + " s=" + ComponentName.flattenToShortString(serviceComponentName)
Felipe Lemee8eae9d42019-02-13 15:41:55 -0800266 + " u=" + mUserId + " f=" + flags + (enabled ? "" : " (disabled)")
Adam He6240eab2019-02-25 13:34:45 -0800267 + " w=" + whiteListed;
Felipe Lemed32d8f6f2019-02-15 10:25:33 -0800268 mMaster.mRequestsHistory.log(historyItem);
269 }
Felipe Leme930f3242019-01-15 16:47:09 -0800270
271 if (!enabled) {
Felipe Lemee127c9a2019-01-04 14:56:38 -0800272 // TODO: it would be better to split in differet reasons, like
Adam He420947c2019-01-23 15:59:09 -0800273 // STATE_DISABLED_NO and STATE_DISABLED_BY_DEVICE_POLICY
Felipe Leme01b87492019-01-15 13:26:52 -0800274 setClientState(clientReceiver, STATE_DISABLED | STATE_NO_SERVICE,
Felipe Lemee127c9a2019-01-04 14:56:38 -0800275 /* binder= */ null);
Adam He420947c2019-01-23 15:59:09 -0800276 // Log metrics.
277 writeSessionEvent(sessionId,
Muhammad Qureshie2b24322020-01-28 10:54:17 -0800278 FrameworkStatsLog.CONTENT_CAPTURE_SESSION_EVENTS__EVENT__SESSION_NOT_CREATED,
Adam He420947c2019-01-23 15:59:09 -0800279 STATE_DISABLED | STATE_NO_SERVICE, serviceComponentName,
280 componentName, /* isChildSession= */ false);
Felipe Leme8e2e3412018-11-14 10:39:29 -0800281 return;
282 }
Felipe Lemee348dc32018-11-05 12:35:29 -0800283 if (serviceComponentName == null) {
284 // TODO(b/111276913): this happens when the system service is starting, we should
285 // probably handle it in a more elegant way (like waiting for boot_complete or
286 // something like that
Felipe Leme8e2e3412018-11-14 10:39:29 -0800287 if (mMaster.debug) {
288 Slog.d(TAG, "startSession(" + activityToken + "): hold your horses");
289 }
Felipe Lemee348dc32018-11-05 12:35:29 -0800290 return;
291 }
292
Adam He6240eab2019-02-25 13:34:45 -0800293 if (!whiteListed) {
Felipe Lemee8eae9d42019-02-13 15:41:55 -0800294 if (mMaster.debug) {
Adam He6240eab2019-02-25 13:34:45 -0800295 Slog.d(TAG, "startSession(" + componentName + "): package or component "
296 + "not whitelisted");
Felipe Lemee8eae9d42019-02-13 15:41:55 -0800297 }
Adam He6240eab2019-02-25 13:34:45 -0800298 setClientState(clientReceiver, STATE_DISABLED | STATE_NOT_WHITELISTED,
Felipe Lemee8eae9d42019-02-13 15:41:55 -0800299 /* binder= */ null);
Adam He420947c2019-01-23 15:59:09 -0800300 // Log metrics.
301 writeSessionEvent(sessionId,
Muhammad Qureshie2b24322020-01-28 10:54:17 -0800302 FrameworkStatsLog.CONTENT_CAPTURE_SESSION_EVENTS__EVENT__SESSION_NOT_CREATED,
Adam He420947c2019-01-23 15:59:09 -0800303 STATE_DISABLED | STATE_NOT_WHITELISTED, serviceComponentName,
304 componentName, /* isChildSession= */ false);
Felipe Lemee8eae9d42019-02-13 15:41:55 -0800305 return;
306 }
307
Felipe Lemeb96878492018-12-17 12:22:29 -0800308 final ContentCaptureServerSession existingSession = mSessions.get(sessionId);
309 if (existingSession != null) {
310 Slog.w(TAG, "startSession(id=" + existingSession + ", token=" + activityToken
311 + ": ignoring because it already exists for " + existingSession.mActivityToken);
Felipe Leme01b87492019-01-15 13:26:52 -0800312 setClientState(clientReceiver, STATE_DISABLED | STATE_DUPLICATED_ID,
Felipe Lemeb96878492018-12-17 12:22:29 -0800313 /* binder=*/ null);
Adam He420947c2019-01-23 15:59:09 -0800314 // Log metrics.
315 writeSessionEvent(sessionId,
Muhammad Qureshie2b24322020-01-28 10:54:17 -0800316 FrameworkStatsLog.CONTENT_CAPTURE_SESSION_EVENTS__EVENT__SESSION_NOT_CREATED,
Adam He420947c2019-01-23 15:59:09 -0800317 STATE_DISABLED | STATE_DUPLICATED_ID,
318 serviceComponentName, componentName, /* isChildSession= */ false);
Felipe Lemee348dc32018-11-05 12:35:29 -0800319 return;
320 }
321
Felipe Leme50b33dc2018-12-20 16:01:39 -0800322 if (mRemoteService == null) {
323 updateRemoteServiceLocked(/* disabled= */ false); // already checked for isEnabled
324 }
325
326 if (mRemoteService == null) {
Felipe Leme50b33dc2018-12-20 16:01:39 -0800327 Slog.w(TAG, "startSession(id=" + existingSession + ", token=" + activityToken
328 + ": ignoring because service is not set");
Felipe Leme01b87492019-01-15 13:26:52 -0800329 setClientState(clientReceiver, STATE_DISABLED | STATE_NO_SERVICE,
Felipe Lemee127c9a2019-01-04 14:56:38 -0800330 /* binder= */ null);
Adam He420947c2019-01-23 15:59:09 -0800331 // Log metrics.
332 writeSessionEvent(sessionId,
Muhammad Qureshie2b24322020-01-28 10:54:17 -0800333 FrameworkStatsLog.CONTENT_CAPTURE_SESSION_EVENTS__EVENT__SESSION_NOT_CREATED,
Adam He420947c2019-01-23 15:59:09 -0800334 STATE_DISABLED | STATE_NO_SERVICE, serviceComponentName,
335 componentName, /* isChildSession= */ false);
Felipe Leme50b33dc2018-12-20 16:01:39 -0800336 return;
337 }
338
Felipe Leme2f288432019-03-21 13:57:45 -0700339 // Make sure service is bound, just in case the initial connection failed somehow
340 mRemoteService.ensureBoundLocked();
341
Felipe Leme63341162019-04-16 14:03:56 -0700342 final ContentCaptureServerSession newSession = new ContentCaptureServerSession(mLock,
Felipe Leme2f288432019-03-21 13:57:45 -0700343 activityToken, this, componentName, clientReceiver, taskId, displayId, sessionId,
344 uid, flags);
Felipe Lemee348dc32018-11-05 12:35:29 -0800345 if (mMaster.verbose) {
Felipe Lemeb96878492018-12-17 12:22:29 -0800346 Slog.v(TAG, "startSession(): new session for "
347 + ComponentName.flattenToShortString(componentName) + " and id " + sessionId);
Felipe Lemee348dc32018-11-05 12:35:29 -0800348 }
Felipe Lemeb96878492018-12-17 12:22:29 -0800349 mSessions.put(sessionId, newSession);
350 newSession.notifySessionStartedLocked(clientReceiver);
Felipe Lemee348dc32018-11-05 12:35:29 -0800351 }
352
Felipe Lemee348dc32018-11-05 12:35:29 -0800353 @GuardedBy("mLock")
Felipe Leme08054202019-03-28 11:29:25 -0700354 public void finishSessionLocked(int sessionId) {
Felipe Leme8e2e3412018-11-14 10:39:29 -0800355 if (!isEnabledLocked()) {
356 return;
357 }
358
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800359 final ContentCaptureServerSession session = mSessions.get(sessionId);
Felipe Lemee348dc32018-11-05 12:35:29 -0800360 if (session == null) {
Felipe Leme8e2e3412018-11-14 10:39:29 -0800361 if (mMaster.debug) {
362 Slog.d(TAG, "finishSession(): no session with id" + sessionId);
363 }
Felipe Lemee348dc32018-11-05 12:35:29 -0800364 return;
365 }
Felipe Lemeb96878492018-12-17 12:22:29 -0800366 if (mMaster.verbose) Slog.v(TAG, "finishSession(): id=" + sessionId);
367 session.removeSelfLocked(/* notifyRemoteService= */ true);
Felipe Leme7a534082018-11-05 15:03:04 -0800368 }
369
370 @GuardedBy("mLock")
Felipe Leme4439ee62019-05-08 10:27:49 -0700371 public void removeDataLocked(@NonNull DataRemovalRequest request) {
Adam He3d0409b2019-01-15 14:22:04 -0800372 if (!isEnabledLocked()) {
373 return;
374 }
375 assertCallerLocked(request.getPackageName());
Felipe Leme4439ee62019-05-08 10:27:49 -0700376 mRemoteService.onDataRemovalRequest(request);
Adam He3d0409b2019-01-15 14:22:04 -0800377 }
378
Felipe Lemef8b87782019-03-20 14:31:18 -0700379 @GuardedBy("mLock")
Sergey Volnov6e049012020-01-10 18:10:48 +0000380 public void onDataSharedLocked(@NonNull DataShareRequest request,
381 IDataShareCallback.Stub dataShareCallback) {
382 if (!isEnabledLocked()) {
383 return;
384 }
385 assertCallerLocked(request.getPackageName());
386 mRemoteService.onDataShareRequest(request, dataShareCallback);
387 }
388
389 @GuardedBy("mLock")
Felipe Lemef8b87782019-03-20 14:31:18 -0700390 @Nullable
391 public ComponentName getServiceSettingsActivityLocked() {
392 if (mInfo == null) return null;
393
394 final String activityName = mInfo.getSettingsActivity();
395 if (activityName == null) return null;
396
397 final String packageName = mInfo.getServiceInfo().packageName;
398 return new ComponentName(packageName, activityName);
399 }
400
Adam He3d0409b2019-01-15 14:22:04 -0800401 /**
402 * Asserts the component is owned by the caller.
403 */
404 @GuardedBy("mLock")
405 private void assertCallerLocked(@NonNull String packageName) {
406 final PackageManager pm = getContext().getPackageManager();
407 final int callingUid = Binder.getCallingUid();
408 final int packageUid;
409 try {
410 packageUid = pm.getPackageUidAsUser(packageName, UserHandle.getCallingUserId());
411 } catch (NameNotFoundException e) {
412 throw new SecurityException("Could not verify UID for " + packageName);
413 }
414 if (callingUid != packageUid && !LocalServices.getService(ActivityManagerInternal.class)
415 .hasRunningActivity(callingUid, packageName)) {
416 final String[] packages = pm.getPackagesForUid(callingUid);
417 final String callingPackage = packages != null ? packages[0] : "uid-" + callingUid;
418 Slog.w(TAG, "App (package=" + callingPackage + ", UID=" + callingUid
419 + ") passed package (" + packageName + ") owned by UID " + packageUid);
420
421 throw new SecurityException("Invalid package: " + packageName);
422 }
423 }
424
425 @GuardedBy("mLock")
Winson Chungfbbb1582018-11-13 16:09:01 -0800426 public boolean sendActivityAssistDataLocked(@NonNull IBinder activityToken,
427 @NonNull Bundle data) {
Felipe Leme08054202019-03-28 11:29:25 -0700428 final int id = getSessionId(activityToken);
429 if (id != NO_SESSION_ID) {
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800430 final ContentCaptureServerSession session = mSessions.get(id);
Winson Chungfbbb1582018-11-13 16:09:01 -0800431 final Bundle assistData = data.getBundle(ASSIST_KEY_DATA);
432 final AssistStructure assistStructure = data.getParcelable(ASSIST_KEY_STRUCTURE);
433 final AssistContent assistContent = data.getParcelable(ASSIST_KEY_CONTENT);
434 final SnapshotData snapshotData = new SnapshotData(assistData,
435 assistStructure, assistContent);
436 session.sendActivitySnapshotLocked(snapshotData);
437 return true;
438 } else {
439 Slog.e(TAG, "Failed to notify activity assist data for activity: " + activityToken);
440 }
441 return false;
442 }
443
444 @GuardedBy("mLock")
Felipe Leme08054202019-03-28 11:29:25 -0700445 public void removeSessionLocked(int sessionId) {
Felipe Lemea7bdb142018-11-05 16:29:29 -0800446 mSessions.remove(sessionId);
Felipe Lemee348dc32018-11-05 12:35:29 -0800447 }
448
Felipe Leme927fbf02018-11-12 17:27:23 -0800449 @GuardedBy("mLock")
Felipe Leme749b8892018-12-03 16:30:30 -0800450 public boolean isContentCaptureServiceForUserLocked(int uid) {
Felipe Leme927fbf02018-11-12 17:27:23 -0800451 return uid == getServiceUidLocked();
452 }
453
Felipe Leme284ad1c2018-11-15 18:16:12 -0800454 @GuardedBy("mLock")
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800455 private ContentCaptureServerSession getSession(@NonNull IBinder activityToken) {
Felipe Leme284ad1c2018-11-15 18:16:12 -0800456 for (int i = 0; i < mSessions.size(); i++) {
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800457 final ContentCaptureServerSession session = mSessions.valueAt(i);
Felipe Leme284ad1c2018-11-15 18:16:12 -0800458 if (session.mActivityToken.equals(activityToken)) {
459 return session;
460 }
461 }
462 return null;
463 }
464
Felipe Leme8e2e3412018-11-14 10:39:29 -0800465 /**
466 * Destroys the service and all state associated with it.
467 *
468 * <p>Called when the service was disabled (for example, if the settings change).
469 */
470 @GuardedBy("mLock")
471 public void destroyLocked() {
472 if (mMaster.debug) Slog.d(TAG, "destroyLocked()");
Adam Hed2954722019-03-08 11:28:52 -0800473 if (mRemoteService != null) {
474 mRemoteService.destroy();
475 }
Felipe Leme5933efb2018-11-29 16:08:49 -0800476 destroySessionsLocked();
477 }
478
479 @GuardedBy("mLock")
480 void destroySessionsLocked() {
Felipe Leme8e2e3412018-11-14 10:39:29 -0800481 final int numSessions = mSessions.size();
482 for (int i = 0; i < numSessions; i++) {
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800483 final ContentCaptureServerSession session = mSessions.valueAt(i);
Felipe Lemee127c9a2019-01-04 14:56:38 -0800484 session.destroyLocked(/* notifyRemoteService= */ true);
Felipe Leme8e2e3412018-11-14 10:39:29 -0800485 }
486 mSessions.clear();
487 }
488
Felipe Leme5933efb2018-11-29 16:08:49 -0800489 @GuardedBy("mLock")
490 void listSessionsLocked(ArrayList<String> output) {
491 final int numSessions = mSessions.size();
492 for (int i = 0; i < numSessions; i++) {
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800493 final ContentCaptureServerSession session = mSessions.valueAt(i);
Felipe Leme5933efb2018-11-29 16:08:49 -0800494 output.add(session.toShortString());
495 }
496 }
497
Felipe Leme326f15a2019-02-19 09:42:24 -0800498 @GuardedBy("mLock")
Felipe Lemea8d33c22019-03-25 16:36:09 -0700499 @Nullable
Felipe Lemea8d33c22019-03-25 16:36:09 -0700500 ArraySet<ContentCaptureCondition> getContentCaptureConditionsLocked(
501 @NonNull String packageName) {
502 return mConditionsByPkg.get(packageName);
503 }
504
505 @GuardedBy("mLock")
Felipe Leme141864d2019-02-27 17:01:51 -0800506 void onActivityEventLocked(@NonNull ComponentName componentName, @ActivityEventType int type) {
507 if (mRemoteService == null) {
508 if (mMaster.debug) Slog.d(mTag, "onActivityEvent(): no remote service");
509 return;
510 }
511 final ActivityEvent event = new ActivityEvent(componentName, type);
512
513 if (mMaster.verbose) Slog.v(mTag, "onActivityEvent(): " + event);
514
515 mRemoteService.onActivityLifecycleEvent(event);
516 }
517
Felipe Lemee348dc32018-11-05 12:35:29 -0800518 @Override
519 protected void dumpLocked(String prefix, PrintWriter pw) {
520 super.dumpLocked(prefix, pw);
Felipe Leme50b33dc2018-12-20 16:01:39 -0800521
Felipe Lemea5d5e2d2019-03-19 16:54:55 -0700522 final String prefix2 = prefix + " ";
523 pw.print(prefix); pw.print("Service Info: ");
524 if (mInfo == null) {
525 pw.println("N/A");
526 } else {
527 pw.println();
528 mInfo.dump(prefix2, pw);
529 }
Felipe Leme518fb622019-03-08 15:35:04 -0800530 pw.print(prefix); pw.print("Zombie: "); pw.println(mZombie);
531
Felipe Leme50b33dc2018-12-20 16:01:39 -0800532 if (mRemoteService != null) {
533 pw.print(prefix); pw.println("remote service:");
534 mRemoteService.dump(prefix2, pw);
535 }
536
Felipe Leme08054202019-03-28 11:29:25 -0700537 if (mSessions.size() == 0) {
Felipe Lemee348dc32018-11-05 12:35:29 -0800538 pw.print(prefix); pw.println("no sessions");
539 } else {
Felipe Lemee8eae9d42019-02-13 15:41:55 -0800540 final int sessionsSize = mSessions.size();
541 pw.print(prefix); pw.print("number sessions: "); pw.println(sessionsSize);
542 for (int i = 0; i < sessionsSize; i++) {
543 pw.print(prefix); pw.print("#"); pw.println(i);
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800544 final ContentCaptureServerSession session = mSessions.valueAt(i);
Felipe Lemee348dc32018-11-05 12:35:29 -0800545 session.dumpLocked(prefix2, pw);
Felipe Lemee8eae9d42019-02-13 15:41:55 -0800546 pw.println();
Felipe Lemee348dc32018-11-05 12:35:29 -0800547 }
548 }
549 }
550
Winson Chungfbbb1582018-11-13 16:09:01 -0800551 /**
Felipe Leme749b8892018-12-03 16:30:30 -0800552 * Returns the session id associated with the given activity.
Winson Chungfbbb1582018-11-13 16:09:01 -0800553 */
554 @GuardedBy("mLock")
Felipe Leme08054202019-03-28 11:29:25 -0700555 private int getSessionId(@NonNull IBinder activityToken) {
Winson Chungfbbb1582018-11-13 16:09:01 -0800556 for (int i = 0; i < mSessions.size(); i++) {
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800557 ContentCaptureServerSession session = mSessions.valueAt(i);
Winson Chungfbbb1582018-11-13 16:09:01 -0800558 if (session.isActivitySession(activityToken)) {
559 return mSessions.keyAt(i);
560 }
561 }
Felipe Leme08054202019-03-28 11:29:25 -0700562 return NO_SESSION_ID;
Winson Chungfbbb1582018-11-13 16:09:01 -0800563 }
Felipe Lemeaf4e4fd2019-01-15 08:32:42 -0800564
Adam He59e96e12019-03-25 15:49:58 -0700565 /**
566 * Resets the content capture whitelist.
567 */
568 @GuardedBy("mLock")
569 private void resetContentCaptureWhitelistLocked() {
570 if (mMaster.verbose) {
571 Slog.v(TAG, "resetting content capture whitelist");
572 }
Felipe Leme9bee9442019-03-27 13:40:40 -0700573 mMaster.mGlobalContentCaptureOptions.resetWhitelist(mUserId);
Adam He59e96e12019-03-25 15:49:58 -0700574 }
575
Felipe Lemeaf4e4fd2019-01-15 08:32:42 -0800576 private final class ContentCaptureServiceRemoteCallback extends
577 IContentCaptureServiceCallback.Stub {
578
579 @Override
580 public void setContentCaptureWhitelist(List<String> packages,
581 List<ComponentName> activities) {
Felipe Leme2f288432019-03-21 13:57:45 -0700582 // TODO(b/122595322): add CTS test for when it's null
Felipe Lemeaf4e4fd2019-01-15 08:32:42 -0800583 if (mMaster.verbose) {
Felipe Leme2f288432019-03-21 13:57:45 -0700584 Slog.v(TAG, "setContentCaptureWhitelist(" + (packages == null
585 ? "null_packages" : packages.size() + " packages")
586 + ", " + (activities == null
Adam He6c0afca2019-04-15 13:54:11 -0700587 ? "null_activities" : activities.size() + " activities") + ")"
588 + " for user " + mUserId);
Felipe Lemeaf4e4fd2019-01-15 08:32:42 -0800589 }
Felipe Leme9bee9442019-03-27 13:40:40 -0700590 mMaster.mGlobalContentCaptureOptions.setWhitelist(mUserId, packages, activities);
Adam He420947c2019-01-23 15:59:09 -0800591 writeSetWhitelistEvent(getServiceComponentName(), packages, activities);
Felipe Leme0a3bec52019-05-06 15:57:30 -0700592
593 // Must disable session that are not the whitelist anymore...
594 final int numSessions = mSessions.size();
595 if (numSessions <= 0) return;
596
597 // ...but without holding the lock on mGlobalContentCaptureOptions
598 final SparseBooleanArray blacklistedSessions = new SparseBooleanArray(numSessions);
599
600 for (int i = 0; i < numSessions; i++) {
601 final ContentCaptureServerSession session = mSessions.valueAt(i);
602 final boolean whitelisted = mMaster.mGlobalContentCaptureOptions
603 .isWhitelisted(mUserId, session.appComponentName);
604 if (!whitelisted) {
605 final int sessionId = mSessions.keyAt(i);
606 if (mMaster.debug) {
607 Slog.d(TAG, "marking session " + sessionId + " (" + session.appComponentName
608 + ") for un-whitelisting");
609 }
610 blacklistedSessions.append(sessionId, true);
611 }
612 }
613 final int numBlacklisted = blacklistedSessions.size();
614
615 if (numBlacklisted <= 0) return;
616
617 synchronized (mLock) {
618 for (int i = 0; i < numBlacklisted; i++) {
619 final int sessionId = blacklistedSessions.keyAt(i);
620 if (mMaster.debug) Slog.d(TAG, "un-whitelisting " + sessionId);
621 final ContentCaptureServerSession session = mSessions.get(sessionId);
622 session.setContentCaptureEnabledLocked(false);
623 }
624 }
Felipe Lemeaf4e4fd2019-01-15 08:32:42 -0800625 }
Felipe Leme72e83d82019-02-13 14:51:17 -0800626
627 @Override
Felipe Lemea8d33c22019-03-25 16:36:09 -0700628 public void setContentCaptureConditions(String packageName,
629 List<ContentCaptureCondition> conditions) {
630 if (mMaster.verbose) {
631 Slog.v(TAG, "setContentCaptureConditions(" + packageName + "): "
632 + (conditions == null ? "null" : conditions.size() + " conditions"));
633 }
634 synchronized (mLock) {
635 if (conditions == null) {
636 mConditionsByPkg.remove(packageName);
637 } else {
638 mConditionsByPkg.put(packageName, new ArraySet<>(conditions));
639 }
640 }
Felipe Lemea8d33c22019-03-25 16:36:09 -0700641 }
642
643 @Override
Felipe Leme72e83d82019-02-13 14:51:17 -0800644 public void disableSelf() {
645 if (mMaster.verbose) Slog.v(TAG, "disableSelf()");
646
647 final long token = Binder.clearCallingIdentity();
648 try {
649 Settings.Secure.putStringForUser(getContext().getContentResolver(),
Felipe Leme8db00362019-03-06 15:38:19 -0800650 Settings.Secure.CONTENT_CAPTURE_ENABLED, "0", mUserId);
Felipe Leme72e83d82019-02-13 14:51:17 -0800651 } finally {
652 Binder.restoreCallingIdentity(token);
653 }
Muhammad Qureshie2b24322020-01-28 10:54:17 -0800654 writeServiceEvent(FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__SET_DISABLED,
Adam He420947c2019-01-23 15:59:09 -0800655 getServiceComponentName());
656 }
657
658 @Override
659 public void writeSessionFlush(int sessionId, ComponentName app, FlushMetrics flushMetrics,
660 ContentCaptureOptions options, int flushReason) {
661 ContentCaptureMetricsLogger.writeSessionFlush(sessionId, getServiceComponentName(), app,
662 flushMetrics, options, flushReason);
Felipe Leme72e83d82019-02-13 14:51:17 -0800663 }
Felipe Lemeaf4e4fd2019-01-15 08:32:42 -0800664 }
Felipe Lemee348dc32018-11-05 12:35:29 -0800665}