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