diff --git a/core/java/android/os/VintfObject.java b/core/java/android/os/VintfObject.java
index 505655775239d06d4152f32e61213b7a765ec05a..bb89e0791053c94fbddc2f4d816c2298065eba6b 100644
--- a/core/java/android/os/VintfObject.java
+++ b/core/java/android/os/VintfObject.java
@@ -17,8 +17,11 @@
 package android.os;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.TestApi;
+import android.app.ActivityThread;
 
+import java.io.IOException;
 import java.util.Map;
 
 /**
@@ -113,5 +116,20 @@ public class VintfObject {
     @TestApi
     public static native Long getTargetFrameworkCompatibilityMatrixVersion();
 
+    /**
+     * Executes a shell command using shell user identity, and return the standard output in string.
+     *
+     * @hide
+     */
+    private static @Nullable String runShellCommand(@NonNull String command) throws IOException {
+        var activityThread = ActivityThread.currentActivityThread();
+        var instrumentation = activityThread.getInstrumentation();
+        var automation = instrumentation.getUiAutomation();
+        var pfd = automation.executeShellCommand(command);
+        try (var is = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
+            return new String(is.readAllBytes());
+        }
+    }
+
     private VintfObject() {}
 }
diff --git a/core/jni/android_os_VintfObject.cpp b/core/jni/android_os_VintfObject.cpp
index 8dc9d0aa578e8051c0be3cf54802727508676665..7a4854bbd260405f0192545d77ae92d882ba3cd6 100644
--- a/core/jni/android_os_VintfObject.cpp
+++ b/core/jni/android_os_VintfObject.cpp
@@ -32,6 +32,8 @@ static jmethodID gHashMapInit;
 static jmethodID gHashMapPut;
 static jclass gLongClazz;
 static jmethodID gLongValueOf;
+static jclass gVintfObjectClazz;
+static jmethodID gRunCommand;
 
 namespace android {
 
@@ -47,6 +49,56 @@ using vintf::VintfObject;
 using vintf::Vndk;
 using vintf::CheckFlags::ENABLE_ALL_CHECKS;
 
+// Instead of VintfObject::GetXxx(), we construct
+// HalManifest/CompatibilityMatrix objects by calling `vintf` through
+// UiAutomation.executeShellCommand() so that the commands are executed
+// using shell identity. Otherwise, we would need to allow "apps" to access
+// files like apex-info-list.xml which we don't want to open to apps.
+// This is okay because VintfObject is @TestApi and only used in CTS tests.
+
+static std::string runCmd(JNIEnv* env, const char* cmd) {
+    jstring jstr = (jstring)env->CallStaticObjectMethod(gVintfObjectClazz, gRunCommand,
+                                                        env->NewStringUTF(cmd));
+    std::string output;
+    if (jstr) {
+        auto cstr = env->GetStringUTFChars(jstr, nullptr);
+        output = std::string(cstr);
+        env->ReleaseStringUTFChars(jstr, cstr);
+    } else {
+        LOG(WARNING) << "Failed to run " << cmd;
+        env->ExceptionDescribe();
+        env->ExceptionClear();
+    }
+    return output;
+}
+
+template <typename T>
+static std::shared_ptr<const T> fromXml(const std::string& content) {
+    std::shared_ptr<T> object = std::make_unique<T>();
+    std::string error;
+    if (fromXml(object.get(), content, &error)) {
+        return object;
+    }
+    LOG(WARNING) << "Unabled to parse: " << error;
+    return nullptr;
+}
+
+static std::shared_ptr<const HalManifest> getDeviceHalManifest(JNIEnv* env) {
+    return fromXml<HalManifest>(runCmd(env, "vintf dm"));
+}
+
+static std::shared_ptr<const HalManifest> getFrameworkHalManifest(JNIEnv* env) {
+    return fromXml<HalManifest>(runCmd(env, "vintf fm"));
+}
+
+static std::shared_ptr<const CompatibilityMatrix> getDeviceCompatibilityMatrix(JNIEnv* env) {
+    return fromXml<CompatibilityMatrix>(runCmd(env, "vintf dcm"));
+}
+
+static std::shared_ptr<const CompatibilityMatrix> getFrameworkCompatibilityMatrix(JNIEnv* env) {
+    return fromXml<CompatibilityMatrix>(runCmd(env, "vintf fcm"));
+}
+
 template<typename V>
 static inline jobjectArray toJavaStringArray(JNIEnv* env, const V& v) {
     size_t i;
@@ -83,12 +135,10 @@ static jobjectArray android_os_VintfObject_report(JNIEnv* env, jclass)
 {
     std::vector<std::string> cStrings;
 
-    tryAddSchema(VintfObject::GetDeviceHalManifest(), "device manifest", &cStrings);
-    tryAddSchema(VintfObject::GetFrameworkHalManifest(), "framework manifest", &cStrings);
-    tryAddSchema(VintfObject::GetDeviceCompatibilityMatrix(), "device compatibility matrix",
-                 &cStrings);
-    tryAddSchema(VintfObject::GetFrameworkCompatibilityMatrix(), "framework compatibility matrix",
-                 &cStrings);
+    tryAddSchema(getDeviceHalManifest(env), "device manifest", &cStrings);
+    tryAddSchema(getFrameworkHalManifest(env), "framework manifest", &cStrings);
+    tryAddSchema(getDeviceCompatibilityMatrix(env), "device compatibility matrix", &cStrings);
+    tryAddSchema(getFrameworkCompatibilityMatrix(env), "framework compatibility matrix", &cStrings);
 
     return toJavaStringArray(env, cStrings);
 }
@@ -108,15 +158,13 @@ static jint android_os_VintfObject_verifyBuildAtBoot(JNIEnv*, jclass) {
 
 static jobjectArray android_os_VintfObject_getHalNamesAndVersions(JNIEnv* env, jclass) {
     std::set<std::string> halNames;
-    tryAddHalNamesAndVersions(VintfObject::GetDeviceHalManifest(),
-            "device manifest", &halNames);
-    tryAddHalNamesAndVersions(VintfObject::GetFrameworkHalManifest(),
-            "framework manifest", &halNames);
+    tryAddHalNamesAndVersions(getDeviceHalManifest(env), "device manifest", &halNames);
+    tryAddHalNamesAndVersions(getFrameworkHalManifest(env), "framework manifest", &halNames);
     return toJavaStringArray(env, halNames);
 }
 
 static jstring android_os_VintfObject_getSepolicyVersion(JNIEnv* env, jclass) {
-    std::shared_ptr<const HalManifest> manifest = VintfObject::GetDeviceHalManifest();
+    std::shared_ptr<const HalManifest> manifest = getDeviceHalManifest(env);
     if (manifest == nullptr || manifest->type() != SchemaType::DEVICE) {
         LOG(WARNING) << __FUNCTION__ << "Cannot get device manifest";
         return nullptr;
@@ -126,8 +174,7 @@ static jstring android_os_VintfObject_getSepolicyVersion(JNIEnv* env, jclass) {
 }
 
 static jstring android_os_VintfObject_getPlatformSepolicyVersion(JNIEnv* env, jclass) {
-    std::shared_ptr<const CompatibilityMatrix> matrix =
-            VintfObject::GetFrameworkCompatibilityMatrix();
+    std::shared_ptr<const CompatibilityMatrix> matrix = getFrameworkCompatibilityMatrix(env);
     if (matrix == nullptr || matrix->type() != SchemaType::FRAMEWORK) {
         jniThrowRuntimeException(env, "Cannot get framework compatibility matrix");
         return nullptr;
@@ -148,7 +195,7 @@ static jstring android_os_VintfObject_getPlatformSepolicyVersion(JNIEnv* env, jc
 }
 
 static jobject android_os_VintfObject_getVndkSnapshots(JNIEnv* env, jclass) {
-    std::shared_ptr<const HalManifest> manifest = VintfObject::GetFrameworkHalManifest();
+    std::shared_ptr<const HalManifest> manifest = getFrameworkHalManifest(env);
     if (manifest == nullptr || manifest->type() != SchemaType::FRAMEWORK) {
         LOG(WARNING) << __FUNCTION__ << "Cannot get framework manifest";
         return nullptr;
@@ -163,7 +210,7 @@ static jobject android_os_VintfObject_getVndkSnapshots(JNIEnv* env, jclass) {
 }
 
 static jobject android_os_VintfObject_getTargetFrameworkCompatibilityMatrixVersion(JNIEnv* env, jclass) {
-    std::shared_ptr<const HalManifest> manifest = VintfObject::GetDeviceHalManifest();
+    std::shared_ptr<const HalManifest> manifest = getDeviceHalManifest(env);
     if (manifest == nullptr || manifest->level() == Level::UNSPECIFIED) {
         return nullptr;
     }
@@ -188,19 +235,20 @@ static const JNINativeMethod gVintfObjectMethods[] = {
 
 const char* const kVintfObjectPathName = "android/os/VintfObject";
 
-int register_android_os_VintfObject(JNIEnv* env)
-{
-
+int register_android_os_VintfObject(JNIEnv* env) {
     gString = MakeGlobalRefOrDie(env, FindClassOrDie(env, "java/lang/String"));
     gHashMapClazz = MakeGlobalRefOrDie(env, FindClassOrDie(env, "java/util/HashMap"));
     gHashMapInit = GetMethodIDOrDie(env, gHashMapClazz, "<init>", "()V");
-    gHashMapPut = GetMethodIDOrDie(env, gHashMapClazz,
-            "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
+    gHashMapPut = GetMethodIDOrDie(env, gHashMapClazz, "put",
+                                   "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
     gLongClazz = MakeGlobalRefOrDie(env, FindClassOrDie(env, "java/lang/Long"));
     gLongValueOf = GetStaticMethodIDOrDie(env, gLongClazz, "valueOf", "(J)Ljava/lang/Long;");
+    gVintfObjectClazz = MakeGlobalRefOrDie(env, FindClassOrDie(env, kVintfObjectPathName));
+    gRunCommand = GetStaticMethodIDOrDie(env, gVintfObjectClazz, "runShellCommand",
+                                         "(Ljava/lang/String;)Ljava/lang/String;");
 
     return RegisterMethodsOrDie(env, kVintfObjectPathName, gVintfObjectMethods,
-            NELEM(gVintfObjectMethods));
+                                NELEM(gVintfObjectMethods));
 }
 
 extern int register_android_os_VintfRuntimeInfo(JNIEnv* env);