• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <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