Loading core/java/android/app/ActivityManager.java +48 −0 Original line number Diff line number Diff line Loading @@ -147,6 +147,7 @@ import java.util.function.Consumer; * </p> */ @SystemService(Context.ACTIVITY_SERVICE) @android.ravenwood.annotation.RavenwoodKeepPartialClass public class ActivityManager { private static String TAG = "ActivityManager"; Loading Loading @@ -966,6 +967,7 @@ public class ActivityManager { * Print capability bits in human-readable form. * @hide */ @android.ravenwood.annotation.RavenwoodKeep public static void printCapabilitiesSummary(PrintWriter pw, @ProcessCapability int caps) { pw.print((caps & PROCESS_CAPABILITY_FOREGROUND_LOCATION) != 0 ? 'L' : '-'); pw.print((caps & PROCESS_CAPABILITY_FOREGROUND_CAMERA) != 0 ? 'C' : '-'); Loading @@ -976,6 +978,7 @@ public class ActivityManager { } /** @hide */ @android.ravenwood.annotation.RavenwoodKeep public static void printCapabilitiesSummary(StringBuilder sb, @ProcessCapability int caps) { sb.append((caps & PROCESS_CAPABILITY_FOREGROUND_LOCATION) != 0 ? 'L' : '-'); sb.append((caps & PROCESS_CAPABILITY_FOREGROUND_CAMERA) != 0 ? 'C' : '-'); Loading @@ -989,6 +992,7 @@ public class ActivityManager { * Print capability bits in human-readable form. * @hide */ @android.ravenwood.annotation.RavenwoodKeep public static void printCapabilitiesFull(PrintWriter pw, @ProcessCapability int caps) { printCapabilitiesSummary(pw, caps); final int remain = caps & ~PROCESS_CAPABILITY_ALL; Loading @@ -999,6 +1003,7 @@ public class ActivityManager { } /** @hide */ @android.ravenwood.annotation.RavenwoodKeep public static String getCapabilitiesSummary(@ProcessCapability int caps) { final StringBuilder sb = new StringBuilder(); printCapabilitiesSummary(sb, caps); Loading @@ -1018,6 +1023,7 @@ public class ActivityManager { * @return the value of the corresponding enums.proto ProcessStateEnum value. * @hide */ @android.ravenwood.annotation.RavenwoodKeep public static final int processStateAmToProto(int amInt) { switch (amInt) { case PROCESS_STATE_UNKNOWN: Loading Loading @@ -1078,16 +1084,19 @@ public class ActivityManager { public static final int MAX_PROCESS_STATE = PROCESS_STATE_NONEXISTENT; /** @hide Should this process state be considered a background state? */ @android.ravenwood.annotation.RavenwoodKeep public static final boolean isProcStateBackground(int procState) { return procState >= PROCESS_STATE_TRANSIENT_BACKGROUND; } /** @hide Should this process state be considered in the cache? */ @android.ravenwood.annotation.RavenwoodKeep public static final boolean isProcStateCached(int procState) { return procState >= PROCESS_STATE_CACHED_ACTIVITY; } /** @hide Is this a foreground service type? */ @android.ravenwood.annotation.RavenwoodKeep public static boolean isForegroundService(int procState) { return procState == PROCESS_STATE_FOREGROUND_SERVICE; } Loading Loading @@ -1161,10 +1170,25 @@ public class ActivityManager { mContext = context; } private static volatile int sCurrentUser$ravenwood = UserHandle.USER_NULL; /** @hide */ @android.ravenwood.annotation.RavenwoodKeep public static void init$ravenwood(int currentUser) { sCurrentUser$ravenwood = currentUser; } /** @hide */ @android.ravenwood.annotation.RavenwoodKeep public static void reset$ravenwood() { sCurrentUser$ravenwood = UserHandle.USER_NULL; } /** * Returns whether the launch was successful. * @hide */ @android.ravenwood.annotation.RavenwoodKeep public static final boolean isStartResultSuccessful(int result) { return FIRST_START_SUCCESS_CODE <= result && result <= LAST_START_SUCCESS_CODE; } Loading @@ -1173,6 +1197,7 @@ public class ActivityManager { * Returns whether the launch result was a fatal error. * @hide */ @android.ravenwood.annotation.RavenwoodKeep public static final boolean isStartResultFatalError(int result) { return FIRST_START_FATAL_ERROR_CODE <= result && result <= LAST_START_FATAL_ERROR_CODE; } Loading Loading @@ -1343,6 +1368,7 @@ public class ActivityManager { public @interface RestrictionLevel{} /** @hide */ @android.ravenwood.annotation.RavenwoodKeep public static String restrictionLevelToName(@RestrictionLevel int level) { switch (level) { case RESTRICTION_LEVEL_UNKNOWN: Loading Loading @@ -4779,6 +4805,7 @@ public class ActivityManager { * Returns "true" if the user interface is currently being messed with * by a monkey. */ @android.ravenwood.annotation.RavenwoodReplace public static boolean isUserAMonkey() { try { return getService().isUserAMonkey(); Loading @@ -4787,6 +4814,12 @@ public class ActivityManager { } } /** @hide */ public static boolean isUserAMonkey$ravenwood() { // Ravenwood environment is never considered a "monkey" return false; } /** * Returns "true" if device is running in a test harness. * Loading Loading @@ -4973,6 +5006,7 @@ public class ActivityManager { "android.permission.INTERACT_ACROSS_USERS", "android.permission.INTERACT_ACROSS_USERS_FULL" }) @android.ravenwood.annotation.RavenwoodReplace public static int getCurrentUser() { try { return getService().getCurrentUserId(); Loading @@ -4981,6 +5015,11 @@ public class ActivityManager { } } /** @hide */ public static int getCurrentUser$ravenwood() { return sCurrentUser$ravenwood; } /** * @param userid the user's id. Zero indicates the default user. * @hide Loading Loading @@ -5320,6 +5359,7 @@ public class ActivityManager { /** * @hide */ @android.ravenwood.annotation.RavenwoodReplace public static boolean isSystemReady() { if (!sSystemReady) { if (ActivityThread.isSystem()) { Loading @@ -5334,6 +5374,12 @@ public class ActivityManager { return sSystemReady; } /** @hide */ public static boolean isSystemReady$ravenwood() { // Ravenwood environment is always considered as booted and ready return true; } /** * @hide */ Loading Loading @@ -5661,11 +5707,13 @@ public class ActivityManager { } /** @hide */ @android.ravenwood.annotation.RavenwoodKeep public static boolean isProcStateConsideredInteraction(@ProcessState int procState) { return (procState <= PROCESS_STATE_TOP || procState == PROCESS_STATE_BOUND_TOP); } /** @hide */ @android.ravenwood.annotation.RavenwoodKeep public static String procStateToString(int procState) { final String procStateStr; switch (procState) { Loading core/java/android/os/VibrationAttributes.java +2 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import java.util.Objects; /** * Encapsulates a collection of attributes describing information about a vibration. */ @android.ravenwood.annotation.RavenwoodKeepWholeClass public final class VibrationAttributes implements Parcelable { private static final String TAG = "VibrationAttributes"; Loading Loading @@ -463,6 +464,7 @@ public final class VibrationAttributes implements Parcelable { * Builder class for {@link VibrationAttributes} objects. * By default, all information is set to UNKNOWN. */ @android.ravenwood.annotation.RavenwoodKeepWholeClass public static final class Builder { private int mUsage = USAGE_UNKNOWN; private int mOriginalAudioUsage = AudioAttributes.USAGE_UNKNOWN; Loading core/tests/coretests/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -208,6 +208,7 @@ android_ravenwood_test { "testng", ], srcs: [ "src/android/app/ActivityManagerTest.java", "src/android/content/pm/PackageManagerTest.java", "src/android/content/pm/UserInfoTest.java", "src/android/database/CursorWindowTest.java", Loading core/tests/coretests/src/android/app/ActivityManagerTest.java 0 → 100644 +83 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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 android.app; import static android.app.ActivityManager.PROCESS_STATE_SERVICE; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import android.os.UserHandle; import android.platform.test.ravenwood.RavenwoodRule; import androidx.test.runner.AndroidJUnit4; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import java.io.ByteArrayOutputStream; import java.io.PrintWriter; @RunWith(AndroidJUnit4.class) public class ActivityManagerTest { @Rule public final RavenwoodRule mRavenwood = new RavenwoodRule(); @Test public void testSimple() throws Exception { assertTrue(ActivityManager.isSystemReady()); assertFalse(ActivityManager.isUserAMonkey()); assertNotEquals(UserHandle.USER_NULL, ActivityManager.getCurrentUser()); } @Test public void testCapabilities() throws Exception { // For the moment mostly want to confirm we don't crash assertNotNull(ActivityManager.getCapabilitiesSummary(~0)); ActivityManager.printCapabilitiesFull(new PrintWriter(new ByteArrayOutputStream()), ~0); ActivityManager.printCapabilitiesSummary(new PrintWriter(new ByteArrayOutputStream()), ~0); ActivityManager.printCapabilitiesSummary(new StringBuilder(), ~0); } @Test public void testProcState() throws Exception { // For the moment mostly want to confirm we don't crash assertNotNull(ActivityManager.procStateToString(PROCESS_STATE_SERVICE)); assertNotNull(ActivityManager.processStateAmToProto(PROCESS_STATE_SERVICE)); assertTrue(ActivityManager.isProcStateBackground(PROCESS_STATE_SERVICE)); assertFalse(ActivityManager.isProcStateCached(PROCESS_STATE_SERVICE)); assertFalse(ActivityManager.isForegroundService(PROCESS_STATE_SERVICE)); assertFalse(ActivityManager.isProcStateConsideredInteraction(PROCESS_STATE_SERVICE)); } @Test public void testStartResult() throws Exception { // For the moment mostly want to confirm we don't crash assertTrue(ActivityManager.isStartResultSuccessful(50)); assertTrue(ActivityManager.isStartResultFatalError(-50)); } @Test public void testRestrictionLevel() throws Exception { // For the moment mostly want to confirm we don't crash assertNotNull(ActivityManager.restrictionLevelToName( ActivityManager.RESTRICTION_LEVEL_HIBERNATION)); } } core/tests/coretests/src/android/os/HandlerThreadTest.java +30 −1 Original line number Diff line number Diff line Loading @@ -28,15 +28,20 @@ import android.platform.test.ravenwood.RavenwoodRule; import androidx.test.filters.MediumTest; import androidx.test.runner.AndroidJUnit4; import org.junit.Assume; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) public class HandlerThreadTest { private static final int TEST_WHAT = 1; @Rule @Rule(order = 1) public ExpectedException mThrown = ExpectedException.none(); @Rule(order = 2) public final RavenwoodRule mRavenwood = new RavenwoodRule(); private boolean mGotMessage = false; Loading Loading @@ -112,4 +117,28 @@ public class HandlerThreadTest { assertTrue(mGotMessage); assertEquals(TEST_WHAT, mGotMessageWhat); } /** * Confirm that a background handler thread throwing an exception during a test results in a * test failure being reported. */ @Test public void testUncaughtExceptionFails() throws Exception { // For the moment we can only test Ravenwood; on a physical device uncaught exceptions // are detected, but reported as test failures at a higher level where we can't inspect Assume.assumeTrue(RavenwoodRule.isOnRavenwood()); mThrown.expect(IllegalStateException.class); final HandlerThread thread = new HandlerThread("HandlerThreadTest"); thread.start(); thread.getThreadHandler().post(() -> { throw new IllegalStateException(); }); // Wait until we've drained past the message above, then terminate test without throwing // directly; the test harness should notice and report the uncaught exception while (!thread.getThreadHandler().getLooper().getQueue().isIdle()) { SystemClock.sleep(10); } } } Loading
core/java/android/app/ActivityManager.java +48 −0 Original line number Diff line number Diff line Loading @@ -147,6 +147,7 @@ import java.util.function.Consumer; * </p> */ @SystemService(Context.ACTIVITY_SERVICE) @android.ravenwood.annotation.RavenwoodKeepPartialClass public class ActivityManager { private static String TAG = "ActivityManager"; Loading Loading @@ -966,6 +967,7 @@ public class ActivityManager { * Print capability bits in human-readable form. * @hide */ @android.ravenwood.annotation.RavenwoodKeep public static void printCapabilitiesSummary(PrintWriter pw, @ProcessCapability int caps) { pw.print((caps & PROCESS_CAPABILITY_FOREGROUND_LOCATION) != 0 ? 'L' : '-'); pw.print((caps & PROCESS_CAPABILITY_FOREGROUND_CAMERA) != 0 ? 'C' : '-'); Loading @@ -976,6 +978,7 @@ public class ActivityManager { } /** @hide */ @android.ravenwood.annotation.RavenwoodKeep public static void printCapabilitiesSummary(StringBuilder sb, @ProcessCapability int caps) { sb.append((caps & PROCESS_CAPABILITY_FOREGROUND_LOCATION) != 0 ? 'L' : '-'); sb.append((caps & PROCESS_CAPABILITY_FOREGROUND_CAMERA) != 0 ? 'C' : '-'); Loading @@ -989,6 +992,7 @@ public class ActivityManager { * Print capability bits in human-readable form. * @hide */ @android.ravenwood.annotation.RavenwoodKeep public static void printCapabilitiesFull(PrintWriter pw, @ProcessCapability int caps) { printCapabilitiesSummary(pw, caps); final int remain = caps & ~PROCESS_CAPABILITY_ALL; Loading @@ -999,6 +1003,7 @@ public class ActivityManager { } /** @hide */ @android.ravenwood.annotation.RavenwoodKeep public static String getCapabilitiesSummary(@ProcessCapability int caps) { final StringBuilder sb = new StringBuilder(); printCapabilitiesSummary(sb, caps); Loading @@ -1018,6 +1023,7 @@ public class ActivityManager { * @return the value of the corresponding enums.proto ProcessStateEnum value. * @hide */ @android.ravenwood.annotation.RavenwoodKeep public static final int processStateAmToProto(int amInt) { switch (amInt) { case PROCESS_STATE_UNKNOWN: Loading Loading @@ -1078,16 +1084,19 @@ public class ActivityManager { public static final int MAX_PROCESS_STATE = PROCESS_STATE_NONEXISTENT; /** @hide Should this process state be considered a background state? */ @android.ravenwood.annotation.RavenwoodKeep public static final boolean isProcStateBackground(int procState) { return procState >= PROCESS_STATE_TRANSIENT_BACKGROUND; } /** @hide Should this process state be considered in the cache? */ @android.ravenwood.annotation.RavenwoodKeep public static final boolean isProcStateCached(int procState) { return procState >= PROCESS_STATE_CACHED_ACTIVITY; } /** @hide Is this a foreground service type? */ @android.ravenwood.annotation.RavenwoodKeep public static boolean isForegroundService(int procState) { return procState == PROCESS_STATE_FOREGROUND_SERVICE; } Loading Loading @@ -1161,10 +1170,25 @@ public class ActivityManager { mContext = context; } private static volatile int sCurrentUser$ravenwood = UserHandle.USER_NULL; /** @hide */ @android.ravenwood.annotation.RavenwoodKeep public static void init$ravenwood(int currentUser) { sCurrentUser$ravenwood = currentUser; } /** @hide */ @android.ravenwood.annotation.RavenwoodKeep public static void reset$ravenwood() { sCurrentUser$ravenwood = UserHandle.USER_NULL; } /** * Returns whether the launch was successful. * @hide */ @android.ravenwood.annotation.RavenwoodKeep public static final boolean isStartResultSuccessful(int result) { return FIRST_START_SUCCESS_CODE <= result && result <= LAST_START_SUCCESS_CODE; } Loading @@ -1173,6 +1197,7 @@ public class ActivityManager { * Returns whether the launch result was a fatal error. * @hide */ @android.ravenwood.annotation.RavenwoodKeep public static final boolean isStartResultFatalError(int result) { return FIRST_START_FATAL_ERROR_CODE <= result && result <= LAST_START_FATAL_ERROR_CODE; } Loading Loading @@ -1343,6 +1368,7 @@ public class ActivityManager { public @interface RestrictionLevel{} /** @hide */ @android.ravenwood.annotation.RavenwoodKeep public static String restrictionLevelToName(@RestrictionLevel int level) { switch (level) { case RESTRICTION_LEVEL_UNKNOWN: Loading Loading @@ -4779,6 +4805,7 @@ public class ActivityManager { * Returns "true" if the user interface is currently being messed with * by a monkey. */ @android.ravenwood.annotation.RavenwoodReplace public static boolean isUserAMonkey() { try { return getService().isUserAMonkey(); Loading @@ -4787,6 +4814,12 @@ public class ActivityManager { } } /** @hide */ public static boolean isUserAMonkey$ravenwood() { // Ravenwood environment is never considered a "monkey" return false; } /** * Returns "true" if device is running in a test harness. * Loading Loading @@ -4973,6 +5006,7 @@ public class ActivityManager { "android.permission.INTERACT_ACROSS_USERS", "android.permission.INTERACT_ACROSS_USERS_FULL" }) @android.ravenwood.annotation.RavenwoodReplace public static int getCurrentUser() { try { return getService().getCurrentUserId(); Loading @@ -4981,6 +5015,11 @@ public class ActivityManager { } } /** @hide */ public static int getCurrentUser$ravenwood() { return sCurrentUser$ravenwood; } /** * @param userid the user's id. Zero indicates the default user. * @hide Loading Loading @@ -5320,6 +5359,7 @@ public class ActivityManager { /** * @hide */ @android.ravenwood.annotation.RavenwoodReplace public static boolean isSystemReady() { if (!sSystemReady) { if (ActivityThread.isSystem()) { Loading @@ -5334,6 +5374,12 @@ public class ActivityManager { return sSystemReady; } /** @hide */ public static boolean isSystemReady$ravenwood() { // Ravenwood environment is always considered as booted and ready return true; } /** * @hide */ Loading Loading @@ -5661,11 +5707,13 @@ public class ActivityManager { } /** @hide */ @android.ravenwood.annotation.RavenwoodKeep public static boolean isProcStateConsideredInteraction(@ProcessState int procState) { return (procState <= PROCESS_STATE_TOP || procState == PROCESS_STATE_BOUND_TOP); } /** @hide */ @android.ravenwood.annotation.RavenwoodKeep public static String procStateToString(int procState) { final String procStateStr; switch (procState) { Loading
core/java/android/os/VibrationAttributes.java +2 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import java.util.Objects; /** * Encapsulates a collection of attributes describing information about a vibration. */ @android.ravenwood.annotation.RavenwoodKeepWholeClass public final class VibrationAttributes implements Parcelable { private static final String TAG = "VibrationAttributes"; Loading Loading @@ -463,6 +464,7 @@ public final class VibrationAttributes implements Parcelable { * Builder class for {@link VibrationAttributes} objects. * By default, all information is set to UNKNOWN. */ @android.ravenwood.annotation.RavenwoodKeepWholeClass public static final class Builder { private int mUsage = USAGE_UNKNOWN; private int mOriginalAudioUsage = AudioAttributes.USAGE_UNKNOWN; Loading
core/tests/coretests/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -208,6 +208,7 @@ android_ravenwood_test { "testng", ], srcs: [ "src/android/app/ActivityManagerTest.java", "src/android/content/pm/PackageManagerTest.java", "src/android/content/pm/UserInfoTest.java", "src/android/database/CursorWindowTest.java", Loading
core/tests/coretests/src/android/app/ActivityManagerTest.java 0 → 100644 +83 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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 android.app; import static android.app.ActivityManager.PROCESS_STATE_SERVICE; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import android.os.UserHandle; import android.platform.test.ravenwood.RavenwoodRule; import androidx.test.runner.AndroidJUnit4; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import java.io.ByteArrayOutputStream; import java.io.PrintWriter; @RunWith(AndroidJUnit4.class) public class ActivityManagerTest { @Rule public final RavenwoodRule mRavenwood = new RavenwoodRule(); @Test public void testSimple() throws Exception { assertTrue(ActivityManager.isSystemReady()); assertFalse(ActivityManager.isUserAMonkey()); assertNotEquals(UserHandle.USER_NULL, ActivityManager.getCurrentUser()); } @Test public void testCapabilities() throws Exception { // For the moment mostly want to confirm we don't crash assertNotNull(ActivityManager.getCapabilitiesSummary(~0)); ActivityManager.printCapabilitiesFull(new PrintWriter(new ByteArrayOutputStream()), ~0); ActivityManager.printCapabilitiesSummary(new PrintWriter(new ByteArrayOutputStream()), ~0); ActivityManager.printCapabilitiesSummary(new StringBuilder(), ~0); } @Test public void testProcState() throws Exception { // For the moment mostly want to confirm we don't crash assertNotNull(ActivityManager.procStateToString(PROCESS_STATE_SERVICE)); assertNotNull(ActivityManager.processStateAmToProto(PROCESS_STATE_SERVICE)); assertTrue(ActivityManager.isProcStateBackground(PROCESS_STATE_SERVICE)); assertFalse(ActivityManager.isProcStateCached(PROCESS_STATE_SERVICE)); assertFalse(ActivityManager.isForegroundService(PROCESS_STATE_SERVICE)); assertFalse(ActivityManager.isProcStateConsideredInteraction(PROCESS_STATE_SERVICE)); } @Test public void testStartResult() throws Exception { // For the moment mostly want to confirm we don't crash assertTrue(ActivityManager.isStartResultSuccessful(50)); assertTrue(ActivityManager.isStartResultFatalError(-50)); } @Test public void testRestrictionLevel() throws Exception { // For the moment mostly want to confirm we don't crash assertNotNull(ActivityManager.restrictionLevelToName( ActivityManager.RESTRICTION_LEVEL_HIBERNATION)); } }
core/tests/coretests/src/android/os/HandlerThreadTest.java +30 −1 Original line number Diff line number Diff line Loading @@ -28,15 +28,20 @@ import android.platform.test.ravenwood.RavenwoodRule; import androidx.test.filters.MediumTest; import androidx.test.runner.AndroidJUnit4; import org.junit.Assume; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) public class HandlerThreadTest { private static final int TEST_WHAT = 1; @Rule @Rule(order = 1) public ExpectedException mThrown = ExpectedException.none(); @Rule(order = 2) public final RavenwoodRule mRavenwood = new RavenwoodRule(); private boolean mGotMessage = false; Loading Loading @@ -112,4 +117,28 @@ public class HandlerThreadTest { assertTrue(mGotMessage); assertEquals(TEST_WHAT, mGotMessageWhat); } /** * Confirm that a background handler thread throwing an exception during a test results in a * test failure being reported. */ @Test public void testUncaughtExceptionFails() throws Exception { // For the moment we can only test Ravenwood; on a physical device uncaught exceptions // are detected, but reported as test failures at a higher level where we can't inspect Assume.assumeTrue(RavenwoodRule.isOnRavenwood()); mThrown.expect(IllegalStateException.class); final HandlerThread thread = new HandlerThread("HandlerThreadTest"); thread.start(); thread.getThreadHandler().post(() -> { throw new IllegalStateException(); }); // Wait until we've drained past the message above, then terminate test without throwing // directly; the test harness should notice and report the uncaught exception while (!thread.getThreadHandler().getLooper().getQueue().isIdle()) { SystemClock.sleep(10); } } }