diff options
| author | 2023-12-06 17:11:11 +0000 | |
|---|---|---|
| committer | 2023-12-06 17:11:11 +0000 | |
| commit | bd647513604b20ed0a28bd4e35bd68a97fc46791 (patch) | |
| tree | 4c664c0ac7384d7da8effdd322200266ec453fca | |
| parent | e86d12460e9179200d195cf133e2bc2ca9326343 (diff) | |
| parent | 55c532525b4a20773c733049cda110fbe8d74568 (diff) | |
Merge "Move AnrTimer to com.android.server.util" into main
| -rw-r--r-- | services/core/Android.bp | 1 | ||||
| -rw-r--r-- | services/core/java/com/android/server/am/ActiveServices.java | 1 | ||||
| -rw-r--r-- | services/core/java/com/android/server/am/ActivityManagerService.java | 1 | ||||
| -rw-r--r-- | services/core/java/com/android/server/am/BroadcastQueueModernImpl.java | 1 | ||||
| -rw-r--r-- | services/core/java/com/android/server/am/flags.aconfig | 8 | ||||
| -rw-r--r-- | services/core/java/com/android/server/utils/Android.bp | 10 | ||||
| -rw-r--r-- | services/core/java/com/android/server/utils/AnrTimer.java (renamed from services/core/java/com/android/server/am/AnrTimer.java) | 20 | ||||
| -rw-r--r-- | services/core/java/com/android/server/utils/flags.aconfig | 9 | ||||
| -rw-r--r-- | services/tests/servicestests/src/com/android/server/am/AnrTimerTest.java | 389 | ||||
| -rw-r--r-- | services/tests/servicestests/src/com/android/server/utils/AnrTimerTest.java | 203 |
10 files changed, 236 insertions, 407 deletions
diff --git a/services/core/Android.bp b/services/core/Android.bp index 8ed3fd696bda..b4cf34e00c53 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -201,6 +201,7 @@ java_library_static { "biometrics_flags_lib", "am_flags_lib", "com_android_wm_shell_flags_lib", + "com.android.server.utils_aconfig-java", "service-jobscheduler-deviceidle.flags-aconfig-java", ], javac_shard_size: 50, diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 5f1a7e7e8123..71916843fe0b 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -241,6 +241,7 @@ import com.android.server.am.LowMemDetector.MemFactor; import com.android.server.am.ServiceRecord.ShortFgsInfo; import com.android.server.pm.KnownPackages; import com.android.server.uri.NeededUriGrants; +import com.android.server.utils.AnrTimer; import com.android.server.wm.ActivityServiceConnectionsHolder; import java.io.FileDescriptor; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 469f209eb9b5..2d687de2f734 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -475,6 +475,7 @@ import com.android.server.sdksandbox.SdkSandboxManagerLocal; import com.android.server.uri.GrantUri; import com.android.server.uri.NeededUriGrants; import com.android.server.uri.UriGrantsManagerInternal; +import com.android.server.utils.AnrTimer; import com.android.server.utils.PriorityDump; import com.android.server.utils.Slogf; import com.android.server.utils.TimingsTraceAndSlog; diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java index ad499911f84a..2cac7a020005 100644 --- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java +++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java @@ -88,6 +88,7 @@ import com.android.internal.util.FrameworkStatsLog; import com.android.server.am.BroadcastProcessQueue.BroadcastConsumer; import com.android.server.am.BroadcastProcessQueue.BroadcastPredicate; import com.android.server.am.BroadcastRecord.DeliveryState; +import com.android.server.utils.AnrTimer; import dalvik.annotation.optimization.NeverCompile; diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig index 2ed079ab0c62..d9e8dddddae4 100644 --- a/services/core/java/com/android/server/am/flags.aconfig +++ b/services/core/java/com/android/server/am/flags.aconfig @@ -9,14 +9,6 @@ flag { } flag { - name: "anr_timer_service_enabled" - namespace: "system_performance" - is_fixed_read_only: true - description: "Feature flag for the ANR timer service" - bug: "282428924" -} - -flag { name: "fgs_abuse_detection" namespace: "backstage_power" description: "Detect abusive FGS behavior for certain types (camera, mic, media, location)." diff --git a/services/core/java/com/android/server/utils/Android.bp b/services/core/java/com/android/server/utils/Android.bp new file mode 100644 index 000000000000..3a334bee93ff --- /dev/null +++ b/services/core/java/com/android/server/utils/Android.bp @@ -0,0 +1,10 @@ +aconfig_declarations { + name: "com.android.server.utils-aconfig", + package: "com.android.server.utils", + srcs: ["*.aconfig"], +} + +java_aconfig_library { + name: "com.android.server.utils_aconfig-java", + aconfig_declarations: "com.android.server.utils-aconfig", +} diff --git a/services/core/java/com/android/server/am/AnrTimer.java b/services/core/java/com/android/server/utils/AnrTimer.java index 3e17930e3cb9..2b6dffb2b271 100644 --- a/services/core/java/com/android/server/am/AnrTimer.java +++ b/services/core/java/com/android/server/utils/AnrTimer.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.am; +package com.android.server.utils; import static android.text.TextUtils.formatSimple; @@ -77,7 +77,7 @@ import java.util.concurrent.atomic.AtomicInteger; * * @hide */ -class AnrTimer<V> { +public class AnrTimer<V> { /** * The log tag. @@ -568,7 +568,7 @@ class AnrTimer<V> { * @param label A name for this instance. * @param extend A flag to indicate if expired timers can be granted extensions. */ - AnrTimer(@NonNull Handler handler, int what, @NonNull String label, boolean extend) { + public AnrTimer(@NonNull Handler handler, int what, @NonNull String label, boolean extend) { this(handler, what, label, extend, new Injector(handler)); } @@ -580,7 +580,7 @@ class AnrTimer<V> { * @param what The "what" parameter for the expiration message. * @param label A name for this instance. */ - AnrTimer(@NonNull Handler handler, int what, @NonNull String label) { + public AnrTimer(@NonNull Handler handler, int what, @NonNull String label) { this(handler, what, label, false); } @@ -591,7 +591,7 @@ class AnrTimer<V> { * * @return true if the service is flag-enabled. */ - boolean serviceEnabled() { + public boolean serviceEnabled() { return mFeature.enabled(); } @@ -856,7 +856,7 @@ class AnrTimer<V> { * @param timeoutMs The timer timeout, in milliseconds. * @return true if the timer was successfully created. */ - boolean start(@NonNull V arg, int pid, int uid, long timeoutMs) { + public boolean start(@NonNull V arg, int pid, int uid, long timeoutMs) { return mFeature.start(arg, pid, uid, timeoutMs); } @@ -867,7 +867,7 @@ class AnrTimer<V> { * * @return true if the timer was found and was running. */ - boolean cancel(@NonNull V arg) { + public boolean cancel(@NonNull V arg) { return mFeature.cancel(arg); } @@ -878,7 +878,7 @@ class AnrTimer<V> { * * @return true if the timer was found and was expired. */ - boolean accept(@NonNull V arg) { + public boolean accept(@NonNull V arg) { return mFeature.accept(arg); } @@ -892,7 +892,7 @@ class AnrTimer<V> { * * @return true if the timer was found and was expired. */ - boolean discard(@NonNull V arg) { + public boolean discard(@NonNull V arg) { return mFeature.discard(arg); } @@ -1010,7 +1010,7 @@ class AnrTimer<V> { /** * Dumpsys output. */ - static void dump(@NonNull PrintWriter pw, boolean verbose) { + public static void dump(@NonNull PrintWriter pw, boolean verbose) { final IndentingPrintWriter ipw = new IndentingPrintWriter(pw); ipw.println("AnrTimer statistics"); ipw.increaseIndent(); diff --git a/services/core/java/com/android/server/utils/flags.aconfig b/services/core/java/com/android/server/utils/flags.aconfig new file mode 100644 index 000000000000..489e21ab06ca --- /dev/null +++ b/services/core/java/com/android/server/utils/flags.aconfig @@ -0,0 +1,9 @@ +package: "com.android.server.utils" + +flag { + name: "anr_timer_service_enabled" + namespace: "system_performance" + is_fixed_read_only: true + description: "Feature flag for the ANR timer service" + bug: "282428924" +} diff --git a/services/tests/servicestests/src/com/android/server/am/AnrTimerTest.java b/services/tests/servicestests/src/com/android/server/am/AnrTimerTest.java deleted file mode 100644 index 44d676052352..000000000000 --- a/services/tests/servicestests/src/com/android/server/am/AnrTimerTest.java +++ /dev/null @@ -1,389 +0,0 @@ -/* - * Copyright (C) 2023 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 com.android.server.am; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import android.platform.test.annotations.Presubmit; - -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.os.SystemClock; - -import android.util.Log; - -import androidx.test.filters.SmallTest; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import java.util.ArrayList; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -/** - * Build/Install/Run: - * atest FrameworksServicesTests:AnrTimerTest - */ -@SmallTest -@Presubmit -public class AnrTimerTest { - - /** - * A handler that allows control over when to dispatch messages and callbacks. Because most - * Handler methods are final, the only thing this handler can intercept is sending messages. - * This handler allows unit tests to be written without a need to sleep (which leads to flaky - * tests). - * - * This code was cloned from {@link com.android.systemui.utils.os.FakeHandler}. - */ - static class TestHandler extends Handler { - - private boolean mImmediate = true; - private ArrayList<Message> mQueuedMessages = new ArrayList<>(); - - ArrayList<Long> mDelays = new ArrayList<>(); - - TestHandler(Looper looper, Callback callback, boolean immediate) { - super(looper, callback); - mImmediate = immediate; - } - - TestHandler(Looper looper, Callback callback) { - this(looper, callback, true); - } - - /** - * Override sendMessageAtTime. In immediate mode, the message is immediately dispatched. - * In non-immediate mode, the message is enqueued to the real handler. In both cases, the - * original delay is computed by comparing the target dispatch time with 'now'. This - * computation is prone to errors if the code experiences delays. The computed time is - * captured in the mDelays list. - */ - @Override - public boolean sendMessageAtTime(Message msg, long uptimeMillis) { - long delay = uptimeMillis - SystemClock.uptimeMillis(); - mDelays.add(delay); - if (mImmediate) { - mQueuedMessages.add(msg); - dispatchQueuedMessages(); - } else { - super.sendMessageAtTime(msg, uptimeMillis); - } - return true; - } - - void setImmediate(boolean immediate) { - mImmediate = immediate; - } - - /** Dispatch any messages that have been queued on the calling thread. */ - void dispatchQueuedMessages() { - ArrayList<Message> messages = new ArrayList<>(mQueuedMessages); - mQueuedMessages.clear(); - for (Message msg : messages) { - dispatchMessage(msg); - } - } - - /** - * Compare the captured delays with the input array. The comparison is fuzzy because the - * captured delay (see sendMessageAtTime) is affected by process delays. - */ - void verifyDelays(long[] r) { - final long FUZZ = 10; - assertEquals(r.length, mDelays.size()); - for (int i = 0; i < mDelays.size(); i++) { - long t = r[i]; - long v = mDelays.get(i); - assertTrue(v >= t - FUZZ && v <= t + FUZZ); - } - } - } - - private Handler mHandler; - private CountDownLatch mLatch = null; - private ArrayList<Message> mMessages; - - // The commonly used message timeout key. - private static final int MSG_TIMEOUT = 1; - - @Before - public void setUp() { - mHandler = new Handler(Looper.getMainLooper(), this::expirationHandler); - mMessages = new ArrayList<>(); - mLatch = new CountDownLatch(1); - AnrTimer.resetTimerListForHermeticTest(); - } - - @After - public void tearDown() { - mHandler = null; - mMessages = null; - } - - // When a timer expires, set the expiration time in the message and add it to the queue. - private boolean expirationHandler(Message msg) { - mMessages.add(Message.obtain(msg)); - mLatch.countDown(); - return false; - } - - // The test argument includes a pid and uid, and a tag. The tag is used to distinguish - // different message instances. - private static class TestArg { - final int pid; - final int uid; - final int tag; - - TestArg(int pid, int uid, int tag) { - this.pid = pid; - this.uid = uid; - this.tag = tag; - } - @Override - public String toString() { - return String.format("pid=%d uid=%d tag=%d", pid, uid, tag); - } - } - - /** - * An instrumented AnrTimer. - */ - private class TestAnrTimer extends AnrTimer { - // A local copy of 'what'. The field in AnrTimer is private. - final int mWhat; - - TestAnrTimer(Handler h, int key, String tag) { - super(h, key, tag); - mWhat = key; - } - - TestAnrTimer() { - this(mHandler, MSG_TIMEOUT, caller()); - } - - TestAnrTimer(Handler h, int key, String tag, boolean extend, TestInjector injector) { - super(h, key, tag, extend, injector); - mWhat = key; - } - - TestAnrTimer(boolean extend, TestInjector injector) { - this(mHandler, MSG_TIMEOUT, caller(), extend, injector); - } - - // Return the name of method that called the constructor, assuming that this function is - // called from inside the constructor. The calling method is used to name the AnrTimer - // instance so that logs are easier to understand. - private static String caller() { - final int n = 4; - StackTraceElement[] stack = Thread.currentThread().getStackTrace(); - if (stack.length < n+1) return "test"; - return stack[n].getMethodName(); - } - - boolean start(TestArg arg, long millis) { - return start(arg, arg.pid, arg.uid, millis); - } - - int what() { - return mWhat; - } - } - - private static class TestTracker extends AnrTimer.CpuTracker { - long index = 0; - final int skip; - TestTracker(int skip) { - this.skip = skip; - } - long delay(int pid) { - return index++ * skip; - } - } - - private class TestInjector extends AnrTimer.Injector { - final boolean mImmediate; - final AnrTimer.CpuTracker mTracker; - TestHandler mTestHandler; - - TestInjector(int skip, boolean immediate) { - super(mHandler); - mTracker = new TestTracker(skip); - mImmediate = immediate; - } - - TestInjector(int skip) { - this(skip, true); - } - - @Override - Handler newHandler(Handler.Callback callback) { - if (mTestHandler == null) { - mTestHandler = new TestHandler(mHandler.getLooper(), callback, mImmediate); - } - return mTestHandler; - } - - /** Fetch the allocated handle. This does not check for nulls. */ - TestHandler getHandler() { - return mTestHandler; - } - - /** - * This override returns the tracker supplied in the constructor. It does not create a - * new one. - */ - @Override - AnrTimer.CpuTracker newTracker() { - return mTracker; - } - - /** For test purposes, always enable the feature. */ - @Override - boolean isFeatureEnabled() { - return true; - } - } - - // Tests - // 1. Start a timer and wait for expiration. - // 2. Start a timer and cancel it. Verify no expiration. - // 3. Start a timer. Shortly thereafter, restart it. Verify only one expiration. - // 4. Start a couple of timers. Verify max active timers. Discard one and verify the active - // count drops by 1. Accept one and verify the active count drops by 1. - - @Test - public void testSimpleTimeout() throws Exception { - // Create an immediate TestHandler. - TestInjector injector = new TestInjector(0); - TestAnrTimer timer = new TestAnrTimer(false, injector); - TestArg t = new TestArg(1, 1, 3); - assertTrue(timer.start(t, 10)); - // Delivery is immediate but occurs on a different thread. - assertTrue(mLatch.await(100, TimeUnit.MILLISECONDS)); - assertEquals(1, mMessages.size()); - Message m = mMessages.get(0); - assertEquals(timer.what(), m.what); - assertEquals(t, m.obj); - - // Verify that the timer is still present. - assertEquals(1, AnrTimer.sizeOfTimerList()); - assertTrue(timer.accept(t)); - assertEquals(0, AnrTimer.sizeOfTimerList()); - - // Verify that the timer no longer exists. - assertFalse(timer.accept(t)); - } - - @Test - public void testCancel() throws Exception { - // Create an non-immediate TestHandler. - TestInjector injector = new TestInjector(0, false); - TestAnrTimer timer = new TestAnrTimer(false, injector); - - Handler handler = injector.getHandler(); - assertNotNull(handler); - assertTrue(handler instanceof TestHandler); - - // The tests that follow check for a 'what' of 0 (zero), which is the message key used - // by AnrTimer internally. - TestArg t = new TestArg(1, 1, 3); - assertFalse(handler.hasMessages(0)); - assertTrue(timer.start(t, 100)); - assertTrue(handler.hasMessages(0)); - assertTrue(timer.cancel(t)); - assertFalse(handler.hasMessages(0)); - - // Verify that no expiration messages were delivered. - assertEquals(0, mMessages.size()); - assertEquals(0, AnrTimer.sizeOfTimerList()); - } - - @Test - public void testRestart() throws Exception { - // Create an non-immediate TestHandler. - TestInjector injector = new TestInjector(0, false); - TestAnrTimer timer = new TestAnrTimer(false, injector); - - TestArg t = new TestArg(1, 1, 3); - assertTrue(timer.start(t, 2500)); - assertTrue(timer.start(t, 1000)); - - // Verify that the test handler saw two timeouts. - injector.getHandler().verifyDelays(new long[] { 2500, 1000 }); - - // Verify that there is a single timer. Then cancel it. - assertEquals(1, AnrTimer.sizeOfTimerList()); - assertTrue(timer.cancel(t)); - assertEquals(0, AnrTimer.sizeOfTimerList()); - } - - @Test - public void testExtendNormal() throws Exception { - // Create an immediate TestHandler. - TestInjector injector = new TestInjector(5); - TestAnrTimer timer = new TestAnrTimer(true, injector); - TestArg t = new TestArg(1, 1, 3); - assertTrue(timer.start(t, 10)); - - assertTrue(mLatch.await(100, TimeUnit.MILLISECONDS)); - assertEquals(1, mMessages.size()); - Message m = mMessages.get(0); - assertEquals(timer.what(), m.what); - assertEquals(t, m.obj); - - // Verify that the test handler saw two timeouts: one of 10ms and one of 5ms. - injector.getHandler().verifyDelays(new long[] { 10, 5 }); - - // Verify that the timer is still present. Then remove it and verify that the list is - // empty. - assertEquals(1, AnrTimer.sizeOfTimerList()); - assertTrue(timer.accept(t)); - assertEquals(0, AnrTimer.sizeOfTimerList()); - } - - @Test - public void testExtendOversize() throws Exception { - // Create an immediate TestHandler. - TestInjector injector = new TestInjector(25); - TestAnrTimer timer = new TestAnrTimer(true, injector); - TestArg t = new TestArg(1, 1, 3); - assertTrue(timer.start(t, 10)); - - assertTrue(mLatch.await(100, TimeUnit.MILLISECONDS)); - assertEquals(1, mMessages.size()); - Message m = mMessages.get(0); - assertEquals(timer.what(), m.what); - assertEquals(t, m.obj); - - // Verify that the test handler saw two timeouts: one of 10ms and one of 10ms. - injector.getHandler().verifyDelays(new long[] { 10, 10 }); - - // Verify that the timer is still present. Then remove it and verify that the list is - // empty. - assertEquals(1, AnrTimer.sizeOfTimerList()); - assertTrue(timer.accept(t)); - assertEquals(0, AnrTimer.sizeOfTimerList()); - } -} diff --git a/services/tests/servicestests/src/com/android/server/utils/AnrTimerTest.java b/services/tests/servicestests/src/com/android/server/utils/AnrTimerTest.java new file mode 100644 index 000000000000..330dbb83e949 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/utils/AnrTimerTest.java @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2023 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 com.android.server.utils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.os.Handler; +import android.os.Looper; +import android.os.Message; + +import android.platform.test.annotations.Presubmit; +import androidx.test.filters.SmallTest; + +import com.android.internal.annotations.GuardedBy; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +@SmallTest +@Presubmit +public class AnrTimerTest { + + // The commonly used message timeout key. + private static final int MSG_TIMEOUT = 1; + + // The test argument includes a pid and uid, and a tag. The tag is used to distinguish + // different message instances. Additional fields (like what) capture delivery information + // that is checked by the test. + private static class TestArg { + final int pid; + final int uid; + int what; + + TestArg(int pid, int uid) { + this.pid = pid; + this.uid = uid; + this.what = 0; + } + } + + /** + * The test handler is a self-contained object for a single test. + */ + private static class Helper { + final Object mLock = new Object(); + + final Handler mHandler; + final CountDownLatch mLatch; + @GuardedBy("mLock") + final ArrayList<TestArg> mMessages; + + Helper(int expect) { + mHandler = new Handler(Looper.getMainLooper(), this::expirationHandler); + mMessages = new ArrayList<>(); + mLatch = new CountDownLatch(expect); + } + + /** + * When a timer expires, the object must be a TestArg. Update the TestArg with + * expiration metadata and save it. + */ + private boolean expirationHandler(Message msg) { + synchronized (mLock) { + TestArg arg = (TestArg) msg.obj; + arg.what = msg.what; + mMessages.add(arg); + mLatch.countDown(); + return false; + } + } + + boolean await(long timeout) throws InterruptedException { + // No need to synchronize, as the CountDownLatch is already thread-safe. + return mLatch.await(timeout, TimeUnit.MILLISECONDS); + } + + /** + * Fetch the received messages. Fail if the count of received messages is other than the + * expected count. + */ + TestArg[] messages(int expected) { + synchronized (mLock) { + assertEquals(expected, mMessages.size()); + return mMessages.toArray(new TestArg[expected]); + } + } + } + + /** + * An instrumented AnrTimer. + */ + private static class TestAnrTimer extends AnrTimer<TestArg> { + private TestAnrTimer(Handler h, int key, String tag) { + super(h, key, tag); + } + + TestAnrTimer(Helper helper) { + this(helper.mHandler, MSG_TIMEOUT, caller()); + } + + void start(TestArg arg, long millis) { + start(arg, arg.pid, arg.uid, millis); + } + + // Return the name of method that called the constructor, assuming that this function is + // called from inside the constructor. The calling method is used to name the AnrTimer + // instance so that logs are easier to understand. + private static String caller() { + final int n = 4; + StackTraceElement[] stack = Thread.currentThread().getStackTrace(); + if (stack.length < n+1) return "test"; + return stack[n].getMethodName(); + } + } + + void validate(TestArg expected, TestArg actual) { + assertEquals(expected, actual); + assertEquals(actual.what, MSG_TIMEOUT); + } + + + /** + * Verify that a simple expiration succeeds. The timer is started for 10ms. The test + * procedure waits 5s for the expiration message, but under correct operation, the test will + * only take 10ms + */ + @Test + public void testSimpleTimeout() throws Exception { + Helper helper = new Helper(1); + TestAnrTimer timer = new TestAnrTimer(helper); + TestArg t = new TestArg(1, 1); + timer.start(t, 10); + // Delivery is immediate but occurs on a different thread. + assertTrue(helper.await(5000)); + TestArg[] result = helper.messages(1); + validate(t, result[0]); + } + + /** + * Verify that if three timers are scheduled, they are delivered in time order. + */ + @Test + public void testMultipleTimers() throws Exception { + // Expect three messages. + Helper helper = new Helper(3); + TestAnrTimer timer = new TestAnrTimer(helper); + TestArg t1 = new TestArg(1, 1); + TestArg t2 = new TestArg(1, 2); + TestArg t3 = new TestArg(1, 3); + timer.start(t1, 50); + timer.start(t2, 60); + timer.start(t3, 40); + // Delivery is immediate but occurs on a different thread. + assertTrue(helper.await(5000)); + TestArg[] result = helper.messages(3); + validate(t3, result[0]); + validate(t1, result[1]); + validate(t2, result[2]); + } + + /** + * Verify that a canceled timer is not delivered. + */ + @Test + public void testCancelTimer() throws Exception { + // Expect two messages. + Helper helper = new Helper(2); + TestAnrTimer timer = new TestAnrTimer(helper); + TestArg t1 = new TestArg(1, 1); + TestArg t2 = new TestArg(1, 2); + TestArg t3 = new TestArg(1, 3); + timer.start(t1, 50); + timer.start(t2, 60); + timer.start(t3, 40); + // Briefly pause. + assertFalse(helper.await(10)); + timer.cancel(t1); + // Delivery is immediate but occurs on a different thread. + assertTrue(helper.await(5000)); + TestArg[] result = helper.messages(2); + validate(t3, result[0]); + validate(t2, result[1]); + } +} |