Loading services/core/java/com/android/server/inputmethod/ImeTrackerService.java +34 −4 Original line number Diff line number Diff line Loading @@ -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, Loading Loading @@ -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); Loading Loading @@ -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); Loading Loading @@ -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; } }; Loading Loading @@ -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. * Loading services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeTrackerServiceTest.java +70 −0 Original line number Diff line number Diff line Loading @@ -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. * Loading Loading
services/core/java/com/android/server/inputmethod/ImeTrackerService.java +34 −4 Original line number Diff line number Diff line Loading @@ -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, Loading Loading @@ -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); Loading Loading @@ -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); Loading Loading @@ -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; } }; Loading Loading @@ -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. * Loading
services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeTrackerServiceTest.java +70 −0 Original line number Diff line number Diff line Loading @@ -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. * Loading