blob: 8bb4d210ad4424c8907027a3c4255a1199977849 [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.net.Uri;
28import android.os.Bundle;
29import android.os.Parcel;
30import android.os.Parcelable;
Adam Hebb82c6a2019-01-17 14:59:02 -080031import android.view.Display;
Felipe Lemeaa5088e2018-12-10 14:53:58 -080032import android.view.View;
33
34import com.android.internal.util.Preconditions;
35
36import java.io.PrintWriter;
37import java.lang.annotation.Retention;
38import java.lang.annotation.RetentionPolicy;
39
40/**
41 * Context associated with a {@link ContentCaptureSession}.
42 */
43public final class ContentCaptureContext implements Parcelable {
44
45 /*
46 * IMPLEMENTATION NOTICE:
47 *
48 * This object contains both the info that's explicitly added by apps (hence it's public), but
49 * it also contains info injected by the server (and are accessible through @SystemApi methods).
50 */
51
52 /**
53 * Flag used to indicate that the app explicitly disabled content capture for the activity
54 * (using
55 * {@link android.view.contentcapture.ContentCaptureManager#setContentCaptureEnabled(boolean)}),
56 * in which case the service will just receive activity-level events.
57 *
58 * @hide
59 */
60 @SystemApi
Felipe Leme19652c02019-02-04 13:01:29 -080061 @TestApi
Felipe Lemeaa5088e2018-12-10 14:53:58 -080062 public static final int FLAG_DISABLED_BY_APP = 0x1;
63
64 /**
65 * Flag used to indicate that the activity's window is tagged with
66 * {@link android.view.Display#FLAG_SECURE}, in which case the service will just receive
67 * activity-level events.
68 *
69 * @hide
70 */
71 @SystemApi
Felipe Leme19652c02019-02-04 13:01:29 -080072 @TestApi
Felipe Lemeaa5088e2018-12-10 14:53:58 -080073 public static final int FLAG_DISABLED_BY_FLAG_SECURE = 0x2;
74
75 /** @hide */
76 @IntDef(flag = true, prefix = { "FLAG_" }, value = {
77 FLAG_DISABLED_BY_APP,
78 FLAG_DISABLED_BY_FLAG_SECURE
79 })
80 @Retention(RetentionPolicy.SOURCE)
81 @interface ContextCreationFlags{}
82
83 /**
84 * Flag indicating if this object has the app-provided context (which is set on
Felipe Leme87a9dc92018-12-18 14:28:07 -080085 * {@link ContentCaptureSession#createContentCaptureSession(ContentCaptureContext)}).
Felipe Lemeaa5088e2018-12-10 14:53:58 -080086 */
87 private final boolean mHasClientContext;
88
89 // Fields below are set by app on Builder
90 private final @Nullable Bundle mExtras;
Felipe Leme044c63b2019-02-12 14:35:20 -080091 private final @Nullable LocusId mId;
Felipe Lemeaa5088e2018-12-10 14:53:58 -080092
93 // Fields below are set by server when the session starts
Felipe Lemeaa5088e2018-12-10 14:53:58 -080094 private final @Nullable ComponentName mComponentName;
95 private final int mTaskId;
Felipe Lemeaa5088e2018-12-10 14:53:58 -080096 private final int mFlags;
Adam Hebb82c6a2019-01-17 14:59:02 -080097 private final int mDisplayId;
Felipe Lemeaa5088e2018-12-10 14:53:58 -080098
Felipe Leme87a9dc92018-12-18 14:28:07 -080099 // Fields below are set by the service upon "delivery" and are not marshalled in the parcel
100 private @Nullable String mParentSessionId;
101
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800102 /** @hide */
103 public ContentCaptureContext(@Nullable ContentCaptureContext clientContext,
104 @NonNull ComponentName componentName, int taskId, int displayId, int flags) {
105 if (clientContext != null) {
106 mHasClientContext = true;
107 mExtras = clientContext.mExtras;
Felipe Leme044c63b2019-02-12 14:35:20 -0800108 mId = clientContext.mId;
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800109 } else {
110 mHasClientContext = false;
111 mExtras = null;
Felipe Leme044c63b2019-02-12 14:35:20 -0800112 mId = null;
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800113 }
114 mComponentName = Preconditions.checkNotNull(componentName);
115 mTaskId = taskId;
116 mDisplayId = displayId;
117 mFlags = flags;
118 }
119
120 private ContentCaptureContext(@NonNull Builder builder) {
121 mHasClientContext = true;
122 mExtras = builder.mExtras;
Felipe Leme044c63b2019-02-12 14:35:20 -0800123 mId = builder.mId;
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800124
125 mComponentName = null;
Adam Hebb82c6a2019-01-17 14:59:02 -0800126 mTaskId = mFlags = 0;
127 mDisplayId = Display.INVALID_DISPLAY;
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800128 }
129
130 /**
Felipe Leme0143d0c2019-01-22 12:51:22 -0800131 * Gets the (optional) extras set by the app (through {@link Builder#setExtras(Bundle)}).
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800132 *
133 * <p>It can be used to provide vendor-specific data that can be modified and examined.
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800134 */
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800135 @Nullable
136 public Bundle getExtras() {
137 return mExtras;
138 }
139
140 /**
Felipe Leme044c63b2019-02-12 14:35:20 -0800141 * Gets the context id.
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800142 */
Felipe Leme044c63b2019-02-12 14:35:20 -0800143 @NonNull
144 public LocusId getLocusId() {
145 return mId;
Felipe Leme0143d0c2019-01-22 12:51:22 -0800146 }
147
148 /**
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800149 * Gets the id of the {@link TaskInfo task} associated with this context.
150 *
151 * @hide
152 */
153 @SystemApi
Felipe Leme19652c02019-02-04 13:01:29 -0800154 @TestApi
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800155 public int getTaskId() {
156 return mTaskId;
157 }
158
159 /**
Felipe Leme87a9dc92018-12-18 14:28:07 -0800160 * Gets the activity associated with this context, or {@code null} when it is a child session.
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800161 *
162 * @hide
163 */
164 @SystemApi
Felipe Leme19652c02019-02-04 13:01:29 -0800165 @TestApi
Felipe Leme87a9dc92018-12-18 14:28:07 -0800166 public @Nullable ComponentName getActivityComponent() {
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800167 return mComponentName;
168 }
169
170 /**
Felipe Leme87a9dc92018-12-18 14:28:07 -0800171 * Gets the id of the session that originated this session (through
172 * {@link ContentCaptureSession#createContentCaptureSession(ContentCaptureContext)}),
173 * or {@code null} if this is the main session associated with the Activity's {@link Context}.
174 *
175 * @hide
176 */
177 @SystemApi
Felipe Leme19652c02019-02-04 13:01:29 -0800178 @TestApi
Felipe Leme87a9dc92018-12-18 14:28:07 -0800179 public @Nullable ContentCaptureSessionId getParentSessionId() {
180 return mParentSessionId == null ? null : new ContentCaptureSessionId(mParentSessionId);
181 }
182
183 /** @hide */
184 public void setParentSessionId(@NonNull String parentSessionId) {
185 mParentSessionId = parentSessionId;
186 }
187
188 /**
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800189 * Gets the ID of the display associated with this context, as defined by
190 * {G android.hardware.display.DisplayManager#getDisplay(int) DisplayManager.getDisplay()}.
191 *
192 * @hide
193 */
194 @SystemApi
Felipe Leme19652c02019-02-04 13:01:29 -0800195 @TestApi
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800196 public int getDisplayId() {
197 return mDisplayId;
198 }
199
200 /**
201 * Gets the flags associated with this context.
202 *
203 * @return any combination of {@link #FLAG_DISABLED_BY_FLAG_SECURE} and
204 * {@link #FLAG_DISABLED_BY_APP}.
205 *
206 * @hide
207 */
208 @SystemApi
Felipe Leme19652c02019-02-04 13:01:29 -0800209 @TestApi
210 public @ContextCreationFlags int getFlags() {
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800211 return mFlags;
212 }
213
214 /**
Felipe Leme044c63b2019-02-12 14:35:20 -0800215 * Helper that creates a {@link ContentCaptureContext} associated with the given {@code uri}.
216 */
217 public static ContentCaptureContext forLocusId(@NonNull Uri uri) {
218 return new Builder(new LocusId(uri)).build();
219 }
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 */
272 public ContentCaptureContext build() {
Felipe Lemeb67e9492019-01-22 12:21:31 -0800273 throwIfDestroyed();
Felipe Lemeb67e9492019-01-22 12:21:31 -0800274 mDestroyed = true;
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800275 return new ContentCaptureContext(this);
276 }
Felipe Lemeb67e9492019-01-22 12:21:31 -0800277
278 private void throwIfDestroyed() {
279 Preconditions.checkState(!mDestroyed, "Already called #build()");
280 }
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800281 }
282
283 /**
284 * @hide
285 */
286 // TODO(b/111276913): dump to proto as well
287 public void dump(PrintWriter pw) {
Felipe Leme044c63b2019-02-12 14:35:20 -0800288 if (mComponentName != null) {
289 pw.print("activity="); pw.print(mComponentName.flattenToShortString());
290 }
291 if (mId != null) {
292 pw.print(", id="); mId.dump(pw);
293 }
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800294 pw.print(", taskId="); pw.print(mTaskId);
295 pw.print(", displayId="); pw.print(mDisplayId);
Felipe Leme87a9dc92018-12-18 14:28:07 -0800296 if (mParentSessionId != null) {
297 pw.print(", parentId="); pw.print(mParentSessionId);
298 }
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800299 if (mFlags > 0) {
300 pw.print(", flags="); pw.print(mFlags);
301 }
302 if (mExtras != null) {
303 // NOTE: cannot dump because it could contain PII
304 pw.print(", hasExtras");
305 }
Felipe Leme044c63b2019-02-12 14:35:20 -0800306 }
307
308 private boolean fromServer() {
309 return mComponentName != null;
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800310 }
311
312 @Override
313 public String toString() {
Felipe Leme044c63b2019-02-12 14:35:20 -0800314 final StringBuilder builder = new StringBuilder("Context[");
315
316 if (fromServer()) {
317 builder.append("act=").append(ComponentName.flattenToShortString(mComponentName))
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800318 .append(", taskId=").append(mTaskId)
319 .append(", displayId=").append(mDisplayId)
320 .append(", flags=").append(mFlags);
Felipe Leme044c63b2019-02-12 14:35:20 -0800321 } else {
322 builder.append("id=").append(mId);
323 if (mExtras != null) {
324 // NOTE: cannot print because it could contain PII
325 builder.append(", hasExtras");
326 }
327 }
Felipe Leme87a9dc92018-12-18 14:28:07 -0800328 if (mParentSessionId != null) {
329 builder.append(", parentId=").append(mParentSessionId);
330 }
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800331 return builder.append(']').toString();
332 }
333
334 @Override
335 public int describeContents() {
336 return 0;
337 }
338
339 @Override
340 public void writeToParcel(Parcel parcel, int flags) {
341 parcel.writeInt(mHasClientContext ? 1 : 0);
342 if (mHasClientContext) {
Felipe Leme044c63b2019-02-12 14:35:20 -0800343 parcel.writeParcelable(mId, flags);
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800344 parcel.writeBundle(mExtras);
345 }
346 parcel.writeParcelable(mComponentName, flags);
Felipe Leme044c63b2019-02-12 14:35:20 -0800347 if (fromServer()) {
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800348 parcel.writeInt(mTaskId);
349 parcel.writeInt(mDisplayId);
350 parcel.writeInt(mFlags);
351 }
352 }
353
354 public static final Parcelable.Creator<ContentCaptureContext> CREATOR =
355 new Parcelable.Creator<ContentCaptureContext>() {
356
357 @Override
358 public ContentCaptureContext createFromParcel(Parcel parcel) {
359 final boolean hasClientContext = parcel.readInt() == 1;
360
361 final ContentCaptureContext clientContext;
362 if (hasClientContext) {
Felipe Leme0143d0c2019-01-22 12:51:22 -0800363 // Must reconstruct the client context using the Builder API
Felipe Leme044c63b2019-02-12 14:35:20 -0800364 final LocusId id = parcel.readParcelable(null);
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800365 final Bundle extras = parcel.readBundle();
Felipe Leme044c63b2019-02-12 14:35:20 -0800366 final Builder builder = new Builder(id);
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800367 if (extras != null) builder.setExtras(extras);
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800368 clientContext = new ContentCaptureContext(builder);
369 } else {
370 clientContext = null;
371 }
372 final ComponentName componentName = parcel.readParcelable(null);
373 if (componentName == null) {
374 // Client-state only
375 return clientContext;
376 } else {
377 final int taskId = parcel.readInt();
378 final int displayId = parcel.readInt();
379 final int flags = parcel.readInt();
Felipe Leme87a9dc92018-12-18 14:28:07 -0800380 return new ContentCaptureContext(clientContext, componentName, taskId, displayId,
381 flags);
382 }
Felipe Lemeaa5088e2018-12-10 14:53:58 -0800383 }
384
385 @Override
386 public ContentCaptureContext[] newArray(int size) {
387 return new ContentCaptureContext[size];
388 }
389 };
390}