[PM] Add the test for forcing multiarch app match natively ABIs

Bug: 282783453
Test: atest -c CtsPackageManagerTestCases --test-filter 'PackageManagerMultiArchAppTest*'
Test: atest CtsPackageManagerHostTestCases:SplitTests
Change-Id: Ic9a71ac432a0802d34f9672fa0d7bf8c3e6fc4ad
diff --git a/hostsidetests/appsecurity/Android.bp b/hostsidetests/appsecurity/Android.bp
index a5e94c0..04b6980 100644
--- a/hostsidetests/appsecurity/Android.bp
+++ b/hostsidetests/appsecurity/Android.bp
@@ -299,6 +299,10 @@
         "src/**/SplitTests.java",
         "src/**/UseProcessTest.java",
     ],
+    static_libs: [
+        "flag-junit-host",
+        "android.content.pm.flags-aconfig-java-host",
+    ],
     data: [
         // ApplicationVisibilityTest
         ":CtsPkgInstallTinyApp",
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseInstallMultiple.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseInstallMultiple.java
index 01c9625..8d9101d 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseInstallMultiple.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseInstallMultiple.java
@@ -219,7 +219,7 @@
         run(false, failure);
     }
 
-    private void run(boolean expectingSuccess, String failure) throws DeviceNotAvailableException {
+    void run(boolean expectingSuccess, String failure) throws DeviceNotAvailableException {
         if (mUseIncremental) {
             runIncremental(expectingSuccess, failure);
         } else {
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/SplitTests.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/SplitTests.java
index 9eddbef..162dea9 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/SplitTests.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/SplitTests.java
@@ -27,10 +27,15 @@
 
 import static org.junit.Assert.assertNotNull;
 
+import android.content.pm.Flags;
 import android.platform.test.annotations.AppModeFull;
 import android.platform.test.annotations.AppModeInstant;
 import android.platform.test.annotations.PlatinumTest;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.host.HostFlagsValueProvider;
 
 import com.android.compatibility.common.util.CpuFeatures;
 import com.android.tradefed.device.DeviceNotAvailableException;
@@ -42,6 +47,7 @@
 import org.junit.After;
 import org.junit.Assume;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -104,6 +110,8 @@
 
     private static final String APK_REVISION_A = "CtsSplitAppRevisionA.apk";
     private static final String APK_FEATURE_WARM_REVISION_A = "CtsSplitAppFeatureWarmRevisionA.apk";
+    private static final String BITNESS_32 = "32";
+    private static final String BITNESS_64 = "64";
 
     // Apk includes a provider and service declared in other split apk. And only could be tested in
     // instant app mode.
@@ -121,6 +129,88 @@
         ABI_TO_REVISION_APK.put("mips", "CtsSplitApp_revision12_mips.apk");
     }
 
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule =
+            HostFlagsValueProvider.createCheckFlagsRule(this::getDevice);
+
+    private String mDeviceDefaultAbi = null;
+    private String mDeviceDefaultBitness = null;
+    private String mDeviceDefaultBaseArch = null;
+    private String[] mDeviceSupported32BitSet = null;
+    private String[] mDeviceSupported64BitSet = null;
+
+    private String[] getDeviceSupported32AbiSet() throws Exception {
+        if (mDeviceSupported32BitSet != null) {
+            return mDeviceSupported32BitSet;
+        }
+        mDeviceSupported32BitSet = getDeviceSupportedAbiSet(BITNESS_32);
+        return mDeviceSupported32BitSet;
+    }
+
+    private String[] getDeviceSupported64AbiSet() throws Exception {
+        if (mDeviceSupported64BitSet != null) {
+            return mDeviceSupported64BitSet;
+        }
+        mDeviceSupported64BitSet = getDeviceSupportedAbiSet(BITNESS_64);
+        return mDeviceSupported64BitSet;
+    }
+
+    private String[] getDeviceSupportedAbiSet(String bitness) throws Exception {
+        String abis = getDevice().getProperty("ro.product.cpu.abilist" + bitness);
+        if (abis == null) {
+            return new String[0];
+        }
+        return abis.split(",");
+    }
+
+    private String getDeviceDefaultAbi() throws Exception {
+        if (mDeviceDefaultAbi != null) {
+            return mDeviceDefaultAbi;
+        }
+        mDeviceDefaultAbi = getDevice().getProperty("ro.product.cpu.abi");
+        return mDeviceDefaultAbi;
+    }
+
+    private String getDeviceDefaultBitness() throws Exception {
+        if (mDeviceDefaultBitness != null) {
+            return mDeviceDefaultBitness;
+        }
+        mDeviceDefaultBitness = AbiUtils.getBitness(getDeviceDefaultAbi());
+        return mDeviceDefaultBitness;
+    }
+
+    private String getDeviceDefaultBaseArch() throws Exception {
+        if (mDeviceDefaultBaseArch != null) {
+            return mDeviceDefaultBaseArch;
+        }
+        mDeviceDefaultBaseArch = AbiUtils.getBaseArchForAbi(getDeviceDefaultAbi());
+        return mDeviceDefaultBaseArch;
+    }
+
+    /*
+     * Return true if the device supports both 32 bit and 64 bit ABIs. Otherwise, false.
+     */
+    private boolean isDeviceSupportedBothBitness() throws Exception {
+        final String deviceDefaultBaseArch = getDeviceDefaultBaseArch();
+        if (getDeviceDefaultBitness().equals(BITNESS_32)) {
+            String[] abis64 = getDeviceSupported64AbiSet();
+            for (int i = 0; i < abis64.length; i++) {
+                if (AbiUtils.getBaseArchForAbi(abis64[i]).equals(deviceDefaultBaseArch)) {
+                    return true;
+                }
+            }
+        } else {
+            String[] abis32 = getDeviceSupported32AbiSet();
+            for (int i = 0; i < abis32.length; i++) {
+                if (AbiUtils.getBaseArchForAbi(abis32[i]).equals(deviceDefaultBaseArch)) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
     @Before
     public void setUp() throws Exception {
         Utils.prepareSingleUser(getDevice());
@@ -260,6 +350,19 @@
         return installMultiple;
     }
 
+    private void testNativeSingle_assertFail(boolean instant, boolean useNaturalAbi,
+            String failure) throws Exception {
+        Assume.assumeTrue(isDeviceSupportedBothBitness());
+        final String abi = getAbi().getName();
+        final String apk = ABI_TO_APK.get(abi);
+        assertNotNull("Failed to find APK for ABI " + abi, apk);
+
+        getInstallMultiple(instant, useNaturalAbi)
+                .addFile(APK)
+                .addFile(apk)
+                .run(/* expectingSuccess= */ false, failure);
+    }
+
     private void testNativeSingle(boolean instant, boolean useNaturalAbi) throws Exception {
         final String abi = getAbi().getName();
         final String apk = ABI_TO_APK.get(abi);
@@ -329,15 +432,32 @@
      */
     @Test
     @AppModeFull(reason = "'full' portion of the hostside test")
+    @RequiresFlagsDisabled(Flags.FLAG_FORCE_MULTI_ARCH_NATIVE_LIBS_MATCH)
     public void testNativeSingleNatural_full() throws Exception {
         testNativeSingle(false, true);
     }
     @Test
     @AppModeInstant(reason = "'instant' portion of the hostside test")
+    @RequiresFlagsDisabled(Flags.FLAG_FORCE_MULTI_ARCH_NATIVE_LIBS_MATCH)
     public void testNativeSingleNatural_instant() throws Exception {
         testNativeSingle(true, true);
     }
 
+    @Test
+    @AppModeFull(reason = "'full' portion of the hostside test")
+    @RequiresFlagsEnabled(Flags.FLAG_FORCE_MULTI_ARCH_NATIVE_LIBS_MATCH)
+    public void testNativeSingleNatural_full_fail() throws Exception {
+        testNativeSingle_assertFail(/* instant= */ false, /* useNaturalAbi= */ true,
+                "don't support all the natively supported ABIs of the device");
+    }
+    @Test
+    @AppModeInstant(reason = "'instant' portion of the hostside test")
+    @RequiresFlagsEnabled(Flags.FLAG_FORCE_MULTI_ARCH_NATIVE_LIBS_MATCH)
+    public void testNativeSingleNatural_instant_fail() throws Exception {
+        testNativeSingle_assertFail(/* instant= */ true, /* useNaturalAbi= */ true,
+                "don't support all the natively supported ABIs of the device");
+    }
+
     private void assumeNativeAbi() throws Exception {
         // Skip this test if not running on the device's native abi.
         Assume.assumeTrue(CpuFeatures.isNativeAbi(getDevice(), getAbi().getName()));
diff --git a/tests/tests/content/Android.bp b/tests/tests/content/Android.bp
index c3c2635..dc76393 100644
--- a/tests/tests/content/Android.bp
+++ b/tests/tests/content/Android.bp
@@ -240,6 +240,18 @@
         ":HelloVerifierDelayedReject",
         ":HelloVerifierDisabled",
         ":HelloWorldSystemUserOnly",
+        ":CtsMultiArchApp32_arm",
+        ":CtsMultiArchApp32_x86",
+        ":CtsMultiArchApp64_arm",
+        ":CtsMultiArchApp64_x86",
+        ":CtsMultiArchAppBoth_arm",
+        ":CtsMultiArchAppBoth_x86",
+        ":CtsMultiArchApp32_targetSdk33_arm",
+        ":CtsMultiArchApp32_targetSdk33_x86",
+        ":CtsMultiArchApp64_targetSdk33_arm",
+        ":CtsMultiArchApp64_targetSdk33_x86",
+        ":CtsMultiArchAppBoth_targetSdk33_arm",
+        ":CtsMultiArchAppBoth_targetSdk33_x86",
     ],
     java_resources: [
         ":PackagePropertyTestApp1",
diff --git a/tests/tests/content/CtsPackageManagerTest.xml b/tests/tests/content/CtsPackageManagerTest.xml
index 62e7333..cad5b92 100644
--- a/tests/tests/content/CtsPackageManagerTest.xml
+++ b/tests/tests/content/CtsPackageManagerTest.xml
@@ -183,6 +183,18 @@
         <option name="push-file" key="HelloVerifierDisabled.apk.idsig" value="/data/local/tmp/cts/content/HelloVerifierDisabled.apk.idsig" />
         <option name="push-file" key="HelloWorldSystemUserOnly.apk" value="/data/local/tmp/cts/content/HelloWorldSystemUserOnly.apk" />
         <option name="push-file" key="HelloWorldSystemUserOnly.apk.idsig" value="/data/local/tmp/cts/content/HelloWorldSystemUserOnly.apk.idsig" />
+        <option name="push-file" key="CtsMultiArchApp32_arm.apk" value="/data/local/tmp/cts/content/CtsMultiArchApp32_arm.apk" />
+        <option name="push-file" key="CtsMultiArchApp32_x86.apk" value="/data/local/tmp/cts/content/CtsMultiArchApp32_x86.apk" />
+        <option name="push-file" key="CtsMultiArchApp64_arm.apk" value="/data/local/tmp/cts/content/CtsMultiArchApp64_arm.apk" />
+        <option name="push-file" key="CtsMultiArchApp64_x86.apk" value="/data/local/tmp/cts/content/CtsMultiArchApp64_x86.apk" />
+        <option name="push-file" key="CtsMultiArchAppBoth_arm.apk" value="/data/local/tmp/cts/content/CtsMultiArchAppBoth_arm.apk" />
+        <option name="push-file" key="CtsMultiArchAppBoth_x86.apk" value="/data/local/tmp/cts/content/CtsMultiArchAppBoth_x86.apk" />
+        <option name="push-file" key="CtsMultiArchApp32_targetSdk33_arm.apk" value="/data/local/tmp/cts/content/CtsMultiArchApp32_targetSdk33_arm.apk" />
+        <option name="push-file" key="CtsMultiArchApp32_targetSdk33_x86.apk" value="/data/local/tmp/cts/content/CtsMultiArchApp32_targetSdk33_x86.apk" />
+        <option name="push-file" key="CtsMultiArchApp64_targetSdk33_arm.apk" value="/data/local/tmp/cts/content/CtsMultiArchApp64_targetSdk33_arm.apk" />
+        <option name="push-file" key="CtsMultiArchApp64_targetSdk33_x86.apk" value="/data/local/tmp/cts/content/CtsMultiArchApp64_targetSdk33_x86.apk" />
+        <option name="push-file" key="CtsMultiArchAppBoth_targetSdk33_arm.apk" value="/data/local/tmp/cts/content/CtsMultiArchAppBoth_targetSdk33_arm.apk" />
+        <option name="push-file" key="CtsMultiArchAppBoth_targetSdk33_x86.apk" value="/data/local/tmp/cts/content/CtsMultiArchAppBoth_targetSdk33_x86.apk" />
     </target_preparer>
 
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/tests/content/MultiArchApp/Android.bp b/tests/tests/content/MultiArchApp/Android.bp
new file mode 100644
index 0000000..d708550
--- /dev/null
+++ b/tests/tests/content/MultiArchApp/Android.bp
@@ -0,0 +1,221 @@
+// Copyright (C) 2024 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 {
+    default_team: "trendy_team_framework_android_packages",
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_test_library {
+    name: "libtest_multi_arch_native_libs",
+    gtest: false,
+    srcs: ["jni/*.cpp"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-unused-parameter",
+    ],
+    header_libs: ["jni_headers"],
+    shared_libs: ["liblog"],
+    sdk_version: "current",
+    target: {
+        android_arm: {
+            cflags: [
+                "-D__ANDROID_ARCH__=\"armeabi-v7a\"",
+            ],
+        },
+        android_arm64: {
+            cflags: [
+                "-D__ANDROID_ARCH__=\"arm64-v8a\"",
+            ],
+        },
+        android_x86: {
+            cflags: [
+                "-D__ANDROID_ARCH__=\"x86\"",
+            ],
+        },
+        android_x86_64: {
+            cflags: [
+                "-D__ANDROID_ARCH__=\"x86_64\"",
+            ],
+        },
+        android_riscv64: {
+            cflags: [
+                "-D__ANDROID_ARCH__=\"riscv64\"",
+            ],
+        },
+    },
+}
+
+android_test_helper_app {
+    name: "CtsMultiArchApp32_arm",
+    defaults: ["cts_support_defaults"],
+    sdk_version: "current",
+    srcs: ["src/**/*.java"],
+    test_suites: [
+        "cts",
+        "general-tests",
+    ],
+    use_embedded_native_libs: false,
+    java_resource_dirs: ["raw/arm"],
+}
+
+android_test_helper_app {
+    name: "CtsMultiArchApp64_arm",
+    defaults: ["cts_support_defaults"],
+    sdk_version: "current",
+    srcs: ["src/**/*.java"],
+    test_suites: [
+        "cts",
+        "general-tests",
+    ],
+    use_embedded_native_libs: false,
+    java_resource_dirs: ["raw/arm64"],
+}
+
+android_test_helper_app {
+    name: "CtsMultiArchAppBoth_arm",
+    defaults: ["cts_support_defaults"],
+    sdk_version: "current",
+    srcs: ["src/**/*.java"],
+    test_suites: [
+        "cts",
+        "general-tests",
+    ],
+    use_embedded_native_libs: false,
+    java_resource_dirs: ["raw/arm_both"],
+}
+
+android_test_helper_app {
+    name: "CtsMultiArchApp32_targetSdk33_arm",
+    defaults: ["cts_support_defaults"],
+    sdk_version: "current",
+    srcs: ["src/**/*.java"],
+    manifest: "AndroidManifest33.xml",
+    test_suites: [
+        "cts",
+        "general-tests",
+    ],
+    use_embedded_native_libs: false,
+    java_resource_dirs: ["raw/arm"],
+}
+
+android_test_helper_app {
+    name: "CtsMultiArchApp64_targetSdk33_arm",
+    defaults: ["cts_support_defaults"],
+    sdk_version: "current",
+    srcs: ["src/**/*.java"],
+    manifest: "AndroidManifest33.xml",
+    test_suites: [
+        "cts",
+        "general-tests",
+    ],
+    use_embedded_native_libs: false,
+    java_resource_dirs: ["raw/arm64"],
+}
+
+android_test_helper_app {
+    name: "CtsMultiArchAppBoth_targetSdk33_arm",
+    defaults: ["cts_support_defaults"],
+    sdk_version: "current",
+    srcs: ["src/**/*.java"],
+    manifest: "AndroidManifest33.xml",
+    test_suites: [
+        "cts",
+        "general-tests",
+    ],
+    use_embedded_native_libs: false,
+    java_resource_dirs: ["raw/arm_both"],
+}
+
+android_test_helper_app {
+    name: "CtsMultiArchApp32_x86",
+    defaults: ["cts_support_defaults"],
+    sdk_version: "current",
+    srcs: ["src/**/*.java"],
+    test_suites: [
+        "cts",
+        "general-tests",
+    ],
+    use_embedded_native_libs: false,
+    java_resource_dirs: ["raw/x86"],
+}
+
+android_test_helper_app {
+    name: "CtsMultiArchApp64_x86",
+    defaults: ["cts_support_defaults"],
+    sdk_version: "current",
+    srcs: ["src/**/*.java"],
+    test_suites: [
+        "cts",
+        "general-tests",
+    ],
+    use_embedded_native_libs: false,
+    java_resource_dirs: ["raw/x86_64"],
+}
+
+android_test_helper_app {
+    name: "CtsMultiArchAppBoth_x86",
+    defaults: ["cts_support_defaults"],
+    sdk_version: "current",
+    srcs: ["src/**/*.java"],
+    test_suites: [
+        "cts",
+        "general-tests",
+    ],
+    use_embedded_native_libs: false,
+    java_resource_dirs: ["raw/x86_both"],
+}
+
+android_test_helper_app {
+    name: "CtsMultiArchApp32_targetSdk33_x86",
+    defaults: ["cts_support_defaults"],
+    sdk_version: "current",
+    srcs: ["src/**/*.java"],
+    manifest: "AndroidManifest33.xml",
+    test_suites: [
+        "cts",
+        "general-tests",
+    ],
+    use_embedded_native_libs: false,
+    java_resource_dirs: ["raw/x86"],
+}
+
+android_test_helper_app {
+    name: "CtsMultiArchApp64_targetSdk33_x86",
+    defaults: ["cts_support_defaults"],
+    sdk_version: "current",
+    srcs: ["src/**/*.java"],
+    manifest: "AndroidManifest33.xml",
+    test_suites: [
+        "cts",
+        "general-tests",
+    ],
+    use_embedded_native_libs: false,
+    java_resource_dirs: ["raw/x86_64"],
+}
+
+android_test_helper_app {
+    name: "CtsMultiArchAppBoth_targetSdk33_x86",
+    defaults: ["cts_support_defaults"],
+    sdk_version: "current",
+    srcs: ["src/**/*.java"],
+    manifest: "AndroidManifest33.xml",
+    test_suites: [
+        "cts",
+        "general-tests",
+    ],
+    use_embedded_native_libs: false,
+    java_resource_dirs: ["raw/x86_both"],
+}
diff --git a/tests/tests/content/MultiArchApp/AndroidManifest.xml b/tests/tests/content/MultiArchApp/AndroidManifest.xml
new file mode 100644
index 0000000..e842bfa
--- /dev/null
+++ b/tests/tests/content/MultiArchApp/AndroidManifest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 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="com.android.cts.multiarch.app">
+    <application
+        android:multiArch="true"
+        android:extractNativeLibs="true">
+        <uses-library android:name="android.test.runner"/>
+        <activity android:name=".MainActivity"
+                  android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
+        </activity>
+    </application>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.cts.multiarch.app"/>
+</manifest>
diff --git a/tests/tests/content/MultiArchApp/AndroidManifest33.xml b/tests/tests/content/MultiArchApp/AndroidManifest33.xml
new file mode 100644
index 0000000..5dd3aa4
--- /dev/null
+++ b/tests/tests/content/MultiArchApp/AndroidManifest33.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 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="com.android.cts.multiarch.app">
+    <uses-sdk android:minSdkVersion="33" android:targetSdkVersion="33" />
+
+    <application
+        android:multiArch="true"
+        android:extractNativeLibs="true">
+        <uses-library android:name="android.test.runner"/>
+        <activity android:name=".MainActivity"
+                  android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
+        </activity>
+    </application>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.cts.multiarch.app"/>
+</manifest>
diff --git a/tests/tests/content/MultiArchApp/README.md b/tests/tests/content/MultiArchApp/README.md
new file mode 100644
index 0000000..a872522
--- /dev/null
+++ b/tests/tests/content/MultiArchApp/README.md
@@ -0,0 +1,8 @@
+How to build the prebuilt libs
+=================================
+1. m libtest_multi_arch_native_libs in the different arch (E.g. arm, x86)
+
+2. Find the libs in out/target/product/{$target}/data/
+E.g. out/target/product/redfin/data/nativetest/ and out/target/product/redfin/data/nativetest64/
+
+3. Put them into the specific folders by the arch (E.g. arm, arm64, x86, x86_64)
diff --git a/tests/tests/content/MultiArchApp/jni/native.cpp b/tests/tests/content/MultiArchApp/jni/native.cpp
new file mode 100644
index 0000000..7108a4d
--- /dev/null
+++ b/tests/tests/content/MultiArchApp/jni/native.cpp
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+#include <android/log.h>
+#include <jni.h>
+#define LOG(...) __android_log_write(ANDROID_LOG_INFO, "NativeLibOutput", __VA_ARGS__)
+
+jint JNI_OnLoad(JavaVM* vm, void* reserved) {
+    JNIEnv* env = nullptr;
+    if (vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK) {
+        return JNI_ERR;
+    }
+    LOG("libtest_multi_arch_native_libs is loaded");
+    return JNI_VERSION_1_6;
+}
diff --git a/tests/tests/content/MultiArchApp/raw/arm/lib/armeabi-v7a/libtest_multi_arch_native_libs.so b/tests/tests/content/MultiArchApp/raw/arm/lib/armeabi-v7a/libtest_multi_arch_native_libs.so
new file mode 100755
index 0000000..c0c95e0
--- /dev/null
+++ b/tests/tests/content/MultiArchApp/raw/arm/lib/armeabi-v7a/libtest_multi_arch_native_libs.so
Binary files differ
diff --git a/tests/tests/content/MultiArchApp/raw/arm64/lib/arm64-v8a/libtest_multi_arch_native_libs.so b/tests/tests/content/MultiArchApp/raw/arm64/lib/arm64-v8a/libtest_multi_arch_native_libs.so
new file mode 100755
index 0000000..b69db7f
--- /dev/null
+++ b/tests/tests/content/MultiArchApp/raw/arm64/lib/arm64-v8a/libtest_multi_arch_native_libs.so
Binary files differ
diff --git a/tests/tests/content/MultiArchApp/raw/arm_both/lib/arm64-v8a/libtest_multi_arch_native_libs.so b/tests/tests/content/MultiArchApp/raw/arm_both/lib/arm64-v8a/libtest_multi_arch_native_libs.so
new file mode 100755
index 0000000..b69db7f
--- /dev/null
+++ b/tests/tests/content/MultiArchApp/raw/arm_both/lib/arm64-v8a/libtest_multi_arch_native_libs.so
Binary files differ
diff --git a/tests/tests/content/MultiArchApp/raw/arm_both/lib/armeabi-v7a/libtest_multi_arch_native_libs.so b/tests/tests/content/MultiArchApp/raw/arm_both/lib/armeabi-v7a/libtest_multi_arch_native_libs.so
new file mode 100755
index 0000000..c0c95e0
--- /dev/null
+++ b/tests/tests/content/MultiArchApp/raw/arm_both/lib/armeabi-v7a/libtest_multi_arch_native_libs.so
Binary files differ
diff --git a/tests/tests/content/MultiArchApp/raw/x86/lib/x86/libtest_multi_arch_native_libs.so b/tests/tests/content/MultiArchApp/raw/x86/lib/x86/libtest_multi_arch_native_libs.so
new file mode 100755
index 0000000..d095622
--- /dev/null
+++ b/tests/tests/content/MultiArchApp/raw/x86/lib/x86/libtest_multi_arch_native_libs.so
Binary files differ
diff --git a/tests/tests/content/MultiArchApp/raw/x86_64/lib/x86_64/libtest_multi_arch_native_libs.so b/tests/tests/content/MultiArchApp/raw/x86_64/lib/x86_64/libtest_multi_arch_native_libs.so
new file mode 100755
index 0000000..94b8dd9
--- /dev/null
+++ b/tests/tests/content/MultiArchApp/raw/x86_64/lib/x86_64/libtest_multi_arch_native_libs.so
Binary files differ
diff --git a/tests/tests/content/MultiArchApp/raw/x86_both/lib/x86/libtest_multi_arch_native_libs.so b/tests/tests/content/MultiArchApp/raw/x86_both/lib/x86/libtest_multi_arch_native_libs.so
new file mode 100755
index 0000000..d095622
--- /dev/null
+++ b/tests/tests/content/MultiArchApp/raw/x86_both/lib/x86/libtest_multi_arch_native_libs.so
Binary files differ
diff --git a/tests/tests/content/MultiArchApp/raw/x86_both/lib/x86_64/libtest_multi_arch_native_libs.so b/tests/tests/content/MultiArchApp/raw/x86_both/lib/x86_64/libtest_multi_arch_native_libs.so
new file mode 100755
index 0000000..94b8dd9
--- /dev/null
+++ b/tests/tests/content/MultiArchApp/raw/x86_both/lib/x86_64/libtest_multi_arch_native_libs.so
Binary files differ
diff --git a/tests/tests/content/MultiArchApp/src/com/android/cts/multiarch/app/MainActivity.java b/tests/tests/content/MultiArchApp/src/com/android/cts/multiarch/app/MainActivity.java
new file mode 100644
index 0000000..64b2fd53
--- /dev/null
+++ b/tests/tests/content/MultiArchApp/src/com/android/cts/multiarch/app/MainActivity.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 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 com.android.cts.multiarch.app;
+
+import android.app.Activity;
+
+/**
+ * Empty activity
+ */
+public class MainActivity extends Activity {
+}
diff --git a/tests/tests/content/src/android/content/pm/cts/PackageManagerMultiArchAppTest.java b/tests/tests/content/src/android/content/pm/cts/PackageManagerMultiArchAppTest.java
new file mode 100644
index 0000000..d301c5d
--- /dev/null
+++ b/tests/tests/content/src/android/content/pm/cts/PackageManagerMultiArchAppTest.java
@@ -0,0 +1,424 @@
+/*
+ * Copyright (C) 2024 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.content.pm.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assume.assumeTrue;
+
+import android.content.Context;
+import android.content.pm.Flags;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.SystemProperties;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.text.TextUtils;
+import android.util.ArraySet;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.SystemUtil;
+
+import dalvik.system.VMRuntime;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+@RunWith(AndroidJUnit4.class)
+public class PackageManagerMultiArchAppTest {
+
+    private static final String TEST_APP_PATH = "/data/local/tmp/cts/content/";
+    private static final String TEST_APP_APK_BASE = "CtsMultiArchApp";
+    private static final String BITNESS_32 = "32";
+    private static final String BITNESS_64 = "64";
+    private static final String BITNESS_BOTH = "Both";
+    private static final String BASE_ARCH_ARM = "arm";
+    private static final String BASE_ARCH_X86 = "x86";
+    private static final String BASE_ARCH_MIPS = "mips";
+
+    // List of supported abi
+    private static final String ABI_ARM_32 = "armeabi";
+    private static final String ABI_ARM_V7A = "armeabi-v7a";
+    private static final String ABI_ARM_64_V8A = "arm64-v8a";
+    private static final String ABI_X86 = "x86";
+    private static final String ABI_X86_64 = "x86_64";
+    private static final String ABI_MIPS = "mips";
+    private static final String ABI_MIPS64 = "mips64";
+    private static final String ABI_RISCV64 = "riscv64";
+
+    private static final String TEST_APP_PKG = "com.android.cts.multiarch.app";
+    private static final String EXPECTED_FAILED_ERROR_MESSAGE =
+            "don't support all the natively supported ABIs of the device";
+
+    private static final Set<String> BITS_32_SET = new HashSet<>(Arrays.asList(
+            "armeabi", "armeabi-v7a", "x86"));
+    private static final Map<String, String> ABI_TO_BASE_ARCH = new LinkedHashMap<String, String>();
+
+    private static String[] sDeviceSupported32Bits = null;
+    private static String[] sDeviceSupported64Bits = null;
+    private static String[] sSupportedEmulatedAbis = null;
+    private static String sDeviceDefaultAbi = null;
+    private static String sDeviceDefaultBitness = null;
+    private static String sDeviceDefaultBaseArch = null;
+    private static String sTestBaseArch = null;
+
+    static {
+        ABI_TO_BASE_ARCH.put(ABI_ARM_32, BASE_ARCH_ARM);
+        ABI_TO_BASE_ARCH.put(ABI_ARM_V7A, BASE_ARCH_ARM);
+        ABI_TO_BASE_ARCH.put(ABI_ARM_64_V8A, BASE_ARCH_ARM);
+        ABI_TO_BASE_ARCH.put(ABI_X86, BASE_ARCH_X86);
+        ABI_TO_BASE_ARCH.put(ABI_X86_64, BASE_ARCH_X86);
+        ABI_TO_BASE_ARCH.put(ABI_MIPS, BASE_ARCH_MIPS);
+        ABI_TO_BASE_ARCH.put(ABI_MIPS64, BASE_ARCH_MIPS);
+        ABI_TO_BASE_ARCH.put(ABI_RISCV64, ABI_RISCV64);
+    }
+
+    private PackageManager mPackageManager;
+
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+    private static String[] getSupportedEmulatedAbis() throws Exception {
+        if (sSupportedEmulatedAbis != null) {
+            return sSupportedEmulatedAbis;
+        }
+
+        Set<String> abiSet = new ArraySet<>();
+        getSupportedEmulatedAbis(getDeviceSupported64Abis(), abiSet);
+        getSupportedEmulatedAbis(getDeviceSupported32Abis(), abiSet);
+        sSupportedEmulatedAbis = abiSet.toArray(new String[0]);
+        return sSupportedEmulatedAbis;
+    }
+
+    private static void getSupportedEmulatedAbis(String[] supportedAbis, Set<String> abiSet)
+            throws Exception {
+        for (int i = 0; i < supportedAbis.length; i++) {
+            final String currentAbi = supportedAbis[i];
+            // In presence of a native bridge this means the Abi is emulated.
+            final String currentIsa = VMRuntime.getInstructionSet(currentAbi);
+            if (!TextUtils.isEmpty(SystemProperties.get("ro.dalvik.vm.isa." + currentIsa))) {
+                abiSet.add(currentAbi);
+            }
+        }
+    }
+
+    /** Returns the base architecture matching the abi. Null if the abi is not supported. */
+    @Nullable
+    private static String getBaseArchForAbi(@NonNull String abi) {
+        Objects.requireNonNull(abi);
+        if (abi.isEmpty()) {
+            throw new IllegalArgumentException("Abi cannot be empty");
+        }
+        return ABI_TO_BASE_ARCH.get(abi);
+    }
+
+    @NonNull
+    private static String getTestAppPath(@NonNull String abi, @NonNull String baseArch) {
+        Objects.requireNonNull(abi);
+
+        String bitness = BITNESS_64;
+        if (getBitness(abi).equals(BITNESS_32)) {
+            bitness = BITNESS_32;
+        }
+
+        return getTestApkPath(bitness, /* isTargetSDK33= */ false, baseArch);
+    }
+
+    @NonNull
+    private static String getTestApkPath(@NonNull String abiBit) {
+        return getTestApkPath(abiBit, /* isTargetSDK33= */ false, sTestBaseArch);
+    }
+
+    @NonNull
+    private static String getTestApkPath(@NonNull String abiBit, boolean isTargetSDK33) {
+        return getTestApkPath(abiBit, isTargetSDK33, sTestBaseArch);
+    }
+
+    @NonNull
+    private static String getTestApkPath(@NonNull String abiBit, boolean isTargetSDK33,
+            @NonNull String baseArch) {
+        Objects.requireNonNull(abiBit);
+        Objects.requireNonNull(baseArch);
+        return TEST_APP_PATH + TEST_APP_APK_BASE + abiBit + (isTargetSDK33 ? "_targetSdk33_" : "_")
+                + baseArch + ".apk";
+    }
+
+    @NonNull
+    private static String getBitness(String abi) {
+        return BITS_32_SET.contains(abi) ? BITNESS_32 : BITNESS_64;
+    }
+
+    @NonNull
+    private static String[] getDeviceSupported32Abis() throws Exception {
+        if (sDeviceSupported32Bits != null) {
+            return sDeviceSupported32Bits;
+        }
+
+        sDeviceSupported32Bits = Build.SUPPORTED_32_BIT_ABIS;
+        return sDeviceSupported32Bits;
+    }
+
+    @NonNull
+    private static String[] getDeviceSupported64Abis() throws Exception {
+        if (sDeviceSupported64Bits != null) {
+            return sDeviceSupported64Bits;
+        }
+
+        sDeviceSupported64Bits = Build.SUPPORTED_64_BIT_ABIS;
+        return sDeviceSupported64Bits;
+    }
+
+    @NonNull
+    private static String getDeviceDefaultAbi() throws Exception {
+        if (sDeviceDefaultAbi != null) {
+            return sDeviceDefaultAbi;
+        }
+        sDeviceDefaultAbi = SystemProperties.get("ro.product.cpu.abi");
+        return sDeviceDefaultAbi;
+    }
+
+    @NonNull
+    private static String getDeviceDefaultBitness() throws Exception {
+        if (sDeviceDefaultBitness != null) {
+            return sDeviceDefaultBitness;
+        }
+        sDeviceDefaultBitness = getBitness(getDeviceDefaultAbi());
+        return sDeviceDefaultBitness;
+    }
+
+    @NonNull
+    private static String getDeviceDefaultBaseArch() throws Exception {
+        if (sDeviceDefaultBaseArch != null) {
+            return sDeviceDefaultBaseArch;
+        }
+        sDeviceDefaultBaseArch = getBaseArchForAbi(getDeviceDefaultAbi());
+        assumeTrue("The default abi on the device is not supported.",
+                sDeviceDefaultBaseArch != null);
+        return sDeviceDefaultBaseArch;
+    }
+
+    private static boolean isBaseArchSupportedInAbis(@NonNull String baseArch,
+            @NonNull String[] abis) {
+        Objects.requireNonNull(baseArch);
+        Objects.requireNonNull(abis);
+
+        for (int i = 0; i < abis.length; i++) {
+            if (baseArch.equals(getBaseArchForAbi(abis[i]))) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static boolean isDeviceSupportsEmulatedAbi() throws Exception {
+        return getSupportedEmulatedAbis().length > 0;
+    }
+
+    /*
+     * Return true if the device supports both 32 bit and 64 bit ABIs. Otherwise, false.
+     */
+    private static boolean isDeviceSupportedBothBitness() throws Exception {
+        if (getDeviceDefaultBitness().equals(BITNESS_32)) {
+            return isBaseArchSupportedInAbis(getDeviceDefaultBaseArch(),
+                    getDeviceSupported64Abis());
+        } else {
+            return isBaseArchSupportedInAbis(getDeviceDefaultBaseArch(),
+                    getDeviceSupported32Abis());
+        }
+    }
+
+    private static boolean isSupportedBaseArch(@Nullable String baseArch) {
+        return BASE_ARCH_ARM.equals(baseArch) || BASE_ARCH_X86.equals(baseArch);
+    }
+
+    private boolean isInstalled() {
+        try {
+            PackageInfo pi = mPackageManager.getPackageInfo(TEST_APP_PKG, 0);
+            return pi != null;
+        } catch (PackageManager.NameNotFoundException e) {
+            return false;
+        }
+    }
+
+    @NonNull
+    private String installPackage(@NonNull String apkPath) {
+        Objects.requireNonNull(apkPath);
+        return SystemUtil.runShellCommand("pm install " + apkPath);
+    }
+
+    @NonNull
+    private void uninstallPackage(@NonNull String packageName) {
+        Objects.requireNonNull(packageName);
+        SystemUtil.runShellCommand("pm uninstall " + packageName);
+    }
+
+    /** Uninstall app before tests. */
+    @Before
+    public void setUp() throws Exception {
+        final Context context = InstrumentationRegistry.getInstrumentation().getContext();
+        mPackageManager = context.getPackageManager();
+        sTestBaseArch = getDeviceDefaultBaseArch();
+        uninstallPackage(TEST_APP_PKG);
+    }
+
+    /** Uninstall app after tests. */
+    @After
+    public void cleanUp() throws Exception {
+        uninstallPackage(TEST_APP_PKG);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_FORCE_MULTI_ARCH_NATIVE_LIBS_MATCH)
+    public void testInstallMultiArchApp_wrongBaseArch_fail() throws Exception {
+        String baseArch = null;
+        if (getDeviceDefaultBaseArch().equals(BASE_ARCH_ARM)) {
+            baseArch = BASE_ARCH_X86;
+        } else {
+            baseArch = BASE_ARCH_ARM;
+        }
+
+        String result = installPackage(getTestApkPath(getDeviceDefaultBitness(),
+                /* isTargetSDK33= */ false, baseArch));
+
+        assertThat(result).contains(EXPECTED_FAILED_ERROR_MESSAGE);
+        assertThat(isInstalled()).isFalse();
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_FORCE_MULTI_ARCH_NATIVE_LIBS_MATCH)
+    public void testInstallMultiArchApp32_notMatchAllNativelyAbis_fail() throws Exception {
+        assumeTrue(isDeviceSupportedBothBitness());
+
+        String result = installPackage(getTestApkPath(BITNESS_32));
+
+        assertThat(result).contains(EXPECTED_FAILED_ERROR_MESSAGE);
+        assertThat(isInstalled()).isFalse();
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_FORCE_MULTI_ARCH_NATIVE_LIBS_MATCH)
+    public void testInstallMultiArchApp64_notMatchAllNativelyAbis_fail() throws Exception {
+        assumeTrue(isDeviceSupportedBothBitness());
+
+        String result = installPackage(getTestApkPath(BITNESS_64));
+
+        assertThat(result).contains(EXPECTED_FAILED_ERROR_MESSAGE);
+        assertThat(isInstalled()).isFalse();
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_FORCE_MULTI_ARCH_NATIVE_LIBS_MATCH)
+    public void testInstallMultiArchApp32_targetSdk33_success() throws Exception {
+        assumeTrue(isDeviceSupportedBothBitness());
+
+        installPackage(getTestApkPath(BITNESS_32, /* isTargetSDK33= */ true));
+
+        assertThat(isInstalled()).isTrue();
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_FORCE_MULTI_ARCH_NATIVE_LIBS_MATCH)
+    public void testInstallMultiArchApp64_targetSdk33_success() throws Exception {
+        assumeTrue(isDeviceSupportedBothBitness());
+
+        installPackage(getTestApkPath(BITNESS_64, /* isTargetSDK33= */ true));
+
+        assertThat(isInstalled()).isTrue();
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_FORCE_MULTI_ARCH_NATIVE_LIBS_MATCH)
+    public void testInstallMultiArchApp_emulatedAbiNoNativelyAbi_fail() throws Exception {
+        assumeTrue(isDeviceSupportsEmulatedAbi());
+        final String firstEmulatedAbi = getSupportedEmulatedAbis()[0];
+        final String baseArch = getBaseArchForAbi(firstEmulatedAbi);
+        assumeTrue(isSupportedBaseArch(baseArch));
+
+        String result = installPackage(getTestAppPath(firstEmulatedAbi, baseArch));
+
+        assertThat(result).contains(EXPECTED_FAILED_ERROR_MESSAGE);
+        assertThat(isInstalled()).isFalse();
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_FORCE_MULTI_ARCH_NATIVE_LIBS_MATCH)
+    public void testInstallMultiArchApp_emulatedAbiNoNativelyAbi_success() throws Exception {
+        assumeTrue(isDeviceSupportsEmulatedAbi());
+        final String firstEmulatedAbi = getSupportedEmulatedAbis()[0];
+        final String baseArch = getBaseArchForAbi(firstEmulatedAbi);
+        assumeTrue(isSupportedBaseArch(baseArch));
+
+        installPackage(getTestAppPath(firstEmulatedAbi, baseArch));
+
+        assertThat(isInstalled()).isTrue();
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_FORCE_MULTI_ARCH_NATIVE_LIBS_MATCH)
+    public void testInstallMultiArchAppBoth_targetSdk33_success() throws Exception {
+        assumeTrue(isDeviceSupportedBothBitness());
+
+        installPackage(getTestApkPath(BITNESS_BOTH, /* isTargetSDK33= */ true));
+
+        assertThat(isInstalled()).isTrue();
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_FORCE_MULTI_ARCH_NATIVE_LIBS_MATCH)
+    public void testInstallMultiArchApp32_success() throws Exception {
+        assumeTrue(isDeviceSupportedBothBitness());
+
+        installPackage(getTestApkPath(BITNESS_32));
+
+        assertThat(isInstalled()).isTrue();
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_FORCE_MULTI_ARCH_NATIVE_LIBS_MATCH)
+    public void testInstallMultiArchApp64_success() throws Exception {
+        assumeTrue(isDeviceSupportedBothBitness());
+
+        installPackage(getTestApkPath(BITNESS_64));
+
+        assertThat(isInstalled()).isTrue();
+    }
+
+    @Test
+    public void testInstallMultiArchAppBoth_success() throws Exception {
+        assumeTrue(isDeviceSupportedBothBitness());
+
+        installPackage(getTestApkPath(BITNESS_BOTH));
+
+        assertThat(isInstalled()).isTrue();
+    }
+}