blob: f83090c6242dd80e4e4917cb3206a103975bad8c [file] [log] [blame]
Felipe Leme749b8892018-12-03 16:30:30 -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 */
16package android.service.contentcapture;
17
Felipe Leme326f15a2019-02-19 09:42:24 -080018import static android.view.contentcapture.ContentCaptureHelper.sDebug;
19import static android.view.contentcapture.ContentCaptureHelper.sVerbose;
Felipe Leme08054202019-03-28 11:29:25 -070020import static android.view.contentcapture.ContentCaptureSession.NO_SESSION_ID;
Felipe Leme326f15a2019-02-19 09:42:24 -080021
Felipe Leme749b8892018-12-03 16:30:30 -080022import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
23
24import android.annotation.CallSuper;
25import android.annotation.NonNull;
26import android.annotation.Nullable;
27import android.annotation.SystemApi;
Felipe Leme19652c02019-02-04 13:01:29 -080028import android.annotation.TestApi;
Felipe Leme749b8892018-12-03 16:30:30 -080029import android.app.Service;
30import android.content.ComponentName;
31import android.content.Intent;
Felipe Lemeb96878492018-12-17 12:22:29 -080032import android.content.pm.ParceledListSlice;
33import android.os.Binder;
34import android.os.Bundle;
Felipe Leme749b8892018-12-03 16:30:30 -080035import android.os.Handler;
36import android.os.IBinder;
37import android.os.Looper;
38import android.os.RemoteException;
Felipe Lemebb98ed62018-12-21 09:29:27 -080039import android.service.autofill.AutofillService;
Felipe Leme749b8892018-12-03 16:30:30 -080040import android.util.Log;
Felipe Lemeb96878492018-12-17 12:22:29 -080041import android.util.Slog;
Felipe Leme08054202019-03-28 11:29:25 -070042import android.util.SparseIntArray;
Felipe Leme790be042019-03-25 09:52:19 -070043import android.view.contentcapture.ContentCaptureCondition;
Felipe Lemeaa5088e2018-12-10 14:53:58 -080044import android.view.contentcapture.ContentCaptureContext;
Felipe Leme749b8892018-12-03 16:30:30 -080045import android.view.contentcapture.ContentCaptureEvent;
Felipe Lemeb96878492018-12-17 12:22:29 -080046import android.view.contentcapture.ContentCaptureManager;
47import android.view.contentcapture.ContentCaptureSession;
Felipe Lemeaa5088e2018-12-10 14:53:58 -080048import android.view.contentcapture.ContentCaptureSessionId;
Felipe Lemeb96878492018-12-17 12:22:29 -080049import android.view.contentcapture.IContentCaptureDirectManager;
Felipe Leme87a9dc92018-12-18 14:28:07 -080050import android.view.contentcapture.MainContentCaptureSession;
Felipe Leme62e45b02019-01-10 10:22:00 -080051import android.view.contentcapture.UserDataRemovalRequest;
Felipe Leme749b8892018-12-03 16:30:30 -080052
Felipe Lemeb96878492018-12-17 12:22:29 -080053import com.android.internal.os.IResultReceiver;
54
55import java.io.FileDescriptor;
56import java.io.PrintWriter;
Felipe Leme7a3c9f52019-02-13 16:32:49 -080057import java.util.ArrayList;
Felipe Leme749b8892018-12-03 16:30:30 -080058import java.util.List;
Felipe Leme7a3c9f52019-02-13 16:32:49 -080059import java.util.Set;
Felipe Leme749b8892018-12-03 16:30:30 -080060
61/**
62 * A service used to capture the content of the screen to provide contextual data in other areas of
63 * the system such as Autofill.
64 *
65 * @hide
66 */
67@SystemApi
Felipe Leme19652c02019-02-04 13:01:29 -080068@TestApi
Felipe Leme749b8892018-12-03 16:30:30 -080069public abstract class ContentCaptureService extends Service {
70
71 private static final String TAG = ContentCaptureService.class.getSimpleName();
72
Felipe Leme749b8892018-12-03 16:30:30 -080073 /**
74 * The {@link Intent} that must be declared as handled by the service.
75 *
76 * <p>To be supported, the service must also require the
77 * {@link android.Manifest.permission#BIND_CONTENT_CAPTURE_SERVICE} permission so
78 * that other applications can not abuse it.
79 */
80 public static final String SERVICE_INTERFACE =
81 "android.service.contentcapture.ContentCaptureService";
82
Felipe Lemea5d5e2d2019-03-19 16:54:55 -070083 /**
84 * Name under which a ContentCaptureService component publishes information about itself.
85 *
86 * <p>This meta-data should reference an XML resource containing a
87 * <code>&lt;{@link
88 * android.R.styleable#ContentCaptureService content-capture-service}&gt;</code> tag.
89 *
90 * <p>This is a a sample XML file configuring a ContentCaptureService:
91 * <pre> &lt;content-capture-service
92 * android:settingsActivity="foo.bar.SettingsActivity"
93 * . . .
94 * /&gt;</pre>
95 */
96 public static final String SERVICE_META_DATA = "android.content_capture";
97
Felipe Leme749b8892018-12-03 16:30:30 -080098 private Handler mHandler;
Felipe Lemeaf4e4fd2019-01-15 08:32:42 -080099 private IContentCaptureServiceCallback mCallback;
Felipe Leme749b8892018-12-03 16:30:30 -0800100
Felipe Lemeb96878492018-12-17 12:22:29 -0800101 /**
102 * Binder that receives calls from the system server.
103 */
104 private final IContentCaptureService mServerInterface = new IContentCaptureService.Stub() {
Felipe Leme749b8892018-12-03 16:30:30 -0800105
106 @Override
Felipe Leme326f15a2019-02-19 09:42:24 -0800107 public void onConnected(IBinder callback, boolean verbose, boolean debug) {
108 sVerbose = verbose;
109 sDebug = debug;
Felipe Lemeaf4e4fd2019-01-15 08:32:42 -0800110 mHandler.sendMessage(obtainMessage(ContentCaptureService::handleOnConnected,
111 ContentCaptureService.this, callback));
112 }
113
114 @Override
115 public void onDisconnected() {
116 mHandler.sendMessage(obtainMessage(ContentCaptureService::handleOnDisconnected,
117 ContentCaptureService.this));
Felipe Lemebb98ed62018-12-21 09:29:27 -0800118 }
119
120 @Override
Felipe Leme08054202019-03-28 11:29:25 -0700121 public void onSessionStarted(ContentCaptureContext context, int sessionId, int uid,
Felipe Leme518fb622019-03-08 15:35:04 -0800122 IResultReceiver clientReceiver, int initialState) {
Felipe Lemeb96878492018-12-17 12:22:29 -0800123 mHandler.sendMessage(obtainMessage(ContentCaptureService::handleOnCreateSession,
Felipe Leme518fb622019-03-08 15:35:04 -0800124 ContentCaptureService.this, context, sessionId, uid, clientReceiver,
125 initialState));
Felipe Leme749b8892018-12-03 16:30:30 -0800126 }
127
128 @Override
Felipe Leme08054202019-03-28 11:29:25 -0700129 public void onActivitySnapshot(int sessionId, SnapshotData snapshotData) {
Felipe Leme749b8892018-12-03 16:30:30 -0800130 mHandler.sendMessage(
131 obtainMessage(ContentCaptureService::handleOnActivitySnapshot,
132 ContentCaptureService.this, sessionId, snapshotData));
133 }
Felipe Lemeb96878492018-12-17 12:22:29 -0800134
135 @Override
Felipe Leme08054202019-03-28 11:29:25 -0700136 public void onSessionFinished(int sessionId) {
Felipe Lemeb96878492018-12-17 12:22:29 -0800137 mHandler.sendMessage(obtainMessage(ContentCaptureService::handleFinishSession,
138 ContentCaptureService.this, sessionId));
139 }
Adam He3d0409b2019-01-15 14:22:04 -0800140
141 @Override
142 public void onUserDataRemovalRequest(UserDataRemovalRequest request) {
143 mHandler.sendMessage(
144 obtainMessage(ContentCaptureService::handleOnUserDataRemovalRequest,
145 ContentCaptureService.this, request));
146 }
Felipe Leme141864d2019-02-27 17:01:51 -0800147
148 @Override
149 public void onActivityEvent(ActivityEvent event) {
150 mHandler.sendMessage(obtainMessage(ContentCaptureService::handleOnActivityEvent,
151 ContentCaptureService.this, event));
152
153 }
Felipe Leme749b8892018-12-03 16:30:30 -0800154 };
155
Felipe Lemeb96878492018-12-17 12:22:29 -0800156 /**
157 * Binder that receives calls from the app.
158 */
159 private final IContentCaptureDirectManager mClientInterface =
160 new IContentCaptureDirectManager.Stub() {
161
162 @Override
Felipe Lemefc24bea2018-12-18 13:19:01 -0800163 public void sendEvents(@SuppressWarnings("rawtypes") ParceledListSlice events) {
Felipe Lemeb96878492018-12-17 12:22:29 -0800164 mHandler.sendMessage(obtainMessage(ContentCaptureService::handleSendEvents,
Felipe Lemefc24bea2018-12-18 13:19:01 -0800165 ContentCaptureService.this, Binder.getCallingUid(), events));
Felipe Lemeb96878492018-12-17 12:22:29 -0800166 }
167 };
168
169 /**
Felipe Lemee127c9a2019-01-04 14:56:38 -0800170 * UIDs associated with each session.
Felipe Lemeb96878492018-12-17 12:22:29 -0800171 *
172 * <p>This map is populated when an session is started, which is called by the system server
173 * and can be trusted. Then subsequent calls made by the app are verified against this map.
174 */
Felipe Leme08054202019-03-28 11:29:25 -0700175 private final SparseIntArray mSessionUids = new SparseIntArray();
Felipe Lemeb96878492018-12-17 12:22:29 -0800176
Felipe Leme749b8892018-12-03 16:30:30 -0800177 @CallSuper
178 @Override
179 public void onCreate() {
180 super.onCreate();
181 mHandler = new Handler(Looper.getMainLooper(), null, true);
182 }
183
184 /** @hide */
185 @Override
186 public final IBinder onBind(Intent intent) {
187 if (SERVICE_INTERFACE.equals(intent.getAction())) {
Felipe Lemeb96878492018-12-17 12:22:29 -0800188 return mServerInterface.asBinder();
Felipe Leme749b8892018-12-03 16:30:30 -0800189 }
190 Log.w(TAG, "Tried to bind to wrong intent (should be " + SERVICE_INTERFACE + ": " + intent);
191 return null;
192 }
193
194 /**
Felipe Leme7a3c9f52019-02-13 16:32:49 -0800195 * Explicitly limits content capture to the given packages and activities.
196 *
197 * <p>To reset the whitelist, call it passing {@code null} to both arguments.
198 *
199 * <p>Useful when the service wants to restrict content capture to a category of apps, like
200 * chat apps. For example, if the service wants to support view captures on all activities of
201 * app {@code ChatApp1} and just activities {@code act1} and {@code act2} of {@code ChatApp2},
202 * it would call: {@code setContentCaptureWhitelist(Sets.newArraySet("ChatApp1"),
203 * Sets.newArraySet(new ComponentName("ChatApp2", "act1"),
204 * new ComponentName("ChatApp2", "act2")));}
205 */
206 public final void setContentCaptureWhitelist(@Nullable Set<String> packages,
207 @Nullable Set<ComponentName> activities) {
Felipe Lemee8eae9d42019-02-13 15:41:55 -0800208 final IContentCaptureServiceCallback callback = mCallback;
209 if (callback == null) {
210 Log.w(TAG, "setContentCaptureWhitelist(): no server callback");
211 return;
212 }
213
214 try {
215 callback.setContentCaptureWhitelist(toList(packages), toList(activities));
216 } catch (RemoteException e) {
217 e.rethrowFromSystemServer();
218 }
Felipe Leme7a3c9f52019-02-13 16:32:49 -0800219 }
220
Felipe Leme790be042019-03-25 09:52:19 -0700221 /**
222 * Explicitly sets the conditions for which content capture should be available by an app.
223 *
224 * <p>Typically used to restrict content capture to a few websites on browser apps. Example:
225 *
226 * <code>
227 * ArraySet<ContentCaptureCondition> conditions = new ArraySet<>(1);
228 * conditions.add(new ContentCaptureCondition(new LocusId("^https://.*\\.example\\.com$"),
229 * ContentCaptureCondition.FLAG_IS_REGEX));
230 * service.setContentCaptureConditions("com.example.browser_app", conditions);
231 *
232 * </code>
233 *
234 * <p>NOTE: </p> this method doesn't automatically disable content capture for the given
235 * conditions; it's up to the {@code packageName} implementation to call
236 * {@link ContentCaptureManager#getContentCaptureConditions()} and disable it accordingly.
237 *
238 * @param packageName name of the packages where the restrictions are set.
239 * @param conditions list of conditions, or {@code null} to reset the conditions for the
240 * package.
241 */
242 public final void setContentCaptureConditions(@NonNull String packageName,
243 @Nullable Set<ContentCaptureCondition> conditions) {
244 // TODO(b/129267994): implement
245 }
246
Felipe Leme7a3c9f52019-02-13 16:32:49 -0800247 private <T> ArrayList<T> toList(@Nullable Set<T> set) {
248 return set == null ? null : new ArrayList<T>(set);
249 }
250
251 /**
Felipe Lemebb98ed62018-12-21 09:29:27 -0800252 * Called when the Android system connects to service.
253 *
254 * <p>You should generally do initialization here rather than in {@link #onCreate}.
255 */
256 public void onConnected() {
257 Slog.i(TAG, "bound to " + getClass().getName());
258 }
259
260 /**
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800261 * Creates a new content capture session.
Felipe Leme749b8892018-12-03 16:30:30 -0800262 *
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800263 * @param context content capture context
Felipe Leme749b8892018-12-03 16:30:30 -0800264 * @param sessionId the session's Id
265 */
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800266 public void onCreateContentCaptureSession(@NonNull ContentCaptureContext context,
267 @NonNull ContentCaptureSessionId sessionId) {
Felipe Leme326f15a2019-02-19 09:42:24 -0800268 if (sVerbose) {
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800269 Log.v(TAG, "onCreateContentCaptureSession(id=" + sessionId + ", ctx=" + context + ")");
Felipe Leme749b8892018-12-03 16:30:30 -0800270 }
271 }
272
273 /**
Felipe Lemefc24bea2018-12-18 13:19:01 -0800274 * Notifies the service of {@link ContentCaptureEvent events} associated with a content capture
275 * session.
276 *
277 * @param sessionId the session's Id
278 * @param event the event
279 */
280 public void onContentCaptureEvent(@NonNull ContentCaptureSessionId sessionId,
281 @NonNull ContentCaptureEvent event) {
Felipe Leme326f15a2019-02-19 09:42:24 -0800282 if (sVerbose) Log.v(TAG, "onContentCaptureEventsRequest(id=" + sessionId + ")");
Felipe Lemefc24bea2018-12-18 13:19:01 -0800283 }
Felipe Leme62e45b02019-01-10 10:22:00 -0800284
285 /**
286 * Notifies the service that the app requested to remove data associated with the user.
287 *
288 * @param request the user data requested to be removed
289 */
290 public void onUserDataRemovalRequest(@NonNull UserDataRemovalRequest request) {
Felipe Leme326f15a2019-02-19 09:42:24 -0800291 if (sVerbose) Log.v(TAG, "onUserDataRemovalRequest()");
Felipe Leme62e45b02019-01-10 10:22:00 -0800292 }
293
Felipe Lemefc24bea2018-12-18 13:19:01 -0800294 /**
Felipe Leme749b8892018-12-03 16:30:30 -0800295 * Notifies the service of {@link SnapshotData snapshot data} associated with a session.
296 *
297 * @param sessionId the session's Id
298 * @param snapshotData the data
299 */
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800300 public void onActivitySnapshot(@NonNull ContentCaptureSessionId sessionId,
Felipe Leme141864d2019-02-27 17:01:51 -0800301 @NonNull SnapshotData snapshotData) {
302 if (sVerbose) Log.v(TAG, "onActivitySnapshot(id=" + sessionId + ")");
303 }
304
305 /**
306 * Notifies the service of an activity-level event that is not associated with a session.
307 *
308 * <p>This method can be used to track some high-level events for all activities, even those
309 * that are not whitelisted for Content Capture.
310 *
311 * @param event high-level activity event
312 */
313 public void onActivityEvent(@NonNull ActivityEvent event) {
314 if (sVerbose) Log.v(TAG, "onActivityEvent(): " + event);
315 }
Felipe Leme749b8892018-12-03 16:30:30 -0800316
317 /**
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800318 * Destroys the content capture session.
Felipe Leme749b8892018-12-03 16:30:30 -0800319 *
320 * @param sessionId the id of the session to destroy
Felipe Lemeb96878492018-12-17 12:22:29 -0800321 * */
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800322 public void onDestroyContentCaptureSession(@NonNull ContentCaptureSessionId sessionId) {
Felipe Leme326f15a2019-02-19 09:42:24 -0800323 if (sVerbose) Log.v(TAG, "onDestroyContentCaptureSession(id=" + sessionId + ")");
Felipe Lemeb96878492018-12-17 12:22:29 -0800324 }
325
Felipe Lemebb98ed62018-12-21 09:29:27 -0800326 /**
Felipe Leme72e83d82019-02-13 14:51:17 -0800327 * Disables the Content Capture service for the given user.
328 */
Felipe Lemebedcd612019-03-22 10:29:30 -0700329 public final void disableSelf() {
330 if (sDebug) Log.d(TAG, "disableSelf()");
Felipe Leme72e83d82019-02-13 14:51:17 -0800331
332 final IContentCaptureServiceCallback callback = mCallback;
333 if (callback == null) {
Felipe Lemebedcd612019-03-22 10:29:30 -0700334 Log.w(TAG, "disableSelf(): no server callback");
Felipe Leme72e83d82019-02-13 14:51:17 -0800335 return;
336 }
337 try {
338 callback.disableSelf();
339 } catch (RemoteException e) {
340 e.rethrowFromSystemServer();
341 }
342 }
343
344 /**
Felipe Lemebb98ed62018-12-21 09:29:27 -0800345 * Called when the Android system disconnects from the service.
346 *
347 * <p> At this point this service may no longer be an active {@link AutofillService}.
348 */
349 public void onDisconnected() {
350 Slog.i(TAG, "unbinding from " + getClass().getName());
351 }
352
Felipe Lemeb96878492018-12-17 12:22:29 -0800353 @Override
354 @CallSuper
355 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Felipe Leme326f15a2019-02-19 09:42:24 -0800356 pw.print("Debug: "); pw.print(sDebug); pw.print(" Verbose: "); pw.println(sVerbose);
Felipe Lemee127c9a2019-01-04 14:56:38 -0800357 final int size = mSessionUids.size();
Felipe Lemeb96878492018-12-17 12:22:29 -0800358 pw.print("Number sessions: "); pw.println(size);
359 if (size > 0) {
360 final String prefix = " ";
361 for (int i = 0; i < size; i++) {
Felipe Lemee127c9a2019-01-04 14:56:38 -0800362 pw.print(prefix); pw.print(mSessionUids.keyAt(i));
363 pw.print(": uid="); pw.println(mSessionUids.valueAt(i));
Felipe Lemeb96878492018-12-17 12:22:29 -0800364 }
Felipe Leme749b8892018-12-03 16:30:30 -0800365 }
366 }
367
Felipe Lemeaf4e4fd2019-01-15 08:32:42 -0800368 private void handleOnConnected(@NonNull IBinder callback) {
369 mCallback = IContentCaptureServiceCallback.Stub.asInterface(callback);
370 onConnected();
371 }
372
373 private void handleOnDisconnected() {
374 onDisconnected();
375 mCallback = null;
Felipe Lemebb98ed62018-12-21 09:29:27 -0800376 }
377
Felipe Leme749b8892018-12-03 16:30:30 -0800378 //TODO(b/111276913): consider caching the InteractionSessionId for the lifetime of the session,
379 // so we don't need to create a temporary InteractionSessionId for each event.
380
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800381 private void handleOnCreateSession(@NonNull ContentCaptureContext context,
Felipe Leme08054202019-03-28 11:29:25 -0700382 int sessionId, int uid, IResultReceiver clientReceiver, int initialState) {
Felipe Lemee127c9a2019-01-04 14:56:38 -0800383 mSessionUids.put(sessionId, uid);
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800384 onCreateContentCaptureSession(context, new ContentCaptureSessionId(sessionId));
Adam He328c0e32019-01-03 15:19:22 -0800385
Felipe Leme01b87492019-01-15 13:26:52 -0800386 final int clientFlags = context.getFlags();
387 int stateFlags = 0;
388 if ((clientFlags & ContentCaptureContext.FLAG_DISABLED_BY_FLAG_SECURE) != 0) {
389 stateFlags |= ContentCaptureSession.STATE_FLAG_SECURE;
Adam He328c0e32019-01-03 15:19:22 -0800390 }
Felipe Leme01b87492019-01-15 13:26:52 -0800391 if ((clientFlags & ContentCaptureContext.FLAG_DISABLED_BY_APP) != 0) {
392 stateFlags |= ContentCaptureSession.STATE_BY_APP;
Adam He6079d152019-01-10 11:37:17 -0800393 }
Felipe Leme01b87492019-01-15 13:26:52 -0800394 if (stateFlags == 0) {
Felipe Leme518fb622019-03-08 15:35:04 -0800395 stateFlags = initialState;
Felipe Leme01b87492019-01-15 13:26:52 -0800396 } else {
397 stateFlags |= ContentCaptureSession.STATE_DISABLED;
Felipe Leme01b87492019-01-15 13:26:52 -0800398 }
399 setClientState(clientReceiver, stateFlags, mClientInterface.asBinder());
Felipe Leme749b8892018-12-03 16:30:30 -0800400 }
401
Felipe Lemefc24bea2018-12-18 13:19:01 -0800402 private void handleSendEvents(int uid,
403 @NonNull ParceledListSlice<ContentCaptureEvent> parceledEvents) {
404
405 // Most events belong to the same session, so we can keep a reference to the last one
406 // to avoid creating too many ContentCaptureSessionId objects
Felipe Leme08054202019-03-28 11:29:25 -0700407 int lastSessionId = NO_SESSION_ID;
Felipe Lemefc24bea2018-12-18 13:19:01 -0800408 ContentCaptureSessionId sessionId = null;
409
410 final List<ContentCaptureEvent> events = parceledEvents.getList();
411 for (int i = 0; i < events.size(); i++) {
412 final ContentCaptureEvent event = events.get(i);
Felipe Leme87a9dc92018-12-18 14:28:07 -0800413 if (!handleIsRightCallerFor(event, uid)) continue;
Felipe Leme08054202019-03-28 11:29:25 -0700414 int sessionIdInt = event.getSessionId();
415 if (sessionIdInt != lastSessionId) {
416 sessionId = new ContentCaptureSessionId(sessionIdInt);
417 lastSessionId = sessionIdInt;
Felipe Lemefc24bea2018-12-18 13:19:01 -0800418 }
Felipe Leme87a9dc92018-12-18 14:28:07 -0800419 switch (event.getType()) {
420 case ContentCaptureEvent.TYPE_SESSION_STARTED:
Felipe Leme4eecbe62019-02-11 17:50:17 -0800421 final ContentCaptureContext clientContext = event.getContentCaptureContext();
Felipe Leme87a9dc92018-12-18 14:28:07 -0800422 clientContext.setParentSessionId(event.getParentSessionId());
Felipe Leme08054202019-03-28 11:29:25 -0700423 mSessionUids.put(sessionIdInt, uid);
Felipe Leme87a9dc92018-12-18 14:28:07 -0800424 onCreateContentCaptureSession(clientContext, sessionId);
425 break;
426 case ContentCaptureEvent.TYPE_SESSION_FINISHED:
Felipe Leme08054202019-03-28 11:29:25 -0700427 mSessionUids.delete(sessionIdInt);
Felipe Leme87a9dc92018-12-18 14:28:07 -0800428 onDestroyContentCaptureSession(sessionId);
429 break;
430 default:
431 onContentCaptureEvent(sessionId, event);
432 }
Felipe Lemeb96878492018-12-17 12:22:29 -0800433 }
Felipe Leme749b8892018-12-03 16:30:30 -0800434 }
435
Felipe Leme08054202019-03-28 11:29:25 -0700436 private void handleOnActivitySnapshot(int sessionId, @NonNull SnapshotData snapshotData) {
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800437 onActivitySnapshot(new ContentCaptureSessionId(sessionId), snapshotData);
Felipe Leme749b8892018-12-03 16:30:30 -0800438 }
439
Felipe Leme08054202019-03-28 11:29:25 -0700440 private void handleFinishSession(int sessionId) {
441 mSessionUids.delete(sessionId);
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800442 onDestroyContentCaptureSession(new ContentCaptureSessionId(sessionId));
Felipe Leme749b8892018-12-03 16:30:30 -0800443 }
Felipe Lemeb96878492018-12-17 12:22:29 -0800444
Adam He3d0409b2019-01-15 14:22:04 -0800445 private void handleOnUserDataRemovalRequest(@NonNull UserDataRemovalRequest request) {
446 onUserDataRemovalRequest(request);
447 }
448
Felipe Leme141864d2019-02-27 17:01:51 -0800449 private void handleOnActivityEvent(@NonNull ActivityEvent event) {
450 onActivityEvent(event);
451 }
452
Felipe Lemeb96878492018-12-17 12:22:29 -0800453 /**
Felipe Leme87a9dc92018-12-18 14:28:07 -0800454 * Checks if the given {@code uid} owns the session associated with the event.
Felipe Lemeb96878492018-12-17 12:22:29 -0800455 */
Felipe Leme87a9dc92018-12-18 14:28:07 -0800456 private boolean handleIsRightCallerFor(@NonNull ContentCaptureEvent event, int uid) {
Felipe Leme08054202019-03-28 11:29:25 -0700457 final int sessionId;
Felipe Leme87a9dc92018-12-18 14:28:07 -0800458 switch (event.getType()) {
459 case ContentCaptureEvent.TYPE_SESSION_STARTED:
460 case ContentCaptureEvent.TYPE_SESSION_FINISHED:
461 sessionId = event.getParentSessionId();
462 break;
463 default:
464 sessionId = event.getSessionId();
465 }
Felipe Leme08054202019-03-28 11:29:25 -0700466 if (mSessionUids.indexOfKey(sessionId) < 0) {
Felipe Leme326f15a2019-02-19 09:42:24 -0800467 if (sVerbose) {
Felipe Leme4eecbe62019-02-11 17:50:17 -0800468 Log.v(TAG, "handleIsRightCallerFor(" + event + "): no session for " + sessionId
Felipe Lemee127c9a2019-01-04 14:56:38 -0800469 + ": " + mSessionUids);
470 }
471 // Just ignore, as the session could have been finished already
Felipe Lemeb96878492018-12-17 12:22:29 -0800472 return false;
473 }
Felipe Leme08054202019-03-28 11:29:25 -0700474 final int rightUid = mSessionUids.get(sessionId);
Felipe Lemeb96878492018-12-17 12:22:29 -0800475 if (rightUid != uid) {
476 Log.e(TAG, "invalid call from UID " + uid + ": session " + sessionId + " belongs to "
477 + rightUid);
478 //TODO(b/111276913): log metrics as this could be a malicious app forging a sessionId
479 return false;
480 }
481 return true;
482
483 }
484
485 /**
Adam He328c0e32019-01-03 15:19:22 -0800486 * Sends the state of the {@link ContentCaptureManager} in the client app.
Felipe Lemeb96878492018-12-17 12:22:29 -0800487 *
488 * @param clientReceiver receiver in the client app.
Adam He328c0e32019-01-03 15:19:22 -0800489 * @param sessionState state of the session
Felipe Lemeb96878492018-12-17 12:22:29 -0800490 * @param binder handle to the {@code IContentCaptureDirectManager} object that resides in the
491 * service.
492 * @hide
493 */
494 public static void setClientState(@NonNull IResultReceiver clientReceiver,
Adam He328c0e32019-01-03 15:19:22 -0800495 int sessionState, @Nullable IBinder binder) {
Felipe Lemeb96878492018-12-17 12:22:29 -0800496 try {
497 final Bundle extras;
498 if (binder != null) {
499 extras = new Bundle();
Felipe Leme87a9dc92018-12-18 14:28:07 -0800500 extras.putBinder(MainContentCaptureSession.EXTRA_BINDER, binder);
Felipe Lemeb96878492018-12-17 12:22:29 -0800501 } else {
502 extras = null;
503 }
Adam He328c0e32019-01-03 15:19:22 -0800504 clientReceiver.send(sessionState, extras);
Felipe Lemeb96878492018-12-17 12:22:29 -0800505 } catch (RemoteException e) {
506 Slog.w(TAG, "Error async reporting result to client: " + e);
507 }
508 }
Felipe Leme749b8892018-12-03 16:30:30 -0800509}