Extend loadLibrary tests with apps in /system/priv-app, /system_ext,
and /product.

In /system_ext we run the same tests as in /system, because there
shouldn't be any difference between them.

For /product this records how it currently works, not what we want when
b/237572732 is fixed.

Test: atest -a libnativeloader_e2e_tests
Bug: 237572732
Change-Id: I4f35c8e3b271e7bf300d410e6952a4d9073fec6b
diff --git a/libnativeloader/test/Android.bp b/libnativeloader/test/Android.bp
index ca98dfa..05d530d 100644
--- a/libnativeloader/test/Android.bp
+++ b/libnativeloader/test/Android.bp
@@ -40,11 +40,9 @@
     jni_libs: ["libnativeloader_testlib"],
 }
 
-android_test_helper_app {
-    name: "loadlibrarytest_system_app",
+java_defaults {
+    name: "loadlibrarytest_app_defaults",
     defaults: ["art_module_source_build_java_defaults"],
-    manifest: "loadlibrarytest_system_app_manifest.xml",
-    srcs: ["src/android/test/app/SystemAppTest.java"],
 
     // TODO(mast): Use old target SDK to avoid filtering on uses_library lists.
     // Figure out what we need to do to make <uses-native-library> work in the
@@ -59,21 +57,40 @@
 }
 
 android_test_helper_app {
+    name: "loadlibrarytest_system_priv_app",
+    defaults: ["loadlibrarytest_app_defaults"],
+    manifest: "loadlibrarytest_system_priv_app_manifest.xml",
+    // /system/priv-app currently reuses the same test as /system/app.
+    srcs: ["src/android/test/app/SystemAppTest.java"],
+}
+
+android_test_helper_app {
+    name: "loadlibrarytest_system_app",
+    defaults: ["loadlibrarytest_app_defaults"],
+    manifest: "loadlibrarytest_system_app_manifest.xml",
+    srcs: ["src/android/test/app/SystemAppTest.java"],
+}
+
+android_test_helper_app {
+    name: "loadlibrarytest_system_ext_app",
+    defaults: ["loadlibrarytest_app_defaults"],
+    manifest: "loadlibrarytest_system_ext_app_manifest.xml",
+    // /system_ext should behave the same as /system, so use the same test class there.
+    srcs: ["src/android/test/app/SystemAppTest.java"],
+}
+
+android_test_helper_app {
+    name: "loadlibrarytest_product_app",
+    defaults: ["loadlibrarytest_app_defaults"],
+    manifest: "loadlibrarytest_product_app_manifest.xml",
+    srcs: ["src/android/test/app/ProductAppTest.java"],
+}
+
+android_test_helper_app {
     name: "loadlibrarytest_vendor_app",
-    defaults: ["art_module_source_build_java_defaults"],
+    defaults: ["loadlibrarytest_app_defaults"],
     manifest: "loadlibrarytest_vendor_app_manifest.xml",
     srcs: ["src/android/test/app/VendorAppTest.java"],
-
-    // TODO(mast): Use old target SDK to avoid filtering on uses_library lists.
-    // Figure out what we need to do to make <uses-native-library> work in the
-    // test apps so we can use that instead.
-    sdk_version: "30",
-
-    static_libs: [
-        "androidx.test.ext.junit",
-        "androidx.test.ext.truth",
-        "androidx.test.rules",
-    ],
 }
 
 java_test_host {
@@ -83,7 +100,10 @@
     libs: ["tradefed"],
     java_resources: [
         ":library_container_app",
+        ":loadlibrarytest_system_priv_app",
         ":loadlibrarytest_system_app",
+        ":loadlibrarytest_system_ext_app",
+        ":loadlibrarytest_product_app",
         ":loadlibrarytest_vendor_app",
     ],
     test_config: "libnativeloader_e2e_tests.xml",
diff --git a/libnativeloader/test/loadlibrarytest_product_app_manifest.xml b/libnativeloader/test/loadlibrarytest_product_app_manifest.xml
new file mode 100644
index 0000000..a791795
--- /dev/null
+++ b/libnativeloader/test/loadlibrarytest_product_app_manifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2018 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.test.app.product">
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="android.test.app.product" />
+</manifest>
+
diff --git a/libnativeloader/test/loadlibrarytest_system_app_manifest.xml b/libnativeloader/test/loadlibrarytest_system_app_manifest.xml
index afb6061..3cbd054 100644
--- a/libnativeloader/test/loadlibrarytest_system_app_manifest.xml
+++ b/libnativeloader/test/loadlibrarytest_system_app_manifest.xml
@@ -16,7 +16,7 @@
  -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="android.test.app.system">
+          package="android.test.app.system">
     <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.test.app.system" />
 </manifest>
diff --git a/libnativeloader/test/loadlibrarytest_system_ext_app_manifest.xml b/libnativeloader/test/loadlibrarytest_system_ext_app_manifest.xml
new file mode 100644
index 0000000..83ca779
--- /dev/null
+++ b/libnativeloader/test/loadlibrarytest_system_ext_app_manifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2018 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.test.app.system_ext">
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="android.test.app.system_ext" />
+</manifest>
+
diff --git a/libnativeloader/test/loadlibrarytest_system_priv_app_manifest.xml b/libnativeloader/test/loadlibrarytest_system_priv_app_manifest.xml
new file mode 100644
index 0000000..0ad1aa5
--- /dev/null
+++ b/libnativeloader/test/loadlibrarytest_system_priv_app_manifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2018 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.test.app.system_priv">
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="android.test.app.system_priv" />
+</manifest>
+
diff --git a/libnativeloader/test/loadlibrarytest_vendor_app_manifest.xml b/libnativeloader/test/loadlibrarytest_vendor_app_manifest.xml
index 578de8d..b7073c6 100644
--- a/libnativeloader/test/loadlibrarytest_vendor_app_manifest.xml
+++ b/libnativeloader/test/loadlibrarytest_vendor_app_manifest.xml
@@ -16,7 +16,7 @@
  -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="android.test.app.vendor">
+          package="android.test.app.vendor">
     <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.test.app.vendor" />
 </manifest>
diff --git a/libnativeloader/test/src/android/test/app/ProductAppTest.java b/libnativeloader/test/src/android/test/app/ProductAppTest.java
new file mode 100644
index 0000000..123e06b
--- /dev/null
+++ b/libnativeloader/test/src/android/test/app/ProductAppTest.java
@@ -0,0 +1,46 @@
+/*
+ * 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.app;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ProductAppTest {
+    @Test
+    public void testLoadLibraries() {
+        assertLinkerNamespaceError("foo.oem1");
+        assertLinkerNamespaceError("bar.oem1");
+        assertLinkerNamespaceError("foo.oem2");
+        assertLinkerNamespaceError("bar.oem2");
+        System.loadLibrary("foo.product1");
+        System.loadLibrary("bar.product1");
+    }
+
+    private void assertLinkerNamespaceError(String libraryName) {
+        Throwable t =
+                assertThrows(UnsatisfiedLinkError.class, () -> System.loadLibrary(libraryName));
+        assertThat(t.getMessage())
+                .containsMatch("dlopen failed: .* is not accessible for the namespace");
+    }
+}
diff --git a/libnativeloader/test/src/android/test/app/SystemAppTest.java b/libnativeloader/test/src/android/test/app/SystemAppTest.java
index 0bf90e0..dd680f5 100644
--- a/libnativeloader/test/src/android/test/app/SystemAppTest.java
+++ b/libnativeloader/test/src/android/test/app/SystemAppTest.java
@@ -24,6 +24,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+// These tests are run in both /system and /system_ext.
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class SystemAppTest {
diff --git a/libnativeloader/test/src/android/test/hostside/LibnativeloaderTest.java b/libnativeloader/test/src/android/test/hostside/LibnativeloaderTest.java
index 7465868..7e2d9de 100644
--- a/libnativeloader/test/src/android/test/hostside/LibnativeloaderTest.java
+++ b/libnativeloader/test/src/android/test/hostside/LibnativeloaderTest.java
@@ -21,8 +21,10 @@
 
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.invoker.IInvocationContext;
 import com.android.tradefed.invoker.TestInformation;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.IAbi;
 import com.android.tradefed.testtype.junit4.AfterClassWithInfo;
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 import com.android.tradefed.testtype.junit4.BeforeClassWithInfo;
@@ -52,7 +54,7 @@
 
     @BeforeClassWithInfo
     public static void beforeClassWithDevice(TestInformation testInfo) throws Exception {
-        DeviceContext ctx = new DeviceContext(testInfo.getDevice());
+        DeviceContext ctx = new DeviceContext(testInfo.getContext(), testInfo.getDevice());
 
         // A soft reboot is slow, so do setup for all tests and reboot once.
 
@@ -66,13 +68,20 @@
         // locations to test library loading restrictions, so we cannot use
         // ITestDevice.installPackage for it since it only installs in /data.
 
+        // For testSystemPrivApp
+        ctx.pushApk("loadlibrarytest_system_priv_app", "/system/priv-app");
+
         // For testSystemApp
-        ctx.pushResource("/loadlibrarytest_system_app.apk",
-                "/system/app/loadlibrarytest_system_app/loadlibrarytest_system_app.apk");
+        ctx.pushApk("loadlibrarytest_system_app", "/system/app");
+
+        // For testSystemExtApp
+        ctx.pushApk("loadlibrarytest_system_ext_app", "/system_ext/app");
+
+        // For testProductApp
+        ctx.pushApk("loadlibrarytest_product_app", "/product/app");
 
         // For testVendorApp
-        ctx.pushResource("/loadlibrarytest_vendor_app.apk",
-                "/vendor/app/loadlibrarytest_vendor_app/loadlibrarytest_vendor_app.apk");
+        ctx.pushApk("loadlibrarytest_vendor_app", "/vendor/app");
 
         ctx.softReboot();
 
@@ -87,11 +96,29 @@
     }
 
     @Test
+    public void testSystemPrivApp() throws Exception {
+        // There's currently no difference in the tests between /system/priv-app and /system/app, so
+        // let's reuse the same one.
+        runDeviceTests("android.test.app.system_priv", "android.test.app.SystemAppTest");
+    }
+
+    @Test
     public void testSystemApp() throws Exception {
         runDeviceTests("android.test.app.system", "android.test.app.SystemAppTest");
     }
 
     @Test
+    public void testSystemExtApp() throws Exception {
+        // /system_ext should behave the same as /system, so run the same test class there.
+        runDeviceTests("android.test.app.system_ext", "android.test.app.SystemAppTest");
+    }
+
+    @Test
+    public void testProductApp() throws Exception {
+        runDeviceTests("android.test.app.product", "android.test.app.ProductAppTest");
+    }
+
+    @Test
     public void testVendorApp() throws Exception {
         runDeviceTests("android.test.app.vendor", "android.test.app.VendorAppTest");
     }
@@ -142,11 +169,13 @@
     // Class for code that needs an ITestDevice. It is instantiated both in tests and in
     // (Before|After)ClassWithInfo.
     private static class DeviceContext implements AutoCloseable {
+        IInvocationContext mContext;
         ITestDevice mDevice;
         CleanupPaths mCleanup;
-        private String mPrimaryArch;
+        private String mTestArch;
 
-        DeviceContext(ITestDevice device) {
+        DeviceContext(IInvocationContext context, ITestDevice device) {
+            mContext = context;
             mDevice = device;
             mCleanup = new CleanupPaths(mDevice);
         }
@@ -182,11 +211,13 @@
             mDevice.waitForDeviceAvailable();
         }
 
-        String getPrimaryArch() throws DeviceNotAvailableException {
-            if (mPrimaryArch == null) {
-                mPrimaryArch = assertCommandSucceeds("getprop ro.bionic.arch");
+        String getTestArch() throws DeviceNotAvailableException {
+            if (mTestArch == null) {
+                IAbi abi = mContext.getConfigurationDescriptor().getAbi();
+                mTestArch = abi != null ? abi.getName()
+                                        : assertCommandSucceeds("getprop ro.bionic.arch");
             }
-            return mPrimaryArch;
+            return mTestArch;
         }
 
         // Pushes the given file contents to the device at the given destination path. destPath is
@@ -204,10 +235,15 @@
             assertThat(mDevice.pushFile(hostTempFile, destPath)).isTrue();
         }
 
+        void pushApk(String apkBaseName, String destPath) throws Exception {
+            pushResource("/" + apkBaseName + ".apk",
+                    destPath + "/" + apkBaseName + "/" + apkBaseName + ".apk");
+        }
+
         // Like pushString, but extracts libnativeloader_testlib.so from the library_container_app
         // APK and pushes it to destPath. "${LIB}" is replaced with "lib" or "lib64" as appropriate.
         void pushNativeTestLib(ZipFile libApk, String destPath) throws Exception {
-            String libApkPath = "lib/" + getPrimaryArch() + "/libnativeloader_testlib.so";
+            String libApkPath = "lib/" + getTestArch() + "/libnativeloader_testlib.so";
             ZipEntry entry = libApk.getEntry(libApkPath);
             assertWithMessage("Failed to find " + libApkPath + " in library_container_app.apk")
                     .that(entry)
@@ -218,7 +254,7 @@
                 libraryTempFile = writeStreamToTempFile("libnativeloader_testlib.so", inStream);
             }
 
-            String libDir = getPrimaryArch().contains("64") ? "lib64" : "lib";
+            String libDir = getTestArch().contains("64") ? "lib64" : "lib";
             destPath = destPath.replace("${LIB}", libDir);
 
             mCleanup.addPath(destPath);