• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "derive_classpath.h"
18 
19 #include <android-base/file.h>
20 #include <android-base/logging.h>
21 #include <android-base/properties.h>
22 #include <android-base/stringprintf.h>
23 #include <android-base/strings.h>
24 #include <gtest/gtest.h>
25 #include <stdlib.h>
26 #include <sys/mman.h>
27 #include <sys/stat.h>
28 
29 #include <cstdlib>
30 #include <string_view>
31 
32 #include "android-base/unique_fd.h"
33 #include "packages/modules/common/proto/classpaths.pb.h"
34 
35 #ifdef SDKEXT_ANDROID
36 #include <android-modules-utils/sdk_level.h>
37 #include <android/api-level.h>
38 #else
39 
40 #define __ANDROID_API_R__ 30
41 #define __NR_memfd_create 319
42 
memfd_create(const char * name,unsigned int flags)43 int memfd_create(const char* name, unsigned int flags) {
44   return syscall(__NR_memfd_create, name, flags);
45 }
46 #endif
47 
48 namespace android {
49 namespace derive_classpath {
50 namespace {
51 
52 static const std::string kFrameworkJarFilepath = "/system/framework/framework.jar";
53 static const std::string kLibartJarFilepath = "/apex/com.android.art/javalib/core-libart.jar";
54 static const std::string kSdkExtensionsJarFilepath =
55     "/apex/com.android.sdkext/javalib/framework-sdkextensions.jar";
56 static const std::string kServicesJarFilepath = "/system/framework/services.jar";
57 
58 // The fixture for testing derive_classpath.
59 class DeriveClasspathTest : public ::testing::Test {
60  protected:
working_dir()61   const std::string working_dir() { return std::string(temp_dir_.path); }
62 
GetOutputPath()63   const std::string GetOutputPath() { return working_dir() + "/classpath"; }
64 
65   // Parses the generated classpath exports file and returns each line individually.
ParseExportsFile(const char * file=nullptr)66   std::vector<std::string> ParseExportsFile(const char* file = nullptr) {
67     if (file == nullptr) {
68       file = output_path_.c_str();
69     }
70     std::string contents;
71     EXPECT_TRUE(android::base::ReadFileToString(file, &contents,
72                                                 /*follow_symlinks=*/true));
73     return android::base::Split(contents, "\n");
74   }
75 
SplitClasspathExportLine(const std::string & line)76   std::vector<std::string> SplitClasspathExportLine(const std::string& line) {
77     std::vector<std::string> contents = android::base::Split(line, " ");
78     // Export lines are expected to be structured as `export <name> <value>`.
79     EXPECT_EQ(3, contents.size());
80     EXPECT_EQ("export", contents[0]);
81     return contents;
82   }
83 
84   // Checks the order of the jars in a given classpath.
85   // Instead of doing a full order check, it assumes the jars are grouped by partition and checks
86   // that partitions come in order of the `prefixes` that is given.
CheckClasspathGroupOrder(const std::string classpath,const std::vector<std::string> prefixes)87   void CheckClasspathGroupOrder(const std::string classpath,
88                                 const std::vector<std::string> prefixes) {
89     ASSERT_NE(0, prefixes.size());
90     ASSERT_NE(0, classpath.size());
91 
92     auto jars = android::base::Split(classpath, ":");
93 
94     auto prefix = prefixes.begin();
95     auto jar = jars.begin();
96     for (; jar != jars.end() && prefix != prefixes.end(); ++jar) {
97       if (*jar == "/apex/com.android.i18n/javalib/core-icu4j.jar") {
98         // core-icu4j.jar is special and is out of order in BOOTCLASSPATH;
99         // ignore it when checking for general order
100         continue;
101       }
102       if (!android::base::StartsWith(*jar, *prefix)) {
103         ++prefix;
104       }
105     }
106     EXPECT_NE(prefix, prefixes.end());
107     // All jars have been iterated over, thus they all have valid prefixes
108     EXPECT_EQ(jar, jars.end());
109   }
110 
WriteConfig(const ExportedClasspathsJars & exported_jars,const std::string & path)111   void WriteConfig(const ExportedClasspathsJars& exported_jars, const std::string& path) {
112     std::string fragment_path = working_dir() + path;
113     std::string buf;
114     exported_jars.SerializeToString(&buf);
115     std::string cmd("mkdir -p " + android::base::Dirname(fragment_path));
116     ASSERT_EQ(0, system(cmd.c_str()));
117     ASSERT_TRUE(android::base::WriteStringToFile(buf, fragment_path, true));
118   }
119 
AddJarToClasspath(const std::string & partition,const std::string & jar_filepath,Classpath classpath)120   void AddJarToClasspath(const std::string& partition, const std::string& jar_filepath,
121                          Classpath classpath) {
122     ExportedClasspathsJars exported_jars;
123     Jar* jar = exported_jars.add_jars();
124     jar->set_path(jar_filepath);
125     jar->set_classpath(classpath);
126 
127     std::string basename = Classpath_Name(classpath) + ".pb";
128     std::transform(basename.begin(), basename.end(), basename.begin(),
129                    [](unsigned char c) { return std::tolower(c); });
130 
131     WriteConfig(exported_jars, partition + "/etc/classpaths/" + basename);
132   }
133 
134   const TemporaryDir temp_dir_;
135   const std::string output_path_ = working_dir() + "/classpath";
136 
137 #ifdef SDKEXT_ANDROID
138   const Args default_args_with_test_dir_ = {
139       .output_path = output_path_,
140       .glob_pattern_prefix = temp_dir_.path,
141   };
142 #else
143   const Args default_args_with_test_dir_ = {
144       .output_path = output_path_,
145       .override_device_sdk_version = 35,
146       .override_device_codename = "REL",
147       .glob_pattern_prefix = temp_dir_.path,
148   };
149 #endif
150 };
151 
152 using DeriveClasspathDeathTest = DeriveClasspathTest;
153 
154 // Check only known *CLASSPATH variables are exported.
TEST_F(DeriveClasspathTest,DefaultNoUnknownClasspaths)155 TEST_F(DeriveClasspathTest, DefaultNoUnknownClasspaths) {
156   // Re-generate default on device classpaths
157   GenerateClasspathExports(default_args_with_test_dir_);
158 
159   const std::vector<std::string> exportLines = ParseExportsFile();
160   // The first four lines are tested below.
161   for (int i = 4; i < exportLines.size(); i++) {
162     EXPECT_EQ(exportLines[i], "");
163   }
164 }
165 
166 // Test that all variables are properly generated.
TEST_F(DeriveClasspathTest,AllVariables)167 TEST_F(DeriveClasspathTest, AllVariables) {
168   ExportedClasspathsJars exported_jars;
169   Jar* jar = exported_jars.add_jars();
170   jar->set_path("/apex/com.android.foo/javalib/foo");
171   jar->set_classpath(BOOTCLASSPATH);
172   jar = exported_jars.add_jars();
173   jar->set_path("/apex/com.android.bar/javalib/bar");
174   jar->set_classpath(DEX2OATBOOTCLASSPATH);
175   WriteConfig(exported_jars, "/system/etc/classpaths/bootclasspath.pb");
176 
177   exported_jars.clear_jars();
178   jar = exported_jars.add_jars();
179   jar->set_path("/apex/com.android.baz/javalib/baz");
180   jar->set_classpath(SYSTEMSERVERCLASSPATH);
181   jar = exported_jars.add_jars();
182   jar->set_path("/apex/com.android.qux/javalib/qux");
183   jar->set_classpath(STANDALONE_SYSTEMSERVER_JARS);
184   WriteConfig(exported_jars, "/system/etc/classpaths/systemserverclasspath.pb");
185 
186   ASSERT_TRUE(GenerateClasspathExports(default_args_with_test_dir_));
187 
188   const std::vector<std::string> exportLines = ParseExportsFile();
189   std::vector<std::string> splitExportLine;
190 
191   splitExportLine = SplitClasspathExportLine(exportLines[0]);
192   EXPECT_EQ("BOOTCLASSPATH", splitExportLine[1]);
193   EXPECT_EQ("/apex/com.android.foo/javalib/foo", splitExportLine[2]);
194   splitExportLine = SplitClasspathExportLine(exportLines[1]);
195   EXPECT_EQ("DEX2OATBOOTCLASSPATH", splitExportLine[1]);
196   EXPECT_EQ("/apex/com.android.bar/javalib/bar", splitExportLine[2]);
197   splitExportLine = SplitClasspathExportLine(exportLines[2]);
198   EXPECT_EQ("SYSTEMSERVERCLASSPATH", splitExportLine[1]);
199   EXPECT_EQ("/apex/com.android.baz/javalib/baz", splitExportLine[2]);
200   splitExportLine = SplitClasspathExportLine(exportLines[3]);
201   EXPECT_EQ("STANDALONE_SYSTEMSERVER_JARS", splitExportLine[1]);
202   EXPECT_EQ("/apex/com.android.qux/javalib/qux", splitExportLine[2]);
203 }
204 
205 // Test that temp directory does not pick up actual jars.
TEST_F(DeriveClasspathTest,TempConfig)206 TEST_F(DeriveClasspathTest, TempConfig) {
207   AddJarToClasspath("/apex/com.android.foo", "/apex/com.android.foo/javalib/foo", BOOTCLASSPATH);
208   AddJarToClasspath("/apex/com.android.baz", "/apex/com.android.baz/javalib/baz",
209                     SYSTEMSERVERCLASSPATH);
210 
211   ASSERT_TRUE(GenerateClasspathExports(default_args_with_test_dir_));
212 
213   const std::vector<std::string> exportLines = ParseExportsFile();
214 
215   std::vector<std::string> splitExportLine;
216 
217   splitExportLine = SplitClasspathExportLine(exportLines[0]);
218   EXPECT_EQ("BOOTCLASSPATH", splitExportLine[1]);
219   EXPECT_EQ("/apex/com.android.foo/javalib/foo", splitExportLine[2]);
220   splitExportLine = SplitClasspathExportLine(exportLines[2]);
221   EXPECT_EQ("SYSTEMSERVERCLASSPATH", splitExportLine[1]);
222   EXPECT_EQ("/apex/com.android.baz/javalib/baz", splitExportLine[2]);
223 }
224 
225 // Test individual modules are sorted by pathnames.
TEST_F(DeriveClasspathTest,ModulesAreSorted)226 TEST_F(DeriveClasspathTest, ModulesAreSorted) {
227   AddJarToClasspath("/apex/com.android.art", "/apex/com.android.art/javalib/art", BOOTCLASSPATH);
228   AddJarToClasspath("/system", "/system/framework/jar", BOOTCLASSPATH);
229   AddJarToClasspath("/apex/com.android.foo", "/apex/com.android.foo/javalib/foo", BOOTCLASSPATH);
230   AddJarToClasspath("/apex/com.android.bar", "/apex/com.android.bar/javalib/bar", BOOTCLASSPATH);
231   AddJarToClasspath("/apex/com.android.baz", "/apex/com.android.baz/javalib/baz", BOOTCLASSPATH);
232 
233   ASSERT_TRUE(GenerateClasspathExports(default_args_with_test_dir_));
234 
235   const std::vector<std::string> exportLines = ParseExportsFile();
236   const std::vector<std::string> splitExportLine = SplitClasspathExportLine(exportLines[0]);
237   const std::string exportValue = splitExportLine[2];
238 
239   const std::string expectedJars(
240       "/apex/com.android.art/javalib/art"
241       ":/system/framework/jar"
242       ":/apex/com.android.bar/javalib/bar"
243       ":/apex/com.android.baz/javalib/baz"
244       ":/apex/com.android.foo/javalib/foo");
245 
246   EXPECT_EQ(expectedJars, exportValue);
247 }
248 
249 // Test we can output to custom files.
TEST_F(DeriveClasspathTest,CustomOutputLocation)250 TEST_F(DeriveClasspathTest, CustomOutputLocation) {
251   AddJarToClasspath("/apex/com.android.art", "/apex/com.android.art/javalib/art", BOOTCLASSPATH);
252   AddJarToClasspath("/system", "/system/framework/jar", BOOTCLASSPATH);
253   AddJarToClasspath("/apex/com.android.foo", "/apex/com.android.foo/javalib/foo", BOOTCLASSPATH);
254   AddJarToClasspath("/apex/com.android.bar", "/apex/com.android.bar/javalib/bar", BOOTCLASSPATH);
255   AddJarToClasspath("/apex/com.android.baz", "/apex/com.android.baz/javalib/baz", BOOTCLASSPATH);
256 
257   android::base::unique_fd fd(memfd_create("temp_file", MFD_CLOEXEC));
258   ASSERT_TRUE(fd.ok()) << "Unable to open temp-file";
259   const std::string file_name = android::base::StringPrintf("/proc/self/fd/%d", fd.get());
260   Args args = {
261       .output_path = file_name,
262       .glob_pattern_prefix = working_dir(),
263   };
264   ASSERT_TRUE(GenerateClasspathExports(args));
265 
266   const std::vector<std::string> exportLines = ParseExportsFile(file_name.c_str());
267   const std::vector<std::string> splitExportLine = SplitClasspathExportLine(exportLines[0]);
268   const std::string exportValue = splitExportLine[2];
269 
270   const std::string expectedJars(
271       "/apex/com.android.art/javalib/art"
272       ":/system/framework/jar"
273       ":/apex/com.android.bar/javalib/bar"
274       ":/apex/com.android.baz/javalib/baz"
275       ":/apex/com.android.foo/javalib/foo");
276 
277   EXPECT_EQ(expectedJars, exportValue);
278 }
279 
280 // Test alternative .pb for bootclasspath and systemclasspath.
TEST_F(DeriveClasspathTest,CustomInputLocation)281 TEST_F(DeriveClasspathTest, CustomInputLocation) {
282   AddJarToClasspath("/other", "/other/bcp-jar", BOOTCLASSPATH);
283   AddJarToClasspath("/other", "/other/systemserver-jar", SYSTEMSERVERCLASSPATH);
284   AddJarToClasspath("/apex/com.android.art", "/apex/com.android.art/javalib/art", BOOTCLASSPATH);
285   AddJarToClasspath("/apex/com.android.foo", "/apex/com.android.foo/javalib/foo", BOOTCLASSPATH);
286   AddJarToClasspath("/apex/com.android.baz", "/apex/com.android.baz/javalib/baz",
287                     SYSTEMSERVERCLASSPATH);
288 
289   Args args = default_args_with_test_dir_;
290   args.system_bootclasspath_fragment = "/other/etc/classpaths/bootclasspath.pb";
291   args.system_systemserverclasspath_fragment = "/other/etc/classpaths/systemserverclasspath.pb";
292 
293   ASSERT_TRUE(GenerateClasspathExports(args));
294 
295   const std::vector<std::string> exportLines = ParseExportsFile();
296 
297   std::vector<std::string> splitExportLine;
298   splitExportLine = SplitClasspathExportLine(exportLines[0]);
299   EXPECT_EQ("BOOTCLASSPATH", splitExportLine[1]);
300   const std::string expectedBcpJars(
301       "/apex/com.android.art/javalib/art"
302       ":/other/bcp-jar"
303       ":/apex/com.android.foo/javalib/foo");
304   EXPECT_EQ(expectedBcpJars, splitExportLine[2]);
305 
306   splitExportLine = SplitClasspathExportLine(exportLines[2]);
307   EXPECT_EQ("SYSTEMSERVERCLASSPATH", splitExportLine[1]);
308   const std::string expectedSystemServerJars(
309       "/other/systemserver-jar"
310       ":/apex/com.android.baz/javalib/baz");
311   EXPECT_EQ(expectedSystemServerJars, splitExportLine[2]);
312 }
313 
314 // Test output location that can't be written to.
TEST_F(DeriveClasspathTest,NonWriteableOutputLocation)315 TEST_F(DeriveClasspathTest, NonWriteableOutputLocation) {
316   AddJarToClasspath("/apex/com.android.art", "/apex/com.android.art/javalib/art", BOOTCLASSPATH);
317   AddJarToClasspath("/system", "/system/framework/jar", BOOTCLASSPATH);
318 
319   Args args = {
320       .output_path = "/system/non_writable_path",
321       .glob_pattern_prefix = working_dir(),
322   };
323   ASSERT_FALSE(GenerateClasspathExports(args));
324 }
325 
TEST_F(DeriveClasspathTest,ScanOnlySpecificDirectories)326 TEST_F(DeriveClasspathTest, ScanOnlySpecificDirectories) {
327   AddJarToClasspath("/system", "/system/framework/jar", BOOTCLASSPATH);
328   AddJarToClasspath("/apex/com.android.foo", "/apex/com.android.foo/javalib/foo", BOOTCLASSPATH);
329   AddJarToClasspath("/apex/com.android.foo", "/apex/com.android.foo/javalib/sys",
330                     SYSTEMSERVERCLASSPATH);
331   AddJarToClasspath("/apex/com.android.bar", "/apex/com.android.bar/javalib/bar", BOOTCLASSPATH);
332   AddJarToClasspath("/apex/com.android.baz", "/apex/com.android.baz/javalib/baz", BOOTCLASSPATH);
333 
334   auto args_with_scan_dirs = default_args_with_test_dir_;
335   args_with_scan_dirs.scan_dirs.push_back("/apex/com.android.foo");
336   args_with_scan_dirs.scan_dirs.push_back("/apex/com.android.bar");
337   ASSERT_TRUE(GenerateClasspathExports(args_with_scan_dirs));
338 
339   const std::vector<std::string> exportLines = ParseExportsFile();
340 
341   std::vector<std::string> splitExportLine;
342 
343   splitExportLine = SplitClasspathExportLine(exportLines[0]);
344   EXPECT_EQ("BOOTCLASSPATH", splitExportLine[1]);
345   // Not sorted. Maintains the ordering provided in scan_dirs
346   const std::string expectedJars(
347       "/apex/com.android.foo/javalib/foo"
348       ":/apex/com.android.bar/javalib/bar");
349   EXPECT_EQ(expectedJars, splitExportLine[2]);
350   splitExportLine = SplitClasspathExportLine(exportLines[2]);
351   EXPECT_EQ("SYSTEMSERVERCLASSPATH", splitExportLine[1]);
352   EXPECT_EQ("/apex/com.android.foo/javalib/sys", splitExportLine[2]);
353 }
354 
355 // Test apexes only export their own jars.
TEST_F(DeriveClasspathDeathTest,ApexJarsBelongToApex)356 TEST_F(DeriveClasspathDeathTest, ApexJarsBelongToApex) {
357   // EXPECT_DEATH expects error messages in stderr, log there
358   android::base::SetLogger(android::base::StderrLogger);
359 
360   AddJarToClasspath("/system", "/system/framework/jar", BOOTCLASSPATH);
361   ASSERT_TRUE(GenerateClasspathExports(default_args_with_test_dir_));
362 
363   AddJarToClasspath("/apex/com.android.foo", "/apex/com.android.foo/javalib/foo", BOOTCLASSPATH);
364   ASSERT_TRUE(GenerateClasspathExports(default_args_with_test_dir_));
365 
366   AddJarToClasspath("/apex/com.android.bar@12345.tmp", "/apex/com.android.bar/javalib/bar",
367                     BOOTCLASSPATH);
368   ASSERT_TRUE(GenerateClasspathExports(default_args_with_test_dir_));
369 
370   AddJarToClasspath("/apex/com.android.baz@12345", "/apex/this/path/is/skipped", BOOTCLASSPATH);
371   ASSERT_TRUE(GenerateClasspathExports(default_args_with_test_dir_));
372 
373   AddJarToClasspath("/apex/com.android.bar", "/apex/wrong/path/bar", BOOTCLASSPATH);
374   EXPECT_DEATH(GenerateClasspathExports(default_args_with_test_dir_),
375                "must not export a jar.*wrong/path/bar");
376 }
377 
378 // Test only bind mounted apexes are skipped
TEST_F(DeriveClasspathTest,OnlyBindMountedApexIsSkipped)379 TEST_F(DeriveClasspathTest, OnlyBindMountedApexIsSkipped) {
380   AddJarToClasspath("/system", "/system/framework/jar", BOOTCLASSPATH);
381   // Normal APEX with format: /apex/<module-name>/*
382   AddJarToClasspath("/apex/com.android.foo", "/apex/com.android.foo/javalib/foo", BOOTCLASSPATH);
383   // Bind mounted APEX with format: /apex/<module-name>@<version>/*
384   AddJarToClasspath("/apex/com.android.bar@123", "/apex/com.android.bar/javalib/bar",
385                     BOOTCLASSPATH);
386   // Temp mounted APEX with format: /apex/<module-name>@<version>.tmp/*
387   AddJarToClasspath("/apex/com.android.baz@123.tmp", "/apex/com.android.baz/javalib/baz",
388                     BOOTCLASSPATH);
389 
390   ASSERT_TRUE(GenerateClasspathExports(default_args_with_test_dir_));
391 
392   const std::vector<std::string> exportLines = ParseExportsFile();
393 
394   std::vector<std::string> splitExportLine;
395 
396   splitExportLine = SplitClasspathExportLine(exportLines[0]);
397   EXPECT_EQ("BOOTCLASSPATH", splitExportLine[1]);
398   const std::string expectedJars(
399       "/system/framework/jar"
400       ":/apex/com.android.baz/javalib/baz"
401       ":/apex/com.android.foo/javalib/foo");
402   EXPECT_EQ(expectedJars, splitExportLine[2]);
403 }
404 
405 // Test classpath fragments export jars for themselves.
TEST_F(DeriveClasspathDeathTest,WrongClasspathInFragments)406 TEST_F(DeriveClasspathDeathTest, WrongClasspathInFragments) {
407   // Valid configs
408   AddJarToClasspath("/system", "/system/framework/framework-jar", BOOTCLASSPATH);
409   AddJarToClasspath("/system", "/system/framework/service-jar", SYSTEMSERVERCLASSPATH);
410 
411   // Manually create an invalid config with both BCP and SSCP jars...
412   ExportedClasspathsJars exported_jars;
413   Jar* jar = exported_jars.add_jars();
414   jar->set_path("/apex/com.android.foo/javalib/foo");
415   jar->set_classpath(BOOTCLASSPATH);
416   // note that DEX2OATBOOTCLASSPATH and BOOTCLASSPATH jars are expected to be in the same config
417   jar = exported_jars.add_jars();
418   jar->set_path("/apex/com.android.foo/javalib/foo");
419   jar->set_classpath(DEX2OATBOOTCLASSPATH);
420   jar = exported_jars.add_jars();
421   jar->set_path("/apex/com.android.foo/javalib/service-foo");
422   jar->set_classpath(SYSTEMSERVERCLASSPATH);
423 
424   // ...and write this config to bootclasspath.pb
425   WriteConfig(exported_jars, "/apex/com.android.foo/etc/classpaths/bootclasspath.pb");
426 
427   EXPECT_DEATH(GenerateClasspathExports(default_args_with_test_dir_),
428                "must not export a jar for SYSTEMSERVERCLASSPATH");
429 }
430 
TEST_F(DeriveClasspathDeathTest,CurrentSdkVersion)431 TEST_F(DeriveClasspathDeathTest, CurrentSdkVersion) {
432 #ifndef SDKEXT_ANDROID
433   GTEST_SKIP();
434 #else
435   if (android_get_device_api_level() < __ANDROID_API_S__) {
436     GTEST_SKIP();
437   }
438 
439   ExportedClasspathsJars exported_jars;
440   Jar* jar = exported_jars.add_jars();
441   jar->set_path("/apex/com.android.foo/javalib/minsdkcurrent");
442   jar->set_min_sdk_version("current");
443   jar->set_classpath(SYSTEMSERVERCLASSPATH);
444   WriteConfig(exported_jars, "/apex/com.android.foo/etc/classpaths/systemserverclasspath.pb");
445 
446   EXPECT_DEATH(GenerateClasspathExports(default_args_with_test_dir_), "no conversion");
447 #endif
448 }
449 
450 // Test jars with different sdk versions.
TEST_F(DeriveClasspathTest,SdkVersionsAreRespected)451 TEST_F(DeriveClasspathTest, SdkVersionsAreRespected) {
452 #ifndef SDKEXT_ANDROID
453   GTEST_SKIP();
454 #else
455   if (android_get_device_api_level() < __ANDROID_API_S__) {
456     GTEST_SKIP();
457   }
458 
459   // List of jars expected to be in SYSTEMSERVERCLASSPATH
460   std::vector<std::string> expected_jars;
461 
462   // Add an unbounded jar
463   AddJarToClasspath("/system", "/system/framework/unbounded", SYSTEMSERVERCLASSPATH);
464   expected_jars.push_back("/system/framework/unbounded");
465 
466   // Manually create a config with jars that set sdk versions...
467   ExportedClasspathsJars exported_jars;
468 
469   // known released versions:
470   Jar* jar = exported_jars.add_jars();
471   jar->set_path("/apex/com.android.foo/javalib/minsdk30");
472   jar->set_min_sdk_version(std::to_string(__ANDROID_API_R__));
473   jar->set_classpath(SYSTEMSERVERCLASSPATH);
474   expected_jars.push_back("/apex/com.android.foo/javalib/minsdk30");
475   jar = exported_jars.add_jars();
476   jar->set_path("/apex/com.android.foo/javalib/maxsdk30");
477   jar->set_max_sdk_version(std::to_string(__ANDROID_API_R__));
478   jar->set_classpath(SYSTEMSERVERCLASSPATH);
479 
480   // Device's reported version:
481   jar = exported_jars.add_jars();
482   jar->set_path("/apex/com.android.foo/javalib/minsdklatest");
483   jar->set_min_sdk_version(std::to_string(android_get_device_api_level()));
484   jar->set_classpath(SYSTEMSERVERCLASSPATH);
485   expected_jars.push_back("/apex/com.android.foo/javalib/minsdklatest");
486   jar = exported_jars.add_jars();
487   jar->set_path("/apex/com.android.foo/javalib/maxsdklatest");
488   jar->set_max_sdk_version(std::to_string(android_get_device_api_level()));
489   jar->set_classpath(SYSTEMSERVERCLASSPATH);
490   if ("REL" == android::base::GetProperty("ro.build.version.codename", "")) {
491     expected_jars.push_back("/apex/com.android.foo/javalib/maxsdklatest");
492   }
493 
494   // unknown SDK_INT+1 version
495   jar = exported_jars.add_jars();
496   jar->set_path("/apex/com.android.foo/javalib/minsdk_plus1");
497   jar->set_min_sdk_version(std::to_string(android_get_device_api_level() + 1));
498   jar->set_classpath(SYSTEMSERVERCLASSPATH);
499   jar = exported_jars.add_jars();
500   jar->set_path("/apex/com.android.foo/javalib/maxsdk_plus1");
501   jar->set_max_sdk_version(std::to_string(android_get_device_api_level() + 1));
502   jar->set_classpath(SYSTEMSERVERCLASSPATH);
503   expected_jars.push_back("/apex/com.android.foo/javalib/maxsdk_plus1");
504 
505   // known min_sdk_version and future max_sdk_version
506   jar = exported_jars.add_jars();
507   jar->set_path("/apex/com.android.foo/javalib/minsdk30maxsdk10000");
508   jar->set_min_sdk_version(std::to_string(__ANDROID_API_R__));
509   jar->set_max_sdk_version(std::to_string(android_get_device_api_level() + 1));
510   jar->set_classpath(SYSTEMSERVERCLASSPATH);
511   expected_jars.push_back("/apex/com.android.foo/javalib/minsdk30maxsdk10000");
512 
513   // codename
514   if ("REL" != android::base::GetProperty("ro.build.version.codename", "")) {
515     jar = exported_jars.add_jars();
516     jar->set_path("/apex/com.android.foo/javalib/minsdkS");
517     jar->set_min_sdk_version("S");
518     jar->set_classpath(SYSTEMSERVERCLASSPATH);
519     expected_jars.push_back("/apex/com.android.foo/javalib/minsdkS");
520 
521     jar = exported_jars.add_jars();
522     jar->set_path("/apex/com.android.foo/javalib/minsdkSv2");
523     jar->set_min_sdk_version("Sv2");
524     jar->set_classpath(SYSTEMSERVERCLASSPATH);
525     expected_jars.push_back("/apex/com.android.foo/javalib/minsdkSv2");
526 
527     jar = exported_jars.add_jars();
528     jar->set_path("/apex/com.android.foo/javalib/minsdkTiramisu");
529     jar->set_min_sdk_version("Tiramisu");
530     jar->set_classpath(SYSTEMSERVERCLASSPATH);
531     expected_jars.push_back("/apex/com.android.foo/javalib/minsdkTiramisu");
532 
533     jar = exported_jars.add_jars();
534     jar->set_path("/apex/com.android.foo/javalib/maxsdkS");
535     jar->set_max_sdk_version("S");
536     jar->set_classpath(SYSTEMSERVERCLASSPATH);
537 
538     jar = exported_jars.add_jars();
539     jar->set_path("/apex/com.android.foo/javalib/maxsdkSv2");
540     jar->set_max_sdk_version("Sv2");
541     jar->set_classpath(SYSTEMSERVERCLASSPATH);
542 
543     jar = exported_jars.add_jars();
544     jar->set_path("/apex/com.android.foo/javalib/maxsdkZFutureSdkVersion");
545     jar->set_max_sdk_version("ZFutureSdkVersion");
546     jar->set_classpath(SYSTEMSERVERCLASSPATH);
547     expected_jars.push_back("/apex/com.android.foo/javalib/maxsdkZFutureSdkVersion");
548   }
549 
550   // ...and write this config to systemserverclasspath.pb
551   WriteConfig(exported_jars, "/apex/com.android.foo/etc/classpaths/systemserverclasspath.pb");
552 
553   // Generate and parse SYSTEMSERVERCLASSPATH
554   GenerateClasspathExports(default_args_with_test_dir_);
555   const std::vector<std::string> exportLines = ParseExportsFile();
556   const std::vector<std::string> splitExportLine = SplitClasspathExportLine(exportLines[2]);
557   const std::string exportValue = splitExportLine[2];
558 
559   EXPECT_EQ(android::base::Join(expected_jars, ":"), exportValue);
560 #endif
561 }
562 
563 // Test jars with different sdk versions against override device values.
TEST_F(DeriveClasspathTest,SdkVersionsAreCheckedAgainstOverrideDeviceValuesRelease)564 TEST_F(DeriveClasspathTest, SdkVersionsAreCheckedAgainstOverrideDeviceValuesRelease) {
565   Args args = default_args_with_test_dir_;
566   args.override_device_sdk_version = 35;
567   args.override_device_codename = "REL";
568 
569   // List of jars expected to be in SYSTEMSERVERCLASSPATH.
570   std::vector<std::string> expected_jars;
571 
572   // Add an unbounded jar.
573   AddJarToClasspath("/system", "/system/framework/unbounded", SYSTEMSERVERCLASSPATH);
574   expected_jars.push_back("/system/framework/unbounded");
575 
576   // Manually create a config with jars that sets sdk versions...
577   ExportedClasspathsJars exported_jars;
578 
579   // Known released versions.
580   Jar* jar = exported_jars.add_jars();
581   jar->set_path("/apex/com.android.foo/javalib/minsdk30");
582   jar->set_min_sdk_version(std::to_string(__ANDROID_API_R__));
583   jar->set_classpath(SYSTEMSERVERCLASSPATH);
584   expected_jars.push_back("/apex/com.android.foo/javalib/minsdk30");
585   jar = exported_jars.add_jars();
586   jar->set_path("/apex/com.android.foo/javalib/maxsdk30");
587   jar->set_max_sdk_version(std::to_string(__ANDROID_API_R__));
588   jar->set_classpath(SYSTEMSERVERCLASSPATH);
589 
590   // Provided override device sdk version.
591   jar = exported_jars.add_jars();
592   jar->set_path("/apex/com.android.foo/javalib/minsdklatest");
593   jar->set_min_sdk_version(std::to_string(args.override_device_sdk_version));
594   jar->set_classpath(SYSTEMSERVERCLASSPATH);
595   expected_jars.push_back("/apex/com.android.foo/javalib/minsdklatest");
596   jar = exported_jars.add_jars();
597   jar->set_path("/apex/com.android.foo/javalib/maxsdklatest");
598   jar->set_max_sdk_version(std::to_string(args.override_device_sdk_version));
599   jar->set_classpath(SYSTEMSERVERCLASSPATH);
600   expected_jars.push_back("/apex/com.android.foo/javalib/maxsdklatest");
601 
602   // Unknown SDK_INT+1 version.
603   jar = exported_jars.add_jars();
604   jar->set_path("/apex/com.android.foo/javalib/minsdk_plus1");
605   jar->set_min_sdk_version(std::to_string(args.override_device_sdk_version + 1));
606   jar->set_classpath(SYSTEMSERVERCLASSPATH);
607   jar = exported_jars.add_jars();
608   jar->set_path("/apex/com.android.foo/javalib/maxsdk_plus1");
609   jar->set_max_sdk_version(std::to_string(args.override_device_sdk_version + 1));
610   jar->set_classpath(SYSTEMSERVERCLASSPATH);
611   expected_jars.push_back("/apex/com.android.foo/javalib/maxsdk_plus1");
612 
613   // Known min_sdk_version and future max_sdk_version.
614   jar = exported_jars.add_jars();
615   jar->set_path("/apex/com.android.foo/javalib/minsdk30maxsdk10000");
616   jar->set_min_sdk_version(std::to_string(__ANDROID_API_R__));
617   jar->set_max_sdk_version(std::to_string(args.override_device_sdk_version + 1));
618   jar->set_classpath(SYSTEMSERVERCLASSPATH);
619   expected_jars.push_back("/apex/com.android.foo/javalib/minsdk30maxsdk10000");
620 
621   // Codename.
622   jar = exported_jars.add_jars();
623   jar->set_path("/apex/com.android.foo/javalib/minsdkBaklava");
624   jar->set_min_sdk_version("Baklava");
625   jar->set_classpath(SYSTEMSERVERCLASSPATH);
626 
627   jar = exported_jars.add_jars();
628   jar->set_path("/apex/com.android.foo/javalib/maxsdkBaklava");
629   jar->set_max_sdk_version("Baklava");
630   jar->set_classpath(SYSTEMSERVERCLASSPATH);
631   expected_jars.push_back("/apex/com.android.foo/javalib/maxsdkBaklava");
632 
633   // ...and write this config to systemserverclasspath.pb.
634   WriteConfig(exported_jars, "/apex/com.android.foo/etc/classpaths/systemserverclasspath.pb");
635 
636   // Generate and parse SYSTEMSERVERCLASSPATH.
637   GenerateClasspathExports(args);
638   const std::vector<std::string> exportLines = ParseExportsFile();
639   const std::vector<std::string> splitExportLine = SplitClasspathExportLine(exportLines[2]);
640   const std::string exportValue = splitExportLine[2];
641 
642   EXPECT_EQ(android::base::Join(expected_jars, ":"), exportValue);
643 }
644 
645 // Test jars with different sdk versions against override device values.
TEST_F(DeriveClasspathTest,SdkVersionsAreCheckedAgainstOverrideDeviceValuesDev)646 TEST_F(DeriveClasspathTest, SdkVersionsAreCheckedAgainstOverrideDeviceValuesDev) {
647   Args args = default_args_with_test_dir_;
648   args.override_device_sdk_version = 35;
649   args.override_device_codename = "Baklava";
650   args.override_device_known_codenames = {
651       "S", "Sv2", "Tiramisu", "UpsideDownCake", "VanillaIceCream", "Baklava"};
652 
653   // List of jars expected to be in SYSTEMSERVERCLASSPATH.
654   std::vector<std::string> expected_jars;
655 
656   // Add an unbounded jar.
657   AddJarToClasspath("/system", "/system/framework/unbounded", SYSTEMSERVERCLASSPATH);
658   expected_jars.push_back("/system/framework/unbounded");
659 
660   // Manually create a config with jars that sets sdk versions...
661   ExportedClasspathsJars exported_jars;
662 
663   // Known released versions.
664   Jar* jar = exported_jars.add_jars();
665   jar->set_path("/apex/com.android.foo/javalib/minsdk30");
666   jar->set_min_sdk_version(std::to_string(__ANDROID_API_R__));
667   jar->set_classpath(SYSTEMSERVERCLASSPATH);
668   expected_jars.push_back("/apex/com.android.foo/javalib/minsdk30");
669   jar = exported_jars.add_jars();
670   jar->set_path("/apex/com.android.foo/javalib/maxsdk30");
671   jar->set_max_sdk_version(std::to_string(__ANDROID_API_R__));
672   jar->set_classpath(SYSTEMSERVERCLASSPATH);
673 
674   // Provided override device sdk version.
675   jar = exported_jars.add_jars();
676   jar->set_path("/apex/com.android.foo/javalib/minsdklatest");
677   jar->set_min_sdk_version(std::to_string(args.override_device_sdk_version));
678   jar->set_classpath(SYSTEMSERVERCLASSPATH);
679   expected_jars.push_back("/apex/com.android.foo/javalib/minsdklatest");
680   jar = exported_jars.add_jars();
681   jar->set_path("/apex/com.android.foo/javalib/maxsdklatest");
682   jar->set_max_sdk_version(std::to_string(args.override_device_sdk_version));
683   jar->set_classpath(SYSTEMSERVERCLASSPATH);
684 
685   // Unknown SDK_INT+1 version.
686   jar = exported_jars.add_jars();
687   jar->set_path("/apex/com.android.foo/javalib/minsdk_plus1");
688   jar->set_min_sdk_version(std::to_string(args.override_device_sdk_version + 1));
689   jar->set_classpath(SYSTEMSERVERCLASSPATH);
690   jar = exported_jars.add_jars();
691   jar->set_path("/apex/com.android.foo/javalib/maxsdk_plus1");
692   jar->set_max_sdk_version(std::to_string(args.override_device_sdk_version + 1));
693   jar->set_classpath(SYSTEMSERVERCLASSPATH);
694   expected_jars.push_back("/apex/com.android.foo/javalib/maxsdk_plus1");
695 
696   // Known min_sdk_version and future max_sdk_version.
697   jar = exported_jars.add_jars();
698   jar->set_path("/apex/com.android.foo/javalib/minsdk30maxsdk10000");
699   jar->set_min_sdk_version(std::to_string(__ANDROID_API_R__));
700   jar->set_max_sdk_version(std::to_string(args.override_device_sdk_version + 1));
701   jar->set_classpath(SYSTEMSERVERCLASSPATH);
702   expected_jars.push_back("/apex/com.android.foo/javalib/minsdk30maxsdk10000");
703 
704   // Codename.
705   jar = exported_jars.add_jars();
706   jar->set_path("/apex/com.android.foo/javalib/minsdkBaklava");
707   jar->set_min_sdk_version("Baklava");
708   jar->set_classpath(SYSTEMSERVERCLASSPATH);
709   expected_jars.push_back("/apex/com.android.foo/javalib/minsdkBaklava");
710 
711   jar = exported_jars.add_jars();
712   jar->set_path("/apex/com.android.foo/javalib/maxsdkBaklava");
713   jar->set_max_sdk_version("Baklava");
714   jar->set_classpath(SYSTEMSERVERCLASSPATH);
715   expected_jars.push_back("/apex/com.android.foo/javalib/maxsdkBaklava");
716 
717   jar = exported_jars.add_jars();
718   jar->set_path("/apex/com.android.foo/javalib/minsdkTiramisu");
719   jar->set_min_sdk_version("Tiramisu");
720   jar->set_classpath(SYSTEMSERVERCLASSPATH);
721   expected_jars.push_back("/apex/com.android.foo/javalib/minsdkTiramisu");
722 
723   jar = exported_jars.add_jars();
724   jar->set_path("/apex/com.android.foo/javalib/maxsdkTiramisu");
725   jar->set_max_sdk_version("Tiramisu");
726   jar->set_classpath(SYSTEMSERVERCLASSPATH);
727 
728   // ...and write this config to systemserverclasspath.pb.
729   WriteConfig(exported_jars, "/apex/com.android.foo/etc/classpaths/systemserverclasspath.pb");
730 
731   // Generate and parse SYSTEMSERVERCLASSPATH.
732   GenerateClasspathExports(args);
733   const std::vector<std::string> exportLines = ParseExportsFile();
734   const std::vector<std::string> splitExportLine = SplitClasspathExportLine(exportLines[2]);
735   const std::string exportValue = splitExportLine[2];
736 
737   EXPECT_EQ(android::base::Join(expected_jars, ":"), exportValue);
738 }
739 
740 }  // namespace
741 }  // namespace derive_classpath
742 }  // namespace android
743 
main(int argc,char ** argv)744 int main(int argc, char** argv) {
745   ::testing::InitGoogleTest(&argc, argv);
746   // Required for EXPECT_DEATH to work correctly
747   android::base::SetLogger(android::base::StderrLogger);
748   return RUN_ALL_TESTS();
749 }
750