| /* |
| * Copyright (C) 2021 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "derive_classpath.h" |
| |
| #include <android-base/file.h> |
| #include <android-base/logging.h> |
| #include <android-base/properties.h> |
| #include <android-base/stringprintf.h> |
| #include <android-base/strings.h> |
| #include <gtest/gtest.h> |
| #include <stdlib.h> |
| #include <sys/mman.h> |
| #include <sys/stat.h> |
| |
| #include <cstdlib> |
| #include <string_view> |
| |
| #include "android-base/unique_fd.h" |
| #include "packages/modules/common/proto/classpaths.pb.h" |
| |
| #ifdef SDKEXT_ANDROID |
| #include <android-modules-utils/sdk_level.h> |
| #include <android/api-level.h> |
| #else |
| |
| #define __ANDROID_API_R__ 30 |
| #define __NR_memfd_create 319 |
| |
| int memfd_create(const char* name, unsigned int flags) { |
| return syscall(__NR_memfd_create, name, flags); |
| } |
| #endif |
| |
| namespace android { |
| namespace derive_classpath { |
| namespace { |
| |
| static const std::string kFrameworkJarFilepath = "/system/framework/framework.jar"; |
| static const std::string kLibartJarFilepath = "/apex/com.android.art/javalib/core-libart.jar"; |
| static const std::string kSdkExtensionsJarFilepath = |
| "/apex/com.android.sdkext/javalib/framework-sdkextensions.jar"; |
| static const std::string kServicesJarFilepath = "/system/framework/services.jar"; |
| |
| // The fixture for testing derive_classpath. |
| class DeriveClasspathTest : public ::testing::Test { |
| protected: |
| const std::string working_dir() { return std::string(temp_dir_.path); } |
| |
| const std::string GetOutputPath() { return working_dir() + "/classpath"; } |
| |
| // Parses the generated classpath exports file and returns each line individually. |
| std::vector<std::string> ParseExportsFile(const char* file = nullptr) { |
| if (file == nullptr) { |
| file = output_path_.c_str(); |
| } |
| std::string contents; |
| EXPECT_TRUE(android::base::ReadFileToString(file, &contents, |
| /*follow_symlinks=*/true)); |
| return android::base::Split(contents, "\n"); |
| } |
| |
| std::vector<std::string> SplitClasspathExportLine(const std::string& line) { |
| std::vector<std::string> contents = android::base::Split(line, " "); |
| // Export lines are expected to be structured as `export <name> <value>`. |
| EXPECT_EQ(3, contents.size()); |
| EXPECT_EQ("export", contents[0]); |
| return contents; |
| } |
| |
| // Checks the order of the jars in a given classpath. |
| // Instead of doing a full order check, it assumes the jars are grouped by partition and checks |
| // that partitions come in order of the `prefixes` that is given. |
| void CheckClasspathGroupOrder(const std::string classpath, |
| const std::vector<std::string> prefixes) { |
| ASSERT_NE(0, prefixes.size()); |
| ASSERT_NE(0, classpath.size()); |
| |
| auto jars = android::base::Split(classpath, ":"); |
| |
| auto prefix = prefixes.begin(); |
| auto jar = jars.begin(); |
| for (; jar != jars.end() && prefix != prefixes.end(); ++jar) { |
| if (*jar == "/apex/com.android.i18n/javalib/core-icu4j.jar") { |
| // core-icu4j.jar is special and is out of order in BOOTCLASSPATH; |
| // ignore it when checking for general order |
| continue; |
| } |
| if (!android::base::StartsWith(*jar, *prefix)) { |
| ++prefix; |
| } |
| } |
| EXPECT_NE(prefix, prefixes.end()); |
| // All jars have been iterated over, thus they all have valid prefixes |
| EXPECT_EQ(jar, jars.end()); |
| } |
| |
| void WriteConfig(const ExportedClasspathsJars& exported_jars, const std::string& path) { |
| std::string fragment_path = working_dir() + path; |
| std::string buf; |
| exported_jars.SerializeToString(&buf); |
| std::string cmd("mkdir -p " + android::base::Dirname(fragment_path)); |
| ASSERT_EQ(0, system(cmd.c_str())); |
| ASSERT_TRUE(android::base::WriteStringToFile(buf, fragment_path, true)); |
| } |
| |
| void AddJarToClasspath(const std::string& partition, const std::string& jar_filepath, |
| Classpath classpath) { |
| ExportedClasspathsJars exported_jars; |
| Jar* jar = exported_jars.add_jars(); |
| jar->set_path(jar_filepath); |
| jar->set_classpath(classpath); |
| |
| std::string basename = Classpath_Name(classpath) + ".pb"; |
| std::transform(basename.begin(), basename.end(), basename.begin(), |
| [](unsigned char c) { return std::tolower(c); }); |
| |
| WriteConfig(exported_jars, partition + "/etc/classpaths/" + basename); |
| } |
| |
| const TemporaryDir temp_dir_; |
| const std::string output_path_ = working_dir() + "/classpath"; |
| |
| #ifdef SDKEXT_ANDROID |
| const Args default_args_with_test_dir_ = { |
| .output_path = output_path_, |
| .glob_pattern_prefix = temp_dir_.path, |
| }; |
| #else |
| const Args default_args_with_test_dir_ = { |
| .output_path = output_path_, |
| .override_device_sdk_version = 35, |
| .override_device_codename = "REL", |
| .glob_pattern_prefix = temp_dir_.path, |
| }; |
| #endif |
| }; |
| |
| using DeriveClasspathDeathTest = DeriveClasspathTest; |
| |
| // Check only known *CLASSPATH variables are exported. |
| TEST_F(DeriveClasspathTest, DefaultNoUnknownClasspaths) { |
| // Re-generate default on device classpaths |
| GenerateClasspathExports(default_args_with_test_dir_); |
| |
| const std::vector<std::string> exportLines = ParseExportsFile(); |
| // The first four lines are tested below. |
| for (int i = 4; i < exportLines.size(); i++) { |
| EXPECT_EQ(exportLines[i], ""); |
| } |
| } |
| |
| // Test that all variables are properly generated. |
| TEST_F(DeriveClasspathTest, AllVariables) { |
| ExportedClasspathsJars exported_jars; |
| Jar* jar = exported_jars.add_jars(); |
| jar->set_path("/apex/com.android.foo/javalib/foo"); |
| jar->set_classpath(BOOTCLASSPATH); |
| jar = exported_jars.add_jars(); |
| jar->set_path("/apex/com.android.bar/javalib/bar"); |
| jar->set_classpath(DEX2OATBOOTCLASSPATH); |
| WriteConfig(exported_jars, "/system/etc/classpaths/bootclasspath.pb"); |
| |
| exported_jars.clear_jars(); |
| jar = exported_jars.add_jars(); |
| jar->set_path("/apex/com.android.baz/javalib/baz"); |
| jar->set_classpath(SYSTEMSERVERCLASSPATH); |
| jar = exported_jars.add_jars(); |
| jar->set_path("/apex/com.android.qux/javalib/qux"); |
| jar->set_classpath(STANDALONE_SYSTEMSERVER_JARS); |
| WriteConfig(exported_jars, "/system/etc/classpaths/systemserverclasspath.pb"); |
| |
| ASSERT_TRUE(GenerateClasspathExports(default_args_with_test_dir_)); |
| |
| const std::vector<std::string> exportLines = ParseExportsFile(); |
| std::vector<std::string> splitExportLine; |
| |
| splitExportLine = SplitClasspathExportLine(exportLines[0]); |
| EXPECT_EQ("BOOTCLASSPATH", splitExportLine[1]); |
| EXPECT_EQ("/apex/com.android.foo/javalib/foo", splitExportLine[2]); |
| splitExportLine = SplitClasspathExportLine(exportLines[1]); |
| EXPECT_EQ("DEX2OATBOOTCLASSPATH", splitExportLine[1]); |
| EXPECT_EQ("/apex/com.android.bar/javalib/bar", splitExportLine[2]); |
| splitExportLine = SplitClasspathExportLine(exportLines[2]); |
| EXPECT_EQ("SYSTEMSERVERCLASSPATH", splitExportLine[1]); |
| EXPECT_EQ("/apex/com.android.baz/javalib/baz", splitExportLine[2]); |
| splitExportLine = SplitClasspathExportLine(exportLines[3]); |
| EXPECT_EQ("STANDALONE_SYSTEMSERVER_JARS", splitExportLine[1]); |
| EXPECT_EQ("/apex/com.android.qux/javalib/qux", splitExportLine[2]); |
| } |
| |
| // Test that temp directory does not pick up actual jars. |
| TEST_F(DeriveClasspathTest, TempConfig) { |
| AddJarToClasspath("/apex/com.android.foo", "/apex/com.android.foo/javalib/foo", BOOTCLASSPATH); |
| AddJarToClasspath("/apex/com.android.baz", "/apex/com.android.baz/javalib/baz", |
| SYSTEMSERVERCLASSPATH); |
| |
| ASSERT_TRUE(GenerateClasspathExports(default_args_with_test_dir_)); |
| |
| const std::vector<std::string> exportLines = ParseExportsFile(); |
| |
| std::vector<std::string> splitExportLine; |
| |
| splitExportLine = SplitClasspathExportLine(exportLines[0]); |
| EXPECT_EQ("BOOTCLASSPATH", splitExportLine[1]); |
| EXPECT_EQ("/apex/com.android.foo/javalib/foo", splitExportLine[2]); |
| splitExportLine = SplitClasspathExportLine(exportLines[2]); |
| EXPECT_EQ("SYSTEMSERVERCLASSPATH", splitExportLine[1]); |
| EXPECT_EQ("/apex/com.android.baz/javalib/baz", splitExportLine[2]); |
| } |
| |
| // Test individual modules are sorted by pathnames. |
| TEST_F(DeriveClasspathTest, ModulesAreSorted) { |
| AddJarToClasspath("/apex/com.android.art", "/apex/com.android.art/javalib/art", BOOTCLASSPATH); |
| AddJarToClasspath("/system", "/system/framework/jar", BOOTCLASSPATH); |
| AddJarToClasspath("/apex/com.android.foo", "/apex/com.android.foo/javalib/foo", BOOTCLASSPATH); |
| AddJarToClasspath("/apex/com.android.bar", "/apex/com.android.bar/javalib/bar", BOOTCLASSPATH); |
| AddJarToClasspath("/apex/com.android.baz", "/apex/com.android.baz/javalib/baz", BOOTCLASSPATH); |
| |
| ASSERT_TRUE(GenerateClasspathExports(default_args_with_test_dir_)); |
| |
| const std::vector<std::string> exportLines = ParseExportsFile(); |
| const std::vector<std::string> splitExportLine = SplitClasspathExportLine(exportLines[0]); |
| const std::string exportValue = splitExportLine[2]; |
| |
| const std::string expectedJars( |
| "/apex/com.android.art/javalib/art" |
| ":/system/framework/jar" |
| ":/apex/com.android.bar/javalib/bar" |
| ":/apex/com.android.baz/javalib/baz" |
| ":/apex/com.android.foo/javalib/foo"); |
| |
| EXPECT_EQ(expectedJars, exportValue); |
| } |
| |
| // Test we can output to custom files. |
| TEST_F(DeriveClasspathTest, CustomOutputLocation) { |
| AddJarToClasspath("/apex/com.android.art", "/apex/com.android.art/javalib/art", BOOTCLASSPATH); |
| AddJarToClasspath("/system", "/system/framework/jar", BOOTCLASSPATH); |
| AddJarToClasspath("/apex/com.android.foo", "/apex/com.android.foo/javalib/foo", BOOTCLASSPATH); |
| AddJarToClasspath("/apex/com.android.bar", "/apex/com.android.bar/javalib/bar", BOOTCLASSPATH); |
| AddJarToClasspath("/apex/com.android.baz", "/apex/com.android.baz/javalib/baz", BOOTCLASSPATH); |
| |
| android::base::unique_fd fd(memfd_create("temp_file", MFD_CLOEXEC)); |
| ASSERT_TRUE(fd.ok()) << "Unable to open temp-file"; |
| const std::string file_name = android::base::StringPrintf("/proc/self/fd/%d", fd.get()); |
| Args args = { |
| .output_path = file_name, |
| .glob_pattern_prefix = working_dir(), |
| }; |
| ASSERT_TRUE(GenerateClasspathExports(args)); |
| |
| const std::vector<std::string> exportLines = ParseExportsFile(file_name.c_str()); |
| const std::vector<std::string> splitExportLine = SplitClasspathExportLine(exportLines[0]); |
| const std::string exportValue = splitExportLine[2]; |
| |
| const std::string expectedJars( |
| "/apex/com.android.art/javalib/art" |
| ":/system/framework/jar" |
| ":/apex/com.android.bar/javalib/bar" |
| ":/apex/com.android.baz/javalib/baz" |
| ":/apex/com.android.foo/javalib/foo"); |
| |
| EXPECT_EQ(expectedJars, exportValue); |
| } |
| |
| // Test alternative .pb for bootclasspath and systemclasspath. |
| TEST_F(DeriveClasspathTest, CustomInputLocation) { |
| AddJarToClasspath("/other", "/other/bcp-jar", BOOTCLASSPATH); |
| AddJarToClasspath("/other", "/other/systemserver-jar", SYSTEMSERVERCLASSPATH); |
| AddJarToClasspath("/apex/com.android.art", "/apex/com.android.art/javalib/art", BOOTCLASSPATH); |
| AddJarToClasspath("/apex/com.android.foo", "/apex/com.android.foo/javalib/foo", BOOTCLASSPATH); |
| AddJarToClasspath("/apex/com.android.baz", "/apex/com.android.baz/javalib/baz", |
| SYSTEMSERVERCLASSPATH); |
| |
| Args args = default_args_with_test_dir_; |
| args.system_bootclasspath_fragment = "/other/etc/classpaths/bootclasspath.pb"; |
| args.system_systemserverclasspath_fragment = "/other/etc/classpaths/systemserverclasspath.pb"; |
| |
| ASSERT_TRUE(GenerateClasspathExports(args)); |
| |
| const std::vector<std::string> exportLines = ParseExportsFile(); |
| |
| std::vector<std::string> splitExportLine; |
| splitExportLine = SplitClasspathExportLine(exportLines[0]); |
| EXPECT_EQ("BOOTCLASSPATH", splitExportLine[1]); |
| const std::string expectedBcpJars( |
| "/apex/com.android.art/javalib/art" |
| ":/other/bcp-jar" |
| ":/apex/com.android.foo/javalib/foo"); |
| EXPECT_EQ(expectedBcpJars, splitExportLine[2]); |
| |
| splitExportLine = SplitClasspathExportLine(exportLines[2]); |
| EXPECT_EQ("SYSTEMSERVERCLASSPATH", splitExportLine[1]); |
| const std::string expectedSystemServerJars( |
| "/other/systemserver-jar" |
| ":/apex/com.android.baz/javalib/baz"); |
| EXPECT_EQ(expectedSystemServerJars, splitExportLine[2]); |
| } |
| |
| // Test output location that can't be written to. |
| TEST_F(DeriveClasspathTest, NonWriteableOutputLocation) { |
| AddJarToClasspath("/apex/com.android.art", "/apex/com.android.art/javalib/art", BOOTCLASSPATH); |
| AddJarToClasspath("/system", "/system/framework/jar", BOOTCLASSPATH); |
| |
| Args args = { |
| .output_path = "/system/non_writable_path", |
| .glob_pattern_prefix = working_dir(), |
| }; |
| ASSERT_FALSE(GenerateClasspathExports(args)); |
| } |
| |
| TEST_F(DeriveClasspathTest, ScanOnlySpecificDirectories) { |
| AddJarToClasspath("/system", "/system/framework/jar", BOOTCLASSPATH); |
| AddJarToClasspath("/apex/com.android.foo", "/apex/com.android.foo/javalib/foo", BOOTCLASSPATH); |
| AddJarToClasspath("/apex/com.android.foo", "/apex/com.android.foo/javalib/sys", |
| SYSTEMSERVERCLASSPATH); |
| AddJarToClasspath("/apex/com.android.bar", "/apex/com.android.bar/javalib/bar", BOOTCLASSPATH); |
| AddJarToClasspath("/apex/com.android.baz", "/apex/com.android.baz/javalib/baz", BOOTCLASSPATH); |
| |
| auto args_with_scan_dirs = default_args_with_test_dir_; |
| args_with_scan_dirs.scan_dirs.push_back("/apex/com.android.foo"); |
| args_with_scan_dirs.scan_dirs.push_back("/apex/com.android.bar"); |
| ASSERT_TRUE(GenerateClasspathExports(args_with_scan_dirs)); |
| |
| const std::vector<std::string> exportLines = ParseExportsFile(); |
| |
| std::vector<std::string> splitExportLine; |
| |
| splitExportLine = SplitClasspathExportLine(exportLines[0]); |
| EXPECT_EQ("BOOTCLASSPATH", splitExportLine[1]); |
| // Not sorted. Maintains the ordering provided in scan_dirs |
| const std::string expectedJars( |
| "/apex/com.android.foo/javalib/foo" |
| ":/apex/com.android.bar/javalib/bar"); |
| EXPECT_EQ(expectedJars, splitExportLine[2]); |
| splitExportLine = SplitClasspathExportLine(exportLines[2]); |
| EXPECT_EQ("SYSTEMSERVERCLASSPATH", splitExportLine[1]); |
| EXPECT_EQ("/apex/com.android.foo/javalib/sys", splitExportLine[2]); |
| } |
| |
| // Test apexes only export their own jars. |
| TEST_F(DeriveClasspathDeathTest, ApexJarsBelongToApex) { |
| // EXPECT_DEATH expects error messages in stderr, log there |
| android::base::SetLogger(android::base::StderrLogger); |
| |
| AddJarToClasspath("/system", "/system/framework/jar", BOOTCLASSPATH); |
| ASSERT_TRUE(GenerateClasspathExports(default_args_with_test_dir_)); |
| |
| AddJarToClasspath("/apex/com.android.foo", "/apex/com.android.foo/javalib/foo", BOOTCLASSPATH); |
| ASSERT_TRUE(GenerateClasspathExports(default_args_with_test_dir_)); |
| |
| AddJarToClasspath("/apex/[email protected]", "/apex/com.android.bar/javalib/bar", |
| BOOTCLASSPATH); |
| ASSERT_TRUE(GenerateClasspathExports(default_args_with_test_dir_)); |
| |
| AddJarToClasspath("/apex/com.android.baz@12345", "/apex/this/path/is/skipped", BOOTCLASSPATH); |
| ASSERT_TRUE(GenerateClasspathExports(default_args_with_test_dir_)); |
| |
| AddJarToClasspath("/apex/com.android.bar", "/apex/wrong/path/bar", BOOTCLASSPATH); |
| EXPECT_DEATH(GenerateClasspathExports(default_args_with_test_dir_), |
| "must not export a jar.*wrong/path/bar"); |
| } |
| |
| // Test only bind mounted apexes are skipped |
| TEST_F(DeriveClasspathTest, OnlyBindMountedApexIsSkipped) { |
| AddJarToClasspath("/system", "/system/framework/jar", BOOTCLASSPATH); |
| // Normal APEX with format: /apex/<module-name>/* |
| AddJarToClasspath("/apex/com.android.foo", "/apex/com.android.foo/javalib/foo", BOOTCLASSPATH); |
| // Bind mounted APEX with format: /apex/<module-name>@<version>/* |
| AddJarToClasspath("/apex/com.android.bar@123", "/apex/com.android.bar/javalib/bar", |
| BOOTCLASSPATH); |
| // Temp mounted APEX with format: /apex/<module-name>@<version>.tmp/* |
| AddJarToClasspath("/apex/[email protected]", "/apex/com.android.baz/javalib/baz", |
| BOOTCLASSPATH); |
| |
| ASSERT_TRUE(GenerateClasspathExports(default_args_with_test_dir_)); |
| |
| const std::vector<std::string> exportLines = ParseExportsFile(); |
| |
| std::vector<std::string> splitExportLine; |
| |
| splitExportLine = SplitClasspathExportLine(exportLines[0]); |
| EXPECT_EQ("BOOTCLASSPATH", splitExportLine[1]); |
| const std::string expectedJars( |
| "/system/framework/jar" |
| ":/apex/com.android.baz/javalib/baz" |
| ":/apex/com.android.foo/javalib/foo"); |
| EXPECT_EQ(expectedJars, splitExportLine[2]); |
| } |
| |
| // Test classpath fragments export jars for themselves. |
| TEST_F(DeriveClasspathDeathTest, WrongClasspathInFragments) { |
| // Valid configs |
| AddJarToClasspath("/system", "/system/framework/framework-jar", BOOTCLASSPATH); |
| AddJarToClasspath("/system", "/system/framework/service-jar", SYSTEMSERVERCLASSPATH); |
| |
| // Manually create an invalid config with both BCP and SSCP jars... |
| ExportedClasspathsJars exported_jars; |
| Jar* jar = exported_jars.add_jars(); |
| jar->set_path("/apex/com.android.foo/javalib/foo"); |
| jar->set_classpath(BOOTCLASSPATH); |
| // note that DEX2OATBOOTCLASSPATH and BOOTCLASSPATH jars are expected to be in the same config |
| jar = exported_jars.add_jars(); |
| jar->set_path("/apex/com.android.foo/javalib/foo"); |
| jar->set_classpath(DEX2OATBOOTCLASSPATH); |
| jar = exported_jars.add_jars(); |
| jar->set_path("/apex/com.android.foo/javalib/service-foo"); |
| jar->set_classpath(SYSTEMSERVERCLASSPATH); |
| |
| // ...and write this config to bootclasspath.pb |
| WriteConfig(exported_jars, "/apex/com.android.foo/etc/classpaths/bootclasspath.pb"); |
| |
| EXPECT_DEATH(GenerateClasspathExports(default_args_with_test_dir_), |
| "must not export a jar for SYSTEMSERVERCLASSPATH"); |
| } |
| |
| TEST_F(DeriveClasspathDeathTest, CurrentSdkVersion) { |
| #ifndef SDKEXT_ANDROID |
| GTEST_SKIP(); |
| #else |
| if (android_get_device_api_level() < __ANDROID_API_S__) { |
| GTEST_SKIP(); |
| } |
| |
| ExportedClasspathsJars exported_jars; |
| Jar* jar = exported_jars.add_jars(); |
| jar->set_path("/apex/com.android.foo/javalib/minsdkcurrent"); |
| jar->set_min_sdk_version("current"); |
| jar->set_classpath(SYSTEMSERVERCLASSPATH); |
| WriteConfig(exported_jars, "/apex/com.android.foo/etc/classpaths/systemserverclasspath.pb"); |
| |
| EXPECT_DEATH(GenerateClasspathExports(default_args_with_test_dir_), "no conversion"); |
| #endif |
| } |
| |
| // Test jars with different sdk versions. |
| TEST_F(DeriveClasspathTest, SdkVersionsAreRespected) { |
| #ifndef SDKEXT_ANDROID |
| GTEST_SKIP(); |
| #else |
| if (android_get_device_api_level() < __ANDROID_API_S__) { |
| GTEST_SKIP(); |
| } |
| |
| // List of jars expected to be in SYSTEMSERVERCLASSPATH |
| std::vector<std::string> expected_jars; |
| |
| // Add an unbounded jar |
| AddJarToClasspath("/system", "/system/framework/unbounded", SYSTEMSERVERCLASSPATH); |
| expected_jars.push_back("/system/framework/unbounded"); |
| |
| // Manually create a config with jars that set sdk versions... |
| ExportedClasspathsJars exported_jars; |
| |
| // known released versions: |
| Jar* jar = exported_jars.add_jars(); |
| jar->set_path("/apex/com.android.foo/javalib/minsdk30"); |
| jar->set_min_sdk_version(std::to_string(__ANDROID_API_R__)); |
| jar->set_classpath(SYSTEMSERVERCLASSPATH); |
| expected_jars.push_back("/apex/com.android.foo/javalib/minsdk30"); |
| jar = exported_jars.add_jars(); |
| jar->set_path("/apex/com.android.foo/javalib/maxsdk30"); |
| jar->set_max_sdk_version(std::to_string(__ANDROID_API_R__)); |
| jar->set_classpath(SYSTEMSERVERCLASSPATH); |
| |
| // Device's reported version: |
| jar = exported_jars.add_jars(); |
| jar->set_path("/apex/com.android.foo/javalib/minsdklatest"); |
| jar->set_min_sdk_version(std::to_string(android_get_device_api_level())); |
| jar->set_classpath(SYSTEMSERVERCLASSPATH); |
| expected_jars.push_back("/apex/com.android.foo/javalib/minsdklatest"); |
| jar = exported_jars.add_jars(); |
| jar->set_path("/apex/com.android.foo/javalib/maxsdklatest"); |
| jar->set_max_sdk_version(std::to_string(android_get_device_api_level())); |
| jar->set_classpath(SYSTEMSERVERCLASSPATH); |
| if ("REL" == android::base::GetProperty("ro.build.version.codename", "")) { |
| expected_jars.push_back("/apex/com.android.foo/javalib/maxsdklatest"); |
| } |
| |
| // unknown SDK_INT+1 version |
| jar = exported_jars.add_jars(); |
| jar->set_path("/apex/com.android.foo/javalib/minsdk_plus1"); |
| jar->set_min_sdk_version(std::to_string(android_get_device_api_level() + 1)); |
| jar->set_classpath(SYSTEMSERVERCLASSPATH); |
| jar = exported_jars.add_jars(); |
| jar->set_path("/apex/com.android.foo/javalib/maxsdk_plus1"); |
| jar->set_max_sdk_version(std::to_string(android_get_device_api_level() + 1)); |
| jar->set_classpath(SYSTEMSERVERCLASSPATH); |
| expected_jars.push_back("/apex/com.android.foo/javalib/maxsdk_plus1"); |
| |
| // known min_sdk_version and future max_sdk_version |
| jar = exported_jars.add_jars(); |
| jar->set_path("/apex/com.android.foo/javalib/minsdk30maxsdk10000"); |
| jar->set_min_sdk_version(std::to_string(__ANDROID_API_R__)); |
| jar->set_max_sdk_version(std::to_string(android_get_device_api_level() + 1)); |
| jar->set_classpath(SYSTEMSERVERCLASSPATH); |
| expected_jars.push_back("/apex/com.android.foo/javalib/minsdk30maxsdk10000"); |
| |
| // codename |
| if ("REL" != android::base::GetProperty("ro.build.version.codename", "")) { |
| jar = exported_jars.add_jars(); |
| jar->set_path("/apex/com.android.foo/javalib/minsdkS"); |
| jar->set_min_sdk_version("S"); |
| jar->set_classpath(SYSTEMSERVERCLASSPATH); |
| expected_jars.push_back("/apex/com.android.foo/javalib/minsdkS"); |
| |
| jar = exported_jars.add_jars(); |
| jar->set_path("/apex/com.android.foo/javalib/minsdkSv2"); |
| jar->set_min_sdk_version("Sv2"); |
| jar->set_classpath(SYSTEMSERVERCLASSPATH); |
| expected_jars.push_back("/apex/com.android.foo/javalib/minsdkSv2"); |
| |
| jar = exported_jars.add_jars(); |
| jar->set_path("/apex/com.android.foo/javalib/minsdkTiramisu"); |
| jar->set_min_sdk_version("Tiramisu"); |
| jar->set_classpath(SYSTEMSERVERCLASSPATH); |
| expected_jars.push_back("/apex/com.android.foo/javalib/minsdkTiramisu"); |
| |
| jar = exported_jars.add_jars(); |
| jar->set_path("/apex/com.android.foo/javalib/maxsdkS"); |
| jar->set_max_sdk_version("S"); |
| jar->set_classpath(SYSTEMSERVERCLASSPATH); |
| |
| jar = exported_jars.add_jars(); |
| jar->set_path("/apex/com.android.foo/javalib/maxsdkSv2"); |
| jar->set_max_sdk_version("Sv2"); |
| jar->set_classpath(SYSTEMSERVERCLASSPATH); |
| |
| jar = exported_jars.add_jars(); |
| jar->set_path("/apex/com.android.foo/javalib/maxsdkZFutureSdkVersion"); |
| jar->set_max_sdk_version("ZFutureSdkVersion"); |
| jar->set_classpath(SYSTEMSERVERCLASSPATH); |
| expected_jars.push_back("/apex/com.android.foo/javalib/maxsdkZFutureSdkVersion"); |
| } |
| |
| // ...and write this config to systemserverclasspath.pb |
| WriteConfig(exported_jars, "/apex/com.android.foo/etc/classpaths/systemserverclasspath.pb"); |
| |
| // Generate and parse SYSTEMSERVERCLASSPATH |
| GenerateClasspathExports(default_args_with_test_dir_); |
| const std::vector<std::string> exportLines = ParseExportsFile(); |
| const std::vector<std::string> splitExportLine = SplitClasspathExportLine(exportLines[2]); |
| const std::string exportValue = splitExportLine[2]; |
| |
| EXPECT_EQ(android::base::Join(expected_jars, ":"), exportValue); |
| #endif |
| } |
| |
| // Test jars with different sdk versions against override device values. |
| TEST_F(DeriveClasspathTest, SdkVersionsAreCheckedAgainstOverrideDeviceValuesRelease) { |
| Args args = default_args_with_test_dir_; |
| args.override_device_sdk_version = 35; |
| args.override_device_codename = "REL"; |
| |
| // List of jars expected to be in SYSTEMSERVERCLASSPATH. |
| std::vector<std::string> expected_jars; |
| |
| // Add an unbounded jar. |
| AddJarToClasspath("/system", "/system/framework/unbounded", SYSTEMSERVERCLASSPATH); |
| expected_jars.push_back("/system/framework/unbounded"); |
| |
| // Manually create a config with jars that sets sdk versions... |
| ExportedClasspathsJars exported_jars; |
| |
| // Known released versions. |
| Jar* jar = exported_jars.add_jars(); |
| jar->set_path("/apex/com.android.foo/javalib/minsdk30"); |
| jar->set_min_sdk_version(std::to_string(__ANDROID_API_R__)); |
| jar->set_classpath(SYSTEMSERVERCLASSPATH); |
| expected_jars.push_back("/apex/com.android.foo/javalib/minsdk30"); |
| jar = exported_jars.add_jars(); |
| jar->set_path("/apex/com.android.foo/javalib/maxsdk30"); |
| jar->set_max_sdk_version(std::to_string(__ANDROID_API_R__)); |
| jar->set_classpath(SYSTEMSERVERCLASSPATH); |
| |
| // Provided override device sdk version. |
| jar = exported_jars.add_jars(); |
| jar->set_path("/apex/com.android.foo/javalib/minsdklatest"); |
| jar->set_min_sdk_version(std::to_string(args.override_device_sdk_version)); |
| jar->set_classpath(SYSTEMSERVERCLASSPATH); |
| expected_jars.push_back("/apex/com.android.foo/javalib/minsdklatest"); |
| jar = exported_jars.add_jars(); |
| jar->set_path("/apex/com.android.foo/javalib/maxsdklatest"); |
| jar->set_max_sdk_version(std::to_string(args.override_device_sdk_version)); |
| jar->set_classpath(SYSTEMSERVERCLASSPATH); |
| expected_jars.push_back("/apex/com.android.foo/javalib/maxsdklatest"); |
| |
| // Unknown SDK_INT+1 version. |
| jar = exported_jars.add_jars(); |
| jar->set_path("/apex/com.android.foo/javalib/minsdk_plus1"); |
| jar->set_min_sdk_version(std::to_string(args.override_device_sdk_version + 1)); |
| jar->set_classpath(SYSTEMSERVERCLASSPATH); |
| jar = exported_jars.add_jars(); |
| jar->set_path("/apex/com.android.foo/javalib/maxsdk_plus1"); |
| jar->set_max_sdk_version(std::to_string(args.override_device_sdk_version + 1)); |
| jar->set_classpath(SYSTEMSERVERCLASSPATH); |
| expected_jars.push_back("/apex/com.android.foo/javalib/maxsdk_plus1"); |
| |
| // Known min_sdk_version and future max_sdk_version. |
| jar = exported_jars.add_jars(); |
| jar->set_path("/apex/com.android.foo/javalib/minsdk30maxsdk10000"); |
| jar->set_min_sdk_version(std::to_string(__ANDROID_API_R__)); |
| jar->set_max_sdk_version(std::to_string(args.override_device_sdk_version + 1)); |
| jar->set_classpath(SYSTEMSERVERCLASSPATH); |
| expected_jars.push_back("/apex/com.android.foo/javalib/minsdk30maxsdk10000"); |
| |
| // Codename. |
| jar = exported_jars.add_jars(); |
| jar->set_path("/apex/com.android.foo/javalib/minsdkBaklava"); |
| jar->set_min_sdk_version("Baklava"); |
| jar->set_classpath(SYSTEMSERVERCLASSPATH); |
| |
| jar = exported_jars.add_jars(); |
| jar->set_path("/apex/com.android.foo/javalib/maxsdkBaklava"); |
| jar->set_max_sdk_version("Baklava"); |
| jar->set_classpath(SYSTEMSERVERCLASSPATH); |
| expected_jars.push_back("/apex/com.android.foo/javalib/maxsdkBaklava"); |
| |
| // ...and write this config to systemserverclasspath.pb. |
| WriteConfig(exported_jars, "/apex/com.android.foo/etc/classpaths/systemserverclasspath.pb"); |
| |
| // Generate and parse SYSTEMSERVERCLASSPATH. |
| GenerateClasspathExports(args); |
| const std::vector<std::string> exportLines = ParseExportsFile(); |
| const std::vector<std::string> splitExportLine = SplitClasspathExportLine(exportLines[2]); |
| const std::string exportValue = splitExportLine[2]; |
| |
| EXPECT_EQ(android::base::Join(expected_jars, ":"), exportValue); |
| } |
| |
| // Test jars with different sdk versions against override device values. |
| TEST_F(DeriveClasspathTest, SdkVersionsAreCheckedAgainstOverrideDeviceValuesDev) { |
| Args args = default_args_with_test_dir_; |
| args.override_device_sdk_version = 35; |
| args.override_device_codename = "Baklava"; |
| args.override_device_known_codenames = { |
| "S", "Sv2", "Tiramisu", "UpsideDownCake", "VanillaIceCream", "Baklava"}; |
| |
| // List of jars expected to be in SYSTEMSERVERCLASSPATH. |
| std::vector<std::string> expected_jars; |
| |
| // Add an unbounded jar. |
| AddJarToClasspath("/system", "/system/framework/unbounded", SYSTEMSERVERCLASSPATH); |
| expected_jars.push_back("/system/framework/unbounded"); |
| |
| // Manually create a config with jars that sets sdk versions... |
| ExportedClasspathsJars exported_jars; |
| |
| // Known released versions. |
| Jar* jar = exported_jars.add_jars(); |
| jar->set_path("/apex/com.android.foo/javalib/minsdk30"); |
| jar->set_min_sdk_version(std::to_string(__ANDROID_API_R__)); |
| jar->set_classpath(SYSTEMSERVERCLASSPATH); |
| expected_jars.push_back("/apex/com.android.foo/javalib/minsdk30"); |
| jar = exported_jars.add_jars(); |
| jar->set_path("/apex/com.android.foo/javalib/maxsdk30"); |
| jar->set_max_sdk_version(std::to_string(__ANDROID_API_R__)); |
| jar->set_classpath(SYSTEMSERVERCLASSPATH); |
| |
| // Provided override device sdk version. |
| jar = exported_jars.add_jars(); |
| jar->set_path("/apex/com.android.foo/javalib/minsdklatest"); |
| jar->set_min_sdk_version(std::to_string(args.override_device_sdk_version)); |
| jar->set_classpath(SYSTEMSERVERCLASSPATH); |
| expected_jars.push_back("/apex/com.android.foo/javalib/minsdklatest"); |
| jar = exported_jars.add_jars(); |
| jar->set_path("/apex/com.android.foo/javalib/maxsdklatest"); |
| jar->set_max_sdk_version(std::to_string(args.override_device_sdk_version)); |
| jar->set_classpath(SYSTEMSERVERCLASSPATH); |
| |
| // Unknown SDK_INT+1 version. |
| jar = exported_jars.add_jars(); |
| jar->set_path("/apex/com.android.foo/javalib/minsdk_plus1"); |
| jar->set_min_sdk_version(std::to_string(args.override_device_sdk_version + 1)); |
| jar->set_classpath(SYSTEMSERVERCLASSPATH); |
| jar = exported_jars.add_jars(); |
| jar->set_path("/apex/com.android.foo/javalib/maxsdk_plus1"); |
| jar->set_max_sdk_version(std::to_string(args.override_device_sdk_version + 1)); |
| jar->set_classpath(SYSTEMSERVERCLASSPATH); |
| expected_jars.push_back("/apex/com.android.foo/javalib/maxsdk_plus1"); |
| |
| // Known min_sdk_version and future max_sdk_version. |
| jar = exported_jars.add_jars(); |
| jar->set_path("/apex/com.android.foo/javalib/minsdk30maxsdk10000"); |
| jar->set_min_sdk_version(std::to_string(__ANDROID_API_R__)); |
| jar->set_max_sdk_version(std::to_string(args.override_device_sdk_version + 1)); |
| jar->set_classpath(SYSTEMSERVERCLASSPATH); |
| expected_jars.push_back("/apex/com.android.foo/javalib/minsdk30maxsdk10000"); |
| |
| // Codename. |
| jar = exported_jars.add_jars(); |
| jar->set_path("/apex/com.android.foo/javalib/minsdkBaklava"); |
| jar->set_min_sdk_version("Baklava"); |
| jar->set_classpath(SYSTEMSERVERCLASSPATH); |
| expected_jars.push_back("/apex/com.android.foo/javalib/minsdkBaklava"); |
| |
| jar = exported_jars.add_jars(); |
| jar->set_path("/apex/com.android.foo/javalib/maxsdkBaklava"); |
| jar->set_max_sdk_version("Baklava"); |
| jar->set_classpath(SYSTEMSERVERCLASSPATH); |
| expected_jars.push_back("/apex/com.android.foo/javalib/maxsdkBaklava"); |
| |
| jar = exported_jars.add_jars(); |
| jar->set_path("/apex/com.android.foo/javalib/minsdkTiramisu"); |
| jar->set_min_sdk_version("Tiramisu"); |
| jar->set_classpath(SYSTEMSERVERCLASSPATH); |
| expected_jars.push_back("/apex/com.android.foo/javalib/minsdkTiramisu"); |
| |
| jar = exported_jars.add_jars(); |
| jar->set_path("/apex/com.android.foo/javalib/maxsdkTiramisu"); |
| jar->set_max_sdk_version("Tiramisu"); |
| jar->set_classpath(SYSTEMSERVERCLASSPATH); |
| |
| // ...and write this config to systemserverclasspath.pb. |
| WriteConfig(exported_jars, "/apex/com.android.foo/etc/classpaths/systemserverclasspath.pb"); |
| |
| // Generate and parse SYSTEMSERVERCLASSPATH. |
| GenerateClasspathExports(args); |
| const std::vector<std::string> exportLines = ParseExportsFile(); |
| const std::vector<std::string> splitExportLine = SplitClasspathExportLine(exportLines[2]); |
| const std::string exportValue = splitExportLine[2]; |
| |
| EXPECT_EQ(android::base::Join(expected_jars, ":"), exportValue); |
| } |
| |
| } // namespace |
| } // namespace derive_classpath |
| } // namespace android |
| |
| int main(int argc, char** argv) { |
| ::testing::InitGoogleTest(&argc, argv); |
| // Required for EXPECT_DEATH to work correctly |
| android::base::SetLogger(android::base::StderrLogger); |
| return RUN_ALL_TESTS(); |
| } |