[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