blob: df113979bacf4af23ac48a363a1c93a12833a980 [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;
20
Felipe Leme749b8892018-12-03 16:30:30 -080021import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
22
23import android.annotation.CallSuper;
24import android.annotation.NonNull;
25import android.annotation.Nullable;
26import android.annotation.SystemApi;
Felipe Leme19652c02019-02-04 13:01:29 -080027import android.annotation.TestApi;
Felipe Leme749b8892018-12-03 16:30:30 -080028import android.app.Service;
29import android.content.ComponentName;
30import android.content.Intent;
Felipe Lemeb96878492018-12-17 12:22:29 -080031import android.content.pm.ParceledListSlice;
32import android.os.Binder;
33import android.os.Bundle;
Felipe Leme749b8892018-12-03 16:30:30 -080034import android.os.Handler;
35import android.os.IBinder;
36import android.os.Looper;
37import android.os.RemoteException;
Felipe Lemebb98ed62018-12-21 09:29:27 -080038import android.service.autofill.AutofillService;
Felipe Lemeb96878492018-12-17 12:22:29 -080039import android.util.ArrayMap;
Felipe Leme749b8892018-12-03 16:30:30 -080040import android.util.Log;
Felipe Lemeb96878492018-12-17 12:22:29 -080041import android.util.Slog;
Felipe Lemeaa5088e2018-12-10 14:53:58 -080042import android.view.contentcapture.ContentCaptureContext;
Felipe Leme749b8892018-12-03 16:30:30 -080043import android.view.contentcapture.ContentCaptureEvent;
Felipe Lemeb96878492018-12-17 12:22:29 -080044import android.view.contentcapture.ContentCaptureManager;
45import android.view.contentcapture.ContentCaptureSession;
Felipe Lemeaa5088e2018-12-10 14:53:58 -080046import android.view.contentcapture.ContentCaptureSessionId;
Felipe Lemeb96878492018-12-17 12:22:29 -080047import android.view.contentcapture.IContentCaptureDirectManager;
Felipe Leme87a9dc92018-12-18 14:28:07 -080048import android.view.contentcapture.MainContentCaptureSession;
Felipe Leme62e45b02019-01-10 10:22:00 -080049import android.view.contentcapture.UserDataRemovalRequest;
Felipe Leme749b8892018-12-03 16:30:30 -080050
Felipe Lemeb96878492018-12-17 12:22:29 -080051import com.android.internal.os.IResultReceiver;
52
53import java.io.FileDescriptor;
54import java.io.PrintWriter;
Felipe Leme7a3c9f52019-02-13 16:32:49 -080055import java.util.ArrayList;
Felipe Leme749b8892018-12-03 16:30:30 -080056import java.util.List;
Felipe Leme7a3c9f52019-02-13 16:32:49 -080057import java.util.Set;
Felipe Leme749b8892018-12-03 16:30:30 -080058
59/**
60 * A service used to capture the content of the screen to provide contextual data in other areas of
61 * the system such as Autofill.
62 *
63 * @hide
64 */
65@SystemApi
Felipe Leme19652c02019-02-04 13:01:29 -080066@TestApi
Felipe Leme749b8892018-12-03 16:30:30 -080067public abstract class ContentCaptureService extends Service {
68
69 private static final String TAG = ContentCaptureService.class.getSimpleName();
70
Felipe Leme749b8892018-12-03 16:30:30 -080071 /**
72 * The {@link Intent} that must be declared as handled by the service.
73 *
74 * <p>To be supported, the service must also require the
75 * {@link android.Manifest.permission#BIND_CONTENT_CAPTURE_SERVICE} permission so
76 * that other applications can not abuse it.
77 */
78 public static final String SERVICE_INTERFACE =
79 "android.service.contentcapture.ContentCaptureService";
80
Felipe Lemea5d5e2d2019-03-19 16:54:55 -070081 /**
82 * Name under which a ContentCaptureService component publishes information about itself.
83 *
84 * <p>This meta-data should reference an XML resource containing a
85 * <code>&lt;{@link
86 * android.R.styleable#ContentCaptureService content-capture-service}&gt;</code> tag.
87 *
88 * <p>This is a a sample XML file configuring a ContentCaptureService:
89 * <pre> &lt;content-capture-service
90 * android:settingsActivity="foo.bar.SettingsActivity"
91 * . . .
92 * /&gt;</pre>
93 */
94 public static final String SERVICE_META_DATA = "android.content_capture";
95
Felipe Leme749b8892018-12-03 16:30:30 -080096 private Handler mHandler;
Felipe Lemeaf4e4fd2019-01-15 08:32:42 -080097 private IContentCaptureServiceCallback mCallback;
Felipe Leme749b8892018-12-03 16:30:30 -080098
Felipe Lemeb96878492018-12-17 12:22:29 -080099 /**
100 * Binder that receives calls from the system server.
101 */
102 private final IContentCaptureService mServerInterface = new IContentCaptureService.Stub() {
Felipe Leme749b8892018-12-03 16:30:30 -0800103
104 @Override
Felipe Leme326f15a2019-02-19 09:42:24 -0800105 public void onConnected(IBinder callback, boolean verbose, boolean debug) {
106 sVerbose = verbose;
107 sDebug = debug;
Felipe Lemeaf4e4fd2019-01-15 08:32:42 -0800108 mHandler.sendMessage(obtainMessage(ContentCaptureService::handleOnConnected,
109 ContentCaptureService.this, callback));
110 }
111
112 @Override
113 public void onDisconnected() {
114 mHandler.sendMessage(obtainMessage(ContentCaptureService::handleOnDisconnected,
115 ContentCaptureService.this));
Felipe Lemebb98ed62018-12-21 09:29:27 -0800116 }
117
118 @Override
Felipe Lemeb96878492018-12-17 12:22:29 -0800119 public void onSessionStarted(ContentCaptureContext context, String sessionId, int uid,
Felipe Leme518fb622019-03-08 15:35:04 -0800120 IResultReceiver clientReceiver, int initialState) {
Felipe Lemeb96878492018-12-17 12:22:29 -0800121 mHandler.sendMessage(obtainMessage(ContentCaptureService::handleOnCreateSession,
Felipe Leme518fb622019-03-08 15:35:04 -0800122 ContentCaptureService.this, context, sessionId, uid, clientReceiver,
123 initialState));
Felipe Leme749b8892018-12-03 16:30:30 -0800124 }
125
126 @Override
127 public void onActivitySnapshot(String sessionId, SnapshotData snapshotData) {
128 mHandler.sendMessage(
129 obtainMessage(ContentCaptureService::handleOnActivitySnapshot,
130 ContentCaptureService.this, sessionId, snapshotData));
131 }
Felipe Lemeb96878492018-12-17 12:22:29 -0800132
133 @Override
134 public void onSessionFinished(String sessionId) {
135 mHandler.sendMessage(obtainMessage(ContentCaptureService::handleFinishSession,
136 ContentCaptureService.this, sessionId));
137 }
Adam He3d0409b2019-01-15 14:22:04 -0800138
139 @Override
140 public void onUserDataRemovalRequest(UserDataRemovalRequest request) {
141 mHandler.sendMessage(
142 obtainMessage(ContentCaptureService::handleOnUserDataRemovalRequest,
143 ContentCaptureService.this, request));
144 }
Felipe Leme141864d2019-02-27 17:01:51 -0800145
146 @Override
147 public void onActivityEvent(ActivityEvent event) {
148 mHandler.sendMessage(obtainMessage(ContentCaptureService::handleOnActivityEvent,
149 ContentCaptureService.this, event));
150
151 }
Felipe Leme749b8892018-12-03 16:30:30 -0800152 };
153
Felipe Lemeb96878492018-12-17 12:22:29 -0800154 /**
155 * Binder that receives calls from the app.
156 */
157 private final IContentCaptureDirectManager mClientInterface =
158 new IContentCaptureDirectManager.Stub() {
159
160 @Override
Felipe Lemefc24bea2018-12-18 13:19:01 -0800161 public void sendEvents(@SuppressWarnings("rawtypes") ParceledListSlice events) {
Felipe Lemeb96878492018-12-17 12:22:29 -0800162 mHandler.sendMessage(obtainMessage(ContentCaptureService::handleSendEvents,
Felipe Lemefc24bea2018-12-18 13:19:01 -0800163 ContentCaptureService.this, Binder.getCallingUid(), events));
Felipe Lemeb96878492018-12-17 12:22:29 -0800164 }
165 };
166
167 /**
Felipe Lemee127c9a2019-01-04 14:56:38 -0800168 * UIDs associated with each session.
Felipe Lemeb96878492018-12-17 12:22:29 -0800169 *
170 * <p>This map is populated when an session is started, which is called by the system server
171 * and can be trusted. Then subsequent calls made by the app are verified against this map.
172 */
Felipe Lemee127c9a2019-01-04 14:56:38 -0800173 private final ArrayMap<String, Integer> mSessionUids = new ArrayMap<>();
Felipe Lemeb96878492018-12-17 12:22:29 -0800174
Felipe Leme749b8892018-12-03 16:30:30 -0800175 @CallSuper
176 @Override
177 public void onCreate() {
178 super.onCreate();
179 mHandler = new Handler(Looper.getMainLooper(), null, true);
180 }
181
182 /** @hide */
183 @Override
184 public final IBinder onBind(Intent intent) {
185 if (SERVICE_INTERFACE.equals(intent.getAction())) {
Felipe Lemeb96878492018-12-17 12:22:29 -0800186 return mServerInterface.asBinder();
Felipe Leme749b8892018-12-03 16:30:30 -0800187 }
188 Log.w(TAG, "Tried to bind to wrong intent (should be " + SERVICE_INTERFACE + ": " + intent);
189 return null;
190 }
191
192 /**
Felipe Leme7a3c9f52019-02-13 16:32:49 -0800193 * Explicitly limits content capture to the given packages and activities.
194 *
195 * <p>To reset the whitelist, call it passing {@code null} to both arguments.
196 *
197 * <p>Useful when the service wants to restrict content capture to a category of apps, like
198 * chat apps. For example, if the service wants to support view captures on all activities of
199 * app {@code ChatApp1} and just activities {@code act1} and {@code act2} of {@code ChatApp2},
200 * it would call: {@code setContentCaptureWhitelist(Sets.newArraySet("ChatApp1"),
201 * Sets.newArraySet(new ComponentName("ChatApp2", "act1"),
202 * new ComponentName("ChatApp2", "act2")));}
203 */
204 public final void setContentCaptureWhitelist(@Nullable Set<String> packages,
205 @Nullable Set<ComponentName> activities) {
Felipe Lemee8eae9d42019-02-13 15:41:55 -0800206 final IContentCaptureServiceCallback callback = mCallback;
207 if (callback == null) {
208 Log.w(TAG, "setContentCaptureWhitelist(): no server callback");
209 return;
210 }
211
212 try {
213 callback.setContentCaptureWhitelist(toList(packages), toList(activities));
214 } catch (RemoteException e) {
215 e.rethrowFromSystemServer();
216 }
Felipe Leme7a3c9f52019-02-13 16:32:49 -0800217 }
218
219 private <T> ArrayList<T> toList(@Nullable Set<T> set) {
220 return set == null ? null : new ArrayList<T>(set);
221 }
222
223 /**
Felipe Lemebb98ed62018-12-21 09:29:27 -0800224 * Called when the Android system connects to service.
225 *
226 * <p>You should generally do initialization here rather than in {@link #onCreate}.
227 */
228 public void onConnected() {
229 Slog.i(TAG, "bound to " + getClass().getName());
230 }
231
232 /**
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800233 * Creates a new content capture session.
Felipe Leme749b8892018-12-03 16:30:30 -0800234 *
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800235 * @param context content capture context
Felipe Leme749b8892018-12-03 16:30:30 -0800236 * @param sessionId the session's Id
237 */
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800238 public void onCreateContentCaptureSession(@NonNull ContentCaptureContext context,
239 @NonNull ContentCaptureSessionId sessionId) {
Felipe Leme326f15a2019-02-19 09:42:24 -0800240 if (sVerbose) {
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800241 Log.v(TAG, "onCreateContentCaptureSession(id=" + sessionId + ", ctx=" + context + ")");
Felipe Leme749b8892018-12-03 16:30:30 -0800242 }
243 }
244
245 /**
Felipe Lemefc24bea2018-12-18 13:19:01 -0800246 * Notifies the service of {@link ContentCaptureEvent events} associated with a content capture
247 * session.
248 *
249 * @param sessionId the session's Id
250 * @param event the event
251 */
252 public void onContentCaptureEvent(@NonNull ContentCaptureSessionId sessionId,
253 @NonNull ContentCaptureEvent event) {
Felipe Leme326f15a2019-02-19 09:42:24 -0800254 if (sVerbose) Log.v(TAG, "onContentCaptureEventsRequest(id=" + sessionId + ")");
Felipe Lemefc24bea2018-12-18 13:19:01 -0800255 }
Felipe Leme62e45b02019-01-10 10:22:00 -0800256
257 /**
258 * Notifies the service that the app requested to remove data associated with the user.
259 *
260 * @param request the user data requested to be removed
261 */
262 public void onUserDataRemovalRequest(@NonNull UserDataRemovalRequest request) {
Felipe Leme326f15a2019-02-19 09:42:24 -0800263 if (sVerbose) Log.v(TAG, "onUserDataRemovalRequest()");
Felipe Leme62e45b02019-01-10 10:22:00 -0800264 }
265
Felipe Lemefc24bea2018-12-18 13:19:01 -0800266 /**
Felipe Leme749b8892018-12-03 16:30:30 -0800267 * Notifies the service of {@link SnapshotData snapshot data} associated with a session.
268 *
269 * @param sessionId the session's Id
270 * @param snapshotData the data
271 */
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800272 public void onActivitySnapshot(@NonNull ContentCaptureSessionId sessionId,
Felipe Leme141864d2019-02-27 17:01:51 -0800273 @NonNull SnapshotData snapshotData) {
274 if (sVerbose) Log.v(TAG, "onActivitySnapshot(id=" + sessionId + ")");
275 }
276
277 /**
278 * Notifies the service of an activity-level event that is not associated with a session.
279 *
280 * <p>This method can be used to track some high-level events for all activities, even those
281 * that are not whitelisted for Content Capture.
282 *
283 * @param event high-level activity event
284 */
285 public void onActivityEvent(@NonNull ActivityEvent event) {
286 if (sVerbose) Log.v(TAG, "onActivityEvent(): " + event);
287 }
Felipe Leme749b8892018-12-03 16:30:30 -0800288
289 /**
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800290 * Destroys the content capture session.
Felipe Leme749b8892018-12-03 16:30:30 -0800291 *
292 * @param sessionId the id of the session to destroy
Felipe Lemeb96878492018-12-17 12:22:29 -0800293 * */
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800294 public void onDestroyContentCaptureSession(@NonNull ContentCaptureSessionId sessionId) {
Felipe Leme326f15a2019-02-19 09:42:24 -0800295 if (sVerbose) Log.v(TAG, "onDestroyContentCaptureSession(id=" + sessionId + ")");
Felipe Lemeb96878492018-12-17 12:22:29 -0800296 }
297
Felipe Lemebb98ed62018-12-21 09:29:27 -0800298 /**
Felipe Leme72e83d82019-02-13 14:51:17 -0800299 * Disables the Content Capture service for the given user.
300 */
301 public final void disableContentCaptureServices() {
Felipe Leme326f15a2019-02-19 09:42:24 -0800302 if (sDebug) Log.d(TAG, "disableContentCaptureServices()");
Felipe Leme72e83d82019-02-13 14:51:17 -0800303
304 final IContentCaptureServiceCallback callback = mCallback;
305 if (callback == null) {
306 Log.w(TAG, "disableContentCaptureServices(): no server callback");
307 return;
308 }
309 try {
310 callback.disableSelf();
311 } catch (RemoteException e) {
312 e.rethrowFromSystemServer();
313 }
314 }
315
316 /**
Felipe Lemebb98ed62018-12-21 09:29:27 -0800317 * Called when the Android system disconnects from the service.
318 *
319 * <p> At this point this service may no longer be an active {@link AutofillService}.
320 */
321 public void onDisconnected() {
322 Slog.i(TAG, "unbinding from " + getClass().getName());
323 }
324
Felipe Lemeb96878492018-12-17 12:22:29 -0800325 @Override
326 @CallSuper
327 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Felipe Leme326f15a2019-02-19 09:42:24 -0800328 pw.print("Debug: "); pw.print(sDebug); pw.print(" Verbose: "); pw.println(sVerbose);
Felipe Lemee127c9a2019-01-04 14:56:38 -0800329 final int size = mSessionUids.size();
Felipe Lemeb96878492018-12-17 12:22:29 -0800330 pw.print("Number sessions: "); pw.println(size);
331 if (size > 0) {
332 final String prefix = " ";
333 for (int i = 0; i < size; i++) {
Felipe Lemee127c9a2019-01-04 14:56:38 -0800334 pw.print(prefix); pw.print(mSessionUids.keyAt(i));
335 pw.print(": uid="); pw.println(mSessionUids.valueAt(i));
Felipe Lemeb96878492018-12-17 12:22:29 -0800336 }
Felipe Leme749b8892018-12-03 16:30:30 -0800337 }
338 }
339
Felipe Lemeaf4e4fd2019-01-15 08:32:42 -0800340 private void handleOnConnected(@NonNull IBinder callback) {
341 mCallback = IContentCaptureServiceCallback.Stub.asInterface(callback);
342 onConnected();
343 }
344
345 private void handleOnDisconnected() {
346 onDisconnected();
347 mCallback = null;
Felipe Lemebb98ed62018-12-21 09:29:27 -0800348 }
349
Felipe Leme749b8892018-12-03 16:30:30 -0800350 //TODO(b/111276913): consider caching the InteractionSessionId for the lifetime of the session,
351 // so we don't need to create a temporary InteractionSessionId for each event.
352
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800353 private void handleOnCreateSession(@NonNull ContentCaptureContext context,
Felipe Leme518fb622019-03-08 15:35:04 -0800354 @NonNull String sessionId, int uid, IResultReceiver clientReceiver, int initialState) {
Felipe Lemee127c9a2019-01-04 14:56:38 -0800355 mSessionUids.put(sessionId, uid);
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800356 onCreateContentCaptureSession(context, new ContentCaptureSessionId(sessionId));
Adam He328c0e32019-01-03 15:19:22 -0800357
Felipe Leme01b87492019-01-15 13:26:52 -0800358 final int clientFlags = context.getFlags();
359 int stateFlags = 0;
360 if ((clientFlags & ContentCaptureContext.FLAG_DISABLED_BY_FLAG_SECURE) != 0) {
361 stateFlags |= ContentCaptureSession.STATE_FLAG_SECURE;
Adam He328c0e32019-01-03 15:19:22 -0800362 }
Felipe Leme01b87492019-01-15 13:26:52 -0800363 if ((clientFlags & ContentCaptureContext.FLAG_DISABLED_BY_APP) != 0) {
364 stateFlags |= ContentCaptureSession.STATE_BY_APP;
Adam He6079d152019-01-10 11:37:17 -0800365 }
Felipe Leme01b87492019-01-15 13:26:52 -0800366 if (stateFlags == 0) {
Felipe Leme518fb622019-03-08 15:35:04 -0800367 stateFlags = initialState;
Felipe Leme01b87492019-01-15 13:26:52 -0800368 } else {
369 stateFlags |= ContentCaptureSession.STATE_DISABLED;
Adam He328c0e32019-01-03 15:19:22 -0800370
Felipe Leme01b87492019-01-15 13:26:52 -0800371 }
372 setClientState(clientReceiver, stateFlags, mClientInterface.asBinder());
Felipe Leme749b8892018-12-03 16:30:30 -0800373 }
374
Felipe Lemefc24bea2018-12-18 13:19:01 -0800375 private void handleSendEvents(int uid,
376 @NonNull ParceledListSlice<ContentCaptureEvent> parceledEvents) {
377
378 // Most events belong to the same session, so we can keep a reference to the last one
379 // to avoid creating too many ContentCaptureSessionId objects
380 String lastSessionId = null;
381 ContentCaptureSessionId sessionId = null;
382
383 final List<ContentCaptureEvent> events = parceledEvents.getList();
384 for (int i = 0; i < events.size(); i++) {
385 final ContentCaptureEvent event = events.get(i);
Felipe Leme87a9dc92018-12-18 14:28:07 -0800386 if (!handleIsRightCallerFor(event, uid)) continue;
Felipe Lemefc24bea2018-12-18 13:19:01 -0800387 String sessionIdString = event.getSessionId();
388 if (!sessionIdString.equals(lastSessionId)) {
Felipe Lemefc24bea2018-12-18 13:19:01 -0800389 sessionId = new ContentCaptureSessionId(sessionIdString);
390 lastSessionId = sessionIdString;
391 }
Felipe Leme87a9dc92018-12-18 14:28:07 -0800392 switch (event.getType()) {
393 case ContentCaptureEvent.TYPE_SESSION_STARTED:
Felipe Leme4eecbe62019-02-11 17:50:17 -0800394 final ContentCaptureContext clientContext = event.getContentCaptureContext();
Felipe Leme87a9dc92018-12-18 14:28:07 -0800395 clientContext.setParentSessionId(event.getParentSessionId());
Felipe Lemee127c9a2019-01-04 14:56:38 -0800396 mSessionUids.put(sessionIdString, uid);
Felipe Leme87a9dc92018-12-18 14:28:07 -0800397 onCreateContentCaptureSession(clientContext, sessionId);
398 break;
399 case ContentCaptureEvent.TYPE_SESSION_FINISHED:
Felipe Lemee127c9a2019-01-04 14:56:38 -0800400 mSessionUids.remove(sessionIdString);
Felipe Leme87a9dc92018-12-18 14:28:07 -0800401 onDestroyContentCaptureSession(sessionId);
402 break;
403 default:
404 onContentCaptureEvent(sessionId, event);
405 }
Felipe Lemeb96878492018-12-17 12:22:29 -0800406 }
Felipe Leme749b8892018-12-03 16:30:30 -0800407 }
408
409 private void handleOnActivitySnapshot(@NonNull String sessionId,
410 @NonNull SnapshotData snapshotData) {
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800411 onActivitySnapshot(new ContentCaptureSessionId(sessionId), snapshotData);
Felipe Leme749b8892018-12-03 16:30:30 -0800412 }
413
Felipe Lemeb96878492018-12-17 12:22:29 -0800414 private void handleFinishSession(@NonNull String sessionId) {
Felipe Lemee127c9a2019-01-04 14:56:38 -0800415 mSessionUids.remove(sessionId);
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800416 onDestroyContentCaptureSession(new ContentCaptureSessionId(sessionId));
Felipe Leme749b8892018-12-03 16:30:30 -0800417 }
Felipe Lemeb96878492018-12-17 12:22:29 -0800418
Adam He3d0409b2019-01-15 14:22:04 -0800419 private void handleOnUserDataRemovalRequest(@NonNull UserDataRemovalRequest request) {
420 onUserDataRemovalRequest(request);
421 }
422
Felipe Leme141864d2019-02-27 17:01:51 -0800423 private void handleOnActivityEvent(@NonNull ActivityEvent event) {
424 onActivityEvent(event);
425 }
426
Felipe Lemeb96878492018-12-17 12:22:29 -0800427 /**
Felipe Leme87a9dc92018-12-18 14:28:07 -0800428 * Checks if the given {@code uid} owns the session associated with the event.
Felipe Lemeb96878492018-12-17 12:22:29 -0800429 */
Felipe Leme87a9dc92018-12-18 14:28:07 -0800430 private boolean handleIsRightCallerFor(@NonNull ContentCaptureEvent event, int uid) {
431 final String sessionId;
432 switch (event.getType()) {
433 case ContentCaptureEvent.TYPE_SESSION_STARTED:
434 case ContentCaptureEvent.TYPE_SESSION_FINISHED:
435 sessionId = event.getParentSessionId();
436 break;
437 default:
438 sessionId = event.getSessionId();
439 }
Felipe Lemee127c9a2019-01-04 14:56:38 -0800440 final Integer rightUid = mSessionUids.get(sessionId);
Felipe Lemeb96878492018-12-17 12:22:29 -0800441 if (rightUid == null) {
Felipe Leme326f15a2019-02-19 09:42:24 -0800442 if (sVerbose) {
Felipe Leme4eecbe62019-02-11 17:50:17 -0800443 Log.v(TAG, "handleIsRightCallerFor(" + event + "): no session for " + sessionId
Felipe Lemee127c9a2019-01-04 14:56:38 -0800444 + ": " + mSessionUids);
445 }
446 // Just ignore, as the session could have been finished already
Felipe Lemeb96878492018-12-17 12:22:29 -0800447 return false;
448 }
449 if (rightUid != uid) {
450 Log.e(TAG, "invalid call from UID " + uid + ": session " + sessionId + " belongs to "
451 + rightUid);
452 //TODO(b/111276913): log metrics as this could be a malicious app forging a sessionId
453 return false;
454 }
455 return true;
456
457 }
458
459 /**
Adam He328c0e32019-01-03 15:19:22 -0800460 * Sends the state of the {@link ContentCaptureManager} in the client app.
Felipe Lemeb96878492018-12-17 12:22:29 -0800461 *
462 * @param clientReceiver receiver in the client app.
Adam He328c0e32019-01-03 15:19:22 -0800463 * @param sessionState state of the session
Felipe Lemeb96878492018-12-17 12:22:29 -0800464 * @param binder handle to the {@code IContentCaptureDirectManager} object that resides in the
465 * service.
466 * @hide
467 */
468 public static void setClientState(@NonNull IResultReceiver clientReceiver,
Adam He328c0e32019-01-03 15:19:22 -0800469 int sessionState, @Nullable IBinder binder) {
Felipe Lemeb96878492018-12-17 12:22:29 -0800470 try {
471 final Bundle extras;
472 if (binder != null) {
473 extras = new Bundle();
Felipe Leme87a9dc92018-12-18 14:28:07 -0800474 extras.putBinder(MainContentCaptureSession.EXTRA_BINDER, binder);
Felipe Lemeb96878492018-12-17 12:22:29 -0800475 } else {
476 extras = null;
477 }
Adam He328c0e32019-01-03 15:19:22 -0800478 clientReceiver.send(sessionState, extras);
Felipe Lemeb96878492018-12-17 12:22:29 -0800479 } catch (RemoteException e) {
480 Slog.w(TAG, "Error async reporting result to client: " + e);
481 }
482 }
Felipe Leme749b8892018-12-03 16:30:30 -0800483}