Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4bc0f6be authored by Felipe Leme's avatar Felipe Leme
Browse files

Implemented nested Content Capture sessions.

Test: atest CtsContentCaptureServiceTestCases
Fixes: 121033016

Change-Id: I46bbd05c363cbda8b66704203455411d38c6a025
parent e127c9a4
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -34,7 +34,7 @@ import java.util.List;
@SystemApi
@Deprecated
public final class ContentCaptureEventsRequest implements Parcelable {
// TODO(b/121033016): remove .java and .aidl once service implementation doesn't use it anymore
// TODO(b/121051220): remove .java and .aidl once service implementation doesn't use it anymore

    private final ContentCaptureEvent mEvent;

+19 −10
Original line number Diff line number Diff line
@@ -33,7 +33,7 @@ import java.io.PrintWriter;
final class ChildContentCaptureSession extends ContentCaptureSession {

    @NonNull
    private final MainContentCaptureSession mParent;
    private final ContentCaptureSession mParent;

    /**
     * {@link ContentCaptureContext} set by client, or {@code null} when it's the
@@ -46,16 +46,25 @@ final class ChildContentCaptureSession extends ContentCaptureSession {
    private final ContentCaptureContext mClientContext;

    /** @hide */
    protected ChildContentCaptureSession(@NonNull MainContentCaptureSession parent,
    protected ChildContentCaptureSession(@NonNull ContentCaptureSession 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");
    MainContentCaptureSession getMainCaptureSession() {
        if (mParent instanceof MainContentCaptureSession) {
            return (MainContentCaptureSession) mParent;
        }
        return mParent.getMainCaptureSession();
    }

    @Override
    ContentCaptureSession newChild(@NonNull ContentCaptureContext clientContext) {
        final ContentCaptureSession child = new ChildContentCaptureSession(this, clientContext);
        getMainCaptureSession().notifyChildSessionStarted(mId, child.mId, clientContext);
        return child;
    }

    @Override
@@ -65,27 +74,27 @@ final class ChildContentCaptureSession extends ContentCaptureSession {

    @Override
    void onDestroy() {
        mParent.notifyChildSessionFinished(mParent.mId, mId);
        getMainCaptureSession().notifyChildSessionFinished(mParent.mId, mId);
    }

    @Override
    void internalNotifyViewAppeared(@NonNull ViewStructureImpl node) {
        mParent.notifyViewAppeared(mId, node);
        getMainCaptureSession().notifyViewAppeared(mId, node);
    }

    @Override
    void internalNotifyViewDisappeared(@NonNull AutofillId id) {
        mParent.notifyViewDisappeared(mId, id);
        getMainCaptureSession().notifyViewDisappeared(mId, id);
    }

    @Override
    void internalNotifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text,
            int flags) {
        mParent.notifyViewTextChanged(mId, id, text, flags);
        getMainCaptureSession().notifyViewTextChanged(mId, id, text, flags);
    }
    @Override
    boolean isContentCaptureEnabled() {
        return mParent.isContentCaptureEnabled();
        return getMainCaptureSession().isContentCaptureEnabled();
    }

    @Override
+0 −3
Original line number Diff line number Diff line
@@ -117,13 +117,11 @@ public final class ContentCaptureManager {
    /** @hide */
    public void onActivityStarted(@NonNull IBinder applicationToken,
            @NonNull ComponentName activityComponent) {
        // TODO(b/121033016): must start all sessions
        getMainContentCaptureSession().start(applicationToken, activityComponent);
    }

    /** @hide */
    public void onActivityStopped() {
        // TODO(b/121033016): must finish all sessions
        getMainContentCaptureSession().destroy();
    }

@@ -135,7 +133,6 @@ public final class ContentCaptureManager {
     * @hide
     */
    public void flush() {
        // TODO(b/121033016): must flush all sessions
        getMainContentCaptureSession().flush();
    }

+51 −40
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import android.view.ViewStructure;
import android.view.autofill.AutofillId;
import android.view.contentcapture.ViewNode.ViewStructureImpl;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;

import dalvik.system.CloseGuard;
@@ -34,7 +35,6 @@ import dalvik.system.CloseGuard;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * Session used to notify a system-provided Content Capture service about events associated with
@@ -90,11 +90,14 @@ public abstract class ContentCaptureSession implements AutoCloseable {

    private final CloseGuard mCloseGuard = CloseGuard.get();

    private final Object mLock = new Object();

    /**
     * Guard use to ignore events after it's destroyed.
     */
    @NonNull
    private final AtomicBoolean mDestroyed = new AtomicBoolean();
    @GuardedBy("mLock")
    private boolean mDestroyed;

    /** @hide */
    @Nullable
@@ -108,11 +111,8 @@ public abstract class ContentCaptureSession implements AutoCloseable {
    /**
     * 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
    @GuardedBy("mLock")
    private ArrayList<ContentCaptureSession> mChildren;

    /** @hide */
@@ -120,6 +120,10 @@ public abstract class ContentCaptureSession implements AutoCloseable {
        mCloseGuard.open("destroy");
    }

    /** @hide */
    @NonNull
    abstract MainContentCaptureSession getMainCaptureSession();

    /**
     * Gets the id used to identify this session.
     */
@@ -143,10 +147,12 @@ public abstract class ContentCaptureSession implements AutoCloseable {
            Log.d(TAG, "createContentCaptureSession(" + context + ": parent=" + mId + ", child="
                    + child.mId);
        }
        synchronized (mLock) {
            if (mChildren == null) {
                mChildren = new ArrayList<>(INITIAL_CHILDREN_CAPACITY);
            }
            mChildren.add(child);
        }
        return child;
    }

@@ -163,10 +169,12 @@ public abstract class ContentCaptureSession implements AutoCloseable {
     * <p>Once destroyed, any new notification will be dropped.
     */
    public final void destroy() {
        if (!mDestroyed.compareAndSet(false, true)) {
        synchronized (mLock) {
            if (mDestroyed) {
                Log.e(TAG, "destroy(" + mId + "): already destroyed");
                return;
            }
            mDestroyed = true;

            mCloseGuard.close();

@@ -175,7 +183,6 @@ public abstract class ContentCaptureSession implements AutoCloseable {
            if (VERBOSE) {
                Log.v(TAG, "destroy(): state=" + getStateAsString(mState) + ", mId=" + mId);
            }

            // Finish children first
            if (mChildren != null) {
                final int numberChildren = mChildren.size();
@@ -189,6 +196,7 @@ public abstract class ContentCaptureSession implements AutoCloseable {
                    }
                }
            }
        }

        try {
            flush();
@@ -305,13 +313,16 @@ public abstract class ContentCaptureSession implements AutoCloseable {
    }

    boolean isContentCaptureEnabled() {
        return !mDestroyed.get();
        synchronized (mLock) {
            return !mDestroyed;
        }
    }

    @CallSuper
    void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
        pw.print(prefix); pw.print("id: "); pw.println(mId);
        pw.print(prefix); pw.print("destroyed: "); pw.println(mDestroyed.get());
        synchronized (mLock) {
            pw.print(prefix); pw.print("destroyed: "); pw.println(mDestroyed);
            if (mChildren != null && !mChildren.isEmpty()) {
                final String prefix2 = prefix + "  ";
                final int numberChildren = mChildren.size();
@@ -321,7 +332,7 @@ public abstract class ContentCaptureSession implements AutoCloseable {
                    pw.print(prefix); pw.print(i); pw.println(": "); child.dump(prefix2, pw);
                }
            }

        }
    }

    @Override
+8 −3
Original line number Diff line number Diff line
@@ -140,6 +140,11 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
        mDisabled = disabled;
    }

    @Override
    MainContentCaptureSession getMainCaptureSession() {
        return this;
    }

    @Override
    ContentCaptureSession newChild(@NonNull ContentCaptureContext clientContext) {
        final ContentCaptureSession child = new ChildContentCaptureSession(this, clientContext);
@@ -389,14 +394,14 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
        handleResetSession(/* resetState= */ true);
    }

    // TODO(b/121033016): once we support multiple sessions, we might need to move some of these
    // TODO(b/122454205): 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)
        // TODO(b/122454205): must reset children (which currently is owned by superclass)
        mApplicationToken = null;
        mComponentName = null;
        mEvents = null;
@@ -429,7 +434,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
                && !mDisabled.get();
    }

    // TODO(b/121033016): refactor "notifyXXXX" methods below to a common "Buffer" object that is
    // TODO(b/122454205): 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,