ART: Add GetThreadState

Add support for GetThreadState. Add test.

Bug: 31684593
Test: m test-art-host-run-test-924-threads
Change-Id: I67a240c711e1165cfb72a856fc59ca69abaec3f6
diff --git a/test/924-threads/src/Main.java b/test/924-threads/src/Main.java
index 89881dd..0487666 100644
--- a/test/924-threads/src/Main.java
+++ b/test/924-threads/src/Main.java
@@ -15,6 +15,12 @@
  */
 
 import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.concurrent.CountDownLatch;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
 public class Main {
   public static void main(String[] args) throws Exception {
@@ -42,9 +48,152 @@
     // Start, and wait for it to die.
     t3.start();
     t3.join();
-    Thread.currentThread().sleep(500);  // Wait a little bit.
+    Thread.sleep(500);  // Wait a little bit.
     // Thread has died, check that we can still get info.
     printThreadInfo(t3);
+
+    doStateTests();
+  }
+
+  private static class Holder {
+    volatile boolean flag = false;
+  }
+
+  private static void doStateTests() throws Exception {
+    System.out.println(Integer.toHexString(getThreadState(null)));
+    System.out.println(Integer.toHexString(getThreadState(Thread.currentThread())));
+
+    final CountDownLatch cdl1 = new CountDownLatch(1);
+    final CountDownLatch cdl2 = new CountDownLatch(1);
+    final CountDownLatch cdl3_1 = new CountDownLatch(1);
+    final CountDownLatch cdl3_2 = new CountDownLatch(1);
+    final CountDownLatch cdl4 = new CountDownLatch(1);
+    final CountDownLatch cdl5 = new CountDownLatch(1);
+    final Holder h = new Holder();
+    Runnable r = new Runnable() {
+      @Override
+      public void run() {
+        try {
+          cdl1.countDown();
+          synchronized(cdl1) {
+            cdl1.wait();
+          }
+
+          cdl2.countDown();
+          synchronized(cdl2) {
+            cdl2.wait(1000);  // Wait a second.
+          }
+
+          cdl3_1.await();
+          cdl3_2.countDown();
+          synchronized(cdl3_2) {
+            // Nothing, just wanted to block on cdl3.
+          }
+
+          cdl4.countDown();
+          Thread.sleep(1000);
+
+          cdl5.countDown();
+          while (!h.flag) {
+            // Busy-loop.
+          }
+        } catch (Exception e) {
+          throw new RuntimeException(e);
+        }
+      }
+    };
+
+    Thread t = new Thread(r);
+    printThreadState(t);
+    t.start();
+
+    // Waiting.
+    cdl1.await();
+    Thread.yield();
+    Thread.sleep(100);
+    printThreadState(t);
+    synchronized(cdl1) {
+      cdl1.notifyAll();
+    }
+
+    // Timed waiting.
+    cdl2.await();
+    Thread.yield();
+    Thread.sleep(100);
+    printThreadState(t);
+    synchronized(cdl2) {
+      cdl2.notifyAll();
+    }
+
+    // Blocked on monitor.
+    synchronized(cdl3_2) {
+      cdl3_1.countDown();
+      cdl3_2.await();
+      Thread.yield();
+      Thread.sleep(100);
+      printThreadState(t);
+    }
+
+    // Sleeping.
+    cdl4.await();
+    Thread.yield();
+    Thread.sleep(100);
+    printThreadState(t);
+
+    // Running.
+    cdl5.await();
+    Thread.yield();
+    Thread.sleep(100);
+    printThreadState(t);
+    h.flag = true;
+
+    // Dying.
+    t.join();
+    Thread.yield();
+    Thread.sleep(100);
+
+    printThreadState(t);
+  }
+
+  private final static Map<Integer, String> STATE_NAMES = new HashMap<Integer, String>();
+  private final static List<Integer> STATE_KEYS = new ArrayList<Integer>();
+  static {
+    STATE_NAMES.put(0x1, "ALIVE");
+    STATE_NAMES.put(0x2, "TERMINATED");
+    STATE_NAMES.put(0x4, "RUNNABLE");
+    STATE_NAMES.put(0x400, "BLOCKED_ON_MONITOR_ENTER");
+    STATE_NAMES.put(0x80, "WAITING");
+    STATE_NAMES.put(0x10, "WAITING_INDEFINITELY");
+    STATE_NAMES.put(0x20, "WAITING_WITH_TIMEOUT");
+    STATE_NAMES.put(0x40, "SLEEPING");
+    STATE_NAMES.put(0x100, "IN_OBJECT_WAIT");
+    STATE_NAMES.put(0x200, "PARKED");
+    STATE_NAMES.put(0x100000, "SUSPENDED");
+    STATE_NAMES.put(0x200000, "INTERRUPTED");
+    STATE_NAMES.put(0x400000, "IN_NATIVE");
+    STATE_KEYS.addAll(STATE_NAMES.keySet());
+    Collections.sort(STATE_KEYS);
+  }
+  
+  private static void printThreadState(Thread t) {
+    int state = getThreadState(t);
+
+    StringBuilder sb = new StringBuilder();
+
+    for (Integer i : STATE_KEYS) {
+      if ((state & i) != 0) {
+        if (sb.length()>0) {
+          sb.append('|');
+        }
+        sb.append(STATE_NAMES.get(i));
+      }
+    }
+
+    if (sb.length() == 0) {
+      sb.append("NEW");
+    }
+
+    System.out.println(Integer.toHexString(state) + " = " + sb.toString());
   }
 
   private static void printThreadInfo(Thread t) {
@@ -63,4 +212,5 @@
 
   private static native Thread getCurrentThread();
   private static native Object[] getThreadInfo(Thread t);
+  private static native int getThreadState(Thread t);
 }