ART: Add currentThread cutout to Unstarted Runtime

Add currentThread() and getThreadState() cutouts to the unstarted
runtime to allow further compile-time initialization. The cutouts
are protected by call-stack checks.

Add tests.

Bug: 34956610
Test: m test-art-host-gtest-unstarted_runtime_test
Change-Id: I6335bccda8bedae90376fc7c47b303576f1ac78b
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 545cc1a..272dfa9 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -897,11 +897,13 @@
     REQUIRES_SHARED(Locks::mutator_lock_) {
   for (const std::string& allowed_caller : allowed_call_stack) {
     if (shadow_frame->GetLink() == nullptr) {
+      LOG(ERROR) << "Link is unexpectedly null";
       return false;
     }
 
     std::string found_caller = ArtMethod::PrettyMethod(shadow_frame->GetLink()->GetMethod());
     if (allowed_caller != found_caller) {
+      LOG(ERROR) << "Non-match: " << allowed_caller << " vs " << found_caller;
       return false;
     }
 
@@ -955,6 +957,59 @@
   }
 }
 
+void UnstartedRuntime::UnstartedThreadCurrentThread(
+    Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset ATTRIBUTE_UNUSED) {
+  if (CheckCallers(shadow_frame,
+                   { "void java.lang.Thread.init(java.lang.ThreadGroup, java.lang.Runnable, "
+                         "java.lang.String, long)",
+                     "void java.lang.Thread.<init>()",
+                     "void java.util.logging.LogManager$Cleaner.<init>("
+                         "java.util.logging.LogManager)" })) {
+    // Whitelist LogManager$Cleaner, which is an unstarted Thread (for a shutdown hook). The
+    // Thread constructor only asks for the current thread to set up defaults and add the
+    // thread as unstarted to the ThreadGroup. A faked-up main thread peer is good enough for
+    // these purposes.
+    Runtime::Current()->InitThreadGroups(self);
+    jobject main_peer =
+        self->CreateCompileTimePeer(self->GetJniEnv(),
+                                    "main",
+                                    false,
+                                    Runtime::Current()->GetMainThreadGroup());
+    if (main_peer == nullptr) {
+      AbortTransactionOrFail(self, "Failed allocating peer");
+      return;
+    }
+
+    result->SetL(self->DecodeJObject(main_peer));
+    self->GetJniEnv()->DeleteLocalRef(main_peer);
+  } else {
+    AbortTransactionOrFail(self,
+                           "Thread.currentThread() does not support %s",
+                           GetImmediateCaller(shadow_frame).c_str());
+  }
+}
+
+void UnstartedRuntime::UnstartedThreadGetNativeState(
+    Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset ATTRIBUTE_UNUSED) {
+  if (CheckCallers(shadow_frame,
+                   { "java.lang.Thread$State java.lang.Thread.getState()",
+                     "java.lang.ThreadGroup java.lang.Thread.getThreadGroup()",
+                     "void java.lang.Thread.init(java.lang.ThreadGroup, java.lang.Runnable, "
+                         "java.lang.String, long)",
+                     "void java.lang.Thread.<init>()",
+                     "void java.util.logging.LogManager$Cleaner.<init>("
+                         "java.util.logging.LogManager)" })) {
+    // Whitelist reading the state of the "main" thread when creating another (unstarted) thread
+    // for LogManager. Report the thread as "new" (it really only counts that it isn't terminated).
+    constexpr int32_t kJavaRunnable = 1;
+    result->SetI(kJavaRunnable);
+  } else {
+    AbortTransactionOrFail(self,
+                           "Thread.getNativeState() does not support %s",
+                           GetImmediateCaller(shadow_frame).c_str());
+  }
+}
+
 void UnstartedRuntime::UnstartedMathCeil(
     Thread* self ATTRIBUTE_UNUSED, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
   result->SetD(ceil(shadow_frame->GetVRegDouble(arg_offset)));