Overlayable actor enforcement

Validates that the caller of an OverlayManager API that mutates state
is actually allowed to act on the target as defined in the target's
overlayable tag.

<overlayable name="MyResources" actor="namespace/name">

An actor is valid if any of the following is true:
 - is root/system
 - is the target overlay package
 - has the CHANGE_OVERLAY_PACKAGES permission and an actor is not defined
 - is the same package name as the sole resolved Activity for the actor specified
     in the overlayable definition, with only pre-installed, namespaced actors
     currently supported

Bug: 119442583
Bug: 135052950

Test: atest SystemConfigNamedActorTest
Test: atest com.android.server.om

Change-Id: If56b9e8366852eaef84f6bb25c3e6871eaa3f219
diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp
index 6370253..f3a626e 100644
--- a/core/jni/android_content_res_ApkAssets.cpp
+++ b/core/jni/android_content_res_ApkAssets.cpp
@@ -194,6 +194,59 @@
   return reinterpret_cast<jlong>(xml_tree.release());
 }
 
+static jobject NativeGetOverlayableInfo(JNIEnv* env, jclass /*clazz*/, jlong ptr,
+                                         jstring overlayable_name) {
+  const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
+
+  const auto& packages = apk_assets->GetLoadedArsc()->GetPackages();
+  if (packages.empty()) {
+    jniThrowException(env, "java/io/IOException", "Error reading overlayable from APK");
+    return 0;
+  }
+
+  // TODO(b/119899133): Convert this to a search for the info rather than assuming it's at index 0
+  const auto& overlayable_map = packages[0]->GetOverlayableMap();
+  if (overlayable_map.empty()) {
+    return nullptr;
+  }
+
+  auto overlayable_name_native = std::string(env->GetStringUTFChars(overlayable_name, NULL));
+  auto actor = overlayable_map.find(overlayable_name_native);
+  if (actor == overlayable_map.end()) {
+    return nullptr;
+  }
+
+  jstring actor_string = env->NewStringUTF(actor->first.c_str());
+  if (env->ExceptionCheck() || actor_string == nullptr) {
+    jniThrowException(env, "java/io/IOException", "Error reading overlayable from APK");
+    return 0;
+  }
+
+  jclass overlayable_class = env->FindClass("android/content/om/OverlayableInfo");
+  jmethodID overlayable_constructor = env->GetMethodID(overlayable_class, "<init>",
+                                                       "(Ljava/lang/String;Ljava/lang/String;I)V");
+  return env->NewObject(
+      overlayable_class,
+      overlayable_constructor,
+      overlayable_name,
+      actor_string
+  );
+}
+
+static jboolean NativeDefinesOverlayable(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
+  const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
+
+  const auto& packages = apk_assets->GetLoadedArsc()->GetPackages();
+  if (packages.empty()) {
+    // Must throw to prevent bypass by returning false
+    jniThrowException(env, "java/io/IOException", "Error reading overlayable from APK");
+    return 0;
+  }
+
+  const auto& overlayable_infos = packages[0]->GetOverlayableMap();
+  return overlayable_infos.empty() ? JNI_FALSE : JNI_TRUE;
+}
+
 // JNI registration.
 static const JNINativeMethod gApkAssetsMethods[] = {
     {"nativeLoad", "(Ljava/lang/String;ZZZZ)J", (void*)NativeLoad},
@@ -208,6 +261,9 @@
     {"nativeGetStringBlock", "(J)J", (void*)NativeGetStringBlock},
     {"nativeIsUpToDate", "(J)Z", (void*)NativeIsUpToDate},
     {"nativeOpenXml", "(JLjava/lang/String;)J", (void*)NativeOpenXml},
+    {"nativeGetOverlayableInfo", "(JLjava/lang/String;)Landroid/content/om/OverlayableInfo;",
+     (void*)NativeGetOverlayableInfo},
+    {"nativeDefinesOverlayable", "(J)Z", (void*)NativeDefinesOverlayable},
 };
 
 int register_android_content_res_ApkAssets(JNIEnv* env) {