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 <string> 18 #include <vector> 19 20 #include <android-base/file.h> 21 #include <android-base/properties.h> 22 #include <android-base/scopeguard.h> 23 #include <android-base/stringprintf.h> 24 #include <gmock/gmock.h> 25 #include <gtest/gtest.h> 26 #include <libdm/dm.h> 27 28 #include "apex_database.h" 29 #include "apex_file_repository.h" 30 #include "apexd.h" 31 #include "apexd_checkpoint.h" 32 #include "apexd_session.h" 33 #include "apexd_test_utils.h" 34 #include "apexd_utils.h" 35 36 #include "apex_manifest.pb.h" 37 #include "com_android_apex.h" 38 #include "gmock/gmock-matchers.h" 39 40 namespace android { 41 namespace apex { 42 43 namespace fs = std::filesystem; 44 45 using MountedApexData = MountedApexDatabase::MountedApexData; 46 using android::apex::testing::ApexFileEq; 47 using android::apex::testing::IsOk; 48 using android::base::GetExecutableDirectory; 49 using android::base::GetProperty; 50 using android::base::make_scope_guard; 51 using android::base::RemoveFileIfExists; 52 using android::base::Result; 53 using android::base::StringPrintf; 54 using android::base::unique_fd; 55 using android::base::WriteStringToFile; 56 using android::dm::DeviceMapper; 57 using com::android::apex::testing::ApexInfoXmlEq; 58 using ::testing::ByRef; 59 using ::testing::HasSubstr; 60 using ::testing::IsEmpty; 61 using ::testing::StartsWith; 62 using ::testing::UnorderedElementsAre; 63 using ::testing::UnorderedElementsAreArray; 64 65 static std::string GetTestDataDir() { return GetExecutableDirectory(); } 66 static std::string GetTestFile(const std::string& name) { 67 return GetTestDataDir() + "/" + name; 68 } 69 70 static int64_t GetMTime(const std::string& path) { 71 struct stat st_buf; 72 if (stat(path.c_str(), &st_buf) != 0) { 73 PLOG(ERROR) << "Failed to stat " << path; 74 return 0; 75 } 76 return st_buf.st_mtime; 77 } 78 79 // A very basic mock of CheckpointInterface. 80 class MockCheckpointInterface : public CheckpointInterface { 81 public: 82 Result<bool> SupportsFsCheckpoints() override { 83 return supports_fs_checkpoint_; 84 } 85 86 Result<bool> NeedsCheckpoint() override { return needs_checkpoint_; } 87 88 Result<bool> NeedsRollback() override { return needs_rollback_; } 89 90 Result<void> StartCheckpoint(int32_t num_retries) override { return {}; } 91 92 Result<void> AbortChanges(const std::string& msg, bool retry) override { 93 return {}; 94 } 95 96 void SetSupportsCheckpoint(bool value) { supports_fs_checkpoint_ = value; } 97 98 void SetNeedsCheckpoint(bool value) { needs_checkpoint_ = value; } 99 100 void SetNeedsRollback(bool value) { needs_rollback_ = value; } 101 102 private: 103 bool supports_fs_checkpoint_, needs_checkpoint_, needs_rollback_; 104 }; 105 106 static constexpr const char* kTestApexdStatusSysprop = "apexd.status.test"; 107 108 // A test fixture that provides frequently required temp directories for tests 109 class ApexdUnitTest : public ::testing::Test { 110 public: 111 ApexdUnitTest() { 112 built_in_dir_ = StringPrintf("%s/pre-installed-apex", td_.path); 113 data_dir_ = StringPrintf("%s/data-apex", td_.path); 114 decompression_dir_ = StringPrintf("%s/decompressed-apex", td_.path); 115 ota_reserved_dir_ = StringPrintf("%s/ota-reserved", td_.path); 116 hash_tree_dir_ = StringPrintf("%s/apex-hash-tree", td_.path); 117 staged_session_dir_ = StringPrintf("%s/staged-session-dir", td_.path); 118 config_ = {kTestApexdStatusSysprop, {built_in_dir_}, 119 data_dir_.c_str(), decompression_dir_.c_str(), 120 ota_reserved_dir_.c_str(), hash_tree_dir_.c_str(), 121 staged_session_dir_.c_str()}; 122 } 123 124 const std::string& GetBuiltInDir() { return built_in_dir_; } 125 const std::string& GetDataDir() { return data_dir_; } 126 const std::string& GetDecompressionDir() { return decompression_dir_; } 127 const std::string& GetOtaReservedDir() { return ota_reserved_dir_; } 128 const std::string& GetHashTreeDir() { return hash_tree_dir_; } 129 const std::string GetStagedDir(int session_id) { 130 return StringPrintf("%s/session_%d", staged_session_dir_.c_str(), 131 session_id); 132 } 133 134 std::string GetRootDigest(const ApexFile& apex) { 135 if (apex.IsCompressed()) { 136 return ""; 137 } 138 auto digest = apex.VerifyApexVerity(apex.GetBundledPublicKey()); 139 if (!digest.ok()) { 140 return ""; 141 } 142 return digest->root_digest; 143 } 144 145 std::string AddPreInstalledApex(const std::string& apex_name) { 146 fs::copy(GetTestFile(apex_name), built_in_dir_); 147 return StringPrintf("%s/%s", built_in_dir_.c_str(), apex_name.c_str()); 148 } 149 150 std::string AddDataApex(const std::string& apex_name) { 151 fs::copy(GetTestFile(apex_name), data_dir_); 152 return StringPrintf("%s/%s", data_dir_.c_str(), apex_name.c_str()); 153 } 154 155 std::string AddDataApex(const std::string& apex_name, 156 const std::string& target_name) { 157 fs::copy(GetTestFile(apex_name), data_dir_ + "/" + target_name); 158 return StringPrintf("%s/%s", data_dir_.c_str(), target_name.c_str()); 159 } 160 161 // Copies the compressed apex to |built_in_dir| and decompresses it to 162 // |decompressed_dir| and then hard links to |target_dir| 163 std::string PrepareCompressedApex(const std::string& name, 164 const std::string& built_in_dir) { 165 fs::copy(GetTestFile(name), built_in_dir); 166 auto compressed_apex = ApexFile::Open( 167 StringPrintf("%s/%s", built_in_dir.c_str(), name.c_str())); 168 std::vector<ApexFileRef> compressed_apex_list; 169 compressed_apex_list.emplace_back(std::cref(*compressed_apex)); 170 auto return_value = 171 ProcessCompressedApex(compressed_apex_list, /*is_ota_chroot*/ false); 172 return StringPrintf("%s/%s", built_in_dir.c_str(), name.c_str()); 173 } 174 175 std::string PrepareCompressedApex(const std::string& name) { 176 return PrepareCompressedApex(name, built_in_dir_); 177 } 178 179 Result<ApexSession> CreateStagedSession(const std::string& apex_name, 180 int session_id) { 181 CreateDirIfNeeded(GetStagedDir(session_id), 0755); 182 fs::copy(GetTestFile(apex_name), GetStagedDir(session_id)); 183 auto result = ApexSession::CreateSession(session_id); 184 result->SetBuildFingerprint(GetProperty("ro.build.fingerprint", "")); 185 return result; 186 } 187 188 protected: 189 void SetUp() override { 190 SetConfig(config_); 191 ApexFileRepository::GetInstance().Reset(decompression_dir_); 192 ASSERT_EQ(mkdir(built_in_dir_.c_str(), 0755), 0); 193 ASSERT_EQ(mkdir(data_dir_.c_str(), 0755), 0); 194 ASSERT_EQ(mkdir(decompression_dir_.c_str(), 0755), 0); 195 ASSERT_EQ(mkdir(ota_reserved_dir_.c_str(), 0755), 0); 196 ASSERT_EQ(mkdir(hash_tree_dir_.c_str(), 0755), 0); 197 ASSERT_EQ(mkdir(staged_session_dir_.c_str(), 0755), 0); 198 199 DeleteDirContent(ApexSession::GetSessionsDir()); 200 } 201 202 void TearDown() override { DeleteDirContent(ApexSession::GetSessionsDir()); } 203 204 private: 205 TemporaryDir td_; 206 std::string built_in_dir_; 207 std::string data_dir_; 208 std::string decompression_dir_; 209 std::string ota_reserved_dir_; 210 std::string hash_tree_dir_; 211 std::string staged_session_dir_; 212 ApexdConfig config_; 213 }; 214 215 // Apex that does not have pre-installed version, does not get selected 216 TEST_F(ApexdUnitTest, ApexMustHavePreInstalledVersionForSelection) { 217 AddPreInstalledApex("apex.apexd_test.apex"); 218 AddPreInstalledApex("com.android.apex.cts.shim.apex"); 219 auto shared_lib_1 = ApexFile::Open(AddPreInstalledApex( 220 "com.android.apex.test.sharedlibs_generated.v1.libvX.apex")); 221 auto& instance = ApexFileRepository::GetInstance(); 222 // Pre-installed data needs to be present so that we can add data apex 223 ASSERT_TRUE(IsOk(instance.AddPreInstalledApex({GetBuiltInDir()}))); 224 225 auto apexd_test_file = ApexFile::Open(AddDataApex("apex.apexd_test.apex")); 226 auto shim_v1 = ApexFile::Open(AddDataApex("com.android.apex.cts.shim.apex")); 227 // Normally both pre-installed and data apex would be activated for a shared 228 // libs apex, but if they are the same version only the data apex will be. 229 auto shared_lib_2 = ApexFile::Open( 230 AddDataApex("com.android.apex.test.sharedlibs_generated.v1.libvX.apex")); 231 ASSERT_TRUE(IsOk(instance.AddDataApex(GetDataDir()))); 232 233 const auto all_apex = instance.AllApexFilesByName(); 234 // Pass a blank instance so that the data apex files are not considered 235 // pre-installed 236 const ApexFileRepository instance_blank; 237 auto result = SelectApexForActivation(all_apex, instance_blank); 238 ASSERT_EQ(result.size(), 0u); 239 // When passed proper instance they should get selected 240 result = SelectApexForActivation(all_apex, instance); 241 ASSERT_EQ(result.size(), 3u); 242 ASSERT_THAT(result, UnorderedElementsAre(ApexFileEq(ByRef(*apexd_test_file)), 243 ApexFileEq(ByRef(*shim_v1)), 244 ApexFileEq(ByRef(*shared_lib_2)))); 245 } 246 247 // Higher version gets priority when selecting for activation 248 TEST_F(ApexdUnitTest, HigherVersionOfApexIsSelected) { 249 auto apexd_test_file_v2 = 250 ApexFile::Open(AddPreInstalledApex("apex.apexd_test_v2.apex")); 251 AddPreInstalledApex("com.android.apex.cts.shim.apex"); 252 auto& instance = ApexFileRepository::GetInstance(); 253 ASSERT_TRUE(IsOk(instance.AddPreInstalledApex({GetBuiltInDir()}))); 254 255 TemporaryDir data_dir; 256 AddDataApex("apex.apexd_test.apex"); 257 auto shim_v2 = 258 ApexFile::Open(AddDataApex("com.android.apex.cts.shim.v2.apex")); 259 ASSERT_TRUE(IsOk(instance.AddDataApex(GetDataDir()))); 260 261 auto all_apex = instance.AllApexFilesByName(); 262 auto result = SelectApexForActivation(all_apex, instance); 263 ASSERT_EQ(result.size(), 2u); 264 265 ASSERT_THAT(result, 266 UnorderedElementsAre(ApexFileEq(ByRef(*apexd_test_file_v2)), 267 ApexFileEq(ByRef(*shim_v2)))); 268 } 269 270 // When versions are equal, non-pre-installed version gets priority 271 TEST_F(ApexdUnitTest, DataApexGetsPriorityForSameVersions) { 272 AddPreInstalledApex("apex.apexd_test.apex"); 273 AddPreInstalledApex("com.android.apex.cts.shim.apex"); 274 // Initialize pre-installed APEX information 275 auto& instance = ApexFileRepository::GetInstance(); 276 ASSERT_TRUE(IsOk(instance.AddPreInstalledApex({GetBuiltInDir()}))); 277 278 auto apexd_test_file = ApexFile::Open(AddDataApex("apex.apexd_test.apex")); 279 auto shim_v1 = ApexFile::Open(AddDataApex("com.android.apex.cts.shim.apex")); 280 // Initialize ApexFile repo 281 ASSERT_TRUE(IsOk(instance.AddDataApex(GetDataDir()))); 282 283 auto all_apex = instance.AllApexFilesByName(); 284 auto result = SelectApexForActivation(all_apex, instance); 285 ASSERT_EQ(result.size(), 2u); 286 287 ASSERT_THAT(result, UnorderedElementsAre(ApexFileEq(ByRef(*apexd_test_file)), 288 ApexFileEq(ByRef(*shim_v1)))); 289 } 290 291 // Both versions of shared libs can be selected when preinstalled version is 292 // lower than data version 293 TEST_F(ApexdUnitTest, SharedLibsCanHaveBothVersionSelected) { 294 auto shared_lib_v1 = ApexFile::Open(AddPreInstalledApex( 295 "com.android.apex.test.sharedlibs_generated.v1.libvX.apex")); 296 // Initialize pre-installed APEX information 297 auto& instance = ApexFileRepository::GetInstance(); 298 ASSERT_TRUE(IsOk(instance.AddPreInstalledApex({GetBuiltInDir()}))); 299 300 auto shared_lib_v2 = ApexFile::Open( 301 AddDataApex("com.android.apex.test.sharedlibs_generated.v2.libvY.apex")); 302 // Initialize data APEX information 303 ASSERT_TRUE(IsOk(instance.AddDataApex(GetDataDir()))); 304 305 auto all_apex = instance.AllApexFilesByName(); 306 auto result = SelectApexForActivation(all_apex, instance); 307 ASSERT_EQ(result.size(), 2u); 308 309 ASSERT_THAT(result, UnorderedElementsAre(ApexFileEq(ByRef(*shared_lib_v1)), 310 ApexFileEq(ByRef(*shared_lib_v2)))); 311 } 312 313 // Data version of shared libs should not be selected if lower than 314 // preinstalled version 315 TEST_F(ApexdUnitTest, SharedLibsDataVersionDeletedIfLower) { 316 auto shared_lib_v2 = ApexFile::Open(AddPreInstalledApex( 317 "com.android.apex.test.sharedlibs_generated.v2.libvY.apex")); 318 // Initialize pre-installed APEX information 319 auto& instance = ApexFileRepository::GetInstance(); 320 ASSERT_TRUE(IsOk(instance.AddPreInstalledApex({GetBuiltInDir()}))); 321 322 auto shared_lib_v1 = ApexFile::Open( 323 AddDataApex("com.android.apex.test.sharedlibs_generated.v1.libvX.apex")); 324 // Initialize data APEX information 325 ASSERT_TRUE(IsOk(instance.AddDataApex(GetDataDir()))); 326 327 auto all_apex = instance.AllApexFilesByName(); 328 auto result = SelectApexForActivation(all_apex, instance); 329 ASSERT_EQ(result.size(), 1u); 330 331 ASSERT_THAT(result, UnorderedElementsAre(ApexFileEq(ByRef(*shared_lib_v2)))); 332 } 333 334 TEST_F(ApexdUnitTest, ProcessCompressedApex) { 335 auto compressed_apex = ApexFile::Open( 336 AddPreInstalledApex("com.android.apex.compressed.v1.capex")); 337 338 std::vector<ApexFileRef> compressed_apex_list; 339 compressed_apex_list.emplace_back(std::cref(*compressed_apex)); 340 auto return_value = 341 ProcessCompressedApex(compressed_apex_list, /* is_ota_chroot= */ false); 342 343 std::string decompressed_file_path = StringPrintf( 344 "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(), 345 kDecompressedApexPackageSuffix); 346 // Assert output path is not empty 347 auto exists = PathExists(decompressed_file_path); 348 ASSERT_TRUE(IsOk(exists)); 349 ASSERT_TRUE(*exists) << decompressed_file_path << " does not exist"; 350 351 // Assert that decompressed apex is same as original apex 352 const std::string original_apex_file_path = 353 GetTestFile("com.android.apex.compressed.v1_original.apex"); 354 auto comparison_result = 355 CompareFiles(original_apex_file_path, decompressed_file_path); 356 ASSERT_TRUE(IsOk(comparison_result)); 357 ASSERT_TRUE(*comparison_result); 358 359 // Assert that return value contains decompressed APEX 360 auto decompressed_apex = ApexFile::Open(decompressed_file_path); 361 ASSERT_THAT(return_value, 362 UnorderedElementsAre(ApexFileEq(ByRef(*decompressed_apex)))); 363 } 364 365 TEST_F(ApexdUnitTest, ProcessCompressedApexRunsVerification) { 366 auto compressed_apex_mismatch_key = ApexFile::Open(AddPreInstalledApex( 367 "com.android.apex.compressed_key_mismatch_with_original.capex")); 368 auto compressed_apex_version_mismatch = ApexFile::Open( 369 AddPreInstalledApex("com.android.apex.compressed.v1_with_v2_apex.capex")); 370 371 std::vector<ApexFileRef> compressed_apex_list; 372 compressed_apex_list.emplace_back(std::cref(*compressed_apex_mismatch_key)); 373 compressed_apex_list.emplace_back( 374 std::cref(*compressed_apex_version_mismatch)); 375 auto return_value = 376 ProcessCompressedApex(compressed_apex_list, /* is_ota_chroot= */ false); 377 ASSERT_EQ(return_value.size(), 0u); 378 } 379 380 TEST_F(ApexdUnitTest, ValidateDecompressedApex) { 381 auto capex = ApexFile::Open( 382 AddPreInstalledApex("com.android.apex.compressed.v1.capex")); 383 auto decompressed_v1 = ApexFile::Open( 384 AddDataApex("com.android.apex.compressed.v1_original.apex")); 385 386 auto result = 387 ValidateDecompressedApex(std::cref(*capex), std::cref(*decompressed_v1)); 388 ASSERT_TRUE(IsOk(result)); 389 390 // Validation checks version 391 auto decompressed_v2 = ApexFile::Open( 392 AddDataApex("com.android.apex.compressed.v2_original.apex")); 393 result = 394 ValidateDecompressedApex(std::cref(*capex), std::cref(*decompressed_v2)); 395 ASSERT_FALSE(IsOk(result)); 396 ASSERT_THAT( 397 result.error().message(), 398 HasSubstr( 399 "Compressed APEX has different version than decompressed APEX")); 400 401 // Validation check root digest 402 auto decompressed_v1_different_digest = ApexFile::Open(AddDataApex( 403 "com.android.apex.compressed.v1_different_digest_original.apex")); 404 result = ValidateDecompressedApex( 405 std::cref(*capex), std::cref(*decompressed_v1_different_digest)); 406 ASSERT_FALSE(IsOk(result)); 407 ASSERT_THAT(result.error().message(), 408 HasSubstr("does not match with expected root digest")); 409 410 // Validation checks key 411 auto capex_different_key = ApexFile::Open( 412 AddDataApex("com.android.apex.compressed_different_key.capex")); 413 result = ValidateDecompressedApex(std::cref(*capex_different_key), 414 std::cref(*decompressed_v1)); 415 ASSERT_FALSE(IsOk(result)); 416 ASSERT_THAT( 417 result.error().message(), 418 HasSubstr("Public key of compressed APEX is different than original")); 419 } 420 421 TEST_F(ApexdUnitTest, ProcessCompressedApexCanBeCalledMultipleTimes) { 422 auto compressed_apex = ApexFile::Open( 423 AddPreInstalledApex("com.android.apex.compressed.v1.capex")); 424 425 std::vector<ApexFileRef> compressed_apex_list; 426 compressed_apex_list.emplace_back(std::cref(*compressed_apex)); 427 auto return_value = 428 ProcessCompressedApex(compressed_apex_list, /* is_ota_chroot= */ false); 429 ASSERT_EQ(return_value.size(), 1u); 430 431 // Capture the creation time of the decompressed APEX 432 std::error_code ec; 433 auto decompressed_apex_path = StringPrintf( 434 "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(), 435 kDecompressedApexPackageSuffix); 436 auto last_write_time_1 = fs::last_write_time(decompressed_apex_path, ec); 437 ASSERT_FALSE(ec) << "Failed to capture last write time of " 438 << decompressed_apex_path; 439 440 // Now try to decompress the same capex again. It should not fail. 441 return_value = 442 ProcessCompressedApex(compressed_apex_list, /* is_ota_chroot= */ false); 443 ASSERT_EQ(return_value.size(), 1u); 444 445 // Ensure the decompressed APEX file did not change 446 auto last_write_time_2 = fs::last_write_time(decompressed_apex_path, ec); 447 ASSERT_FALSE(ec) << "Failed to capture last write time of " 448 << decompressed_apex_path; 449 ASSERT_EQ(last_write_time_1, last_write_time_2); 450 } 451 452 // Test behavior of ProcessCompressedApex when is_ota_chroot is true 453 TEST_F(ApexdUnitTest, ProcessCompressedApexOnOtaChroot) { 454 auto compressed_apex = ApexFile::Open( 455 AddPreInstalledApex("com.android.apex.compressed.v1.capex")); 456 457 std::vector<ApexFileRef> compressed_apex_list; 458 compressed_apex_list.emplace_back(std::cref(*compressed_apex)); 459 auto return_value = 460 ProcessCompressedApex(compressed_apex_list, /* is_ota_chroot= */ true); 461 ASSERT_EQ(return_value.size(), 1u); 462 463 // Decompressed APEX should be located in decompression_dir 464 std::string decompressed_file_path = 465 StringPrintf("%s/com.android.apex.compressed@1%s", 466 GetDecompressionDir().c_str(), kOtaApexPackageSuffix); 467 // Assert output path is not empty 468 auto exists = PathExists(decompressed_file_path); 469 ASSERT_TRUE(IsOk(exists)); 470 ASSERT_TRUE(*exists) << decompressed_file_path << " does not exist"; 471 472 // Assert that decompressed apex is same as original apex 473 const std::string original_apex_file_path = 474 GetTestFile("com.android.apex.compressed.v1_original.apex"); 475 auto comparison_result = 476 CompareFiles(original_apex_file_path, decompressed_file_path); 477 ASSERT_TRUE(IsOk(comparison_result)); 478 ASSERT_TRUE(*comparison_result); 479 480 // Assert that return value contains the decompressed APEX 481 auto apex_file = ApexFile::Open(decompressed_file_path); 482 ASSERT_THAT(return_value, 483 UnorderedElementsAre(ApexFileEq(ByRef(*apex_file)))); 484 } 485 486 // When decompressing APEX, reuse existing OTA APEX 487 TEST_F(ApexdUnitTest, ProcessCompressedApexReuseOtaApex) { 488 // Push a compressed APEX that will fail to decompress 489 auto compressed_apex = ApexFile::Open(AddPreInstalledApex( 490 "com.android.apex.compressed.v1_not_decompressible.capex")); 491 492 std::vector<ApexFileRef> compressed_apex_list; 493 compressed_apex_list.emplace_back(std::cref(*compressed_apex)); 494 495 // If we try to decompress capex directly, it should fail since the capex 496 // pushed is faulty and cannot be decompressed 497 auto return_value = 498 ProcessCompressedApex(compressed_apex_list, /* is_ota_chroot= */ false); 499 ASSERT_EQ(return_value.size(), 0u); 500 501 // But, if there is an ota_apex present for reuse, it should reuse that 502 // and avoid decompressing the faulty capex 503 504 // Push an OTA apex that should be reused to skip decompression 505 auto ota_apex_path = 506 StringPrintf("%s/com.android.apex.compressed@1%s", 507 GetDecompressionDir().c_str(), kOtaApexPackageSuffix); 508 fs::copy(GetTestFile("com.android.apex.compressed.v1_original.apex"), 509 ota_apex_path); 510 return_value = 511 ProcessCompressedApex(compressed_apex_list, /* is_ota_chroot= */ false); 512 ASSERT_EQ(return_value.size(), 1u); 513 514 // Ota Apex should be cleaned up 515 ASSERT_FALSE(*PathExists(ota_apex_path)); 516 ASSERT_EQ(return_value[0].GetPath(), 517 StringPrintf("%s/com.android.apex.compressed@1%s", 518 GetDecompressionDir().c_str(), 519 kDecompressedApexPackageSuffix)); 520 } 521 522 TEST_F(ApexdUnitTest, ShouldAllocateSpaceForDecompressionNewApex) { 523 auto& instance = ApexFileRepository::GetInstance(); 524 ASSERT_TRUE(IsOk(instance.AddPreInstalledApex({GetBuiltInDir()}))); 525 526 // A brand new compressed APEX is being introduced: selected 527 auto result = 528 ShouldAllocateSpaceForDecompression("com.android.brand.new", 1, instance); 529 ASSERT_TRUE(IsOk(result)); 530 ASSERT_TRUE(*result); 531 } 532 533 TEST_F(ApexdUnitTest, 534 ShouldAllocateSpaceForDecompressionWasNotCompressedBefore) { 535 // Prepare fake pre-installed apex 536 AddPreInstalledApex("apex.apexd_test.apex"); 537 auto& instance = ApexFileRepository::GetInstance(); 538 ASSERT_TRUE(IsOk(instance.AddPreInstalledApex({GetBuiltInDir()}))); 539 540 // An existing pre-installed APEX is now compressed in the OTA: selected 541 { 542 auto result = ShouldAllocateSpaceForDecompression( 543 "com.android.apex.test_package", 1, instance); 544 ASSERT_TRUE(IsOk(result)); 545 ASSERT_TRUE(*result); 546 } 547 548 // Even if there is a data apex (lower version) 549 // Include data apex within calculation now 550 AddDataApex("apex.apexd_test_v2.apex"); 551 ASSERT_TRUE(IsOk(instance.AddDataApex(GetDataDir()))); 552 { 553 auto result = ShouldAllocateSpaceForDecompression( 554 "com.android.apex.test_package", 3, instance); 555 ASSERT_TRUE(IsOk(result)); 556 ASSERT_TRUE(*result); 557 } 558 559 // But not if data apex has equal or higher version 560 { 561 auto result = ShouldAllocateSpaceForDecompression( 562 "com.android.apex.test_package", 2, instance); 563 ASSERT_TRUE(IsOk(result)); 564 ASSERT_FALSE(*result); 565 } 566 } 567 568 TEST_F(ApexdUnitTest, ShouldAllocateSpaceForDecompressionVersionCompare) { 569 // Prepare fake pre-installed apex 570 PrepareCompressedApex("com.android.apex.compressed.v1.capex"); 571 auto& instance = ApexFileRepository::GetInstance(); 572 ASSERT_TRUE(IsOk(instance.AddPreInstalledApex({GetBuiltInDir()}))); 573 ASSERT_TRUE(IsOk(instance.AddDataApex(GetDataDir()))); 574 575 { 576 // New Compressed apex has higher version than decompressed data apex: 577 // selected 578 auto result = ShouldAllocateSpaceForDecompression( 579 "com.android.apex.compressed", 2, instance); 580 ASSERT_TRUE(IsOk(result)); 581 ASSERT_TRUE(*result) 582 << "Higher version test with decompressed data returned false"; 583 } 584 585 // Compare against decompressed data apex 586 { 587 // New Compressed apex has same version as decompressed data apex: not 588 // selected 589 auto result = ShouldAllocateSpaceForDecompression( 590 "com.android.apex.compressed", 1, instance); 591 ASSERT_TRUE(IsOk(result)); 592 ASSERT_FALSE(*result) 593 << "Same version test with decompressed data returned true"; 594 } 595 596 { 597 // New Compressed apex has lower version than decompressed data apex: 598 // selected 599 auto result = ShouldAllocateSpaceForDecompression( 600 "com.android.apex.compressed", 0, instance); 601 ASSERT_TRUE(IsOk(result)); 602 ASSERT_TRUE(*result) 603 << "lower version test with decompressed data returned false"; 604 } 605 606 // Replace decompressed data apex with a higher version 607 ApexFileRepository instance_new(GetDecompressionDir()); 608 ASSERT_TRUE(IsOk(instance_new.AddPreInstalledApex({GetBuiltInDir()}))); 609 TemporaryDir data_dir_new; 610 fs::copy(GetTestFile("com.android.apex.compressed.v2_original.apex"), 611 data_dir_new.path); 612 ASSERT_TRUE(IsOk(instance_new.AddDataApex(data_dir_new.path))); 613 614 { 615 // New Compressed apex has higher version as data apex: selected 616 auto result = ShouldAllocateSpaceForDecompression( 617 "com.android.apex.compressed", 3, instance_new); 618 ASSERT_TRUE(IsOk(result)); 619 ASSERT_TRUE(*result) << "Higher version test with new data returned false"; 620 } 621 622 { 623 // New Compressed apex has same version as data apex: not selected 624 auto result = ShouldAllocateSpaceForDecompression( 625 "com.android.apex.compressed", 2, instance_new); 626 ASSERT_TRUE(IsOk(result)); 627 ASSERT_FALSE(*result) << "Same version test with new data returned true"; 628 } 629 630 { 631 // New Compressed apex has lower version than data apex: not selected 632 auto result = ShouldAllocateSpaceForDecompression( 633 "com.android.apex.compressed", 1, instance_new); 634 ASSERT_TRUE(IsOk(result)); 635 ASSERT_FALSE(*result) << "lower version test with new data returned true"; 636 } 637 } 638 639 TEST_F(ApexdUnitTest, ReserveSpaceForCompressedApexCreatesSingleFile) { 640 TemporaryDir dest_dir; 641 // Reserving space should create a single file in dest_dir with exact size 642 643 ASSERT_TRUE(IsOk(ReserveSpaceForCompressedApex(100, dest_dir.path))); 644 auto files = ReadDir(dest_dir.path, [](auto _) { return true; }); 645 ASSERT_TRUE(IsOk(files)); 646 ASSERT_EQ(files->size(), 1u); 647 EXPECT_EQ(fs::file_size((*files)[0]), 100u); 648 } 649 650 TEST_F(ApexdUnitTest, ReserveSpaceForCompressedApexSafeToCallMultipleTimes) { 651 TemporaryDir dest_dir; 652 // Calling ReserveSpaceForCompressedApex multiple times should still create 653 // a single file 654 ASSERT_TRUE(IsOk(ReserveSpaceForCompressedApex(100, dest_dir.path))); 655 ASSERT_TRUE(IsOk(ReserveSpaceForCompressedApex(100, dest_dir.path))); 656 auto files = ReadDir(dest_dir.path, [](auto _) { return true; }); 657 ASSERT_TRUE(IsOk(files)); 658 ASSERT_EQ(files->size(), 1u); 659 EXPECT_EQ(fs::file_size((*files)[0]), 100u); 660 } 661 662 TEST_F(ApexdUnitTest, ReserveSpaceForCompressedApexShrinkAndGrow) { 663 TemporaryDir dest_dir; 664 665 // Create a 100 byte file 666 ASSERT_TRUE(IsOk(ReserveSpaceForCompressedApex(100, dest_dir.path))); 667 668 // Should be able to shrink and grow the reserved space 669 ASSERT_TRUE(IsOk(ReserveSpaceForCompressedApex(1000, dest_dir.path))); 670 auto files = ReadDir(dest_dir.path, [](auto _) { return true; }); 671 ASSERT_TRUE(IsOk(files)); 672 ASSERT_EQ(files->size(), 1u); 673 EXPECT_EQ(fs::file_size((*files)[0]), 1000u); 674 675 ASSERT_TRUE(IsOk(ReserveSpaceForCompressedApex(10, dest_dir.path))); 676 files = ReadDir(dest_dir.path, [](auto _) { return true; }); 677 ASSERT_TRUE(IsOk(files)); 678 ASSERT_EQ(files->size(), 1u); 679 EXPECT_EQ(fs::file_size((*files)[0]), 10u); 680 } 681 682 TEST_F(ApexdUnitTest, ReserveSpaceForCompressedApexDeallocateIfPassedZero) { 683 TemporaryDir dest_dir; 684 685 // Create a file first 686 ASSERT_TRUE(IsOk(ReserveSpaceForCompressedApex(100, dest_dir.path))); 687 auto files = ReadDir(dest_dir.path, [](auto _) { return true; }); 688 ASSERT_TRUE(IsOk(files)); 689 ASSERT_EQ(files->size(), 1u); 690 691 // Should delete the reserved file if size passed is 0 692 ASSERT_TRUE(IsOk(ReserveSpaceForCompressedApex(0, dest_dir.path))); 693 files = ReadDir(dest_dir.path, [](auto _) { return true; }); 694 ASSERT_TRUE(IsOk(files)); 695 ASSERT_EQ(files->size(), 0u); 696 } 697 698 TEST_F(ApexdUnitTest, ReserveSpaceForCapexCleansOtaApex) { 699 TemporaryDir dest_dir; 700 701 auto ota_apex_path = StringPrintf( 702 "%s/ota_apex%s", GetDecompressionDir().c_str(), kOtaApexPackageSuffix); 703 auto create_ota_apex = [&]() { 704 // Create an ota_apex first 705 fs::copy(GetTestFile("com.android.apex.compressed.v1_original.apex"), 706 ota_apex_path); 707 auto path_exists = PathExists(ota_apex_path); 708 ASSERT_TRUE(*path_exists); 709 }; 710 create_ota_apex(); 711 712 // Should not delete the reserved file if size passed is negative 713 ASSERT_FALSE(IsOk(ReserveSpaceForCompressedApex(-1, dest_dir.path))); 714 auto path_exists = PathExists(ota_apex_path); 715 ASSERT_TRUE(*path_exists); 716 717 // Should delete the reserved file if size passed is 0 718 ASSERT_TRUE(IsOk(ReserveSpaceForCompressedApex(0, dest_dir.path))); 719 path_exists = PathExists(ota_apex_path); 720 ASSERT_FALSE(*path_exists); 721 722 create_ota_apex(); 723 // Should delete the reserved file if size passed is positive 724 ASSERT_TRUE(IsOk(ReserveSpaceForCompressedApex(10, dest_dir.path))); 725 path_exists = PathExists(ota_apex_path); 726 ASSERT_FALSE(*path_exists); 727 } 728 729 TEST_F(ApexdUnitTest, ReserveSpaceForCompressedApexErrorForNegativeValue) { 730 TemporaryDir dest_dir; 731 // Should return error if negative value is passed 732 ASSERT_FALSE(IsOk(ReserveSpaceForCompressedApex(-1, dest_dir.path))); 733 } 734 735 // A test fixture to use for tests that mount/unmount apexes. 736 class ApexdMountTest : public ApexdUnitTest { 737 public: 738 739 void UnmountOnTearDown(const std::string& apex_file) { 740 to_unmount_.push_back(apex_file); 741 } 742 743 protected: 744 void SetUp() final { 745 ApexdUnitTest::SetUp(); 746 GetApexDatabaseForTesting().Reset(); 747 ASSERT_TRUE(IsOk(SetUpApexTestEnvironment())); 748 } 749 750 void TearDown() final { 751 ApexdUnitTest::TearDown(); 752 for (const auto& apex : to_unmount_) { 753 if (auto status = DeactivatePackage(apex); !status.ok()) { 754 LOG(ERROR) << "Failed to unmount " << apex << " : " << status.error(); 755 } 756 } 757 } 758 759 private: 760 MountNamespaceRestorer restorer_; 761 std::vector<std::string> to_unmount_; 762 }; 763 764 // TODO(b/187864524): cover other negative scenarios. 765 TEST_F(ApexdMountTest, InstallPackageRejectsApexWithoutRebootlessSupport) { 766 std::string file_path = AddPreInstalledApex("apex.apexd_test.apex"); 767 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); 768 769 ASSERT_TRUE(IsOk(ActivatePackage(file_path))); 770 UnmountOnTearDown(file_path); 771 772 auto ret = InstallPackage(GetTestFile("apex.apexd_test.apex")); 773 ASSERT_FALSE(IsOk(ret)); 774 ASSERT_THAT(ret.error().message(), 775 HasSubstr("does not support non-staged update")); 776 } 777 778 TEST_F(ApexdMountTest, InstallPackageRejectsNoPreInstalledApex) { 779 auto ret = InstallPackage(GetTestFile("test.rebootless_apex_v1.apex")); 780 ASSERT_FALSE(IsOk(ret)); 781 ASSERT_THAT( 782 ret.error().message(), 783 HasSubstr("No active version found for package test.apex.rebootless")); 784 } 785 786 TEST_F(ApexdMountTest, InstallPackageRejectsNoHashtree) { 787 std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex"); 788 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); 789 790 ASSERT_TRUE(IsOk(ActivatePackage(file_path))); 791 UnmountOnTearDown(file_path); 792 793 auto ret = 794 InstallPackage(GetTestFile("test.rebootless_apex_v2_no_hashtree.apex")); 795 ASSERT_FALSE(IsOk(ret)); 796 ASSERT_THAT(ret.error().message(), 797 HasSubstr(" does not have an embedded hash tree")); 798 } 799 800 TEST_F(ApexdMountTest, InstallPackageRejectsNoActiveApex) { 801 std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex"); 802 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); 803 804 auto ret = InstallPackage(GetTestFile("test.rebootless_apex_v2.apex")); 805 ASSERT_FALSE(IsOk(ret)); 806 ASSERT_THAT( 807 ret.error().message(), 808 HasSubstr("No active version found for package test.apex.rebootless")); 809 } 810 811 TEST_F(ApexdMountTest, InstallPackageRejectsManifestMismatch) { 812 std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex"); 813 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); 814 815 ASSERT_TRUE(IsOk(ActivatePackage(file_path))); 816 UnmountOnTearDown(file_path); 817 818 auto ret = InstallPackage( 819 GetTestFile("test.rebootless_apex_manifest_mismatch.apex")); 820 ASSERT_FALSE(IsOk(ret)); 821 ASSERT_THAT( 822 ret.error().message(), 823 HasSubstr( 824 "Manifest inside filesystem does not match manifest outside it")); 825 } 826 827 TEST_F(ApexdMountTest, InstallPackageRejectsCorrupted) { 828 std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex"); 829 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); 830 831 ASSERT_TRUE(IsOk(ActivatePackage(file_path))); 832 UnmountOnTearDown(file_path); 833 834 auto ret = InstallPackage(GetTestFile("test.rebootless_apex_corrupted.apex")); 835 ASSERT_FALSE(IsOk(ret)); 836 ASSERT_THAT(ret.error().message(), HasSubstr("Can't verify /dev/block/dm-")); 837 } 838 839 TEST_F(ApexdMountTest, InstallPackageRejectsProvidesSharedLibs) { 840 std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex"); 841 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); 842 843 ASSERT_TRUE(IsOk(ActivatePackage(file_path))); 844 UnmountOnTearDown(file_path); 845 846 auto ret = InstallPackage( 847 GetTestFile("test.rebootless_apex_provides_sharedlibs.apex")); 848 ASSERT_FALSE(IsOk(ret)); 849 ASSERT_THAT(ret.error().message(), HasSubstr(" is a shared libs APEX")); 850 } 851 852 TEST_F(ApexdMountTest, InstallPackageRejectsProvidesNativeLibs) { 853 std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex"); 854 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); 855 856 ASSERT_TRUE(IsOk(ActivatePackage(file_path))); 857 UnmountOnTearDown(file_path); 858 859 auto ret = InstallPackage( 860 GetTestFile("test.rebootless_apex_provides_native_libs.apex")); 861 ASSERT_FALSE(IsOk(ret)); 862 ASSERT_THAT(ret.error().message(), HasSubstr(" provides native libs")); 863 } 864 865 TEST_F(ApexdMountTest, InstallPackageRejectsRequiresSharedApexLibs) { 866 std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex"); 867 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); 868 869 ASSERT_TRUE(IsOk(ActivatePackage(file_path))); 870 UnmountOnTearDown(file_path); 871 872 auto ret = InstallPackage( 873 GetTestFile("test.rebootless_apex_requires_shared_apex_libs.apex")); 874 ASSERT_FALSE(IsOk(ret)); 875 ASSERT_THAT(ret.error().message(), HasSubstr(" requires shared apex libs")); 876 } 877 878 TEST_F(ApexdMountTest, InstallPackageRejectsJniLibs) { 879 std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex"); 880 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); 881 882 ASSERT_TRUE(IsOk(ActivatePackage(file_path))); 883 UnmountOnTearDown(file_path); 884 885 auto ret = InstallPackage(GetTestFile("test.rebootless_apex_jni_libs.apex")); 886 ASSERT_FALSE(IsOk(ret)); 887 ASSERT_THAT(ret.error().message(), HasSubstr(" requires JNI libs")); 888 } 889 890 TEST_F(ApexdMountTest, InstallPackageRejectsAddRequiredNativeLib) { 891 std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex"); 892 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); 893 894 ASSERT_TRUE(IsOk(ActivatePackage(file_path))); 895 UnmountOnTearDown(file_path); 896 897 auto ret = 898 InstallPackage(GetTestFile("test.rebootless_apex_add_native_lib.apex")); 899 ASSERT_FALSE(IsOk(ret)); 900 ASSERT_THAT(ret.error().message(), 901 HasSubstr("Set of native libs required by")); 902 ASSERT_THAT( 903 ret.error().message(), 904 HasSubstr("differs from the one required by the currently active")); 905 } 906 907 TEST_F(ApexdMountTest, InstallPackageRejectsRemovesRequiredNativeLib) { 908 std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex"); 909 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); 910 911 ASSERT_TRUE(IsOk(ActivatePackage(file_path))); 912 UnmountOnTearDown(file_path); 913 914 auto ret = InstallPackage( 915 GetTestFile("test.rebootless_apex_remove_native_lib.apex")); 916 ASSERT_FALSE(IsOk(ret)); 917 ASSERT_THAT(ret.error().message(), 918 HasSubstr("Set of native libs required by")); 919 ASSERT_THAT( 920 ret.error().message(), 921 HasSubstr("differs from the one required by the currently active")); 922 } 923 924 TEST_F(ApexdMountTest, InstallPackageRejectsAppInApex) { 925 std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex"); 926 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); 927 928 ASSERT_TRUE(IsOk(ActivatePackage(file_path))); 929 UnmountOnTearDown(file_path); 930 931 auto ret = 932 InstallPackage(GetTestFile("test.rebootless_apex_app_in_apex.apex")); 933 ASSERT_FALSE(IsOk(ret)); 934 ASSERT_THAT(ret.error().message(), HasSubstr("contains app inside")); 935 } 936 937 TEST_F(ApexdMountTest, InstallPackageRejectsPrivAppInApex) { 938 std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex"); 939 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); 940 941 ASSERT_TRUE(IsOk(ActivatePackage(file_path))); 942 UnmountOnTearDown(file_path); 943 944 auto ret = 945 InstallPackage(GetTestFile("test.rebootless_apex_priv_app_in_apex.apex")); 946 ASSERT_FALSE(IsOk(ret)); 947 ASSERT_THAT(ret.error().message(), HasSubstr("contains priv-app inside")); 948 } 949 950 TEST_F(ApexdMountTest, InstallPackagePreInstallVersionActive) { 951 std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex"); 952 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); 953 954 ASSERT_TRUE(IsOk(ActivatePackage(file_path))); 955 UnmountOnTearDown(file_path); 956 957 { 958 auto active_apex = GetActivePackage("test.apex.rebootless"); 959 ASSERT_TRUE(IsOk(active_apex)); 960 ASSERT_EQ(active_apex->GetPath(), file_path); 961 } 962 963 auto ret = InstallPackage(GetTestFile("test.rebootless_apex_v2.apex")); 964 ASSERT_TRUE(IsOk(ret)); 965 UnmountOnTearDown(ret->GetPath()); 966 967 auto apex_mounts = GetApexMounts(); 968 ASSERT_THAT(apex_mounts, 969 UnorderedElementsAre("/apex/test.apex.rebootless", 970 "/apex/test.apex.rebootless@2")); 971 972 // Check that /apex/test.apex.rebootless is a bind mount of 973 // /apex/test.apex.rebootless@2. 974 auto manifest = ReadManifest("/apex/test.apex.rebootless/apex_manifest.pb"); 975 ASSERT_TRUE(IsOk(manifest)); 976 ASSERT_EQ(2u, manifest->version()); 977 978 // Check that GetActivePackage correctly reports upgraded version. 979 auto active_apex = GetActivePackage("test.apex.rebootless"); 980 ASSERT_TRUE(IsOk(active_apex)); 981 ASSERT_EQ(active_apex->GetPath(), ret->GetPath()); 982 983 // Check that pre-installed APEX is still around 984 ASSERT_EQ(0, access(file_path.c_str(), F_OK)) 985 << "Can't access " << file_path << " : " << strerror(errno); 986 987 auto& db = GetApexDatabaseForTesting(); 988 // Check that upgraded APEX is mounted on top of dm-verity device. 989 db.ForallMountedApexes( 990 "test.apex.rebootless", [&](const MountedApexData& data, bool latest) { 991 ASSERT_TRUE(latest); 992 ASSERT_EQ(data.full_path, ret->GetPath()); 993 ASSERT_EQ(data.device_name, "test.apex.rebootless@2_1"); 994 }); 995 } 996 997 TEST_F(ApexdMountTest, InstallPackagePreInstallVersionActiveSamegrade) { 998 std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex"); 999 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); 1000 1001 ASSERT_TRUE(IsOk(ActivatePackage(file_path))); 1002 UnmountOnTearDown(file_path); 1003 1004 { 1005 auto active_apex = GetActivePackage("test.apex.rebootless"); 1006 ASSERT_TRUE(IsOk(active_apex)); 1007 ASSERT_EQ(active_apex->GetPath(), file_path); 1008 } 1009 1010 auto ret = InstallPackage(GetTestFile("test.rebootless_apex_v1.apex")); 1011 ASSERT_TRUE(IsOk(ret)); 1012 UnmountOnTearDown(ret->GetPath()); 1013 1014 auto apex_mounts = GetApexMounts(); 1015 ASSERT_THAT(apex_mounts, 1016 UnorderedElementsAre("/apex/test.apex.rebootless", 1017 "/apex/test.apex.rebootless@1")); 1018 1019 // Check that GetActivePackage correctly reports upgraded version. 1020 auto active_apex = GetActivePackage("test.apex.rebootless"); 1021 ASSERT_TRUE(IsOk(active_apex)); 1022 ASSERT_EQ(active_apex->GetPath(), ret->GetPath()); 1023 1024 // Check that pre-installed APEX is still around 1025 ASSERT_EQ(0, access(file_path.c_str(), F_OK)) 1026 << "Can't access " << file_path << " : " << strerror(errno); 1027 1028 auto& db = GetApexDatabaseForTesting(); 1029 // Check that upgraded APEX is mounted on top of dm-verity device. 1030 db.ForallMountedApexes( 1031 "test.apex.rebootless", [&](const MountedApexData& data, bool latest) { 1032 ASSERT_TRUE(latest); 1033 ASSERT_EQ(data.full_path, ret->GetPath()); 1034 ASSERT_EQ(data.device_name, "test.apex.rebootless@1_1"); 1035 }); 1036 } 1037 1038 TEST_F(ApexdMountTest, InstallPackageDataVersionActive) { 1039 AddPreInstalledApex("test.rebootless_apex_v1.apex"); 1040 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); 1041 1042 std::string file_path = AddDataApex("test.rebootless_apex_v1.apex"); 1043 ASSERT_TRUE(IsOk(ActivatePackage(file_path))); 1044 UnmountOnTearDown(file_path); 1045 1046 { 1047 auto active_apex = GetActivePackage("test.apex.rebootless"); 1048 ASSERT_TRUE(IsOk(active_apex)); 1049 ASSERT_EQ(active_apex->GetPath(), file_path); 1050 } 1051 1052 auto ret = InstallPackage(GetTestFile("test.rebootless_apex_v2.apex")); 1053 ASSERT_TRUE(IsOk(ret)); 1054 UnmountOnTearDown(ret->GetPath()); 1055 1056 auto apex_mounts = GetApexMounts(); 1057 ASSERT_THAT(apex_mounts, 1058 UnorderedElementsAre("/apex/test.apex.rebootless", 1059 "/apex/test.apex.rebootless@2")); 1060 1061 // Check that /apex/test.apex.rebootless is a bind mount of 1062 // /apex/test.apex.rebootless@2. 1063 auto manifest = ReadManifest("/apex/test.apex.rebootless/apex_manifest.pb"); 1064 ASSERT_TRUE(IsOk(manifest)); 1065 ASSERT_EQ(2u, manifest->version()); 1066 1067 // Check that GetActivePackage correctly reports upgraded version. 1068 auto active_apex = GetActivePackage("test.apex.rebootless"); 1069 ASSERT_TRUE(IsOk(active_apex)); 1070 ASSERT_EQ(active_apex->GetPath(), ret->GetPath()); 1071 1072 // Check that previously active APEX was deleted. 1073 ASSERT_EQ(-1, access(file_path.c_str(), F_OK)); 1074 ASSERT_EQ(ENOENT, errno); 1075 1076 auto& db = GetApexDatabaseForTesting(); 1077 // Check that upgraded APEX is mounted on top of dm-verity device. 1078 db.ForallMountedApexes( 1079 "test.apex.rebootless", [&](const MountedApexData& data, bool latest) { 1080 ASSERT_TRUE(latest); 1081 ASSERT_EQ(data.full_path, ret->GetPath()); 1082 ASSERT_EQ(data.device_name, "test.apex.rebootless@2_1"); 1083 }); 1084 } 1085 1086 TEST_F(ApexdMountTest, InstallPackageResolvesPathCollision) { 1087 AddPreInstalledApex("test.rebootless_apex_v1.apex"); 1088 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); 1089 1090 std::string file_path = AddDataApex("test.rebootless_apex_v1.apex", 1091 "test.apex.rebootless@1_1.apex"); 1092 ASSERT_TRUE(IsOk(ActivatePackage(file_path))); 1093 UnmountOnTearDown(file_path); 1094 1095 { 1096 auto active_apex = GetActivePackage("test.apex.rebootless"); 1097 ASSERT_TRUE(IsOk(active_apex)); 1098 ASSERT_EQ(active_apex->GetPath(), file_path); 1099 } 1100 1101 auto ret = InstallPackage(GetTestFile("test.rebootless_apex_v1.apex")); 1102 ASSERT_TRUE(IsOk(ret)); 1103 UnmountOnTearDown(ret->GetPath()); 1104 1105 auto apex_mounts = GetApexMounts(); 1106 ASSERT_THAT(apex_mounts, 1107 UnorderedElementsAre("/apex/test.apex.rebootless", 1108 "/apex/test.apex.rebootless@1")); 1109 1110 // Check that /apex/test.apex.rebootless is a bind mount of 1111 // /apex/test.apex.rebootless@2. 1112 auto manifest = ReadManifest("/apex/test.apex.rebootless/apex_manifest.pb"); 1113 ASSERT_TRUE(IsOk(manifest)); 1114 ASSERT_EQ(1u, manifest->version()); 1115 1116 // Check that GetActivePackage correctly reports upgraded version. 1117 auto active_apex = GetActivePackage("test.apex.rebootless"); 1118 ASSERT_TRUE(IsOk(active_apex)); 1119 ASSERT_EQ(active_apex->GetPath(), ret->GetPath()); 1120 1121 // Check that we correctly resolved active apex path collision. 1122 ASSERT_EQ(active_apex->GetPath(), 1123 GetDataDir() + "/test.apex.rebootless@1_2.apex"); 1124 1125 // Check that previously active APEX was deleted. 1126 ASSERT_EQ(-1, access(file_path.c_str(), F_OK)); 1127 ASSERT_EQ(ENOENT, errno); 1128 1129 auto& db = GetApexDatabaseForTesting(); 1130 // Check that upgraded APEX is mounted on top of dm-verity device. 1131 db.ForallMountedApexes( 1132 "test.apex.rebootless", [&](const MountedApexData& data, bool latest) { 1133 ASSERT_TRUE(latest); 1134 ASSERT_EQ(data.full_path, ret->GetPath()); 1135 ASSERT_EQ(data.device_name, "test.apex.rebootless@1_2"); 1136 }); 1137 } 1138 1139 TEST_F(ApexdMountTest, InstallPackageDataVersionActiveSamegrade) { 1140 AddPreInstalledApex("test.rebootless_apex_v1.apex"); 1141 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); 1142 1143 std::string file_path = AddDataApex("test.rebootless_apex_v2.apex"); 1144 ASSERT_TRUE(IsOk(ActivatePackage(file_path))); 1145 UnmountOnTearDown(file_path); 1146 1147 { 1148 auto active_apex = GetActivePackage("test.apex.rebootless"); 1149 ASSERT_TRUE(IsOk(active_apex)); 1150 ASSERT_EQ(active_apex->GetPath(), file_path); 1151 } 1152 1153 auto ret = InstallPackage(GetTestFile("test.rebootless_apex_v2.apex")); 1154 ASSERT_TRUE(IsOk(ret)); 1155 UnmountOnTearDown(ret->GetPath()); 1156 1157 auto apex_mounts = GetApexMounts(); 1158 ASSERT_THAT(apex_mounts, 1159 UnorderedElementsAre("/apex/test.apex.rebootless", 1160 "/apex/test.apex.rebootless@2")); 1161 1162 // Check that /apex/test.apex.rebootless is a bind mount of 1163 // /apex/test.apex.rebootless@2. 1164 auto manifest = ReadManifest("/apex/test.apex.rebootless/apex_manifest.pb"); 1165 ASSERT_TRUE(IsOk(manifest)); 1166 ASSERT_EQ(2u, manifest->version()); 1167 1168 // Check that GetActivePackage correctly reports upgraded version. 1169 auto active_apex = GetActivePackage("test.apex.rebootless"); 1170 ASSERT_TRUE(IsOk(active_apex)); 1171 ASSERT_EQ(active_apex->GetPath(), ret->GetPath()); 1172 1173 // Check that previously active APEX was deleted. 1174 ASSERT_EQ(-1, access(file_path.c_str(), F_OK)); 1175 ASSERT_EQ(ENOENT, errno); 1176 1177 auto& db = GetApexDatabaseForTesting(); 1178 // Check that upgraded APEX is mounted on top of dm-verity device. 1179 db.ForallMountedApexes( 1180 "test.apex.rebootless", [&](const MountedApexData& data, bool latest) { 1181 ASSERT_TRUE(latest); 1182 ASSERT_EQ(data.full_path, ret->GetPath()); 1183 ASSERT_EQ(data.device_name, "test.apex.rebootless@2_1"); 1184 }); 1185 } 1186 1187 TEST_F(ApexdMountTest, InstallPackageUnmountFailsPreInstalledApexActive) { 1188 std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex"); 1189 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); 1190 1191 ASSERT_TRUE(IsOk(ActivatePackage(file_path))); 1192 UnmountOnTearDown(file_path); 1193 1194 { 1195 auto active_apex = GetActivePackage("test.apex.rebootless"); 1196 ASSERT_TRUE(IsOk(active_apex)); 1197 ASSERT_EQ(active_apex->GetPath(), file_path); 1198 } 1199 1200 unique_fd fd(open("/apex/test.apex.rebootless/apex_manifest.pb", 1201 O_RDONLY | O_CLOEXEC)); 1202 ASSERT_NE(-1, fd.get()); 1203 1204 auto ret = InstallPackage(GetTestFile("test.rebootless_apex_v2.apex")); 1205 ASSERT_FALSE(IsOk(ret)); 1206 1207 auto apex_mounts = GetApexMounts(); 1208 ASSERT_THAT(apex_mounts, 1209 UnorderedElementsAre("/apex/test.apex.rebootless", 1210 "/apex/test.apex.rebootless@1")); 1211 1212 // Check that GetActivePackage correctly reports upgraded version. 1213 auto active_apex = GetActivePackage("test.apex.rebootless"); 1214 ASSERT_TRUE(IsOk(active_apex)); 1215 ASSERT_EQ(active_apex->GetPath(), file_path); 1216 1217 // Check that old APEX is still around 1218 ASSERT_EQ(0, access(file_path.c_str(), F_OK)) 1219 << "Can't access " << file_path << " : " << strerror(errno); 1220 1221 auto& db = GetApexDatabaseForTesting(); 1222 // Check that upgraded APEX is mounted on top of dm-verity device. 1223 db.ForallMountedApexes("test.apex.rebootless", 1224 [&](const MountedApexData& data, bool latest) { 1225 ASSERT_TRUE(latest); 1226 ASSERT_EQ(data.full_path, file_path); 1227 }); 1228 } 1229 1230 TEST_F(ApexdMountTest, InstallPackageUnmountFailedUpdatedApexActive) { 1231 AddPreInstalledApex("test.rebootless_apex_v1.apex"); 1232 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); 1233 1234 std::string file_path = AddDataApex("test.rebootless_apex_v1.apex"); 1235 1236 ASSERT_TRUE(IsOk(ActivatePackage(file_path))); 1237 UnmountOnTearDown(file_path); 1238 1239 { 1240 auto active_apex = GetActivePackage("test.apex.rebootless"); 1241 ASSERT_TRUE(IsOk(active_apex)); 1242 ASSERT_EQ(active_apex->GetPath(), file_path); 1243 } 1244 1245 unique_fd fd(open("/apex/test.apex.rebootless/apex_manifest.pb", 1246 O_RDONLY | O_CLOEXEC)); 1247 ASSERT_NE(-1, fd.get()); 1248 1249 auto ret = InstallPackage(GetTestFile("test.rebootless_apex_v2.apex")); 1250 ASSERT_FALSE(IsOk(ret)); 1251 1252 auto apex_mounts = GetApexMounts(); 1253 ASSERT_THAT(apex_mounts, 1254 UnorderedElementsAre("/apex/test.apex.rebootless", 1255 "/apex/test.apex.rebootless@1")); 1256 1257 // Check that GetActivePackage correctly reports old apex. 1258 auto active_apex = GetActivePackage("test.apex.rebootless"); 1259 ASSERT_TRUE(IsOk(active_apex)); 1260 ASSERT_EQ(active_apex->GetPath(), file_path); 1261 1262 // Check that old APEX is still around 1263 ASSERT_EQ(0, access(file_path.c_str(), F_OK)) 1264 << "Can't access " << file_path << " : " << strerror(errno); 1265 1266 auto& db = GetApexDatabaseForTesting(); 1267 db.ForallMountedApexes( 1268 "test.apex.rebootless", [&](const MountedApexData& data, bool latest) { 1269 ASSERT_TRUE(latest); 1270 ASSERT_EQ(data.full_path, file_path); 1271 ASSERT_EQ(data.device_name, "test.apex.rebootless@1"); 1272 }); 1273 } 1274 1275 TEST_F(ApexdMountTest, InstallPackageUpdatesApexInfoList) { 1276 auto apex_1 = AddPreInstalledApex("test.rebootless_apex_v1.apex"); 1277 auto apex_2 = AddPreInstalledApex("apex.apexd_test.apex"); 1278 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); 1279 1280 UnmountOnTearDown(apex_1); 1281 UnmountOnTearDown(apex_2); 1282 ASSERT_TRUE(IsOk(ActivatePackage(apex_1))); 1283 ASSERT_TRUE(IsOk(ActivatePackage(apex_2))); 1284 1285 // Call OnAllPackagesActivated to create /apex/apex-info-list.xml. 1286 OnAllPackagesActivated(/* is_bootstrap= */ false); 1287 // Check /apex/apex-info-list.xml was created. 1288 ASSERT_EQ(0, access("/apex/apex-info-list.xml", F_OK)); 1289 1290 auto ret = InstallPackage(GetTestFile("test.rebootless_apex_v2.apex")); 1291 ASSERT_TRUE(IsOk(ret)); 1292 UnmountOnTearDown(ret->GetPath()); 1293 1294 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); 1295 auto info_list = 1296 com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); 1297 ASSERT_TRUE(info_list.has_value()); 1298 auto apex_info_xml_1 = com::android::apex::ApexInfo( 1299 /* moduleName= */ "test.apex.rebootless", 1300 /* modulePath= */ apex_1, 1301 /* preinstalledModulePath= */ apex_1, 1302 /* versionCode= */ 1, /* versionName= */ "1", 1303 /* isFactory= */ true, /* isActive= */ false, GetMTime(apex_1)); 1304 auto apex_info_xml_2 = com::android::apex::ApexInfo( 1305 /* moduleName= */ "com.android.apex.test_package", 1306 /* modulePath= */ apex_2, /* preinstalledModulePath= */ apex_2, 1307 /* versionCode= */ 1, /* versionName= */ "1", /* isFactory= */ true, 1308 /* isActive= */ true, GetMTime(apex_2)); 1309 auto apex_info_xml_3 = com::android::apex::ApexInfo( 1310 /* moduleName= */ "test.apex.rebootless", 1311 /* modulePath= */ ret->GetPath(), 1312 /* preinstalledModulePath= */ apex_1, 1313 /* versionCode= */ 2, /* versionName= */ "2", 1314 /* isFactory= */ false, /* isActive= */ true, GetMTime(ret->GetPath())); 1315 ASSERT_THAT(info_list->getApexInfo(), 1316 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1), 1317 ApexInfoXmlEq(apex_info_xml_2), 1318 ApexInfoXmlEq(apex_info_xml_3))); 1319 } 1320 1321 TEST_F(ApexdMountTest, ActivatePackage) { 1322 std::string file_path = AddPreInstalledApex("apex.apexd_test.apex"); 1323 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); 1324 1325 ASSERT_TRUE(IsOk(ActivatePackage(file_path))); 1326 UnmountOnTearDown(file_path); 1327 1328 auto active_apex = GetActivePackage("com.android.apex.test_package"); 1329 ASSERT_TRUE(IsOk(active_apex)); 1330 ASSERT_EQ(active_apex->GetPath(), file_path); 1331 1332 auto apex_mounts = GetApexMounts(); 1333 ASSERT_THAT(apex_mounts, 1334 UnorderedElementsAre("/apex/com.android.apex.test_package", 1335 "/apex/com.android.apex.test_package@1")); 1336 1337 ASSERT_TRUE(IsOk(DeactivatePackage(file_path))); 1338 ASSERT_FALSE(IsOk(GetActivePackage("com.android.apex.test_package"))); 1339 1340 auto new_apex_mounts = GetApexMounts(); 1341 ASSERT_EQ(new_apex_mounts.size(), 0u); 1342 } 1343 1344 TEST_F(ApexdMountTest, ActivateDeactivateSharedLibsApex) { 1345 ASSERT_EQ(mkdir("/apex/sharedlibs", 0755), 0); 1346 ASSERT_EQ(mkdir("/apex/sharedlibs/lib", 0755), 0); 1347 ASSERT_EQ(mkdir("/apex/sharedlibs/lib64", 0755), 0); 1348 auto deleter = make_scope_guard([]() { 1349 std::error_code ec; 1350 fs::remove_all("/apex/sharedlibs", ec); 1351 if (ec) { 1352 LOG(ERROR) << "Failed to delete /apex/sharedlibs : " << ec; 1353 } 1354 }); 1355 1356 std::string file_path = AddPreInstalledApex( 1357 "com.android.apex.test.sharedlibs_generated.v1.libvX.apex"); 1358 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); 1359 1360 UnmountOnTearDown(file_path); 1361 ASSERT_TRUE(IsOk(ActivatePackage(file_path))); 1362 1363 auto active_apex = GetActivePackage("com.android.apex.test.sharedlibs"); 1364 ASSERT_TRUE(IsOk(active_apex)); 1365 ASSERT_EQ(active_apex->GetPath(), file_path); 1366 1367 auto apex_mounts = GetApexMounts(); 1368 ASSERT_THAT(apex_mounts, 1369 UnorderedElementsAre("/apex/com.android.apex.test.sharedlibs@1")); 1370 1371 ASSERT_TRUE(IsOk(DeactivatePackage(file_path))); 1372 ASSERT_FALSE(IsOk(GetActivePackage("com.android.apex.test.sharedlibs"))); 1373 1374 auto new_apex_mounts = GetApexMounts(); 1375 ASSERT_EQ(new_apex_mounts.size(), 0u); 1376 } 1377 1378 TEST_F(ApexdMountTest, RemoveInactiveDataApex) { 1379 AddPreInstalledApex("com.android.apex.compressed.v2.capex"); 1380 // Add a decompressed apex that will not be mounted, so should be removed 1381 auto decompressed_apex = StringPrintf("%s/com.android.apex.compressed@1%s", 1382 GetDecompressionDir().c_str(), 1383 kDecompressedApexPackageSuffix); 1384 fs::copy(GetTestFile("com.android.apex.compressed.v1_original.apex"), 1385 decompressed_apex); 1386 // Add a decompressed apex that will be mounted, so should be not be removed 1387 auto active_decompressed_apex = StringPrintf( 1388 "%s/com.android.apex.compressed@2%s", GetDecompressionDir().c_str(), 1389 kDecompressedApexPackageSuffix); 1390 fs::copy(GetTestFile("com.android.apex.compressed.v2_original.apex"), 1391 active_decompressed_apex); 1392 // Apex that do not have kDecompressedApexPackageSuffix, should not be removed 1393 // from decompression_dir 1394 auto decompressed_different_suffix = 1395 StringPrintf("%s/com.android.apex.compressed@2%s", 1396 GetDecompressionDir().c_str(), kApexPackageSuffix); 1397 fs::copy(GetTestFile("com.android.apex.compressed.v2_original.apex"), 1398 decompressed_different_suffix); 1399 1400 AddPreInstalledApex("apex.apexd_test.apex"); 1401 auto data_apex = AddDataApex("apex.apexd_test.apex"); 1402 auto active_data_apex = AddDataApex("apex.apexd_test_v2.apex"); 1403 1404 // Activate some of the apex 1405 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); 1406 UnmountOnTearDown(active_decompressed_apex); 1407 UnmountOnTearDown(active_data_apex); 1408 ASSERT_TRUE(IsOk(ActivatePackage(active_decompressed_apex))); 1409 ASSERT_TRUE(IsOk(ActivatePackage(active_data_apex))); 1410 // Clean up inactive apex packages 1411 RemoveInactiveDataApex(); 1412 1413 // Verify inactive apex packages have been deleted 1414 ASSERT_TRUE(*PathExists(active_decompressed_apex)); 1415 ASSERT_TRUE(*PathExists(active_data_apex)); 1416 ASSERT_TRUE(*PathExists(decompressed_different_suffix)); 1417 ASSERT_FALSE(*PathExists(decompressed_apex)); 1418 ASSERT_FALSE(*PathExists(data_apex)); 1419 } 1420 1421 TEST_F(ApexdMountTest, OnOtaChrootBootstrapOnlyPreInstalledApexes) { 1422 std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex"); 1423 std::string apex_path_2 = 1424 AddPreInstalledApex("apex.apexd_test_different_app.apex"); 1425 1426 ASSERT_EQ(OnOtaChrootBootstrap(), 0); 1427 UnmountOnTearDown(apex_path_1); 1428 UnmountOnTearDown(apex_path_2); 1429 1430 auto apex_mounts = GetApexMounts(); 1431 ASSERT_THAT(apex_mounts, 1432 UnorderedElementsAre("/apex/com.android.apex.test_package", 1433 "/apex/com.android.apex.test_package@1", 1434 "/apex/com.android.apex.test_package_2", 1435 "/apex/com.android.apex.test_package_2@1")); 1436 1437 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); 1438 auto info_list = 1439 com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); 1440 ASSERT_TRUE(info_list.has_value()); 1441 auto apex_info_xml_1 = com::android::apex::ApexInfo( 1442 /* moduleName= */ "com.android.apex.test_package", 1443 /* modulePath= */ apex_path_1, 1444 /* preinstalledModulePath= */ apex_path_1, 1445 /* versionCode= */ 1, /* versionName= */ "1", 1446 /* isFactory= */ true, /* isActive= */ true, GetMTime(apex_path_1)); 1447 auto apex_info_xml_2 = com::android::apex::ApexInfo( 1448 /* moduleName= */ "com.android.apex.test_package_2", 1449 /* modulePath= */ apex_path_2, /* preinstalledModulePath= */ apex_path_2, 1450 /* versionCode= */ 1, /* versionName= */ "1", /* isFactory= */ true, 1451 /* isActive= */ true, GetMTime(apex_path_2)); 1452 ASSERT_THAT(info_list->getApexInfo(), 1453 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1), 1454 ApexInfoXmlEq(apex_info_xml_2))); 1455 } 1456 1457 TEST_F(ApexdMountTest, OnOtaChrootBootstrapFailsToScanPreInstalledApexes) { 1458 AddPreInstalledApex("apex.apexd_test.apex"); 1459 AddPreInstalledApex("apex.apexd_test_corrupt_superblock_apex.apex"); 1460 1461 ASSERT_EQ(OnOtaChrootBootstrap(), 1); 1462 } 1463 1464 TEST_F(ApexdMountTest, OnOtaChrootBootstrapDataHasHigherVersion) { 1465 std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex"); 1466 std::string apex_path_2 = 1467 AddPreInstalledApex("apex.apexd_test_different_app.apex"); 1468 std::string apex_path_3 = AddDataApex("apex.apexd_test_v2.apex"); 1469 1470 ASSERT_EQ(OnOtaChrootBootstrap(), 0); 1471 1472 UnmountOnTearDown(apex_path_2); 1473 UnmountOnTearDown(apex_path_3); 1474 1475 auto apex_mounts = GetApexMounts(); 1476 ASSERT_THAT(apex_mounts, 1477 UnorderedElementsAre("/apex/com.android.apex.test_package", 1478 "/apex/com.android.apex.test_package@2", 1479 "/apex/com.android.apex.test_package_2", 1480 "/apex/com.android.apex.test_package_2@1")); 1481 1482 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); 1483 auto info_list = 1484 com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); 1485 ASSERT_TRUE(info_list.has_value()); 1486 auto apex_info_xml_1 = com::android::apex::ApexInfo( 1487 /* moduleName= */ "com.android.apex.test_package", 1488 /* modulePath= */ apex_path_1, 1489 /* preinstalledModulePath= */ apex_path_1, 1490 /* versionCode= */ 1, /* versionName= */ "1", 1491 /* isFactory= */ true, /* isActive= */ false, GetMTime(apex_path_1)); 1492 auto apex_info_xml_2 = com::android::apex::ApexInfo( 1493 /* moduleName= */ "com.android.apex.test_package_2", 1494 /* modulePath= */ apex_path_2, /* preinstalledModulePath= */ apex_path_2, 1495 /* versionCode= */ 1, /* versionName= */ "1", /* isFactory= */ true, 1496 /* isActive= */ true, GetMTime(apex_path_2)); 1497 auto apex_info_xml_3 = com::android::apex::ApexInfo( 1498 /* moduleName= */ "com.android.apex.test_package", 1499 /* modulePath= */ apex_path_3, 1500 /* preinstalledModulePath= */ apex_path_1, 1501 /* versionCode= */ 2, /* versionName= */ "2", 1502 /* isFactory= */ false, /* isActive= */ true, GetMTime(apex_path_3)); 1503 ASSERT_THAT(info_list->getApexInfo(), 1504 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1), 1505 ApexInfoXmlEq(apex_info_xml_2), 1506 ApexInfoXmlEq(apex_info_xml_3))); 1507 } 1508 1509 TEST_F(ApexdMountTest, OnOtaChrootBootstrapDataHasSameVersion) { 1510 std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex"); 1511 std::string apex_path_2 = 1512 AddPreInstalledApex("apex.apexd_test_different_app.apex"); 1513 std::string apex_path_3 = AddDataApex("apex.apexd_test.apex"); 1514 1515 ASSERT_EQ(OnOtaChrootBootstrap(), 0); 1516 1517 UnmountOnTearDown(apex_path_2); 1518 UnmountOnTearDown(apex_path_3); 1519 1520 auto apex_mounts = GetApexMounts(); 1521 ASSERT_THAT(apex_mounts, 1522 UnorderedElementsAre("/apex/com.android.apex.test_package", 1523 "/apex/com.android.apex.test_package@1", 1524 "/apex/com.android.apex.test_package_2", 1525 "/apex/com.android.apex.test_package_2@1")); 1526 1527 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); 1528 auto info_list = 1529 com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); 1530 ASSERT_TRUE(info_list.has_value()); 1531 auto apex_info_xml_1 = com::android::apex::ApexInfo( 1532 /* moduleName= */ "com.android.apex.test_package", 1533 /* modulePath= */ apex_path_1, 1534 /* preinstalledModulePath= */ apex_path_1, 1535 /* versionCode= */ 1, /* versionName= */ "1", 1536 /* isFactory= */ true, /* isActive= */ false, GetMTime(apex_path_1)); 1537 auto apex_info_xml_2 = com::android::apex::ApexInfo( 1538 /* moduleName= */ "com.android.apex.test_package_2", 1539 /* modulePath= */ apex_path_2, /* preinstalledModulePath= */ apex_path_2, 1540 /* versionCode= */ 1, /* versionName= */ "1", /* isFactory= */ true, 1541 /* isActive= */ true, GetMTime(apex_path_2)); 1542 auto apex_info_xml_3 = com::android::apex::ApexInfo( 1543 /* moduleName= */ "com.android.apex.test_package", 1544 /* modulePath= */ apex_path_3, 1545 /* preinstalledModulePath= */ apex_path_1, 1546 /* versionCode= */ 1, /* versionName= */ "1", 1547 /* isFactory= */ false, /* isActive= */ true, GetMTime(apex_path_3)); 1548 ASSERT_THAT(info_list->getApexInfo(), 1549 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1), 1550 ApexInfoXmlEq(apex_info_xml_2), 1551 ApexInfoXmlEq(apex_info_xml_3))); 1552 } 1553 1554 TEST_F(ApexdMountTest, OnOtaChrootBootstrapSystemHasHigherVersion) { 1555 std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test_v2.apex"); 1556 std::string apex_path_2 = 1557 AddPreInstalledApex("apex.apexd_test_different_app.apex"); 1558 AddDataApex("apex.apexd_test.apex"); 1559 1560 ASSERT_EQ(OnOtaChrootBootstrap(), 0); 1561 1562 UnmountOnTearDown(apex_path_1); 1563 UnmountOnTearDown(apex_path_2); 1564 1565 auto apex_mounts = GetApexMounts(); 1566 ASSERT_THAT(apex_mounts, 1567 UnorderedElementsAre("/apex/com.android.apex.test_package", 1568 "/apex/com.android.apex.test_package@2", 1569 "/apex/com.android.apex.test_package_2", 1570 "/apex/com.android.apex.test_package_2@1")); 1571 1572 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); 1573 auto info_list = 1574 com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); 1575 ASSERT_TRUE(info_list.has_value()); 1576 auto apex_info_xml_1 = com::android::apex::ApexInfo( 1577 /* moduleName= */ "com.android.apex.test_package", 1578 /* modulePath= */ apex_path_1, 1579 /* preinstalledModulePath= */ apex_path_1, 1580 /* versionCode= */ 2, /* versionName= */ "2", 1581 /* isFactory= */ true, /* isActive= */ true, GetMTime(apex_path_1)); 1582 auto apex_info_xml_2 = com::android::apex::ApexInfo( 1583 /* moduleName= */ "com.android.apex.test_package_2", 1584 /* modulePath= */ apex_path_2, /* preinstalledModulePath= */ apex_path_2, 1585 /* versionCode= */ 1, /* versionName= */ "1", /* isFactory= */ true, 1586 /* isActive= */ true, GetMTime(apex_path_2)); 1587 1588 ASSERT_THAT(info_list->getApexInfo(), 1589 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1), 1590 ApexInfoXmlEq(apex_info_xml_2))); 1591 } 1592 1593 TEST_F(ApexdMountTest, OnOtaChrootBootstrapDataHasSameVersionButDifferentKey) { 1594 std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex"); 1595 std::string apex_path_2 = 1596 AddPreInstalledApex("apex.apexd_test_different_app.apex"); 1597 AddDataApex("apex.apexd_test_different_key.apex"); 1598 1599 ASSERT_EQ(OnOtaChrootBootstrap(), 0); 1600 1601 UnmountOnTearDown(apex_path_1); 1602 UnmountOnTearDown(apex_path_2); 1603 1604 auto apex_mounts = GetApexMounts(); 1605 ASSERT_THAT(apex_mounts, 1606 UnorderedElementsAre("/apex/com.android.apex.test_package", 1607 "/apex/com.android.apex.test_package@1", 1608 "/apex/com.android.apex.test_package_2", 1609 "/apex/com.android.apex.test_package_2@1")); 1610 1611 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); 1612 auto info_list = 1613 com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); 1614 ASSERT_TRUE(info_list.has_value()); 1615 auto apex_info_xml_1 = com::android::apex::ApexInfo( 1616 /* moduleName= */ "com.android.apex.test_package", 1617 /* modulePath= */ apex_path_1, 1618 /* preinstalledModulePath= */ apex_path_1, 1619 /* versionCode= */ 1, /* versionName= */ "1", 1620 /* isFactory= */ true, /* isActive= */ true, GetMTime(apex_path_1)); 1621 auto apex_info_xml_2 = com::android::apex::ApexInfo( 1622 /* moduleName= */ "com.android.apex.test_package_2", 1623 /* modulePath= */ apex_path_2, /* preinstalledModulePath= */ apex_path_2, 1624 /* versionCode= */ 1, /* versionName= */ "1", /* isFactory= */ true, 1625 /* isActive= */ true, GetMTime(apex_path_2)); 1626 1627 ASSERT_THAT(info_list->getApexInfo(), 1628 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1), 1629 ApexInfoXmlEq(apex_info_xml_2))); 1630 } 1631 1632 TEST_F(ApexdMountTest, 1633 OnOtaChrootBootstrapDataHasHigherVersionButDifferentKey) { 1634 std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex"); 1635 std::string apex_path_2 = 1636 AddPreInstalledApex("apex.apexd_test_different_app.apex"); 1637 std::string apex_path_3 = 1638 AddDataApex("apex.apexd_test_different_key_v2.apex"); 1639 1640 { 1641 auto apex = ApexFile::Open(apex_path_3); 1642 ASSERT_TRUE(IsOk(apex)); 1643 ASSERT_EQ(static_cast<uint64_t>(apex->GetManifest().version()), 2ULL); 1644 } 1645 1646 ASSERT_EQ(OnOtaChrootBootstrap(), 0); 1647 1648 UnmountOnTearDown(apex_path_1); 1649 UnmountOnTearDown(apex_path_2); 1650 1651 auto apex_mounts = GetApexMounts(); 1652 ASSERT_THAT(apex_mounts, 1653 UnorderedElementsAre("/apex/com.android.apex.test_package", 1654 "/apex/com.android.apex.test_package@1", 1655 "/apex/com.android.apex.test_package_2", 1656 "/apex/com.android.apex.test_package_2@1")); 1657 1658 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); 1659 auto info_list = 1660 com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); 1661 ASSERT_TRUE(info_list.has_value()); 1662 auto apex_info_xml_1 = com::android::apex::ApexInfo( 1663 /* moduleName= */ "com.android.apex.test_package", 1664 /* modulePath= */ apex_path_1, 1665 /* preinstalledModulePath= */ apex_path_1, 1666 /* versionCode= */ 1, /* versionName= */ "1", 1667 /* isFactory= */ true, /* isActive= */ true, GetMTime(apex_path_1)); 1668 auto apex_info_xml_2 = com::android::apex::ApexInfo( 1669 /* moduleName= */ "com.android.apex.test_package_2", 1670 /* modulePath= */ apex_path_2, /* preinstalledModulePath= */ apex_path_2, 1671 /* versionCode= */ 1, /* versionName= */ "1", /* isFactory= */ true, 1672 /* isActive= */ true, GetMTime(apex_path_2)); 1673 1674 ASSERT_THAT(info_list->getApexInfo(), 1675 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1), 1676 ApexInfoXmlEq(apex_info_xml_2))); 1677 } 1678 1679 TEST_F(ApexdMountTest, OnOtaChrootBootstrapDataApexWithoutPreInstalledApex) { 1680 std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex"); 1681 AddDataApex("apex.apexd_test_different_app.apex"); 1682 1683 ASSERT_EQ(OnOtaChrootBootstrap(), 0); 1684 1685 UnmountOnTearDown(apex_path_1); 1686 1687 auto apex_mounts = GetApexMounts(); 1688 ASSERT_THAT(apex_mounts, 1689 UnorderedElementsAre("/apex/com.android.apex.test_package", 1690 "/apex/com.android.apex.test_package@1")); 1691 1692 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); 1693 auto info_list = 1694 com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); 1695 ASSERT_TRUE(info_list.has_value()); 1696 auto apex_info_xml_1 = com::android::apex::ApexInfo( 1697 /* moduleName= */ "com.android.apex.test_package", 1698 /* modulePath= */ apex_path_1, 1699 /* preinstalledModulePath= */ apex_path_1, 1700 /* versionCode= */ 1, /* versionName= */ "1", 1701 /* isFactory= */ true, /* isActive= */ true, GetMTime(apex_path_1)); 1702 1703 ASSERT_THAT(info_list->getApexInfo(), 1704 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1))); 1705 } 1706 1707 TEST_F(ApexdMountTest, OnOtaChrootBootstrapPreInstalledSharedLibsApex) { 1708 std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex"); 1709 std::string apex_path_2 = AddPreInstalledApex( 1710 "com.android.apex.test.sharedlibs_generated.v1.libvX.apex"); 1711 std::string apex_path_3 = AddDataApex("apex.apexd_test_v2.apex"); 1712 1713 ASSERT_EQ(OnOtaChrootBootstrap(), 0); 1714 1715 UnmountOnTearDown(apex_path_2); 1716 UnmountOnTearDown(apex_path_3); 1717 1718 auto apex_mounts = GetApexMounts(); 1719 ASSERT_THAT(apex_mounts, 1720 UnorderedElementsAre("/apex/com.android.apex.test_package", 1721 "/apex/com.android.apex.test_package@2", 1722 "/apex/com.android.apex.test.sharedlibs@1")); 1723 1724 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); 1725 auto info_list = 1726 com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); 1727 ASSERT_TRUE(info_list.has_value()); 1728 auto apex_info_xml_1 = com::android::apex::ApexInfo( 1729 /* moduleName= */ "com.android.apex.test_package", 1730 /* modulePath= */ apex_path_1, 1731 /* preinstalledModulePath= */ apex_path_1, 1732 /* versionCode= */ 1, /* versionName= */ "1", 1733 /* isFactory= */ true, /* isActive= */ false, GetMTime(apex_path_1)); 1734 auto apex_info_xml_2 = com::android::apex::ApexInfo( 1735 /* moduleName= */ "com.android.apex.test.sharedlibs", 1736 /* modulePath= */ apex_path_2, 1737 /* preinstalledModulePath= */ apex_path_2, 1738 /* versionCode= */ 1, /* versionName= */ "1", 1739 /* isFactory= */ true, /* isActive= */ true, GetMTime(apex_path_2)); 1740 auto apex_info_xml_3 = com::android::apex::ApexInfo( 1741 /* moduleName= */ "com.android.apex.test_package", 1742 /* modulePath= */ apex_path_3, 1743 /* preinstalledModulePath= */ apex_path_1, 1744 /* versionCode= */ 2, /* versionName= */ "2", 1745 /* isFactory= */ false, /* isActive= */ true, GetMTime(apex_path_3)); 1746 1747 ASSERT_THAT(info_list->getApexInfo(), 1748 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1), 1749 ApexInfoXmlEq(apex_info_xml_2), 1750 ApexInfoXmlEq(apex_info_xml_3))); 1751 1752 ASSERT_EQ(access("/apex/sharedlibs", F_OK), 0); 1753 1754 // Check /apex/sharedlibs is populated properly. 1755 std::vector<std::string> sharedlibs; 1756 for (const auto& p : fs::recursive_directory_iterator("/apex/sharedlibs")) { 1757 if (fs::is_symlink(p)) { 1758 auto src = fs::read_symlink(p.path()); 1759 ASSERT_EQ(p.path().filename(), src.filename()); 1760 sharedlibs.push_back(p.path().parent_path().string() + "->" + 1761 src.parent_path().string()); 1762 } 1763 } 1764 1765 std::vector<std::string> expected = { 1766 "/apex/sharedlibs/lib/libsharedlibtest.so->" 1767 "/apex/com.android.apex.test.sharedlibs@1/lib/libsharedlibtest.so", 1768 "/apex/sharedlibs/lib/libc++.so->" 1769 "/apex/com.android.apex.test.sharedlibs@1/lib/libc++.so", 1770 }; 1771 1772 // On 64bit devices we also have lib64. 1773 if (!GetProperty("ro.product.cpu.abilist64", "").empty()) { 1774 expected.push_back( 1775 "/apex/sharedlibs/lib64/libsharedlibtest.so->" 1776 "/apex/com.android.apex.test.sharedlibs@1/lib64/libsharedlibtest.so"); 1777 expected.push_back( 1778 "/apex/sharedlibs/lib64/libc++.so->" 1779 "/apex/com.android.apex.test.sharedlibs@1/lib64/libc++.so"); 1780 } 1781 ASSERT_THAT(sharedlibs, UnorderedElementsAreArray(expected)); 1782 } 1783 1784 TEST_F(ApexdMountTest, OnOtaChrootBootstrapSharedLibsApexBothVersions) { 1785 std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex"); 1786 std::string apex_path_2 = AddPreInstalledApex( 1787 "com.android.apex.test.sharedlibs_generated.v1.libvX.apex"); 1788 std::string apex_path_3 = AddDataApex("apex.apexd_test_v2.apex"); 1789 std::string apex_path_4 = 1790 AddDataApex("com.android.apex.test.sharedlibs_generated.v2.libvY.apex"); 1791 1792 ASSERT_EQ(OnOtaChrootBootstrap(), 0); 1793 1794 UnmountOnTearDown(apex_path_2); 1795 UnmountOnTearDown(apex_path_3); 1796 UnmountOnTearDown(apex_path_4); 1797 1798 auto apex_mounts = GetApexMounts(); 1799 ASSERT_THAT(apex_mounts, 1800 UnorderedElementsAre("/apex/com.android.apex.test_package", 1801 "/apex/com.android.apex.test_package@2", 1802 "/apex/com.android.apex.test.sharedlibs@1", 1803 "/apex/com.android.apex.test.sharedlibs@2")); 1804 1805 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); 1806 auto info_list = 1807 com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); 1808 ASSERT_TRUE(info_list.has_value()); 1809 auto apex_info_xml_1 = com::android::apex::ApexInfo( 1810 /* moduleName= */ "com.android.apex.test_package", 1811 /* modulePath= */ apex_path_1, 1812 /* preinstalledModulePath= */ apex_path_1, 1813 /* versionCode= */ 1, /* versionName= */ "1", 1814 /* isFactory= */ true, /* isActive= */ false, GetMTime(apex_path_1)); 1815 auto apex_info_xml_2 = com::android::apex::ApexInfo( 1816 /* moduleName= */ "com.android.apex.test.sharedlibs", 1817 /* modulePath= */ apex_path_2, 1818 /* preinstalledModulePath= */ apex_path_2, 1819 /* versionCode= */ 1, /* versionName= */ "1", 1820 /* isFactory= */ true, /* isActive= */ false, GetMTime(apex_path_2)); 1821 auto apex_info_xml_3 = com::android::apex::ApexInfo( 1822 /* moduleName= */ "com.android.apex.test_package", 1823 /* modulePath= */ apex_path_3, 1824 /* preinstalledModulePath= */ apex_path_1, 1825 /* versionCode= */ 2, /* versionName= */ "2", 1826 /* isFactory= */ false, /* isActive= */ true, GetMTime(apex_path_3)); 1827 auto apex_info_xml_4 = com::android::apex::ApexInfo( 1828 /* moduleName= */ "com.android.apex.test.sharedlibs", 1829 /* modulePath= */ apex_path_4, 1830 /* preinstalledModulePath= */ apex_path_2, 1831 /* versionCode= */ 2, /* versionName= */ "2", 1832 /* isFactory= */ false, /* isActive= */ true, GetMTime(apex_path_4)); 1833 1834 ASSERT_THAT(info_list->getApexInfo(), 1835 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1), 1836 ApexInfoXmlEq(apex_info_xml_2), 1837 ApexInfoXmlEq(apex_info_xml_3), 1838 ApexInfoXmlEq(apex_info_xml_4))); 1839 1840 ASSERT_EQ(access("/apex/sharedlibs", F_OK), 0); 1841 1842 // Check /apex/sharedlibs is populated properly. 1843 // Because we don't want to hardcode full paths (they are pretty long and have 1844 // a hash in them which might change if new prebuilts are dropped in), the 1845 // assertion logic is a little bit clunky. 1846 std::vector<std::string> sharedlibs; 1847 for (const auto& p : fs::recursive_directory_iterator("/apex/sharedlibs")) { 1848 if (fs::is_symlink(p)) { 1849 auto src = fs::read_symlink(p.path()); 1850 ASSERT_EQ(p.path().filename(), src.filename()); 1851 sharedlibs.push_back(p.path().parent_path().string() + "->" + 1852 src.parent_path().string()); 1853 } 1854 } 1855 1856 std::vector<std::string> expected = { 1857 "/apex/sharedlibs/lib/libsharedlibtest.so->" 1858 "/apex/com.android.apex.test.sharedlibs@2/lib/libsharedlibtest.so", 1859 "/apex/sharedlibs/lib/libsharedlibtest.so->" 1860 "/apex/com.android.apex.test.sharedlibs@1/lib/libsharedlibtest.so", 1861 "/apex/sharedlibs/lib/libc++.so->" 1862 "/apex/com.android.apex.test.sharedlibs@1/lib/libc++.so", 1863 }; 1864 // On 64bit devices we also have lib64. 1865 if (!GetProperty("ro.product.cpu.abilist64", "").empty()) { 1866 expected.push_back( 1867 "/apex/sharedlibs/lib64/libsharedlibtest.so->" 1868 "/apex/com.android.apex.test.sharedlibs@2/lib64/libsharedlibtest.so"); 1869 expected.push_back( 1870 "/apex/sharedlibs/lib64/libsharedlibtest.so->" 1871 "/apex/com.android.apex.test.sharedlibs@1/lib64/libsharedlibtest.so"); 1872 expected.push_back( 1873 "/apex/sharedlibs/lib64/libc++.so->" 1874 "/apex/com.android.apex.test.sharedlibs@1/lib64/libc++.so"); 1875 } 1876 1877 ASSERT_THAT(sharedlibs, UnorderedElementsAreArray(expected)); 1878 } 1879 1880 // Test when we move from uncompressed APEX to CAPEX via ota 1881 TEST_F(ApexdMountTest, OnOtaChrootBootstrapOnlyCompressedApexes) { 1882 std::string apex_path = 1883 AddPreInstalledApex("com.android.apex.compressed.v1.capex"); 1884 1885 ASSERT_EQ(OnOtaChrootBootstrap(), 0); 1886 1887 // Decompressed APEX should be mounted from decompression_dir 1888 std::string decompressed_apex = 1889 StringPrintf("%s/com.android.apex.compressed@1%s", 1890 GetDecompressionDir().c_str(), kOtaApexPackageSuffix); 1891 UnmountOnTearDown(decompressed_apex); 1892 1893 auto apex_mounts = GetApexMounts(); 1894 ASSERT_THAT(apex_mounts, 1895 UnorderedElementsAre("/apex/com.android.apex.compressed", 1896 "/apex/com.android.apex.compressed@1")); 1897 1898 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); 1899 auto info_list = 1900 com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); 1901 ASSERT_TRUE(info_list.has_value()); 1902 auto apex_info_xml_decompressed = com::android::apex::ApexInfo( 1903 /* moduleName= */ "com.android.apex.compressed", 1904 /* modulePath= */ decompressed_apex, 1905 /* preinstalledModulePath= */ apex_path, 1906 /* versionCode= */ 1, /* versionName= */ "1", 1907 /* isFactory= */ true, /* isActive= */ true, GetMTime(decompressed_apex)); 1908 ASSERT_THAT(info_list->getApexInfo(), 1909 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_decompressed))); 1910 auto& db = GetApexDatabaseForTesting(); 1911 // Check that it was mounted from decompressed apex. It should also be mounted 1912 // on dm-verity device. 1913 db.ForallMountedApexes("com.android.apex.compressed", 1914 [&](const MountedApexData& data, bool latest) { 1915 ASSERT_TRUE(latest); 1916 ASSERT_EQ(data.full_path, decompressed_apex); 1917 ASSERT_EQ(data.device_name, 1918 "com.android.apex.compressed@1.chroot"); 1919 }); 1920 } 1921 1922 // Test we decompress only once even if OnOtaChrootBootstrap is called multiple 1923 // times 1924 TEST_F(ApexdMountTest, OnOtaChrootBootstrapDecompressOnlyOnceMultipleCalls) { 1925 std::string apex_path = 1926 AddPreInstalledApex("com.android.apex.compressed.v1.capex"); 1927 1928 ASSERT_EQ(OnOtaChrootBootstrap(), 0); 1929 1930 // Decompressed OTA APEX should be mounted 1931 std::string decompressed_ota_apex = 1932 StringPrintf("%s/com.android.apex.compressed@1%s", 1933 GetDecompressionDir().c_str(), kOtaApexPackageSuffix); 1934 UnmountOnTearDown(decompressed_ota_apex); 1935 1936 // Capture the creation time of the OTA APEX 1937 std::error_code ec; 1938 auto last_write_time_1 = fs::last_write_time(decompressed_ota_apex, ec); 1939 ASSERT_FALSE(ec) << "Failed to capture last write time of " 1940 << decompressed_ota_apex; 1941 1942 // Call OnOtaChrootBootstrap again. Since we do not hardlink decompressed APEX 1943 // to /data/apex/active directory when in chroot, when selecting apex for 1944 // activation, we will end up selecting compressed APEX again. 1945 ASSERT_EQ(OnOtaChrootBootstrap(), 0); 1946 1947 // Compare write time to ensure we did not decompress again 1948 auto last_write_time_2 = fs::last_write_time(decompressed_ota_apex, ec); 1949 ASSERT_FALSE(ec) << "Failed to capture last write time of " 1950 << decompressed_ota_apex << ec.message(); 1951 ASSERT_EQ(last_write_time_1, last_write_time_2); 1952 } 1953 1954 // Test when we upgrade existing CAPEX to higher version via OTA 1955 TEST_F(ApexdMountTest, OnOtaChrootBootstrapUpgradeCapex) { 1956 TemporaryDir previous_built_in_dir; 1957 PrepareCompressedApex("com.android.apex.compressed.v1.capex", 1958 previous_built_in_dir.path); 1959 // Place a higher version capex in current built_in_dir 1960 std::string apex_path = 1961 AddPreInstalledApex("com.android.apex.compressed.v2.capex"); 1962 1963 ASSERT_EQ(OnOtaChrootBootstrap(), 0); 1964 1965 // Upgraded decompressed APEX should be mounted from decompression dir 1966 std::string decompressed_active_apex = 1967 StringPrintf("%s/com.android.apex.compressed@2%s", 1968 GetDecompressionDir().c_str(), kOtaApexPackageSuffix); 1969 UnmountOnTearDown(decompressed_active_apex); 1970 1971 auto apex_mounts = GetApexMounts(); 1972 ASSERT_THAT(apex_mounts, 1973 UnorderedElementsAre("/apex/com.android.apex.compressed", 1974 "/apex/com.android.apex.compressed@2")); 1975 1976 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); 1977 auto info_list = 1978 com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); 1979 ASSERT_TRUE(info_list.has_value()); 1980 auto apex_info_xml_decompressed = com::android::apex::ApexInfo( 1981 /* moduleName= */ "com.android.apex.compressed", 1982 /* modulePath= */ decompressed_active_apex, 1983 /* preinstalledModulePath= */ apex_path, 1984 /* versionCode= */ 2, /* versionName= */ "2", 1985 /* isFactory= */ true, /* isActive= */ true, 1986 GetMTime(decompressed_active_apex)); 1987 ASSERT_THAT(info_list->getApexInfo(), 1988 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_decompressed))); 1989 auto& db = GetApexDatabaseForTesting(); 1990 // Check that it was mounted from decompressed apex. It should also be mounted 1991 // on dm-verity device. 1992 db.ForallMountedApexes("com.android.apex.compressed", 1993 [&](const MountedApexData& data, bool latest) { 1994 ASSERT_TRUE(latest); 1995 ASSERT_EQ(data.full_path, decompressed_active_apex); 1996 ASSERT_EQ(data.device_name, 1997 "com.android.apex.compressed@2.chroot"); 1998 }); 1999 } 2000 2001 // Test when we update existing CAPEX to same version via OTA 2002 TEST_F(ApexdMountTest, OnOtaChrootBootstrapSamegradeCapex) { 2003 TemporaryDir previous_built_in_dir; 2004 PrepareCompressedApex("com.android.apex.compressed.v1.capex", 2005 previous_built_in_dir.path); 2006 // Place a same version capex in current built_in_dir, under a different name 2007 auto apex_path = 2008 StringPrintf("%s/different-name.capex", GetBuiltInDir().c_str()); 2009 fs::copy(GetTestFile("com.android.apex.compressed.v1.capex"), apex_path); 2010 2011 ASSERT_EQ(OnOtaChrootBootstrap(), 0); 2012 2013 // Previously decompressed APEX should be mounted from decompression_dir 2014 std::string decompressed_active_apex = StringPrintf( 2015 "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(), 2016 kDecompressedApexPackageSuffix); 2017 UnmountOnTearDown(decompressed_active_apex); 2018 2019 auto apex_mounts = GetApexMounts(); 2020 ASSERT_THAT(apex_mounts, 2021 UnorderedElementsAre("/apex/com.android.apex.compressed", 2022 "/apex/com.android.apex.compressed@1")); 2023 2024 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); 2025 auto info_list = 2026 com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); 2027 ASSERT_TRUE(info_list.has_value()); 2028 auto apex_info_xml_decompressed = com::android::apex::ApexInfo( 2029 /* moduleName= */ "com.android.apex.compressed", 2030 /* modulePath= */ decompressed_active_apex, 2031 /* preinstalledModulePath= */ apex_path, 2032 /* versionCode= */ 1, /* versionName= */ "1", 2033 /* isFactory= */ true, /* isActive= */ true, 2034 GetMTime(decompressed_active_apex)); 2035 ASSERT_THAT(info_list->getApexInfo(), 2036 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_decompressed))); 2037 auto& db = GetApexDatabaseForTesting(); 2038 // Check that it was mounted from decompressed apex. It should also be mounted 2039 // on dm-verity device. 2040 db.ForallMountedApexes("com.android.apex.compressed", 2041 [&](const MountedApexData& data, bool latest) { 2042 ASSERT_TRUE(latest); 2043 ASSERT_EQ(data.full_path, decompressed_active_apex); 2044 ASSERT_EQ(data.device_name, 2045 "com.android.apex.compressed@1.chroot"); 2046 }); 2047 } 2048 2049 // Test when we update existing CAPEX to same version, but different digest 2050 TEST_F(ApexdMountTest, OnOtaChrootBootstrapSamegradeCapexDifferentDigest) { 2051 TemporaryDir previous_built_in_dir; 2052 auto different_digest_apex_path = PrepareCompressedApex( 2053 "com.android.apex.compressed.v1_different_digest.capex", 2054 previous_built_in_dir.path); 2055 // Place a same version capex in current built_in_dir, which has different 2056 // digest 2057 auto apex_path = AddPreInstalledApex("com.android.apex.compressed.v1.capex"); 2058 2059 ASSERT_EQ(OnOtaChrootBootstrap(), 0); 2060 2061 // New decompressed ota APEX should be mounted with kOtaApexPackageSuffix 2062 std::string decompressed_ota_apex = 2063 StringPrintf("%s/com.android.apex.compressed@1%s", 2064 GetDecompressionDir().c_str(), kOtaApexPackageSuffix); 2065 UnmountOnTearDown(decompressed_ota_apex); 2066 2067 auto apex_mounts = GetApexMounts(); 2068 ASSERT_THAT(apex_mounts, 2069 UnorderedElementsAre("/apex/com.android.apex.compressed", 2070 "/apex/com.android.apex.compressed@1")); 2071 2072 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); 2073 auto info_list = 2074 com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); 2075 ASSERT_TRUE(info_list.has_value()); 2076 auto apex_info_xml_decompressed = com::android::apex::ApexInfo( 2077 /* moduleName= */ "com.android.apex.compressed", 2078 /* modulePath= */ decompressed_ota_apex, 2079 /* preinstalledModulePath= */ apex_path, 2080 /* versionCode= */ 1, /* versionName= */ "1", 2081 /* isFactory= */ true, /* isActive= */ true, 2082 GetMTime(decompressed_ota_apex)); 2083 ASSERT_THAT(info_list->getApexInfo(), 2084 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_decompressed))); 2085 auto& db = GetApexDatabaseForTesting(); 2086 // Check that it was mounted from decompressed apex. It should also be mounted 2087 // on dm-verity device. 2088 db.ForallMountedApexes("com.android.apex.compressed", 2089 [&](const MountedApexData& data, bool latest) { 2090 ASSERT_TRUE(latest); 2091 ASSERT_EQ(data.full_path, decompressed_ota_apex); 2092 ASSERT_EQ(data.device_name, 2093 "com.android.apex.compressed@1.chroot"); 2094 }); 2095 2096 // Ensure decompressed apex has same digest as pre-installed 2097 auto pre_installed_apex = ApexFile::Open(apex_path); 2098 auto decompressed_apex = ApexFile::Open(decompressed_ota_apex); 2099 auto different_digest_apex = ApexFile::Open(different_digest_apex_path); 2100 ASSERT_EQ( 2101 pre_installed_apex->GetManifest().capexmetadata().originalapexdigest(), 2102 GetRootDigest(*decompressed_apex)); 2103 ASSERT_NE( 2104 pre_installed_apex->GetManifest().capexmetadata().originalapexdigest(), 2105 GetRootDigest(*different_digest_apex)); 2106 2107 // Ensure we didn't remove previous decompressed APEX 2108 std::string previous_decompressed_apex = StringPrintf( 2109 "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(), 2110 kDecompressedApexPackageSuffix); 2111 auto path_exists = PathExists(previous_decompressed_apex); 2112 ASSERT_TRUE(*path_exists); 2113 } 2114 2115 // Test when we update existing CAPEX to same version, but different key via OTA 2116 TEST_F(ApexdMountTest, OnOtaChrootBootstrapSamegradeCapexDifferentKey) { 2117 TemporaryDir previous_built_in_dir; 2118 PrepareCompressedApex("com.android.apex.compressed_different_key.capex", 2119 previous_built_in_dir.path); 2120 // Place a same version capex in current built_in_dir, which has different key 2121 auto apex_path = AddPreInstalledApex("com.android.apex.compressed.v1.capex"); 2122 2123 ASSERT_EQ(OnOtaChrootBootstrap(), 0); 2124 2125 // New decompressed APEX should be mounted from ota_reserved directory 2126 std::string decompressed_active_apex = 2127 StringPrintf("%s/com.android.apex.compressed@1%s", 2128 GetDecompressionDir().c_str(), kOtaApexPackageSuffix); 2129 UnmountOnTearDown(decompressed_active_apex); 2130 2131 auto apex_mounts = GetApexMounts(); 2132 ASSERT_THAT(apex_mounts, 2133 UnorderedElementsAre("/apex/com.android.apex.compressed", 2134 "/apex/com.android.apex.compressed@1")); 2135 2136 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); 2137 auto info_list = 2138 com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); 2139 ASSERT_TRUE(info_list.has_value()); 2140 auto apex_info_xml_decompressed = com::android::apex::ApexInfo( 2141 /* moduleName= */ "com.android.apex.compressed", 2142 /* modulePath= */ decompressed_active_apex, 2143 /* preinstalledModulePath= */ apex_path, 2144 /* versionCode= */ 1, /* versionName= */ "1", 2145 /* isFactory= */ true, /* isActive= */ true, 2146 GetMTime(decompressed_active_apex)); 2147 ASSERT_THAT(info_list->getApexInfo(), 2148 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_decompressed))); 2149 auto& db = GetApexDatabaseForTesting(); 2150 // Check that it was mounted from decompressed apex. It should also be mounted 2151 // on dm-verity device. 2152 db.ForallMountedApexes("com.android.apex.compressed", 2153 [&](const MountedApexData& data, bool latest) { 2154 ASSERT_TRUE(latest); 2155 ASSERT_EQ(data.full_path, decompressed_active_apex); 2156 ASSERT_EQ(data.device_name, 2157 "com.android.apex.compressed@1.chroot"); 2158 }); 2159 } 2160 2161 // Test when we remove CAPEX via OTA 2162 TEST_F(ApexdMountTest, OnOtaChrootBootstrapCapexToApex) { 2163 TemporaryDir previous_built_in_dir; 2164 PrepareCompressedApex("com.android.apex.compressed.v1.capex", 2165 previous_built_in_dir.path); 2166 // Place a uncompressed version apex in current built_in_dir 2167 std::string apex_path = 2168 AddPreInstalledApex("com.android.apex.compressed.v1_original.apex"); 2169 2170 ASSERT_EQ(OnOtaChrootBootstrap(), 0); 2171 2172 // New uncompressed APEX should be mounted 2173 UnmountOnTearDown(apex_path); 2174 2175 auto apex_mounts = GetApexMounts(); 2176 ASSERT_THAT(apex_mounts, 2177 UnorderedElementsAre("/apex/com.android.apex.compressed", 2178 "/apex/com.android.apex.compressed@1")); 2179 2180 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); 2181 auto info_list = 2182 com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); 2183 ASSERT_TRUE(info_list.has_value()); 2184 auto apex_info_xml_uncompressed = com::android::apex::ApexInfo( 2185 /* moduleName= */ "com.android.apex.compressed", 2186 /* modulePath= */ apex_path, 2187 /* preinstalledModulePath= */ apex_path, 2188 /* versionCode= */ 1, /* versionName= */ "1", 2189 /* isFactory= */ true, /* isActive= */ true, GetMTime(apex_path)); 2190 ASSERT_THAT(info_list->getApexInfo(), 2191 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_uncompressed))); 2192 } 2193 2194 TEST_F(ApexdMountTest, 2195 OnOtaChrootBootstrapDecompressedApexVersionDifferentThanCapex) { 2196 TemporaryDir previous_built_in_dir; 2197 PrepareCompressedApex("com.android.apex.compressed.v2.capex", 2198 previous_built_in_dir.path); 2199 // Place a lower version capex in current built_in_dir, so that previously 2200 // decompressed APEX has higher version but still doesn't get picked during 2201 // selection. 2202 std::string apex_path = 2203 AddPreInstalledApex("com.android.apex.compressed.v1.capex"); 2204 2205 ASSERT_EQ(OnOtaChrootBootstrap(), 0); 2206 2207 // Pre-installed CAPEX should be decompressed again and mounted from 2208 // decompression_dir 2209 std::string decompressed_active_apex = 2210 StringPrintf("%s/com.android.apex.compressed@1%s", 2211 GetDecompressionDir().c_str(), kOtaApexPackageSuffix); 2212 UnmountOnTearDown(decompressed_active_apex); 2213 2214 auto apex_mounts = GetApexMounts(); 2215 ASSERT_THAT(apex_mounts, 2216 UnorderedElementsAre("/apex/com.android.apex.compressed", 2217 "/apex/com.android.apex.compressed@1")); 2218 2219 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); 2220 auto info_list = 2221 com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); 2222 ASSERT_TRUE(info_list.has_value()); 2223 auto apex_info_xml_decompressed = com::android::apex::ApexInfo( 2224 /* moduleName= */ "com.android.apex.compressed", 2225 /* modulePath= */ decompressed_active_apex, 2226 /* preinstalledModulePath= */ apex_path, 2227 /* versionCode= */ 1, /* versionName= */ "1", 2228 /* isFactory= */ true, /* isActive= */ true, 2229 GetMTime(decompressed_active_apex)); 2230 ASSERT_THAT(info_list->getApexInfo(), 2231 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_decompressed))); 2232 } 2233 2234 // Test when we update CAPEX and there is a higher version present in data 2235 TEST_F(ApexdMountTest, OnOtaChrootBootstrapDataHigherThanCapex) { 2236 auto system_apex_path = 2237 PrepareCompressedApex("com.android.apex.compressed.v1.capex"); 2238 auto data_apex_path = 2239 AddDataApex("com.android.apex.compressed.v2_original.apex"); 2240 2241 ASSERT_EQ(OnOtaChrootBootstrap(), 0); 2242 2243 // Data APEX should be mounted 2244 UnmountOnTearDown(data_apex_path); 2245 2246 auto apex_mounts = GetApexMounts(); 2247 ASSERT_THAT(apex_mounts, 2248 UnorderedElementsAre("/apex/com.android.apex.compressed", 2249 "/apex/com.android.apex.compressed@2")); 2250 2251 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); 2252 auto info_list = 2253 com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); 2254 ASSERT_TRUE(info_list.has_value()); 2255 auto apex_info_xml_data = com::android::apex::ApexInfo( 2256 /* moduleName= */ "com.android.apex.compressed", 2257 /* modulePath= */ data_apex_path, 2258 /* preinstalledModulePath= */ system_apex_path, 2259 /* versionCode= */ 2, /* versionName= */ "2", 2260 /* isFactory= */ false, /* isActive= */ true, GetMTime(data_apex_path)); 2261 auto apex_info_xml_system = com::android::apex::ApexInfo( 2262 /* moduleName= */ "com.android.apex.compressed", 2263 /* modulePath= */ system_apex_path, 2264 /* preinstalledModulePath= */ system_apex_path, 2265 /* versionCode= */ 1, /* versionName= */ "1", 2266 /* isFactory= */ true, /* isActive= */ false, GetMTime(system_apex_path)); 2267 ASSERT_THAT(info_list->getApexInfo(), 2268 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_data), 2269 ApexInfoXmlEq(apex_info_xml_system))); 2270 auto& db = GetApexDatabaseForTesting(); 2271 // Check that it was mounted from decompressed apex. It should also be mounted 2272 // on dm-verity device. 2273 db.ForallMountedApexes("com.android.apex.compressed", 2274 [&](const MountedApexData& data, bool latest) { 2275 ASSERT_TRUE(latest); 2276 ASSERT_EQ(data.full_path, data_apex_path); 2277 ASSERT_EQ(data.device_name, 2278 "com.android.apex.compressed@2.chroot"); 2279 }); 2280 } 2281 2282 // Test when we update CAPEX and there is a lower version present in data 2283 TEST_F(ApexdMountTest, OnOtaChrootBootstrapDataLowerThanCapex) { 2284 auto apex_path = AddPreInstalledApex("com.android.apex.compressed.v2.capex"); 2285 AddDataApex("com.android.apex.compressed.v1_original.apex"); 2286 2287 ASSERT_EQ(OnOtaChrootBootstrap(), 0); 2288 2289 // Decompressed APEX should be mounted from reserved dir 2290 std::string decompressed_active_apex = 2291 StringPrintf("%s/com.android.apex.compressed@2%s", 2292 GetDecompressionDir().c_str(), kOtaApexPackageSuffix); 2293 UnmountOnTearDown(decompressed_active_apex); 2294 2295 auto apex_mounts = GetApexMounts(); 2296 ASSERT_THAT(apex_mounts, 2297 UnorderedElementsAre("/apex/com.android.apex.compressed", 2298 "/apex/com.android.apex.compressed@2")); 2299 2300 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); 2301 auto info_list = 2302 com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); 2303 ASSERT_TRUE(info_list.has_value()); 2304 auto apex_info_xml = com::android::apex::ApexInfo( 2305 /* moduleName= */ "com.android.apex.compressed", 2306 /* modulePath= */ decompressed_active_apex, 2307 /* preinstalledModulePath= */ apex_path, 2308 /* versionCode= */ 2, /* versionName= */ "2", 2309 /* isFactory= */ true, /* isActive= */ true, 2310 GetMTime(decompressed_active_apex)); 2311 ASSERT_THAT(info_list->getApexInfo(), 2312 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml))); 2313 auto& db = GetApexDatabaseForTesting(); 2314 // Check that it was mounted from decompressed apex. It should also be mounted 2315 // on dm-verity device. 2316 db.ForallMountedApexes("com.android.apex.compressed", 2317 [&](const MountedApexData& data, bool latest) { 2318 ASSERT_TRUE(latest); 2319 ASSERT_EQ(data.full_path, decompressed_active_apex); 2320 ASSERT_EQ(data.device_name, 2321 "com.android.apex.compressed@2.chroot"); 2322 }); 2323 } 2324 2325 // Test when we update CAPEX and there is a same version present in data 2326 TEST_F(ApexdMountTest, OnOtaChrootBootstrapDataSameAsCapex) { 2327 auto system_apex_path = 2328 PrepareCompressedApex("com.android.apex.compressed.v1.capex"); 2329 auto data_apex_path = 2330 AddDataApex("com.android.apex.compressed.v1_original.apex"); 2331 2332 ASSERT_EQ(OnOtaChrootBootstrap(), 0); 2333 2334 // Data APEX should be mounted 2335 UnmountOnTearDown(data_apex_path); 2336 2337 auto apex_mounts = GetApexMounts(); 2338 ASSERT_THAT(apex_mounts, 2339 UnorderedElementsAre("/apex/com.android.apex.compressed", 2340 "/apex/com.android.apex.compressed@1")); 2341 2342 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); 2343 auto info_list = 2344 com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); 2345 ASSERT_TRUE(info_list.has_value()); 2346 auto apex_info_xml_data = com::android::apex::ApexInfo( 2347 /* moduleName= */ "com.android.apex.compressed", 2348 /* modulePath= */ data_apex_path, 2349 /* preinstalledModulePath= */ system_apex_path, 2350 /* versionCode= */ 1, /* versionName= */ "1", 2351 /* isFactory= */ false, /* isActive= */ true, GetMTime(data_apex_path)); 2352 auto apex_info_xml_system = com::android::apex::ApexInfo( 2353 /* moduleName= */ "com.android.apex.compressed", 2354 /* modulePath= */ system_apex_path, 2355 /* preinstalledModulePath= */ system_apex_path, 2356 /* versionCode= */ 1, /* versionName= */ "1", 2357 /* isFactory= */ true, /* isActive= */ false, GetMTime(system_apex_path)); 2358 ASSERT_THAT(info_list->getApexInfo(), 2359 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_data), 2360 ApexInfoXmlEq(apex_info_xml_system))); 2361 auto& db = GetApexDatabaseForTesting(); 2362 // Check that it was mounted from decompressed apex. It should also be mounted 2363 // on dm-verity device. 2364 db.ForallMountedApexes("com.android.apex.compressed", 2365 [&](const MountedApexData& data, bool latest) { 2366 ASSERT_TRUE(latest); 2367 ASSERT_EQ(data.full_path, data_apex_path); 2368 ASSERT_EQ(data.device_name, 2369 "com.android.apex.compressed@1.chroot"); 2370 }); 2371 } 2372 2373 TEST_F(ApexdMountTest, OnOtaChrootBootstrapDataHasDifferentKeyThanCapex) { 2374 AddDataApex("com.android.apex.compressed_different_key.capex"); 2375 // Place a same version capex in current built_in_dir, which has different key 2376 auto apex_path = AddPreInstalledApex("com.android.apex.compressed.v1.capex"); 2377 2378 ASSERT_EQ(OnOtaChrootBootstrap(), 0); 2379 2380 // New decompressed APEX should be mounted from ota_reserved directory 2381 std::string decompressed_active_apex = 2382 StringPrintf("%s/com.android.apex.compressed@1%s", 2383 GetDecompressionDir().c_str(), kOtaApexPackageSuffix); 2384 UnmountOnTearDown(decompressed_active_apex); 2385 2386 auto apex_mounts = GetApexMounts(); 2387 ASSERT_THAT(apex_mounts, 2388 UnorderedElementsAre("/apex/com.android.apex.compressed", 2389 "/apex/com.android.apex.compressed@1")); 2390 2391 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); 2392 auto info_list = 2393 com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); 2394 ASSERT_TRUE(info_list.has_value()); 2395 auto apex_info_xml_decompressed = com::android::apex::ApexInfo( 2396 /* moduleName= */ "com.android.apex.compressed", 2397 /* modulePath= */ decompressed_active_apex, 2398 /* preinstalledModulePath= */ apex_path, 2399 /* versionCode= */ 1, /* versionName= */ "1", 2400 /* isFactory= */ true, /* isActive= */ true, 2401 GetMTime(decompressed_active_apex)); 2402 ASSERT_THAT(info_list->getApexInfo(), 2403 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_decompressed))); 2404 auto& db = GetApexDatabaseForTesting(); 2405 // Check that it was mounted from decompressed apex. It should also be mounted 2406 // on dm-verity device. 2407 db.ForallMountedApexes("com.android.apex.compressed", 2408 [&](const MountedApexData& data, bool latest) { 2409 ASSERT_TRUE(latest); 2410 ASSERT_EQ(data.full_path, decompressed_active_apex); 2411 ASSERT_EQ(data.device_name, 2412 "com.android.apex.compressed@1.chroot"); 2413 }); 2414 } 2415 2416 static std::string GetSelinuxContext(const std::string& file) { 2417 char* ctx; 2418 if (getfilecon(file.c_str(), &ctx) < 0) { 2419 PLOG(ERROR) << "Failed to getfilecon " << file; 2420 return ""; 2421 } 2422 std::string result(ctx); 2423 freecon(ctx); 2424 return result; 2425 } 2426 2427 TEST_F(ApexdMountTest, OnOtaChrootBootstrapSelinuxLabelsAreCorrect) { 2428 std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex"); 2429 std::string apex_path_2 = AddPreInstalledApex( 2430 "com.android.apex.test.sharedlibs_generated.v1.libvX.apex"); 2431 std::string apex_path_3 = AddDataApex("apex.apexd_test_v2.apex"); 2432 2433 UnmountOnTearDown(apex_path_2); 2434 UnmountOnTearDown(apex_path_3); 2435 ASSERT_EQ(OnOtaChrootBootstrap(), 0); 2436 2437 EXPECT_EQ(GetSelinuxContext("/apex/apex-info-list.xml"), 2438 "u:object_r:apex_info_file:s0"); 2439 2440 EXPECT_EQ(GetSelinuxContext("/apex/sharedlibs"), 2441 "u:object_r:apex_mnt_dir:s0"); 2442 2443 EXPECT_EQ(GetSelinuxContext("/apex/com.android.apex.test_package"), 2444 "u:object_r:system_file:s0"); 2445 EXPECT_EQ(GetSelinuxContext("/apex/com.android.apex.test_package@2"), 2446 "u:object_r:system_file:s0"); 2447 } 2448 2449 TEST_F(ApexdMountTest, OnOtaChrootBootstrapDmDevicesHaveCorrectName) { 2450 std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex"); 2451 std::string apex_path_2 = 2452 AddPreInstalledApex("apex.apexd_test_different_app.apex"); 2453 std::string apex_path_3 = AddDataApex("apex.apexd_test_v2.apex"); 2454 2455 ASSERT_EQ(OnOtaChrootBootstrap(), 0); 2456 UnmountOnTearDown(apex_path_2); 2457 UnmountOnTearDown(apex_path_3); 2458 2459 MountedApexDatabase& db = GetApexDatabaseForTesting(); 2460 // com.android.apex.test_package_2 should be mounted directly on top of loop 2461 // device. 2462 db.ForallMountedApexes("com.android.apex.test_package_2", 2463 [&](const MountedApexData& data, bool latest) { 2464 ASSERT_TRUE(latest); 2465 ASSERT_THAT(data.device_name, IsEmpty()); 2466 ASSERT_THAT(data.loop_name, StartsWith("/dev")); 2467 }); 2468 // com.android.apex.test_package should be mounted on top of dm-verity device. 2469 db.ForallMountedApexes("com.android.apex.test_package", 2470 [&](const MountedApexData& data, bool latest) { 2471 ASSERT_TRUE(latest); 2472 ASSERT_EQ(data.device_name, 2473 "com.android.apex.test_package@2.chroot"); 2474 ASSERT_THAT(data.loop_name, StartsWith("/dev")); 2475 }); 2476 } 2477 2478 TEST_F(ApexdMountTest, 2479 OnOtaChrootBootstrapFailsToActivatePreInstalledApexKeepsGoing) { 2480 std::string apex_path_1 = 2481 AddPreInstalledApex("apex.apexd_test_manifest_mismatch.apex"); 2482 std::string apex_path_2 = 2483 AddPreInstalledApex("apex.apexd_test_different_app.apex"); 2484 2485 ASSERT_EQ(OnOtaChrootBootstrap(), 0); 2486 UnmountOnTearDown(apex_path_2); 2487 2488 auto apex_mounts = GetApexMounts(); 2489 ASSERT_THAT(apex_mounts, 2490 UnorderedElementsAre("/apex/com.android.apex.test_package_2", 2491 "/apex/com.android.apex.test_package_2@1")); 2492 2493 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); 2494 auto info_list = 2495 com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); 2496 ASSERT_TRUE(info_list.has_value()); 2497 auto apex_info_xml_1 = com::android::apex::ApexInfo( 2498 /* moduleName= */ "com.android.apex.test_package", 2499 /* modulePath= */ apex_path_1, 2500 /* preinstalledModulePath= */ apex_path_1, 2501 /* versionCode= */ 137, /* versionName= */ "1", 2502 /* isFactory= */ true, /* isActive= */ false, GetMTime(apex_path_1)); 2503 auto apex_info_xml_2 = com::android::apex::ApexInfo( 2504 /* moduleName= */ "com.android.apex.test_package_2", 2505 /* modulePath= */ apex_path_2, /* preinstalledModulePath= */ apex_path_2, 2506 /* versionCode= */ 1, /* versionName= */ "1", /* isFactory= */ true, 2507 /* isActive= */ true, GetMTime(apex_path_2)); 2508 2509 ASSERT_THAT(info_list->getApexInfo(), 2510 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1), 2511 ApexInfoXmlEq(apex_info_xml_2))); 2512 } 2513 2514 TEST_F(ApexdMountTest, 2515 OnOtaChrootBootstrapFailsToActivateDataApexFallsBackToPreInstalled) { 2516 std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex"); 2517 std::string apex_path_2 = 2518 AddPreInstalledApex("apex.apexd_test_different_app.apex"); 2519 std::string apex_path_3 = 2520 AddDataApex("apex.apexd_test_manifest_mismatch.apex"); 2521 2522 ASSERT_EQ(OnOtaChrootBootstrap(), 0); 2523 UnmountOnTearDown(apex_path_1); 2524 UnmountOnTearDown(apex_path_2); 2525 2526 auto apex_mounts = GetApexMounts(); 2527 ASSERT_THAT(apex_mounts, 2528 UnorderedElementsAre("/apex/com.android.apex.test_package", 2529 "/apex/com.android.apex.test_package@1", 2530 "/apex/com.android.apex.test_package_2", 2531 "/apex/com.android.apex.test_package_2@1")); 2532 2533 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); 2534 auto info_list = 2535 com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); 2536 ASSERT_TRUE(info_list.has_value()); 2537 auto apex_info_xml_1 = com::android::apex::ApexInfo( 2538 /* moduleName= */ "com.android.apex.test_package", 2539 /* modulePath= */ apex_path_1, 2540 /* preinstalledModulePath= */ apex_path_1, 2541 /* versionCode= */ 1, /* versionName= */ "1", 2542 /* isFactory= */ true, /* isActive= */ true, GetMTime(apex_path_1)); 2543 auto apex_info_xml_2 = com::android::apex::ApexInfo( 2544 /* moduleName= */ "com.android.apex.test_package_2", 2545 /* modulePath= */ apex_path_2, /* preinstalledModulePath= */ apex_path_2, 2546 /* versionCode= */ 1, /* versionName= */ "1", /* isFactory= */ true, 2547 /* isActive= */ true, GetMTime(apex_path_2)); 2548 2549 ASSERT_THAT(info_list->getApexInfo(), 2550 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1), 2551 ApexInfoXmlEq(apex_info_xml_2))); 2552 } 2553 2554 TEST_F(ApexdMountTest, OnOtaChrootBootstrapFlattenedApex) { 2555 std::string apex_dir_1 = GetBuiltInDir() + "/com.android.apex.test_package"; 2556 std::string apex_dir_2 = GetBuiltInDir() + "/com.android.apex.test_package_2"; 2557 2558 ASSERT_EQ(mkdir(apex_dir_1.c_str(), 0755), 0); 2559 ASSERT_EQ(mkdir(apex_dir_2.c_str(), 0755), 0); 2560 2561 auto write_manifest_fn = [&](const std::string& apex_dir, 2562 const std::string& module_name, int version) { 2563 using ::apex::proto::ApexManifest; 2564 2565 ApexManifest manifest; 2566 manifest.set_name(module_name); 2567 manifest.set_version(version); 2568 manifest.set_versionname(std::to_string(version)); 2569 2570 std::string out; 2571 manifest.SerializeToString(&out); 2572 ASSERT_TRUE(WriteStringToFile(out, apex_dir + "/apex_manifest.pb")); 2573 }; 2574 2575 write_manifest_fn(apex_dir_1, "com.android.apex.test_package", 2); 2576 write_manifest_fn(apex_dir_2, "com.android.apex.test_package_2", 1); 2577 2578 ASSERT_EQ(OnOtaChrootBootstrapFlattenedApex(), 0); 2579 2580 auto apex_mounts = GetApexMounts(); 2581 ASSERT_THAT(apex_mounts, 2582 UnorderedElementsAre("/apex/com.android.apex.test_package", 2583 "/apex/com.android.apex.test_package_2")); 2584 2585 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); 2586 ASSERT_EQ(GetSelinuxContext("/apex/apex-info-list.xml"), 2587 "u:object_r:apex_info_file:s0"); 2588 2589 auto info_list = 2590 com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); 2591 ASSERT_TRUE(info_list.has_value()); 2592 auto apex_info_xml_1 = com::android::apex::ApexInfo( 2593 /* moduleName= */ "com.android.apex.test_package", 2594 /* modulePath= */ apex_dir_1, 2595 /* preinstalledModulePath= */ apex_dir_1, 2596 /* versionCode= */ 2, /* versionName= */ "2", 2597 /* isFactory= */ true, /* isActive= */ true, 2598 /* lastUpdateMillis= */ 0); 2599 auto apex_info_xml_2 = com::android::apex::ApexInfo( 2600 /* moduleName= */ "com.android.apex.test_package_2", 2601 /* modulePath= */ apex_dir_2, 2602 /* preinstalledModulePath= */ apex_dir_2, 2603 /* versionCode= */ 1, /* versionName= */ "1", 2604 /* isFactory= */ true, /* isActive= */ true, 2605 /* lastUpdateMillis= */ 0); 2606 2607 ASSERT_THAT(info_list->getApexInfo(), 2608 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1), 2609 ApexInfoXmlEq(apex_info_xml_2))); 2610 } 2611 2612 TEST_F(ApexdMountTest, OnStartOnlyPreInstalledApexes) { 2613 MockCheckpointInterface checkpoint_interface; 2614 // Need to call InitializeVold before calling OnStart 2615 InitializeVold(&checkpoint_interface); 2616 2617 std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex"); 2618 std::string apex_path_2 = 2619 AddPreInstalledApex("apex.apexd_test_different_app.apex"); 2620 2621 ASSERT_RESULT_OK( 2622 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()})); 2623 2624 OnStart(); 2625 2626 UnmountOnTearDown(apex_path_1); 2627 UnmountOnTearDown(apex_path_2); 2628 2629 ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting"); 2630 auto apex_mounts = GetApexMounts(); 2631 ASSERT_THAT(apex_mounts, 2632 UnorderedElementsAre("/apex/com.android.apex.test_package", 2633 "/apex/com.android.apex.test_package@1", 2634 "/apex/com.android.apex.test_package_2", 2635 "/apex/com.android.apex.test_package_2@1")); 2636 } 2637 2638 TEST_F(ApexdMountTest, OnStartDataHasHigherVersion) { 2639 MockCheckpointInterface checkpoint_interface; 2640 // Need to call InitializeVold before calling OnStart 2641 InitializeVold(&checkpoint_interface); 2642 2643 AddPreInstalledApex("apex.apexd_test.apex"); 2644 std::string apex_path_2 = 2645 AddPreInstalledApex("apex.apexd_test_different_app.apex"); 2646 std::string apex_path_3 = AddDataApex("apex.apexd_test_v2.apex"); 2647 2648 ASSERT_RESULT_OK( 2649 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()})); 2650 2651 OnStart(); 2652 2653 UnmountOnTearDown(apex_path_2); 2654 UnmountOnTearDown(apex_path_3); 2655 2656 ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting"); 2657 auto apex_mounts = GetApexMounts(); 2658 ASSERT_THAT(apex_mounts, 2659 UnorderedElementsAre("/apex/com.android.apex.test_package", 2660 "/apex/com.android.apex.test_package@2", 2661 "/apex/com.android.apex.test_package_2", 2662 "/apex/com.android.apex.test_package_2@1")); 2663 } 2664 2665 TEST_F(ApexdMountTest, OnStartDataHasWrongSHA) { 2666 MockCheckpointInterface checkpoint_interface; 2667 // Need to call InitializeVold before calling OnStart 2668 InitializeVold(&checkpoint_interface); 2669 2670 std::string apex_path = AddPreInstalledApex("com.android.apex.cts.shim.apex"); 2671 AddDataApex("com.android.apex.cts.shim.v2_wrong_sha.apex"); 2672 2673 ASSERT_RESULT_OK( 2674 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()})); 2675 2676 UnmountOnTearDown(apex_path); 2677 OnStart(); 2678 2679 // Check system shim apex is activated instead of the data one. 2680 auto apex_mounts = GetApexMounts(); 2681 ASSERT_THAT(apex_mounts, 2682 UnorderedElementsAre("/apex/com.android.apex.cts.shim", 2683 "/apex/com.android.apex.cts.shim@1")); 2684 } 2685 2686 TEST_F(ApexdMountTest, OnStartDataHasSameVersion) { 2687 MockCheckpointInterface checkpoint_interface; 2688 // Need to call InitializeVold before calling OnStart 2689 InitializeVold(&checkpoint_interface); 2690 2691 AddPreInstalledApex("apex.apexd_test.apex"); 2692 std::string apex_path_2 = 2693 AddPreInstalledApex("apex.apexd_test_different_app.apex"); 2694 std::string apex_path_3 = AddDataApex("apex.apexd_test.apex"); 2695 2696 ASSERT_RESULT_OK( 2697 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()})); 2698 2699 OnStart(); 2700 2701 UnmountOnTearDown(apex_path_2); 2702 UnmountOnTearDown(apex_path_3); 2703 2704 ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting"); 2705 auto apex_mounts = GetApexMounts(); 2706 ASSERT_THAT(apex_mounts, 2707 UnorderedElementsAre("/apex/com.android.apex.test_package", 2708 "/apex/com.android.apex.test_package@1", 2709 "/apex/com.android.apex.test_package_2", 2710 "/apex/com.android.apex.test_package_2@1")); 2711 2712 auto& db = GetApexDatabaseForTesting(); 2713 // Check that it was mounted from data apex, not pre-installed one. 2714 db.ForallMountedApexes("com.android.apex.test_package", 2715 [&](const MountedApexData& data, bool latest) { 2716 ASSERT_TRUE(latest); 2717 ASSERT_EQ(data.full_path, apex_path_3); 2718 }); 2719 } 2720 2721 TEST_F(ApexdMountTest, OnStartSystemHasHigherVersion) { 2722 MockCheckpointInterface checkpoint_interface; 2723 // Need to call InitializeVold before calling OnStart 2724 InitializeVold(&checkpoint_interface); 2725 2726 std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test_v2.apex"); 2727 std::string apex_path_2 = 2728 AddPreInstalledApex("apex.apexd_test_different_app.apex"); 2729 AddDataApex("apex.apexd_test.apex"); 2730 2731 ASSERT_RESULT_OK( 2732 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()})); 2733 2734 OnStart(); 2735 2736 UnmountOnTearDown(apex_path_1); 2737 UnmountOnTearDown(apex_path_2); 2738 2739 ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting"); 2740 auto apex_mounts = GetApexMounts(); 2741 ASSERT_THAT(apex_mounts, 2742 UnorderedElementsAre("/apex/com.android.apex.test_package", 2743 "/apex/com.android.apex.test_package@2", 2744 "/apex/com.android.apex.test_package_2", 2745 "/apex/com.android.apex.test_package_2@1")); 2746 2747 auto& db = GetApexDatabaseForTesting(); 2748 // Check that it was mounted from pre-installed one. 2749 db.ForallMountedApexes("com.android.apex.test_package", 2750 [&](const MountedApexData& data, bool latest) { 2751 ASSERT_TRUE(latest); 2752 ASSERT_EQ(data.full_path, apex_path_1); 2753 }); 2754 } 2755 2756 TEST_F(ApexdMountTest, OnStartFailsToActivateApexOnDataFallsBackToBuiltIn) { 2757 MockCheckpointInterface checkpoint_interface; 2758 // Need to call InitializeVold before calling OnStart 2759 InitializeVold(&checkpoint_interface); 2760 2761 std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex"); 2762 std::string apex_path_2 = 2763 AddPreInstalledApex("apex.apexd_test_different_app.apex"); 2764 AddDataApex("apex.apexd_test_manifest_mismatch.apex"); 2765 2766 ASSERT_RESULT_OK( 2767 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()})); 2768 2769 OnStart(); 2770 2771 UnmountOnTearDown(apex_path_1); 2772 UnmountOnTearDown(apex_path_2); 2773 2774 ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting"); 2775 auto apex_mounts = GetApexMounts(); 2776 ASSERT_THAT(apex_mounts, 2777 UnorderedElementsAre("/apex/com.android.apex.test_package", 2778 "/apex/com.android.apex.test_package@1", 2779 "/apex/com.android.apex.test_package_2", 2780 "/apex/com.android.apex.test_package_2@1")); 2781 2782 auto& db = GetApexDatabaseForTesting(); 2783 // Check that it was mounted from pre-installed apex. 2784 db.ForallMountedApexes("com.android.apex.test_package", 2785 [&](const MountedApexData& data, bool latest) { 2786 ASSERT_TRUE(latest); 2787 ASSERT_EQ(data.full_path, apex_path_1); 2788 }); 2789 } 2790 2791 TEST_F(ApexdMountTest, OnStartApexOnDataHasWrongKeyFallsBackToBuiltIn) { 2792 MockCheckpointInterface checkpoint_interface; 2793 // Need to call InitializeVold before calling OnStart 2794 InitializeVold(&checkpoint_interface); 2795 2796 std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex"); 2797 std::string apex_path_2 = 2798 AddPreInstalledApex("apex.apexd_test_different_app.apex"); 2799 std::string apex_path_3 = 2800 AddDataApex("apex.apexd_test_different_key_v2.apex"); 2801 2802 { 2803 auto apex = ApexFile::Open(apex_path_3); 2804 ASSERT_TRUE(IsOk(apex)); 2805 ASSERT_EQ(static_cast<uint64_t>(apex->GetManifest().version()), 2ULL); 2806 } 2807 2808 ASSERT_RESULT_OK( 2809 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()})); 2810 2811 OnStart(); 2812 2813 UnmountOnTearDown(apex_path_1); 2814 UnmountOnTearDown(apex_path_2); 2815 2816 ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting"); 2817 auto apex_mounts = GetApexMounts(); 2818 ASSERT_THAT(apex_mounts, 2819 UnorderedElementsAre("/apex/com.android.apex.test_package", 2820 "/apex/com.android.apex.test_package@1", 2821 "/apex/com.android.apex.test_package_2", 2822 "/apex/com.android.apex.test_package_2@1")); 2823 2824 auto& db = GetApexDatabaseForTesting(); 2825 // Check that it was mounted from pre-installed apex. 2826 db.ForallMountedApexes("com.android.apex.test_package", 2827 [&](const MountedApexData& data, bool latest) { 2828 ASSERT_TRUE(latest); 2829 ASSERT_EQ(data.full_path, apex_path_1); 2830 }); 2831 } 2832 2833 TEST_F(ApexdMountTest, OnStartOnlyPreInstalledCapexes) { 2834 MockCheckpointInterface checkpoint_interface; 2835 // Need to call InitializeVold before calling OnStart 2836 InitializeVold(&checkpoint_interface); 2837 2838 std::string apex_path_1 = 2839 AddPreInstalledApex("com.android.apex.compressed.v1.capex"); 2840 2841 ASSERT_RESULT_OK( 2842 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()})); 2843 2844 OnStart(); 2845 2846 // Decompressed APEX should be mounted 2847 std::string decompressed_active_apex = StringPrintf( 2848 "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(), 2849 kDecompressedApexPackageSuffix); 2850 UnmountOnTearDown(decompressed_active_apex); 2851 2852 ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting"); 2853 auto apex_mounts = GetApexMounts(); 2854 ASSERT_THAT(apex_mounts, 2855 UnorderedElementsAre("/apex/com.android.apex.compressed", 2856 "/apex/com.android.apex.compressed@1")); 2857 auto& db = GetApexDatabaseForTesting(); 2858 // Check that it was mounted from decompressed apex. 2859 db.ForallMountedApexes("com.android.apex.compressed", 2860 [&](const MountedApexData& data, bool latest) { 2861 ASSERT_TRUE(latest); 2862 ASSERT_EQ(data.full_path, decompressed_active_apex); 2863 ASSERT_EQ(data.device_name, 2864 "com.android.apex.compressed"); 2865 }); 2866 } 2867 2868 TEST_F(ApexdMountTest, OnStartDataHasHigherVersionThanCapex) { 2869 MockCheckpointInterface checkpoint_interface; 2870 // Need to call InitializeVold before calling OnStart 2871 InitializeVold(&checkpoint_interface); 2872 2873 AddPreInstalledApex("com.android.apex.compressed.v1.capex"); 2874 std::string apex_path_2 = 2875 AddDataApex("com.android.apex.compressed.v2_original.apex"); 2876 2877 ASSERT_RESULT_OK( 2878 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()})); 2879 2880 OnStart(); 2881 2882 UnmountOnTearDown(apex_path_2); 2883 2884 ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting"); 2885 auto apex_mounts = GetApexMounts(); 2886 ASSERT_THAT(apex_mounts, 2887 UnorderedElementsAre("/apex/com.android.apex.compressed", 2888 "/apex/com.android.apex.compressed@2")); 2889 auto& db = GetApexDatabaseForTesting(); 2890 // Check that it was mounted from data apex. 2891 db.ForallMountedApexes("com.android.apex.compressed", 2892 [&](const MountedApexData& data, bool latest) { 2893 ASSERT_TRUE(latest); 2894 ASSERT_EQ(data.full_path, apex_path_2); 2895 ASSERT_EQ(data.device_name, 2896 "com.android.apex.compressed"); 2897 }); 2898 } 2899 2900 TEST_F(ApexdMountTest, OnStartDataHasSameVersionAsCapex) { 2901 MockCheckpointInterface checkpoint_interface; 2902 // Need to call InitializeVold before calling OnStart 2903 InitializeVold(&checkpoint_interface); 2904 2905 AddPreInstalledApex("com.android.apex.compressed.v1.capex"); 2906 std::string apex_path_2 = 2907 AddDataApex("com.android.apex.compressed.v1_original.apex"); 2908 2909 ASSERT_RESULT_OK( 2910 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()})); 2911 2912 OnStart(); 2913 2914 // Data APEX should be mounted 2915 UnmountOnTearDown(apex_path_2); 2916 2917 ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting"); 2918 auto apex_mounts = GetApexMounts(); 2919 ASSERT_THAT(apex_mounts, 2920 UnorderedElementsAre("/apex/com.android.apex.compressed", 2921 "/apex/com.android.apex.compressed@1")); 2922 2923 auto& db = GetApexDatabaseForTesting(); 2924 // Check that it was mounted from data apex, not pre-installed one. 2925 db.ForallMountedApexes("com.android.apex.compressed", 2926 [&](const MountedApexData& data, bool latest) { 2927 ASSERT_TRUE(latest); 2928 ASSERT_EQ(data.full_path, apex_path_2); 2929 ASSERT_EQ(data.device_name, 2930 "com.android.apex.compressed"); 2931 }); 2932 } 2933 2934 TEST_F(ApexdMountTest, OnStartSystemHasHigherVersionCapexThanData) { 2935 MockCheckpointInterface checkpoint_interface; 2936 // Need to call InitializeVold before calling OnStart 2937 InitializeVold(&checkpoint_interface); 2938 2939 std::string apex_path_1 = 2940 AddPreInstalledApex("com.android.apex.compressed.v2.capex"); 2941 AddDataApex("com.android.apex.compressed.v1_original.apex"); 2942 2943 ASSERT_RESULT_OK( 2944 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()})); 2945 2946 OnStart(); 2947 2948 // Decompressed APEX should be mounted 2949 std::string decompressed_active_apex = StringPrintf( 2950 "%s/com.android.apex.compressed@2%s", GetDecompressionDir().c_str(), 2951 kDecompressedApexPackageSuffix); 2952 UnmountOnTearDown(decompressed_active_apex); 2953 2954 ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting"); 2955 auto apex_mounts = GetApexMounts(); 2956 ASSERT_THAT(apex_mounts, 2957 UnorderedElementsAre("/apex/com.android.apex.compressed", 2958 "/apex/com.android.apex.compressed@2")); 2959 2960 auto& db = GetApexDatabaseForTesting(); 2961 // Check that it was mounted from compressed apex 2962 db.ForallMountedApexes("com.android.apex.compressed", 2963 [&](const MountedApexData& data, bool latest) { 2964 ASSERT_TRUE(latest); 2965 ASSERT_EQ(data.full_path, decompressed_active_apex); 2966 ASSERT_EQ(data.device_name, 2967 "com.android.apex.compressed"); 2968 }); 2969 } 2970 2971 TEST_F(ApexdMountTest, OnStartFailsToActivateApexOnDataFallsBackToCapex) { 2972 MockCheckpointInterface checkpoint_interface; 2973 // Need to call InitializeVold before calling OnStart 2974 InitializeVold(&checkpoint_interface); 2975 2976 AddPreInstalledApex("com.android.apex.compressed.v1.capex"); 2977 AddDataApex("com.android.apex.compressed.v2_manifest_mismatch.apex"); 2978 2979 ASSERT_RESULT_OK( 2980 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()})); 2981 2982 OnStart(); 2983 2984 // Decompressed APEX should be mounted 2985 std::string decompressed_active_apex = StringPrintf( 2986 "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(), 2987 kDecompressedApexPackageSuffix); 2988 UnmountOnTearDown(decompressed_active_apex); 2989 2990 ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting"); 2991 auto apex_mounts = GetApexMounts(); 2992 ASSERT_THAT(apex_mounts, 2993 UnorderedElementsAre("/apex/com.android.apex.compressed", 2994 "/apex/com.android.apex.compressed@1")); 2995 auto& db = GetApexDatabaseForTesting(); 2996 // Check that it was mounted from decompressed apex. It should also be mounted 2997 // on dm-verity device. 2998 db.ForallMountedApexes("com.android.apex.compressed", 2999 [&](const MountedApexData& data, bool latest) { 3000 ASSERT_TRUE(latest); 3001 ASSERT_EQ(data.full_path, decompressed_active_apex); 3002 ASSERT_EQ(data.device_name, 3003 "com.android.apex.compressed"); 3004 }); 3005 } 3006 3007 // Test scenario when we fallback to capex but it already has a decompressed 3008 // version on data 3009 TEST_F(ApexdMountTest, OnStartFallbackToAlreadyDecompressedCapex) { 3010 MockCheckpointInterface checkpoint_interface; 3011 // Need to call InitializeVold before calling OnStart 3012 InitializeVold(&checkpoint_interface); 3013 3014 PrepareCompressedApex("com.android.apex.compressed.v1.capex"); 3015 AddDataApex("com.android.apex.compressed.v2_manifest_mismatch.apex"); 3016 3017 ASSERT_RESULT_OK( 3018 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()})); 3019 3020 OnStart(); 3021 3022 // Decompressed APEX should be mounted 3023 std::string decompressed_active_apex = StringPrintf( 3024 "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(), 3025 kDecompressedApexPackageSuffix); 3026 UnmountOnTearDown(decompressed_active_apex); 3027 3028 ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting"); 3029 auto apex_mounts = GetApexMounts(); 3030 ASSERT_THAT(apex_mounts, 3031 UnorderedElementsAre("/apex/com.android.apex.compressed", 3032 "/apex/com.android.apex.compressed@1")); 3033 auto& db = GetApexDatabaseForTesting(); 3034 // Check that it was mounted from decompressed apex. 3035 db.ForallMountedApexes("com.android.apex.compressed", 3036 [&](const MountedApexData& data, bool latest) { 3037 ASSERT_TRUE(latest); 3038 ASSERT_EQ(data.full_path, decompressed_active_apex); 3039 ASSERT_EQ(data.device_name, 3040 "com.android.apex.compressed"); 3041 }); 3042 } 3043 3044 // Test scenario when we fallback to capex but it has same version as corrupt 3045 // data apex 3046 TEST_F(ApexdMountTest, OnStartFallbackToCapexSameVersion) { 3047 MockCheckpointInterface checkpoint_interface; 3048 // Need to call InitializeVold before calling OnStart 3049 InitializeVold(&checkpoint_interface); 3050 3051 AddPreInstalledApex("com.android.apex.compressed.v2.capex"); 3052 // Add data apex using the common naming convention for /data/apex/active 3053 // directory 3054 fs::copy(GetTestFile("com.android.apex.compressed.v2_manifest_mismatch.apex"), 3055 GetDataDir() + "/com.android.apex.compressed@2.apex"); 3056 3057 ASSERT_RESULT_OK( 3058 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()})); 3059 3060 OnStart(); 3061 3062 // Decompressed APEX should be mounted 3063 std::string decompressed_active_apex = StringPrintf( 3064 "%s/com.android.apex.compressed@2%s", GetDecompressionDir().c_str(), 3065 kDecompressedApexPackageSuffix); 3066 UnmountOnTearDown(decompressed_active_apex); 3067 3068 ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting"); 3069 auto apex_mounts = GetApexMounts(); 3070 ASSERT_THAT(apex_mounts, 3071 UnorderedElementsAre("/apex/com.android.apex.compressed", 3072 "/apex/com.android.apex.compressed@2")); 3073 auto& db = GetApexDatabaseForTesting(); 3074 // Check that it was mounted from decompressed apex. 3075 db.ForallMountedApexes("com.android.apex.compressed", 3076 [&](const MountedApexData& data, bool latest) { 3077 ASSERT_TRUE(latest); 3078 ASSERT_EQ(data.full_path, decompressed_active_apex); 3079 ASSERT_EQ(data.device_name, 3080 "com.android.apex.compressed"); 3081 }); 3082 } 3083 3084 TEST_F(ApexdMountTest, OnStartCapexToApex) { 3085 MockCheckpointInterface checkpoint_interface; 3086 // Need to call InitializeVold before calling OnStart 3087 InitializeVold(&checkpoint_interface); 3088 3089 TemporaryDir previous_built_in_dir; 3090 PrepareCompressedApex("com.android.apex.compressed.v1.capex", 3091 previous_built_in_dir.path); 3092 auto apex_path = 3093 AddPreInstalledApex("com.android.apex.compressed.v1_original.apex"); 3094 3095 ASSERT_RESULT_OK( 3096 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()})); 3097 3098 OnStart(); 3099 3100 // Uncompressed APEX should be mounted 3101 UnmountOnTearDown(apex_path); 3102 3103 ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting"); 3104 auto apex_mounts = GetApexMounts(); 3105 ASSERT_THAT(apex_mounts, 3106 UnorderedElementsAre("/apex/com.android.apex.compressed", 3107 "/apex/com.android.apex.compressed@1")); 3108 auto& db = GetApexDatabaseForTesting(); 3109 // Check that it was mounted from decompressed apex. 3110 db.ForallMountedApexes("com.android.apex.compressed", 3111 [&](const MountedApexData& data, bool latest) { 3112 ASSERT_TRUE(latest); 3113 ASSERT_EQ(data.full_path, apex_path); 3114 ASSERT_THAT(data.device_name, IsEmpty()); 3115 }); 3116 } 3117 3118 // Test to ensure we do not mount decompressed APEX from /data/apex/active 3119 TEST_F(ApexdMountTest, OnStartOrphanedDecompressedApexInActiveDirectory) { 3120 MockCheckpointInterface checkpoint_interface; 3121 // Need to call InitializeVold before calling OnStart 3122 InitializeVold(&checkpoint_interface); 3123 3124 // Place a decompressed APEX in /data/apex/active. This apex should not 3125 // be mounted since it's not in correct location. Instead, the 3126 // pre-installed APEX should be mounted. 3127 auto decompressed_apex_in_active_dir = 3128 StringPrintf("%s/com.android.apex.compressed@1%s", GetDataDir().c_str(), 3129 kDecompressedApexPackageSuffix); 3130 fs::copy(GetTestFile("com.android.apex.compressed.v1_original.apex"), 3131 decompressed_apex_in_active_dir); 3132 auto apex_path = 3133 AddPreInstalledApex("com.android.apex.compressed.v1_original.apex"); 3134 3135 ASSERT_RESULT_OK( 3136 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()})); 3137 3138 OnStart(); 3139 3140 // Pre-installed APEX should be mounted 3141 UnmountOnTearDown(apex_path); 3142 auto& db = GetApexDatabaseForTesting(); 3143 // Check that pre-installed APEX has been activated 3144 db.ForallMountedApexes("com.android.apex.compressed", 3145 [&](const MountedApexData& data, bool latest) { 3146 ASSERT_TRUE(latest); 3147 ASSERT_EQ(data.full_path, apex_path); 3148 ASSERT_THAT(data.device_name, IsEmpty()); 3149 }); 3150 } 3151 3152 // Test scenario when decompressed version has different version than 3153 // pre-installed CAPEX 3154 TEST_F(ApexdMountTest, OnStartDecompressedApexVersionDifferentThanCapex) { 3155 MockCheckpointInterface checkpoint_interface; 3156 // Need to call InitializeVold before calling OnStart 3157 InitializeVold(&checkpoint_interface); 3158 3159 TemporaryDir previous_built_in_dir; 3160 PrepareCompressedApex("com.android.apex.compressed.v2.capex", 3161 previous_built_in_dir.path); 3162 auto apex_path = AddPreInstalledApex("com.android.apex.compressed.v1.capex"); 3163 3164 ASSERT_RESULT_OK( 3165 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()})); 3166 3167 OnStart(); 3168 3169 // Existing higher version decompressed APEX should be ignored and new 3170 // pre-installed CAPEX should be decompressed and mounted 3171 std::string decompressed_active_apex = StringPrintf( 3172 "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(), 3173 kDecompressedApexPackageSuffix); 3174 UnmountOnTearDown(decompressed_active_apex); 3175 3176 ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting"); 3177 auto apex_mounts = GetApexMounts(); 3178 ASSERT_THAT(apex_mounts, 3179 UnorderedElementsAre("/apex/com.android.apex.compressed", 3180 "/apex/com.android.apex.compressed@1")); 3181 auto& db = GetApexDatabaseForTesting(); 3182 // Check that it was mounted from newly decompressed apex. 3183 db.ForallMountedApexes("com.android.apex.compressed", 3184 [&](const MountedApexData& data, bool latest) { 3185 ASSERT_TRUE(latest); 3186 ASSERT_EQ(data.full_path, decompressed_active_apex); 3187 ASSERT_EQ(data.device_name, 3188 "com.android.apex.compressed"); 3189 }); 3190 } 3191 3192 // Test that ota_apex is persisted until slot switch 3193 TEST_F(ApexdMountTest, OnStartOtaApexKeptUntilSlotSwitch) { 3194 MockCheckpointInterface checkpoint_interface; 3195 // Need to call InitializeVold before calling OnStart 3196 InitializeVold(&checkpoint_interface); 3197 3198 // Imagine current system has v1 capex and we have v2 incoming via ota 3199 auto old_capex = AddPreInstalledApex("com.android.apex.compressed.v1.capex"); 3200 auto ota_apex_path = 3201 StringPrintf("%s/com.android.apex.compressed@2%s", 3202 GetDecompressionDir().c_str(), kOtaApexPackageSuffix); 3203 fs::copy(GetTestFile("com.android.apex.compressed.v2_original.apex"), 3204 ota_apex_path.c_str()); 3205 3206 ASSERT_RESULT_OK( 3207 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()})); 3208 3209 // When we call OnStart for the first time, it will decompress v1 capex and 3210 // activate it, while after second call it will decompress v2 capex and 3211 // activate it. We need to make sure that activated APEXes are cleaned up 3212 // after test finishes. 3213 auto old_decompressed_apex = StringPrintf( 3214 "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(), 3215 kDecompressedApexPackageSuffix); 3216 auto new_decompressed_apex = StringPrintf( 3217 "%s/com.android.apex.compressed@2%s", GetDecompressionDir().c_str(), 3218 kDecompressedApexPackageSuffix); 3219 UnmountOnTearDown(old_decompressed_apex); 3220 UnmountOnTearDown(new_decompressed_apex); 3221 3222 // First try starting without slot switch. Since we are booting with 3223 // old pre-installed capex, ota_apex should not be deleted 3224 OnStart(); 3225 auto path_exists = PathExists(ota_apex_path); 3226 ASSERT_TRUE(*path_exists); 3227 3228 // When we switch slot, the pre-installed APEX will match ota_apex 3229 // and the ota_apex will end up getting renamed. 3230 RemoveFileIfExists(old_capex); 3231 AddPreInstalledApex("com.android.apex.compressed.v2.capex"); 3232 ApexFileRepository::GetInstance().Reset(GetDecompressionDir()); 3233 ASSERT_RESULT_OK( 3234 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()})); 3235 OnStart(); 3236 path_exists = PathExists(ota_apex_path); 3237 ASSERT_FALSE(*path_exists); 3238 } 3239 3240 // Test scenario when decompressed version has same version but different 3241 // digest 3242 TEST_F(ApexdMountTest, 3243 OnStartDecompressedApexVersionSameAsCapexDifferentDigest) { 3244 MockCheckpointInterface checkpoint_interface; 3245 // Need to call InitializeVold before calling OnStart 3246 InitializeVold(&checkpoint_interface); 3247 3248 // Push a CAPEX to system without decompressing it 3249 auto apex_path = AddPreInstalledApex("com.android.apex.compressed.v1.capex"); 3250 auto pre_installed_apex = ApexFile::Open(apex_path); 3251 // Now push an APEX with different root digest as decompressed APEX 3252 auto decompressed_apex_path = StringPrintf( 3253 "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(), 3254 kDecompressedApexPackageSuffix); 3255 fs::copy(GetTestFile( 3256 "com.android.apex.compressed.v1_different_digest_original.apex"), 3257 decompressed_apex_path); 3258 auto different_digest_apex = ApexFile::Open(decompressed_apex_path); 3259 auto different_digest = GetRootDigest(*different_digest_apex); 3260 ASSERT_NE( 3261 pre_installed_apex->GetManifest().capexmetadata().originalapexdigest(), 3262 different_digest); 3263 3264 ASSERT_RESULT_OK( 3265 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()})); 3266 3267 OnStart(); 3268 3269 // Existing same version decompressed APEX with different root digest should 3270 // be ignored and the pre-installed CAPEX should be decompressed again. 3271 UnmountOnTearDown(decompressed_apex_path); 3272 3273 // Ensure decompressed apex has same digest as pre-installed 3274 auto decompressed_apex = ApexFile::Open(decompressed_apex_path); 3275 ASSERT_EQ( 3276 pre_installed_apex->GetManifest().capexmetadata().originalapexdigest(), 3277 GetRootDigest(*decompressed_apex)); 3278 ASSERT_NE(GetRootDigest(*decompressed_apex), different_digest); 3279 } 3280 3281 // Test when decompressed APEX has different key than CAPEX 3282 TEST_F(ApexdMountTest, OnStartDecompressedApexVersionSameAsCapexDifferentKey) { 3283 MockCheckpointInterface checkpoint_interface; 3284 // Need to call InitializeVold before calling OnStart 3285 InitializeVold(&checkpoint_interface); 3286 3287 TemporaryDir previous_built_in_dir; 3288 auto different_key_apex_path = 3289 PrepareCompressedApex("com.android.apex.compressed_different_key.capex", 3290 previous_built_in_dir.path); 3291 // Place a same version capex in current built_in_dir, which has different key 3292 auto apex_path = AddPreInstalledApex("com.android.apex.compressed.v1.capex"); 3293 3294 ASSERT_RESULT_OK( 3295 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()})); 3296 3297 OnStart(); 3298 3299 // Existing same version decompressed APEX should be ignored and new 3300 // pre-installed CAPEX should be decompressed and mounted 3301 std::string decompressed_active_apex = StringPrintf( 3302 "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(), 3303 kDecompressedApexPackageSuffix); 3304 UnmountOnTearDown(decompressed_active_apex); 3305 3306 // Ensure decompressed apex has same digest as pre-installed 3307 auto pre_installed_apex = ApexFile::Open(apex_path); 3308 auto decompressed_apex = ApexFile::Open(decompressed_active_apex); 3309 auto different_key_apex = ApexFile::Open(different_key_apex_path); 3310 ASSERT_EQ( 3311 pre_installed_apex->GetManifest().capexmetadata().originalapexdigest(), 3312 GetRootDigest(*decompressed_apex)); 3313 ASSERT_NE( 3314 pre_installed_apex->GetManifest().capexmetadata().originalapexdigest(), 3315 GetRootDigest(*different_key_apex)); 3316 } 3317 3318 TEST_F(ApexdMountTest, PopulateFromMountsChecksPathPrefix) { 3319 AddPreInstalledApex("apex.apexd_test.apex"); 3320 std::string apex_path = AddDataApex("apex.apexd_test_v2.apex"); 3321 3322 // Mount an apex from decomrpession_dir 3323 PrepareCompressedApex("com.android.apex.compressed.v1.capex"); 3324 std::string decompressed_apex = 3325 StringPrintf("%s/com.android.apex.compressed@1.decompressed.apex", 3326 GetDecompressionDir().c_str()); 3327 3328 // Mount an apex from some other directory 3329 TemporaryDir td; 3330 AddPreInstalledApex("apex.apexd_test_different_app.apex"); 3331 fs::copy(GetTestFile("apex.apexd_test_different_app.apex"), td.path); 3332 std::string other_apex = 3333 StringPrintf("%s/apex.apexd_test_different_app.apex", td.path); 3334 3335 auto& instance = ApexFileRepository::GetInstance(); 3336 ASSERT_RESULT_OK(instance.AddPreInstalledApex({GetBuiltInDir()})); 3337 3338 ASSERT_TRUE(IsOk(ActivatePackage(apex_path))); 3339 ASSERT_TRUE(IsOk(ActivatePackage(decompressed_apex))); 3340 ASSERT_TRUE(IsOk(ActivatePackage(other_apex))); 3341 3342 auto& db = GetApexDatabaseForTesting(); 3343 // Remember mount information for |other_apex|, since it won't be available in 3344 // the database. We will need to tear it down manually. 3345 std::optional<MountedApexData> other_apex_mount_data; 3346 db.ForallMountedApexes( 3347 "com.android.apex.test_package_2", 3348 [&other_apex_mount_data](const MountedApexData& data, bool latest) { 3349 if (latest) { 3350 other_apex_mount_data.emplace(data); 3351 } 3352 }); 3353 UnmountOnTearDown(apex_path); 3354 UnmountOnTearDown(decompressed_apex); 3355 ASSERT_TRUE(other_apex_mount_data.has_value()); 3356 auto deleter = make_scope_guard([&other_apex_mount_data]() { 3357 if (!other_apex_mount_data.has_value()) { 3358 return; 3359 } 3360 if (umount2("/apex/com.android.apex.test_package_2", 0) != 0) { 3361 PLOG(ERROR) << "Failed to unmount /apex/com.android.apex.test_package_2"; 3362 } 3363 auto res = Unmount(*other_apex_mount_data, /* deferred= */ false); 3364 if (!res.ok()) { 3365 LOG(ERROR) << res.error(); 3366 } 3367 }); 3368 3369 auto apex_mounts = GetApexMounts(); 3370 ASSERT_THAT(apex_mounts, 3371 UnorderedElementsAre("/apex/com.android.apex.test_package", 3372 "/apex/com.android.apex.test_package@2", 3373 "/apex/com.android.apex.compressed", 3374 "/apex/com.android.apex.compressed@1", 3375 "/apex/com.android.apex.test_package_2", 3376 "/apex/com.android.apex.test_package_2@1")); 3377 3378 // Clear the database before calling PopulateFromMounts 3379 db.Reset(); 3380 3381 // Populate from mount 3382 db.PopulateFromMounts(GetDataDir(), GetDecompressionDir(), GetHashTreeDir()); 3383 3384 // Count number of package and collect package names 3385 int package_count = 0; 3386 std::vector<std::string> mounted_paths; 3387 db.ForallMountedApexes([&](const std::string& package, 3388 const MountedApexData& data, bool latest) { 3389 package_count++; 3390 mounted_paths.push_back(data.full_path); 3391 }); 3392 ASSERT_EQ(package_count, 2); 3393 ASSERT_THAT(mounted_paths, 3394 UnorderedElementsAre(apex_path, decompressed_apex)); 3395 } 3396 3397 TEST_F(ApexdMountTest, UnmountAll) { 3398 AddPreInstalledApex("apex.apexd_test.apex"); 3399 std::string apex_path_2 = 3400 AddPreInstalledApex("apex.apexd_test_different_app.apex"); 3401 std::string apex_path_3 = AddDataApex("apex.apexd_test_v2.apex"); 3402 3403 // Mount an apex from decomrpession_dir 3404 PrepareCompressedApex("com.android.apex.compressed.v1.capex"); 3405 std::string decompressed_apex = 3406 StringPrintf("%s/com.android.apex.compressed@1.decompressed.apex", 3407 GetDecompressionDir().c_str()); 3408 3409 auto& instance = ApexFileRepository::GetInstance(); 3410 ASSERT_RESULT_OK(instance.AddPreInstalledApex({GetBuiltInDir()})); 3411 3412 ASSERT_TRUE(IsOk(ActivatePackage(apex_path_2))); 3413 ASSERT_TRUE(IsOk(ActivatePackage(apex_path_3))); 3414 ASSERT_TRUE(IsOk(ActivatePackage(decompressed_apex))); 3415 UnmountOnTearDown(apex_path_2); 3416 UnmountOnTearDown(apex_path_3); 3417 UnmountOnTearDown(decompressed_apex); 3418 3419 auto apex_mounts = GetApexMounts(); 3420 ASSERT_THAT(apex_mounts, 3421 UnorderedElementsAre("/apex/com.android.apex.test_package", 3422 "/apex/com.android.apex.test_package@2", 3423 "/apex/com.android.apex.compressed", 3424 "/apex/com.android.apex.compressed@1", 3425 "/apex/com.android.apex.test_package_2", 3426 "/apex/com.android.apex.test_package_2@1")); 3427 3428 auto& db = GetApexDatabaseForTesting(); 3429 // UnmountAll expects apex database to empty, hence this reset. 3430 db.Reset(); 3431 3432 ASSERT_EQ(0, UnmountAll()); 3433 3434 auto new_apex_mounts = GetApexMounts(); 3435 ASSERT_EQ(new_apex_mounts.size(), 0u); 3436 } 3437 3438 TEST_F(ApexdMountTest, UnmountAllSharedLibsApex) { 3439 ASSERT_EQ(mkdir("/apex/sharedlibs", 0755), 0); 3440 ASSERT_EQ(mkdir("/apex/sharedlibs/lib", 0755), 0); 3441 ASSERT_EQ(mkdir("/apex/sharedlibs/lib64", 0755), 0); 3442 auto deleter = make_scope_guard([]() { 3443 std::error_code ec; 3444 fs::remove_all("/apex/sharedlibs", ec); 3445 if (ec) { 3446 LOG(ERROR) << "Failed to delete /apex/sharedlibs : " << ec; 3447 } 3448 }); 3449 3450 std::string apex_path_1 = AddPreInstalledApex( 3451 "com.android.apex.test.sharedlibs_generated.v1.libvX.apex"); 3452 std::string apex_path_2 = 3453 AddDataApex("com.android.apex.test.sharedlibs_generated.v2.libvY.apex"); 3454 3455 auto& instance = ApexFileRepository::GetInstance(); 3456 ASSERT_RESULT_OK(instance.AddPreInstalledApex({GetBuiltInDir()})); 3457 3458 ASSERT_TRUE(IsOk(ActivatePackage(apex_path_1))); 3459 ASSERT_TRUE(IsOk(ActivatePackage(apex_path_2))); 3460 UnmountOnTearDown(apex_path_1); 3461 UnmountOnTearDown(apex_path_2); 3462 3463 auto apex_mounts = GetApexMounts(); 3464 ASSERT_THAT(apex_mounts, 3465 UnorderedElementsAre("/apex/com.android.apex.test.sharedlibs@1", 3466 "/apex/com.android.apex.test.sharedlibs@2")); 3467 3468 auto& db = GetApexDatabaseForTesting(); 3469 // UnmountAll expects apex database to empty, hence this reset. 3470 db.Reset(); 3471 3472 ASSERT_EQ(0, UnmountAll()); 3473 3474 auto new_apex_mounts = GetApexMounts(); 3475 ASSERT_EQ(new_apex_mounts.size(), 0u); 3476 } 3477 3478 class ApexActivationFailureTests : public ApexdMountTest {}; 3479 3480 TEST_F(ApexActivationFailureTests, BuildFingerprintDifferent) { 3481 auto apex_session = CreateStagedSession("apex.apexd_test.apex", 123); 3482 apex_session->SetBuildFingerprint("wrong fingerprint"); 3483 apex_session->UpdateStateAndCommit(SessionState::STAGED); 3484 3485 OnStart(); 3486 3487 apex_session = ApexSession::GetSession(123); 3488 ASSERT_THAT(apex_session->GetErrorMessage(), 3489 HasSubstr("APEX build fingerprint has changed")); 3490 } 3491 3492 TEST_F(ApexActivationFailureTests, ApexFileMissingInStagingDirectory) { 3493 auto apex_session = CreateStagedSession("apex.apexd_test.apex", 123); 3494 apex_session->UpdateStateAndCommit(SessionState::STAGED); 3495 // Delete the apex file in staging directory 3496 DeleteDirContent(GetStagedDir(123)); 3497 3498 OnStart(); 3499 3500 apex_session = ApexSession::GetSession(123); 3501 ASSERT_THAT(apex_session->GetErrorMessage(), 3502 HasSubstr("No APEX packages found")); 3503 } 3504 3505 TEST_F(ApexActivationFailureTests, MultipleApexFileInStagingDirectory) { 3506 auto apex_session = CreateStagedSession("apex.apexd_test.apex", 123); 3507 CreateStagedSession("com.android.apex.compressed.v1_original.apex", 123); 3508 apex_session->UpdateStateAndCommit(SessionState::STAGED); 3509 3510 OnStart(); 3511 3512 apex_session = ApexSession::GetSession(123); 3513 ASSERT_THAT(apex_session->GetErrorMessage(), 3514 HasSubstr("More than one APEX package found")); 3515 } 3516 3517 TEST_F(ApexActivationFailureTests, PostInstallFailsForApex) { 3518 auto apex_session = 3519 CreateStagedSession("apex.apexd_test_corrupt_superblock_apex.apex", 123); 3520 apex_session->UpdateStateAndCommit(SessionState::STAGED); 3521 3522 OnStart(); 3523 3524 apex_session = ApexSession::GetSession(123); 3525 ASSERT_THAT(apex_session->GetErrorMessage(), 3526 HasSubstr("Postinstall failed for session")); 3527 } 3528 3529 TEST_F(ApexActivationFailureTests, CorruptedApexCannotBeStaged) { 3530 auto apex_session = CreateStagedSession("corrupted_b146895998.apex", 123); 3531 apex_session->UpdateStateAndCommit(SessionState::STAGED); 3532 3533 OnStart(); 3534 3535 apex_session = ApexSession::GetSession(123); 3536 ASSERT_THAT(apex_session->GetErrorMessage(), 3537 HasSubstr("Activation failed for packages")); 3538 } 3539 3540 TEST_F(ApexActivationFailureTests, ActivatePackageImplFails) { 3541 auto shim_path = AddPreInstalledApex("com.android.apex.cts.shim.apex"); 3542 auto& instance = ApexFileRepository::GetInstance(); 3543 ASSERT_RESULT_OK(instance.AddPreInstalledApex({GetBuiltInDir()})); 3544 3545 auto apex_session = 3546 CreateStagedSession("com.android.apex.cts.shim.v2_wrong_sha.apex", 123); 3547 apex_session->UpdateStateAndCommit(SessionState::STAGED); 3548 3549 UnmountOnTearDown(shim_path); 3550 OnStart(); 3551 3552 apex_session = ApexSession::GetSession(123); 3553 ASSERT_THAT(apex_session->GetErrorMessage(), 3554 HasSubstr("Failed to activate packages")); 3555 ASSERT_THAT(apex_session->GetErrorMessage(), 3556 HasSubstr("has unexpected SHA512 hash")); 3557 } 3558 3559 TEST_F(ApexActivationFailureTests, 3560 StagedSessionFailsWhenNotInFsCheckpointMode) { 3561 MockCheckpointInterface checkpoint_interface; 3562 checkpoint_interface.SetSupportsCheckpoint(true); 3563 // Need to call InitializeVold before calling OnStart 3564 InitializeVold(&checkpoint_interface); 3565 3566 auto pre_installed_apex = AddPreInstalledApex("apex.apexd_test.apex"); 3567 auto& instance = ApexFileRepository::GetInstance(); 3568 ASSERT_RESULT_OK(instance.AddPreInstalledApex({GetBuiltInDir()})); 3569 3570 auto apex_session = CreateStagedSession("apex.apexd_test.apex", 123); 3571 apex_session->UpdateStateAndCommit(SessionState::STAGED); 3572 3573 UnmountOnTearDown(pre_installed_apex); 3574 OnStart(); 3575 3576 apex_session = ApexSession::GetSession(123); 3577 ASSERT_EQ(apex_session->GetState(), SessionState::ACTIVATION_FAILED); 3578 ASSERT_THAT( 3579 apex_session->GetErrorMessage(), 3580 HasSubstr("Cannot install apex session if not in fs-checkpoint mode")); 3581 } 3582 3583 TEST_F(ApexActivationFailureTests, StagedSessionRevertsWhenInFsRollbackMode) { 3584 MockCheckpointInterface checkpoint_interface; 3585 checkpoint_interface.SetSupportsCheckpoint(true); 3586 checkpoint_interface.SetNeedsRollback(true); 3587 // Need to call InitializeVold before calling OnStart 3588 InitializeVold(&checkpoint_interface); 3589 3590 auto pre_installed_apex = AddPreInstalledApex("apex.apexd_test.apex"); 3591 auto& instance = ApexFileRepository::GetInstance(); 3592 ASSERT_RESULT_OK(instance.AddPreInstalledApex({GetBuiltInDir()})); 3593 3594 auto apex_session = CreateStagedSession("apex.apexd_test.apex", 123); 3595 apex_session->UpdateStateAndCommit(SessionState::STAGED); 3596 3597 UnmountOnTearDown(pre_installed_apex); 3598 OnStart(); 3599 3600 apex_session = ApexSession::GetSession(123); 3601 ASSERT_EQ(apex_session->GetState(), SessionState::REVERTED); 3602 } 3603 3604 TEST_F(ApexdMountTest, OnBootstrapCreatesEmptyDmDevices) { 3605 AddPreInstalledApex("apex.apexd_test.apex"); 3606 AddPreInstalledApex("com.android.apex.compressed.v1.capex"); 3607 3608 DeviceMapper& dm = DeviceMapper::Instance(); 3609 3610 auto cleaner = make_scope_guard([&]() { 3611 dm.DeleteDevice("com.android.apex.test_package", 1s); 3612 dm.DeleteDevice("com.android.apex.compressed", 1s); 3613 }); 3614 3615 ASSERT_EQ(0, OnBootstrap()); 3616 3617 std::string ignored; 3618 ASSERT_TRUE(dm.WaitForDevice("com.android.apex.test_package", 1s, &ignored)); 3619 ASSERT_TRUE(dm.WaitForDevice("com.android.apex.compressed", 1s, &ignored)); 3620 } 3621 3622 } // namespace apex 3623 } // namespace android 3624