Give full access to system libs from other system libs.

In particular this addresses the case when an app without a shared
namespace loads a shared system library that in turn loads a system JNI
library.

Give full access both ways between /system and /system_ext libraries,
and add a shared system_ext library and loading from system_ext to test
that.

Cherry-picked from https://r.android.com/2211602.

Test: atest libnativeloader_e2e_tests
Bug: 237577392
Change-Id: Ief4e24dadbadd26c5602c9e593276fae01bd7038
Merged-In: Ief4e24dadbadd26c5602c9e593276fae01bd7038
diff --git a/libnativeloader/library_namespaces.cpp b/libnativeloader/library_namespaces.cpp
index 96d4dde..4fbf798 100644
--- a/libnativeloader/library_namespaces.cpp
+++ b/libnativeloader/library_namespaces.cpp
@@ -82,15 +82,18 @@
 
 constexpr const char* kVendorLibPath = "/vendor/" LIB;
 constexpr const char* kProductLibPath = "/product/" LIB ":/system/product/" LIB;
+constexpr const char* kSystemLibPath = "/system/" LIB ":/system_ext/" LIB;
 
 const std::regex kVendorDexPathRegex("(^|:)/vendor/");
 const std::regex kProductDexPathRegex("(^|:)(/system)?/product/");
+const std::regex kSystemDexPathRegex("(^|:)/system(_ext)?/");  // MUST be tested last.
 
-// Define origin of APK if it is from vendor partition or product partition
+// Define origin partition of APK
 using ApkOrigin = enum {
   APK_ORIGIN_DEFAULT = 0,
   APK_ORIGIN_VENDOR = 1,
-  APK_ORIGIN_PRODUCT = 2,
+  APK_ORIGIN_PRODUCT = 2,  // Includes both /product and /system/product
+  APK_ORIGIN_SYSTEM = 3,   // Includes both /system and /system_ext but not /system/product
 };
 
 jobject GetParentClassLoader(JNIEnv* env, jobject class_loader) {
@@ -113,6 +116,9 @@
 
     apk_origin = APK_ORIGIN_PRODUCT;
   }
+  if (apk_origin == APK_ORIGIN_DEFAULT && std::regex_search(dex_path, kSystemDexPathRegex)) {
+    apk_origin = APK_ORIGIN_SYSTEM;
+  }
   return apk_origin;
 }
 
@@ -234,7 +240,18 @@
   const char* apk_origin_msg = "other apk";  // Only for debug logging.
 
   if (!is_shared) {
-    if (apk_origin == APK_ORIGIN_VENDOR) {
+    if (apk_origin == APK_ORIGIN_SYSTEM) {
+      // System apps commonly get shared namespaces and hence don't need this.
+      // In practice it's necessary for shared system libraries (i.e. JARs
+      // rather than actual APKs) that are loaded by ordinary apps which don't
+      // get shared namespaces.
+      apk_origin_msg = "system apk";
+
+      // Give access to all libraries in the system and system_ext partitions
+      // (they can freely access each other's private APIs).
+      library_path = library_path + ":" + kSystemLibPath;
+      permitted_path = permitted_path + ":" + kSystemLibPath;
+    } else if (apk_origin == APK_ORIGIN_VENDOR) {
       unbundled_app_origin = APK_ORIGIN_VENDOR;
       apk_origin_msg = "unbundled vendor apk";
 
@@ -288,8 +305,7 @@
     // they are to other apps, including those in system, system_ext, and
     // product partitions. The reason is that when GSI is used, the system
     // partition may get replaced, and then vendor apps may fail. It's fine for
-    // product (and system_ext) apps, because those partitions aren't mounted in
-    // GSI tests.
+    // product apps, because that partition isn't mounted in GSI tests.
     auto libs =
         filter_public_libraries(target_sdk_version, uses_libraries, extended_public_libraries());
     if (!libs.empty()) {
diff --git a/libnativeloader/test/Android.bp b/libnativeloader/test/Android.bp
index b43a02c..1d3a07a 100644
--- a/libnativeloader/test/Android.bp
+++ b/libnativeloader/test/Android.bp
@@ -55,6 +55,13 @@
     srcs: ["src/android/test/systemsharedlib/SystemSharedLib.java"],
 }
 
+// Test fixture that represents a shared library in /system_ext/framework.
+java_library {
+    name: "libnativeloader_system_ext_shared_lib",
+    installable: true,
+    srcs: ["src/android/test/systemextsharedlib/SystemExtSharedLib.java"],
+}
+
 java_defaults {
     name: "loadlibrarytest_app_defaults",
     defaults: ["art_module_source_build_java_defaults"],
@@ -63,7 +70,10 @@
         "androidx.test.rules",
         "loadlibrarytest_test_utils",
     ],
-    libs: ["libnativeloader_system_shared_lib"],
+    libs: [
+        "libnativeloader_system_shared_lib",
+        "libnativeloader_system_ext_shared_lib",
+    ],
 }
 
 android_test_helper_app {
@@ -125,6 +135,7 @@
     data: [
         ":library_container_app",
         ":libnativeloader_system_shared_lib",
+        ":libnativeloader_system_ext_shared_lib",
         ":loadlibrarytest_system_priv_app",
         ":loadlibrarytest_system_app",
         ":loadlibrarytest_system_ext_app",
diff --git a/libnativeloader/test/loadlibrarytest_data_app_manifest.xml b/libnativeloader/test/loadlibrarytest_data_app_manifest.xml
index 9b663e6..2af0af4 100644
--- a/libnativeloader/test/loadlibrarytest_data_app_manifest.xml
+++ b/libnativeloader/test/loadlibrarytest_data_app_manifest.xml
@@ -21,6 +21,7 @@
                      android:targetPackage="android.test.app.data" />
     <application>
         <uses-library android:name="android.test.systemsharedlib" />
+        <uses-library android:name="android.test.systemextsharedlib" />
         <uses-native-library android:required="false" android:name="libfoo.oem1.so" />
         <uses-native-library android:required="false" android:name="libbar.oem1.so" />
         <uses-native-library android:required="false" android:name="libfoo.oem2.so" />
diff --git a/libnativeloader/test/loadlibrarytest_product_app_manifest.xml b/libnativeloader/test/loadlibrarytest_product_app_manifest.xml
index c1d997a..614f33f 100644
--- a/libnativeloader/test/loadlibrarytest_product_app_manifest.xml
+++ b/libnativeloader/test/loadlibrarytest_product_app_manifest.xml
@@ -21,6 +21,7 @@
                      android:targetPackage="android.test.app.product" />
     <application>
         <uses-library android:name="android.test.systemsharedlib" />
+        <uses-library android:name="android.test.systemextsharedlib" />
         <uses-native-library android:required="false" android:name="libfoo.oem1.so" />
         <uses-native-library android:required="false" android:name="libbar.oem1.so" />
         <uses-native-library android:required="false" android:name="libfoo.oem2.so" />
diff --git a/libnativeloader/test/loadlibrarytest_system_app_manifest.xml b/libnativeloader/test/loadlibrarytest_system_app_manifest.xml
index 5c6af09..5711f65 100644
--- a/libnativeloader/test/loadlibrarytest_system_app_manifest.xml
+++ b/libnativeloader/test/loadlibrarytest_system_app_manifest.xml
@@ -21,6 +21,7 @@
                      android:targetPackage="android.test.app.system" />
     <application>
         <uses-library android:name="android.test.systemsharedlib" />
+        <uses-library android:name="android.test.systemextsharedlib" />
         <!-- System apps get a shared classloader namespace, so they don't need
              uses-native-library entries for anything in /system. -->
         <uses-native-library android:required="false" android:name="libfoo.product1.so" />
diff --git a/libnativeloader/test/loadlibrarytest_system_ext_app_manifest.xml b/libnativeloader/test/loadlibrarytest_system_ext_app_manifest.xml
index 961f9ba..8aa3fa9 100644
--- a/libnativeloader/test/loadlibrarytest_system_ext_app_manifest.xml
+++ b/libnativeloader/test/loadlibrarytest_system_ext_app_manifest.xml
@@ -21,6 +21,7 @@
                      android:targetPackage="android.test.app.system_ext" />
     <application>
         <uses-library android:name="android.test.systemsharedlib" />
+        <uses-library android:name="android.test.systemextsharedlib" />
         <!-- System apps get a shared classloader namespace, so they don't need
              uses-native-library entries for anything in /system. -->
         <uses-native-library android:required="false" android:name="libfoo.product1.so" />
diff --git a/libnativeloader/test/loadlibrarytest_system_priv_app_manifest.xml b/libnativeloader/test/loadlibrarytest_system_priv_app_manifest.xml
index f4bf3c0..126453c 100644
--- a/libnativeloader/test/loadlibrarytest_system_priv_app_manifest.xml
+++ b/libnativeloader/test/loadlibrarytest_system_priv_app_manifest.xml
@@ -21,6 +21,7 @@
                      android:targetPackage="android.test.app.system_priv" />
     <application>
         <uses-library android:name="android.test.systemsharedlib" />
+        <uses-library android:name="android.test.systemextsharedlib" />
         <!-- System apps get a shared classloader namespace, so they don't need
              uses-native-library entries for anything in /system. -->
         <uses-native-library android:required="false" android:name="libfoo.product1.so" />
diff --git a/libnativeloader/test/loadlibrarytest_vendor_app_manifest.xml b/libnativeloader/test/loadlibrarytest_vendor_app_manifest.xml
index 1a8cbcc..a2a9f64 100644
--- a/libnativeloader/test/loadlibrarytest_vendor_app_manifest.xml
+++ b/libnativeloader/test/loadlibrarytest_vendor_app_manifest.xml
@@ -21,6 +21,7 @@
                      android:targetPackage="android.test.app.vendor" />
     <application>
         <uses-library android:name="android.test.systemsharedlib" />
+        <uses-library android:name="android.test.systemextsharedlib" />
         <uses-native-library android:required="false" android:name="libfoo.oem1.so" />
         <uses-native-library android:required="false" android:name="libbar.oem1.so" />
         <uses-native-library android:required="false" android:name="libfoo.oem2.so" />
diff --git a/libnativeloader/test/src/android/test/app/DataAppTest.java b/libnativeloader/test/src/android/test/app/DataAppTest.java
index db97e8d..767a7b1 100644
--- a/libnativeloader/test/src/android/test/app/DataAppTest.java
+++ b/libnativeloader/test/src/android/test/app/DataAppTest.java
@@ -17,6 +17,7 @@
 package android.test.app;
 
 import android.test.lib.TestUtils;
+import android.test.systemextsharedlib.SystemExtSharedLib;
 import android.test.systemsharedlib.SystemSharedLib;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -40,16 +41,24 @@
     @Test
     public void testLoadPrivateLibraries() {
         TestUtils.assertLinkerNamespaceError(() -> System.loadLibrary("system_private1"));
+        TestUtils.assertLinkerNamespaceError(() -> System.loadLibrary("systemext_private1"));
         TestUtils.assertLibraryNotFound(() -> System.loadLibrary("product_private1"));
         TestUtils.assertLibraryNotFound(() -> System.loadLibrary("vendor_private1"));
     }
 
     @Test
     public void testLoadPrivateLibrariesViaSystemSharedLib() {
-        // TODO(b/237577392): Fix this use case.
-        TestUtils.assertLinkerNamespaceError(() -> SystemSharedLib.loadLibrary("system_private2"));
-
+        SystemSharedLib.loadLibrary("system_private2");
+        SystemSharedLib.loadLibrary("systemext_private2");
         TestUtils.assertLibraryNotFound(() -> SystemSharedLib.loadLibrary("product_private2"));
         TestUtils.assertLibraryNotFound(() -> SystemSharedLib.loadLibrary("vendor_private2"));
     }
+
+    @Test
+    public void testLoadPrivateLibrariesViaSystemExtSharedLib() {
+        SystemExtSharedLib.loadLibrary("system_private3");
+        SystemExtSharedLib.loadLibrary("systemext_private3");
+        TestUtils.assertLibraryNotFound(() -> SystemExtSharedLib.loadLibrary("product_private3"));
+        TestUtils.assertLibraryNotFound(() -> SystemExtSharedLib.loadLibrary("vendor_private3"));
+    }
 }
diff --git a/libnativeloader/test/src/android/test/app/ProductAppTest.java b/libnativeloader/test/src/android/test/app/ProductAppTest.java
index a9b8697..1f36798 100644
--- a/libnativeloader/test/src/android/test/app/ProductAppTest.java
+++ b/libnativeloader/test/src/android/test/app/ProductAppTest.java
@@ -17,6 +17,7 @@
 package android.test.app;
 
 import android.test.lib.TestUtils;
+import android.test.systemextsharedlib.SystemExtSharedLib;
 import android.test.systemsharedlib.SystemSharedLib;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -40,16 +41,24 @@
     @Test
     public void testLoadPrivateLibraries() {
         TestUtils.assertLinkerNamespaceError(() -> System.loadLibrary("system_private1"));
+        TestUtils.assertLinkerNamespaceError(() -> System.loadLibrary("systemext_private1"));
         System.loadLibrary("product_private1");
         TestUtils.assertLibraryNotFound(() -> System.loadLibrary("vendor_private1"));
     }
 
     @Test
     public void testLoadPrivateLibrariesViaSystemSharedLib() {
-        // TODO(b/237577392): Fix this use case.
-        TestUtils.assertLinkerNamespaceError(() -> SystemSharedLib.loadLibrary("system_private2"));
-
+        SystemSharedLib.loadLibrary("system_private2");
+        SystemSharedLib.loadLibrary("systemext_private2");
         TestUtils.assertLibraryNotFound(() -> SystemSharedLib.loadLibrary("product_private2"));
         TestUtils.assertLibraryNotFound(() -> SystemSharedLib.loadLibrary("vendor_private2"));
     }
+
+    @Test
+    public void testLoadPrivateLibrariesViaSystemExtSharedLib() {
+        SystemExtSharedLib.loadLibrary("system_private3");
+        SystemExtSharedLib.loadLibrary("systemext_private3");
+        TestUtils.assertLibraryNotFound(() -> SystemExtSharedLib.loadLibrary("product_private3"));
+        TestUtils.assertLibraryNotFound(() -> SystemExtSharedLib.loadLibrary("vendor_private3"));
+    }
 }
diff --git a/libnativeloader/test/src/android/test/app/SystemAppTest.java b/libnativeloader/test/src/android/test/app/SystemAppTest.java
index 6644478..197a40c 100644
--- a/libnativeloader/test/src/android/test/app/SystemAppTest.java
+++ b/libnativeloader/test/src/android/test/app/SystemAppTest.java
@@ -17,6 +17,7 @@
 package android.test.app;
 
 import android.test.lib.TestUtils;
+import android.test.systemextsharedlib.SystemExtSharedLib;
 import android.test.systemsharedlib.SystemSharedLib;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -40,6 +41,7 @@
     @Test
     public void testLoadPrivateLibraries() {
         System.loadLibrary("system_private1");
+        System.loadLibrary("systemext_private1");
         TestUtils.assertLibraryNotFound(() -> System.loadLibrary("product_private1"));
         TestUtils.assertLibraryNotFound(() -> System.loadLibrary("vendor_private1"));
     }
@@ -47,7 +49,16 @@
     @Test
     public void testLoadPrivateLibrariesViaSystemSharedLib() {
         SystemSharedLib.loadLibrary("system_private2");
+        SystemSharedLib.loadLibrary("systemext_private2");
         TestUtils.assertLibraryNotFound(() -> SystemSharedLib.loadLibrary("product_private2"));
         TestUtils.assertLibraryNotFound(() -> SystemSharedLib.loadLibrary("vendor_private2"));
     }
+
+    @Test
+    public void testLoadPrivateLibrariesViaSystemExtSharedLib() {
+        SystemExtSharedLib.loadLibrary("system_private3");
+        SystemExtSharedLib.loadLibrary("systemext_private3");
+        TestUtils.assertLibraryNotFound(() -> SystemExtSharedLib.loadLibrary("product_private3"));
+        TestUtils.assertLibraryNotFound(() -> SystemExtSharedLib.loadLibrary("vendor_private3"));
+    }
 }
diff --git a/libnativeloader/test/src/android/test/app/VendorAppTest.java b/libnativeloader/test/src/android/test/app/VendorAppTest.java
index 5187ac8..c9ce8db 100644
--- a/libnativeloader/test/src/android/test/app/VendorAppTest.java
+++ b/libnativeloader/test/src/android/test/app/VendorAppTest.java
@@ -17,6 +17,7 @@
 package android.test.app;
 
 import android.test.lib.TestUtils;
+import android.test.systemextsharedlib.SystemExtSharedLib;
 import android.test.systemsharedlib.SystemSharedLib;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -39,6 +40,7 @@
     @Test
     public void testLoadPrivateLibraries() {
         TestUtils.assertLinkerNamespaceError(() -> System.loadLibrary("system_private1"));
+        TestUtils.assertLinkerNamespaceError(() -> System.loadLibrary("systemext_private1"));
         TestUtils.assertLibraryNotFound(() -> System.loadLibrary("product_private1"));
         // TODO(mast): The vendor app fails to load a private vendor library because it gets
         // classified as untrusted_app in SELinux, which doesn't have access to vendor_file. Even an
@@ -49,10 +51,17 @@
 
     @Test
     public void testLoadPrivateLibrariesViaSystemSharedLib() {
-        // TODO(b/237577392): Fix this use case.
-        TestUtils.assertLinkerNamespaceError(() -> SystemSharedLib.loadLibrary("system_private2"));
-
+        SystemSharedLib.loadLibrary("system_private2");
+        SystemSharedLib.loadLibrary("systemext_private2");
         TestUtils.assertLibraryNotFound(() -> SystemSharedLib.loadLibrary("product_private2"));
         TestUtils.assertLibraryNotFound(() -> SystemSharedLib.loadLibrary("vendor_private2"));
     }
+
+    @Test
+    public void testLoadPrivateLibrariesViaSystemExtSharedLib() {
+        SystemExtSharedLib.loadLibrary("system_private3");
+        SystemExtSharedLib.loadLibrary("systemext_private3");
+        TestUtils.assertLibraryNotFound(() -> SystemExtSharedLib.loadLibrary("product_private3"));
+        TestUtils.assertLibraryNotFound(() -> SystemExtSharedLib.loadLibrary("vendor_private3"));
+    }
 }
diff --git a/libnativeloader/test/src/android/test/hostside/LibnativeloaderTest.java b/libnativeloader/test/src/android/test/hostside/LibnativeloaderTest.java
index c908a49..c929037 100644
--- a/libnativeloader/test/src/android/test/hostside/LibnativeloaderTest.java
+++ b/libnativeloader/test/src/android/test/hostside/LibnativeloaderTest.java
@@ -69,7 +69,10 @@
             ctx.pushExtendedPublicProductLibs(libApk);
             ctx.pushPrivateLibs(libApk);
         }
-        ctx.pushSystemSharedLib();
+        ctx.pushSystemSharedLib("/system/framework", "android.test.systemsharedlib",
+                "libnativeloader_system_shared_lib.jar");
+        ctx.pushSystemSharedLib("/system_ext/framework", "android.test.systemextsharedlib",
+                "libnativeloader_system_ext_shared_lib.jar");
 
         // "Install" apps in various partitions through plain adb push followed by a soft reboot. We
         // need them in these locations to test library loading restrictions, so for all except
@@ -230,17 +233,18 @@
         void pushPrivateLibs(ZipFile libApk) throws Exception {
             // Push the libraries once for each test. Since we cannot unload them, we need a fresh
             // never-before-loaded library in each loadLibrary call.
-            for (int i = 1; i <= 2; ++i) {
+            for (int i = 1; i <= 3; ++i) {
                 pushNativeTestLib(libApk, "/system/${LIB}/libsystem_private" + i + ".so");
+                pushNativeTestLib(libApk, "/system_ext/${LIB}/libsystemext_private" + i + ".so");
                 pushNativeTestLib(libApk, "/product/${LIB}/libproduct_private" + i + ".so");
                 pushNativeTestLib(libApk, "/vendor/${LIB}/libvendor_private" + i + ".so");
             }
         }
 
-        void pushSystemSharedLib() throws Exception {
-            String packageName = "android.test.systemsharedlib";
-            String path = "/system/framework/" + packageName + ".jar";
-            pushFile("libnativeloader_system_shared_lib.jar", path);
+        void pushSystemSharedLib(String packageDir, String packageName, String buildJarName)
+                throws Exception {
+            String path = packageDir + "/" + packageName + ".jar";
+            pushFile(buildJarName, path);
             pushString("<permissions>\n"
                             + "<library name=\"" + packageName + "\" file=\"" + path + "\" />\n"
                             + "</permissions>\n",
diff --git a/libnativeloader/test/src/android/test/systemextsharedlib/SystemExtSharedLib.java b/libnativeloader/test/src/android/test/systemextsharedlib/SystemExtSharedLib.java
new file mode 100644
index 0000000..1240e12
--- /dev/null
+++ b/libnativeloader/test/src/android/test/systemextsharedlib/SystemExtSharedLib.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.test.systemextsharedlib;
+
+public final class SystemExtSharedLib {
+    public static void loadLibrary(String name) { System.loadLibrary(name); }
+}