[automerger skipped] DO NOT MERGE Allow extension version 12 am: 6faadcdd56 -s ours am: 25c8a905db -s ours am: f9d6219a1f -s ours am: d9518f8231 -s ours

am skip reason: contains skip directive

Original change: https://android-review.googlesource.com/c/platform/packages/modules/SdkExtensions/+/2988969

Change-Id: If84d021774188e8bd3548ce947e9d65c18f0c4ff
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/Android.bp b/Android.bp
index 954e222..e73db99 100644
--- a/Android.bp
+++ b/Android.bp
@@ -107,7 +107,7 @@
     static_libs: ["ClasspathFetcher"],
     libs: [
         "tradefed",
-        "truth-prebuilt",
+        "truth",
     ],
     test_config: "sdkext-conformance-framework.xml",
     test_suites: [
@@ -115,3 +115,18 @@
         "device-tests",
     ],
 }
+
+// TODO: b/316383039 - Remove this module and the script when udc-mainline-prod
+// branch is removed.
+// The options passed to Metalava when generating API signature files and stubs
+// for SDK extension releases.
+genrule {
+    name: "sdkext-released-flagged-apis",
+    visibility: [
+        "//visibility:public",
+    ],
+    tool_files: ["keep-flagged-apis.sh"],
+    srcs: ["released-flagged-apis.txt"],
+    out: ["metalava-keep-flagged-apis.txt"],
+    cmd: "$(location keep-flagged-apis.sh) \"$(in)\" > \"$(out)\"",
+}
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 0771489..ab1d052 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -3,6 +3,7 @@
 clang_format = true
 commit_msg_changeid_field = true
 commit_msg_test_field = true
+google_java_format = true
 
 [Builtin Hooks Options]
 clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
@@ -10,3 +11,7 @@
 [Hook Scripts]
 do_not_use_DO_NOT_MERGE = ${REPO_ROOT}/build/soong/scripts/check_do_not_merge.sh ${PREUPLOAD_COMMIT}
 check_java_paths = ${REPO_ROOT}/frameworks/libs/modules-utils/tools/check_java_paths.py
+
+[Tool Paths]
+google-java-format = ${REPO_ROOT}/prebuilts/tools/common/google-java-format/google-java-format
+google-java-format-diff = ${REPO_ROOT}/prebuilts/tools/common/google-java-format/google-java-format-diff.py
diff --git a/derive_sdk/Android.bp b/derive_sdk/Android.bp
index 7cb5be4..e381ef9 100644
--- a/derive_sdk/Android.bp
+++ b/derive_sdk/Android.bp
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 package {
+    default_team: "trendy_team_updatable_sdk_apis",
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
@@ -42,7 +43,19 @@
     defaults: ["derive_sdk-defaults"],
     srcs: ["main.cpp"],
     static_libs: ["libderive_sdk"],
+}
 
+cc_library {
+    name: "libderive_sdk_jni",
+    srcs: ["derive_sdk_jni.cpp"],
+    header_libs: ["libnativehelper_header_only"],
+    shared_libs: [
+        "liblog",
+        "libderive_sdk",
+    ],
+    static_libs: ["libbase"],
+    min_sdk_version: "30",
+    stl: "c++_static",
 }
 
 cc_binary {
diff --git a/derive_sdk/derive_sdk.cpp b/derive_sdk/derive_sdk.cpp
index 056f2bf..936a36a 100644
--- a/derive_sdk/derive_sdk.cpp
+++ b/derive_sdk/derive_sdk.cpp
@@ -152,29 +152,12 @@
 
 bool ReadSdkInfoFromApexes(const std::string& mountpath,
                            std::unordered_map<SdkModule, int>& versions) {
-  std::unique_ptr<DIR, decltype(&closedir)> apex(opendir(mountpath.c_str()),
-                                                 closedir);
-  if (!apex) {
-    LOG(ERROR) << "Could not read " + mountpath;
-    return false;
-  }
-  struct dirent* de;
-  while ((de = readdir(apex.get()))) {
-    std::string name = de->d_name;
-    if (name[0] == '.' || name.find('@') != std::string::npos) {
-      // Skip <name>@<ver> dirs, as they are bind-mounted to <name>
-      continue;
-    }
-    std::string path = mountpath + "/" + name + "/etc/sdkinfo.pb";
+  for (const auto& module_itr : kApexNameToModule) {
+    std::string path = mountpath + "/" + module_itr.first + "/etc/sdkinfo.pb";
     struct stat statbuf;
     if (stat(path.c_str(), &statbuf) != 0) {
       continue;
     }
-    auto module_itr = kApexNameToModule.find(name);
-    if (module_itr == kApexNameToModule.end()) {
-      LOG(WARNING) << "Found sdkinfo in unexpected apex " << name;
-      continue;
-    }
     std::string contents;
     if (!android::base::ReadFileToString(path, &contents, true)) {
       LOG(ERROR) << "failed to read " << path;
@@ -185,7 +168,7 @@
       LOG(ERROR) << "failed to parse " << path;
       continue;
     }
-    SdkModule module = module_itr->second;
+    SdkModule module = module_itr.second;
     LOG(INFO) << "Read version " << sdk_version.version() << " from " << module;
     versions[module] = sdk_version.version();
   }
@@ -279,7 +262,7 @@
   return true;
 }
 
-bool PrintDump(const std::string& mountpath) {
+bool PrintDump(const std::string& mountpath, std::ostream& ostream) {
   std::map<std::string, std::string> properties;
   ReadSystemProperties(properties);
 
@@ -289,14 +272,38 @@
     return false;
   }
 
-  std::cout << "system properties:\n";
+  ostream << "system properties:\n";
   for (const auto& property : properties) {
-    std::cout << "  " << property.first << ":" << property.second << "\n";
+    ostream << "  " << property.first << ":" << property.second << "\n";
   }
 
-  std::cout << "apex module versions:\n";
+  ostream << "apex module versions:\n";
   for (const auto& version : versions) {
-    std::cout << "  " << SdkModule_Name(version.first) << ":" << version.second << "\n";
+    ostream << "  " << SdkModule_Name(version.first) << ":" << version.second << "\n";
+  }
+
+  ExtensionDatabase db;
+  if (!ReadDatabase(mountpath + "/com.android.sdkext/etc/extensions_db.pb", db)) {
+    LOG(ERROR) << "Failed to read database";
+    return false;
+  }
+  std::map<int, std::unordered_set<SdkModule>> new_requirements;
+  for (const auto& ext_version : db.versions()) {
+    std::unordered_set<SdkModule> new_required;
+    for (const auto& requirement : ext_version.requirements()) {
+      if (requirement.version().version() == ext_version.version())
+        new_required.insert(requirement.module());
+    }
+    new_requirements[ext_version.version()] = new_required;
+  }
+
+  ostream << "last 3 version requirements:\n";
+  int i = 0;
+  for (auto itr = new_requirements.crbegin(); itr != new_requirements.crend() && i < 3;
+       ++itr, ++i) {
+    ostream << "  " << itr->first << ": ";
+    for (auto const& module : itr->second) ostream << SdkModule_Name(module) << " ";
+    ostream << std::endl;
   }
 
   return true;
diff --git a/derive_sdk/derive_sdk.h b/derive_sdk/derive_sdk.h
index 24c26fc..30c1b5b 100644
--- a/derive_sdk/derive_sdk.h
+++ b/derive_sdk/derive_sdk.h
@@ -22,7 +22,7 @@
 namespace derivesdk {
 
 bool PrintHeader();
-bool PrintDump(const std::string& mountpath);
+bool PrintDump(const std::string& mountpath, std::ostream&);
 bool SetSdkLevels(const std::string& mountpath);
 
 }  // namespace derivesdk
diff --git a/derive_sdk/derive_sdk_jni.cpp b/derive_sdk/derive_sdk_jni.cpp
new file mode 100644
index 0000000..5ccc465
--- /dev/null
+++ b/derive_sdk/derive_sdk_jni.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+#define LOG_TAG "derive_sdk"
+
+#include <sstream>
+#include "android-base/logging.h"
+#include "derive_sdk.h"
+#include "jni.h"
+
+namespace android {
+namespace derivesdk {
+
+constexpr const char* DERIVE_SDK_CLASS_NAME = "com/android/os/ext/testing/DeriveSdk";
+
+jstring com_android_os_ext_testing_DeriveSdk_dump(JNIEnv* env) {
+  std::stringstream stream;
+  PrintDump("/apex", stream);
+
+  return env->NewStringUTF(stream.str().c_str());
+}
+
+const JNINativeMethod methods[] = {
+    {"native_dump", "()Ljava/lang/String;",
+     reinterpret_cast<void*>(com_android_os_ext_testing_DeriveSdk_dump)},
+};
+
+void register_com_android_os_ext_testing_DeriveSdk(JNIEnv* env) {
+  auto gDeriveSdkClass =
+      static_cast<jclass>(env->NewGlobalRef(env->FindClass(DERIVE_SDK_CLASS_NAME)));
+
+  if (gDeriveSdkClass == nullptr) {
+    LOG(FATAL) << "Unable to find class : " << DERIVE_SDK_CLASS_NAME;
+  }
+
+  if (env->RegisterNatives(gDeriveSdkClass, methods, sizeof(methods) / sizeof(methods[0])) < 0) {
+    LOG(FATAL) << "Unable to register native methods";
+  }
+}
+}  // namespace derivesdk
+}  // namespace android
+
+extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) {
+  JNIEnv* env;
+  if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+    LOG(ERROR) << "ERROR: GetEnv failed";
+    return JNI_ERR;
+  }
+
+  android::derivesdk::register_com_android_os_ext_testing_DeriveSdk(env);
+
+  return JNI_VERSION_1_6;
+}
diff --git a/derive_sdk/main.cpp b/derive_sdk/main.cpp
index c652d54..ffd8291 100644
--- a/derive_sdk/main.cpp
+++ b/derive_sdk/main.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <cstdlib>
+#include <iostream>
 
 #include "derive_sdk.h"
 
@@ -24,7 +25,7 @@
   if (argc > 1 && !strcmp("--header", argv[1])) {
     return android::derivesdk::PrintHeader() ? EXIT_SUCCESS : EXIT_FAILURE;
   } else if (argc > 1 && !strcmp("--dump", argv[1])) {
-    return android::derivesdk::PrintDump(MOUNTPOINT) ? EXIT_SUCCESS : EXIT_FAILURE;
+    return android::derivesdk::PrintDump(MOUNTPOINT, std::cout) ? EXIT_SUCCESS : EXIT_FAILURE;
   } else {
     return android::derivesdk::SetSdkLevels(MOUNTPOINT) ? EXIT_SUCCESS : EXIT_FAILURE;
   }
diff --git a/gen_sdk/Android.bp b/gen_sdk/Android.bp
index 655b2c3..e014d5d 100644
--- a/gen_sdk/Android.bp
+++ b/gen_sdk/Android.bp
@@ -15,6 +15,7 @@
  */
 
 package {
+    default_team: "trendy_team_updatable_sdk_apis",
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
diff --git a/gen_sdk/extensions_db.textpb b/gen_sdk/extensions_db.textpb
index 7342367..1edc5b9 100644
--- a/gen_sdk/extensions_db.textpb
+++ b/gen_sdk/extensions_db.textpb
@@ -447,12 +447,6 @@
       version: 3
     }
   }
-  requirements {
-    module: EXT_SERVICES
-    version {
-      version: 6
-    }
-  }
 }
 versions {
   version: 7
@@ -535,12 +529,6 @@
     }
   }
   requirements {
-    module: EXT_SERVICES
-    version {
-      version: 7
-    }
-  }
-  requirements {
     module: CONFIG_INFRASTRUCTURE
     version {
       version: 7
diff --git a/gen_sdk/gen_sdk.py b/gen_sdk/gen_sdk.py
index 07bfdcc..438157b 100644
--- a/gen_sdk/gen_sdk.py
+++ b/gen_sdk/gen_sdk.py
@@ -109,8 +109,8 @@
         return 'SDK %d has a requirement on an undefined module value' % version.version
       has_adservices = SdkModule.AD_SERVICES in required_modules
       has_extservices = SdkModule.EXT_SERVICES in required_modules
-      if version.version >= 6 and (has_adservices ^ has_extservices):
-        return 'AD_SERVICES and EXT_SERVICES must be finalized together as of version 6'
+      if version.version >= 9 and (has_adservices ^ has_extservices):
+        return 'AD_SERVICES and EXT_SERVICES must be finalized together as of version 9'
 
     return None
 
diff --git a/gen_sdk/gen_sdk_test.sh b/gen_sdk/gen_sdk_test.sh
old mode 100644
new mode 100755
index c4f4232..fe11eef
--- a/gen_sdk/gen_sdk_test.sh
+++ b/gen_sdk/gen_sdk_test.sh
@@ -113,14 +113,14 @@
 
   set +e
   for m in AD_SERVICES EXT_SERVICES; do
-    if gen_sdk --action new_sdk --sdk 6 --modules $m --database ${db}; then
+    if gen_sdk --action new_sdk --sdk 9 --modules $m --database ${db}; then
       echo "FAILED: expected new sdk with module $m to be invalid"
       exit 1
     fi
   done
   set -e
 
-  gen_sdk --action new_sdk --sdk 6 --modules AD_SERVICES,EXT_SERVICES --database ${db}
+  gen_sdk --action new_sdk --sdk 9 --modules AD_SERVICES,EXT_SERVICES --database ${db}
 }
 test_dependent_modules
 
diff --git a/java/android/os/ext/Android.bp b/java/android/os/ext/Android.bp
index 76bcc2f..831bad5 100644
--- a/java/android/os/ext/Android.bp
+++ b/java/android/os/ext/Android.bp
@@ -47,12 +47,3 @@
         "test_com.android.sdkext",
     ],
 }
-
-java_api_contribution {
-    name: "framework-sdkextensions-public-stubs",
-    api_surface: "public",
-    api_file: "api/current.txt",
-    visibility: [
-        "//build/orchestrator/apis",
-    ],
-}
diff --git a/java/android/os/ext/SdkExtensions.java b/java/android/os/ext/SdkExtensions.java
index e54bf03..fe0c4f1 100644
--- a/java/android/os/ext/SdkExtensions.java
+++ b/java/android/os/ext/SdkExtensions.java
@@ -32,12 +32,12 @@
 /**
  * Methods for interacting with the extension SDK.
  *
- * This class provides information about the extension SDK versions present
- * on this device. Use the {@link #getExtensionVersion(int) getExtension} method
- * to lookup the version of a given extension.
+ * <p>This class provides information about the extension SDK versions present on this device. Use
+ * the {@link #getExtensionVersion(int) getExtension} method to lookup the version of a given
+ * extension.
  *
- * The extension version advances as the platform evolves and new APIs are added,
- * so is suitable to use for determining API availability at runtime.
+ * <p>The extension version advances as the platform evolves and new APIs are added, so is suitable
+ * to use for determining API availability at runtime.
  */
 public class SdkExtensions {
 
@@ -49,6 +49,7 @@
     private static final int U_EXTENSION_INT;
     private static final int AD_SERVICES_EXTENSION_INT;
     private static final Map<Integer, Integer> ALL_EXTENSION_INTS;
+
     static {
         R_EXTENSION_INT = SystemProperties.getInt("build.version.extensions.r", 0);
         S_EXTENSION_INT = SystemProperties.getInt("build.version.extensions.s", 0);
@@ -73,25 +74,28 @@
 
     /**
      * Values suitable as parameters for {@link #getExtensionVersion(int)}.
+     *
      * @hide
      */
-    @IntDef(value = {
-          VERSION_CODES.R,
-          VERSION_CODES.S,
-          VERSION_CODES.TIRAMISU,
-          VERSION_CODES.UPSIDE_DOWN_CAKE,
-          AD_SERVICES,
-        })
+    @IntDef(
+            value = {
+                VERSION_CODES.R,
+                VERSION_CODES.S,
+                VERSION_CODES.TIRAMISU,
+                VERSION_CODES.UPSIDE_DOWN_CAKE,
+                AD_SERVICES,
+            })
     @Retention(RetentionPolicy.SOURCE)
     public @interface Extension {}
 
-    private SdkExtensions() { }
+    private SdkExtensions() {}
 
     /**
      * Return the version of the specified extensions.
      *
-     * This method is suitable to use in conditional statements to determine whether an API is
+     * <p>This method is suitable to use in conditional statements to determine whether an API is
      * available and is safe to use. For example:
+     *
      * <pre>
      * if (getExtensionVersion(VERSION_CODES.R) >= 3) {
      *   // Safely use API available since R extensions version 3
@@ -133,5 +137,4 @@
     public static Map<Integer, Integer> getAllExtensionVersions() {
         return ALL_EXTENSION_INTS;
     }
-
 }
diff --git a/java/android/os/ext/testing/Test.java b/java/android/os/ext/testing/Test.java
index f96f6c7..1b050fc 100644
--- a/java/android/os/ext/testing/Test.java
+++ b/java/android/os/ext/testing/Test.java
@@ -57,5 +57,4 @@
 
     /** @hide */
     public static void staticHiddenMethod() {}
-
 }
diff --git a/java/com/android/os/ext/testing/Android.bp b/java/com/android/os/ext/testing/Android.bp
index db39061..10163dc 100644
--- a/java/com/android/os/ext/testing/Android.bp
+++ b/java/com/android/os/ext/testing/Android.bp
@@ -17,9 +17,27 @@
     default_visibility: ["//packages/modules/SdkExtensions/javatests:__subpackages__"],
 }
 
+genrule {
+    name: "current_version_src",
+    srcs: ["CurrentVersionTemplate.java"],
+    out: ["CurrentVersion.java"],
+    product_variables: {
+        platform_sdk_extension_version: {
+            cmd: "sed -E -e '/CURRENT_TRAIN_VERSION =/ s/\\S+;/%d;/' $(in) > $(out)",
+        },
+    },
+}
+
 java_library {
     name: "test_util_current_version",
-    srcs: ["CurrentVersion.java"],
+    srcs: [":current_version_src"],
+    host_supported: true,
+    min_sdk_version: "30",
+}
+
+java_library {
+    name: "test_util_derive_sdk",
+    srcs: ["DeriveSdk.java"],
     host_supported: true,
     min_sdk_version: "30",
 }
diff --git a/java/com/android/os/ext/testing/CurrentVersion.java b/java/com/android/os/ext/testing/CurrentVersionTemplate.java
similarity index 73%
rename from java/com/android/os/ext/testing/CurrentVersion.java
rename to java/com/android/os/ext/testing/CurrentVersionTemplate.java
index 0352982..f95341d 100644
--- a/java/com/android/os/ext/testing/CurrentVersion.java
+++ b/java/com/android/os/ext/testing/CurrentVersionTemplate.java
@@ -18,16 +18,16 @@
 import java.util.Set;
 
 /**
- * This class is intended to serve as a single place to define the current SDK extension
- * versions to expect / allow in tests.
+ * This class is intended to serve as a single place to define the current SDK extension versions to
+ * expect / allow in tests.
  */
 public class CurrentVersion {
 
     /**
-     * The latest train's version. Note that the value is inserted by the build
-     * pre-processing the source code.
+     * The latest train's version. Note that the value is inserted by the build pre-processing the
+     * source code.
      */
-    public static final int CURRENT_TRAIN_VERSION = 12;
+    public static final int CURRENT_TRAIN_VERSION = {INSERTED_BY_BUILD};
 
     /** The version R shipped with (0) */
     public static final int R_BASE_VERSION = 0;
@@ -38,23 +38,20 @@
     /** The version T shipped with (3) */
     public static final int T_BASE_VERSION = 3;
 
-    /** The version U shipped with (7) */
-    public static final int U_BASE_VERSION = 7;
-
     /** The current platform's version */
-    public static final int CURRENT_BASE_VERSION = U_BASE_VERSION;
+    public static final int CURRENT_BASE_VERSION = CURRENT_TRAIN_VERSION;
 
     /**
      * The current SDK Extension versions to expect / allow in CTS.
      *
-     * Note: This construct exists because CTS is currently versioned together with the dessert
+     * <p>Note: This construct exists because CTS is currently versioned together with the dessert
      * versions, and not with the module itself. For example, Android R shipped with extension
      * version 0, but it is allowed to preload new mainline trains with a higher extension version.
      * When a new extension version is defined, this Set must therefore be extended to include the
      * new version.
      */
     public static final Set<Integer> ALLOWED_VERSIONS_CTS =
-        CURRENT_BASE_VERSION == CURRENT_TRAIN_VERSION ? Set.of(CURRENT_BASE_VERSION)
-            : Set.of(CURRENT_BASE_VERSION, 8, 9, 10, 11, CURRENT_TRAIN_VERSION);
-
+            CURRENT_BASE_VERSION == CURRENT_TRAIN_VERSION
+                    ? Set.of(CURRENT_BASE_VERSION)
+                    : Set.of(CURRENT_BASE_VERSION, CURRENT_TRAIN_VERSION);
 }
diff --git a/java/com/android/os/ext/testing/DeriveSdk.java b/java/com/android/os/ext/testing/DeriveSdk.java
new file mode 100644
index 0000000..a87a171
--- /dev/null
+++ b/java/com/android/os/ext/testing/DeriveSdk.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 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.os.ext.testing;
+
+public class DeriveSdk {
+
+    static {
+        System.loadLibrary("derive_sdk_jni");
+    }
+
+    private DeriveSdk() {}
+
+    public static String[] dump() {
+        return native_dump().split("\n");
+    }
+
+    private static native String native_dump();
+}
diff --git a/javatests/com/android/os/classpath/ClasspathsTest.java b/javatests/com/android/os/classpath/ClasspathsTest.java
index 4f59add..d4fc05c 100644
--- a/javatests/com/android/os/classpath/ClasspathsTest.java
+++ b/javatests/com/android/os/classpath/ClasspathsTest.java
@@ -20,6 +20,7 @@
 import static android.compat.testing.Classpaths.ClasspathType.DEX2OATBOOTCLASSPATH;
 import static android.compat.testing.Classpaths.ClasspathType.SYSTEMSERVERCLASSPATH;
 import static android.compat.testing.Classpaths.getJarsOnClasspath;
+
 import static com.android.os.classpath.ClasspathsTest.ClasspathSubject.assertThat;
 
 import static com.google.common.base.Preconditions.checkArgument;
@@ -47,9 +48,7 @@
 
 import java.nio.file.Paths;
 
-/**
- * Tests for the contents of *CLASSPATH environ variables on a device.
- */
+/** Tests for the contents of *CLASSPATH environ variables on a device. */
 @RunWith(DeviceJUnit4ClassRunner.class)
 public class ClasspathsTest extends BaseHostJUnit4Test {
 
@@ -82,18 +81,16 @@
         assertThat(jars)
                 .containsAtLeast(LIBART_JAR, FRAMEWORK_JAR, ICU4J_JAR, SDKEXTENSIONS_JAR)
                 .inOrder();
-        assertThat(jars)
-                .doesNotContain(SERVICES_JAR);
+        assertThat(jars).doesNotContain(SERVICES_JAR);
 
-        ImmutableList<String> expectedPrefixes = ImmutableList.of(
-                "/apex/com.android.art/",
-                "/system/",
-                "/system_ext/",
-                "/apex/com.android.i18n/",
-                "/apex/");
-        assertThat(jars)
-                .prefixesMatch(expectedPrefixes)
-                .inOrder();
+        ImmutableList<String> expectedPrefixes =
+                ImmutableList.of(
+                        "/apex/com.android.art/",
+                        "/system/",
+                        "/system_ext/",
+                        "/apex/com.android.i18n/",
+                        "/apex/");
+        assertThat(jars).prefixesMatch(expectedPrefixes).inOrder();
 
         assertThat(getUpdatableApexes(jars)).isInOrder();
     }
@@ -105,21 +102,20 @@
         assertThat(jars).containsNoDuplicates();
 
         // DEX2OATBOOTCLASSPATH must only contain ART, core-icu4j, and platform system jars
-        assertThat(jars)
-                .containsAtLeast(LIBART_JAR, FRAMEWORK_JAR, ICU4J_JAR)
-                .inOrder();
-        assertThat(jars)
-                .containsNoneOf(SDKEXTENSIONS_JAR, SERVICES_JAR);
+        assertThat(jars).containsAtLeast(LIBART_JAR, FRAMEWORK_JAR, ICU4J_JAR).inOrder();
+        assertThat(jars).containsNoneOf(SDKEXTENSIONS_JAR, SERVICES_JAR);
 
         // DEX2OATBOOTCLASSPATH must be a subset of BOOTCLASSPATH
         ImmutableList<String> bootJars = getJarsOnClasspath(getDevice(), BOOTCLASSPATH);
         assertThat(bootJars).containsAtLeastElementsIn(jars);
 
-        ImmutableList<String> expectedPrefixes = ImmutableList.of(
-                "/apex/com.android.art/", "/system/", "/system_ext/", "/apex/com.android.i18n/");
-        assertThat(jars)
-                .prefixesMatch(expectedPrefixes)
-                .inOrder();
+        ImmutableList<String> expectedPrefixes =
+                ImmutableList.of(
+                        "/apex/com.android.art/",
+                        "/system/",
+                        "/system_ext/",
+                        "/apex/com.android.i18n/");
+        assertThat(jars).prefixesMatch(expectedPrefixes).inOrder();
 
         // No updatable jars on DEX2OATBOOTCLASSPATH
         assertThat(getUpdatableApexes(jars)).isEmpty();
@@ -134,11 +130,9 @@
         assertThat(jars).containsNoneOf(LIBART_JAR, FRAMEWORK_JAR, ICU4J_JAR, SDKEXTENSIONS_JAR);
         assertThat(jars).contains(SERVICES_JAR);
 
-        ImmutableList<String> expectedPrefixes = ImmutableList.of(
-                "/system/", "/system_ext/", "/apex/");
-        assertThat(jars)
-                .prefixesMatch(expectedPrefixes)
-                .inOrder();
+        ImmutableList<String> expectedPrefixes =
+                ImmutableList.of("/system/", "/system_ext/", "/apex/");
+        assertThat(jars).prefixesMatch(expectedPrefixes).inOrder();
 
         assertThat(getUpdatableApexes(jars)).isInOrder();
     }
@@ -153,8 +147,7 @@
     }
 
     /**
-     * Returns a derived subject with names of the updatable APEXes preserving the original
-     * order.
+     * Returns a derived subject with names of the updatable APEXes preserving the original order.
      */
     private static ImmutableList<String> getUpdatableApexes(ImmutableList<String> jars) {
         return jars.stream()
@@ -168,10 +161,9 @@
                 .collect(ImmutableList.toImmutableList());
     }
 
-    final static class ClasspathSubject extends IterableSubject {
+    static final class ClasspathSubject extends IterableSubject {
 
-        private static final Ordered EMPTY_ORDERED = () -> {
-        };
+        private static final Ordered EMPTY_ORDERED = () -> {};
 
         private final ImmutableList<String> actual;
 
@@ -189,12 +181,12 @@
          * or fails.
          *
          * <p>To also test that the prefixes appear in the given order, make a call to {@code
-         * inOrder}
-         * on the object returned by this method. The expected elements must appear in the given
-         * order within the actual elements.
+         * inOrder} on the object returned by this method. The expected elements must appear in the
+         * given order within the actual elements.
          */
         public Ordered prefixesMatch(ImmutableList<String> expected) {
-            checkArgument(expected.stream().distinct().count() == expected.size(),
+            checkArgument(
+                    expected.stream().distinct().count() == expected.size(),
                     "No duplicates are allowed in expected values.");
 
             ImmutableList.Builder<String> unexpectedJars = ImmutableList.builder();
@@ -216,8 +208,8 @@
 
             ImmutableList<String> unexpected = unexpectedJars.build();
             if (!unexpected.isEmpty()) {
-                Fact expectedOrder = fact("expected jar filepaths to be prefixes with one of",
-                        expected);
+                Fact expectedOrder =
+                        fact("expected jar filepaths to be prefixes with one of", expected);
                 ImmutableList.Builder<Fact> facts = ImmutableList.builder();
                 for (String e : unexpected) {
                     facts.add(fact("unexpected", e));
@@ -228,22 +220,25 @@
                 return EMPTY_ORDERED;
             }
 
-            return ordered ? EMPTY_ORDERED : () -> failWithActual(
-                    simpleFact("all jars have valid partitions, but the order was wrong"),
-                    fact("expected order", expected)
-            );
+            if (ordered) {
+                return EMPTY_ORDERED;
+            }
+
+            return () ->
+                    failWithActual(
+                            simpleFact("all jars have valid partitions, but the order was wrong"),
+                            fact("expected order", expected));
         }
 
-        /**
-         * Checks that the actual iterable starts with expected elements.
-         */
+        /** Checks that the actual iterable starts with expected elements. */
         public void startsWith(ImmutableList<String> expected) {
             if (actual.size() < expected.size()) {
                 failWithActual("expected at least number of elements", expected.size());
                 return;
             }
             assertWithMessage("Unexpected initial elements of the list")
-                    .that(actual.subList(0, expected.size())).isEqualTo(expected);
+                    .that(actual.subList(0, expected.size()))
+                    .isEqualTo(expected);
         }
 
         private static int findFirstMatchingPrefix(String value, ImmutableList<String> prefixes) {
@@ -254,6 +249,5 @@
             }
             return -1;
         }
-
     }
 }
diff --git a/javatests/com/android/os/classpath/OWNERS b/javatests/com/android/os/classpath/OWNERS
index 5d85f3c..039ef5e 100644
--- a/javatests/com/android/os/classpath/OWNERS
+++ b/javatests/com/android/os/classpath/OWNERS
@@ -1 +1,3 @@
+# Bug component: 1133050
+
 include /derive_classpath/OWNERS
diff --git a/javatests/com/android/os/ext/Android.bp b/javatests/com/android/os/ext/Android.bp
index 27d065c..4750e4d 100644
--- a/javatests/com/android/os/ext/Android.bp
+++ b/javatests/com/android/os/ext/Android.bp
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 package {
+    default_team: "trendy_team_updatable_sdk_apis",
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
@@ -24,7 +25,8 @@
         "ctstestrunner-axt",
         "modules-utils-build",
         "test_util_current_version",
-        "truth-prebuilt",
+        "test_util_derive_sdk",
+        "truth",
     ],
     srcs: ["*.java"],
     test_config: "CtsSdkExtensionsTestCases.xml",
@@ -33,7 +35,11 @@
         "mts-sdkextensions",
         "general-tests",
     ],
-    sdk_version: "system_current",
+    jni_libs: [
+        "libderive_sdk",
+        "libderive_sdk_jni",
+    ],
+    compile_multilib: "both",
     min_sdk_version: "30",
     jarjar_rules: "jarjar-rules.txt",
 }
diff --git a/javatests/com/android/os/ext/SdkExtensionsTest.java b/javatests/com/android/os/ext/SdkExtensionsTest.java
index 0605eb3..1481ba1 100644
--- a/javatests/com/android/os/ext/SdkExtensionsTest.java
+++ b/javatests/com/android/os/ext/SdkExtensionsTest.java
@@ -22,65 +22,74 @@
 import static android.os.Build.VERSION_CODES.TIRAMISU;
 import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
 import static android.os.ext.SdkExtensions.AD_SERVICES;
-import static com.android.os.ext.testing.CurrentVersion.ALLOWED_VERSIONS_CTS;
+
+import static com.android.os.ext.testing.CurrentVersion.CURRENT_TRAIN_VERSION;
 import static com.android.os.ext.testing.CurrentVersion.R_BASE_VERSION;
 import static com.android.os.ext.testing.CurrentVersion.S_BASE_VERSION;
 import static com.android.os.ext.testing.CurrentVersion.T_BASE_VERSION;
-import static com.android.os.ext.testing.CurrentVersion.U_BASE_VERSION;
+
 import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThrows;
 
 import android.app.ActivityManager;
 import android.content.Context;
 import android.content.pm.ModuleInfo;
-import android.content.pm.PackageManager;
 import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
 import android.os.SystemProperties;
 import android.os.ext.SdkExtensions;
+import android.util.Log;
+
 import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
+
 import com.android.modules.utils.build.SdkLevel;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-import org.junit.Rule;
+import com.android.os.ext.testing.DeriveSdk;
+
+import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.HashSet;
+import java.util.Set;
+
 @RunWith(AndroidJUnit4.class)
 public class SdkExtensionsTest {
 
+    private static final String TAG = "SdkExtensionsTest";
+
     private enum Expectation {
         /** Expect an extension to be the current / latest defined version */
-        CTS,
+        CURRENT,
         /** Expect an extension to be missing / version 0 */
         MISSING,
         /** Expect an extension to be at least the base extension version of the device */
         AT_LEAST_BASE,
     }
 
-    private static final Expectation CTS = Expectation.CTS;
+    private static final Expectation CURRENT = Expectation.CURRENT;
     private static final Expectation MISSING = Expectation.MISSING;
     private static final Expectation AT_LEAST_BASE = Expectation.AT_LEAST_BASE;
 
     private static void assertAtLeastBaseVersion(int version) {
         int minVersion = R_BASE_VERSION;
         if (SdkLevel.isAtLeastU()) {
-            minVersion = U_BASE_VERSION;
+            minVersion = CURRENT_TRAIN_VERSION;
         } else if (SdkLevel.isAtLeastT()) {
             minVersion = T_BASE_VERSION;
         } else if (SdkLevel.isAtLeastS()) {
             minVersion = S_BASE_VERSION;
         }
         assertThat(version).isAtLeast(minVersion);
-        assertThat(version).isAtMost(Collections.max(ALLOWED_VERSIONS_CTS));
+        assertThat(version).isAtMost(CURRENT_TRAIN_VERSION);
     }
 
     private static void assertVersion(Expectation expectation, int version) {
         switch (expectation) {
-            case CTS:
-                assertThat(version).isIn(ALLOWED_VERSIONS_CTS);
+            case CURRENT:
+                assertEquals(CURRENT_TRAIN_VERSION, version);
                 break;
             case AT_LEAST_BASE:
                 assertAtLeastBaseVersion(version);
@@ -110,6 +119,16 @@
         }
     }
 
+    /* This method runs the copy of the dump code that is bundled inside the test APK. */
+    @BeforeClass
+    public static void runTestDeriveSdkDump() {
+        Log.i(TAG, "derive_sdk dump (bundled with test):");
+
+        for (String line : DeriveSdk.dump()) {
+            Log.i(TAG, "  " + line);
+        }
+    }
+
     /** Verify that getExtensionVersion only accepts valid extension SDKs */
     @Test
     public void testBadArgument() throws Exception {
@@ -118,7 +137,8 @@
         int step = (int) ((VERSION_CODES.R - (long) Integer.MIN_VALUE) / 10_000);
         for (int sdk = Integer.MIN_VALUE; sdk < VERSION_CODES.R; sdk += step) {
             final int finalSdk = sdk;
-            assertThrows(IllegalArgumentException.class,
+            assertThrows(
+                    IllegalArgumentException.class,
                     () -> SdkExtensions.getExtensionVersion(finalSdk));
         }
     }
@@ -126,12 +146,7 @@
     /** Verifies that getExtensionVersion returns zero value for non-existing extensions */
     @Test
     public void testZeroValues() throws Exception {
-        Set<Integer> assignedCodes = Set.of(
-            R,
-            S,
-            TIRAMISU,
-            UPSIDE_DOWN_CAKE,
-            AD_SERVICES);
+        Set<Integer> assignedCodes = Set.of(R, S, TIRAMISU, UPSIDE_DOWN_CAKE, AD_SERVICES);
         for (int sdk = VERSION_CODES.R; sdk <= 1_000_000; sdk++) {
             if (assignedCodes.contains(sdk)) {
                 continue;
@@ -167,13 +182,13 @@
     }
 
     @Test
-    public void testExtensionS() throws Exception  {
+    public void testExtensionS() throws Exception {
         Expectation expectation = dessertExpectation(SdkLevel.isAtLeastS());
         assertVersion(expectation, S, "s");
     }
 
     @Test
-    public void testExtensionT() throws Exception  {
+    public void testExtensionT() throws Exception {
         Expectation expectation = dessertExpectation(SdkLevel.isAtLeastT());
         assertVersion(expectation, TIRAMISU, "t");
     }
@@ -200,7 +215,7 @@
         // Go trains don't include all modules, so even when all trains for a particular release
         // have been installed correctly on a Go device, we can't generally expect the extension
         // version to be the current train version.
-        return SdkLevel.isAtLeastT() && isGoWithSideloadedModules() ? AT_LEAST_BASE : CTS;
+        return SdkLevel.isAtLeastT() && isGoWithSideloadedModules() ? AT_LEAST_BASE : CURRENT;
     }
 
     private boolean isGoWithSideloadedModules() throws Exception {
@@ -229,7 +244,5 @@
         flags |= PackageManager.MATCH_FACTORY_ONLY;
         PackageInfo factoryInfo = packageManager.getPackageInfo(packageName, flags);
         return !factoryInfo.applicationInfo.sourceDir.equals(currentInfo.applicationInfo.sourceDir);
-
     }
-
 }
diff --git a/javatests/com/android/sdkext/extensions/SdkExtensionsHostTest.java b/javatests/com/android/sdkext/extensions/SdkExtensionsHostTest.java
index b0b86a0..1b1a4e1 100644
--- a/javatests/com/android/sdkext/extensions/SdkExtensionsHostTest.java
+++ b/javatests/com/android/sdkext/extensions/SdkExtensionsHostTest.java
@@ -41,7 +41,6 @@
 import org.junit.runner.RunWith;
 
 import java.io.File;
-import java.lang.NumberFormatException;
 import java.time.Duration;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -71,8 +70,7 @@
     private Boolean mIsAtLeastT = null;
     private Boolean mIsAtLeastU = null;
 
-    @Rule
-    public AbandonSessionsRule mHostTestRule = new AbandonSessionsRule(this);
+    @Rule public AbandonSessionsRule mHostTestRule = new AbandonSessionsRule(this);
 
     @Before
     public void setUp() throws Exception {
@@ -100,7 +98,7 @@
     }
 
     @Test
-    public void upgradeOneApexWithBump()  throws Exception {
+    public void upgradeOneApexWithBump() throws Exception {
         assertVersionDefault();
         mInstallUtils.installApexes(SDKEXTENSIONS_FILENAME);
         reboot();
@@ -180,10 +178,14 @@
     }
 
     private void assertVersionDefault() throws Exception {
-        int expected = isAtLeastU() ? CurrentVersion.CURRENT_TRAIN_VERSION
-            : isAtLeastT() ? CurrentVersion.T_BASE_VERSION
-            : isAtLeastS() ? CurrentVersion.S_BASE_VERSION
-            : CurrentVersion.R_BASE_VERSION;
+        int expected =
+                isAtLeastU()
+                        ? CurrentVersion.CURRENT_TRAIN_VERSION
+                        : isAtLeastT()
+                                ? CurrentVersion.T_BASE_VERSION
+                                : isAtLeastS()
+                                        ? CurrentVersion.S_BASE_VERSION
+                                        : CurrentVersion.R_BASE_VERSION;
         assertRVersionEquals(expected);
         assertSVersionEquals(expected);
         assertTVersionEquals(expected);
@@ -213,25 +215,29 @@
     }
 
     private void assertRVersionEquals(int version) throws Exception {
-        String[] apps = version >= 45 ? new String[]{"r12", "r45"} :
-            version >= 12 ? new String[]{"r12"} : new String[]{};
+        String[] apps =
+                version >= 45
+                        ? new String[] {"r12", "r45"}
+                        : version >= 12 ? new String[] {"r12"} : new String[] {};
         assertExtensionVersionEquals("r", version, apps, true);
     }
 
     private void assertSVersionEquals(int version) throws Exception {
         // These APKs require the same R version as they do S version.
         int minVersion = Math.min(version, broadcastForInt("GET_SDK_VERSION", "r"));
-        String[] apps = minVersion >= 45 ? new String[]{"s12", "s45"}
-                : minVersion >= 12 ? new String[]{"s12"} : new String[]{};
+        String[] apps =
+                minVersion >= 45
+                        ? new String[] {"s12", "s45"}
+                        : minVersion >= 12 ? new String[] {"s12"} : new String[] {};
         assertExtensionVersionEquals("s", version, apps, isAtLeastS());
     }
 
     private void assertTVersionEquals(int version) throws Exception {
-        assertExtensionVersionEquals("t", version, new String[]{}, isAtLeastT());
+        assertExtensionVersionEquals("t", version, new String[] {}, isAtLeastT());
     }
 
-    private void assertExtensionVersionEquals(String extension, int version, String[] apps,
-            boolean expected) throws Exception {
+    private void assertExtensionVersionEquals(
+            String extension, int version, String[] apps, boolean expected) throws Exception {
         int appValue = broadcastForInt("GET_SDK_VERSION", extension);
         String syspropValue = getExtensionVersionFromSysprop(extension);
         if (expected) {
diff --git a/javatests/com/android/sdkext/extensions/apps/Receiver.java b/javatests/com/android/sdkext/extensions/apps/Receiver.java
index 677c424..c662b0c 100644
--- a/javatests/com/android/sdkext/extensions/apps/Receiver.java
+++ b/javatests/com/android/sdkext/extensions/apps/Receiver.java
@@ -24,8 +24,6 @@
 import android.os.ext.testing.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 =
@@ -37,9 +35,12 @@
 
     private static int dessertLetterToInt(char letter) {
         switch (letter) {
-            case 'r': return Build.VERSION_CODES.R;
-            case 's': return Build.VERSION_CODES.S;
-            case 't': return Build.VERSION_CODES.TIRAMISU;
+            case 'r':
+                return Build.VERSION_CODES.R;
+            case 's':
+                return Build.VERSION_CODES.S;
+            case 't':
+                return Build.VERSION_CODES.TIRAMISU;
         }
         throw new IllegalArgumentException(String.valueOf(letter));
     }
@@ -69,13 +70,13 @@
     }
 
     private static void makeCallsDefault() {
-        expectException(NoClassDefFoundError.class, "test class", () -> new Test() );
-        expectException(NoClassDefFoundError.class, "test class", () -> Test.staticPublicMethod() );
-        expectException(NoClassDefFoundError.class, "test class",
-            () -> Test.staticSystemApiMethod() );
-        expectException(NoClassDefFoundError.class, "test class",
-            () -> Test.staticModuleLibsApiMethod() );
-        expectException(NoClassDefFoundError.class, "test class", () -> Test.staticHiddenMethod() );
+        expectException(NoClassDefFoundError.class, "test class", () -> new Test());
+        expectException(NoClassDefFoundError.class, "test class", () -> Test.staticPublicMethod());
+        expectException(
+                NoClassDefFoundError.class, "test class", () -> Test.staticSystemApiMethod());
+        expectException(
+                NoClassDefFoundError.class, "test class", () -> Test.staticModuleLibsApiMethod());
+        expectException(NoClassDefFoundError.class, "test class", () -> Test.staticHiddenMethod());
     }
 
     private static void makeCallsVersion45() {
@@ -89,12 +90,14 @@
 
         Test.staticPublicMethod();
         Test.staticSystemApiMethod();
-        expectException(NoSuchMethodError.class, "static module-libs method",
-            () -> Test.staticModuleLibsApiMethod());
-        expectException(NoSuchMethodError.class, "static testapi method",
-            () -> Test.staticTestApiMethod());
-        expectException(NoSuchMethodError.class, "static hidden method",
-            () -> Test.staticHiddenMethod());
+        expectException(
+                NoSuchMethodError.class,
+                "static module-libs method",
+                () -> Test.staticModuleLibsApiMethod());
+        expectException(
+                NoSuchMethodError.class, "static testapi method", () -> Test.staticTestApiMethod());
+        expectException(
+                NoSuchMethodError.class, "static hidden method", () -> Test.staticHiddenMethod());
     }
 
     private static void expectException(Class exceptionClass, String type, Runnable runnable) {
diff --git a/keep-flagged-apis.sh b/keep-flagged-apis.sh
new file mode 100755
index 0000000..efc336c
--- /dev/null
+++ b/keep-flagged-apis.sh
@@ -0,0 +1,33 @@
+#!/bin/bash -e
+#
+# Copyright 2023 Google Inc. All rights reserved.
+#
+# 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.
+
+# Convert a list of flags in the input file to a list of metalava options
+# that will keep the APIs for those flags will hiding all other flagged
+# APIs.
+
+FLAGS="$1"
+
+FLAGGED="android.annotation.FlaggedApi"
+
+# Convert the list of feature flags in the input file to Metalava options
+# of the form `--revert-annotation !android.annotation.FlaggedApi("<flag>")`
+# to prevent the annotated APIs from being hidden, i.e. include the annotated
+# APIs in the SDK snapshots. This also preserves the line comments, they will
+# be ignored by Metalava but might be useful when debugging.
+sed "s|^[^#].*$|--revert-annotation '!$FLAGGED(\"\\0\")'|" $FLAGS
+
+# Revert all flagged APIs, unless listed above.
+echo "--revert-annotation $FLAGGED"
diff --git a/released-flagged-apis.txt b/released-flagged-apis.txt
new file mode 100644
index 0000000..8bb3356
--- /dev/null
+++ b/released-flagged-apis.txt
@@ -0,0 +1,44 @@
+# This file contains a list of feature flags whose corresponding API should be
+# included in the next SDK extension. This allows APIs to be released in an SDK
+# extension without removing @FlaggeApi annotations from the source which means
+# those APIs will be hidden from the next QPR release.
+#
+# These flags are converted into command line options that are passed to
+# Metalava when generating API signature files and stubs for SDK snapshots for
+# previous dessert releases, e.g. R, S, Tiramisu, etc. and also for the next
+# SDK extension (and eventually Android SDK) release.
+#
+# Flags must only be added to this once they are suitable for release and only
+# immediately before the next SDK extension is to be created. Adding them here
+# before a normal train release will cause additional APIs to be included in
+# that train release.
+#
+# Flags must only be removed from this once the corresponding @FlaggedApi
+# annotations are removed from the source code which can only be done once those
+# features have been approved for general release.
+#
+# Once an SDK extension has been created it will not be possible to use any of
+# the flags in this file in any new @FlaggedApi annotations as otherwise that
+# would cause those APIs to be included in SDK snapshots used by the next train
+# release.
+#
+# The file should be structured in sections with flags in alphabetical order
+# within each section. Each section should be separated from the preceding one
+# by a comment indicating which SDK extension the section relates to.
+
+# Released in SDK Extension #10 - M-2023-11
+# Tethering
+com.android.net.flags.register_nsd_offload_engine_api
+
+# Released in SDK Extension #11 - M-2024-02
+# Adservices
+ad_id_cache_enabled
+adservices_enablement_check_enabled
+adservices_outcomereceiver_r_api_enabled
+enable_adext_data_service_apis
+enable_adservices_api_enabled
+topics_encryption_enabled
+
+# Tethering
+com.android.net.flags.basic_background_restrictions_enabled
+com.android.net.flags.support_is_uid_networking_blocked