Check that ClassValue is visible in unstarted runtime.

Bug: 32299208
Test: art/test.py -b --host -r
Change-Id: Ib05ebcff4a8ed530ad64ae37bd1eb5a01d9e5849
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 0d40e13..cafd7c4 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -47,6 +47,7 @@
 #include "mirror/array-alloc-inl.h"
 #include "mirror/array-inl.h"
 #include "mirror/class-alloc-inl.h"
+#include "mirror/class.h"
 #include "mirror/executable-inl.h"
 #include "mirror/field.h"
 #include "mirror/method.h"
@@ -138,6 +139,10 @@
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
 
   ObjPtr<mirror::Class> found = class_linker->FindClass(self, descriptor.c_str(), class_loader);
+  if (found != nullptr && !found->CheckIsVisibleWithTargetSdk(self)) {
+    CHECK(self->IsExceptionPending());
+    return;
+  }
   if (found != nullptr && initialize_class) {
     StackHandleScope<1> hs(self);
     HandleWrapperObjPtr<mirror::Class> h_class = hs.NewHandleWrapper(&found);
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 7967600..6458613 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -27,6 +27,7 @@
 #include "art_method-inl.h"
 #include "base/enums.h"
 #include "base/logging.h"  // For VLOG.
+#include "base/sdk_version.h"
 #include "base/utils.h"
 #include "class-inl.h"
 #include "class_ext-inl.h"
@@ -2128,6 +2129,19 @@
   return res;
 }
 
+bool Class::CheckIsVisibleWithTargetSdk(Thread* self) {
+  uint32_t targetSdkVersion = Runtime::Current()->GetTargetSdkVersion();
+  if (IsSdkVersionSetAndAtMost(targetSdkVersion, SdkVersion::kT)) {
+    ObjPtr<mirror::Class> java_lang_ClassValue =
+        WellKnownClasses::ToClass(WellKnownClasses::java_lang_ClassValue);
+    if (this == java_lang_ClassValue.Ptr()) {
+      self->ThrowNewException("Ljava/lang/ClassNotFoundException;", "java.lang.ClassValue");
+      return false;
+    }
+  }
+  return true;
+}
+
 ArtMethod* Class::FindAccessibleInterfaceMethod(ArtMethod* implementation_method,
                                                 PointerSize pointer_size)
     REQUIRES_SHARED(Locks::mutator_lock_) {
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index ca13462..bd37534 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -1356,6 +1356,13 @@
   size_t GetMethodIdOffset(ArtMethod* method, PointerSize pointer_size)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  // Returns whether the class should be visible to an app.
+  // Notorious example is java.lang.ClassValue, which was added in Android U and proguarding tools
+  // used that as justification to remove computeValue method implementation. Such an app running
+  // on U+ will fail with AbstractMethodError as computeValue is not implemented.
+  // See b/259501764.
+  bool CheckIsVisibleWithTargetSdk(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_);
+
  private:
   template <typename T, VerifyObjectFlags kVerifyFlags, typename Visitor>
   void FixupNativePointer(
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index c5fdda7..593a454 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -21,7 +21,6 @@
 #include "art_field-inl.h"
 #include "art_method-alloc-inl.h"
 #include "base/enums.h"
-#include "base/sdk_version.h"
 #include "class_linker-inl.h"
 #include "class_root-inl.h"
 #include "common_throws.h"
@@ -45,7 +44,6 @@
 #include "mirror/proxy.h"
 #include "mirror/string-alloc-inl.h"
 #include "mirror/string-inl.h"
-#include "mirror/string.h"
 #include "native_util.h"
 #include "nativehelper/jni_macros.h"
 #include "nativehelper/scoped_local_ref.h"
@@ -140,14 +138,9 @@
   // So far ClassValue is the only class with such a problem and hence this
   // ad-hoc check.
   // See b/259501764.
-  uint32_t targetSdkVersion = Runtime::Current()->GetTargetSdkVersion();
-  if (IsSdkVersionSetAndAtMost(targetSdkVersion, SdkVersion::kT)) {
-    ObjPtr<mirror::Class> java_lang_ClassValue =
-        WellKnownClasses::ToClass(WellKnownClasses::java_lang_ClassValue);
-    if (c.Get() == java_lang_ClassValue.Ptr()) {
-      soa.Self()->ThrowNewException("Ljava/lang/ClassNotFoundException;", "java.lang.ClassValue");
-      return nullptr;
-    }
+  if (!c->CheckIsVisibleWithTargetSdk(soa.Self())) {
+    DCHECK(soa.Self()->IsExceptionPending());
+    return nullptr;
   }
 
   return soa.AddLocalReference<jclass>(c.Get());
diff --git a/test/2252-class-value-before-and-after-u/src-art/Main.java b/test/2252-class-value-before-and-after-u/src-art/Main.java
index 88746e1..53704d7 100644
--- a/test/2252-class-value-before-and-after-u/src-art/Main.java
+++ b/test/2252-class-value-before-and-after-u/src-art/Main.java
@@ -18,6 +18,14 @@
 
 public class Main {
     public static void main(String[] args) {
+        VMRuntime.getRuntime().setTargetSdkVersion(0);
+        try {
+            Class classValueClass = Class.forName("java.lang.ClassValue");
+        } catch (ClassNotFoundException ignored) {
+            throw new Error(
+                    "java.lang.ClassValue should be available when targetSdkLevel is not set");
+        }
+
         VMRuntime.getRuntime().setTargetSdkVersion(34);
 
         try {
@@ -30,6 +38,10 @@
         try {
             Class classValueClass = Class.forName("java.lang.ClassValue");
             throw new Error("Was able to find " + classValueClass + " on targetSdkLevel 33");
-        } catch (ClassNotFoundException expected) {}
+        } catch (ClassNotFoundException expected) {
+            if (!expected.getMessage().contains("java.lang.ClassValue")) {
+                throw new Error("Thrown exception should contain class name, but was: " + expected);
+            }
+        }
     }
 }