Add S extensions

Update the tests to ensure it is set on S+ but not otherwise.

Note that the implementation of how the version is derived is still a
placeholder (see b/173188089 for plans).

Bug: 173114427
Test: presubmit (SdkExtensionsTest on R and HEAD)
Merged-In: Ifc46e785a005e12cacb7ad86e8c7798e1d00b37f
Change-Id: Ifc46e785a005e12cacb7ad86e8c7798e1d00b37f
diff --git a/README.md b/README.md
index 8decafb..7f3831c 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,9 @@
 SdkExtensions is a module that decides the extension SDK level of the device,
 and provides APIs for applications to query the extension SDK level.
 
-## Structure
+## General information
+
+### Structure
 
 The module is packaged in an apex, `com.android.sdkext`, and has two components:
 - `bin/derive_sdk`: Native binary that runs early in the device boot process and
@@ -12,14 +14,14 @@
 - `javalib/framework-sdkextension.jar`: This is a jar on the bootclasspath that
   exposes APIs to applications to query the extension SDK level.
 
-## Deriving extension SDK level
+### Deriving extension SDK level
 `derive_sdk` is a program that reads metadata stored in other apex modules, in
 the form of binary protobuf files in subpath `etc/sdkinfo.binarypb` inside each
 apex. The structure of this protobuf can be seen [here][sdkinfo-proto]. The
 exact steps for converting a set of metadata files to actual extension versions
 is likely to change over time, and should not be depended upon.
 
-## Reading extension SDK level
+### Reading extension SDK level
 The module exposes a java class [`SdkExtensions`][sdkextensions-java] in the
 package `android.os.ext`. The method `getExtensionVersion(int)` can be used to
 read the version of a particular sdk extension, e.g.
@@ -27,3 +29,17 @@
 
 [sdkinfo-proto]: sdk.proto
 [sdkextensions-java]: framework/java/android/os/ext/SdkExtensions.java
+
+## Developer information
+
+### Adding a new extension version
+For every new Android SDK level a new extension version should be defined. These
+are the steps necessary to do that:
+- Make derive_sdk correctly set the relevant system property for the new
+  extensions.
+- Make the `SdkExtensions.getExtensionVersion` API support the new extensions.
+- Extend the CTS test to verify the above two behaviors.
+- Update `RollbackManagerServiceImpl#getExtensionVersions` to account for the
+  new extension version.
+
+TODO(b/173188089): expand this section when derive_sdk is reimplemented
diff --git a/derive_sdk/Android.bp b/derive_sdk/Android.bp
index 85672bd..3e030e8 100644
--- a/derive_sdk/Android.bp
+++ b/derive_sdk/Android.bp
@@ -26,7 +26,10 @@
     shared_libs: ["liblog"],
     // static c++/libbase for smaller size
     stl: "c++_static",
-    static_libs: ["libbase"],
+    static_libs: [
+        "libbase",
+        "libmodules-utils-build",
+    ],
 }
 
 cc_binary {
diff --git a/derive_sdk/derive_sdk.cpp b/derive_sdk/derive_sdk.cpp
index d8ab519..89c9adf 100644
--- a/derive_sdk/derive_sdk.cpp
+++ b/derive_sdk/derive_sdk.cpp
@@ -25,6 +25,7 @@
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/properties.h>
+#include <android-modules-utils/sdk_level.h>
 
 #include "packages/modules/SdkExtensions/derive_sdk/sdk.pb.h"
 
@@ -70,10 +71,16 @@
     std::string prop_value = itr == versions.end() ? "0" : std::to_string(*itr);
 
     if (!android::base::SetProperty("build.version.extensions.r", prop_value)) {
-        LOG(ERROR) << "failed to set sdk_info prop";
+        LOG(ERROR) << "failed to set r sdk_info prop";
         return EXIT_FAILURE;
     }
+    if (android::modules::sdklevel::IsAtLeastS()) {
+        if (!android::base::SetProperty("build.version.extensions.s", prop_value)) {
+            LOG(ERROR) << "failed to set s sdk_info prop";
+            return EXIT_FAILURE;
+        }
+    }
 
-    LOG(INFO) << "R extension version is " << prop_value;
+    LOG(INFO) << "Extension version is " << prop_value;
     return EXIT_SUCCESS;
 }
diff --git a/framework/java/android/os/ext/SdkExtensions.java b/framework/java/android/os/ext/SdkExtensions.java
index 6c25f28..299a0d9 100644
--- a/framework/java/android/os/ext/SdkExtensions.java
+++ b/framework/java/android/os/ext/SdkExtensions.java
@@ -37,18 +37,20 @@
 public class SdkExtensions {
 
     private static final int R_EXTENSION_INT;
+    private static final int S_EXTENSION_INT;
     static {
         // Note: when adding more extension versions, the logic that records current
         // extension versions when saving a rollback must also be updated.
         // At the time of writing this is in RollbackManagerServiceImpl#getExtensionVersions()
         R_EXTENSION_INT = SystemProperties.getInt("build.version.extensions.r", 0);
+        S_EXTENSION_INT = SystemProperties.getInt("build.version.extensions.s", 0);
     }
 
     /**
      * Values suitable as parameters for {@link #getExtensionVersion(int)}.
      * @hide
      */
-    @IntDef(value = { VERSION_CODES.R })
+    @IntDef(value = { VERSION_CODES.R, VERSION_CODES.CUR_DEVELOPMENT })
     @Retention(RetentionPolicy.SOURCE)
     public @interface SdkVersion {}
 
@@ -69,6 +71,9 @@
         if (sdk == VERSION_CODES.R) {
             return R_EXTENSION_INT;
         }
+        if (sdk == VERSION_CODES.CUR_DEVELOPMENT) {
+            return S_EXTENSION_INT;
+        }
         return 0;
     }
 
diff --git a/tests/cts/Android.bp b/tests/cts/Android.bp
index 89b241b..caa233f 100644
--- a/tests/cts/Android.bp
+++ b/tests/cts/Android.bp
@@ -18,6 +18,7 @@
     static_libs: [
         "androidx.test.rules",
         "ctstestrunner-axt",
+        "modules-utils-build",
     ],
     srcs: [ "src/**/*.java" ],
     test_config: "CtsSdkExtensionsTestCases.xml",
diff --git a/tests/cts/src/android/os/ext/cts/SdkExtensionsTest.java b/tests/cts/src/android/os/ext/cts/SdkExtensionsTest.java
index a320a84..dba4791 100644
--- a/tests/cts/src/android/os/ext/cts/SdkExtensionsTest.java
+++ b/tests/cts/src/android/os/ext/cts/SdkExtensionsTest.java
@@ -19,6 +19,7 @@
 import android.os.Build;
 import android.os.SystemProperties;
 import android.os.ext.SdkExtensions;
+import com.android.modules.utils.build.SdkLevel;
 import junit.framework.TestCase;
 
 public class SdkExtensionsTest extends TestCase {
@@ -45,5 +46,8 @@
     /** Verifies that the public sysprops are set as expected */
     public void testSystemProperties() throws Exception {
         assertEquals("0", SystemProperties.get("build.version.extensions.r"));
+        String expectedS = SdkLevel.isAtLeastS() ? "0" : "";
+        assertEquals(expectedS, SystemProperties.get("build.version.extensions.s"));
     }
+
 }