blob: b9dc0dd99a1d5dc5e589664a8d2d7b6be641d4b3 [file] [log] [blame]
Felipe Lemeaa5088e2018-12-10 14:53:58 -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.view.contentcapture;
17
18import android.annotation.IntDef;
19import android.annotation.NonNull;
20import android.annotation.Nullable;
21import android.annotation.SystemApi;
Felipe Leme19652c02019-02-04 13:01:29 -080022import android.annotation.TestApi;
Felipe Lemeaa5088e2018-12-10 14:53:58 -080023import android.app.TaskInfo;
24import android.content.ComponentName;
Felipe Leme87a9dc92018-12-18 14:28:07 -080025import android.content.Context;
Felipe Leme044c63b2019-02-12 14:35:20 -080026import android.content.LocusId;
Felipe Lemeaa5088e2018-12-10 14:53:58 -080027import android.os.Bundle;
28import android.os.Parcel;
29import android.os.Parcelable;
Adam Hebb82c6a2019-01-17 14:59:02 -080030import android.view.Display;
Felipe Lemeaa5088e2018-12-10 14:53:58 -080031import android.view.View;
32
33import com.android.internal.util.Preconditions;
34
35import java.io.PrintWriter;
36import java.lang.annotation.Retention;
37import java.lang.annotation.RetentionPolicy;
38
39/**
40 * Context associated with a {@link ContentCaptureSession}.
41 */
42public final class ContentCaptureContext implements Parcelable {
43
44 /*
45 * IMPLEMENTATION NOTICE:
46 *
47 * This object contains both the info that's explicitly added by apps (hence it's public), but
48 * it also contains info injected by the server (and are accessible through @SystemApi methods).
49 */
50
51 /**
52 * Flag used to indicate that the app explicitly disabled content capture for the activity
53 * (using
54 * {@link android.view.contentcapture.ContentCaptureManager#setContentCaptureEnabled(boolean)}),
55 * in which case the service will just receive activity-level events.
56 *
57 * @hide
58 */
59 @SystemApi
Felipe Leme19652c02019-02-04 13:01:29 -080060 @TestApi
Felipe Lemeaa5088e2018-12-10 14:53:58 -080061 public static final int FLAG_DISABLED_BY_APP = 0x1;
62
63 /**
64 * Flag used to indicate that the activity's window is tagged with
65 * {@link android.view.Display#FLAG_SECURE}, in which case the service will just receive
66 * activity-level events.
67 *
68 * @hide
69 */
70 @SystemApi
Felipe Leme19652c02019-02-04 13:01:29 -080071 @TestApi
Felipe Lemeaa5088e2018-12-10 14:53:58 -080072 public static final int FLAG_DISABLED_BY_FLAG_SECURE = 0x2;
73
74 /** @hide */
75 @IntDef(flag = true, prefix = { "FLAG_" }, value = {
76 FLAG_DISABLED_BY_APP,
77 FLAG_DISABLED_BY_FLAG_SECURE
78 })
79 @Retention(RetentionPolicy.SOURCE)
80 @interface ContextCreationFlags{}
81
82 /**
83 * Flag indicating if this object has the app-provided context (which is set on
Felipe Leme87a9dc92018-12-18 14:28:07 -080084 * {@link ContentCaptureSession#createContentCaptureSession(ContentCaptureContext)}).
Felipe Lemeaa5088e2018-12-10 14:53:58 -080085 */
86 private final boolean mHasClientContext;
87
88 // Fields below are set by app on Builder
89 private final @Nullable Bundle mExtras;
Felipe Leme044c63b2019-02-12 14:35:20 -080090 private final @Nullable LocusId mId;
Felipe Lemeaa5088e2018-12-10 14:53:58 -080091
92 // Fields below are set by server when the session starts
Felipe Lemeaa5088e2018-12-10 14:53:58 -080093 private final @Nullable ComponentName mComponentName;
94 private final int mTaskId;
Felipe Lemeaa5088e2018-12-10 14:53:58 -080095 private final int mFlags;
Adam Hebb82c6a2019-01-17 14:59:02 -080096 private final int mDisplayId;
Felipe Lemeaa5088e2018-12-10 14:53:58 -080097
Felipe Leme87a9dc92018-12-18 14:28:07 -080098 // Fields below are set by the service upon "delivery" and are not marshalled in the parcel
99 private @Nullable String mParentSessionId;
100
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800101 /** @hide */
102 public ContentCaptureContext(@Nullable ContentCaptureContext clientContext,
103 @NonNull ComponentName componentName, int taskId, int displayId, int flags) {
104 if (clientContext != null) {
105 mHasClientContext = true;
106 mExtras = clientContext.mExtras;
Felipe Leme044c63b2019-02-12 14:35:20 -0800107 mId = clientContext.mId;
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800108 } else {
109 mHasClientContext = false;
110 mExtras = null;
Felipe Leme044c63b2019-02-12 14:35:20 -0800111 mId = null;
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800112 }
113 mComponentName = Preconditions.checkNotNull(componentName);
114 mTaskId = taskId;
115 mDisplayId = displayId;
116 mFlags = flags;
117 }
118
119 private ContentCaptureContext(@NonNull Builder builder) {
120 mHasClientContext = true;
121 mExtras = builder.mExtras;
Felipe Leme044c63b2019-02-12 14:35:20 -0800122 mId = builder.mId;
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800123
124 mComponentName = null;
Adam Hebb82c6a2019-01-17 14:59:02 -0800125 mTaskId = mFlags = 0;
126 mDisplayId = Display.INVALID_DISPLAY;
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800127 }
128
129 /**
Felipe Leme0143d0c2019-01-22 12:51:22 -0800130 * Gets the (optional) extras set by the app (through {@link Builder#setExtras(Bundle)}).
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800131 *
132 * <p>It can be used to provide vendor-specific data that can be modified and examined.
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800133 */
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800134 @Nullable
135 public Bundle getExtras() {
136 return mExtras;
137 }
138
139 /**
Felipe Leme044c63b2019-02-12 14:35:20 -0800140 * Gets the context id.
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800141 */
Felipe Leme044c63b2019-02-12 14:35:20 -0800142 @NonNull
143 public LocusId getLocusId() {
144 return mId;
Felipe Leme0143d0c2019-01-22 12:51:22 -0800145 }
146
147 /**
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800148 * Gets the id of the {@link TaskInfo task} associated with this context.
149 *
150 * @hide
151 */
152 @SystemApi
Felipe Leme19652c02019-02-04 13:01:29 -0800153 @TestApi
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800154 public int getTaskId() {
155 return mTaskId;
156 }
157
158 /**
Felipe Leme87a9dc92018-12-18 14:28:07 -0800159 * Gets the activity associated with this context, or {@code null} when it is a child session.
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800160 *
161 * @hide
162 */
163 @SystemApi
Felipe Leme19652c02019-02-04 13:01:29 -0800164 @TestApi
Felipe Leme87a9dc92018-12-18 14:28:07 -0800165 public @Nullable ComponentName getActivityComponent() {
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800166 return mComponentName;
167 }
168
169 /**
Felipe Leme87a9dc92018-12-18 14:28:07 -0800170 * Gets the id of the session that originated this session (through
171 * {@link ContentCaptureSession#createContentCaptureSession(ContentCaptureContext)}),
172 * or {@code null} if this is the main session associated with the Activity's {@link Context}.
173 *
174 * @hide
175 */
176 @SystemApi
Felipe Leme19652c02019-02-04 13:01:29 -0800177 @TestApi
Felipe Leme87a9dc92018-12-18 14:28:07 -0800178 public @Nullable ContentCaptureSessionId getParentSessionId() {
179 return mParentSessionId == null ? null : new ContentCaptureSessionId(mParentSessionId);
180 }
181
182 /** @hide */
183 public void setParentSessionId(@NonNull String parentSessionId) {
184 mParentSessionId = parentSessionId;
185 }
186
187 /**
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800188 * Gets the ID of the display associated with this context, as defined by
189 * {G android.hardware.display.DisplayManager#getDisplay(int) DisplayManager.getDisplay()}.
190 *
191 * @hide
192 */
193 @SystemApi
Felipe Leme19652c02019-02-04 13:01:29 -0800194 @TestApi
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800195 public int getDisplayId() {
196 return mDisplayId;
197 }
198
199 /**
200 * Gets the flags associated with this context.
201 *
202 * @return any combination of {@link #FLAG_DISABLED_BY_FLAG_SECURE} and
203 * {@link #FLAG_DISABLED_BY_APP}.
204 *
205 * @hide
206 */
207 @SystemApi
Felipe Leme19652c02019-02-04 13:01:29 -0800208 @TestApi
209 public @ContextCreationFlags int getFlags() {
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800210 return mFlags;
211 }
212
213 /**
Felipe Leme7e735752019-03-01 16:27:24 -0800214 * Helper that creates a {@link ContentCaptureContext} associated with the given {@code id}.
Felipe Leme044c63b2019-02-12 14:35:20 -0800215 */
Felipe Lemece6877b2019-02-28 09:02:26 -0800216 @NonNull
Felipe Leme7e735752019-03-01 16:27:24 -0800217 public static ContentCaptureContext forLocusId(@NonNull String id) {
218 return new Builder(new LocusId(id)).build();
Felipe Leme044c63b2019-02-12 14:35:20 -0800219 }
220
221 /**
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800222 * Builder for {@link ContentCaptureContext} objects.
223 */
224 public static final class Builder {
225 private Bundle mExtras;
Felipe Leme044c63b2019-02-12 14:35:20 -0800226 private final LocusId mId;
Felipe Lemeb67e9492019-01-22 12:21:31 -0800227 private boolean mDestroyed;
Felipe Leme044c63b2019-02-12 14:35:20 -0800228
229 /**
230 * Creates a new builder.
231 *
232 * <p>The context must have an id, which is usually one of the following:
233 *
234 * <ul>
235 * <li>A URL representing a web page (or {@code IFRAME}) that's being rendered by the
236 * activity (See {@link View#setContentCaptureSession(ContentCaptureSession)} for an
237 * example).
238 * <li>A unique identifier of the application state (for example, a conversation between
239 * 2 users in a chat app).
240 *
241 * @param id id associated with this context.
242 */
243 // TODO(b/123577059): make sure this is well documented and understandable
244 public Builder(@NonNull LocusId id) {
245 mId = Preconditions.checkNotNull(id);
246 }
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800247
248 /**
249 * Sets extra options associated with this context.
250 *
251 * <p>It can be used to provide vendor-specific data that can be modified and examined.
252 *
253 * @param extras extra options.
254 * @return this builder.
Felipe Lemeb67e9492019-01-22 12:21:31 -0800255 *
256 * @throws IllegalStateException if {@link #build()} was already called.
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800257 */
258 @NonNull
259 public Builder setExtras(@NonNull Bundle extras) {
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800260 mExtras = Preconditions.checkNotNull(extras);
Felipe Lemeb67e9492019-01-22 12:21:31 -0800261 throwIfDestroyed();
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800262 return this;
263 }
264
265 /**
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800266 * Builds the {@link ContentCaptureContext}.
Felipe Lemeb67e9492019-01-22 12:21:31 -0800267 *
Felipe Leme044c63b2019-02-12 14:35:20 -0800268 * @throws IllegalStateException if {@link #build()} was already called.
Felipe Lemeb67e9492019-01-22 12:21:31 -0800269 *
270 * @return the built {@code ContentCaptureContext}
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800271 */
Felipe Lemece6877b2019-02-28 09:02:26 -0800272 @NonNull
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800273 public ContentCaptureContext build() {
Felipe Lemeb67e9492019-01-22 12:21:31 -0800274 throwIfDestroyed();
Felipe Lemeb67e9492019-01-22 12:21:31 -0800275 mDestroyed = true;
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800276 return new ContentCaptureContext(this);
277 }
Felipe Lemeb67e9492019-01-22 12:21:31 -0800278
279 private void throwIfDestroyed() {
280 Preconditions.checkState(!mDestroyed, "Already called #build()");
281 }
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800282 }
283
284 /**
285 * @hide
286 */
287 // TODO(b/111276913): dump to proto as well
288 public void dump(PrintWriter pw) {
Felipe Leme044c63b2019-02-12 14:35:20 -0800289 if (mComponentName != null) {
290 pw.print("activity="); pw.print(mComponentName.flattenToShortString());
291 }
292 if (mId != null) {
293 pw.print(", id="); mId.dump(pw);
294 }
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800295 pw.print(", taskId="); pw.print(mTaskId);
296 pw.print(", displayId="); pw.print(mDisplayId);
Felipe Leme87a9dc92018-12-18 14:28:07 -0800297 if (mParentSessionId != null) {
298 pw.print(", parentId="); pw.print(mParentSessionId);
299 }
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800300 if (mFlags > 0) {
301 pw.print(", flags="); pw.print(mFlags);
302 }
303 if (mExtras != null) {
304 // NOTE: cannot dump because it could contain PII
305 pw.print(", hasExtras");
306 }
Felipe Leme044c63b2019-02-12 14:35:20 -0800307 }
308
309 private boolean fromServer() {
310 return mComponentName != null;
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800311 }
312
313 @Override
314 public String toString() {
Felipe Leme044c63b2019-02-12 14:35:20 -0800315 final StringBuilder builder = new StringBuilder("Context[");
316
317 if (fromServer()) {
318 builder.append("act=").append(ComponentName.flattenToShortString(mComponentName))
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800319 .append(", taskId=").append(mTaskId)
320 .append(", displayId=").append(mDisplayId)
321 .append(", flags=").append(mFlags);
Felipe Leme044c63b2019-02-12 14:35:20 -0800322 } else {
323 builder.append("id=").append(mId);
324 if (mExtras != null) {
325 // NOTE: cannot print because it could contain PII
326 builder.append(", hasExtras");
327 }
328 }
Felipe Leme87a9dc92018-12-18 14:28:07 -0800329 if (mParentSessionId != null) {
330 builder.append(", parentId=").append(mParentSessionId);
331 }
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800332 return builder.append(']').toString();
333 }
334
335 @Override
336 public int describeContents() {
337 return 0;
338 }
339
340 @Override
341 public void writeToParcel(Parcel parcel, int flags) {
342 parcel.writeInt(mHasClientContext ? 1 : 0);
343 if (mHasClientContext) {
Felipe Leme044c63b2019-02-12 14:35:20 -0800344 parcel.writeParcelable(mId, flags);
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800345 parcel.writeBundle(mExtras);
346 }
347 parcel.writeParcelable(mComponentName, flags);
Felipe Leme044c63b2019-02-12 14:35:20 -0800348 if (fromServer()) {
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800349 parcel.writeInt(mTaskId);
350 parcel.writeInt(mDisplayId);
351 parcel.writeInt(mFlags);
352 }
353 }
354
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -0700355 public static final @android.annotation.NonNull Parcelable.Creator<ContentCaptureContext> CREATOR =
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800356 new Parcelable.Creator<ContentCaptureContext>() {
357
358 @Override
Felipe Lemece6877b2019-02-28 09:02:26 -0800359 @NonNull
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800360 public ContentCaptureContext createFromParcel(Parcel parcel) {
361 final boolean hasClientContext = parcel.readInt() == 1;
362
363 final ContentCaptureContext clientContext;
364 if (hasClientContext) {
Felipe Leme0143d0c2019-01-22 12:51:22 -0800365 // Must reconstruct the client context using the Builder API
Felipe Leme044c63b2019-02-12 14:35:20 -0800366 final LocusId id = parcel.readParcelable(null);
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800367 final Bundle extras = parcel.readBundle();
Felipe Leme044c63b2019-02-12 14:35:20 -0800368 final Builder builder = new Builder(id);
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800369 if (extras != null) builder.setExtras(extras);
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800370 clientContext = new ContentCaptureContext(builder);
371 } else {
372 clientContext = null;
373 }
374 final ComponentName componentName = parcel.readParcelable(null);
375 if (componentName == null) {
376 // Client-state only
377 return clientContext;
378 } else {
379 final int taskId = parcel.readInt();
380 final int displayId = parcel.readInt();
381 final int flags = parcel.readInt();
Felipe Leme87a9dc92018-12-18 14:28:07 -0800382 return new ContentCaptureContext(clientContext, componentName, taskId, displayId,
383 flags);
384 }
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800385 }
386
387 @Override
Felipe Lemece6877b2019-02-28 09:02:26 -0800388 @NonNull
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800389 public ContentCaptureContext[] newArray(int size) {
390 return new ContentCaptureContext[size];
391 }
392 };
393}