Remove support for stuck threads

The experimental support for stuck threads requires access to
the java.lang.management package which is not supported on
Android.

This change re-lands https://r.android.com/1601635

Bug: 129054170
Test: m checkbuild
Change-Id: I4b37c2d09b5c68afde438c343b41fcdc788c578e
(cherry picked from commit 4156f43b51c6f2d42df6e45535e1967aa46514d3)
diff --git a/README.version b/README.version
index 74c20ad..ebab252 100644
--- a/README.version
+++ b/README.version
@@ -5,3 +5,4 @@
 Local Changes:
     Extra generic type information to aid certain javacs.
     Remove DisableOnDebug (new in 4.12) as it is not supported on Android
+    Remove support for stuck threads
diff --git a/src/main/java/org/junit/internal/runners/statements/FailOnTimeout.java b/src/main/java/org/junit/internal/runners/statements/FailOnTimeout.java
index 9362cc1..9fad35b 100644
--- a/src/main/java/org/junit/internal/runners/statements/FailOnTimeout.java
+++ b/src/main/java/org/junit/internal/runners/statements/FailOnTimeout.java
@@ -1,8 +1,5 @@
 package org.junit.internal.runners.statements;
 
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
 import java.util.concurrent.Callable;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
@@ -10,9 +7,6 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 
-import org.junit.internal.management.ManagementFactory;
-import org.junit.internal.management.ThreadMXBean;
-import org.junit.runners.model.MultipleFailureException;
 import org.junit.runners.model.Statement;
 import org.junit.runners.model.TestTimedOutException;
 
@@ -20,7 +14,6 @@
     private final Statement originalStatement;
     private final TimeUnit timeUnit;
     private final long timeout;
-    private final boolean lookForStuckThread;
 
     /**
      * Returns a new builder for building an instance.
@@ -47,7 +40,6 @@
         originalStatement = statement;
         timeout = builder.timeout;
         timeUnit = builder.unit;
-        lookForStuckThread = builder.lookForStuckThread;
     }
 
     /**
@@ -56,7 +48,6 @@
      * @since 4.12
      */
     public static class Builder {
-        private boolean lookForStuckThread = false;
         private long timeout = 0;
         private TimeUnit unit = TimeUnit.SECONDS;
 
@@ -89,20 +80,6 @@
         }
 
         /**
-         * Specifies whether to look for a stuck thread.  If a timeout occurs and this
-         * feature is enabled, the test will look for a thread that appears to be stuck
-         * and dump its backtrace.  This feature is experimental.  Behavior may change
-         * after the 4.12 release in response to feedback.
-         *
-         * @param enable {@code true} to enable the feature
-         * @return {@code this} for method chaining.
-         */
-        public Builder withLookingForStuckThread(boolean enable) {
-            this.lookForStuckThread = enable;
-            return this;
-        }
-
-        /**
          * Builds a {@link FailOnTimeout} instance using the values in this builder,
          * wrapping the given statement.
          *
@@ -120,8 +97,7 @@
     public void evaluate() throws Throwable {
         CallableStatement callable = new CallableStatement();
         FutureTask<Throwable> task = new FutureTask<Throwable>(callable);
-        ThreadGroup threadGroup = threadGroupForNewThread();
-        Thread thread = new Thread(threadGroup, task, "Time-limited test");
+        Thread thread = new Thread(task, "Time-limited test");
         thread.setDaemon(true);
         thread.start();
         callable.awaitStarted();
@@ -131,31 +107,6 @@
         }
     }
 
-    private ThreadGroup threadGroupForNewThread() {
-        if (!lookForStuckThread) {
-            // Use the default ThreadGroup (usually the one from the current
-            // thread).
-            return null;
-        }
-
-        // Create the thread in a new ThreadGroup, so if the time-limited thread
-        // becomes stuck, getStuckThread() can find the thread likely to be the
-        // culprit.
-        ThreadGroup threadGroup = new ThreadGroup("FailOnTimeoutGroup");
-        if (!threadGroup.isDaemon()) {
-            // Mark the new ThreadGroup as a daemon thread group, so it will be
-            // destroyed after the time-limited thread completes. By ensuring the
-            // ThreadGroup is destroyed, any data associated with the ThreadGroup
-            // (ex: via java.beans.ThreadGroupContext) is destroyed.
-            try {
-                threadGroup.setDaemon(true);
-            } catch (SecurityException e) {
-                // Swallow the exception to keep the same behavior as in JUnit 4.12.
-            }
-        }
-        return threadGroup;
-    }
-
     /**
      * Wait for the test task, returning the exception thrown by the test if the
      * test failed, an exception indicating a timeout if the test timed out, or
@@ -180,114 +131,12 @@
 
     private Exception createTimeoutException(Thread thread) {
         StackTraceElement[] stackTrace = thread.getStackTrace();
-        final Thread stuckThread = lookForStuckThread ? getStuckThread(thread) : null;
         Exception currThreadException = new TestTimedOutException(timeout, timeUnit);
         if (stackTrace != null) {
             currThreadException.setStackTrace(stackTrace);
             thread.interrupt();
         }
-        if (stuckThread != null) {
-            Exception stuckThreadException = 
-                new Exception("Appears to be stuck in thread " +
-                               stuckThread.getName());
-            stuckThreadException.setStackTrace(getStackTrace(stuckThread));
-            return new MultipleFailureException(
-                Arrays.<Throwable>asList(currThreadException, stuckThreadException));
-        } else {
-            return currThreadException;
-        }
-    }
-
-    /**
-     * Retrieves the stack trace for a given thread.
-     * @param thread The thread whose stack is to be retrieved.
-     * @return The stack trace; returns a zero-length array if the thread has 
-     * terminated or the stack cannot be retrieved for some other reason.
-     */
-    private StackTraceElement[] getStackTrace(Thread thread) {
-        try {
-            return thread.getStackTrace();
-        } catch (SecurityException e) {
-            return new StackTraceElement[0];
-        }
-    }
-
-    /**
-     * Determines whether the test appears to be stuck in some thread other than
-     * the "main thread" (the one created to run the test).  This feature is experimental.
-     * Behavior may change after the 4.12 release in response to feedback.
-     * @param mainThread The main thread created by {@code evaluate()}
-     * @return The thread which appears to be causing the problem, if different from
-     * {@code mainThread}, or {@code null} if the main thread appears to be the
-     * problem or if the thread cannot be determined.  The return value is never equal 
-     * to {@code mainThread}.
-     */
-    private Thread getStuckThread(Thread mainThread) {
-        List<Thread> threadsInGroup = getThreadsInGroup(mainThread.getThreadGroup());
-        if (threadsInGroup.isEmpty()) {
-            return null;
-        }
-
-        // Now that we have all the threads in the test's thread group: Assume that
-        // any thread we're "stuck" in is RUNNABLE.  Look for all RUNNABLE threads. 
-        // If just one, we return that (unless it equals threadMain).  If there's more
-        // than one, pick the one that's using the most CPU time, if this feature is
-        // supported.
-        Thread stuckThread = null;
-        long maxCpuTime = 0;
-        for (Thread thread : threadsInGroup) {
-            if (thread.getState() == Thread.State.RUNNABLE) {
-                long threadCpuTime = cpuTime(thread);
-                if (stuckThread == null || threadCpuTime > maxCpuTime) {
-                    stuckThread = thread;
-                    maxCpuTime = threadCpuTime;
-                }
-            }               
-        }
-        return (stuckThread == mainThread) ? null : stuckThread;
-    }
-
-    /**
-     * Returns all active threads belonging to a thread group.  
-     * @param group The thread group.
-     * @return The active threads in the thread group.  The result should be a
-     * complete list of the active threads at some point in time.  Returns an empty list
-     * if this cannot be determined, e.g. because new threads are being created at an
-     * extremely fast rate.
-     */
-    private List<Thread> getThreadsInGroup(ThreadGroup group) {
-        final int activeThreadCount = group.activeCount(); // this is just an estimate
-        int threadArraySize = Math.max(activeThreadCount * 2, 100);
-        for (int loopCount = 0; loopCount < 5; loopCount++) {
-            Thread[] threads = new Thread[threadArraySize];
-            int enumCount = group.enumerate(threads);
-            if (enumCount < threadArraySize) {
-                return Arrays.asList(threads).subList(0, enumCount);
-            }
-            // if there are too many threads to fit into the array, enumerate's result
-            // is >= the array's length; therefore we can't trust that it returned all
-            // the threads.  Try again.
-            threadArraySize += 100;
-        }
-        // threads are proliferating too fast for us.  Bail before we get into 
-        // trouble.
-        return Collections.emptyList();
-    }
-
-    /**
-     * Returns the CPU time used by a thread, if possible.
-     * @param thr The thread to query.
-     * @return The CPU time used by {@code thr}, or 0 if it cannot be determined.
-     */
-    private long cpuTime(Thread thr) {
-        ThreadMXBean mxBean = ManagementFactory.getThreadMXBean();
-        if (mxBean.isThreadCpuTimeSupported()) {
-            try {
-                return mxBean.getThreadCpuTime(thr.getId());
-            } catch (UnsupportedOperationException e) {
-            }
-        }
-        return 0;
+        return currThreadException;
     }
 
     private class CallableStatement implements Callable<Throwable> {
diff --git a/src/main/java/org/junit/rules/Timeout.java b/src/main/java/org/junit/rules/Timeout.java
index 334a923..5cf905a 100644
--- a/src/main/java/org/junit/rules/Timeout.java
+++ b/src/main/java/org/junit/rules/Timeout.java
@@ -40,7 +40,6 @@
 public class Timeout implements TestRule {
     private final long timeout;
     private final TimeUnit timeUnit;
-    private final boolean lookForStuckThread;
 
     /**
      * Returns a new builder for building an instance.
@@ -80,7 +79,6 @@
     public Timeout(long timeout, TimeUnit timeUnit) {
         this.timeout = timeout;
         this.timeUnit = timeUnit;
-        lookForStuckThread = false;
     }
 
     /**
@@ -92,7 +90,6 @@
     protected Timeout(Builder builder) {
         timeout = builder.getTimeout();
         timeUnit = builder.getTimeUnit();
-        lookForStuckThread = builder.getLookingForStuckThread();
     }
 
     /**
@@ -125,16 +122,6 @@
     }
 
     /**
-     * Gets whether this {@code Timeout} will look for a stuck thread
-     * when the test times out.
-     *
-     * @since 4.12
-     */
-    protected final boolean getLookingForStuckThread() {
-        return lookForStuckThread;
-    }
-
-    /**
      * Creates a {@link Statement} that will run the given
      * {@code statement}, and timeout the operation based
      * on the values configured in this rule. Subclasses
@@ -146,7 +133,6 @@
             Statement statement) throws Exception {
         return FailOnTimeout.builder()
             .withTimeout(timeout, timeUnit)
-            .withLookingForStuckThread(lookForStuckThread)
             .build(statement);
     }
 
@@ -205,25 +191,6 @@
         }
 
         /**
-         * Specifies whether to look for a stuck thread.  If a timeout occurs and this
-         * feature is enabled, the rule will look for a thread that appears to be stuck
-         * and dump its backtrace.  This feature is experimental.  Behavior may change
-         * after the 4.12 release in response to feedback.
-         *
-         * @param enable {@code true} to enable the feature
-         * @return {@code this} for method chaining.
-         */
-        public Builder withLookingForStuckThread(boolean enable) {
-            this.lookForStuckThread = enable;
-            return this;
-        }
-
-        protected boolean getLookingForStuckThread() {
-            return lookForStuckThread;
-        }
-
-
-        /**
          * Builds a {@link Timeout} instance using the values in this builder.,
          */
         public Timeout build() {