Merge "Fix typo in TEST_MAPPING"
diff --git a/tests/e2e/Android.bp b/tests/e2e/Android.bp
index a8f539e..c34f316 100644
--- a/tests/e2e/Android.bp
+++ b/tests/e2e/Android.bp
@@ -27,7 +27,9 @@
     data: [
         ":sdkextensions_e2e_test_app",
         ":sdkextensions_e2e_test_app_req_r12",
+        ":sdkextensions_e2e_test_app_req_s12",
         ":sdkextensions_e2e_test_app_req_r45",
+        ":sdkextensions_e2e_test_app_req_s45",
         ":test_com.android.media",
         ":test_com.android.sdkext",
     ],
@@ -39,6 +41,7 @@
     name: "sdkextensions_e2e_test_app",
     srcs: ["app-src/**/*.java"],
     libs: ["test_framework-sdkextensions-stubs"],
+    static_libs: ["modules-utils-build_system"],
     sdk_version: "system_current",
     min_sdk_version: "30",
 }
diff --git a/tests/e2e/AndroidManifest.xml b/tests/e2e/AndroidManifest.xml
index 0e4f8d1..d3fe85f 100644
--- a/tests/e2e/AndroidManifest.xml
+++ b/tests/e2e/AndroidManifest.xml
@@ -30,6 +30,9 @@
                 <action android:name="com.android.tests.apex.sdkextensions.MAKE_CALLS_45" />
             </intent-filter>
             <intent-filter>
+                <action android:name="com.android.tests.apex.sdkextensions.IS_AT_LEAST" />
+            </intent-filter>
+            <intent-filter>
                 <action android:name="com.android.tests.apex.sdkextensions.GET_SDK_VERSION" />
             </intent-filter>
         </receiver>
diff --git a/tests/e2e/app-src/com/android/tests/apex/sdkextensions/Receiver.java b/tests/e2e/app-src/com/android/tests/apex/sdkextensions/Receiver.java
index d9f74fa..4b1a599 100644
--- a/tests/e2e/app-src/com/android/tests/apex/sdkextensions/Receiver.java
+++ b/tests/e2e/app-src/com/android/tests/apex/sdkextensions/Receiver.java
@@ -24,10 +24,14 @@
 import android.os.ext.test.Test;
 import android.util.Log;
 
+import com.android.modules.utils.build.SdkLevel;
+
 public class Receiver extends BroadcastReceiver {
 
     private static final String ACTION_GET_SDK_VERSION =
             "com.android.tests.apex.sdkextensions.GET_SDK_VERSION";
+    private static final String ACTION_IS_AT_LEAST =
+            "com.android.tests.apex.sdkextensions.IS_AT_LEAST";
     private static final String ACTION_MAKE_CALLS_0 =
             "com.android.tests.apex.sdkextensions.MAKE_CALLS_0";
     private static final String ACTION_MAKE_CALLS_45 =
@@ -41,6 +45,14 @@
         throw new IllegalArgumentException(String.valueOf(letter));
     }
 
+    private static boolean isAtLeast(char letter) {
+        switch (letter) {
+            case 'r': return true; // our min sdk version is 30
+            case 's': return SdkLevel.isAtLeastS();
+        }
+        return false;
+    }
+
     @Override
     public void onReceive(Context context, Intent intent) {
         try {
@@ -50,6 +62,10 @@
                     int sdkVersion = SdkExtensions.getExtensionVersion(extension);
                     setResultData(String.valueOf(sdkVersion));
                     break;
+                case ACTION_IS_AT_LEAST:
+                    boolean value = isAtLeast(intent.getStringExtra("extra").charAt(0));
+                    setResultData(String.valueOf(value));
+                    break;
                 case ACTION_MAKE_CALLS_0:
                     makeCallsVersion0();
                     setResultData("true");
diff --git a/tests/e2e/apps-with-reqs/Android.bp b/tests/e2e/apps-with-reqs/Android.bp
index c293665..d5257d7 100644
--- a/tests/e2e/apps-with-reqs/Android.bp
+++ b/tests/e2e/apps-with-reqs/Android.bp
@@ -24,8 +24,22 @@
 }
 
 android_app {
+    name: "sdkextensions_e2e_test_app_req_s12",
+    sdk_version: "current",
+    manifest: "AndroidManifest-s12.xml",
+    min_sdk_version: "30",
+}
+
+android_app {
     name: "sdkextensions_e2e_test_app_req_r45",
     sdk_version: "current",
     manifest: "AndroidManifest-r45.xml",
     min_sdk_version: "30",
 }
+
+android_app {
+    name: "sdkextensions_e2e_test_app_req_s45",
+    sdk_version: "current",
+    manifest: "AndroidManifest-s45.xml",
+    min_sdk_version: "30",
+}
diff --git a/tests/e2e/apps-with-reqs/AndroidManifest-r12.xml b/tests/e2e/apps-with-reqs/AndroidManifest-r12.xml
index a74f8be..92aff31 100644
--- a/tests/e2e/apps-with-reqs/AndroidManifest-r12.xml
+++ b/tests/e2e/apps-with-reqs/AndroidManifest-r12.xml
@@ -18,7 +18,6 @@
 
     <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="29">
         <extension-sdk android:sdkVersion="30" android:minExtensionVersion="12" />
-        <extension-sdk android:sdkVersion="10000" android:minExtensionVersion="12" />
     </uses-sdk>
 
     <application android:hasCode="false"></application>
diff --git a/tests/e2e/apps-with-reqs/AndroidManifest-r45.xml b/tests/e2e/apps-with-reqs/AndroidManifest-r45.xml
index f3cc3a3..ff5eb0e 100644
--- a/tests/e2e/apps-with-reqs/AndroidManifest-r45.xml
+++ b/tests/e2e/apps-with-reqs/AndroidManifest-r45.xml
@@ -18,7 +18,6 @@
 
     <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="29">
         <extension-sdk android:sdkVersion="30" android:minExtensionVersion="45" />
-        <extension-sdk android:sdkVersion="10000" android:minExtensionVersion="45" />
     </uses-sdk>
 
     <application android:hasCode="false"></application>
diff --git a/tests/e2e/apps-with-reqs/AndroidManifest-s12.xml b/tests/e2e/apps-with-reqs/AndroidManifest-s12.xml
new file mode 100644
index 0000000..fc5995c
--- /dev/null
+++ b/tests/e2e/apps-with-reqs/AndroidManifest-s12.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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.tests.apex.sdkextensions.s12">
+
+    <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="29">
+        <extension-sdk android:sdkVersion="30" android:minExtensionVersion="12" />
+        <extension-sdk android:sdkVersion="10000" android:minExtensionVersion="12" />
+    </uses-sdk>
+
+    <application android:hasCode="false"></application>
+</manifest>
diff --git a/tests/e2e/apps-with-reqs/AndroidManifest-s45.xml b/tests/e2e/apps-with-reqs/AndroidManifest-s45.xml
new file mode 100644
index 0000000..478553c
--- /dev/null
+++ b/tests/e2e/apps-with-reqs/AndroidManifest-s45.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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.tests.apex.sdkextensions.s45">
+
+    <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="29">
+        <extension-sdk android:sdkVersion="30" android:minExtensionVersion="45" />
+        <extension-sdk android:sdkVersion="10000" android:minExtensionVersion="45" />
+    </uses-sdk>
+
+    <application android:hasCode="false"></application>
+</manifest>
diff --git a/tests/e2e/test-src/com/android/tests/apex/sdkextensions/SdkExtensionsHostTest.java b/tests/e2e/test-src/com/android/tests/apex/sdkextensions/SdkExtensionsHostTest.java
index c251cac..e47f6dc 100644
--- a/tests/e2e/test-src/com/android/tests/apex/sdkextensions/SdkExtensionsHostTest.java
+++ b/tests/e2e/test-src/com/android/tests/apex/sdkextensions/SdkExtensionsHostTest.java
@@ -50,8 +50,12 @@
     private static final String APP_PACKAGE = "com.android.tests.apex.sdkextensions";
     private static final String APP_R12_FILENAME = "sdkextensions_e2e_test_app_req_r12.apk";
     private static final String APP_R12_PACKAGE = "com.android.tests.apex.sdkextensions.r12";
+    private static final String APP_S12_FILENAME = "sdkextensions_e2e_test_app_req_s12.apk";
+    private static final String APP_S12_PACKAGE = "com.android.tests.apex.sdkextensions.s12";
     private static final String APP_R45_FILENAME = "sdkextensions_e2e_test_app_req_r45.apk";
     private static final String APP_R45_PACKAGE = "com.android.tests.apex.sdkextensions.r45";
+    private static final String APP_S45_FILENAME = "sdkextensions_e2e_test_app_req_s45.apk";
+    private static final String APP_S45_PACKAGE = "com.android.tests.apex.sdkextensions.s45";
     private static final String MEDIA_FILENAME = "test_com.android.media.apex";
     private static final String SDKEXTENSIONS_FILENAME = "test_com.android.sdkext.apex";
 
@@ -59,6 +63,8 @@
 
     private final InstallUtilsHost mInstallUtils = new InstallUtilsHost(this);
 
+    private Boolean mIsAtLeastS = null;
+
     @Rule
     public AbandonSessionsRule mHostTestRule = new AbandonSessionsRule(this);
 
@@ -95,11 +101,9 @@
         // R-Version 12 requires sdkext, which is fulfilled
         // R-Version 45 requires sdkext + media, which isn't fulfilled
         // S-Version 45 does not have any particular requirements.
-        assertEquals(12, getExtensionVersionR());
-        assertEquals(45, getExtensionVersionS());
+        assertRVersionEquals(12);
+        assertSVersionEquals(45);
         assertEquals("true", broadcast("MAKE_CALLS_45")); // sdkext 45 APIs are available in 12 too.
-        assertTrue(canInstallApp(APP_R12_FILENAME, APP_R12_PACKAGE));
-        assertFalse(canInstallApp(APP_R45_FILENAME, APP_R45_PACKAGE));
     }
 
     @Test
@@ -123,32 +127,18 @@
     private boolean canInstallApp(String filename, String packageName) throws Exception {
         File appFile = mInstallUtils.getTestFile(filename);
         String installResult = getDevice().installPackage(appFile, true);
-        if (installResult == null) {
-            assertNull(getDevice().uninstallPackage(packageName));
+        if (installResult != null) {
+            return false;
         }
-        return installResult == null;
+        assertNull(getDevice().uninstallPackage(packageName));
+        return true;
     }
 
-    private int getExtensionVersionR() throws Exception {
-        int appValue = Integer.parseInt(broadcast("GET_SDK_VERSION", "r"));
-        int syspropValue = getExtensionVersionFromSysprop("r");
-        assertEquals(appValue, syspropValue);
-        return appValue;
-    }
-
-    private int getExtensionVersionS() throws Exception {
-        int appValue = Integer.parseInt(broadcast("GET_SDK_VERSION", "s"));
-        int syspropValue = getExtensionVersionFromSysprop("s");
-        assertEquals(appValue, syspropValue);
-        return appValue;
-    }
-
-    private int getExtensionVersionFromSysprop(String v) throws Exception {
+    private String getExtensionVersionFromSysprop(String v) throws Exception {
         String command = "getprop build.version.extensions." + v;
         CommandResult res = getDevice().executeShellV2Command(command);
         assertEquals(0, (int) res.getExitCode());
-        String syspropString = res.getStdout().replace("\n", "");
-        return Integer.parseInt(syspropString);
+        return res.getStdout().replace("\n", "");
     }
 
     private String broadcast(String action) throws Exception {
@@ -165,19 +155,43 @@
     }
 
     private void assertVersion0() throws Exception {
-        assertEquals(0, getExtensionVersionR());
-        assertEquals(0, getExtensionVersionS());
+        assertRVersionEquals(0);
+        assertSVersionEquals(0);
         assertEquals("true", broadcast("MAKE_CALLS_0"));
-        assertFalse(canInstallApp(APP_R12_FILENAME, APP_R12_PACKAGE));
-        assertFalse(canInstallApp(APP_R45_FILENAME, APP_R45_PACKAGE));
     }
 
     private void assertVersion45() throws Exception {
-        assertEquals(45, getExtensionVersionR());
-        assertEquals(45, getExtensionVersionS());
+        assertRVersionEquals(45);
+        assertSVersionEquals(45);
         assertEquals("true", broadcast("MAKE_CALLS_45"));
-        assertTrue(canInstallApp(APP_R12_FILENAME, APP_R12_PACKAGE));
-        assertTrue(canInstallApp(APP_R45_FILENAME, APP_R45_PACKAGE));
+    }
+
+    private void assertRVersionEquals(int version) throws Exception {
+        int appValue = Integer.parseInt(broadcast("GET_SDK_VERSION", "r"));
+        String syspropValue = getExtensionVersionFromSysprop("r");
+        assertEquals(version, appValue);
+        assertEquals(String.valueOf(version), syspropValue);
+        assertEquals(version >= 12, canInstallApp(APP_R12_FILENAME, APP_R12_PACKAGE));
+        assertEquals(version >= 45, canInstallApp(APP_R45_FILENAME, APP_R45_PACKAGE));
+    }
+
+    private void assertSVersionEquals(int version) throws Exception {
+        int appValue = Integer.parseInt(broadcast("GET_SDK_VERSION", "s"));
+        String syspropValue = getExtensionVersionFromSysprop("s");
+        if (isAtLeastS()) {
+            assertEquals(version, appValue);
+            assertEquals(String.valueOf(version), syspropValue);
+
+            // These APKs require the same R version as they do S version.
+            int minVersion = Math.min(version, Integer.parseInt(broadcast("GET_SDK_VERSION", "r")));
+            assertEquals(minVersion >= 12, canInstallApp(APP_S12_FILENAME, APP_S12_PACKAGE));
+            assertEquals(minVersion >= 45, canInstallApp(APP_S45_FILENAME, APP_S45_PACKAGE));
+        } else {
+            assertEquals(0, appValue);
+            assertEquals("", syspropValue);
+            assertFalse(canInstallApp(APP_S12_FILENAME, APP_S12_PACKAGE));
+            assertFalse(canInstallApp(APP_S45_FILENAME, APP_S45_PACKAGE));
+        }
     }
 
     private static String getBroadcastCommand(String action, String extra) {
@@ -190,6 +204,13 @@
         return cmd;
     }
 
+    private boolean isAtLeastS() throws Exception {
+        if (mIsAtLeastS == null) {
+            mIsAtLeastS = broadcast("IS_AT_LEAST", "s").equals("true");
+        }
+        return mIsAtLeastS;
+    }
+
     private boolean uninstallApexes(String... filenames) throws Exception {
         boolean reboot = false;
         for (String filename : filenames) {