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

Commit e127c9a4 authored by Felipe Leme's avatar Felipe Leme
Browse files

Fixed how session created / removed events are generated.

Long-story short, they must be flushed right away...

Test: atest CtsContentCaptureServiceTestCases
Bug: 121033016

Change-Id: I1b3132ad49674d43bf63717f79848b6e4b23b605
parent 7751e68a
Loading
Loading
Loading
Loading
+15 −12
Original line number Diff line number Diff line
@@ -123,12 +123,12 @@ public abstract class ContentCaptureService extends Service {
    };

    /**
     * List of sessions per UID.
     * UIDs associated with each session.
     *
     * <p>This map is populated when an session is started, which is called by the system server
     * and can be trusted. Then subsequent calls made by the app are verified against this map.
     */
    private final ArrayMap<String, Integer> mSessionsByUid = new ArrayMap<>();
    private final ArrayMap<String, Integer> mSessionUids = new ArrayMap<>();

    @CallSuper
    @Override
@@ -285,13 +285,13 @@ public abstract class ContentCaptureService extends Service {
    @Override
    @CallSuper
    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
        final int size = mSessionsByUid.size();
        final int size = mSessionUids.size();
        pw.print("Number sessions: "); pw.println(size);
        if (size > 0) {
            final String prefix = "  ";
            for (int i = 0; i < size; i++) {
                pw.print(prefix); pw.print(mSessionsByUid.keyAt(i));
                pw.print(": uid="); pw.println(mSessionsByUid.valueAt(i));
                pw.print(prefix); pw.print(mSessionUids.keyAt(i));
                pw.print(": uid="); pw.println(mSessionUids.valueAt(i));
            }
        }
    }
@@ -309,7 +309,7 @@ public abstract class ContentCaptureService extends Service {

    private void handleOnCreateSession(@NonNull ContentCaptureContext context,
            @NonNull String sessionId, int uid, IResultReceiver clientReceiver) {
        mSessionsByUid.put(sessionId, uid);
        mSessionUids.put(sessionId, uid);
        onCreateContentCaptureSession(context, new ContentCaptureSessionId(sessionId));
        setClientState(clientReceiver, ContentCaptureSession.STATE_ACTIVE,
                mClientInterface.asBinder());
@@ -336,11 +336,11 @@ public abstract class ContentCaptureService extends Service {
                case ContentCaptureEvent.TYPE_SESSION_STARTED:
                    final ContentCaptureContext clientContext = event.getClientContext();
                    clientContext.setParentSessionId(event.getParentSessionId());
                    mSessionsByUid.put(sessionIdString, uid);
                    mSessionUids.put(sessionIdString, uid);
                    onCreateContentCaptureSession(clientContext, sessionId);
                    break;
                case ContentCaptureEvent.TYPE_SESSION_FINISHED:
                    mSessionsByUid.remove(sessionIdString);
                    mSessionUids.remove(sessionIdString);
                    onDestroyContentCaptureSession(sessionId);
                    break;
                default:
@@ -355,7 +355,7 @@ public abstract class ContentCaptureService extends Service {
    }

    private void handleFinishSession(@NonNull String sessionId) {
        mSessionsByUid.remove(sessionId);
        mSessionUids.remove(sessionId);
        onDestroyContentCaptureSession(new ContentCaptureSessionId(sessionId));
    }

@@ -372,10 +372,13 @@ public abstract class ContentCaptureService extends Service {
            default:
                sessionId = event.getSessionId();
        }
        final Integer rightUid = mSessionsByUid.get(sessionId);
        final Integer rightUid = mSessionUids.get(sessionId);
        if (rightUid == null) {
            if (VERBOSE) Log.v(TAG, "No session for " + sessionId);
            // Just ignore, as the session could have finished
            if (DEBUG) {
                Log.d(TAG, "handleIsRightCallerFor(" + event + "): no session for " + sessionId
                        + ": " + mSessionUids);
            }
            // Just ignore, as the session could have been finished already
            return false;
        }
        if (rightUid != uid) {
+4 −0
Original line number Diff line number Diff line
@@ -251,6 +251,10 @@ public final class ContentCaptureEvent implements Parcelable {
    public String toString() {
        final StringBuilder string = new StringBuilder("ContentCaptureEvent[type=")
                .append(getTypeAsString(mType));
        string.append(", session=").append(mSessionId);
        if (mType == TYPE_SESSION_STARTED && mParentSessionId != null) {
            string.append(", parent=").append(mParentSessionId);
        }
        if (mFlags > 0) {
            string.append(", flags=").append(mFlags);
        }
+5 −5
Original line number Diff line number Diff line
@@ -73,11 +73,11 @@ public abstract class ContentCaptureSession implements AutoCloseable {
    public static final int STATE_ACTIVE = 2;

    /**
     * Session is disabled.
     * Session is disabled because there is no service for this user.
     *
     * @hide
     */
    public static final int STATE_DISABLED = 3;
    public static final int STATE_DISABLED_NO_SERVICE = 3;

    /**
     * Session is disabled because its id already existed on server.
@@ -164,7 +164,7 @@ public abstract class ContentCaptureSession implements AutoCloseable {
     */
    public final void destroy() {
        if (!mDestroyed.compareAndSet(false, true)) {
            Log.e(TAG, "destroy(): already destroyed");
            Log.e(TAG, "destroy(" + mId + "): already destroyed");
            return;
        }

@@ -341,8 +341,8 @@ public abstract class ContentCaptureSession implements AutoCloseable {
                return "WAITING_FOR_SERVER";
            case STATE_ACTIVE:
                return "ACTIVE";
            case STATE_DISABLED:
                return "DISABLED";
            case STATE_DISABLED_NO_SERVICE:
                return "DISABLED_NO_SERVICE";
            case STATE_DISABLED_DUPLICATED_ID:
                return "DISABLED_DUPLICATED_ID";
            default:
+11 −8
Original line number Diff line number Diff line
@@ -171,6 +171,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession {

    @Override
    void onDestroy() {
        mHandler.removeMessages(MSG_FLUSH);
        mHandler.sendMessage(
                obtainMessage(MainContentCaptureSession::handleDestroySession, this));
    }
@@ -237,7 +238,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
                Log.w(TAG, "Failed to link to death on " + binder + ": " + e);
            }
        }
        if (resultCode == STATE_DISABLED || resultCode == STATE_DISABLED_DUPLICATED_ID) {
        if (resultCode == STATE_DISABLED_NO_SERVICE || resultCode == STATE_DISABLED_DUPLICATED_ID) {
            mDisabled.set(true);
            handleResetSession(/* resetState= */ false);
        } else {
@@ -246,7 +247,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
        if (VERBOSE) {
            Log.v(TAG, "handleSessionStarted() result: code=" + resultCode + ", id=" + mId
                    + ", state=" + getStateAsString(mState) + ", disabled=" + mDisabled.get()
                    + ", binder=" + binder);
                    + ", binder=" + binder + ", events=" + (mEvents == null ? 0 : mEvents.size()));
        }
    }

@@ -285,14 +286,14 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
            return;
        }

        if (mState != STATE_ACTIVE) {
        if (mState != STATE_ACTIVE && numberEvents >= MAX_BUFFER_SIZE) {
            // Callback from startSession hasn't been called yet - typically happens on system
            // apps that are started before the system service
            // TODO(b/111276913): try to ignore session while system is not ready / boot
            // not complete instead. Similarly, the manager service should return right away
            // when the user does not have a service set
            if (VERBOSE) {
                Log.v(TAG, "Closing session for " + getActivityDebugName()
            if (DEBUG) {
                Log.d(TAG, "Closing session for " + getActivityDebugName()
                        + " after " + numberEvents + " delayed events and state "
                        + getStateAsString(mState));
            }
@@ -331,7 +332,9 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
        if (mEvents == null) return;

        if (mDirectServiceInterface == null) {
            if (DEBUG) Log.d(TAG, "handleForceFlush(): hold your horses, client not ready yet!");
            if (VERBOSE) {
                Log.v(TAG, "handleForceFlush(): hold your horses, client not ready: " + mEvents);
            }
            if (!mHandler.hasMessages(MSG_FLUSH)) {
                handleScheduleFlush(/* checkExisting= */ false);
            }
@@ -435,14 +438,14 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
                new ContentCaptureEvent(childSessionId, TYPE_SESSION_STARTED)
                        .setParentSessionId(parentSessionId)
                        .setClientContext(clientContext),
                        /* forceFlush= */ false));
                        /* forceFlush= */ true));
    }

    void notifyChildSessionFinished(@NonNull String parentSessionId,
            @NonNull String childSessionId) {
        mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this,
                new ContentCaptureEvent(childSessionId, TYPE_SESSION_FINISHED)
                        .setParentSessionId(parentSessionId), /* forceFlush= */ false));
                        .setParentSessionId(parentSessionId), /* forceFlush= */ true));
    }

    void notifyViewAppeared(@NonNull String sessionId, @NonNull ViewStructureImpl node) {
+8 −5
Original line number Diff line number Diff line
@@ -95,7 +95,7 @@ final class ContentCapturePerUserService
        final ComponentName serviceComponentName = updateServiceInfoLocked();

        if (serviceComponentName == null) {
            Slog.w(TAG, "updateRemoteService(): no service componennt name");
            if (mMaster.debug) Slog.d(TAG, "updateRemoteService(): no service component name");
            return;
        }

@@ -163,7 +163,10 @@ final class ContentCapturePerUserService
            @NonNull String sessionId, int uid, int flags,
            @NonNull IResultReceiver clientReceiver) {
        if (!isEnabledLocked()) {
            setClientState(clientReceiver, ContentCaptureSession.STATE_DISABLED, /* binder=*/ null);
            // TODO: it would be better to split in differet reasons, like
            // STATE_DISABLED_NO_SERVICE and STATE_DISABLED_BY_DEVICE_POLICY
            setClientState(clientReceiver, ContentCaptureSession.STATE_DISABLED_NO_SERVICE,
                    /* binder= */ null);
            return;
        }
        final ComponentName serviceComponentName = getServiceComponentName();
@@ -195,7 +198,7 @@ final class ContentCapturePerUserService
            Slog.w(TAG, "startSession(id=" + existingSession + ", token=" + activityToken
                    + ": ignoring because service is not set");
            // TODO(b/111276913): use a new disabled state?
            setClientState(clientReceiver, ContentCaptureSession.STATE_DISABLED,
            setClientState(clientReceiver, ContentCaptureSession.STATE_DISABLED_NO_SERVICE,
                    /* binder= */ null);
            return;
        }
@@ -285,7 +288,7 @@ final class ContentCapturePerUserService
        final int numSessions = mSessions.size();
        for (int i = 0; i < numSessions; i++) {
            final ContentCaptureServerSession session = mSessions.valueAt(i);
            session.destroyLocked(true);
            session.destroyLocked(/* notifyRemoteService= */ true);
        }
        mSessions.clear();
    }