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 <android-modules-utils/sdk_level.h>
25 #include <android/api-level.h>
26 #include <gtest/gtest.h>
27 #include <stdlib.h>
28 #include <sys/mman.h>
29 #include <sys/stat.h>
30
31 #include <cstdlib>
32 #include <string_view>
33
34 #include "android-base/unique_fd.h"
35 #include "packages/modules/common/proto/classpaths.pb.h"
36
37 namespace android {
38 namespace derive_classpath {
39 namespace {
40
41 static const std::string kFrameworkJarFilepath = "/system/framework/framework.jar";
42 static const std::string kLibartJarFilepath = "/apex/com.android.art/javalib/core-libart.jar";
43 static const std::string kSdkExtensionsJarFilepath =
44 "/apex/com.android.sdkext/javalib/framework-sdkextensions.jar";
45 static const std::string kServicesJarFilepath = "/system/framework/services.jar";
46
47 // The fixture for testing derive_classpath.
48 class DeriveClasspathTest : public ::testing::Test {
49 protected:
~DeriveClasspathTest()50 ~DeriveClasspathTest() override {
51 // Not really needed, as a test device will re-generate a proper classpath on reboot,
52 // but it's better to leave it in a clean state after a test.
53 GenerateClasspathExports(default_args_);
54 }
55
working_dir()56 const std::string working_dir() { return std::string(temp_dir_.path); }
57
58 // Parses the generated classpath exports file and returns each line individually.
ParseExportsFile(const char * file="/data/system/environ/classpath")59 std::vector<std::string> ParseExportsFile(const char* file = "/data/system/environ/classpath") {
60 std::string contents;
61 EXPECT_TRUE(android::base::ReadFileToString(file, &contents, /*follow_symlinks=*/true));
62 return android::base::Split(contents, "\n");
63 }
64
SplitClasspathExportLine(const std::string & line)65 std::vector<std::string> SplitClasspathExportLine(const std::string& line) {
66 std::vector<std::string> contents = android::base::Split(line, " ");
67 // Export lines are expected to be structured as `export <name> <value>`.
68 EXPECT_EQ(3, contents.size());
69 EXPECT_EQ("export", contents[0]);
70 return contents;
71 }
72
73 // Checks the order of the jars in a given classpath.
74 // Instead of doing a full order check, it assumes the jars are grouped by partition and checks
75 // that partitions come in order of the `prefixes` that is given.
CheckClasspathGroupOrder(const std::string classpath,const std::vector<std::string> prefixes)76 void CheckClasspathGroupOrder(const std::string classpath,
77 const std::vector<std::string> prefixes) {
78 ASSERT_NE(0, prefixes.size());
79 ASSERT_NE(0, classpath.size());
80
81 auto jars = android::base::Split(classpath, ":");
82
83 auto prefix = prefixes.begin();
84 auto jar = jars.begin();
85 for (; jar != jars.end() && prefix != prefixes.end(); ++jar) {
86 if (*jar == "/apex/com.android.i18n/javalib/core-icu4j.jar") {
87 // core-icu4j.jar is special and is out of order in BOOTCLASSPATH;
88 // ignore it when checking for general order
89 continue;
90 }
91 if (!android::base::StartsWith(*jar, *prefix)) {
92 ++prefix;
93 }
94 }
95 EXPECT_NE(prefix, prefixes.end());
96 // All jars have been iterated over, thus they all have valid prefixes
97 EXPECT_EQ(jar, jars.end());
98 }
99
WriteConfig(const ExportedClasspathsJars & exported_jars,const std::string & path)100 void WriteConfig(const ExportedClasspathsJars& exported_jars, const std::string& path) {
101 std::string fragment_path = working_dir() + path;
102 std::string buf;
103 exported_jars.SerializeToString(&buf);
104 std::string cmd("mkdir -p " + android::base::Dirname(fragment_path));
105 ASSERT_EQ(0, system(cmd.c_str()));
106 ASSERT_TRUE(android::base::WriteStringToFile(buf, fragment_path, true));
107 }
108
AddJarToClasspath(const std::string & partition,const std::string & jar_filepath,Classpath classpath)109 void AddJarToClasspath(const std::string& partition, const std::string& jar_filepath,
110 Classpath classpath) {
111 ExportedClasspathsJars exported_jars;
112 Jar* jar = exported_jars.add_jars();
113 jar->set_path(jar_filepath);
114 jar->set_classpath(classpath);
115
116 std::string basename = Classpath_Name(classpath) + ".pb";
117 std::transform(basename.begin(), basename.end(), basename.begin(),
118 [](unsigned char c) { return std::tolower(c); });
119
120 WriteConfig(exported_jars, partition + "/etc/classpaths/" + basename);
121 }
122
123 const TemporaryDir temp_dir_;
124
125 const Args default_args_ = {
126 .output_path = kGeneratedClasspathExportsFilepath,
127 };
128
129 const Args default_args_with_test_dir_ = {
130 .output_path = kGeneratedClasspathExportsFilepath,
131 .glob_pattern_prefix = temp_dir_.path,
132 };
133 };
134
135 using DeriveClasspathDeathTest = DeriveClasspathTest;
136
137 // Check only known *CLASSPATH variables are exported.
TEST_F(DeriveClasspathTest,DefaultNoUnknownClasspaths)138 TEST_F(DeriveClasspathTest, DefaultNoUnknownClasspaths) {
139 // Re-generate default on device classpaths
140 GenerateClasspathExports(default_args_);
141
142 const std::vector<std::string> exportLines = ParseExportsFile();
143 // The first four lines are tested below.
144 for (int i = 4; i < exportLines.size(); i++) {
145 EXPECT_EQ(exportLines[i], "");
146 }
147 }
148
149 // Test that all variables are properly generated.
TEST_F(DeriveClasspathTest,AllVariables)150 TEST_F(DeriveClasspathTest, AllVariables) {
151 ExportedClasspathsJars exported_jars;
152 Jar* jar = exported_jars.add_jars();
153 jar->set_path("/apex/com.android.foo/javalib/foo");
154 jar->set_classpath(BOOTCLASSPATH);
155 jar = exported_jars.add_jars();
156 jar->set_path("/apex/com.android.bar/javalib/bar");
157 jar->set_classpath(DEX2OATBOOTCLASSPATH);
158 WriteConfig(exported_jars, "/system/etc/classpaths/bootclasspath.pb");
159
160 exported_jars.clear_jars();
161 jar = exported_jars.add_jars();
162 jar->set_path("/apex/com.android.baz/javalib/baz");
163 jar->set_classpath(SYSTEMSERVERCLASSPATH);
164 jar = exported_jars.add_jars();
165 jar->set_path("/apex/com.android.qux/javalib/qux");
166 jar->set_classpath(STANDALONE_SYSTEMSERVER_JARS);
167 WriteConfig(exported_jars, "/system/etc/classpaths/systemserverclasspath.pb");
168
169 ASSERT_TRUE(GenerateClasspathExports(default_args_with_test_dir_));
170
171 const std::vector<std::string> exportLines = ParseExportsFile();
172 std::vector<std::string> splitExportLine;
173
174 splitExportLine = SplitClasspathExportLine(exportLines[0]);
175 EXPECT_EQ("BOOTCLASSPATH", splitExportLine[1]);
176 EXPECT_EQ("/apex/com.android.foo/javalib/foo", splitExportLine[2]);
177 splitExportLine = SplitClasspathExportLine(exportLines[1]);
178 EXPECT_EQ("DEX2OATBOOTCLASSPATH", splitExportLine[1]);
179 EXPECT_EQ("/apex/com.android.bar/javalib/bar", splitExportLine[2]);
180 splitExportLine = SplitClasspathExportLine(exportLines[2]);
181 EXPECT_EQ("SYSTEMSERVERCLASSPATH", splitExportLine[1]);
182 EXPECT_EQ("/apex/com.android.baz/javalib/baz", splitExportLine[2]);
183 splitExportLine = SplitClasspathExportLine(exportLines[3]);
184 EXPECT_EQ("STANDALONE_SYSTEMSERVER_JARS", splitExportLine[1]);
185 EXPECT_EQ("/apex/com.android.qux/javalib/qux", splitExportLine[2]);
186 }
187
188 // Test that temp directory does not pick up actual jars.
TEST_F(DeriveClasspathTest,TempConfig)189 TEST_F(DeriveClasspathTest, TempConfig) {
190 AddJarToClasspath("/apex/com.android.foo", "/apex/com.android.foo/javalib/foo", BOOTCLASSPATH);
191 AddJarToClasspath("/apex/com.android.baz", "/apex/com.android.baz/javalib/baz",
192 SYSTEMSERVERCLASSPATH);
193
194 ASSERT_TRUE(GenerateClasspathExports(default_args_with_test_dir_));
195
196 const std::vector<std::string> exportLines = ParseExportsFile();
197
198 std::vector<std::string> splitExportLine;
199
200 splitExportLine = SplitClasspathExportLine(exportLines[0]);
201 EXPECT_EQ("BOOTCLASSPATH", splitExportLine[1]);
202 EXPECT_EQ("/apex/com.android.foo/javalib/foo", splitExportLine[2]);
203 splitExportLine = SplitClasspathExportLine(exportLines[2]);
204 EXPECT_EQ("SYSTEMSERVERCLASSPATH", splitExportLine[1]);
205 EXPECT_EQ("/apex/com.android.baz/javalib/baz", splitExportLine[2]);
206 }
207
208 // Test individual modules are sorted by pathnames.
TEST_F(DeriveClasspathTest,ModulesAreSorted)209 TEST_F(DeriveClasspathTest, ModulesAreSorted) {
210 AddJarToClasspath("/apex/com.android.art", "/apex/com.android.art/javalib/art", BOOTCLASSPATH);
211 AddJarToClasspath("/system", "/system/framework/jar", BOOTCLASSPATH);
212 AddJarToClasspath("/apex/com.android.foo", "/apex/com.android.foo/javalib/foo", BOOTCLASSPATH);
213 AddJarToClasspath("/apex/com.android.bar", "/apex/com.android.bar/javalib/bar", BOOTCLASSPATH);
214 AddJarToClasspath("/apex/com.android.baz", "/apex/com.android.baz/javalib/baz", BOOTCLASSPATH);
215
216 ASSERT_TRUE(GenerateClasspathExports(default_args_with_test_dir_));
217
218 const std::vector<std::string> exportLines = ParseExportsFile();
219 const std::vector<std::string> splitExportLine = SplitClasspathExportLine(exportLines[0]);
220 const std::string exportValue = splitExportLine[2];
221
222 const std::string expectedJars(
223 "/apex/com.android.art/javalib/art"
224 ":/system/framework/jar"
225 ":/apex/com.android.bar/javalib/bar"
226 ":/apex/com.android.baz/javalib/baz"
227 ":/apex/com.android.foo/javalib/foo");
228
229 EXPECT_EQ(expectedJars, exportValue);
230 }
231
232 // Test we can output to custom files.
TEST_F(DeriveClasspathTest,CustomOutputLocation)233 TEST_F(DeriveClasspathTest, CustomOutputLocation) {
234 AddJarToClasspath("/apex/com.android.art", "/apex/com.android.art/javalib/art", BOOTCLASSPATH);
235 AddJarToClasspath("/system", "/system/framework/jar", BOOTCLASSPATH);
236 AddJarToClasspath("/apex/com.android.foo", "/apex/com.android.foo/javalib/foo", BOOTCLASSPATH);
237 AddJarToClasspath("/apex/com.android.bar", "/apex/com.android.bar/javalib/bar", BOOTCLASSPATH);
238 AddJarToClasspath("/apex/com.android.baz", "/apex/com.android.baz/javalib/baz", BOOTCLASSPATH);
239
240 android::base::unique_fd fd(memfd_create("temp_file", MFD_CLOEXEC));
241 ASSERT_TRUE(fd.ok()) << "Unable to open temp-file";
242 const std::string file_name = android::base::StringPrintf("/proc/self/fd/%d", fd.get());
243 Args args = {
244 .output_path = file_name,
245 .glob_pattern_prefix = working_dir(),
246 };
247 ASSERT_TRUE(GenerateClasspathExports(args));
248
249 const std::vector<std::string> exportLines = ParseExportsFile(file_name.c_str());
250 const std::vector<std::string> splitExportLine = SplitClasspathExportLine(exportLines[0]);
251 const std::string exportValue = splitExportLine[2];
252
253 const std::string expectedJars(
254 "/apex/com.android.art/javalib/art"
255 ":/system/framework/jar"
256 ":/apex/com.android.bar/javalib/bar"
257 ":/apex/com.android.baz/javalib/baz"
258 ":/apex/com.android.foo/javalib/foo");
259
260 EXPECT_EQ(expectedJars, exportValue);
261 }
262
263 // Test alternative .pb for bootclasspath and systemclasspath.
TEST_F(DeriveClasspathTest,CustomInputLocation)264 TEST_F(DeriveClasspathTest, CustomInputLocation) {
265 AddJarToClasspath("/other", "/other/bcp-jar", BOOTCLASSPATH);
266 AddJarToClasspath("/other", "/other/systemserver-jar", SYSTEMSERVERCLASSPATH);
267 AddJarToClasspath("/apex/com.android.art", "/apex/com.android.art/javalib/art", BOOTCLASSPATH);
268 AddJarToClasspath("/apex/com.android.foo", "/apex/com.android.foo/javalib/foo", BOOTCLASSPATH);
269 AddJarToClasspath("/apex/com.android.baz", "/apex/com.android.baz/javalib/baz",
270 SYSTEMSERVERCLASSPATH);
271
272 Args args = default_args_with_test_dir_;
273 args.system_bootclasspath_fragment = "/other/etc/classpaths/bootclasspath.pb";
274 args.system_systemserverclasspath_fragment = "/other/etc/classpaths/systemserverclasspath.pb";
275
276 ASSERT_TRUE(GenerateClasspathExports(args));
277
278 const std::vector<std::string> exportLines = ParseExportsFile();
279
280 std::vector<std::string> splitExportLine;
281 splitExportLine = SplitClasspathExportLine(exportLines[0]);
282 EXPECT_EQ("BOOTCLASSPATH", splitExportLine[1]);
283 const std::string expectedBcpJars(
284 "/apex/com.android.art/javalib/art"
285 ":/other/bcp-jar"
286 ":/apex/com.android.foo/javalib/foo");
287 EXPECT_EQ(expectedBcpJars, splitExportLine[2]);
288
289 splitExportLine = SplitClasspathExportLine(exportLines[2]);
290 EXPECT_EQ("SYSTEMSERVERCLASSPATH", splitExportLine[1]);
291 const std::string expectedSystemServerJars(
292 "/other/systemserver-jar"
293 ":/apex/com.android.baz/javalib/baz");
294 EXPECT_EQ(expectedSystemServerJars, splitExportLine[2]);
295 }
296
297 // Test output location that can't be written to.
TEST_F(DeriveClasspathTest,NonWriteableOutputLocation)298 TEST_F(DeriveClasspathTest, NonWriteableOutputLocation) {
299 AddJarToClasspath("/apex/com.android.art", "/apex/com.android.art/javalib/art", BOOTCLASSPATH);
300 AddJarToClasspath("/system", "/system/framework/jar", BOOTCLASSPATH);
301
302 Args args = {
303 .output_path = "/system/non_writable_path",
304 .glob_pattern_prefix = working_dir(),
305 };
306 ASSERT_FALSE(GenerateClasspathExports(args));
307 }
308
TEST_F(DeriveClasspathTest,ScanOnlySpecificDirectories)309 TEST_F(DeriveClasspathTest, ScanOnlySpecificDirectories) {
310 AddJarToClasspath("/system", "/system/framework/jar", BOOTCLASSPATH);
311 AddJarToClasspath("/apex/com.android.foo", "/apex/com.android.foo/javalib/foo", BOOTCLASSPATH);
312 AddJarToClasspath("/apex/com.android.foo", "/apex/com.android.foo/javalib/sys",
313 SYSTEMSERVERCLASSPATH);
314 AddJarToClasspath("/apex/com.android.bar", "/apex/com.android.bar/javalib/bar", BOOTCLASSPATH);
315 AddJarToClasspath("/apex/com.android.baz", "/apex/com.android.baz/javalib/baz", BOOTCLASSPATH);
316
317 auto args_with_scan_dirs = default_args_with_test_dir_;
318 args_with_scan_dirs.scan_dirs.push_back("/apex/com.android.foo");
319 args_with_scan_dirs.scan_dirs.push_back("/apex/com.android.bar");
320 ASSERT_TRUE(GenerateClasspathExports(args_with_scan_dirs));
321
322 const std::vector<std::string> exportLines = ParseExportsFile();
323
324 std::vector<std::string> splitExportLine;
325
326 splitExportLine = SplitClasspathExportLine(exportLines[0]);
327 EXPECT_EQ("BOOTCLASSPATH", splitExportLine[1]);
328 // Not sorted. Maintains the ordering provided in scan_dirs
329 const std::string expectedJars(
330 "/apex/com.android.foo/javalib/foo"
331 ":/apex/com.android.bar/javalib/bar");
332 EXPECT_EQ(expectedJars, splitExportLine[2]);
333 splitExportLine = SplitClasspathExportLine(exportLines[2]);
334 EXPECT_EQ("SYSTEMSERVERCLASSPATH", splitExportLine[1]);
335 EXPECT_EQ("/apex/com.android.foo/javalib/sys", splitExportLine[2]);
336 }
337
338 // Test apexes only export their own jars.
TEST_F(DeriveClasspathDeathTest,ApexJarsBelongToApex)339 TEST_F(DeriveClasspathDeathTest, ApexJarsBelongToApex) {
340 // EXPECT_DEATH expects error messages in stderr, log there
341 android::base::SetLogger(android::base::StderrLogger);
342
343 AddJarToClasspath("/system", "/system/framework/jar", BOOTCLASSPATH);
344 ASSERT_TRUE(GenerateClasspathExports(default_args_with_test_dir_));
345
346 AddJarToClasspath("/apex/com.android.foo", "/apex/com.android.foo/javalib/foo", BOOTCLASSPATH);
347 ASSERT_TRUE(GenerateClasspathExports(default_args_with_test_dir_));
348
349 AddJarToClasspath("/apex/com.android.bar@12345.tmp", "/apex/com.android.bar/javalib/bar",
350 BOOTCLASSPATH);
351 ASSERT_TRUE(GenerateClasspathExports(default_args_with_test_dir_));
352
353 AddJarToClasspath("/apex/com.android.baz@12345", "/apex/this/path/is/skipped", BOOTCLASSPATH);
354 ASSERT_TRUE(GenerateClasspathExports(default_args_with_test_dir_));
355
356 AddJarToClasspath("/apex/com.android.bar", "/apex/wrong/path/bar", BOOTCLASSPATH);
357 EXPECT_DEATH(GenerateClasspathExports(default_args_with_test_dir_),
358 "must not export a jar.*wrong/path/bar");
359 }
360
361 // Test only bind mounted apexes are skipped
TEST_F(DeriveClasspathTest,OnlyBindMountedApexIsSkipped)362 TEST_F(DeriveClasspathTest, OnlyBindMountedApexIsSkipped) {
363 AddJarToClasspath("/system", "/system/framework/jar", BOOTCLASSPATH);
364 // Normal APEX with format: /apex/<module-name>/*
365 AddJarToClasspath("/apex/com.android.foo", "/apex/com.android.foo/javalib/foo", BOOTCLASSPATH);
366 // Bind mounted APEX with format: /apex/<module-name>@<version>/*
367 AddJarToClasspath("/apex/com.android.bar@123", "/apex/com.android.bar/javalib/bar",
368 BOOTCLASSPATH);
369 // Temp mounted APEX with format: /apex/<module-name>@<version>.tmp/*
370 AddJarToClasspath("/apex/com.android.baz@123.tmp", "/apex/com.android.baz/javalib/baz",
371 BOOTCLASSPATH);
372
373 ASSERT_TRUE(GenerateClasspathExports(default_args_with_test_dir_));
374
375 const std::vector<std::string> exportLines = ParseExportsFile();
376
377 std::vector<std::string> splitExportLine;
378
379 splitExportLine = SplitClasspathExportLine(exportLines[0]);
380 EXPECT_EQ("BOOTCLASSPATH", splitExportLine[1]);
381 const std::string expectedJars(
382 "/system/framework/jar"
383 ":/apex/com.android.baz/javalib/baz"
384 ":/apex/com.android.foo/javalib/foo");
385 EXPECT_EQ(expectedJars, splitExportLine[2]);
386 }
387
388 // Test classpath fragments export jars for themselves.
TEST_F(DeriveClasspathDeathTest,WrongClasspathInFragments)389 TEST_F(DeriveClasspathDeathTest, WrongClasspathInFragments) {
390 // Valid configs
391 AddJarToClasspath("/system", "/system/framework/framework-jar", BOOTCLASSPATH);
392 AddJarToClasspath("/system", "/system/framework/service-jar", SYSTEMSERVERCLASSPATH);
393
394 // Manually create an invalid config with both BCP and SSCP jars...
395 ExportedClasspathsJars exported_jars;
396 Jar* jar = exported_jars.add_jars();
397 jar->set_path("/apex/com.android.foo/javalib/foo");
398 jar->set_classpath(BOOTCLASSPATH);
399 // note that DEX2OATBOOTCLASSPATH and BOOTCLASSPATH jars are expected to be in the same config
400 jar = exported_jars.add_jars();
401 jar->set_path("/apex/com.android.foo/javalib/foo");
402 jar->set_classpath(DEX2OATBOOTCLASSPATH);
403 jar = exported_jars.add_jars();
404 jar->set_path("/apex/com.android.foo/javalib/service-foo");
405 jar->set_classpath(SYSTEMSERVERCLASSPATH);
406
407 // ...and write this config to bootclasspath.pb
408 WriteConfig(exported_jars, "/apex/com.android.foo/etc/classpaths/bootclasspath.pb");
409
410 EXPECT_DEATH(GenerateClasspathExports(default_args_with_test_dir_),
411 "must not export a jar for SYSTEMSERVERCLASSPATH");
412 }
413
TEST_F(DeriveClasspathDeathTest,CurrentSdkVersion)414 TEST_F(DeriveClasspathDeathTest, CurrentSdkVersion) {
415 if (android_get_device_api_level() < __ANDROID_API_S__) {
416 GTEST_SKIP();
417 }
418
419 ExportedClasspathsJars exported_jars;
420 Jar* jar = exported_jars.add_jars();
421 jar->set_path("/apex/com.android.foo/javalib/minsdkcurrent");
422 jar->set_min_sdk_version("current");
423 jar->set_classpath(SYSTEMSERVERCLASSPATH);
424 WriteConfig(exported_jars, "/apex/com.android.foo/etc/classpaths/systemserverclasspath.pb");
425
426 EXPECT_DEATH(GenerateClasspathExports(default_args_with_test_dir_), "no conversion");
427 }
428
429 // Test jars with different sdk versions.
TEST_F(DeriveClasspathTest,SdkVersionsAreRespected)430 TEST_F(DeriveClasspathTest, SdkVersionsAreRespected) {
431 if (android_get_device_api_level() < __ANDROID_API_S__) {
432 GTEST_SKIP();
433 }
434
435 // List of jars expected to be in SYSTEMSERVERCLASSPATH
436 std::vector<std::string> expected_jars;
437
438 // Add an unbounded jar
439 AddJarToClasspath("/system", "/system/framework/unbounded", SYSTEMSERVERCLASSPATH);
440 expected_jars.push_back("/system/framework/unbounded");
441
442 // Manually create a config with jars that set sdk versions...
443 ExportedClasspathsJars exported_jars;
444
445 // known released versions:
446 Jar* jar = exported_jars.add_jars();
447 jar->set_path("/apex/com.android.foo/javalib/minsdk30");
448 jar->set_min_sdk_version(std::to_string(__ANDROID_API_R__));
449 jar->set_classpath(SYSTEMSERVERCLASSPATH);
450 expected_jars.push_back("/apex/com.android.foo/javalib/minsdk30");
451 jar = exported_jars.add_jars();
452 jar->set_path("/apex/com.android.foo/javalib/maxsdk30");
453 jar->set_max_sdk_version(std::to_string(__ANDROID_API_R__));
454 jar->set_classpath(SYSTEMSERVERCLASSPATH);
455
456 // Device's reported version:
457 jar = exported_jars.add_jars();
458 jar->set_path("/apex/com.android.foo/javalib/minsdklatest");
459 jar->set_min_sdk_version(std::to_string(android_get_device_api_level()));
460 jar->set_classpath(SYSTEMSERVERCLASSPATH);
461 expected_jars.push_back("/apex/com.android.foo/javalib/minsdklatest");
462 jar = exported_jars.add_jars();
463 jar->set_path("/apex/com.android.foo/javalib/maxsdklatest");
464 jar->set_max_sdk_version(std::to_string(android_get_device_api_level()));
465 jar->set_classpath(SYSTEMSERVERCLASSPATH);
466 if ("REL" == android::base::GetProperty("ro.build.version.codename", "")) {
467 expected_jars.push_back("/apex/com.android.foo/javalib/maxsdklatest");
468 }
469
470 // unknown SDK_INT+1 version
471 jar = exported_jars.add_jars();
472 jar->set_path("/apex/com.android.foo/javalib/minsdk_plus1");
473 jar->set_min_sdk_version(std::to_string(android_get_device_api_level() + 1));
474 jar->set_classpath(SYSTEMSERVERCLASSPATH);
475 jar = exported_jars.add_jars();
476 jar->set_path("/apex/com.android.foo/javalib/maxsdk_plus1");
477 jar->set_max_sdk_version(std::to_string(android_get_device_api_level() + 1));
478 jar->set_classpath(SYSTEMSERVERCLASSPATH);
479 expected_jars.push_back("/apex/com.android.foo/javalib/maxsdk_plus1");
480
481 // known min_sdk_version and future max_sdk_version
482 jar = exported_jars.add_jars();
483 jar->set_path("/apex/com.android.foo/javalib/minsdk30maxsdk10000");
484 jar->set_min_sdk_version(std::to_string(__ANDROID_API_R__));
485 jar->set_max_sdk_version(std::to_string(android_get_device_api_level() + 1));
486 jar->set_classpath(SYSTEMSERVERCLASSPATH);
487 expected_jars.push_back("/apex/com.android.foo/javalib/minsdk30maxsdk10000");
488
489 // codename
490 if ("REL" != android::base::GetProperty("ro.build.version.codename", "")) {
491 jar = exported_jars.add_jars();
492 jar->set_path("/apex/com.android.foo/javalib/minsdkS");
493 jar->set_min_sdk_version("S");
494 jar->set_classpath(SYSTEMSERVERCLASSPATH);
495 expected_jars.push_back("/apex/com.android.foo/javalib/minsdkS");
496
497 jar = exported_jars.add_jars();
498 jar->set_path("/apex/com.android.foo/javalib/minsdkSv2");
499 jar->set_min_sdk_version("Sv2");
500 jar->set_classpath(SYSTEMSERVERCLASSPATH);
501 expected_jars.push_back("/apex/com.android.foo/javalib/minsdkSv2");
502
503 jar = exported_jars.add_jars();
504 jar->set_path("/apex/com.android.foo/javalib/minsdkTiramisu");
505 jar->set_min_sdk_version("Tiramisu");
506 jar->set_classpath(SYSTEMSERVERCLASSPATH);
507 expected_jars.push_back("/apex/com.android.foo/javalib/minsdkTiramisu");
508
509 jar = exported_jars.add_jars();
510 jar->set_path("/apex/com.android.foo/javalib/maxsdkS");
511 jar->set_max_sdk_version("S");
512 jar->set_classpath(SYSTEMSERVERCLASSPATH);
513
514 jar = exported_jars.add_jars();
515 jar->set_path("/apex/com.android.foo/javalib/maxsdkSv2");
516 jar->set_max_sdk_version("Sv2");
517 jar->set_classpath(SYSTEMSERVERCLASSPATH);
518
519 jar = exported_jars.add_jars();
520 jar->set_path("/apex/com.android.foo/javalib/maxsdkZFutureSdkVersion");
521 jar->set_max_sdk_version("ZFutureSdkVersion");
522 jar->set_classpath(SYSTEMSERVERCLASSPATH);
523 expected_jars.push_back("/apex/com.android.foo/javalib/maxsdkZFutureSdkVersion");
524 }
525
526 // ...and write this config to systemserverclasspath.pb
527 WriteConfig(exported_jars, "/apex/com.android.foo/etc/classpaths/systemserverclasspath.pb");
528
529 // Generate and parse SYSTEMSERVERCLASSPATH
530 GenerateClasspathExports(default_args_with_test_dir_);
531 const std::vector<std::string> exportLines = ParseExportsFile();
532 const std::vector<std::string> splitExportLine = SplitClasspathExportLine(exportLines[2]);
533 const std::string exportValue = splitExportLine[2];
534
535 EXPECT_EQ(android::base::Join(expected_jars, ":"), exportValue);
536 }
537
538 } // namespace
539 } // namespace derive_classpath
540 } // namespace android
541
main(int argc,char ** argv)542 int main(int argc, char** argv) {
543 ::testing::InitGoogleTest(&argc, argv);
544 // Required for EXPECT_DEATH to work correctly
545 android::base::SetLogger(android::base::StderrLogger);
546 return RUN_ALL_TESTS();
547 }
548