Introduce VMRuntime.isValidClassLoaderContext

In the PackageManager server we need to validate class loader contexts
that we receive from an untrusted process. In order to keep class loader
context parsing logic centralized within the runtime let's create an API
which returns whether or not the runtime can parse the encoded class
loader context.

Test: Build system image, open app using secondary dex
Test: atest atest com.android.server.pm.dex.DexManagerTests
Test: m test-art-host-gtest-class_loader_context_test
Change-Id: Ied676e3239f70a8ab9d066df79f377b7036b28b7
diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc
index 5dfbfe9..d2a1b7f 100644
--- a/runtime/class_loader_context.cc
+++ b/runtime/class_loader_context.cc
@@ -1217,6 +1217,11 @@
   return results;
 }
 
+bool ClassLoaderContext::IsValidEncoding(const std::string& possible_encoded_class_loader_context) {
+  return ClassLoaderContext::Create(possible_encoded_class_loader_context.c_str()) != nullptr
+      || possible_encoded_class_loader_context == kUnsupportedClassLoaderContextEncoding;
+}
+
 ClassLoaderContext::VerificationResult ClassLoaderContext::VerifyClassLoaderContextMatch(
     const std::string& context_spec,
     bool verify_names,
diff --git a/runtime/class_loader_context.h b/runtime/class_loader_context.h
index 73b8476..42ecd3d 100644
--- a/runtime/class_loader_context.h
+++ b/runtime/class_loader_context.h
@@ -202,6 +202,10 @@
   static std::map<std::string, std::string> EncodeClassPathContextsForClassLoader(
       jobject class_loader);
 
+  // Returns whether `encoded_class_loader_context` is a valid encoded ClassLoaderContext or
+  // EncodedUnsupportedClassLoaderContext.
+  static bool IsValidEncoding(const std::string& possible_encoded_class_loader_context);
+
   struct ClassLoaderInfo {
     // The type of this class loader.
     ClassLoaderType type;
diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc
index e082c8c..4d7e390 100644
--- a/runtime/class_loader_context_test.cc
+++ b/runtime/class_loader_context_test.cc
@@ -1316,6 +1316,20 @@
       encodings.at(GetTestDexFileName("MultiDex")));
 }
 
+TEST_F(ClassLoaderContextTest, IsValidEncoding) {
+  ASSERT_TRUE(ClassLoaderContext::IsValidEncoding("PCL[]"));
+  ASSERT_TRUE(ClassLoaderContext::IsValidEncoding("PCL[foo.dex]"));
+  ASSERT_TRUE(ClassLoaderContext::IsValidEncoding("PCL[foo.dex];PCL[bar.dex]"));
+  ASSERT_TRUE(ClassLoaderContext::IsValidEncoding("DLC[];PCL[bar.dex]"));
+  ASSERT_TRUE(
+      ClassLoaderContext::IsValidEncoding(
+        ClassLoaderContext::kUnsupportedClassLoaderContextEncoding));
+  ASSERT_FALSE(ClassLoaderContext::IsValidEncoding("not_valid"));
+  ASSERT_FALSE(ClassLoaderContext::IsValidEncoding("[]"));
+  ASSERT_FALSE(ClassLoaderContext::IsValidEncoding("FCL[]"));
+  ASSERT_FALSE(ClassLoaderContext::IsValidEncoding("foo.dex:bar.dex"));
+}
+
 // TODO(calin) add a test which creates the context for a class loader together with dex_elements.
 TEST_F(ClassLoaderContextTest, CreateContextForClassLoader) {
   // The chain is
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index a4dc042..dbfa2a8 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -34,6 +34,7 @@
 #include "base/enums.h"
 #include "base/sdk_version.h"
 #include "class_linker-inl.h"
+#include "class_loader_context.h"
 #include "common_throws.h"
 #include "debugger.h"
 #include "dex/class_accessor-inl.h"
@@ -767,6 +768,18 @@
   Runtime::Current()->GetClassLinker()->VisitClasses(&visitor);
 }
 
+static jboolean VMRuntime_isValidClassLoaderContext(JNIEnv* env,
+                                                    jclass klass ATTRIBUTE_UNUSED,
+                                                    jstring jencoded_class_loader_context) {
+  if (UNLIKELY(jencoded_class_loader_context == nullptr)) {
+    ScopedFastNativeObjectAccess soa(env);
+    ThrowNullPointerException("encoded_class_loader_context == null");
+    return false;
+  }
+  ScopedUtfChars encoded_class_loader_context(env, jencoded_class_loader_context);
+  return ClassLoaderContext::IsValidEncoding(encoded_class_loader_context.c_str());
+}
+
 static JNINativeMethod gMethods[] = {
   FAST_NATIVE_METHOD(VMRuntime, addressOf, "(Ljava/lang/Object;)J"),
   NATIVE_METHOD(VMRuntime, bootClassPath, "()Ljava/lang/String;"),
@@ -818,6 +831,7 @@
   NATIVE_METHOD(VMRuntime, setProcessDataDirectory, "(Ljava/lang/String;)V"),
   NATIVE_METHOD(VMRuntime, bootCompleted, "()V"),
   NATIVE_METHOD(VMRuntime, resetJitCounters, "()V"),
+  NATIVE_METHOD(VMRuntime, isValidClassLoaderContext, "(Ljava/lang/String;)Z"),
 };
 
 void register_dalvik_system_VMRuntime(JNIEnv* env) {