diff options
| author | Felipe Leme <felipeal@google.com> | 2018-12-18 14:28:07 -0800 |
|---|---|---|
| committer | Felipe Leme <felipeal@google.com> | 2018-12-20 08:59:48 -0800 |
| commit | 87a9dc9a976b042cbcf84572ee240f2a1898104c (patch) | |
| tree | e41c1b51c9b65492c41db26ec5902b81220d423f /core/java/android | |
| parent | 5acec68de4f998c38e0e8cb31e280171c6c4cfcf (diff) | |
Moved createContentCaptureSession() to ContentCaptureSession.
Such move will allow nested sessions. For example, WebView could create a
new session for the main page, then child sessions for IFRAMEs contained on it.
This CL changes the API and provides an initial implementation, although it's
not quite ready yet - it only allows 1 level of children (from the activity
session), but the full implementation is coming soom to a movie theather near
you...
Bug: 121033016
Bug: 117944706
Test: atest CtsContentCaptureServiceTestCases
Change-Id: I86156bb3b8a2c08cb00b9518599eb6d67fbf77c2
Diffstat (limited to 'core/java/android')
| -rw-r--r-- | core/java/android/service/contentcapture/ContentCaptureService.java | 34 | ||||
| -rw-r--r-- | core/java/android/view/View.java | 11 | ||||
| -rw-r--r-- | core/java/android/view/contentcapture/ChildContentCaptureSession.java | 99 | ||||
| -rw-r--r-- | core/java/android/view/contentcapture/ContentCaptureContext.java | 39 | ||||
| -rw-r--r-- | core/java/android/view/contentcapture/ContentCaptureEvent.java | 74 | ||||
| -rw-r--r-- | core/java/android/view/contentcapture/ContentCaptureManager.java | 42 | ||||
| -rw-r--r-- | core/java/android/view/contentcapture/ContentCaptureSession.java | 83 | ||||
| -rw-r--r-- | core/java/android/view/contentcapture/IContentCaptureManager.aidl | 3 | ||||
| -rw-r--r-- | core/java/android/view/contentcapture/MainContentCaptureSession.java (renamed from core/java/android/view/contentcapture/ActivityContentCaptureSession.java) | 94 |
9 files changed, 379 insertions, 100 deletions
diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java index c5e62f1dfe60..64f235546201 100644 --- a/core/java/android/service/contentcapture/ContentCaptureService.java +++ b/core/java/android/service/contentcapture/ContentCaptureService.java @@ -34,13 +34,13 @@ import android.os.RemoteException; import android.util.ArrayMap; import android.util.Log; import android.util.Slog; -import android.view.contentcapture.ActivityContentCaptureSession; import android.view.contentcapture.ContentCaptureContext; import android.view.contentcapture.ContentCaptureEvent; import android.view.contentcapture.ContentCaptureManager; import android.view.contentcapture.ContentCaptureSession; import android.view.contentcapture.ContentCaptureSessionId; import android.view.contentcapture.IContentCaptureDirectManager; +import android.view.contentcapture.MainContentCaptureSession; import com.android.internal.os.IResultReceiver; @@ -293,13 +293,26 @@ public abstract class ContentCaptureService extends Service { final List<ContentCaptureEvent> events = parceledEvents.getList(); for (int i = 0; i < events.size(); i++) { final ContentCaptureEvent event = events.get(i); + if (!handleIsRightCallerFor(event, uid)) continue; String sessionIdString = event.getSessionId(); if (!sessionIdString.equals(lastSessionId)) { - if (!handleIsRightCallerFor(sessionIdString, uid)) continue; sessionId = new ContentCaptureSessionId(sessionIdString); lastSessionId = sessionIdString; } - onContentCaptureEvent(sessionId, event); + switch (event.getType()) { + case ContentCaptureEvent.TYPE_SESSION_STARTED: + final ContentCaptureContext clientContext = event.getClientContext(); + clientContext.setParentSessionId(event.getParentSessionId()); + mSessionsByUid.put(sessionIdString, uid); + onCreateContentCaptureSession(clientContext, sessionId); + break; + case ContentCaptureEvent.TYPE_SESSION_FINISHED: + mSessionsByUid.remove(sessionIdString); + onDestroyContentCaptureSession(sessionId); + break; + default: + onContentCaptureEvent(sessionId, event); + } } } @@ -314,9 +327,18 @@ public abstract class ContentCaptureService extends Service { } /** - * Checks if the given {@code uid} owns the session. + * Checks if the given {@code uid} owns the session associated with the event. */ - private boolean handleIsRightCallerFor(@NonNull String sessionId, int uid) { + private boolean handleIsRightCallerFor(@NonNull ContentCaptureEvent event, int uid) { + final String sessionId; + switch (event.getType()) { + case ContentCaptureEvent.TYPE_SESSION_STARTED: + case ContentCaptureEvent.TYPE_SESSION_FINISHED: + sessionId = event.getParentSessionId(); + break; + default: + sessionId = event.getSessionId(); + } final Integer rightUid = mSessionsByUid.get(sessionId); if (rightUid == null) { if (VERBOSE) Log.v(TAG, "No session for " + sessionId); @@ -347,7 +369,7 @@ public abstract class ContentCaptureService extends Service { final Bundle extras; if (binder != null) { extras = new Bundle(); - extras.putBinder(ActivityContentCaptureSession.EXTRA_BINDER, binder); + extras.putBinder(MainContentCaptureSession.EXTRA_BINDER, binder); } else { extras = null; } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 468d92290c13..69cd3e6487c9 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -9046,17 +9046,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * {@code onCreate()} and associate it with the root view of the activity: * * <pre> - * ContentCaptureManager mgr = getSystemService(ContentCaptureManager.class); - * if (mgr != null && mgr.isContentCaptureEnabled()) { - * View rootView = findViewById(R.my_root_view); - * ContentCaptureSession session = mgr.createContentCaptureSession(new + * ContentCaptureSession oldSession = rootView.getContentCaptureSession(); + * if (oldSession != null) { + * ContentCaptureSession newSession = oldSession.createContentCaptureSession(new * ContentCaptureContext.Builder().setUri(myUrl).build()); - * rootView.setContentCaptureSession(session); + * rootView.setContentCaptureSession(newSession); * } * </pre> * * @param contentCaptureSession a session created by - * {@link ContentCaptureManager#createContentCaptureSession( + * {@link ContentCaptureSession#createContentCaptureSession( * android.view.contentcapture.ContentCaptureContext)}. */ public void setContentCaptureSession(@NonNull ContentCaptureSession contentCaptureSession) { diff --git a/core/java/android/view/contentcapture/ChildContentCaptureSession.java b/core/java/android/view/contentcapture/ChildContentCaptureSession.java new file mode 100644 index 000000000000..51668319cde2 --- /dev/null +++ b/core/java/android/view/contentcapture/ChildContentCaptureSession.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.view.contentcapture; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.view.autofill.AutofillId; +import android.view.contentcapture.ViewNode.ViewStructureImpl; + +import com.android.internal.util.Preconditions; + +import java.io.PrintWriter; + +/** + * A session that is explicitly created by the app (and hence is a descendant of + * {@link MainContentCaptureSession}). + * + * @hide + */ +final class ChildContentCaptureSession extends ContentCaptureSession { + + @NonNull + private final MainContentCaptureSession mParent; + + /** + * {@link ContentCaptureContext} set by client, or {@code null} when it's the + * {@link ContentCaptureManager#getMainContentCaptureSession() default session} for the + * context. + * + * @hide + */ + @NonNull + private final ContentCaptureContext mClientContext; + + /** @hide */ + protected ChildContentCaptureSession(@NonNull MainContentCaptureSession parent, + @NonNull ContentCaptureContext clientContext) { + mParent = parent; + mClientContext = Preconditions.checkNotNull(clientContext); + } + + @Override + ContentCaptureSession newChild(@NonNull ContentCaptureContext context) { + // TODO(b/121033016): implement it + throw new UnsupportedOperationException("grand-children not implemented yet"); + } + + @Override + void flush() { + mParent.flush(); + } + + @Override + void onDestroy() { + mParent.notifyChildSessionFinished(mParent.mId, mId); + } + + @Override + void internalNotifyViewAppeared(@NonNull ViewStructureImpl node) { + mParent.notifyViewAppeared(mId, node); + } + + @Override + void internalNotifyViewDisappeared(@NonNull AutofillId id) { + mParent.notifyViewDisappeared(mId, id); + } + + @Override + void internalNotifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text, + int flags) { + mParent.notifyViewTextChanged(mId, id, text, flags); + } + @Override + boolean isContentCaptureEnabled() { + return mParent.isContentCaptureEnabled(); + } + + @Override + void dump(String prefix, PrintWriter pw) { + if (mClientContext != null) { + // NOTE: we don't dump clientContent because it could have PII + pw.print(prefix); pw.println("hasClientContext"); + } + super.dump(prefix, pw); + } +} diff --git a/core/java/android/view/contentcapture/ContentCaptureContext.java b/core/java/android/view/contentcapture/ContentCaptureContext.java index 9c11743fdf19..2d2987a035da 100644 --- a/core/java/android/view/contentcapture/ContentCaptureContext.java +++ b/core/java/android/view/contentcapture/ContentCaptureContext.java @@ -21,6 +21,7 @@ import android.annotation.Nullable; import android.annotation.SystemApi; import android.app.TaskInfo; import android.content.ComponentName; +import android.content.Context; import android.net.Uri; import android.os.Bundle; import android.os.Parcel; @@ -76,7 +77,7 @@ public final class ContentCaptureContext implements Parcelable { /** * Flag indicating if this object has the app-provided context (which is set on - * {@link ContentCaptureManager#createContentCaptureSession(ContentCaptureContext)}). + * {@link ContentCaptureSession#createContentCaptureSession(ContentCaptureContext)}). */ private final boolean mHasClientContext; @@ -91,6 +92,9 @@ public final class ContentCaptureContext implements Parcelable { private final int mDisplayId; private final int mFlags; + // Fields below are set by the service upon "delivery" and are not marshalled in the parcel + private @Nullable String mParentSessionId; + /** @hide */ public ContentCaptureContext(@Nullable ContentCaptureContext clientContext, @NonNull ComponentName componentName, int taskId, int displayId, int flags) { @@ -153,16 +157,33 @@ public final class ContentCaptureContext implements Parcelable { } /** - * Gets the activity associated with this context. + * Gets the activity associated with this context, or {@code null} when it is a child session. * * @hide */ @SystemApi - public @NonNull ComponentName getActivityComponent() { + public @Nullable ComponentName getActivityComponent() { return mComponentName; } /** + * Gets the id of the session that originated this session (through + * {@link ContentCaptureSession#createContentCaptureSession(ContentCaptureContext)}), + * or {@code null} if this is the main session associated with the Activity's {@link Context}. + * + * @hide + */ + @SystemApi + public @Nullable ContentCaptureSessionId getParentSessionId() { + return mParentSessionId == null ? null : new ContentCaptureSessionId(mParentSessionId); + } + + /** @hide */ + public void setParentSessionId(@NonNull String parentSessionId) { + mParentSessionId = parentSessionId; + } + + /** * Gets the ID of the display associated with this context, as defined by * {G android.hardware.display.DisplayManager#getDisplay(int) DisplayManager.getDisplay()}. * @@ -242,6 +263,9 @@ public final class ContentCaptureContext implements Parcelable { pw.print("comp="); pw.print(ComponentName.flattenToShortString(mComponentName)); pw.print(", taskId="); pw.print(mTaskId); pw.print(", displayId="); pw.print(mDisplayId); + if (mParentSessionId != null) { + pw.print(", parentId="); pw.print(mParentSessionId); + } if (mFlags > 0) { pw.print(", flags="); pw.print(mFlags); } @@ -262,6 +286,9 @@ public final class ContentCaptureContext implements Parcelable { .append(", taskId=").append(mTaskId) .append(", displayId=").append(mDisplayId) .append(", flags=").append(mFlags); + if (mParentSessionId != null) { + builder.append(", parentId=").append(mParentSessionId); + } if (mExtras != null) { // NOTE: cannot print because it could contain PII builder.append(", hasExtras"); @@ -320,9 +347,9 @@ public final class ContentCaptureContext implements Parcelable { final int taskId = parcel.readInt(); final int displayId = parcel.readInt(); final int flags = parcel.readInt(); - return new ContentCaptureContext(clientContext, componentName, taskId, - displayId, flags); - } + return new ContentCaptureContext(clientContext, componentName, taskId, displayId, + flags); + } } @Override diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java index 43c9699b54b8..9e3da9234b58 100644 --- a/core/java/android/view/contentcapture/ContentCaptureEvent.java +++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java @@ -34,9 +34,9 @@ import java.lang.annotation.RetentionPolicy; public final class ContentCaptureEvent implements Parcelable { /** @hide */ - public static final int TYPE_ACTIVITY_DESTROYED = -2; + public static final int TYPE_SESSION_FINISHED = -2; /** @hide */ - public static final int TYPE_ACTIVITY_CREATED = -1; + public static final int TYPE_SESSION_STARTED = -1; /** * Called when a node has been added to the screen and is visible to the user. @@ -78,6 +78,8 @@ public final class ContentCaptureEvent implements Parcelable { private @Nullable AutofillId mId; private @Nullable ViewNode mNode; private @Nullable CharSequence mText; + private @Nullable String mParentSessionId; + private @Nullable ContentCaptureContext mClientContext; /** @hide */ public ContentCaptureEvent(@NonNull String sessionId, int type, long eventTime, int flags) { @@ -103,12 +105,52 @@ public final class ContentCaptureEvent implements Parcelable { return this; } + /** + * Used by {@link #TYPE_SESSION_STARTED} and {@link #TYPE_SESSION_FINISHED}. + * + * @hide + */ + public ContentCaptureEvent setParentSessionId(@NonNull String parentSessionId) { + mParentSessionId = parentSessionId; + return this; + } + + /** + * Used by {@link #TYPE_SESSION_STARTED} and {@link #TYPE_SESSION_FINISHED}. + * + * @hide + */ + public ContentCaptureEvent setClientContext(@NonNull ContentCaptureContext clientContext) { + mClientContext = clientContext; + return this; + } + /** @hide */ @NonNull public String getSessionId() { return mSessionId; } + /** + * Used by {@link #TYPE_SESSION_STARTED} and {@link #TYPE_SESSION_FINISHED}. + * + * @hide + */ + @Nullable + public String getParentSessionId() { + return mParentSessionId; + } + + /** + * Used by {@link #TYPE_SESSION_STARTED}. + * + * @hide + */ + @Nullable + public ContentCaptureContext getClientContext() { + return mClientContext; + } + /** @hide */ @NonNull public ContentCaptureEvent setViewNode(@NonNull ViewNode node) { @@ -191,7 +233,17 @@ public final class ContentCaptureEvent implements Parcelable { pw.print(", id="); pw.print(mId); } if (mNode != null) { - pw.print(", id="); pw.print(mNode.getAutofillId()); + pw.print(", mNode.id="); pw.print(mNode.getAutofillId()); + } + if (mSessionId != null) { + pw.print(", sessionId="); pw.print(mSessionId); + } + if (mParentSessionId != null) { + pw.print(", parentSessionId="); pw.print(mParentSessionId); + } + if (mText != null) { + // Cannot print content because could have PII + pw.print(", text="); pw.print(mText.length()); pw.print("_chars"); } } @@ -229,6 +281,12 @@ public final class ContentCaptureEvent implements Parcelable { parcel.writeParcelable(mId, flags); ViewNode.writeToParcel(parcel, mNode, flags); parcel.writeCharSequence(mText); + if (mType == TYPE_SESSION_STARTED || mType == TYPE_SESSION_FINISHED) { + parcel.writeString(mParentSessionId); + } + if (mType == TYPE_SESSION_STARTED) { + parcel.writeParcelable(mClientContext, flags); + } } public static final Parcelable.Creator<ContentCaptureEvent> CREATOR = @@ -251,6 +309,12 @@ public final class ContentCaptureEvent implements Parcelable { event.setViewNode(node); } event.setText(parcel.readCharSequence()); + if (type == TYPE_SESSION_STARTED || type == TYPE_SESSION_FINISHED) { + event.setParentSessionId(parcel.readString()); + } + if (type == TYPE_SESSION_STARTED) { + event.setClientContext(parcel.readParcelable(null)); + } return event; } @@ -263,6 +327,10 @@ public final class ContentCaptureEvent implements Parcelable { /** @hide */ public static String getTypeAsString(@EventType int type) { switch (type) { + case TYPE_SESSION_STARTED: + return "SESSION_STARTED"; + case TYPE_SESSION_FINISHED: + return "SESSION_FINISHED"; case TYPE_VIEW_APPEARED: return "VIEW_APPEARED"; case TYPE_VIEW_DISAPPEARED: diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java index fca2857d6e06..983079073d02 100644 --- a/core/java/android/view/contentcapture/ContentCaptureManager.java +++ b/core/java/android/view/contentcapture/ContentCaptureManager.java @@ -18,13 +18,13 @@ package android.view.contentcapture; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemService; +import android.annotation.UiThread; import android.content.ComponentName; import android.content.Context; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; import android.util.Log; -import android.view.View; import com.android.internal.util.Preconditions; @@ -66,7 +66,7 @@ public final class ContentCaptureManager { @NonNull private final Handler mHandler; - private ActivityContentCaptureSession mMainSession; + private MainContentCaptureSession mMainSession; /** @hide */ public ContentCaptureManager(@NonNull Context context, @@ -93,46 +93,20 @@ public final class ContentCaptureManager { } /** - * Creates a new {@link ContentCaptureSession}. - * - * <p>See {@link View#setContentCaptureSession(ContentCaptureSession)} for more info. - */ - @NonNull - public ContentCaptureSession createContentCaptureSession( - @NonNull ContentCaptureContext context) { - if (DEBUG) Log.d(TAG, "createContentCaptureSession(): " + context); - // TODO(b/121033016): for now we're updating the main session, but we need instead: - // 1.Keep a list of sessions - // 2.Making sure the applicationToken and componentName passed by - // the activity is used on all of these sessions - // 3.We might also need to delay the start of all of these sessions until - // onActivityStarted() is called (and the main session is created). - // 4.Close (and delete) these sessions when onActivityStopped() is called. - // 5.Figure out whether each session will have its own mDisabled AtomicBoolean. - if (mMainSession == null) { - mMainSession = new ActivityContentCaptureSession(mContext, mHandler, mService, - mDisabled, Preconditions.checkNotNull(context)); - } else { - throw new IllegalStateException("Manager already has a session: " + mMainSession); - } - return mMainSession; - } - - /** * Gets the main session associated with the context. * * <p>By default there's just one (associated with the activity lifecycle), but apps could - * explicitly add more using {@link #createContentCaptureSession(ContentCaptureContext)}. + * explicitly add more using + * {@link ContentCaptureSession#createContentCaptureSession(ContentCaptureContext)}. * * @hide */ @NonNull - public ActivityContentCaptureSession getMainContentCaptureSession() { - // TODO(b/121033016): figure out how to manage the "default" session when it support - // multiple sessions (can't just be the first one, as it could be closed). + @UiThread + public MainContentCaptureSession getMainContentCaptureSession() { if (mMainSession == null) { - mMainSession = new ActivityContentCaptureSession(mContext, mHandler, mService, - mDisabled, /* clientContext= */ null); + mMainSession = new MainContentCaptureSession(mContext, mHandler, mService, + mDisabled); if (VERBOSE) { Log.v(TAG, "getDefaultContentCaptureSession(): created " + mMainSession); } diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java index aedb7a94ff5d..9f666a40bef3 100644 --- a/core/java/android/view/contentcapture/ContentCaptureSession.java +++ b/core/java/android/view/contentcapture/ContentCaptureSession.java @@ -15,8 +15,10 @@ */ package android.view.contentcapture; +import static android.view.contentcapture.ContentCaptureManager.DEBUG; import static android.view.contentcapture.ContentCaptureManager.VERBOSE; +import android.annotation.CallSuper; import android.annotation.NonNull; import android.annotation.Nullable; import android.util.Log; @@ -30,6 +32,7 @@ import com.android.internal.util.Preconditions; import dalvik.system.CloseGuard; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.UUID; /** @@ -80,6 +83,8 @@ public abstract class ContentCaptureSession implements AutoCloseable { */ public static final int STATE_DISABLED_DUPLICATED_ID = 4; + private static final int INITIAL_CHILDREN_CAPACITY = 5; + /** @hide */ protected final String mTag = getClass().getSimpleName(); @@ -95,19 +100,17 @@ public abstract class ContentCaptureSession implements AutoCloseable { private ContentCaptureSessionId mContentCaptureSessionId; /** - * {@link ContentCaptureContext} set by client, or {@code null} when it's the - * {@link ContentCaptureManager#getMainContentCaptureSession() default session} for the - * context. - * - * @hide + * List of children session. */ + // TODO(b/121033016): need to synchonize access, either by changing on handler or UI thread + // (for now there's no handler on this class, so we need to wait for the next refactoring), + // most likely the former (as we have no guarantee that createContentCaptureSession() + // it will be called in the UiThread; for example, WebView most likely won't call on it) @Nullable - // TODO(b/121042846): move to ChildContentCaptureSession.java - protected final ContentCaptureContext mClientContext; + private ArrayList<ContentCaptureSession> mChildren; /** @hide */ - protected ContentCaptureSession(@Nullable ContentCaptureContext clientContext) { - mClientContext = clientContext; + protected ContentCaptureSession() { mCloseGuard.open("destroy"); } @@ -122,6 +125,28 @@ public abstract class ContentCaptureSession implements AutoCloseable { } /** + * Creates a new {@link ContentCaptureSession}. + * + * <p>See {@link View#setContentCaptureSession(ContentCaptureSession)} for more info. + */ + @NonNull + public final ContentCaptureSession createContentCaptureSession( + @NonNull ContentCaptureContext context) { + final ContentCaptureSession child = newChild(context); + if (DEBUG) { + Log.d(mTag, "createContentCaptureSession(" + context + ": parent=" + mId + ", child= " + + child.mId); + } + if (mChildren == null) { + mChildren = new ArrayList<>(INITIAL_CHILDREN_CAPACITY); + } + mChildren.add(child); + return child; + } + + abstract ContentCaptureSession newChild(@NonNull ContentCaptureContext context); + + /** * Flushes the buffered events to the service. */ abstract void flush(); @@ -134,24 +159,40 @@ public abstract class ContentCaptureSession implements AutoCloseable { public final void destroy() { //TODO(b/111276913): mark it as destroyed so other methods are ignored (and test on CTS) + //TODO(b/111276913): probably shouldn't check for it if (!isContentCaptureEnabled()) return; + mCloseGuard.close(); + //TODO(b/111276913): check state (for example, how to handle if it's waiting for remote // id) and send it to the cache of batched commands if (VERBOSE) { Log.v(mTag, "destroy(): state=" + getStateAsString(mState) + ", mId=" + mId); } - flush(); - - onDestroy(); + // Finish children first + if (mChildren != null) { + final int numberChildren = mChildren.size(); + if (VERBOSE) Log.v(mTag, "Destroying " + numberChildren + " children first"); + for (int i = 0; i < numberChildren; i++) { + final ContentCaptureSession child = mChildren.get(i); + try { + child.destroy(); + } catch (Exception e) { + Log.w(mTag, "exception destroying child session #" + i + ": " + e); + } + } + } - mCloseGuard.close(); + try { + flush(); + } finally { + onDestroy(); + } } abstract void onDestroy(); - /** @hide */ @Override public void close() { @@ -259,7 +300,19 @@ public abstract class ContentCaptureSession implements AutoCloseable { abstract boolean isContentCaptureEnabled(); - abstract void dump(@NonNull String prefix, @NonNull PrintWriter pw); + @CallSuper + void dump(@NonNull String prefix, @NonNull PrintWriter pw) { + if (mChildren != null && !mChildren.isEmpty()) { + final String prefix2 = prefix + " "; + final int numberChildren = mChildren.size(); + pw.print(prefix); pw.print("number children: "); pw.print(numberChildren); + for (int i = 0; i < numberChildren; i++) { + final ContentCaptureSession child = mChildren.get(i); + pw.print(prefix); pw.print(i); pw.println(": "); child.dump(prefix2, pw); + } + } + + } @Override public String toString() { diff --git a/core/java/android/view/contentcapture/IContentCaptureManager.aidl b/core/java/android/view/contentcapture/IContentCaptureManager.aidl index cbd37017038e..01776f846434 100644 --- a/core/java/android/view/contentcapture/IContentCaptureManager.aidl +++ b/core/java/android/view/contentcapture/IContentCaptureManager.aidl @@ -33,7 +33,6 @@ import java.util.List; */ oneway interface IContentCaptureManager { void startSession(int userId, IBinder activityToken, in ComponentName componentName, - String sessionId, in ContentCaptureContext clientContext, int flags, - in IResultReceiver result); + String sessionId, int flags, in IResultReceiver result); void finishSession(int userId, String sessionId); } diff --git a/core/java/android/view/contentcapture/ActivityContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java index a8f3e0bbd45d..ea6f2fe16ef6 100644 --- a/core/java/android/view/contentcapture/ActivityContentCaptureSession.java +++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java @@ -15,6 +15,8 @@ */ package android.view.contentcapture; +import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_FINISHED; +import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_STARTED; import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_APPEARED; import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_DISAPPEARED; import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED; @@ -59,7 +61,7 @@ import java.util.concurrent.atomic.AtomicBoolean; * * @hide */ -public final class ActivityContentCaptureSession extends ContentCaptureSession { +public final class MainContentCaptureSession extends ContentCaptureSession { /** * Handler message used to flush the buffer. @@ -129,18 +131,23 @@ public final class ActivityContentCaptureSession extends ContentCaptureSession { // Lazily created on demand. private ContentCaptureSessionId mContentCaptureSessionId; - /** - * @hide */ - protected ActivityContentCaptureSession(@NonNull Context context, @NonNull Handler handler, - @Nullable IContentCaptureManager systemServerInterface, @NonNull AtomicBoolean disabled, - @Nullable ContentCaptureContext clientContext) { - super(clientContext); + /** @hide */ + protected MainContentCaptureSession(@NonNull Context context, @NonNull Handler handler, + @Nullable IContentCaptureManager systemServerInterface, + @NonNull AtomicBoolean disabled) { mContext = context; mHandler = handler; mSystemServerInterface = systemServerInterface; mDisabled = disabled; } + @Override + ContentCaptureSession newChild(@NonNull ContentCaptureContext clientContext) { + final ContentCaptureSession child = new ChildContentCaptureSession(this, clientContext); + notifyChildSessionStarted(mId, child.mId, clientContext); + return child; + } + /** * Starts this session. * @@ -154,19 +161,19 @@ public final class ActivityContentCaptureSession extends ContentCaptureSession { + ComponentName.flattenToShortString(activityComponent)); } - mHandler.sendMessage(obtainMessage(ActivityContentCaptureSession::handleStartSession, this, + mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleStartSession, this, applicationToken, activityComponent)); } @Override void flush() { - mHandler.sendMessage(obtainMessage(ActivityContentCaptureSession::handleForceFlush, this)); + mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleForceFlush, this)); } @Override void onDestroy() { mHandler.sendMessage( - obtainMessage(ActivityContentCaptureSession::handleDestroySession, this)); + obtainMessage(MainContentCaptureSession::handleDestroySession, this)); } private void handleStartSession(@NonNull IBinder token, @NonNull ComponentName componentName) { @@ -188,7 +195,7 @@ public final class ActivityContentCaptureSession extends ContentCaptureSession { try { mSystemServerInterface.startSession(mContext.getUserId(), mApplicationToken, - componentName, mId, mClientContext, flags, new IResultReceiver.Stub() { + componentName, mId, flags, new IResultReceiver.Stub() { @Override public void send(int resultCode, Bundle resultData) { IBinder binder = null; @@ -296,7 +303,7 @@ public final class ActivityContentCaptureSession extends ContentCaptureSession { Log.v(mTag, "Scheduled to flush in " + FLUSHING_FREQUENCY_MS + "ms: " + mNextFlush); } mHandler.sendMessageDelayed( - obtainMessage(ActivityContentCaptureSession::handleFlushIfNeeded, this) + obtainMessage(MainContentCaptureSession::handleFlushIfNeeded, this) .setWhat(MSG_FLUSH), FLUSHING_FREQUENCY_MS); } @@ -312,7 +319,7 @@ public final class ActivityContentCaptureSession extends ContentCaptureSession { if (mEvents == null) return; if (mDirectServiceInterface == null) { - Log.w(mTag, "handleForceFlush(): client not available yet"); + if (DEBUG) Log.d(mTag, "handleForceFlush(): hold your horses, client not ready yet!"); if (!mHandler.hasMessages(MSG_FLUSH)) { handleScheduleFlush(/* checkExisting= */ false); } @@ -367,12 +374,15 @@ public final class ActivityContentCaptureSession extends ContentCaptureSession { handleResetSession(/* resetState= */ true); } - // TODO(b/121042846): once we support multiple sessions, we might need to move some of these + // TODO(b/121033016): once we support multiple sessions, we might need to move some of these // clearings out. private void handleResetSession(boolean resetState) { if (resetState) { mState = STATE_UNKNOWN; } + + // TODO(b/121033016): must reset children (which currently is owned by superclass) + mContentCaptureSessionId = null; mApplicationToken = null; mComponentName = null; @@ -386,24 +396,18 @@ public final class ActivityContentCaptureSession extends ContentCaptureSession { @Override void internalNotifyViewAppeared(@NonNull ViewStructureImpl node) { - mHandler.sendMessage(obtainMessage(ActivityContentCaptureSession::handleSendEvent, this, - new ContentCaptureEvent(mId, TYPE_VIEW_APPEARED) - .setViewNode(node.mNode), /* forceFlush= */ false)); + notifyViewAppeared(mId, node); } @Override void internalNotifyViewDisappeared(@NonNull AutofillId id) { - mHandler.sendMessage(obtainMessage(ActivityContentCaptureSession::handleSendEvent, this, - new ContentCaptureEvent(mId, TYPE_VIEW_DISAPPEARED).setAutofillId(id), - /* forceFlush= */ false)); + notifyViewDisappeared(mId, id); } @Override void internalNotifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text, int flags) { - mHandler.sendMessage(obtainMessage(ActivityContentCaptureSession::handleSendEvent, this, - new ContentCaptureEvent(mId, TYPE_VIEW_TEXT_CHANGED, flags).setAutofillId(id) - .setText(text), /* forceFlush= */ false)); + notifyViewTextChanged(mId, id, text, flags); } @Override @@ -411,6 +415,44 @@ public final class ActivityContentCaptureSession extends ContentCaptureSession { return mSystemServerInterface != null && !mDisabled.get(); } + // TODO(b/121033016): refactor "notifyXXXX" methods below to a common "Buffer" object that is + // shared between ActivityContentCaptureSession and ChildContentCaptureSession objects. Such + // change should also get get rid of the "internalNotifyXXXX" methods above + void notifyChildSessionStarted(@NonNull String parentSessionId, + @NonNull String childSessionId, @NonNull ContentCaptureContext clientContext) { + mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this, + new ContentCaptureEvent(childSessionId, TYPE_SESSION_STARTED) + .setParentSessionId(parentSessionId) + .setClientContext(clientContext), + /* forceFlush= */ false)); + } + + void notifyChildSessionFinished(@NonNull String parentSessionId, + @NonNull String childSessionId) { + mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this, + new ContentCaptureEvent(childSessionId, TYPE_SESSION_FINISHED) + .setParentSessionId(parentSessionId), /* forceFlush= */ false)); + } + + void notifyViewAppeared(@NonNull String sessionId, @NonNull ViewStructureImpl node) { + mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this, + new ContentCaptureEvent(sessionId, TYPE_VIEW_APPEARED) + .setViewNode(node.mNode), /* forceFlush= */ false)); + } + + void notifyViewDisappeared(@NonNull String sessionId, @NonNull AutofillId id) { + mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this, + new ContentCaptureEvent(sessionId, TYPE_VIEW_DISAPPEARED).setAutofillId(id), + /* forceFlush= */ false)); + } + + void notifyViewTextChanged(@NonNull String sessionId, @NonNull AutofillId id, + @Nullable CharSequence text, int flags) { + mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this, + new ContentCaptureEvent(sessionId, TYPE_VIEW_TEXT_CHANGED, flags).setAutofillId(id) + .setText(text), /* forceFlush= */ false)); + } + @Override void dump(@NonNull String prefix, @NonNull PrintWriter pw) { pw.print(prefix); pw.print("id: "); pw.println(mId); @@ -424,11 +466,6 @@ public final class ActivityContentCaptureSession extends ContentCaptureSession { pw.print(prefix); pw.print("mDirectServiceInterface: "); pw.println(mDirectServiceInterface); } - if (mClientContext != null) { - // NOTE: we don't dump clientContent because it could have PII - pw.print(prefix); pw.println("hasClientContext"); - - } pw.print(prefix); pw.print("mDisabled: "); pw.println(mDisabled.get()); pw.print(prefix); pw.print("isEnabled(): "); pw.println(isContentCaptureEnabled()); if (mContentCaptureSessionId != null) { @@ -459,6 +496,7 @@ public final class ActivityContentCaptureSession extends ContentCaptureSession { pw.print(prefix); pw.print("next flush: "); TimeUtils.formatDuration(mNextFlush - SystemClock.elapsedRealtime(), pw); pw.println(); } + super.dump(prefix, pw); } /** |
