1 /*
2 * Copyright (C) 2020 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 "apex_file_repository.h"
18
19 #include <android-base/file.h>
20 #include <android-base/logging.h>
21 #include <android-base/properties.h>
22 #include <android-base/result-gmock.h>
23 #include <android-base/stringprintf.h>
24 #include <errno.h>
25 #include <gmock/gmock.h>
26 #include <gtest/gtest.h>
27 #include <microdroid/metadata.h>
28 #include <sys/stat.h>
29
30 #include <filesystem>
31 #include <string>
32
33 #include "apex_blocklist.h"
34 #include "apex_constants.h"
35 #include "apex_file.h"
36 #include "apexd.h"
37 #include "apexd_brand_new_verifier.h"
38 #include "apexd_metrics.h"
39 #include "apexd_private.h"
40 #include "apexd_test_utils.h"
41 #include "apexd_verity.h"
42
43 namespace android {
44 namespace apex {
45
46 using namespace std::literals;
47
48 namespace fs = std::filesystem;
49
50 using android::apex::testing::ApexFileEq;
51 using android::base::StringPrintf;
52 using android::base::testing::Ok;
53 using ::testing::ByRef;
54 using ::testing::ContainerEq;
55 using ::testing::Not;
56 using ::testing::UnorderedElementsAre;
57
58 namespace {
59 // Copies the compressed apex to |built_in_dir| and decompresses it to
60 // |decompression_dir
PrepareCompressedApex(const std::string & name,const std::string & built_in_dir,const std::string & decompression_dir)61 void PrepareCompressedApex(const std::string& name,
62 const std::string& built_in_dir,
63 const std::string& decompression_dir) {
64 fs::copy(GetTestFile(name), built_in_dir);
65 auto compressed_apex =
66 ApexFile::Open(StringPrintf("%s/%s", built_in_dir.c_str(), name.c_str()));
67
68 const auto& pkg_name = compressed_apex->GetManifest().name();
69 const int version = compressed_apex->GetManifest().version();
70
71 auto decompression_path =
72 StringPrintf("%s/%s@%d%s", decompression_dir.c_str(), pkg_name.c_str(),
73 version, kDecompressedApexPackageSuffix);
74 compressed_apex->Decompress(decompression_path);
75 }
76 } // namespace
77
TEST(ApexFileRepositoryTest,InitializeSuccess)78 TEST(ApexFileRepositoryTest, InitializeSuccess) {
79 // Prepare test data.
80 TemporaryDir built_in_dir, data_dir, decompression_dir;
81 fs::copy(GetTestFile("apex.apexd_test.apex"), built_in_dir.path);
82 fs::copy(GetTestFile("apex.apexd_test_different_app.apex"),
83 built_in_dir.path);
84 ApexPartition partition = ApexPartition::System;
85
86 fs::copy(GetTestFile("apex.apexd_test.apex"), data_dir.path);
87 fs::copy(GetTestFile("apex.apexd_test_different_app.apex"), data_dir.path);
88
89 ApexFileRepository instance;
90 ASSERT_RESULT_OK(
91 instance.AddPreInstalledApex({{partition, built_in_dir.path}}));
92 ASSERT_RESULT_OK(instance.AddDataApex(data_dir.path));
93
94 // Now test that apexes were scanned correctly;
95 auto test_fn = [&](const std::string& apex_name) {
96 auto apex = ApexFile::Open(GetTestFile(apex_name));
97 ASSERT_RESULT_OK(apex);
98
99 {
100 auto ret = instance.GetPublicKey(apex->GetManifest().name());
101 ASSERT_RESULT_OK(ret);
102 ASSERT_EQ(apex->GetBundledPublicKey(), *ret);
103 }
104
105 {
106 auto ret = instance.GetPreinstalledPath(apex->GetManifest().name());
107 ASSERT_RESULT_OK(ret);
108 ASSERT_EQ(StringPrintf("%s/%s", built_in_dir.path, apex_name.c_str()),
109 *ret);
110 }
111
112 {
113 auto ret = instance.GetPartition(*apex);
114 ASSERT_RESULT_OK(ret);
115 ASSERT_EQ(partition, *ret);
116 }
117
118 ASSERT_TRUE(instance.HasPreInstalledVersion(apex->GetManifest().name()));
119 ASSERT_TRUE(instance.HasDataVersion(apex->GetManifest().name()));
120 };
121
122 test_fn("apex.apexd_test.apex");
123 test_fn("apex.apexd_test_different_app.apex");
124
125 // Check that second call will succeed as well.
126 ASSERT_RESULT_OK(
127 instance.AddPreInstalledApex({{partition, built_in_dir.path}}));
128 ASSERT_RESULT_OK(instance.AddDataApex(data_dir.path));
129
130 test_fn("apex.apexd_test.apex");
131 test_fn("apex.apexd_test_different_app.apex");
132 }
133
TEST(ApexFileRepositoryTest,AddPreInstalledApexParallel)134 TEST(ApexFileRepositoryTest, AddPreInstalledApexParallel) {
135 TemporaryDir built_in_dir;
136 fs::copy(GetTestFile("apex.apexd_test.apex"), built_in_dir.path);
137 fs::copy(GetTestFile("apex.apexd_test_different_app.apex"),
138 built_in_dir.path);
139 ApexPartition partition = ApexPartition::System;
140 std::unordered_map<ApexPartition, std::string> apex_dir = {
141 {partition, built_in_dir.path}};
142
143 ApexFileRepository instance0;
144 instance0.AddPreInstalledApex(apex_dir);
145 auto expected = instance0.GetPreInstalledApexFiles();
146
147 ApexFileRepository instance;
148 ASSERT_RESULT_OK(instance.AddPreInstalledApexParallel(apex_dir));
149 auto actual = instance.GetPreInstalledApexFiles();
150 ASSERT_EQ(actual.size(), expected.size());
151 for (size_t i = 0; i < actual.size(); ++i) {
152 ASSERT_THAT(actual[i], ApexFileEq(expected[i]));
153 }
154 }
155
TEST(ApexFileRepositoryTest,InitializeFailureCorruptApex)156 TEST(ApexFileRepositoryTest, InitializeFailureCorruptApex) {
157 // Prepare test data.
158 TemporaryDir td;
159 fs::copy(GetTestFile("apex.apexd_test.apex"), td.path);
160 fs::copy(GetTestFile("apex.apexd_test_corrupt_superblock_apex.apex"),
161 td.path);
162
163 ApexFileRepository instance;
164 ASSERT_THAT(instance.AddPreInstalledApex({{ApexPartition::System, td.path}}),
165 Not(Ok()));
166 }
167
TEST(ApexFileRepositoryTest,InitializeCompressedApexWithoutApex)168 TEST(ApexFileRepositoryTest, InitializeCompressedApexWithoutApex) {
169 // Prepare test data.
170 TemporaryDir td;
171 fs::copy(GetTestFile("com.android.apex.compressed.v1_without_apex.capex"),
172 td.path);
173
174 ApexFileRepository instance;
175 // Compressed APEX without APEX cannot be opened
176 ASSERT_THAT(instance.AddPreInstalledApex({{ApexPartition::System, td.path}}),
177 Not(Ok()));
178 }
179
TEST(ApexFileRepositoryTest,InitializeSameNameDifferentPathAborts)180 TEST(ApexFileRepositoryTest, InitializeSameNameDifferentPathAborts) {
181 // Prepare test data.
182 TemporaryDir td;
183 fs::copy(GetTestFile("apex.apexd_test.apex"), td.path);
184 fs::copy(GetTestFile("apex.apexd_test.apex"),
185 StringPrintf("%s/other.apex", td.path));
186
187 ASSERT_DEATH(
188 {
189 ApexFileRepository instance;
190 instance.AddPreInstalledApex({{ApexPartition::System, td.path}});
191 },
192 "");
193 }
194
TEST(ApexFileRepositoryTest,InitializeMultiInstalledSuccess)195 TEST(ApexFileRepositoryTest, InitializeMultiInstalledSuccess) {
196 // Prepare test data.
197 TemporaryDir td;
198 std::string apex_file = GetTestFile("apex.apexd_test.apex");
199 fs::copy(apex_file, StringPrintf("%s/version_a.apex", td.path));
200 fs::copy(apex_file, StringPrintf("%s/version_b.apex", td.path));
201 auto apex = ApexFile::Open(apex_file);
202 std::string apex_name = apex->GetManifest().name();
203
204 std::string persist_prefix = "debug.apexd.test.persistprefix.";
205 std::string bootconfig_prefix = "debug.apexd.test.bootconfigprefix.";
206 ApexFileRepository instance(/*enforce_multi_install_partition=*/false,
207 /*multi_install_select_prop_prefixes=*/{
208 persist_prefix, bootconfig_prefix});
209
210 auto test_fn = [&](const std::string& selected_filename) {
211 ASSERT_RESULT_OK(
212 instance.AddPreInstalledApex({{ApexPartition::System, td.path}}));
213 auto ret = instance.GetPreinstalledPath(apex->GetManifest().name());
214 ASSERT_RESULT_OK(ret);
215 ASSERT_EQ(StringPrintf("%s/%s", td.path, selected_filename.c_str()), *ret);
216 instance.Reset();
217 };
218
219 // Start with version_a in bootconfig.
220 android::base::SetProperty(bootconfig_prefix + apex_name, "version_a.apex");
221 test_fn("version_a.apex");
222 // Developer chooses version_b with persist prop.
223 android::base::SetProperty(persist_prefix + apex_name, "version_b.apex");
224 test_fn("version_b.apex");
225 // Developer goes back to version_a with persist prop.
226 android::base::SetProperty(persist_prefix + apex_name, "version_a.apex");
227 test_fn("version_a.apex");
228
229 android::base::SetProperty(persist_prefix + apex_name, "");
230 android::base::SetProperty(bootconfig_prefix + apex_name, "");
231 }
232
TEST(ApexFileRepositoryTest,InitializeMultiInstalledSkipsForDifferingKeys)233 TEST(ApexFileRepositoryTest, InitializeMultiInstalledSkipsForDifferingKeys) {
234 // Prepare test data.
235 TemporaryDir td;
236 fs::copy(GetTestFile("apex.apexd_test.apex"),
237 StringPrintf("%s/version_a.apex", td.path));
238 fs::copy(GetTestFile("apex.apexd_test_different_key.apex"),
239 StringPrintf("%s/version_b.apex", td.path));
240 auto apex = ApexFile::Open(GetTestFile("apex.apexd_test.apex"));
241 std::string apex_name = apex->GetManifest().name();
242 std::string prop_prefix = "debug.apexd.test.bootconfigprefix.";
243 std::string prop = prop_prefix + apex_name;
244 android::base::SetProperty(prop, "version_a.apex");
245
246 ApexFileRepository instance(
247 /*enforce_multi_install_partition=*/false,
248 /*multi_install_select_prop_prefixes=*/{prop_prefix});
249 ASSERT_RESULT_OK(
250 instance.AddPreInstalledApex({{ApexPartition::System, td.path}}));
251 // Neither version should be have been installed.
252 ASSERT_THAT(instance.GetPreinstalledPath(apex->GetManifest().name()),
253 Not(Ok()));
254
255 android::base::SetProperty(prop, "");
256 }
257
TEST(ApexFileRepositoryTest,InitializeMultiInstalledSkipsForInvalidPartition)258 TEST(ApexFileRepositoryTest, InitializeMultiInstalledSkipsForInvalidPartition) {
259 // Prepare test data.
260 TemporaryDir td;
261 // Note: These test files are on /data, which is not a valid partition for
262 // multi-installed APEXes.
263 fs::copy(GetTestFile("apex.apexd_test.apex"),
264 StringPrintf("%s/version_a.apex", td.path));
265 fs::copy(GetTestFile("apex.apexd_test.apex"),
266 StringPrintf("%s/version_b.apex", td.path));
267 auto apex = ApexFile::Open(GetTestFile("apex.apexd_test.apex"));
268 std::string apex_name = apex->GetManifest().name();
269 std::string prop_prefix = "debug.apexd.test.bootconfigprefix.";
270 std::string prop = prop_prefix + apex_name;
271 android::base::SetProperty(prop, "version_a.apex");
272
273 ApexFileRepository instance(
274 /*enforce_multi_install_partition=*/true,
275 /*multi_install_select_prop_prefixes=*/{prop_prefix});
276 ASSERT_RESULT_OK(
277 instance.AddPreInstalledApex({{ApexPartition::System, td.path}}));
278 // Neither version should be have been installed.
279 ASSERT_THAT(instance.GetPreinstalledPath(apex->GetManifest().name()),
280 Not(Ok()));
281
282 android::base::SetProperty(prop, "");
283 }
284
TEST(ApexFileRepositoryTest,InitializeSameNameDifferentPathAbortsCompressedApex)285 TEST(ApexFileRepositoryTest,
286 InitializeSameNameDifferentPathAbortsCompressedApex) {
287 // Prepare test data.
288 TemporaryDir td;
289 fs::copy(GetTestFile("com.android.apex.compressed.v1.capex"), td.path);
290 fs::copy(GetTestFile("com.android.apex.compressed.v1.capex"),
291 StringPrintf("%s/other.capex", td.path));
292
293 ASSERT_DEATH(
294 {
295 ApexFileRepository instance;
296 instance.AddPreInstalledApex({{ApexPartition::System, td.path}});
297 },
298 "");
299 }
300
TEST(ApexFileRepositoryTest,InitializePublicKeyUnexpectdlyChangedAborts)301 TEST(ApexFileRepositoryTest, InitializePublicKeyUnexpectdlyChangedAborts) {
302 // Prepare test data.
303 TemporaryDir td;
304 fs::copy(GetTestFile("apex.apexd_test.apex"), td.path);
305
306 ApexFileRepository instance;
307 ASSERT_RESULT_OK(
308 instance.AddPreInstalledApex({{ApexPartition::System, td.path}}));
309
310 auto apex_file = ApexFile::Open(GetTestFile("apex.apexd_test.apex"));
311
312 // Check that apex was loaded.
313 auto path = instance.GetPreinstalledPath(apex_file->GetManifest().name());
314 ASSERT_RESULT_OK(path);
315 ASSERT_EQ(StringPrintf("%s/apex.apexd_test.apex", td.path), *path);
316
317 auto public_key = instance.GetPublicKey("com.android.apex.test_package");
318 ASSERT_RESULT_OK(public_key);
319
320 // Substitute it with another apex with the same name, but different public
321 // key.
322 fs::copy(GetTestFile("apex.apexd_test_different_key.apex"), *path,
323 fs::copy_options::overwrite_existing);
324
325 {
326 auto apex = ApexFile::Open(*path);
327 ASSERT_RESULT_OK(apex);
328 // Check module name hasn't changed.
329 ASSERT_EQ("com.android.apex.test_package", apex->GetManifest().name());
330 // Check public key has changed.
331 ASSERT_NE(*public_key, apex->GetBundledPublicKey());
332 }
333
334 ASSERT_DEATH(
335 { instance.AddPreInstalledApex({{ApexPartition::System, td.path}}); },
336 "");
337 }
338
TEST(ApexFileRepositoryTest,InitializePublicKeyUnexpectdlyChangedAbortsCompressedApex)339 TEST(ApexFileRepositoryTest,
340 InitializePublicKeyUnexpectdlyChangedAbortsCompressedApex) {
341 // Prepare test data.
342 TemporaryDir td;
343 fs::copy(GetTestFile("com.android.apex.compressed.v1.capex"), td.path);
344
345 ApexFileRepository instance;
346 ASSERT_RESULT_OK(
347 instance.AddPreInstalledApex({{ApexPartition::System, td.path}}));
348
349 // Check that apex was loaded.
350 auto apex_file =
351 ApexFile::Open(GetTestFile("com.android.apex.compressed.v1.capex"));
352 auto path = instance.GetPreinstalledPath(apex_file->GetManifest().name());
353 ASSERT_RESULT_OK(path);
354 ASSERT_EQ(StringPrintf("%s/com.android.apex.compressed.v1.capex", td.path),
355 *path);
356
357 auto public_key = instance.GetPublicKey("com.android.apex.compressed");
358 ASSERT_RESULT_OK(public_key);
359
360 // Substitute it with another apex with the same name, but different public
361 // key.
362 fs::copy(GetTestFile("com.android.apex.compressed_different_key.capex"),
363 *path, fs::copy_options::overwrite_existing);
364
365 {
366 auto apex = ApexFile::Open(*path);
367 ASSERT_RESULT_OK(apex);
368 // Check module name hasn't changed.
369 ASSERT_EQ("com.android.apex.compressed", apex->GetManifest().name());
370 // Check public key has changed.
371 ASSERT_NE(*public_key, apex->GetBundledPublicKey());
372 }
373
374 ASSERT_DEATH(
375 { instance.AddPreInstalledApex({{ApexPartition::System, td.path}}); },
376 "");
377 }
378
TEST(ApexFileRepositoryTest,IsPreInstalledApex)379 TEST(ApexFileRepositoryTest, IsPreInstalledApex) {
380 // Prepare test data.
381 TemporaryDir td;
382 fs::copy(GetTestFile("apex.apexd_test.apex"), td.path);
383 fs::copy(GetTestFile("com.android.apex.compressed.v1.capex"), td.path);
384
385 ApexFileRepository instance;
386 ASSERT_RESULT_OK(
387 instance.AddPreInstalledApex({{ApexPartition::System, td.path}}));
388
389 auto compressed_apex = ApexFile::Open(
390 StringPrintf("%s/com.android.apex.compressed.v1.capex", td.path));
391 ASSERT_RESULT_OK(compressed_apex);
392 ASSERT_TRUE(instance.IsPreInstalledApex(*compressed_apex));
393
394 auto apex1 = ApexFile::Open(StringPrintf("%s/apex.apexd_test.apex", td.path));
395 ASSERT_RESULT_OK(apex1);
396 ASSERT_TRUE(instance.IsPreInstalledApex(*apex1));
397
398 // It's same apex, but path is different. Shouldn't be treated as
399 // pre-installed.
400 auto apex2 = ApexFile::Open(GetTestFile("apex.apexd_test.apex"));
401 ASSERT_RESULT_OK(apex2);
402 ASSERT_FALSE(instance.IsPreInstalledApex(*apex2));
403
404 auto apex3 =
405 ApexFile::Open(GetTestFile("apex.apexd_test_different_app.apex"));
406 ASSERT_RESULT_OK(apex3);
407 ASSERT_FALSE(instance.IsPreInstalledApex(*apex3));
408 }
409
TEST(ApexFileRepositoryTest,IsDecompressedApex)410 TEST(ApexFileRepositoryTest, IsDecompressedApex) {
411 // Prepare instance
412 TemporaryDir decompression_dir;
413 ApexFileRepository instance(decompression_dir.path);
414
415 // Prepare decompressed apex
416 std::string filename = "com.android.apex.compressed.v1.apex";
417 fs::copy(GetTestFile(filename), decompression_dir.path);
418 auto decompressed_path =
419 StringPrintf("%s/%s", decompression_dir.path, filename.c_str());
420 auto decompressed_apex = ApexFile::Open(decompressed_path);
421
422 // Any file which is already located in |decompression_dir| should be
423 // considered decompressed
424 ASSERT_TRUE(instance.IsDecompressedApex(*decompressed_apex));
425
426 // Hard links with same file name is not considered decompressed
427 TemporaryDir active_dir;
428 auto active_path = StringPrintf("%s/%s", active_dir.path, filename.c_str());
429 std::error_code ec;
430 fs::create_hard_link(decompressed_path, active_path, ec);
431 ASSERT_FALSE(ec) << "Failed to create hardlink";
432 auto active_apex = ApexFile::Open(active_path);
433 ASSERT_FALSE(instance.IsDecompressedApex(*active_apex));
434 }
435
TEST(ApexFileRepositoryTest,AddAndGetDataApex)436 TEST(ApexFileRepositoryTest, AddAndGetDataApex) {
437 // Prepare test data.
438 TemporaryDir built_in_dir, data_dir, decompression_dir;
439 fs::copy(GetTestFile("apex.apexd_test.apex"), built_in_dir.path);
440 fs::copy(GetTestFile("apex.apexd_test_v2.apex"), data_dir.path);
441 PrepareCompressedApex("com.android.apex.compressed.v1.capex",
442 built_in_dir.path, decompression_dir.path);
443 // Add a data apex that has kDecompressedApexPackageSuffix
444 fs::copy(GetTestFile("com.android.apex.compressed.v1.apex"),
445 StringPrintf("%s/com.android.apex.compressed@1%s", data_dir.path,
446 kDecompressedApexPackageSuffix));
447
448 ApexFileRepository instance(decompression_dir.path);
449 ASSERT_RESULT_OK(instance.AddPreInstalledApex(
450 {{ApexPartition::System, built_in_dir.path}}));
451 ASSERT_RESULT_OK(instance.AddDataApex(data_dir.path));
452
453 // ApexFileRepository should only deal with APEX in /data/apex/active.
454 // Decompressed APEX should not be included
455 auto data_apexs = instance.GetDataApexFiles();
456 auto normal_apex =
457 ApexFile::Open(StringPrintf("%s/apex.apexd_test_v2.apex", data_dir.path));
458 ASSERT_THAT(data_apexs,
459 UnorderedElementsAre(ApexFileEq(ByRef(*normal_apex))));
460 }
461
TEST(ApexFileRepositoryTest,AddDataApexIgnoreCompressedApex)462 TEST(ApexFileRepositoryTest, AddDataApexIgnoreCompressedApex) {
463 // Prepare test data.
464 TemporaryDir data_dir, decompression_dir;
465 fs::copy(GetTestFile("com.android.apex.compressed.v1.capex"), data_dir.path);
466
467 ApexFileRepository instance;
468 ASSERT_RESULT_OK(instance.AddDataApex(data_dir.path));
469
470 auto data_apexs = instance.GetDataApexFiles();
471 ASSERT_EQ(data_apexs.size(), 0u);
472 }
473
TEST(ApexFileRepositoryTest,AddDataApexIgnoreIfNotPreInstalled)474 TEST(ApexFileRepositoryTest, AddDataApexIgnoreIfNotPreInstalled) {
475 // Prepare test data.
476 TemporaryDir data_dir, decompression_dir;
477 fs::copy(GetTestFile("apex.apexd_test.apex"), data_dir.path);
478
479 ApexFileRepository instance;
480 ASSERT_RESULT_OK(instance.AddDataApex(data_dir.path));
481
482 auto data_apexs = instance.GetDataApexFiles();
483 ASSERT_EQ(data_apexs.size(), 0u);
484 }
485
TEST(ApexFileRepositoryTest,AddDataApexPrioritizeHigherVersionApex)486 TEST(ApexFileRepositoryTest, AddDataApexPrioritizeHigherVersionApex) {
487 // Prepare test data.
488 TemporaryDir built_in_dir, data_dir, decompression_dir;
489 fs::copy(GetTestFile("apex.apexd_test.apex"), built_in_dir.path);
490 fs::copy(GetTestFile("apex.apexd_test.apex"), data_dir.path);
491 fs::copy(GetTestFile("apex.apexd_test_v2.apex"), data_dir.path);
492
493 ApexFileRepository instance;
494 ASSERT_RESULT_OK(instance.AddPreInstalledApex(
495 {{ApexPartition::System, built_in_dir.path}}));
496 ASSERT_RESULT_OK(instance.AddDataApex(data_dir.path));
497
498 auto data_apexs = instance.GetDataApexFiles();
499 auto normal_apex =
500 ApexFile::Open(StringPrintf("%s/apex.apexd_test_v2.apex", data_dir.path));
501 ASSERT_THAT(data_apexs,
502 UnorderedElementsAre(ApexFileEq(ByRef(*normal_apex))));
503 }
504
TEST(ApexFileRepositoryTest,AddDataApexDoesNotScanDecompressedApex)505 TEST(ApexFileRepositoryTest, AddDataApexDoesNotScanDecompressedApex) {
506 // Prepare test data.
507 TemporaryDir built_in_dir, data_dir, decompression_dir;
508 PrepareCompressedApex("com.android.apex.compressed.v1.capex",
509 built_in_dir.path, decompression_dir.path);
510
511 ApexFileRepository instance(decompression_dir.path);
512 ASSERT_RESULT_OK(instance.AddPreInstalledApex(
513 {{ApexPartition::System, built_in_dir.path}}));
514 ASSERT_RESULT_OK(instance.AddDataApex(data_dir.path));
515
516 auto data_apexs = instance.GetDataApexFiles();
517 ASSERT_EQ(data_apexs.size(), 0u);
518 }
519
TEST(ApexFileRepositoryTest,AddDataApexIgnoreWrongPublicKey)520 TEST(ApexFileRepositoryTest, AddDataApexIgnoreWrongPublicKey) {
521 // Prepare test data.
522 TemporaryDir built_in_dir, data_dir, decompression_dir;
523 fs::copy(GetTestFile("apex.apexd_test.apex"), built_in_dir.path);
524 fs::copy(GetTestFile("apex.apexd_test_different_key.apex"), data_dir.path);
525
526 ApexFileRepository instance;
527 ASSERT_RESULT_OK(instance.AddPreInstalledApex(
528 {{ApexPartition::System, built_in_dir.path}}));
529 ASSERT_RESULT_OK(instance.AddDataApex(data_dir.path));
530
531 auto data_apexs = instance.GetDataApexFiles();
532 ASSERT_EQ(data_apexs.size(), 0u);
533 }
534
TEST(ApexFileRepositoryTest,GetPreInstalledApexFiles)535 TEST(ApexFileRepositoryTest, GetPreInstalledApexFiles) {
536 // Prepare test data.
537 TemporaryDir built_in_dir;
538 fs::copy(GetTestFile("apex.apexd_test.apex"), built_in_dir.path);
539 fs::copy(GetTestFile("com.android.apex.compressed.v1.capex"),
540 built_in_dir.path);
541
542 ApexFileRepository instance;
543 ASSERT_RESULT_OK(instance.AddPreInstalledApex(
544 {{ApexPartition::System, built_in_dir.path}}));
545
546 auto pre_installed_apexs = instance.GetPreInstalledApexFiles();
547 auto pre_apex_1 = ApexFile::Open(
548 StringPrintf("%s/apex.apexd_test.apex", built_in_dir.path));
549 auto pre_apex_2 = ApexFile::Open(StringPrintf(
550 "%s/com.android.apex.compressed.v1.capex", built_in_dir.path));
551 ASSERT_THAT(pre_installed_apexs,
552 UnorderedElementsAre(ApexFileEq(ByRef(*pre_apex_1)),
553 ApexFileEq(ByRef(*pre_apex_2))));
554 }
555
TEST(ApexFileRepositoryTest,AllApexFilesByName)556 TEST(ApexFileRepositoryTest, AllApexFilesByName) {
557 TemporaryDir built_in_dir, decompression_dir;
558 fs::copy(GetTestFile("apex.apexd_test.apex"), built_in_dir.path);
559 fs::copy(GetTestFile("com.android.apex.cts.shim.apex"), built_in_dir.path);
560 fs::copy(GetTestFile("com.android.apex.compressed.v1.capex"),
561 built_in_dir.path);
562 ApexFileRepository instance;
563 ASSERT_RESULT_OK(instance.AddPreInstalledApex(
564 {{ApexPartition::System, built_in_dir.path}}));
565
566 TemporaryDir data_dir;
567 fs::copy(GetTestFile("com.android.apex.cts.shim.v2.apex"), data_dir.path);
568 ASSERT_RESULT_OK(instance.AddDataApex(data_dir.path));
569
570 auto result = instance.AllApexFilesByName();
571
572 // Verify the contents of result
573 auto apexd_test_file = ApexFile::Open(
574 StringPrintf("%s/apex.apexd_test.apex", built_in_dir.path));
575 auto shim_v1 = ApexFile::Open(
576 StringPrintf("%s/com.android.apex.cts.shim.apex", built_in_dir.path));
577 auto compressed_apex = ApexFile::Open(StringPrintf(
578 "%s/com.android.apex.compressed.v1.capex", built_in_dir.path));
579 auto shim_v2 = ApexFile::Open(
580 StringPrintf("%s/com.android.apex.cts.shim.v2.apex", data_dir.path));
581
582 ASSERT_EQ(result.size(), 3u);
583 ASSERT_THAT(result[apexd_test_file->GetManifest().name()],
584 UnorderedElementsAre(ApexFileEq(ByRef(*apexd_test_file))));
585 ASSERT_THAT(result[shim_v1->GetManifest().name()],
586 UnorderedElementsAre(ApexFileEq(ByRef(*shim_v1)),
587 ApexFileEq(ByRef(*shim_v2))));
588 ASSERT_THAT(result[compressed_apex->GetManifest().name()],
589 UnorderedElementsAre(ApexFileEq(ByRef(*compressed_apex))));
590 }
591
TEST(ApexFileRepositoryTest,GetDataApex)592 TEST(ApexFileRepositoryTest, GetDataApex) {
593 // Prepare test data.
594 TemporaryDir built_in_dir, data_dir;
595 fs::copy(GetTestFile("apex.apexd_test.apex"), built_in_dir.path);
596 fs::copy(GetTestFile("apex.apexd_test_v2.apex"), data_dir.path);
597
598 ApexFileRepository instance;
599 ASSERT_RESULT_OK(instance.AddPreInstalledApex(
600 {{ApexPartition::System, built_in_dir.path}}));
601 ASSERT_RESULT_OK(instance.AddDataApex(data_dir.path));
602
603 auto apex =
604 ApexFile::Open(StringPrintf("%s/apex.apexd_test_v2.apex", data_dir.path));
605 ASSERT_RESULT_OK(apex);
606
607 auto ret = instance.GetDataApex("com.android.apex.test_package");
608 ASSERT_THAT(ret, ApexFileEq(ByRef(*apex)));
609 }
610
TEST(ApexFileRepositoryTest,GetDataApexNoSuchApexAborts)611 TEST(ApexFileRepositoryTest, GetDataApexNoSuchApexAborts) {
612 ASSERT_DEATH(
613 {
614 ApexFileRepository instance;
615 instance.GetDataApex("whatever");
616 },
617 "");
618 }
619
TEST(ApexFileRepositoryTest,GetPreInstalledApex)620 TEST(ApexFileRepositoryTest, GetPreInstalledApex) {
621 // Prepare test data.
622 TemporaryDir built_in_dir;
623 fs::copy(GetTestFile("apex.apexd_test.apex"), built_in_dir.path);
624
625 ApexFileRepository instance;
626 ASSERT_RESULT_OK(instance.AddPreInstalledApex(
627 {{ApexPartition::System, built_in_dir.path}}));
628
629 auto apex = ApexFile::Open(
630 StringPrintf("%s/apex.apexd_test.apex", built_in_dir.path));
631 ASSERT_RESULT_OK(apex);
632
633 auto ret = instance.GetPreInstalledApex("com.android.apex.test_package");
634 ASSERT_THAT(ret, ApexFileEq(ByRef(*apex)));
635 }
636
TEST(ApexFileRepositoryTest,GetPreInstalledApexNoSuchApexAborts)637 TEST(ApexFileRepositoryTest, GetPreInstalledApexNoSuchApexAborts) {
638 ASSERT_DEATH(
639 {
640 ApexFileRepository instance;
641 instance.GetPreInstalledApex("whatever");
642 },
643 "");
644 }
645
646 struct ApexFileRepositoryTestAddBlockApex : public ::testing::Test {
647 TemporaryDir test_dir;
648
649 struct ApexMetadata {
650 std::string public_key;
651 std::string root_digest;
652 int64_t last_update_seconds;
653 bool is_factory = true;
654 int64_t manifest_version;
655 std::string manifest_name;
656 };
657
658 struct PayloadMetadata {
659 android::microdroid::Metadata metadata;
660 std::string path;
PayloadMetadataandroid::apex::ApexFileRepositoryTestAddBlockApex::PayloadMetadata661 PayloadMetadata(const std::string& path) : path(path) {}
apexandroid::apex::ApexFileRepositoryTestAddBlockApex::PayloadMetadata662 PayloadMetadata& apex(const std::string& name) {
663 return apex(name, ApexMetadata{});
664 }
apexandroid::apex::ApexFileRepositoryTestAddBlockApex::PayloadMetadata665 PayloadMetadata& apex(const std::string& name,
666 const ApexMetadata& apex_metadata) {
667 auto apex = metadata.add_apexes();
668 apex->set_name(name);
669 apex->set_public_key(apex_metadata.public_key);
670 apex->set_root_digest(apex_metadata.root_digest);
671 apex->set_last_update_seconds(apex_metadata.last_update_seconds);
672 apex->set_is_factory(apex_metadata.is_factory);
673 apex->set_manifest_version(apex_metadata.manifest_version);
674 apex->set_manifest_name(apex_metadata.manifest_name);
675 return *this;
676 }
~PayloadMetadataandroid::apex::ApexFileRepositoryTestAddBlockApex::PayloadMetadata677 ~PayloadMetadata() {
678 metadata.set_version(1);
679 std::ofstream out(path);
680 android::microdroid::WriteMetadata(metadata, out);
681 }
682 };
683 };
684
TEST_F(ApexFileRepositoryTestAddBlockApex,ScansPayloadDisksAndAddApexFilesToPreInstalled)685 TEST_F(ApexFileRepositoryTestAddBlockApex,
686 ScansPayloadDisksAndAddApexFilesToPreInstalled) {
687 // prepare payload disk
688 // <test-dir>/vdc1 : metadata
689 // /vdc2 : apex.apexd_test.apex
690 // /vdc3 : apex.apexd_test_different_app.apex
691
692 const auto& test_apex_foo = GetTestFile("apex.apexd_test.apex");
693 const auto& test_apex_bar = GetTestFile("apex.apexd_test_different_app.apex");
694
695 const std::string metadata_partition_path = test_dir.path + "/vdc1"s;
696 const std::string apex_foo_path = test_dir.path + "/vdc2"s;
697 const std::string apex_bar_path = test_dir.path + "/vdc3"s;
698
699 PayloadMetadata(metadata_partition_path)
700 .apex(test_apex_foo)
701 .apex(test_apex_bar);
702 auto block_apex1 = WriteBlockApex(test_apex_foo, apex_foo_path);
703 auto block_apex2 = WriteBlockApex(test_apex_bar, apex_bar_path);
704
705 // call ApexFileRepository::AddBlockApex()
706 ApexFileRepository instance;
707 auto status = instance.AddBlockApex(metadata_partition_path);
708 ASSERT_RESULT_OK(status);
709
710 auto apex_foo = ApexFile::Open(apex_foo_path);
711 ASSERT_RESULT_OK(apex_foo);
712 // block apexes can be identified with IsBlockApex
713 ASSERT_TRUE(instance.IsBlockApex(*apex_foo));
714
715 // "block" apexes are treated as "pre-installed" with "is_factory: true"
716 auto ret_foo = instance.GetPreInstalledApex("com.android.apex.test_package");
717 ASSERT_THAT(ret_foo, ApexFileEq(ByRef(*apex_foo)));
718
719 auto partition_foo = instance.GetPartition(*apex_foo);
720 ASSERT_RESULT_OK(partition_foo);
721 ASSERT_EQ(*partition_foo, ApexPartition::System);
722
723 auto apex_bar = ApexFile::Open(apex_bar_path);
724 ASSERT_RESULT_OK(apex_bar);
725 auto ret_bar =
726 instance.GetPreInstalledApex("com.android.apex.test_package_2");
727 ASSERT_THAT(ret_bar, ApexFileEq(ByRef(*apex_bar)));
728
729 auto partition_bar = instance.GetPartition(*apex_bar);
730 ASSERT_EQ(*partition_bar, ApexPartition::System);
731 }
732
TEST_F(ApexFileRepositoryTestAddBlockApex,ScansOnlySpecifiedInMetadataPartition)733 TEST_F(ApexFileRepositoryTestAddBlockApex,
734 ScansOnlySpecifiedInMetadataPartition) {
735 // prepare payload disk
736 // <test-dir>/vdc1 : metadata with apex.apexd_test.apex only
737 // /vdc2 : apex.apexd_test.apex
738 // /vdc3 : apex.apexd_test_different_app.apex
739
740 const auto& test_apex_foo = GetTestFile("apex.apexd_test.apex");
741 const auto& test_apex_bar = GetTestFile("apex.apexd_test_different_app.apex");
742
743 const std::string metadata_partition_path = test_dir.path + "/vdc1"s;
744 const std::string apex_foo_path = test_dir.path + "/vdc2"s;
745 const std::string apex_bar_path = test_dir.path + "/vdc3"s;
746
747 // metadata lists only "foo"
748 PayloadMetadata(metadata_partition_path).apex(test_apex_foo);
749 auto block_apex1 = WriteBlockApex(test_apex_foo, apex_foo_path);
750 auto block_apex2 = WriteBlockApex(test_apex_bar, apex_bar_path);
751
752 // call ApexFileRepository::AddBlockApex()
753 ApexFileRepository instance;
754 auto status = instance.AddBlockApex(metadata_partition_path);
755 ASSERT_RESULT_OK(status);
756
757 // foo is added, but bar is not
758 ASSERT_TRUE(instance.HasPreInstalledVersion("com.android.apex.test_package"));
759 ASSERT_FALSE(
760 instance.HasPreInstalledVersion("com.android.apex.test_package_2"));
761 }
762
TEST_F(ApexFileRepositoryTestAddBlockApex,FailsWhenTheresDuplicateNames)763 TEST_F(ApexFileRepositoryTestAddBlockApex, FailsWhenTheresDuplicateNames) {
764 // prepare payload disk
765 // <test-dir>/vdc1 : metadata with v1 and v2 of apex.apexd_test
766 // /vdc2 : apex.apexd_test.apex
767 // /vdc3 : apex.apexd_test_v2.apex
768
769 const auto& test_apex_foo = GetTestFile("apex.apexd_test.apex");
770 const auto& test_apex_bar = GetTestFile("apex.apexd_test_v2.apex");
771
772 const std::string metadata_partition_path = test_dir.path + "/vdc1"s;
773 const std::string apex_foo_path = test_dir.path + "/vdc2"s;
774 const std::string apex_bar_path = test_dir.path + "/vdc3"s;
775
776 PayloadMetadata(metadata_partition_path)
777 .apex(test_apex_foo)
778 .apex(test_apex_bar);
779 auto block_apex1 = WriteBlockApex(test_apex_foo, apex_foo_path);
780 auto block_apex2 = WriteBlockApex(test_apex_bar, apex_bar_path);
781
782 ApexFileRepository instance;
783 auto status = instance.AddBlockApex(metadata_partition_path);
784 ASSERT_THAT(status, Not(Ok()));
785 }
786
TEST_F(ApexFileRepositoryTestAddBlockApex,GetBlockApexRootDigest)787 TEST_F(ApexFileRepositoryTestAddBlockApex, GetBlockApexRootDigest) {
788 // prepare payload disk with root digest
789 // <test-dir>/vdc1 : metadata with apex.apexd_test.apex only
790 // /vdc2 : apex.apexd_test.apex
791
792 const auto& test_apex_foo = GetTestFile("apex.apexd_test.apex");
793
794 const std::string metadata_partition_path = test_dir.path + "/vdc1"s;
795 const std::string apex_foo_path = test_dir.path + "/vdc2"s;
796
797 // root digest is stored as bytes in metadata and as hexadecimal in
798 // ApexFileRepository
799 const std::string root_digest = "root_digest";
800 const std::string hex_root_digest = BytesToHex(
801 reinterpret_cast<const uint8_t*>(root_digest.data()), root_digest.size());
802
803 // metadata lists "foo"
804 ApexMetadata apex_metadata;
805 apex_metadata.root_digest = root_digest;
806 PayloadMetadata(metadata_partition_path).apex(test_apex_foo, apex_metadata);
807 auto block_apex1 = WriteBlockApex(test_apex_foo, apex_foo_path);
808
809 // call ApexFileRepository::AddBlockApex()
810 ApexFileRepository instance;
811 auto status = instance.AddBlockApex(metadata_partition_path);
812 ASSERT_RESULT_OK(status);
813
814 ASSERT_EQ(hex_root_digest, instance.GetBlockApexRootDigest(apex_foo_path));
815 }
816
TEST_F(ApexFileRepositoryTestAddBlockApex,GetBlockApexLastUpdateSeconds)817 TEST_F(ApexFileRepositoryTestAddBlockApex, GetBlockApexLastUpdateSeconds) {
818 // prepare payload disk with last update time
819 // <test-dir>/vdc1 : metadata with apex.apexd_test.apex only
820 // /vdc2 : apex.apexd_test.apex
821
822 const auto& test_apex_foo = GetTestFile("apex.apexd_test.apex");
823
824 const std::string metadata_partition_path = test_dir.path + "/vdc1"s;
825 const std::string apex_foo_path = test_dir.path + "/vdc2"s;
826
827 const int64_t last_update_seconds = 123456789;
828
829 // metadata lists "foo"
830 ApexMetadata apex_metadata;
831 apex_metadata.last_update_seconds = last_update_seconds;
832 PayloadMetadata(metadata_partition_path).apex(test_apex_foo, apex_metadata);
833 auto block_apex1 = WriteBlockApex(test_apex_foo, apex_foo_path);
834
835 // call ApexFileRepository::AddBlockApex()
836 ApexFileRepository instance;
837 auto status = instance.AddBlockApex(metadata_partition_path);
838 ASSERT_RESULT_OK(status);
839
840 ASSERT_EQ(last_update_seconds,
841 instance.GetBlockApexLastUpdateSeconds(apex_foo_path));
842 }
843
TEST_F(ApexFileRepositoryTestAddBlockApex,SucceedsWhenMetadataMatches)844 TEST_F(ApexFileRepositoryTestAddBlockApex, SucceedsWhenMetadataMatches) {
845 // prepare payload disk
846 // <test-dir>/vdc1 : metadata with apex.apexd_test.apex only
847 // /vdc2 : apex.apexd_test.apex
848
849 const auto& test_apex_foo = GetTestFile("apex.apexd_test.apex");
850
851 const std::string metadata_partition_path = test_dir.path + "/vdc1"s;
852 const std::string apex_foo_path = test_dir.path + "/vdc2"s;
853
854 std::string public_key;
855 const auto& key_path =
856 GetTestFile("apexd_testdata/com.android.apex.test_package.avbpubkey");
857 ASSERT_TRUE(android::base::ReadFileToString(key_path, &public_key))
858 << "Failed to read " << key_path;
859
860 // metadata lists "foo"
861 ApexMetadata apex_metadata;
862 apex_metadata.public_key = public_key;
863 apex_metadata.manifest_version = 1;
864 apex_metadata.manifest_name = "com.android.apex.test_package";
865 PayloadMetadata(metadata_partition_path).apex(test_apex_foo, apex_metadata);
866 auto block_apex1 = WriteBlockApex(test_apex_foo, apex_foo_path);
867
868 // call ApexFileRepository::AddBlockApex()
869 ApexFileRepository instance;
870 auto status = instance.AddBlockApex(metadata_partition_path);
871 ASSERT_RESULT_OK(status);
872 }
873
TEST_F(ApexFileRepositoryTestAddBlockApex,VerifyPublicKeyWhenAddingBlockApex)874 TEST_F(ApexFileRepositoryTestAddBlockApex, VerifyPublicKeyWhenAddingBlockApex) {
875 // prepare payload disk
876 // <test-dir>/vdc1 : metadata with apex.apexd_test.apex only
877 // /vdc2 : apex.apexd_test.apex
878
879 const auto& test_apex_foo = GetTestFile("apex.apexd_test.apex");
880
881 const std::string metadata_partition_path = test_dir.path + "/vdc1"s;
882 const std::string apex_foo_path = test_dir.path + "/vdc2"s;
883
884 // metadata lists "foo"
885 ApexMetadata apex_metadata;
886 apex_metadata.public_key = "wrong public key";
887 PayloadMetadata(metadata_partition_path).apex(test_apex_foo, apex_metadata);
888 auto block_apex1 = WriteBlockApex(test_apex_foo, apex_foo_path);
889
890 // call ApexFileRepository::AddBlockApex()
891 ApexFileRepository instance;
892 auto status = instance.AddBlockApex(metadata_partition_path);
893 ASSERT_THAT(status, Not(Ok()));
894 }
895
TEST_F(ApexFileRepositoryTestAddBlockApex,VerifyManifestVersionWhenAddingBlockApex)896 TEST_F(ApexFileRepositoryTestAddBlockApex,
897 VerifyManifestVersionWhenAddingBlockApex) {
898 // prepare payload disk
899 // <test-dir>/vdc1 : metadata with apex.apexd_test.apex only
900 // /vdc2 : apex.apexd_test.apex
901
902 const auto& test_apex_foo = GetTestFile("apex.apexd_test.apex");
903
904 const std::string metadata_partition_path = test_dir.path + "/vdc1"s;
905 const std::string apex_foo_path = test_dir.path + "/vdc2"s;
906
907 // metadata lists "foo"
908 ApexMetadata apex_metadata;
909 apex_metadata.manifest_version = 2;
910 PayloadMetadata(metadata_partition_path).apex(test_apex_foo, apex_metadata);
911 auto block_apex1 = WriteBlockApex(test_apex_foo, apex_foo_path);
912
913 // call ApexFileRepository::AddBlockApex()
914 ApexFileRepository instance;
915 auto status = instance.AddBlockApex(metadata_partition_path);
916 ASSERT_THAT(status, Not(Ok()));
917 }
918
TEST_F(ApexFileRepositoryTestAddBlockApex,VerifyManifestNameWhenAddingBlockApex)919 TEST_F(ApexFileRepositoryTestAddBlockApex,
920 VerifyManifestNameWhenAddingBlockApex) {
921 // prepare payload disk
922 // <test-dir>/vdc1 : metadata with apex.apexd_test.apex only
923 // /vdc2 : apex.apexd_test.apex
924
925 const auto& test_apex_foo = GetTestFile("apex.apexd_test.apex");
926
927 const std::string metadata_partition_path = test_dir.path + "/vdc1"s;
928 const std::string apex_foo_path = test_dir.path + "/vdc2"s;
929
930 // metadata lists "foo"
931 ApexMetadata apex_metadata;
932 apex_metadata.manifest_name = "Wrong name";
933 PayloadMetadata(metadata_partition_path).apex(test_apex_foo, apex_metadata);
934 auto block_apex1 = WriteBlockApex(test_apex_foo, apex_foo_path);
935
936 // call ApexFileRepository::AddBlockApex()
937 ApexFileRepository instance;
938 auto status = instance.AddBlockApex(metadata_partition_path);
939 ASSERT_THAT(status, Not(Ok()));
940 }
941
TEST_F(ApexFileRepositoryTestAddBlockApex,RespectIsFactoryBitFromMetadata)942 TEST_F(ApexFileRepositoryTestAddBlockApex, RespectIsFactoryBitFromMetadata) {
943 // prepare payload disk
944 // <test-dir>/vdc1 : metadata with apex.apexd_test.apex only
945 // /vdc2 : apex.apexd_test.apex
946
947 const auto& test_apex_foo = GetTestFile("apex.apexd_test.apex");
948
949 const std::string metadata_partition_path = test_dir.path + "/vdc1"s;
950 const std::string apex_foo_path = test_dir.path + "/vdc2"s;
951 auto block_apex1 = WriteBlockApex(test_apex_foo, apex_foo_path);
952
953 for (const bool is_factory : {true, false}) {
954 // metadata lists "foo"
955 ApexMetadata apex_metadata;
956 apex_metadata.is_factory = is_factory;
957 PayloadMetadata(metadata_partition_path).apex(test_apex_foo, apex_metadata);
958
959 // call ApexFileRepository::AddBlockApex()
960 ApexFileRepository instance;
961 auto status = instance.AddBlockApex(metadata_partition_path);
962 ASSERT_RESULT_OK(status)
963 << "failed to add block apex with is_factory=" << is_factory;
964 ASSERT_EQ(is_factory,
965 instance.HasPreInstalledVersion("com.android.apex.test_package"));
966 }
967 }
968
TEST(ApexFileRepositoryTestBrandNewApex,AddAndGetPublicKeyPartition)969 TEST(ApexFileRepositoryTestBrandNewApex, AddAndGetPublicKeyPartition) {
970 TemporaryDir credential_dir_1, credential_dir_2;
971 auto key_path_1 =
972 GetTestFile("apexd_testdata/com.android.apex.brand.new.avbpubkey");
973 fs::copy(key_path_1, credential_dir_1.path);
974 auto key_path_2 = GetTestFile(
975 "apexd_testdata/com.android.apex.brand.new.another.avbpubkey");
976 fs::copy(key_path_2, credential_dir_2.path);
977
978 ApexFileRepository instance;
979 const auto expected_partition_1 = ApexPartition::System;
980 const auto expected_partition_2 = ApexPartition::Odm;
981 auto ret = instance.AddBrandNewApexCredentialAndBlocklist(
982 {{expected_partition_1, credential_dir_1.path},
983 {expected_partition_2, credential_dir_2.path}});
984 ASSERT_RESULT_OK(ret);
985
986 std::string key_1;
987 std::string key_2;
988 const std::string& key_3 = "random key";
989 android::base::ReadFileToString(key_path_1, &key_1);
990 android::base::ReadFileToString(key_path_2, &key_2);
991 auto partition_1 = instance.GetBrandNewApexPublicKeyPartition(key_1);
992 auto partition_2 = instance.GetBrandNewApexPublicKeyPartition(key_2);
993 auto partition_3 = instance.GetBrandNewApexPublicKeyPartition(key_3);
994 ASSERT_EQ(partition_1.value(), expected_partition_1);
995 ASSERT_EQ(partition_2.value(), expected_partition_2);
996 ASSERT_FALSE(partition_3.has_value());
997 }
998
TEST(ApexFileRepositoryTestBrandNewApex,AddPublicKeyFailDuplicateKeyInDiffPartition)999 TEST(ApexFileRepositoryTestBrandNewApex,
1000 AddPublicKeyFailDuplicateKeyInDiffPartition) {
1001 TemporaryDir credential_dir_1, credential_dir_2;
1002 auto key_path_1 =
1003 GetTestFile("apexd_testdata/com.android.apex.brand.new.avbpubkey");
1004 fs::copy(key_path_1, credential_dir_1.path);
1005 auto key_path_2 = GetTestFile(
1006 "apexd_testdata/com.android.apex.brand.new.renamed.avbpubkey");
1007 fs::copy(key_path_2, credential_dir_2.path);
1008
1009 ApexFileRepository instance;
1010 const auto expected_partition_1 = ApexPartition::System;
1011 const auto expected_partition_2 = ApexPartition::Odm;
1012 ASSERT_DEATH(
1013 {
1014 instance.AddBrandNewApexCredentialAndBlocklist(
1015 {{expected_partition_1, credential_dir_1.path},
1016 {expected_partition_2, credential_dir_2.path}});
1017 },
1018 "Duplicate public keys are found in different partitions.");
1019 }
1020
TEST(ApexFileRepositoryTestBrandNewApex,AddAndGetBlockedVersion)1021 TEST(ApexFileRepositoryTestBrandNewApex, AddAndGetBlockedVersion) {
1022 TemporaryDir blocklist_dir;
1023 auto blocklist_path = GetTestFile("apexd_testdata/blocklist.json");
1024 fs::copy(blocklist_path, blocklist_dir.path);
1025
1026 ApexFileRepository instance;
1027 const auto expected_partition = ApexPartition::System;
1028 const auto blocked_apex_name = "com.android.apex.brand.new";
1029 const auto expected_blocked_version = 1;
1030 auto ret = instance.AddBrandNewApexCredentialAndBlocklist(
1031 {{expected_partition, blocklist_dir.path}});
1032 ASSERT_RESULT_OK(ret);
1033
1034 const auto non_existent_partition = ApexPartition::Odm;
1035 const auto non_existent_apex_name = "randome.apex";
1036 auto blocked_version = instance.GetBrandNewApexBlockedVersion(
1037 expected_partition, blocked_apex_name);
1038 ASSERT_EQ(blocked_version, expected_blocked_version);
1039 auto blocked_version_non_existent_apex =
1040 instance.GetBrandNewApexBlockedVersion(expected_partition,
1041 non_existent_apex_name);
1042 ASSERT_FALSE(blocked_version_non_existent_apex.has_value());
1043 auto blocked_version_non_existent_partition =
1044 instance.GetBrandNewApexBlockedVersion(non_existent_partition,
1045 blocked_apex_name);
1046 ASSERT_FALSE(blocked_version_non_existent_partition.has_value());
1047 }
1048
TEST(ApexFileRepositoryTestBrandNewApex,AddCredentialAndBlocklistSucceedEmptyFile)1049 TEST(ApexFileRepositoryTestBrandNewApex,
1050 AddCredentialAndBlocklistSucceedEmptyFile) {
1051 TemporaryDir empty_dir;
1052
1053 ApexFileRepository instance;
1054 const auto expected_partition = ApexPartition::System;
1055 auto ret = instance.AddBrandNewApexCredentialAndBlocklist(
1056 {{expected_partition, empty_dir.path}});
1057 ASSERT_RESULT_OK(ret);
1058 }
1059
TEST(ApexFileRepositoryTestBrandNewApex,AddBlocklistSucceedDuplicateApexNameInDiffPartition)1060 TEST(ApexFileRepositoryTestBrandNewApex,
1061 AddBlocklistSucceedDuplicateApexNameInDiffPartition) {
1062 TemporaryDir blocklist_dir_1, blocklist_dir_2;
1063 auto blocklist_path = GetTestFile("apexd_testdata/blocklist.json");
1064 fs::copy(blocklist_path, blocklist_dir_1.path);
1065 fs::copy(blocklist_path, blocklist_dir_2.path);
1066
1067 ApexFileRepository instance;
1068 const auto expected_partition = ApexPartition::System;
1069 const auto other_partition = ApexPartition::Product;
1070 auto ret = instance.AddBrandNewApexCredentialAndBlocklist(
1071 {{expected_partition, blocklist_dir_1.path},
1072 {other_partition, blocklist_dir_2.path}});
1073 ASSERT_RESULT_OK(ret);
1074 }
1075
TEST(ApexFileRepositoryTestBrandNewApex,AddBlocklistFailDuplicateApexNameInSamePartition)1076 TEST(ApexFileRepositoryTestBrandNewApex,
1077 AddBlocklistFailDuplicateApexNameInSamePartition) {
1078 TemporaryDir blocklist_dir;
1079 auto blocklist_path = GetTestFile("apexd_testdata/blocklist_invalid.json");
1080 fs::copy(blocklist_path, fs::path(blocklist_dir.path) / "blocklist.json");
1081
1082 ApexFileRepository instance;
1083 const auto expected_partition = ApexPartition::System;
1084 ASSERT_DEATH(
1085 {
1086 instance.AddBrandNewApexCredentialAndBlocklist(
1087 {{expected_partition, blocklist_dir.path}});
1088 },
1089 "Duplicate APEX names are found in blocklist.");
1090 }
1091
TEST(ApexFileRepositoryTestBrandNewApex,AddDataApexSucceedVerifiedBrandNewApex)1092 TEST(ApexFileRepositoryTestBrandNewApex,
1093 AddDataApexSucceedVerifiedBrandNewApex) {
1094 // Prepares test data.
1095 ApexFileRepository::EnableBrandNewApex();
1096 const auto partition = ApexPartition::System;
1097 TemporaryDir data_dir, trusted_key_dir;
1098 fs::copy(GetTestFile("com.android.apex.brand.new.apex"), data_dir.path);
1099 fs::copy(GetTestFile("apexd_testdata/com.android.apex.brand.new.avbpubkey"),
1100 trusted_key_dir.path);
1101
1102 ApexFileRepository& instance = ApexFileRepository::GetInstance();
1103 instance.AddBrandNewApexCredentialAndBlocklist(
1104 {{partition, trusted_key_dir.path}});
1105
1106 // Now test that apexes were scanned correctly;
1107 auto apex = ApexFile::Open(GetTestFile("com.android.apex.brand.new.apex"));
1108 ASSERT_RESULT_OK(apex);
1109
1110 ASSERT_RESULT_OK(instance.AddDataApex(data_dir.path));
1111
1112 {
1113 auto ret = instance.GetPartition(*apex);
1114 ASSERT_RESULT_OK(ret);
1115 ASSERT_EQ(partition, *ret);
1116 }
1117
1118 ASSERT_THAT(instance.GetPreinstalledPath(apex->GetManifest().name()),
1119 Not(Ok()));
1120 ASSERT_FALSE(instance.HasPreInstalledVersion(apex->GetManifest().name()));
1121 ASSERT_TRUE(instance.HasDataVersion(apex->GetManifest().name()));
1122
1123 instance.Reset();
1124 }
1125
TEST(ApexFileRepositoryTestBrandNewApex,AddDataApexFailUnverifiedBrandNewApex)1126 TEST(ApexFileRepositoryTestBrandNewApex,
1127 AddDataApexFailUnverifiedBrandNewApex) {
1128 ApexFileRepository::EnableBrandNewApex();
1129 TemporaryDir data_dir;
1130 fs::copy(GetTestFile("com.android.apex.brand.new.apex"), data_dir.path);
1131
1132 ApexFileRepository& instance = ApexFileRepository::GetInstance();
1133 auto apex = ApexFile::Open(GetTestFile("com.android.apex.brand.new.apex"));
1134 ASSERT_RESULT_OK(apex);
1135 ASSERT_RESULT_OK(instance.AddDataApex(data_dir.path));
1136
1137 ASSERT_FALSE(instance.HasDataVersion(apex->GetManifest().name()));
1138 instance.Reset();
1139 }
1140
TEST(ApexFileRepositoryTestBrandNewApex,AddDataApexFailBrandNewApexDisabled)1141 TEST(ApexFileRepositoryTestBrandNewApex, AddDataApexFailBrandNewApexDisabled) {
1142 TemporaryDir data_dir;
1143 fs::copy(GetTestFile("com.android.apex.brand.new.apex"), data_dir.path);
1144
1145 ApexFileRepository& instance = ApexFileRepository::GetInstance();
1146 auto apex = ApexFile::Open(GetTestFile("com.android.apex.brand.new.apex"));
1147 ASSERT_RESULT_OK(apex);
1148 ASSERT_RESULT_OK(instance.AddDataApex(data_dir.path));
1149
1150 ASSERT_FALSE(instance.HasDataVersion(apex->GetManifest().name()));
1151 instance.Reset();
1152 }
1153
TEST(ApexFileRepositoryTestBrandNewApex,GetPartitionSucceedVerifiedBrandNewApex)1154 TEST(ApexFileRepositoryTestBrandNewApex,
1155 GetPartitionSucceedVerifiedBrandNewApex) {
1156 ApexFileRepository::EnableBrandNewApex();
1157 TemporaryDir trusted_key_dir;
1158 fs::copy(GetTestFile("apexd_testdata/com.android.apex.brand.new.avbpubkey"),
1159 trusted_key_dir.path);
1160
1161 ApexFileRepository& instance = ApexFileRepository::GetInstance();
1162 const auto partition = ApexPartition::System;
1163 instance.AddBrandNewApexCredentialAndBlocklist(
1164 {{partition, trusted_key_dir.path}});
1165
1166 auto apex = ApexFile::Open(GetTestFile("com.android.apex.brand.new.apex"));
1167 ASSERT_RESULT_OK(apex);
1168
1169 auto ret = instance.GetPartition(*apex);
1170 ASSERT_RESULT_OK(ret);
1171 ASSERT_EQ(*ret, partition);
1172 instance.Reset();
1173 }
1174
TEST(ApexFileRepositoryTestBrandNewApex,GetPartitionFailUnverifiedBrandNewApex)1175 TEST(ApexFileRepositoryTestBrandNewApex,
1176 GetPartitionFailUnverifiedBrandNewApex) {
1177 ApexFileRepository::EnableBrandNewApex();
1178 ApexFileRepository& instance = ApexFileRepository::GetInstance();
1179
1180 auto apex = ApexFile::Open(GetTestFile("com.android.apex.brand.new.apex"));
1181 ASSERT_RESULT_OK(apex);
1182
1183 auto ret = instance.GetPartition(*apex);
1184 ASSERT_THAT(ret, Not(Ok()));
1185 instance.Reset();
1186 }
1187
TEST(ApexFileRepositoryTestBrandNewApex,GetPartitionFailBrandNewApexDisabled)1188 TEST(ApexFileRepositoryTestBrandNewApex, GetPartitionFailBrandNewApexDisabled) {
1189 TemporaryDir trusted_key_dir;
1190 fs::copy(GetTestFile("apexd_testdata/com.android.apex.brand.new.avbpubkey"),
1191 trusted_key_dir.path);
1192
1193 ApexFileRepository& instance = ApexFileRepository::GetInstance();
1194 const auto partition = ApexPartition::System;
1195 instance.AddBrandNewApexCredentialAndBlocklist(
1196 {{partition, trusted_key_dir.path}});
1197
1198 auto apex = ApexFile::Open(GetTestFile("com.android.apex.brand.new.apex"));
1199 ASSERT_RESULT_OK(apex);
1200
1201 auto ret = instance.GetPartition(*apex);
1202 ASSERT_THAT(ret, Not(Ok()));
1203 instance.Reset();
1204 }
1205
1206 } // namespace apex
1207 } // namespace android
1208