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

Commit 9b50b2b2 authored by Cosmin Băieș's avatar Cosmin Băieș
Browse files

Track ImeTrackerService active capacity

This adds a maximum capacity for the number of simultaneously active
requests in ImeTrackerService. When this capacity is reached, updates
for currently active requests are allowed, however no new entries can be
created to start new active requests, until the capacity clears. This
will protect against too many repeated requests in a short time frame.

This also increases the maximum capacity for completed entries, such
that we can track a slightly longer history. This also helps in
verifying against updates on already completed tokens.

Flag: EXEMPT bugfix
Test: atest ImeTrackerServiceTest#testCannotCreateWhenActiveFull
Bug: 342111149
Change-Id: I83d1f7af540d61cc73410b667d6f97733a16f5c8
parent 4208d046
Loading
Loading
Loading
Loading
+34 −4
Original line number Diff line number Diff line
@@ -133,6 +133,11 @@ public final class ImeTrackerService extends IImeTracker.Stub {
                    complete(id, entry);
                }
            } else {
                if (mHistory.isActiveFull()) {
                    log("%s: onStart while active entries are full, not tracking request", tag);
                    return;
                }

                final var newEntry = new History.Entry();
                // Prefer current time to startTime when creating the entry in onStart.
                newEntry.onStart(tag, uid, type, origin, reason, fromUser,
@@ -165,6 +170,11 @@ public final class ImeTrackerService extends IImeTracker.Stub {
                    return;
                }

                if (mHistory.isActiveFull()) {
                    log("%s: onProgress while active entries are full, not tracking request", tag);
                    return;
                }

                final var newEntry = new History.Entry();
                newEntry.mTag = tag;
                newEntry.onProgress(phase);
@@ -217,6 +227,14 @@ public final class ImeTrackerService extends IImeTracker.Stub {
                return;
            }

            if (mHistory.isActiveFull()) {
                log("%s: onFinished at %s with %s while active entries are full,"
                        + " not tracking request", tag,
                        ImeTracker.Debug.phaseToString(phase),
                        ImeTracker.Debug.statusToString(status));
                return;
            }

            final var newEntry = new History.Entry();
            newEntry.mTag = tag;
            newEntry.onFinish(status, phase);
@@ -384,20 +402,26 @@ public final class ImeTrackerService extends IImeTracker.Stub {
    static final class History {

        /** The maximum number of completed entries to store in {@link #mCompletedEntries}. */
        private static final int CAPACITY = 100;
        private static final int COMPLETED_CAPACITY = 200;

        /**
         * The maximum number of active entries to track in {@link #mActiveEntries} simultaneously.
         */
        @VisibleForTesting
        static final int ACTIVE_CAPACITY = 1000;

        /**
         * Circular buffer of completed requests, mapped by their unique ID. The buffer has a fixed
         * capacity ({@link #CAPACITY}), with older entries overwritten when the capacity
         * capacity ({@link #COMPLETED_CAPACITY}), with older entries overwritten when the capacity
         * is reached.
         */
        @GuardedBy("mLock")
        private final LinkedHashMap<Long, History.Entry> mCompletedEntries =
                new LinkedHashMap<>(CAPACITY) {
                new LinkedHashMap<>(COMPLETED_CAPACITY) {

                    @Override
                    protected boolean removeEldestEntry(Map.Entry<Long, History.Entry> eldest) {
                        return size() > CAPACITY;
                        return size() > COMPLETED_CAPACITY;
                    }
                };

@@ -460,6 +484,12 @@ public final class ImeTrackerService extends IImeTracker.Stub {
            return mCompletedEntries.containsKey(id);
        }

        /** Checks whether the collection of active entries is full. */
        @GuardedBy("mLock")
        boolean isActiveFull() {
            return mActiveEntries.size() >= ACTIVE_CAPACITY;
        }

        /**
         * Dump the internal state to the history to the given print writer.
         *
+70 −0
Original line number Diff line number Diff line
@@ -834,6 +834,76 @@ public class ImeTrackerServiceTest {
                origin, reason, ImeTracker.PHASE_SERVER_SHOULD_HIDE, fromUser);
    }

    /**
     * Verifies that no new request entries can be created when the collection of active entries
     * is full.
     */
    @Test
    public void testCannotCreateWhenActiveFull() {
        ImeTracker.Token token = null;
        final int uid = 10;
        final int type = ImeTracker.TYPE_SHOW;
        final int origin = ImeTracker.ORIGIN_CLIENT;
        final int reason = SoftInputShowHideReason.SHOW_SOFT_INPUT;
        final boolean fromUser = false;

        synchronized (mService.mLock) {
            for (int id = 0; id < History.ACTIVE_CAPACITY; id++) {
                final var tag = "tag#" + id;
                token = new ImeTracker.Token(id, tag);
                mService.onStart(token, uid, type, origin, reason, fromUser,
                        System.currentTimeMillis());
                assertWithMessage("Created entry").that(mHistory.getActive(id)).isNotNull();
            }

            assertWithMessage("Active entries should be full")
                    .that(mHistory.isActiveFull()).isTrue();
        }

        final var otherId = -1;
        final var otherTag = "B";
        final var otherToken = new ImeTracker.Token(otherId, otherTag);
        final int otherType = ImeTracker.TYPE_HIDE;

        mService.onStart(otherToken, uid, otherType, origin, reason, fromUser,
                System.currentTimeMillis());
        synchronized (mService.mLock) {
            assertWithMessage(
                    "Other entry should not be created in onStart when active entries are full")
                    .that(mHistory.getActive(otherId)).isNull();
        }

        mService.onProgress(otherToken, ImeTracker.PHASE_SERVER_HAS_IME);
        synchronized (mService.mLock) {
            assertWithMessage(
                    "Other entry should not be created in onProgress when active entries are full")
                    .that(mHistory.getActive(otherId)).isNull();
        }

        mService.onHidden(otherToken);
        synchronized (mService.mLock) {
            assertWithMessage(
                    "Other entry should not be created in onHidden when active entries are full")
                    .that(mHistory.getActive(otherId)).isNull();
        }

        mService.onShown(token);
        synchronized (mService.mLock) {
            assertWithMessage("Last created entry was completed")
                    .that(mHistory.getActive(token.getId())).isNull();
            assertWithMessage("Active entries should no longer be full")
                    .that(mHistory.isActiveFull()).isFalse();
        }

        mService.onStart(otherToken, uid, otherType, origin, reason, fromUser,
                System.currentTimeMillis());
        synchronized (mService.mLock) {
            assertWithMessage(
                    "Other entry should be created in onStart when active entries are not full")
                    .that(mHistory.getActive(otherId)).isNotNull();
        }
    }

    /**
     * Advances the time on the test handler by the specified amount.
     *