• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "apexd.h"
18 
19 #include <android-base/file.h>
20 #include <android-base/properties.h>
21 #include <android-base/result-gmock.h>
22 #include <android-base/scopeguard.h>
23 #include <android-base/stringprintf.h>
24 #include <gmock/gmock.h>
25 #include <gtest/gtest.h>
26 #include <libdm/dm.h>
27 #include <microdroid/metadata.h>
28 #include <selinux/selinux.h>
29 #include <sys/stat.h>
30 
31 #include <functional>
32 #include <optional>
33 #include <string>
34 #include <tuple>
35 #include <unordered_set>
36 #include <vector>
37 
38 #include "apex_database.h"
39 #include "apex_file.h"
40 #include "apex_file_repository.h"
41 #include "apex_manifest.pb.h"
42 #include "apexd_checkpoint.h"
43 #include "apexd_loop.h"
44 #include "apexd_session.h"
45 #include "apexd_test_utils.h"
46 #include "apexd_utils.h"
47 #include "com_android_apex.h"
48 #include "gmock/gmock-matchers.h"
49 
50 namespace android {
51 namespace apex {
52 
53 namespace fs = std::filesystem;
54 
55 using MountedApexData = MountedApexDatabase::MountedApexData;
56 using android::apex::testing::ApexFileEq;
57 using android::base::GetExecutableDirectory;
58 using android::base::GetProperty;
59 using android::base::Join;
60 using android::base::make_scope_guard;
61 using android::base::ReadFileToString;
62 using android::base::ReadFully;
63 using android::base::RemoveFileIfExists;
64 using android::base::Result;
65 using android::base::Split;
66 using android::base::StringPrintf;
67 using android::base::unique_fd;
68 using android::base::WriteStringToFile;
69 using android::base::testing::HasError;
70 using android::base::testing::HasValue;
71 using android::base::testing::Ok;
72 using android::base::testing::WithMessage;
73 using android::dm::DeviceMapper;
74 using ::apex::proto::SessionState;
75 using com::android::apex::testing::ApexInfoXmlEq;
76 using ::testing::ByRef;
77 using ::testing::Contains;
78 using ::testing::ElementsAre;
79 using ::testing::HasSubstr;
80 using ::testing::IsEmpty;
81 using ::testing::Not;
82 using ::testing::StartsWith;
83 using ::testing::UnorderedElementsAre;
84 using ::testing::UnorderedElementsAreArray;
85 using ::testing::internal::CaptureStderr;
86 using ::testing::internal::GetCapturedStderr;
87 
GetTestDataDir()88 static std::string GetTestDataDir() { return GetExecutableDirectory(); }
GetTestFile(const std::string & name)89 static std::string GetTestFile(const std::string& name) {
90   return GetTestDataDir() + "/" + name;
91 }
92 
GetMTime(const std::string & path)93 static int64_t GetMTime(const std::string& path) {
94   struct stat st_buf;
95   if (stat(path.c_str(), &st_buf) != 0) {
96     PLOG(ERROR) << "Failed to stat " << path;
97     return 0;
98   }
99   return st_buf.st_mtime;
100 }
101 
102 // A very basic mock of CheckpointInterface.
103 class MockCheckpointInterface : public CheckpointInterface {
104  public:
SupportsFsCheckpoints()105   Result<bool> SupportsFsCheckpoints() override {
106     return supports_fs_checkpoint_;
107   }
108 
NeedsCheckpoint()109   Result<bool> NeedsCheckpoint() override { return needs_checkpoint_; }
110 
NeedsRollback()111   Result<bool> NeedsRollback() override { return needs_rollback_; }
112 
StartCheckpoint(int32_t num_retries)113   Result<void> StartCheckpoint(int32_t num_retries) override { return {}; }
114 
AbortChanges(const std::string & msg,bool retry)115   Result<void> AbortChanges(const std::string& msg, bool retry) override {
116     return {};
117   }
118 
SetSupportsCheckpoint(bool value)119   void SetSupportsCheckpoint(bool value) { supports_fs_checkpoint_ = value; }
120 
SetNeedsCheckpoint(bool value)121   void SetNeedsCheckpoint(bool value) { needs_checkpoint_ = value; }
122 
SetNeedsRollback(bool value)123   void SetNeedsRollback(bool value) { needs_rollback_ = value; }
124 
125  private:
126   bool supports_fs_checkpoint_, needs_checkpoint_, needs_rollback_;
127 };
128 
129 static constexpr const char* kTestApexdStatusSysprop = "apexd.status.test";
130 static constexpr const char* kTestVmPayloadMetadataPartitionProp =
131     "apexd.vm.payload_metadata_partition.test";
132 
133 static constexpr const char* kTestActiveApexSelinuxCtx =
134     "u:object_r:shell_data_file:s0";
135 
136 // A test fixture that provides frequently required temp directories for tests
137 class ApexdUnitTest : public ::testing::Test {
138  public:
ApexdUnitTest()139   ApexdUnitTest() {
140     built_in_dir_ = StringPrintf("%s/pre-installed-apex", td_.path);
141     data_dir_ = StringPrintf("%s/data-apex", td_.path);
142     decompression_dir_ = StringPrintf("%s/decompressed-apex", td_.path);
143     ota_reserved_dir_ = StringPrintf("%s/ota-reserved", td_.path);
144     hash_tree_dir_ = StringPrintf("%s/apex-hash-tree", td_.path);
145     staged_session_dir_ = StringPrintf("%s/staged-session-dir", td_.path);
146     metadata_sepolicy_staged_dir_ =
147         StringPrintf("%s/metadata-sepolicy-staged-dir", td_.path);
148 
149     vm_payload_disk_ = StringPrintf("%s/vm-payload", td_.path);
150 
151     config_ = {kTestApexdStatusSysprop,
152                {built_in_dir_},
153                data_dir_.c_str(),
154                decompression_dir_.c_str(),
155                ota_reserved_dir_.c_str(),
156                hash_tree_dir_.c_str(),
157                staged_session_dir_.c_str(),
158                metadata_sepolicy_staged_dir_.c_str(),
159                kTestVmPayloadMetadataPartitionProp,
160                kTestActiveApexSelinuxCtx};
161   }
162 
GetBuiltInDir()163   const std::string& GetBuiltInDir() { return built_in_dir_; }
GetDataDir()164   const std::string& GetDataDir() { return data_dir_; }
GetDecompressionDir()165   const std::string& GetDecompressionDir() { return decompression_dir_; }
GetOtaReservedDir()166   const std::string& GetOtaReservedDir() { return ota_reserved_dir_; }
GetHashTreeDir()167   const std::string& GetHashTreeDir() { return hash_tree_dir_; }
GetStagedDir(int session_id)168   const std::string GetStagedDir(int session_id) {
169     return StringPrintf("%s/session_%d", staged_session_dir_.c_str(),
170                         session_id);
171   }
GetMetadataSepolicyStagedDir()172   const std::string& GetMetadataSepolicyStagedDir() {
173     return metadata_sepolicy_staged_dir_;
174   }
175 
GetRootDigest(const ApexFile & apex)176   std::string GetRootDigest(const ApexFile& apex) {
177     if (apex.IsCompressed()) {
178       return "";
179     }
180     auto digest = apex.VerifyApexVerity(apex.GetBundledPublicKey());
181     if (!digest.ok()) {
182       return "";
183     }
184     return digest->root_digest;
185   }
186 
AddPreInstalledApex(const std::string & apex_name)187   std::string AddPreInstalledApex(const std::string& apex_name) {
188     fs::copy(GetTestFile(apex_name), built_in_dir_);
189     return StringPrintf("%s/%s", built_in_dir_.c_str(), apex_name.c_str());
190   }
191 
AddDataApex(const std::string & apex_name)192   std::string AddDataApex(const std::string& apex_name) {
193     fs::copy(GetTestFile(apex_name), data_dir_);
194     return StringPrintf("%s/%s", data_dir_.c_str(), apex_name.c_str());
195   }
196 
AddDataApex(const std::string & apex_name,const std::string & target_name)197   std::string AddDataApex(const std::string& apex_name,
198                           const std::string& target_name) {
199     fs::copy(GetTestFile(apex_name), data_dir_ + "/" + target_name);
200     return StringPrintf("%s/%s", data_dir_.c_str(), target_name.c_str());
201   }
202 
AddDecompressedApex(const std::string & apex_name)203   std::string AddDecompressedApex(const std::string& apex_name) {
204     auto apex_file = ApexFile::Open(GetTestFile(apex_name));
205     CHECK(apex_file.ok());
206     std::string target_name =
207         apex_file->GetManifest().name() + "@" +
208         std::to_string(apex_file->GetManifest().version()) +
209         std::string(kDecompressedApexPackageSuffix);
210     fs::copy(GetTestFile(apex_name), decompression_dir_ + "/" + target_name);
211     return StringPrintf("%s/%s", decompression_dir_.c_str(),
212                         target_name.c_str());
213   }
214 
AddBlockApex(const std::string & apex_name,const std::string & public_key="",const std::string & root_digest="",bool is_factory=true)215   std::string AddBlockApex(const std::string& apex_name,
216                            const std::string& public_key = "",
217                            const std::string& root_digest = "",
218                            bool is_factory = true) {
219     auto apex_path = vm_payload_disk_ + std::to_string(block_device_index_++);
220     auto apex_file = GetTestFile(apex_name);
221     AddToMetadata(apex_name, public_key, root_digest, is_factory);
222     // loop_devices_ will be disposed after each test
223     loop_devices_.push_back(*WriteBlockApex(apex_file, apex_path));
224     return apex_path;
225   }
226 
227   // Copies the compressed apex to |built_in_dir| and decompresses it to
228   // |decompressed_dir| and then hard links to |target_dir|
PrepareCompressedApex(const std::string & name,const std::string & built_in_dir)229   std::string PrepareCompressedApex(const std::string& name,
230                                     const std::string& built_in_dir) {
231     fs::copy(GetTestFile(name), built_in_dir);
232     auto compressed_apex = ApexFile::Open(
233         StringPrintf("%s/%s", built_in_dir.c_str(), name.c_str()));
234     std::vector<ApexFileRef> compressed_apex_list;
235     compressed_apex_list.emplace_back(std::cref(*compressed_apex));
236     auto return_value =
237         ProcessCompressedApex(compressed_apex_list, /*is_ota_chroot*/ false);
238     return StringPrintf("%s/%s", built_in_dir.c_str(), name.c_str());
239   }
240 
PrepareCompressedApex(const std::string & name)241   std::string PrepareCompressedApex(const std::string& name) {
242     return PrepareCompressedApex(name, built_in_dir_);
243   }
244 
CreateStagedSession(const std::string & apex_name,int session_id)245   Result<ApexSession> CreateStagedSession(const std::string& apex_name,
246                                           int session_id) {
247     CreateDirIfNeeded(GetStagedDir(session_id), 0755);
248     fs::copy(GetTestFile(apex_name), GetStagedDir(session_id));
249     auto result = ApexSession::CreateSession(session_id);
250     result->SetBuildFingerprint(GetProperty("ro.build.fingerprint", ""));
251     return result;
252   }
253 
SetBlockApexEnabled(bool enabled)254   void SetBlockApexEnabled(bool enabled) {
255     // The first partition(1) is "metadata" partition
256     base::SetProperty(kTestVmPayloadMetadataPartitionProp,
257                       enabled ? (vm_payload_disk_ + "1") : "");
258   }
259 
260  protected:
SetUp()261   void SetUp() override {
262     SetConfig(config_);
263     ApexFileRepository::GetInstance().Reset(decompression_dir_);
264     ASSERT_EQ(mkdir(built_in_dir_.c_str(), 0755), 0);
265     ASSERT_EQ(mkdir(data_dir_.c_str(), 0755), 0);
266     ASSERT_EQ(mkdir(decompression_dir_.c_str(), 0755), 0);
267     ASSERT_EQ(mkdir(ota_reserved_dir_.c_str(), 0755), 0);
268     ASSERT_EQ(mkdir(hash_tree_dir_.c_str(), 0755), 0);
269     ASSERT_EQ(mkdir(staged_session_dir_.c_str(), 0755), 0);
270     ASSERT_EQ(mkdir(metadata_sepolicy_staged_dir_.c_str(), 0755), 0);
271 
272     DeleteDirContent(ApexSession::GetSessionsDir());
273   }
AddToMetadata(const std::string & apex_name,const std::string & public_key,const std::string & root_digest,bool is_factory)274   void AddToMetadata(const std::string& apex_name,
275                      const std::string& public_key,
276                      const std::string& root_digest, bool is_factory) {
277     android::microdroid::Metadata metadata;
278     // The first partition is metadata partition
279     auto metadata_partition = vm_payload_disk_ + "1";
280     if (access(metadata_partition.c_str(), F_OK) == 0) {
281       metadata = *android::microdroid::ReadMetadata(metadata_partition);
282     }
283 
284     auto apex = metadata.add_apexes();
285     apex->set_name(apex_name);
286     apex->set_public_key(public_key);
287     apex->set_root_digest(root_digest);
288     apex->set_is_factory(is_factory);
289 
290     std::ofstream out(metadata_partition);
291     android::microdroid::WriteMetadata(metadata, out);
292   }
293 
TearDown()294   void TearDown() override {
295     DeleteDirContent(ApexSession::GetSessionsDir());
296     SetBlockApexEnabled(false);
297   }
298 
299  private:
300   TemporaryDir td_;
301   std::string built_in_dir_;
302   std::string data_dir_;
303   std::string decompression_dir_;
304   std::string ota_reserved_dir_;
305   std::string hash_tree_dir_;
306   std::string vm_payload_disk_;
307   std::string vm_payload_metadata_path_;
308   std::string staged_session_dir_;
309   std::string metadata_sepolicy_staged_dir_;
310   ApexdConfig config_;
311   std::vector<loop::LoopbackDeviceUniqueFd> loop_devices_;  // to be cleaned up
312   int block_device_index_ = 2;  // "1" is reserved for metadata;
313 };
314 
315 // Apex that does not have pre-installed version, does not get selected
TEST_F(ApexdUnitTest,ApexMustHavePreInstalledVersionForSelection)316 TEST_F(ApexdUnitTest, ApexMustHavePreInstalledVersionForSelection) {
317   AddPreInstalledApex("apex.apexd_test.apex");
318   AddPreInstalledApex("com.android.apex.cts.shim.apex");
319   auto shared_lib_1 = ApexFile::Open(AddPreInstalledApex(
320       "com.android.apex.test.sharedlibs_generated.v1.libvX.apex"));
321   auto& instance = ApexFileRepository::GetInstance();
322   // Pre-installed data needs to be present so that we can add data apex
323   ASSERT_THAT(instance.AddPreInstalledApex({GetBuiltInDir()}), Ok());
324 
325   auto apexd_test_file = ApexFile::Open(AddDataApex("apex.apexd_test.apex"));
326   auto shim_v1 = ApexFile::Open(AddDataApex("com.android.apex.cts.shim.apex"));
327   // Normally both pre-installed and data apex would be activated for a shared
328   // libs apex, but if they are the same version only the data apex will be.
329   auto shared_lib_2 = ApexFile::Open(
330       AddDataApex("com.android.apex.test.sharedlibs_generated.v1.libvX.apex"));
331   ASSERT_THAT(instance.AddDataApex(GetDataDir()), Ok());
332 
333   const auto all_apex = instance.AllApexFilesByName();
334   // Pass a blank instance so that the data apex files are not considered
335   // pre-installed
336   const ApexFileRepository instance_blank;
337   auto result = SelectApexForActivation(all_apex, instance_blank);
338   ASSERT_EQ(result.size(), 0u);
339   // When passed proper instance they should get selected
340   result = SelectApexForActivation(all_apex, instance);
341   ASSERT_EQ(result.size(), 3u);
342   ASSERT_THAT(result, UnorderedElementsAre(ApexFileEq(ByRef(*apexd_test_file)),
343                                            ApexFileEq(ByRef(*shim_v1)),
344                                            ApexFileEq(ByRef(*shared_lib_2))));
345 }
346 
347 // Higher version gets priority when selecting for activation
TEST_F(ApexdUnitTest,HigherVersionOfApexIsSelected)348 TEST_F(ApexdUnitTest, HigherVersionOfApexIsSelected) {
349   auto apexd_test_file_v2 =
350       ApexFile::Open(AddPreInstalledApex("apex.apexd_test_v2.apex"));
351   AddPreInstalledApex("com.android.apex.cts.shim.apex");
352   auto& instance = ApexFileRepository::GetInstance();
353   ASSERT_THAT(instance.AddPreInstalledApex({GetBuiltInDir()}), Ok());
354 
355   TemporaryDir data_dir;
356   AddDataApex("apex.apexd_test.apex");
357   auto shim_v2 =
358       ApexFile::Open(AddDataApex("com.android.apex.cts.shim.v2.apex"));
359   ASSERT_THAT(instance.AddDataApex(GetDataDir()), Ok());
360 
361   auto all_apex = instance.AllApexFilesByName();
362   auto result = SelectApexForActivation(all_apex, instance);
363   ASSERT_EQ(result.size(), 2u);
364 
365   ASSERT_THAT(result,
366               UnorderedElementsAre(ApexFileEq(ByRef(*apexd_test_file_v2)),
367                                    ApexFileEq(ByRef(*shim_v2))));
368 }
369 
370 // When versions are equal, non-pre-installed version gets priority
TEST_F(ApexdUnitTest,DataApexGetsPriorityForSameVersions)371 TEST_F(ApexdUnitTest, DataApexGetsPriorityForSameVersions) {
372   AddPreInstalledApex("apex.apexd_test.apex");
373   AddPreInstalledApex("com.android.apex.cts.shim.apex");
374   // Initialize pre-installed APEX information
375   auto& instance = ApexFileRepository::GetInstance();
376   ASSERT_THAT(instance.AddPreInstalledApex({GetBuiltInDir()}), Ok());
377 
378   auto apexd_test_file = ApexFile::Open(AddDataApex("apex.apexd_test.apex"));
379   auto shim_v1 = ApexFile::Open(AddDataApex("com.android.apex.cts.shim.apex"));
380   // Initialize ApexFile repo
381   ASSERT_THAT(instance.AddDataApex(GetDataDir()), Ok());
382 
383   auto all_apex = instance.AllApexFilesByName();
384   auto result = SelectApexForActivation(all_apex, instance);
385   ASSERT_EQ(result.size(), 2u);
386 
387   ASSERT_THAT(result, UnorderedElementsAre(ApexFileEq(ByRef(*apexd_test_file)),
388                                            ApexFileEq(ByRef(*shim_v1))));
389 }
390 
391 // Both versions of shared libs can be selected when preinstalled version is
392 // lower than data version
TEST_F(ApexdUnitTest,SharedLibsCanHaveBothVersionSelected)393 TEST_F(ApexdUnitTest, SharedLibsCanHaveBothVersionSelected) {
394   auto shared_lib_v1 = ApexFile::Open(AddPreInstalledApex(
395       "com.android.apex.test.sharedlibs_generated.v1.libvX.apex"));
396   // Initialize pre-installed APEX information
397   auto& instance = ApexFileRepository::GetInstance();
398   ASSERT_THAT(instance.AddPreInstalledApex({GetBuiltInDir()}), Ok());
399 
400   auto shared_lib_v2 = ApexFile::Open(
401       AddDataApex("com.android.apex.test.sharedlibs_generated.v2.libvY.apex"));
402   // Initialize data APEX information
403   ASSERT_THAT(instance.AddDataApex(GetDataDir()), Ok());
404 
405   auto all_apex = instance.AllApexFilesByName();
406   auto result = SelectApexForActivation(all_apex, instance);
407   ASSERT_EQ(result.size(), 2u);
408 
409   ASSERT_THAT(result, UnorderedElementsAre(ApexFileEq(ByRef(*shared_lib_v1)),
410                                            ApexFileEq(ByRef(*shared_lib_v2))));
411 }
412 
413 // Data version of shared libs should not be selected if lower than
414 // preinstalled version
TEST_F(ApexdUnitTest,SharedLibsDataVersionDeletedIfLower)415 TEST_F(ApexdUnitTest, SharedLibsDataVersionDeletedIfLower) {
416   auto shared_lib_v2 = ApexFile::Open(AddPreInstalledApex(
417       "com.android.apex.test.sharedlibs_generated.v2.libvY.apex"));
418   // Initialize pre-installed APEX information
419   auto& instance = ApexFileRepository::GetInstance();
420   ASSERT_THAT(instance.AddPreInstalledApex({GetBuiltInDir()}), Ok());
421 
422   auto shared_lib_v1 = ApexFile::Open(
423       AddDataApex("com.android.apex.test.sharedlibs_generated.v1.libvX.apex"));
424   // Initialize data APEX information
425   ASSERT_THAT(instance.AddDataApex(GetDataDir()), Ok());
426 
427   auto all_apex = instance.AllApexFilesByName();
428   auto result = SelectApexForActivation(all_apex, instance);
429   ASSERT_EQ(result.size(), 1u);
430 
431   ASSERT_THAT(result, UnorderedElementsAre(ApexFileEq(ByRef(*shared_lib_v2))));
432 }
433 
TEST_F(ApexdUnitTest,ProcessCompressedApex)434 TEST_F(ApexdUnitTest, ProcessCompressedApex) {
435   auto compressed_apex = ApexFile::Open(
436       AddPreInstalledApex("com.android.apex.compressed.v1.capex"));
437 
438   std::vector<ApexFileRef> compressed_apex_list;
439   compressed_apex_list.emplace_back(std::cref(*compressed_apex));
440   auto return_value =
441       ProcessCompressedApex(compressed_apex_list, /* is_ota_chroot= */ false);
442 
443   std::string decompressed_file_path = StringPrintf(
444       "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(),
445       kDecompressedApexPackageSuffix);
446   // Assert output path is not empty
447   auto exists = PathExists(decompressed_file_path);
448   ASSERT_THAT(exists, HasValue(true));
449 
450   // Assert that return value contains decompressed APEX
451   auto decompressed_apex = ApexFile::Open(decompressed_file_path);
452   ASSERT_THAT(return_value,
453               UnorderedElementsAre(ApexFileEq(ByRef(*decompressed_apex))));
454 }
455 
TEST_F(ApexdUnitTest,ProcessCompressedApexRunsVerification)456 TEST_F(ApexdUnitTest, ProcessCompressedApexRunsVerification) {
457   auto compressed_apex_mismatch_key = ApexFile::Open(AddPreInstalledApex(
458       "com.android.apex.compressed_key_mismatch_with_original.capex"));
459   auto compressed_apex_version_mismatch = ApexFile::Open(
460       AddPreInstalledApex("com.android.apex.compressed.v1_with_v2_apex.capex"));
461 
462   std::vector<ApexFileRef> compressed_apex_list;
463   compressed_apex_list.emplace_back(std::cref(*compressed_apex_mismatch_key));
464   compressed_apex_list.emplace_back(
465       std::cref(*compressed_apex_version_mismatch));
466   auto return_value =
467       ProcessCompressedApex(compressed_apex_list, /* is_ota_chroot= */ false);
468   ASSERT_EQ(return_value.size(), 0u);
469 }
470 
TEST_F(ApexdUnitTest,ValidateDecompressedApex)471 TEST_F(ApexdUnitTest, ValidateDecompressedApex) {
472   auto capex = ApexFile::Open(
473       AddPreInstalledApex("com.android.apex.compressed.v1.capex"));
474   auto decompressed_v1 = ApexFile::Open(
475       AddDataApex("com.android.apex.compressed.v1_original.apex"));
476 
477   auto result =
478       ValidateDecompressedApex(std::cref(*capex), std::cref(*decompressed_v1));
479   ASSERT_THAT(result, Ok());
480 
481   // Validation checks version
482   auto decompressed_v2 = ApexFile::Open(
483       AddDataApex("com.android.apex.compressed.v2_original.apex"));
484   result =
485       ValidateDecompressedApex(std::cref(*capex), std::cref(*decompressed_v2));
486   ASSERT_THAT(
487       result,
488       HasError(WithMessage(HasSubstr(
489           "Compressed APEX has different version than decompressed APEX"))));
490 
491   // Validation check root digest
492   auto decompressed_v1_different_digest = ApexFile::Open(AddDataApex(
493       "com.android.apex.compressed.v1_different_digest_original.apex"));
494   result = ValidateDecompressedApex(
495       std::cref(*capex), std::cref(*decompressed_v1_different_digest));
496   ASSERT_THAT(result, HasError(WithMessage(HasSubstr(
497                           "does not match with expected root digest"))));
498 
499   // Validation checks key
500   auto capex_different_key = ApexFile::Open(
501       AddDataApex("com.android.apex.compressed_different_key.capex"));
502   result = ValidateDecompressedApex(std::cref(*capex_different_key),
503                                     std::cref(*decompressed_v1));
504   ASSERT_THAT(
505       result,
506       HasError(WithMessage(HasSubstr(
507           "Public key of compressed APEX is different than original"))));
508 }
509 
TEST_F(ApexdUnitTest,ProcessCompressedApexCanBeCalledMultipleTimes)510 TEST_F(ApexdUnitTest, ProcessCompressedApexCanBeCalledMultipleTimes) {
511   auto compressed_apex = ApexFile::Open(
512       AddPreInstalledApex("com.android.apex.compressed.v1.capex"));
513 
514   std::vector<ApexFileRef> compressed_apex_list;
515   compressed_apex_list.emplace_back(std::cref(*compressed_apex));
516   auto return_value =
517       ProcessCompressedApex(compressed_apex_list, /* is_ota_chroot= */ false);
518   ASSERT_EQ(return_value.size(), 1u);
519 
520   // Capture the creation time of the decompressed APEX
521   std::error_code ec;
522   auto decompressed_apex_path = StringPrintf(
523       "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(),
524       kDecompressedApexPackageSuffix);
525   auto last_write_time_1 = fs::last_write_time(decompressed_apex_path, ec);
526   ASSERT_FALSE(ec) << "Failed to capture last write time of "
527                    << decompressed_apex_path;
528 
529   // Now try to decompress the same capex again. It should not fail.
530   return_value =
531       ProcessCompressedApex(compressed_apex_list, /* is_ota_chroot= */ false);
532   ASSERT_EQ(return_value.size(), 1u);
533 
534   // Ensure the decompressed APEX file did not change
535   auto last_write_time_2 = fs::last_write_time(decompressed_apex_path, ec);
536   ASSERT_FALSE(ec) << "Failed to capture last write time of "
537                    << decompressed_apex_path;
538   ASSERT_EQ(last_write_time_1, last_write_time_2);
539 }
540 
541 // Test behavior of ProcessCompressedApex when is_ota_chroot is true
TEST_F(ApexdUnitTest,ProcessCompressedApexOnOtaChroot)542 TEST_F(ApexdUnitTest, ProcessCompressedApexOnOtaChroot) {
543   auto compressed_apex = ApexFile::Open(
544       AddPreInstalledApex("com.android.apex.compressed.v1.capex"));
545 
546   std::vector<ApexFileRef> compressed_apex_list;
547   compressed_apex_list.emplace_back(std::cref(*compressed_apex));
548   auto return_value =
549       ProcessCompressedApex(compressed_apex_list, /* is_ota_chroot= */ true);
550   ASSERT_EQ(return_value.size(), 1u);
551 
552   // Decompressed APEX should be located in decompression_dir
553   std::string decompressed_file_path =
554       StringPrintf("%s/com.android.apex.compressed@1%s",
555                    GetDecompressionDir().c_str(), kOtaApexPackageSuffix);
556   // Assert output path is not empty
557   auto exists = PathExists(decompressed_file_path);
558   ASSERT_THAT(exists, HasValue(true))
559       << decompressed_file_path << " does not exist";
560 
561   // Assert that return value contains the decompressed APEX
562   auto apex_file = ApexFile::Open(decompressed_file_path);
563   ASSERT_THAT(return_value,
564               UnorderedElementsAre(ApexFileEq(ByRef(*apex_file))));
565 }
566 
567 // When decompressing APEX, reuse existing OTA APEX
TEST_F(ApexdUnitTest,ProcessCompressedApexReuseOtaApex)568 TEST_F(ApexdUnitTest, ProcessCompressedApexReuseOtaApex) {
569   // Push a compressed APEX that will fail to decompress
570   auto compressed_apex = ApexFile::Open(AddPreInstalledApex(
571       "com.android.apex.compressed.v1_not_decompressible.capex"));
572 
573   std::vector<ApexFileRef> compressed_apex_list;
574   compressed_apex_list.emplace_back(std::cref(*compressed_apex));
575 
576   // If we try to decompress capex directly, it should fail since the capex
577   // pushed is faulty and cannot be decompressed
578   auto return_value =
579       ProcessCompressedApex(compressed_apex_list, /* is_ota_chroot= */ false);
580   ASSERT_EQ(return_value.size(), 0u);
581 
582   // But, if there is an ota_apex present for reuse, it should reuse that
583   // and avoid decompressing the faulty capex
584 
585   // Push an OTA apex that should be reused to skip decompression
586   auto ota_apex_path =
587       StringPrintf("%s/com.android.apex.compressed@1%s",
588                    GetDecompressionDir().c_str(), kOtaApexPackageSuffix);
589   fs::copy(GetTestFile("com.android.apex.compressed.v1_original.apex"),
590            ota_apex_path);
591   return_value =
592       ProcessCompressedApex(compressed_apex_list, /* is_ota_chroot= */ false);
593   ASSERT_EQ(return_value.size(), 1u);
594 
595   // Ota Apex should be cleaned up
596   ASSERT_THAT(PathExists(ota_apex_path), HasValue(false));
597   ASSERT_EQ(return_value[0].GetPath(),
598             StringPrintf("%s/com.android.apex.compressed@1%s",
599                          GetDecompressionDir().c_str(),
600                          kDecompressedApexPackageSuffix));
601 }
602 
TEST_F(ApexdUnitTest,ShouldAllocateSpaceForDecompressionNewApex)603 TEST_F(ApexdUnitTest, ShouldAllocateSpaceForDecompressionNewApex) {
604   auto& instance = ApexFileRepository::GetInstance();
605   ASSERT_THAT(instance.AddPreInstalledApex({GetBuiltInDir()}), Ok());
606 
607   // A brand new compressed APEX is being introduced: selected
608   bool result =
609       ShouldAllocateSpaceForDecompression("com.android.brand.new", 1, instance);
610   ASSERT_TRUE(result);
611 }
612 
TEST_F(ApexdUnitTest,ShouldAllocateSpaceForDecompressionWasNotCompressedBefore)613 TEST_F(ApexdUnitTest,
614        ShouldAllocateSpaceForDecompressionWasNotCompressedBefore) {
615   // Prepare fake pre-installed apex
616   AddPreInstalledApex("apex.apexd_test.apex");
617   auto& instance = ApexFileRepository::GetInstance();
618   ASSERT_THAT(instance.AddPreInstalledApex({GetBuiltInDir()}), Ok());
619 
620   // An existing pre-installed APEX is now compressed in the OTA: selected
621   {
622     bool result = ShouldAllocateSpaceForDecompression(
623         "com.android.apex.test_package", 1, instance);
624     ASSERT_TRUE(result);
625   }
626 
627   // Even if there is a data apex (lower version)
628   // Include data apex within calculation now
629   AddDataApex("apex.apexd_test_v2.apex");
630   ASSERT_THAT(instance.AddDataApex(GetDataDir()), Ok());
631   {
632     bool result = ShouldAllocateSpaceForDecompression(
633         "com.android.apex.test_package", 3, instance);
634     ASSERT_TRUE(result);
635   }
636 
637   // But not if data apex has equal or higher version
638   {
639     bool result = ShouldAllocateSpaceForDecompression(
640         "com.android.apex.test_package", 2, instance);
641     ASSERT_FALSE(result);
642   }
643 }
644 
TEST_F(ApexdUnitTest,ShouldAllocateSpaceForDecompressionVersionCompare)645 TEST_F(ApexdUnitTest, ShouldAllocateSpaceForDecompressionVersionCompare) {
646   // Prepare fake pre-installed apex
647   PrepareCompressedApex("com.android.apex.compressed.v1.capex");
648   auto& instance = ApexFileRepository::GetInstance();
649   ASSERT_THAT(instance.AddPreInstalledApex({GetBuiltInDir()}), Ok());
650   ASSERT_THAT(instance.AddDataApex(GetDataDir()), Ok());
651 
652   {
653     // New Compressed apex has higher version than decompressed data apex:
654     // selected
655     bool result = ShouldAllocateSpaceForDecompression(
656         "com.android.apex.compressed", 2, instance);
657     ASSERT_TRUE(result)
658         << "Higher version test with decompressed data returned false";
659   }
660 
661   // Compare against decompressed data apex
662   {
663     // New Compressed apex has same version as decompressed data apex: not
664     // selected
665     bool result = ShouldAllocateSpaceForDecompression(
666         "com.android.apex.compressed", 1, instance);
667     ASSERT_FALSE(result)
668         << "Same version test with decompressed data returned true";
669   }
670 
671   {
672     // New Compressed apex has lower version than decompressed data apex:
673     // selected
674     bool result = ShouldAllocateSpaceForDecompression(
675         "com.android.apex.compressed", 0, instance);
676     ASSERT_TRUE(result)
677         << "lower version test with decompressed data returned false";
678   }
679 
680   // Replace decompressed data apex with a higher version
681   ApexFileRepository instance_new(GetDecompressionDir());
682   ASSERT_THAT(instance_new.AddPreInstalledApex({GetBuiltInDir()}), Ok());
683   TemporaryDir data_dir_new;
684   fs::copy(GetTestFile("com.android.apex.compressed.v2_original.apex"),
685            data_dir_new.path);
686   ASSERT_THAT(instance_new.AddDataApex(data_dir_new.path), Ok());
687 
688   {
689     // New Compressed apex has higher version as data apex: selected
690     bool result = ShouldAllocateSpaceForDecompression(
691         "com.android.apex.compressed", 3, instance_new);
692     ASSERT_TRUE(result) << "Higher version test with new data returned false";
693   }
694 
695   {
696     // New Compressed apex has same version as data apex: not selected
697     bool result = ShouldAllocateSpaceForDecompression(
698         "com.android.apex.compressed", 2, instance_new);
699     ASSERT_FALSE(result) << "Same version test with new data returned true";
700   }
701 
702   {
703     // New Compressed apex has lower version than data apex: not selected
704     bool result = ShouldAllocateSpaceForDecompression(
705         "com.android.apex.compressed", 1, instance_new);
706     ASSERT_FALSE(result) << "lower version test with new data returned true";
707   }
708 }
709 
TEST_F(ApexdUnitTest,CalculateSizeForCompressedApexEmptyList)710 TEST_F(ApexdUnitTest, CalculateSizeForCompressedApexEmptyList) {
711   ApexFileRepository instance;
712   int64_t result = CalculateSizeForCompressedApex({}, instance);
713   ASSERT_EQ(0LL, result);
714 }
715 
TEST_F(ApexdUnitTest,CalculateSizeForCompressedApex)716 TEST_F(ApexdUnitTest, CalculateSizeForCompressedApex) {
717   ApexFileRepository instance;
718   AddPreInstalledApex("com.android.apex.compressed.v1.capex");
719   ASSERT_THAT(instance.AddPreInstalledApex({GetBuiltInDir()}), Ok());
720 
721   std::vector<std::tuple<std::string, int64_t, int64_t>> input = {
722       std::make_tuple("new_apex", 1, 1),
723       std::make_tuple("new_apex_2", 1, 2),
724       std::make_tuple("com.android.apex.compressed", 1, 4),  // will be ignored
725       std::make_tuple("com.android.apex.compressed", 2, 8),
726   };
727   int64_t result = CalculateSizeForCompressedApex(input, instance);
728   ASSERT_EQ(1 + 2 + 8LL, result);
729 }
730 
TEST_F(ApexdUnitTest,ReserveSpaceForCompressedApexCreatesSingleFile)731 TEST_F(ApexdUnitTest, ReserveSpaceForCompressedApexCreatesSingleFile) {
732   TemporaryDir dest_dir;
733   // Reserving space should create a single file in dest_dir with exact size
734 
735   ASSERT_THAT(ReserveSpaceForCompressedApex(100, dest_dir.path), Ok());
736   auto files = ReadDir(dest_dir.path, [](auto _) { return true; });
737   ASSERT_THAT(files, Ok());
738   ASSERT_EQ(files->size(), 1u);
739   EXPECT_EQ(fs::file_size((*files)[0]), 100u);
740 }
741 
TEST_F(ApexdUnitTest,ReserveSpaceForCompressedApexSafeToCallMultipleTimes)742 TEST_F(ApexdUnitTest, ReserveSpaceForCompressedApexSafeToCallMultipleTimes) {
743   TemporaryDir dest_dir;
744   // Calling ReserveSpaceForCompressedApex multiple times should still create
745   // a single file
746   ASSERT_THAT(ReserveSpaceForCompressedApex(100, dest_dir.path), Ok());
747   ASSERT_THAT(ReserveSpaceForCompressedApex(100, dest_dir.path), Ok());
748   auto files = ReadDir(dest_dir.path, [](auto _) { return true; });
749   ASSERT_THAT(files, Ok());
750   ASSERT_EQ(files->size(), 1u);
751   EXPECT_EQ(fs::file_size((*files)[0]), 100u);
752 }
753 
TEST_F(ApexdUnitTest,ReserveSpaceForCompressedApexShrinkAndGrow)754 TEST_F(ApexdUnitTest, ReserveSpaceForCompressedApexShrinkAndGrow) {
755   TemporaryDir dest_dir;
756 
757   // Create a 100 byte file
758   ASSERT_THAT(ReserveSpaceForCompressedApex(100, dest_dir.path), Ok());
759 
760   // Should be able to shrink and grow the reserved space
761   ASSERT_THAT(ReserveSpaceForCompressedApex(1000, dest_dir.path), Ok());
762 
763   auto files = ReadDir(dest_dir.path, [](auto _) { return true; });
764   ASSERT_THAT(files, Ok());
765   ASSERT_EQ(files->size(), 1u);
766   EXPECT_EQ(fs::file_size((*files)[0]), 1000u);
767 
768   ASSERT_THAT(ReserveSpaceForCompressedApex(10, dest_dir.path), Ok());
769   files = ReadDir(dest_dir.path, [](auto _) { return true; });
770   ASSERT_THAT(files, Ok());
771   ASSERT_EQ(files->size(), 1u);
772   EXPECT_EQ(fs::file_size((*files)[0]), 10u);
773 }
774 
TEST_F(ApexdUnitTest,ReserveSpaceForCompressedApexDeallocateIfPassedZero)775 TEST_F(ApexdUnitTest, ReserveSpaceForCompressedApexDeallocateIfPassedZero) {
776   TemporaryDir dest_dir;
777 
778   // Create a file first
779   ASSERT_THAT(ReserveSpaceForCompressedApex(100, dest_dir.path), Ok());
780   auto files = ReadDir(dest_dir.path, [](auto _) { return true; });
781   ASSERT_THAT(files, Ok());
782   ASSERT_EQ(files->size(), 1u);
783 
784   // Should delete the reserved file if size passed is 0
785   ASSERT_THAT(ReserveSpaceForCompressedApex(0, dest_dir.path), Ok());
786   files = ReadDir(dest_dir.path, [](auto _) { return true; });
787   ASSERT_THAT(files, Ok());
788   ASSERT_EQ(files->size(), 0u);
789 }
790 
TEST_F(ApexdUnitTest,ReserveSpaceForCapexCleansOtaApex)791 TEST_F(ApexdUnitTest, ReserveSpaceForCapexCleansOtaApex) {
792   TemporaryDir dest_dir;
793 
794   auto ota_apex_path = StringPrintf(
795       "%s/ota_apex%s", GetDecompressionDir().c_str(), kOtaApexPackageSuffix);
796   auto create_ota_apex = [&]() {
797     // Create an ota_apex first
798     fs::copy(GetTestFile("com.android.apex.compressed.v1_original.apex"),
799              ota_apex_path);
800     ASSERT_THAT(PathExists(ota_apex_path), HasValue(true));
801   };
802   create_ota_apex();
803 
804   // Should not delete the reserved file if size passed is negative
805   ASSERT_THAT(ReserveSpaceForCompressedApex(-1, dest_dir.path), Not(Ok()));
806   ASSERT_THAT(PathExists(ota_apex_path), HasValue(true));
807 
808   // Should delete the reserved file if size passed is 0
809   ASSERT_THAT(ReserveSpaceForCompressedApex(0, dest_dir.path), Ok());
810   ASSERT_THAT(PathExists(ota_apex_path), HasValue(false));
811 
812   create_ota_apex();
813   // Should delete the reserved file if size passed is positive
814   ASSERT_THAT(ReserveSpaceForCompressedApex(10, dest_dir.path), Ok());
815   ASSERT_THAT(PathExists(ota_apex_path), HasValue(false));
816 }
817 
TEST_F(ApexdUnitTest,ReserveSpaceForCompressedApexErrorForNegativeValue)818 TEST_F(ApexdUnitTest, ReserveSpaceForCompressedApexErrorForNegativeValue) {
819   TemporaryDir dest_dir;
820   // Should return error if negative value is passed
821   ASSERT_THAT(ReserveSpaceForCompressedApex(-1, dest_dir.path), Not(Ok()));
822 }
823 
TEST_F(ApexdUnitTest,GetStagedApexFilesNoChild)824 TEST_F(ApexdUnitTest, GetStagedApexFilesNoChild) {
825   // Create staged session
826   auto apex_session = CreateStagedSession("apex.apexd_test.apex", 123);
827   apex_session->UpdateStateAndCommit(SessionState::STAGED);
828 
829   // Query for its file
830   auto result = GetStagedApexFiles(123, {});
831 
832   auto apex_file = ApexFile::Open(
833       StringPrintf("%s/apex.apexd_test.apex", GetStagedDir(123).c_str()));
834   ASSERT_THAT(result,
835               HasValue(UnorderedElementsAre(ApexFileEq(ByRef(*apex_file)))));
836 }
837 
TEST_F(ApexdUnitTest,GetStagedApexFilesOnlyStaged)838 TEST_F(ApexdUnitTest, GetStagedApexFilesOnlyStaged) {
839   // Create staged session
840   auto apex_session = CreateStagedSession("apex.apexd_test.apex", 123);
841   apex_session->UpdateStateAndCommit(SessionState::VERIFIED);
842 
843   // Query for its file
844   auto result = GetStagedApexFiles(123, {});
845 
846   ASSERT_THAT(
847       result,
848       HasError(WithMessage(HasSubstr("Session 123 is not in state STAGED"))));
849 }
850 
TEST_F(ApexdUnitTest,GetStagedApexFilesChecksNumberOfApexFiles)851 TEST_F(ApexdUnitTest, GetStagedApexFilesChecksNumberOfApexFiles) {
852   // Create staged session
853   auto apex_session = CreateStagedSession("apex.apexd_test.apex", 123);
854   apex_session->UpdateStateAndCommit(SessionState::STAGED);
855   auto staged_dir = GetStagedDir(123);
856 
857   {
858     // Delete the staged apex file
859     DeleteDirContent(staged_dir);
860 
861     // Query for its file
862     auto result = GetStagedApexFiles(123, {});
863     ASSERT_THAT(result, HasError(WithMessage(HasSubstr(
864                             "Expected exactly one APEX file in directory"))));
865     ASSERT_THAT(result, HasError(WithMessage(HasSubstr("Found: 0"))));
866   }
867   {
868     // Copy multiple files to staged dir
869     fs::copy(GetTestFile("apex.apexd_test.apex"), staged_dir);
870     fs::copy(GetTestFile("apex.apexd_test_v2.apex"), staged_dir);
871 
872     // Query for its file
873     auto result = GetStagedApexFiles(123, {});
874     ASSERT_THAT(result, HasError(WithMessage(HasSubstr(
875                             "Expected exactly one APEX file in directory"))));
876     ASSERT_THAT(result, HasError(WithMessage(HasSubstr("Found: 2"))));
877   }
878 }
879 
TEST_F(ApexdUnitTest,GetStagedApexFilesWithChildren)880 TEST_F(ApexdUnitTest, GetStagedApexFilesWithChildren) {
881   // Create staged session
882   auto parent_apex_session = CreateStagedSession("apex.apexd_test.apex", 123);
883   parent_apex_session->UpdateStateAndCommit(SessionState::STAGED);
884   auto child_session_1 = CreateStagedSession("apex.apexd_test.apex", 124);
885   auto child_session_2 = CreateStagedSession("apex.apexd_test.apex", 125);
886 
887   // Query for its file
888   auto result = GetStagedApexFiles(123, {124, 125});
889 
890   ASSERT_THAT(result, Ok());
891   auto child_apex_file_1 = ApexFile::Open(
892       StringPrintf("%s/apex.apexd_test.apex", GetStagedDir(124).c_str()));
893   auto child_apex_file_2 = ApexFile::Open(
894       StringPrintf("%s/apex.apexd_test.apex", GetStagedDir(125).c_str()));
895   ASSERT_THAT(*result,
896               UnorderedElementsAre(ApexFileEq(ByRef(*child_apex_file_1)),
897                                    ApexFileEq(ByRef(*child_apex_file_2))));
898 }
899 
900 // A test fixture to use for tests that mount/unmount apexes.
901 class ApexdMountTest : public ApexdUnitTest {
902  public:
UnmountOnTearDown(const std::string & apex_file)903   void UnmountOnTearDown(const std::string& apex_file) {
904     to_unmount_.push_back(apex_file);
905   }
906 
907  protected:
SetUp()908   void SetUp() final {
909     ApexdUnitTest::SetUp();
910     GetApexDatabaseForTesting().Reset();
911     GetChangedActiveApexesForTesting().clear();
912     ASSERT_THAT(SetUpApexTestEnvironment(), Ok());
913   }
914 
TearDown()915   void TearDown() final {
916     ApexdUnitTest::TearDown();
917     for (const auto& apex : to_unmount_) {
918       if (auto status = DeactivatePackage(apex); !status.ok()) {
919         LOG(ERROR) << "Failed to unmount " << apex << " : " << status.error();
920       }
921     }
922   }
923 
924  private:
925   MountNamespaceRestorer restorer_;
926   std::vector<std::string> to_unmount_;
927 };
928 
929 // TODO(b/187864524): cover other negative scenarios.
TEST_F(ApexdMountTest,InstallPackageRejectsApexWithoutRebootlessSupport)930 TEST_F(ApexdMountTest, InstallPackageRejectsApexWithoutRebootlessSupport) {
931   std::string file_path = AddPreInstalledApex("apex.apexd_test.apex");
932   ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
933 
934   ASSERT_THAT(ActivatePackage(file_path), Ok());
935   UnmountOnTearDown(file_path);
936 
937   auto ret = InstallPackage(GetTestFile("apex.apexd_test.apex"));
938   ASSERT_THAT(
939       ret,
940       HasError(WithMessage(HasSubstr("does not support non-staged update"))));
941 }
942 
TEST_F(ApexdMountTest,InstallPackageRejectsNoPreInstalledApex)943 TEST_F(ApexdMountTest, InstallPackageRejectsNoPreInstalledApex) {
944   auto ret = InstallPackage(GetTestFile("test.rebootless_apex_v1.apex"));
945   ASSERT_THAT(
946       ret, HasError(WithMessage(HasSubstr(
947                "No active version found for package test.apex.rebootless"))));
948 }
949 
TEST_F(ApexdMountTest,InstallPackageRejectsNoHashtree)950 TEST_F(ApexdMountTest, InstallPackageRejectsNoHashtree) {
951   std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex");
952   ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
953 
954   ASSERT_THAT(ActivatePackage(file_path), Ok());
955   UnmountOnTearDown(file_path);
956 
957   auto ret =
958       InstallPackage(GetTestFile("test.rebootless_apex_v2_no_hashtree.apex"));
959   ASSERT_THAT(
960       ret,
961       HasError(WithMessage(HasSubstr(" does not have an embedded hash tree"))));
962 }
963 
TEST_F(ApexdMountTest,InstallPackageRejectsNoActiveApex)964 TEST_F(ApexdMountTest, InstallPackageRejectsNoActiveApex) {
965   std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex");
966   ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
967 
968   auto ret = InstallPackage(GetTestFile("test.rebootless_apex_v2.apex"));
969   ASSERT_THAT(
970       ret, HasError(WithMessage(HasSubstr(
971                "No active version found for package test.apex.rebootless"))));
972 }
973 
TEST_F(ApexdMountTest,InstallPackageRejectsManifestMismatch)974 TEST_F(ApexdMountTest, InstallPackageRejectsManifestMismatch) {
975   std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex");
976   ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
977 
978   ASSERT_THAT(ActivatePackage(file_path), Ok());
979   UnmountOnTearDown(file_path);
980 
981   auto ret = InstallPackage(
982       GetTestFile("test.rebootless_apex_manifest_mismatch.apex"));
983   ASSERT_THAT(
984       ret,
985       HasError(WithMessage(HasSubstr(
986           "Manifest inside filesystem does not match manifest outside it"))));
987 }
988 
TEST_F(ApexdMountTest,InstallPackageRejectsCorrupted)989 TEST_F(ApexdMountTest, InstallPackageRejectsCorrupted) {
990   std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex");
991   ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
992 
993   ASSERT_THAT(ActivatePackage(file_path), Ok());
994   UnmountOnTearDown(file_path);
995 
996   auto ret = InstallPackage(GetTestFile("test.rebootless_apex_corrupted.apex"));
997   ASSERT_THAT(ret,
998               HasError(WithMessage(HasSubstr("Can't verify /dev/block/dm-"))));
999 }
1000 
TEST_F(ApexdMountTest,InstallPackageRejectsProvidesSharedLibs)1001 TEST_F(ApexdMountTest, InstallPackageRejectsProvidesSharedLibs) {
1002   std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex");
1003   ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
1004 
1005   ASSERT_THAT(ActivatePackage(file_path), Ok());
1006   UnmountOnTearDown(file_path);
1007 
1008   auto ret = InstallPackage(
1009       GetTestFile("test.rebootless_apex_provides_sharedlibs.apex"));
1010   ASSERT_THAT(ret, HasError(WithMessage(HasSubstr(" is a shared libs APEX"))));
1011 }
1012 
TEST_F(ApexdMountTest,InstallPackageRejectsProvidesNativeLibs)1013 TEST_F(ApexdMountTest, InstallPackageRejectsProvidesNativeLibs) {
1014   std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex");
1015   ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
1016 
1017   ASSERT_THAT(ActivatePackage(file_path), Ok());
1018   UnmountOnTearDown(file_path);
1019 
1020   auto ret = InstallPackage(
1021       GetTestFile("test.rebootless_apex_provides_native_libs.apex"));
1022   ASSERT_THAT(ret, HasError(WithMessage(HasSubstr(" provides native libs"))));
1023 }
1024 
TEST_F(ApexdMountTest,InstallPackageRejectsRequiresSharedApexLibs)1025 TEST_F(ApexdMountTest, InstallPackageRejectsRequiresSharedApexLibs) {
1026   std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex");
1027   ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
1028 
1029   ASSERT_THAT(ActivatePackage(file_path), Ok());
1030   UnmountOnTearDown(file_path);
1031 
1032   auto ret = InstallPackage(
1033       GetTestFile("test.rebootless_apex_requires_shared_apex_libs.apex"));
1034   ASSERT_THAT(ret,
1035               HasError(WithMessage(HasSubstr(" requires shared apex libs"))));
1036 }
1037 
TEST_F(ApexdMountTest,InstallPackageRejectsJniLibs)1038 TEST_F(ApexdMountTest, InstallPackageRejectsJniLibs) {
1039   std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex");
1040   ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
1041 
1042   ASSERT_THAT(ActivatePackage(file_path), Ok());
1043   UnmountOnTearDown(file_path);
1044 
1045   auto ret = InstallPackage(GetTestFile("test.rebootless_apex_jni_libs.apex"));
1046   ASSERT_THAT(ret, HasError(WithMessage(HasSubstr(" requires JNI libs"))));
1047 }
1048 
TEST_F(ApexdMountTest,InstallPackageAcceptsAddRequiredNativeLib)1049 TEST_F(ApexdMountTest, InstallPackageAcceptsAddRequiredNativeLib) {
1050   std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex");
1051   ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
1052 
1053   ASSERT_THAT(ActivatePackage(file_path), Ok());
1054   UnmountOnTearDown(file_path);
1055 
1056   auto ret =
1057       InstallPackage(GetTestFile("test.rebootless_apex_add_native_lib.apex"));
1058   ASSERT_THAT(ret, Ok());
1059   UnmountOnTearDown(ret->GetPath());
1060 }
1061 
TEST_F(ApexdMountTest,InstallPackageAcceptsRemoveRequiredNativeLib)1062 TEST_F(ApexdMountTest, InstallPackageAcceptsRemoveRequiredNativeLib) {
1063   std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex");
1064   ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
1065 
1066   ASSERT_THAT(ActivatePackage(file_path), Ok());
1067   UnmountOnTearDown(file_path);
1068 
1069   auto ret = InstallPackage(
1070       GetTestFile("test.rebootless_apex_remove_native_lib.apex"));
1071   ASSERT_THAT(ret, Ok());
1072   UnmountOnTearDown(ret->GetPath());
1073 }
1074 
TEST_F(ApexdMountTest,InstallPackageRejectsAppInApex)1075 TEST_F(ApexdMountTest, InstallPackageRejectsAppInApex) {
1076   std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex");
1077   ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
1078 
1079   ASSERT_THAT(ActivatePackage(file_path), Ok());
1080   UnmountOnTearDown(file_path);
1081 
1082   auto ret =
1083       InstallPackage(GetTestFile("test.rebootless_apex_app_in_apex.apex"));
1084   ASSERT_THAT(ret, HasError(WithMessage(HasSubstr("contains app inside"))));
1085 }
1086 
TEST_F(ApexdMountTest,InstallPackageRejectsPrivAppInApex)1087 TEST_F(ApexdMountTest, InstallPackageRejectsPrivAppInApex) {
1088   std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex");
1089   ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
1090 
1091   ASSERT_THAT(ActivatePackage(file_path), Ok());
1092   UnmountOnTearDown(file_path);
1093 
1094   auto ret =
1095       InstallPackage(GetTestFile("test.rebootless_apex_priv_app_in_apex.apex"));
1096   ASSERT_THAT(ret,
1097               HasError(WithMessage(HasSubstr("contains priv-app inside"))));
1098 }
1099 
TEST_F(ApexdMountTest,InstallPackagePreInstallVersionActive)1100 TEST_F(ApexdMountTest, InstallPackagePreInstallVersionActive) {
1101   std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex");
1102   ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
1103 
1104   ASSERT_THAT(ActivatePackage(file_path), Ok());
1105   UnmountOnTearDown(file_path);
1106 
1107   {
1108     auto active_apex = GetActivePackage("test.apex.rebootless");
1109     ASSERT_THAT(active_apex, Ok());
1110     ASSERT_EQ(active_apex->GetPath(), file_path);
1111   }
1112 
1113   auto ret = InstallPackage(GetTestFile("test.rebootless_apex_v2.apex"));
1114   ASSERT_THAT(ret, Ok());
1115   UnmountOnTearDown(ret->GetPath());
1116 
1117   auto apex_mounts = GetApexMounts();
1118   ASSERT_THAT(apex_mounts,
1119               UnorderedElementsAre("/apex/test.apex.rebootless",
1120                                    "/apex/test.apex.rebootless@2"));
1121 
1122   // Check that /apex/test.apex.rebootless is a bind mount of
1123   // /apex/test.apex.rebootless@2.
1124   auto manifest = ReadManifest("/apex/test.apex.rebootless/apex_manifest.pb");
1125   ASSERT_THAT(manifest, Ok());
1126   ASSERT_EQ(2u, manifest->version());
1127 
1128   // Check that GetActivePackage correctly reports upgraded version.
1129   auto active_apex = GetActivePackage("test.apex.rebootless");
1130   ASSERT_THAT(active_apex, Ok());
1131   ASSERT_EQ(active_apex->GetPath(), ret->GetPath());
1132 
1133   // Check that pre-installed APEX is still around
1134   ASSERT_EQ(0, access(file_path.c_str(), F_OK))
1135       << "Can't access " << file_path << " : " << strerror(errno);
1136 
1137   auto& db = GetApexDatabaseForTesting();
1138   // Check that upgraded APEX is mounted on top of dm-verity device.
1139   db.ForallMountedApexes(
1140       "test.apex.rebootless", [&](const MountedApexData& data, bool latest) {
1141         ASSERT_TRUE(latest);
1142         ASSERT_EQ(data.full_path, ret->GetPath());
1143         ASSERT_EQ(data.device_name, "test.apex.rebootless@2_1");
1144       });
1145 }
1146 
TEST_F(ApexdMountTest,InstallPackagePreInstallVersionActiveSamegrade)1147 TEST_F(ApexdMountTest, InstallPackagePreInstallVersionActiveSamegrade) {
1148   std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex");
1149   ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
1150 
1151   ASSERT_THAT(ActivatePackage(file_path), Ok());
1152   UnmountOnTearDown(file_path);
1153 
1154   {
1155     auto active_apex = GetActivePackage("test.apex.rebootless");
1156     ASSERT_THAT(active_apex, Ok());
1157     ASSERT_EQ(active_apex->GetPath(), file_path);
1158   }
1159 
1160   auto ret = InstallPackage(GetTestFile("test.rebootless_apex_v1.apex"));
1161   ASSERT_THAT(ret, Ok());
1162   UnmountOnTearDown(ret->GetPath());
1163 
1164   auto apex_mounts = GetApexMounts();
1165   ASSERT_THAT(apex_mounts,
1166               UnorderedElementsAre("/apex/test.apex.rebootless",
1167                                    "/apex/test.apex.rebootless@1"));
1168 
1169   // Check that GetActivePackage correctly reports upgraded version.
1170   auto active_apex = GetActivePackage("test.apex.rebootless");
1171   ASSERT_THAT(active_apex, Ok());
1172   ASSERT_EQ(active_apex->GetPath(), ret->GetPath());
1173 
1174   // Check that pre-installed APEX is still around
1175   ASSERT_EQ(0, access(file_path.c_str(), F_OK))
1176       << "Can't access " << file_path << " : " << strerror(errno);
1177 
1178   auto& db = GetApexDatabaseForTesting();
1179   // Check that upgraded APEX is mounted on top of dm-verity device.
1180   db.ForallMountedApexes(
1181       "test.apex.rebootless", [&](const MountedApexData& data, bool latest) {
1182         ASSERT_TRUE(latest);
1183         ASSERT_EQ(data.full_path, ret->GetPath());
1184         ASSERT_EQ(data.device_name, "test.apex.rebootless@1_1");
1185       });
1186 }
1187 
TEST_F(ApexdMountTest,InstallPackageUnloadOldApex)1188 TEST_F(ApexdMountTest, InstallPackageUnloadOldApex) {
1189   std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex");
1190   ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
1191 
1192   bool unloaded = false;
1193   bool loaded = false;
1194   const std::string prop = "apex.test.apex.rebootless.ready";
1195   std::thread monitor_apex_ready_prop([&]() {
1196     unloaded = base::WaitForProperty(prop, "false", 10s);
1197     loaded = base::WaitForProperty(prop, "true", 10s);
1198   });
1199 
1200   ASSERT_THAT(ActivatePackage(file_path), Ok());
1201   UnmountOnTearDown(file_path);
1202 
1203   auto ret = InstallPackage(GetTestFile("test.rebootless_apex_v2.apex"));
1204   ASSERT_THAT(ret, Ok());
1205   UnmountOnTearDown(ret->GetPath());
1206 
1207   monitor_apex_ready_prop.join();
1208   ASSERT_TRUE(unloaded);
1209   ASSERT_TRUE(loaded);
1210 }
1211 
TEST_F(ApexdMountTest,InstallPackageWithService)1212 TEST_F(ApexdMountTest, InstallPackageWithService) {
1213   std::string file_path = AddPreInstalledApex("test.rebootless_apex_service_v1.apex");
1214   ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
1215 
1216   ASSERT_THAT(ActivatePackage(file_path), Ok());
1217   UnmountOnTearDown(file_path);
1218 
1219   auto ret = InstallPackage(GetTestFile("test.rebootless_apex_service_v2.apex"));
1220   ASSERT_THAT(ret, Ok());
1221   auto manifest = ReadManifest("/apex/test.apex.rebootless/apex_manifest.pb");
1222   ASSERT_THAT(manifest, Ok());
1223   ASSERT_EQ(2u, manifest->version());
1224   UnmountOnTearDown(ret->GetPath());
1225 }
1226 
TEST_F(ApexdMountTest,InstallPackageDataVersionActive)1227 TEST_F(ApexdMountTest, InstallPackageDataVersionActive) {
1228   AddPreInstalledApex("test.rebootless_apex_v1.apex");
1229   ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
1230 
1231   std::string file_path = AddDataApex("test.rebootless_apex_v1.apex");
1232   ASSERT_THAT(ActivatePackage(file_path), Ok());
1233   UnmountOnTearDown(file_path);
1234 
1235   {
1236     auto active_apex = GetActivePackage("test.apex.rebootless");
1237     ASSERT_THAT(active_apex, Ok());
1238     ASSERT_EQ(active_apex->GetPath(), file_path);
1239   }
1240 
1241   auto ret = InstallPackage(GetTestFile("test.rebootless_apex_v2.apex"));
1242   ASSERT_THAT(ret, Ok());
1243   UnmountOnTearDown(ret->GetPath());
1244 
1245   auto apex_mounts = GetApexMounts();
1246   ASSERT_THAT(apex_mounts,
1247               UnorderedElementsAre("/apex/test.apex.rebootless",
1248                                    "/apex/test.apex.rebootless@2"));
1249 
1250   // Check that /apex/test.apex.rebootless is a bind mount of
1251   // /apex/test.apex.rebootless@2.
1252   auto manifest = ReadManifest("/apex/test.apex.rebootless/apex_manifest.pb");
1253   ASSERT_THAT(manifest, Ok());
1254   ASSERT_EQ(2u, manifest->version());
1255 
1256   // Check that GetActivePackage correctly reports upgraded version.
1257   auto active_apex = GetActivePackage("test.apex.rebootless");
1258   ASSERT_THAT(active_apex, Ok());
1259   ASSERT_EQ(active_apex->GetPath(), ret->GetPath());
1260 
1261   // Check that previously active APEX was deleted.
1262   ASSERT_EQ(-1, access(file_path.c_str(), F_OK));
1263   ASSERT_EQ(ENOENT, errno);
1264 
1265   auto& db = GetApexDatabaseForTesting();
1266   // Check that upgraded APEX is mounted on top of dm-verity device.
1267   db.ForallMountedApexes(
1268       "test.apex.rebootless", [&](const MountedApexData& data, bool latest) {
1269         ASSERT_TRUE(latest);
1270         ASSERT_EQ(data.full_path, ret->GetPath());
1271         ASSERT_EQ(data.device_name, "test.apex.rebootless@2_1");
1272       });
1273 }
1274 
TEST_F(ApexdMountTest,InstallPackageResolvesPathCollision)1275 TEST_F(ApexdMountTest, InstallPackageResolvesPathCollision) {
1276   AddPreInstalledApex("test.rebootless_apex_v1.apex");
1277   ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
1278 
1279   std::string file_path = AddDataApex("test.rebootless_apex_v1.apex",
1280                                       "test.apex.rebootless@1_1.apex");
1281   ASSERT_THAT(ActivatePackage(file_path), Ok());
1282   UnmountOnTearDown(file_path);
1283 
1284   {
1285     auto active_apex = GetActivePackage("test.apex.rebootless");
1286     ASSERT_THAT(active_apex, Ok());
1287     ASSERT_EQ(active_apex->GetPath(), file_path);
1288   }
1289 
1290   auto ret = InstallPackage(GetTestFile("test.rebootless_apex_v1.apex"));
1291   ASSERT_THAT(ret, Ok());
1292   UnmountOnTearDown(ret->GetPath());
1293 
1294   auto apex_mounts = GetApexMounts();
1295   ASSERT_THAT(apex_mounts,
1296               UnorderedElementsAre("/apex/test.apex.rebootless",
1297                                    "/apex/test.apex.rebootless@1"));
1298 
1299   // Check that /apex/test.apex.rebootless is a bind mount of
1300   // /apex/test.apex.rebootless@2.
1301   auto manifest = ReadManifest("/apex/test.apex.rebootless/apex_manifest.pb");
1302   ASSERT_THAT(manifest, Ok());
1303   ASSERT_EQ(1u, manifest->version());
1304 
1305   // Check that GetActivePackage correctly reports upgraded version.
1306   auto active_apex = GetActivePackage("test.apex.rebootless");
1307   ASSERT_THAT(active_apex, Ok());
1308   ASSERT_EQ(active_apex->GetPath(), ret->GetPath());
1309 
1310   // Check that we correctly resolved active apex path collision.
1311   ASSERT_EQ(active_apex->GetPath(),
1312             GetDataDir() + "/test.apex.rebootless@1_2.apex");
1313 
1314   // Check that previously active APEX was deleted.
1315   ASSERT_EQ(-1, access(file_path.c_str(), F_OK));
1316   ASSERT_EQ(ENOENT, errno);
1317 
1318   auto& db = GetApexDatabaseForTesting();
1319   // Check that upgraded APEX is mounted on top of dm-verity device.
1320   db.ForallMountedApexes(
1321       "test.apex.rebootless", [&](const MountedApexData& data, bool latest) {
1322         ASSERT_TRUE(latest);
1323         ASSERT_EQ(data.full_path, ret->GetPath());
1324         ASSERT_EQ(data.device_name, "test.apex.rebootless@1_2");
1325       });
1326 }
1327 
TEST_F(ApexdMountTest,InstallPackageDataVersionActiveSamegrade)1328 TEST_F(ApexdMountTest, InstallPackageDataVersionActiveSamegrade) {
1329   AddPreInstalledApex("test.rebootless_apex_v1.apex");
1330   ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
1331 
1332   std::string file_path = AddDataApex("test.rebootless_apex_v2.apex");
1333   ASSERT_THAT(ActivatePackage(file_path), Ok());
1334   UnmountOnTearDown(file_path);
1335 
1336   {
1337     auto active_apex = GetActivePackage("test.apex.rebootless");
1338     ASSERT_THAT(active_apex, Ok());
1339     ASSERT_EQ(active_apex->GetPath(), file_path);
1340   }
1341 
1342   auto ret = InstallPackage(GetTestFile("test.rebootless_apex_v2.apex"));
1343   ASSERT_THAT(ret, Ok());
1344   UnmountOnTearDown(ret->GetPath());
1345 
1346   auto apex_mounts = GetApexMounts();
1347   ASSERT_THAT(apex_mounts,
1348               UnorderedElementsAre("/apex/test.apex.rebootless",
1349                                    "/apex/test.apex.rebootless@2"));
1350 
1351   // Check that /apex/test.apex.rebootless is a bind mount of
1352   // /apex/test.apex.rebootless@2.
1353   auto manifest = ReadManifest("/apex/test.apex.rebootless/apex_manifest.pb");
1354   ASSERT_THAT(manifest, Ok());
1355   ASSERT_EQ(2u, manifest->version());
1356 
1357   // Check that GetActivePackage correctly reports upgraded version.
1358   auto active_apex = GetActivePackage("test.apex.rebootless");
1359   ASSERT_THAT(active_apex, Ok());
1360   ASSERT_EQ(active_apex->GetPath(), ret->GetPath());
1361 
1362   // Check that previously active APEX was deleted.
1363   ASSERT_EQ(-1, access(file_path.c_str(), F_OK));
1364   ASSERT_EQ(ENOENT, errno);
1365 
1366   auto& db = GetApexDatabaseForTesting();
1367   // Check that upgraded APEX is mounted on top of dm-verity device.
1368   db.ForallMountedApexes(
1369       "test.apex.rebootless", [&](const MountedApexData& data, bool latest) {
1370         ASSERT_TRUE(latest);
1371         ASSERT_EQ(data.full_path, ret->GetPath());
1372         ASSERT_EQ(data.device_name, "test.apex.rebootless@2_1");
1373       });
1374 }
1375 
TEST_F(ApexdMountTest,InstallPackageUnmountFailsPreInstalledApexActive)1376 TEST_F(ApexdMountTest, InstallPackageUnmountFailsPreInstalledApexActive) {
1377   std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex");
1378   ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
1379 
1380   ASSERT_THAT(ActivatePackage(file_path), Ok());
1381   UnmountOnTearDown(file_path);
1382 
1383   {
1384     auto active_apex = GetActivePackage("test.apex.rebootless");
1385     ASSERT_THAT(active_apex, Ok());
1386     ASSERT_EQ(active_apex->GetPath(), file_path);
1387   }
1388 
1389   unique_fd fd(open("/apex/test.apex.rebootless/apex_manifest.pb",
1390                     O_RDONLY | O_CLOEXEC));
1391   ASSERT_NE(-1, fd.get());
1392 
1393   auto ret = InstallPackage(GetTestFile("test.rebootless_apex_v2.apex"));
1394   ASSERT_THAT(ret, Not(Ok()));
1395 
1396   auto apex_mounts = GetApexMounts();
1397   ASSERT_THAT(apex_mounts,
1398               UnorderedElementsAre("/apex/test.apex.rebootless",
1399                                    "/apex/test.apex.rebootless@1"));
1400 
1401   // Check that GetActivePackage correctly reports upgraded version.
1402   auto active_apex = GetActivePackage("test.apex.rebootless");
1403   ASSERT_THAT(active_apex, Ok());
1404   ASSERT_EQ(active_apex->GetPath(), file_path);
1405 
1406   // Check that old APEX is still around
1407   ASSERT_EQ(0, access(file_path.c_str(), F_OK))
1408       << "Can't access " << file_path << " : " << strerror(errno);
1409 
1410   auto& db = GetApexDatabaseForTesting();
1411   // Check that upgraded APEX is mounted on top of dm-verity device.
1412   db.ForallMountedApexes("test.apex.rebootless",
1413                          [&](const MountedApexData& data, bool latest) {
1414                            ASSERT_TRUE(latest);
1415                            ASSERT_EQ(data.full_path, file_path);
1416                          });
1417 }
1418 
TEST_F(ApexdMountTest,InstallPackageUnmountFailedUpdatedApexActive)1419 TEST_F(ApexdMountTest, InstallPackageUnmountFailedUpdatedApexActive) {
1420   AddPreInstalledApex("test.rebootless_apex_v1.apex");
1421   ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
1422 
1423   std::string file_path = AddDataApex("test.rebootless_apex_v1.apex");
1424 
1425   ASSERT_THAT(ActivatePackage(file_path), Ok());
1426   UnmountOnTearDown(file_path);
1427 
1428   {
1429     auto active_apex = GetActivePackage("test.apex.rebootless");
1430     ASSERT_THAT(active_apex, Ok());
1431     ASSERT_EQ(active_apex->GetPath(), file_path);
1432   }
1433 
1434   unique_fd fd(open("/apex/test.apex.rebootless/apex_manifest.pb",
1435                     O_RDONLY | O_CLOEXEC));
1436   ASSERT_NE(-1, fd.get());
1437 
1438   auto ret = InstallPackage(GetTestFile("test.rebootless_apex_v2.apex"));
1439   ASSERT_THAT(ret, Not(Ok()));
1440 
1441   auto apex_mounts = GetApexMounts();
1442   ASSERT_THAT(apex_mounts,
1443               UnorderedElementsAre("/apex/test.apex.rebootless",
1444                                    "/apex/test.apex.rebootless@1"));
1445 
1446   // Check that GetActivePackage correctly reports old apex.
1447   auto active_apex = GetActivePackage("test.apex.rebootless");
1448   ASSERT_THAT(active_apex, Ok());
1449   ASSERT_EQ(active_apex->GetPath(), file_path);
1450 
1451   // Check that old APEX is still around
1452   ASSERT_EQ(0, access(file_path.c_str(), F_OK))
1453       << "Can't access " << file_path << " : " << strerror(errno);
1454 
1455   auto& db = GetApexDatabaseForTesting();
1456   db.ForallMountedApexes(
1457       "test.apex.rebootless", [&](const MountedApexData& data, bool latest) {
1458         ASSERT_TRUE(latest);
1459         ASSERT_EQ(data.full_path, file_path);
1460         ASSERT_EQ(data.device_name, "test.apex.rebootless@1");
1461       });
1462 }
1463 
TEST_F(ApexdMountTest,InstallPackageUpdatesApexInfoList)1464 TEST_F(ApexdMountTest, InstallPackageUpdatesApexInfoList) {
1465   auto apex_1 = AddPreInstalledApex("test.rebootless_apex_v1.apex");
1466   auto apex_2 = AddPreInstalledApex("apex.apexd_test.apex");
1467   ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
1468 
1469   UnmountOnTearDown(apex_1);
1470   UnmountOnTearDown(apex_2);
1471   ASSERT_THAT(ActivatePackage(apex_1), Ok());
1472   ASSERT_THAT(ActivatePackage(apex_2), Ok());
1473 
1474   // Call OnAllPackagesActivated to create /apex/apex-info-list.xml.
1475   OnAllPackagesActivated(/* is_bootstrap= */ false);
1476   // Check /apex/apex-info-list.xml was created.
1477   ASSERT_EQ(0, access("/apex/apex-info-list.xml", F_OK));
1478 
1479   auto ret = InstallPackage(GetTestFile("test.rebootless_apex_v2.apex"));
1480   ASSERT_THAT(ret, Ok());
1481   UnmountOnTearDown(ret->GetPath());
1482 
1483   ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
1484   auto info_list =
1485       com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
1486   ASSERT_TRUE(info_list.has_value());
1487   auto apex_info_xml_1 = com::android::apex::ApexInfo(
1488       /* moduleName= */ "test.apex.rebootless",
1489       /* modulePath= */ apex_1,
1490       /* preinstalledModulePath= */ apex_1,
1491       /* versionCode= */ 1, /* versionName= */ "1",
1492       /* isFactory= */ true, /* isActive= */ false, GetMTime(apex_1),
1493       /* provideSharedApexLibs= */ false);
1494   auto apex_info_xml_2 = com::android::apex::ApexInfo(
1495       /* moduleName= */ "com.android.apex.test_package",
1496       /* modulePath= */ apex_2, /* preinstalledModulePath= */ apex_2,
1497       /* versionCode= */ 1, /* versionName= */ "1", /* isFactory= */ true,
1498       /* isActive= */ true, GetMTime(apex_2),
1499       /* provideSharedApexLibs= */ false);
1500   auto apex_info_xml_3 = com::android::apex::ApexInfo(
1501       /* moduleName= */ "test.apex.rebootless",
1502       /* modulePath= */ ret->GetPath(),
1503       /* preinstalledModulePath= */ apex_1,
1504       /* versionCode= */ 2, /* versionName= */ "2",
1505       /* isFactory= */ false, /* isActive= */ true, GetMTime(ret->GetPath()),
1506       /* provideSharedApexLibs= */ false);
1507   ASSERT_THAT(info_list->getApexInfo(),
1508               UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1),
1509                                    ApexInfoXmlEq(apex_info_xml_2),
1510                                    ApexInfoXmlEq(apex_info_xml_3)));
1511 }
1512 
TEST_F(ApexdMountTest,ActivatePackageBannedName)1513 TEST_F(ApexdMountTest, ActivatePackageBannedName) {
1514   auto status = ActivatePackage(GetTestFile("sharedlibs.apex"));
1515   ASSERT_THAT(status,
1516               HasError(WithMessage("Package name sharedlibs is not allowed.")));
1517 }
1518 
TEST_F(ApexdMountTest,ActivatePackageNoCode)1519 TEST_F(ApexdMountTest, ActivatePackageNoCode) {
1520   std::string file_path = AddPreInstalledApex("apex.apexd_test_nocode.apex");
1521   ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
1522 
1523   ASSERT_THAT(ActivatePackage(file_path), Ok());
1524   UnmountOnTearDown(file_path);
1525 
1526   std::string mountinfo;
1527   ASSERT_TRUE(ReadFileToString("/proc/self/mountinfo", &mountinfo));
1528   bool found_apex_mountpoint = false;
1529   for (const auto& line : Split(mountinfo, "\n")) {
1530     std::vector<std::string> tokens = Split(line, " ");
1531     // line format:
1532     // mnt_id parent_mnt_id major:minor source target option propagation_type
1533     // ex) 33 260:19 / /apex rw,nosuid,nodev -
1534     if (tokens.size() >= 7 &&
1535         tokens[4] == "/apex/com.android.apex.test_package@1") {
1536       found_apex_mountpoint = true;
1537       // Make sure that option contains noexec
1538       std::vector<std::string> options = Split(tokens[5], ",");
1539       EXPECT_THAT(options, Contains("noexec"));
1540       break;
1541     }
1542   }
1543   EXPECT_TRUE(found_apex_mountpoint);
1544 }
1545 
TEST_F(ApexdMountTest,ActivatePackageManifestMissmatch)1546 TEST_F(ApexdMountTest, ActivatePackageManifestMissmatch) {
1547   std::string file_path =
1548       AddPreInstalledApex("apex.apexd_test_manifest_mismatch.apex");
1549   ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
1550 
1551   auto status = ActivatePackage(file_path);
1552   ASSERT_THAT(
1553       status,
1554       HasError(WithMessage(HasSubstr(
1555           "Manifest inside filesystem does not match manifest outside it"))));
1556 }
1557 
TEST_F(ApexdMountTest,ActivatePackage)1558 TEST_F(ApexdMountTest, ActivatePackage) {
1559   std::string file_path = AddPreInstalledApex("apex.apexd_test.apex");
1560   ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
1561 
1562   ASSERT_THAT(ActivatePackage(file_path), Ok());
1563   UnmountOnTearDown(file_path);
1564 
1565   auto active_apex = GetActivePackage("com.android.apex.test_package");
1566   ASSERT_THAT(active_apex, Ok());
1567   ASSERT_EQ(active_apex->GetPath(), file_path);
1568 
1569   auto apex_mounts = GetApexMounts();
1570   ASSERT_THAT(apex_mounts,
1571               UnorderedElementsAre("/apex/com.android.apex.test_package",
1572                                    "/apex/com.android.apex.test_package@1"));
1573 
1574   ASSERT_THAT(DeactivatePackage(file_path), Ok());
1575   ASSERT_THAT(GetActivePackage("com.android.apex.test_package"), Not(Ok()));
1576 
1577   auto new_apex_mounts = GetApexMounts();
1578   ASSERT_EQ(new_apex_mounts.size(), 0u);
1579 }
1580 
TEST_F(ApexdMountTest,ActivatePackageShowsUpInMountedApexDatabase)1581 TEST_F(ApexdMountTest, ActivatePackageShowsUpInMountedApexDatabase) {
1582   std::string file_path = AddPreInstalledApex("apex.apexd_test.apex");
1583   ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
1584 
1585   ASSERT_THAT(ActivatePackage(file_path), Ok());
1586   UnmountOnTearDown(file_path);
1587 
1588   auto active_apex = GetActivePackage("com.android.apex.test_package");
1589   ASSERT_THAT(active_apex, Ok());
1590   ASSERT_EQ(active_apex->GetPath(), file_path);
1591 
1592   auto apex_mounts = GetApexMounts();
1593   ASSERT_THAT(apex_mounts,
1594               UnorderedElementsAre("/apex/com.android.apex.test_package",
1595                                    "/apex/com.android.apex.test_package@1"));
1596 
1597   // Check that mounted apex database contains information about our APEX.
1598   auto& db = GetApexDatabaseForTesting();
1599   std::optional<MountedApexData> mounted_apex;
1600   db.ForallMountedApexes("com.android.apex.test_package",
1601                          [&](const MountedApexData& d, bool active) {
1602                            if (active) {
1603                              mounted_apex.emplace(d);
1604                            }
1605                          });
1606   ASSERT_TRUE(mounted_apex)
1607       << "Haven't found com.android.apex.test_package in the database of "
1608       << "mounted apexes";
1609 }
1610 
TEST_F(ApexdMountTest,ActivatePackageNoHashtree)1611 TEST_F(ApexdMountTest, ActivatePackageNoHashtree) {
1612   AddPreInstalledApex("apex.apexd_test.apex");
1613   ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
1614 
1615   std::string file_path = AddDataApex("apex.apexd_test_no_hashtree.apex");
1616   ASSERT_THAT(ActivatePackage(file_path), Ok());
1617   UnmountOnTearDown(file_path);
1618 
1619   // Check that hashtree was generated
1620   std::string hashtree_path =
1621       GetHashTreeDir() + "/com.android.apex.test_package@1";
1622   ASSERT_EQ(0, access(hashtree_path.c_str(), F_OK));
1623 
1624   // Check that block device can be read.
1625   auto block_device = GetBlockDeviceForApex("com.android.apex.test_package@1");
1626   ASSERT_THAT(block_device, Ok());
1627   ASSERT_THAT(ReadDevice(*block_device), Ok());
1628 }
1629 
TEST_F(ApexdMountTest,ActivatePackageNoHashtreeShowsUpInMountedDatabase)1630 TEST_F(ApexdMountTest, ActivatePackageNoHashtreeShowsUpInMountedDatabase) {
1631   AddPreInstalledApex("apex.apexd_test.apex");
1632   ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
1633 
1634   std::string file_path = AddDataApex("apex.apexd_test_no_hashtree.apex");
1635   ASSERT_THAT(ActivatePackage(file_path), Ok());
1636   UnmountOnTearDown(file_path);
1637 
1638   // Get loop devices that were used to mount APEX.
1639   auto children = ListChildLoopDevices("com.android.apex.test_package@1");
1640   ASSERT_THAT(children, Ok());
1641   ASSERT_EQ(2u, children->size())
1642       << "Unexpected number of children: " << Join(*children, ",");
1643 
1644   auto& db = GetApexDatabaseForTesting();
1645   std::optional<MountedApexData> mounted_apex;
1646   db.ForallMountedApexes("com.android.apex.test_package",
1647                          [&](const MountedApexData& d, bool active) {
1648                            if (active) {
1649                              mounted_apex.emplace(d);
1650                            }
1651                          });
1652   ASSERT_TRUE(mounted_apex)
1653       << "Haven't found com.android.apex.test_package@1  in the database of "
1654       << "mounted apexes";
1655 
1656   ASSERT_EQ(file_path, mounted_apex->full_path);
1657   ASSERT_EQ("/apex/com.android.apex.test_package@1", mounted_apex->mount_point);
1658   ASSERT_EQ("com.android.apex.test_package@1", mounted_apex->device_name);
1659   // For loops we only check that both loop_name and hashtree_loop_name are
1660   // children of the top device mapper device.
1661   ASSERT_THAT(*children, Contains(mounted_apex->loop_name));
1662   ASSERT_THAT(*children, Contains(mounted_apex->hashtree_loop_name));
1663   ASSERT_NE(mounted_apex->loop_name, mounted_apex->hashtree_loop_name);
1664 }
1665 
TEST_F(ApexdMountTest,DeactivePackageFreesLoopDevices)1666 TEST_F(ApexdMountTest, DeactivePackageFreesLoopDevices) {
1667   AddPreInstalledApex("apex.apexd_test.apex");
1668   ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
1669 
1670   std::string file_path = AddDataApex("apex.apexd_test_no_hashtree.apex");
1671   ASSERT_THAT(ActivatePackage(file_path), Ok());
1672   UnmountOnTearDown(file_path);
1673 
1674   // Get loop devices that were used to mount APEX.
1675   auto children = ListChildLoopDevices("com.android.apex.test_package@1");
1676   ASSERT_THAT(children, Ok());
1677   ASSERT_EQ(2u, children->size())
1678       << "Unexpected number of children: " << Join(*children, ",");
1679 
1680   ASSERT_THAT(DeactivatePackage(file_path), Ok());
1681   for (const auto& loop : *children) {
1682     struct loop_info li;
1683     unique_fd fd(TEMP_FAILURE_RETRY(open(loop.c_str(), O_RDWR | O_CLOEXEC)));
1684     EXPECT_NE(-1, fd.get())
1685         << "Failed to open " << loop << " : " << strerror(errno);
1686     EXPECT_EQ(-1, ioctl(fd.get(), LOOP_GET_STATUS, &li))
1687         << loop << " is still alive";
1688     EXPECT_EQ(ENXIO, errno) << "Unexpected errno : " << strerror(errno);
1689   }
1690 }
1691 
TEST_F(ApexdMountTest,NoHashtreeApexNewSessionDoesNotImpactActivePackage)1692 TEST_F(ApexdMountTest, NoHashtreeApexNewSessionDoesNotImpactActivePackage) {
1693   MockCheckpointInterface checkpoint_interface;
1694   checkpoint_interface.SetSupportsCheckpoint(true);
1695   InitializeVold(&checkpoint_interface);
1696 
1697   AddPreInstalledApex("apex.apexd_test_no_hashtree.apex");
1698   ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
1699 
1700   std::string file_path = AddDataApex("apex.apexd_test_no_hashtree.apex");
1701   ASSERT_THAT(ActivatePackage(file_path), Ok());
1702   UnmountOnTearDown(file_path);
1703 
1704   ASSERT_THAT(CreateStagedSession("apex.apexd_test_no_hashtree_2.apex", 239),
1705               Ok());
1706   auto status =
1707       SubmitStagedSession(239, {}, /* has_rollback_enabled= */ false,
1708                           /* is_rollback= */ false, /* rollback_id= */ -1);
1709   ASSERT_THAT(status, Ok());
1710 
1711   // Check that new hashtree file was created.
1712   {
1713     std::string hashtree_path =
1714         GetHashTreeDir() + "/com.android.apex.test_package@1.new";
1715     ASSERT_THAT(PathExists(hashtree_path), HasValue(true))
1716         << hashtree_path << " does not exist";
1717   }
1718   // Check that active hashtree is still there.
1719   {
1720     std::string hashtree_path =
1721         GetHashTreeDir() + "/com.android.apex.test_package@1";
1722     ASSERT_THAT(PathExists(hashtree_path), HasValue(true))
1723         << hashtree_path << " does not exist";
1724   }
1725 
1726   // Check that block device of active APEX can still be read.
1727   auto block_device = GetBlockDeviceForApex("com.android.apex.test_package@1");
1728   ASSERT_THAT(block_device, Ok());
1729   ASSERT_THAT(ReadDevice(*block_device), Ok());
1730 }
1731 
TEST_F(ApexdMountTest,NoHashtreeApexStagePackagesMovesHashtree)1732 TEST_F(ApexdMountTest, NoHashtreeApexStagePackagesMovesHashtree) {
1733   MockCheckpointInterface checkpoint_interface;
1734   checkpoint_interface.SetSupportsCheckpoint(true);
1735   InitializeVold(&checkpoint_interface);
1736 
1737   AddPreInstalledApex("apex.apexd_test_no_hashtree.apex");
1738   ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
1739 
1740   auto read_fn = [](const std::string& path) -> std::vector<uint8_t> {
1741     static constexpr size_t kBufSize = 4096;
1742     std::vector<uint8_t> buffer(kBufSize);
1743     unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
1744     if (fd.get() == -1) {
1745       PLOG(ERROR) << "Failed to open " << path;
1746       ADD_FAILURE();
1747       return buffer;
1748     }
1749     if (!ReadFully(fd.get(), buffer.data(), kBufSize)) {
1750       PLOG(ERROR) << "Failed to read " << path;
1751       ADD_FAILURE();
1752     }
1753     return buffer;
1754   };
1755 
1756   ASSERT_THAT(CreateStagedSession("apex.apexd_test_no_hashtree_2.apex", 37),
1757               Ok());
1758   auto status =
1759       SubmitStagedSession(37, {}, /* has_rollback_enabled= */ false,
1760                           /* is_rollback= */ false, /* rollback_id= */ -1);
1761   ASSERT_THAT(status, Ok());
1762   auto staged_apex = std::move((*status)[0]);
1763 
1764   // Check that new hashtree file was created.
1765   std::vector<uint8_t> original_hashtree_data;
1766   {
1767     std::string hashtree_path =
1768         GetHashTreeDir() + "/com.android.apex.test_package@1.new";
1769     ASSERT_THAT(PathExists(hashtree_path), HasValue(true));
1770     original_hashtree_data = read_fn(hashtree_path);
1771   }
1772 
1773   ASSERT_THAT(StagePackages({staged_apex.GetPath()}), Ok());
1774   // Check that hashtree file was moved.
1775   {
1776     std::string hashtree_path =
1777         GetHashTreeDir() + "/com.android.apex.test_package@1.new";
1778     ASSERT_THAT(PathExists(hashtree_path), HasValue(false));
1779   }
1780   {
1781     std::string hashtree_path =
1782         GetHashTreeDir() + "/com.android.apex.test_package@1";
1783     ASSERT_THAT(PathExists(hashtree_path), HasValue(true));
1784     std::vector<uint8_t> moved_hashtree_data = read_fn(hashtree_path);
1785     ASSERT_EQ(moved_hashtree_data, original_hashtree_data);
1786   }
1787 }
1788 
TEST_F(ApexdMountTest,DeactivePackageTearsDownVerityDevice)1789 TEST_F(ApexdMountTest, DeactivePackageTearsDownVerityDevice) {
1790   AddPreInstalledApex("apex.apexd_test.apex");
1791   ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
1792 
1793   std::string file_path = AddDataApex("apex.apexd_test_v2.apex");
1794   ASSERT_THAT(ActivatePackage(file_path), Ok());
1795   UnmountOnTearDown(file_path);
1796 
1797   ASSERT_THAT(DeactivatePackage(file_path), Ok());
1798   auto& dm = DeviceMapper::Instance();
1799   ASSERT_EQ(dm::DmDeviceState::INVALID,
1800             dm.GetState("com.android.apex.test_package@2"));
1801 }
1802 
TEST_F(ApexdMountTest,ActivateDeactivateSharedLibsApex)1803 TEST_F(ApexdMountTest, ActivateDeactivateSharedLibsApex) {
1804   ASSERT_EQ(mkdir("/apex/sharedlibs", 0755), 0);
1805   ASSERT_EQ(mkdir("/apex/sharedlibs/lib", 0755), 0);
1806   ASSERT_EQ(mkdir("/apex/sharedlibs/lib64", 0755), 0);
1807   auto deleter = make_scope_guard([]() {
1808     std::error_code ec;
1809     fs::remove_all("/apex/sharedlibs", ec);
1810     if (ec) {
1811       LOG(ERROR) << "Failed to delete /apex/sharedlibs : " << ec;
1812     }
1813   });
1814 
1815   std::string file_path = AddPreInstalledApex(
1816       "com.android.apex.test.sharedlibs_generated.v1.libvX.apex");
1817   ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
1818 
1819   UnmountOnTearDown(file_path);
1820   ASSERT_THAT(ActivatePackage(file_path), Ok());
1821 
1822   auto active_apex = GetActivePackage("com.android.apex.test.sharedlibs");
1823   ASSERT_THAT(active_apex, Ok());
1824   ASSERT_EQ(active_apex->GetPath(), file_path);
1825 
1826   auto apex_mounts = GetApexMounts();
1827   ASSERT_THAT(apex_mounts,
1828               UnorderedElementsAre("/apex/com.android.apex.test.sharedlibs@1"));
1829 
1830   ASSERT_THAT(DeactivatePackage(file_path), Ok());
1831   ASSERT_THAT(GetActivePackage("com.android.apex.test.sharedlibs"), Not(Ok()));
1832 
1833   auto new_apex_mounts = GetApexMounts();
1834   ASSERT_EQ(new_apex_mounts.size(), 0u);
1835 }
1836 
TEST_F(ApexdMountTest,RemoveInactiveDataApex)1837 TEST_F(ApexdMountTest, RemoveInactiveDataApex) {
1838   AddPreInstalledApex("com.android.apex.compressed.v2.capex");
1839   // Add a decompressed apex that will not be mounted, so should be removed
1840   auto decompressed_apex = StringPrintf("%s/com.android.apex.compressed@1%s",
1841                                         GetDecompressionDir().c_str(),
1842                                         kDecompressedApexPackageSuffix);
1843   fs::copy(GetTestFile("com.android.apex.compressed.v1_original.apex"),
1844            decompressed_apex);
1845   // Add a decompressed apex that will be mounted, so should be not be removed
1846   auto active_decompressed_apex = StringPrintf(
1847       "%s/com.android.apex.compressed@2%s", GetDecompressionDir().c_str(),
1848       kDecompressedApexPackageSuffix);
1849   fs::copy(GetTestFile("com.android.apex.compressed.v2_original.apex"),
1850            active_decompressed_apex);
1851   // Apex that do not have kDecompressedApexPackageSuffix, should not be removed
1852   // from decompression_dir
1853   auto decompressed_different_suffix =
1854       StringPrintf("%s/com.android.apex.compressed@2%s",
1855                    GetDecompressionDir().c_str(), kApexPackageSuffix);
1856   fs::copy(GetTestFile("com.android.apex.compressed.v2_original.apex"),
1857            decompressed_different_suffix);
1858 
1859   AddPreInstalledApex("apex.apexd_test.apex");
1860   auto data_apex = AddDataApex("apex.apexd_test.apex");
1861   auto active_data_apex = AddDataApex("apex.apexd_test_v2.apex");
1862 
1863   // Activate some of the apex
1864   ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
1865   UnmountOnTearDown(active_decompressed_apex);
1866   UnmountOnTearDown(active_data_apex);
1867   ASSERT_THAT(ActivatePackage(active_decompressed_apex), Ok());
1868   ASSERT_THAT(ActivatePackage(active_data_apex), Ok());
1869   // Clean up inactive apex packages
1870   RemoveInactiveDataApex();
1871 
1872   // Verify inactive apex packages have been deleted
1873   ASSERT_TRUE(*PathExists(active_decompressed_apex));
1874   ASSERT_TRUE(*PathExists(active_data_apex));
1875   ASSERT_TRUE(*PathExists(decompressed_different_suffix));
1876   ASSERT_FALSE(*PathExists(decompressed_apex));
1877   ASSERT_FALSE(*PathExists(data_apex));
1878 }
1879 
TEST_F(ApexdMountTest,OnOtaChrootBootstrapOnlyPreInstalledApexes)1880 TEST_F(ApexdMountTest, OnOtaChrootBootstrapOnlyPreInstalledApexes) {
1881   std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex");
1882   std::string apex_path_2 =
1883       AddPreInstalledApex("apex.apexd_test_different_app.apex");
1884 
1885   ASSERT_EQ(OnOtaChrootBootstrap(), 0);
1886   UnmountOnTearDown(apex_path_1);
1887   UnmountOnTearDown(apex_path_2);
1888 
1889   auto apex_mounts = GetApexMounts();
1890   ASSERT_THAT(apex_mounts,
1891               UnorderedElementsAre("/apex/com.android.apex.test_package",
1892                                    "/apex/com.android.apex.test_package@1",
1893                                    "/apex/com.android.apex.test_package_2",
1894                                    "/apex/com.android.apex.test_package_2@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_1 = com::android::apex::ApexInfo(
1901       /* moduleName= */ "com.android.apex.test_package",
1902       /* modulePath= */ apex_path_1,
1903       /* preinstalledModulePath= */ apex_path_1,
1904       /* versionCode= */ 1, /* versionName= */ "1",
1905       /* isFactory= */ true, /* isActive= */ true, GetMTime(apex_path_1),
1906       /* provideSharedApexLibs= */ false);
1907   auto apex_info_xml_2 = com::android::apex::ApexInfo(
1908       /* moduleName= */ "com.android.apex.test_package_2",
1909       /* modulePath= */ apex_path_2, /* preinstalledModulePath= */ apex_path_2,
1910       /* versionCode= */ 1, /* versionName= */ "1", /* isFactory= */ true,
1911       /* isActive= */ true, GetMTime(apex_path_2),
1912       /* provideSharedApexLibs= */ false);
1913   ASSERT_THAT(info_list->getApexInfo(),
1914               UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1),
1915                                    ApexInfoXmlEq(apex_info_xml_2)));
1916 }
1917 
TEST_F(ApexdMountTest,OnOtaChrootBootstrapFailsToScanPreInstalledApexes)1918 TEST_F(ApexdMountTest, OnOtaChrootBootstrapFailsToScanPreInstalledApexes) {
1919   AddPreInstalledApex("apex.apexd_test.apex");
1920   AddPreInstalledApex("apex.apexd_test_corrupt_superblock_apex.apex");
1921 
1922   ASSERT_EQ(OnOtaChrootBootstrap(), 1);
1923 }
1924 
TEST_F(ApexdMountTest,OnOtaChrootBootstrapDataHasHigherVersion)1925 TEST_F(ApexdMountTest, OnOtaChrootBootstrapDataHasHigherVersion) {
1926   std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex");
1927   std::string apex_path_2 =
1928       AddPreInstalledApex("apex.apexd_test_different_app.apex");
1929   std::string apex_path_3 = AddDataApex("apex.apexd_test_v2.apex");
1930 
1931   ASSERT_EQ(OnOtaChrootBootstrap(), 0);
1932 
1933   UnmountOnTearDown(apex_path_2);
1934   UnmountOnTearDown(apex_path_3);
1935 
1936   auto apex_mounts = GetApexMounts();
1937   ASSERT_THAT(apex_mounts,
1938               UnorderedElementsAre("/apex/com.android.apex.test_package",
1939                                    "/apex/com.android.apex.test_package@2",
1940                                    "/apex/com.android.apex.test_package_2",
1941                                    "/apex/com.android.apex.test_package_2@1"));
1942 
1943   ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
1944   auto info_list =
1945       com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
1946   ASSERT_TRUE(info_list.has_value());
1947   auto apex_info_xml_1 = com::android::apex::ApexInfo(
1948       /* moduleName= */ "com.android.apex.test_package",
1949       /* modulePath= */ apex_path_1,
1950       /* preinstalledModulePath= */ apex_path_1,
1951       /* versionCode= */ 1, /* versionName= */ "1",
1952       /* isFactory= */ true, /* isActive= */ false, GetMTime(apex_path_1),
1953       /* provideSharedApexLibs= */ false);
1954   auto apex_info_xml_2 = com::android::apex::ApexInfo(
1955       /* moduleName= */ "com.android.apex.test_package_2",
1956       /* modulePath= */ apex_path_2, /* preinstalledModulePath= */ apex_path_2,
1957       /* versionCode= */ 1, /* versionName= */ "1", /* isFactory= */ true,
1958       /* isActive= */ true, GetMTime(apex_path_2),
1959       /* provideSharedApexLibs= */ false);
1960   auto apex_info_xml_3 = com::android::apex::ApexInfo(
1961       /* moduleName= */ "com.android.apex.test_package",
1962       /* modulePath= */ apex_path_3,
1963       /* preinstalledModulePath= */ apex_path_1,
1964       /* versionCode= */ 2, /* versionName= */ "2",
1965       /* isFactory= */ false, /* isActive= */ true, GetMTime(apex_path_3),
1966       /* provideSharedApexLibs= */ false);
1967   ASSERT_THAT(info_list->getApexInfo(),
1968               UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1),
1969                                    ApexInfoXmlEq(apex_info_xml_2),
1970                                    ApexInfoXmlEq(apex_info_xml_3)));
1971 }
1972 
TEST_F(ApexdMountTest,OnOtaChrootBootstrapDataHasSameVersion)1973 TEST_F(ApexdMountTest, OnOtaChrootBootstrapDataHasSameVersion) {
1974   std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex");
1975   std::string apex_path_2 =
1976       AddPreInstalledApex("apex.apexd_test_different_app.apex");
1977   std::string apex_path_3 = AddDataApex("apex.apexd_test.apex");
1978 
1979   ASSERT_EQ(OnOtaChrootBootstrap(), 0);
1980 
1981   UnmountOnTearDown(apex_path_2);
1982   UnmountOnTearDown(apex_path_3);
1983 
1984   auto apex_mounts = GetApexMounts();
1985   ASSERT_THAT(apex_mounts,
1986               UnorderedElementsAre("/apex/com.android.apex.test_package",
1987                                    "/apex/com.android.apex.test_package@1",
1988                                    "/apex/com.android.apex.test_package_2",
1989                                    "/apex/com.android.apex.test_package_2@1"));
1990 
1991   ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
1992   auto info_list =
1993       com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
1994   ASSERT_TRUE(info_list.has_value());
1995   auto apex_info_xml_1 = com::android::apex::ApexInfo(
1996       /* moduleName= */ "com.android.apex.test_package",
1997       /* modulePath= */ apex_path_1,
1998       /* preinstalledModulePath= */ apex_path_1,
1999       /* versionCode= */ 1, /* versionName= */ "1",
2000       /* isFactory= */ true, /* isActive= */ false, GetMTime(apex_path_1),
2001       /* provideSharedApexLibs= */ false);
2002   auto apex_info_xml_2 = com::android::apex::ApexInfo(
2003       /* moduleName= */ "com.android.apex.test_package_2",
2004       /* modulePath= */ apex_path_2, /* preinstalledModulePath= */ apex_path_2,
2005       /* versionCode= */ 1, /* versionName= */ "1", /* isFactory= */ true,
2006       /* isActive= */ true, GetMTime(apex_path_2),
2007       /* provideSharedApexLibs= */ false);
2008   auto apex_info_xml_3 = com::android::apex::ApexInfo(
2009       /* moduleName= */ "com.android.apex.test_package",
2010       /* modulePath= */ apex_path_3,
2011       /* preinstalledModulePath= */ apex_path_1,
2012       /* versionCode= */ 1, /* versionName= */ "1",
2013       /* isFactory= */ false, /* isActive= */ true, GetMTime(apex_path_3),
2014       /* provideSharedApexLibs= */ false);
2015   ASSERT_THAT(info_list->getApexInfo(),
2016               UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1),
2017                                    ApexInfoXmlEq(apex_info_xml_2),
2018                                    ApexInfoXmlEq(apex_info_xml_3)));
2019 }
2020 
TEST_F(ApexdMountTest,OnOtaChrootBootstrapSystemHasHigherVersion)2021 TEST_F(ApexdMountTest, OnOtaChrootBootstrapSystemHasHigherVersion) {
2022   std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test_v2.apex");
2023   std::string apex_path_2 =
2024       AddPreInstalledApex("apex.apexd_test_different_app.apex");
2025   AddDataApex("apex.apexd_test.apex");
2026 
2027   ASSERT_EQ(OnOtaChrootBootstrap(), 0);
2028 
2029   UnmountOnTearDown(apex_path_1);
2030   UnmountOnTearDown(apex_path_2);
2031 
2032   auto apex_mounts = GetApexMounts();
2033   ASSERT_THAT(apex_mounts,
2034               UnorderedElementsAre("/apex/com.android.apex.test_package",
2035                                    "/apex/com.android.apex.test_package@2",
2036                                    "/apex/com.android.apex.test_package_2",
2037                                    "/apex/com.android.apex.test_package_2@1"));
2038 
2039   ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
2040   auto info_list =
2041       com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
2042   ASSERT_TRUE(info_list.has_value());
2043   auto apex_info_xml_1 = com::android::apex::ApexInfo(
2044       /* moduleName= */ "com.android.apex.test_package",
2045       /* modulePath= */ apex_path_1,
2046       /* preinstalledModulePath= */ apex_path_1,
2047       /* versionCode= */ 2, /* versionName= */ "2",
2048       /* isFactory= */ true, /* isActive= */ true, GetMTime(apex_path_1),
2049       /* provideSharedApexLibs= */ false);
2050   auto apex_info_xml_2 = com::android::apex::ApexInfo(
2051       /* moduleName= */ "com.android.apex.test_package_2",
2052       /* modulePath= */ apex_path_2, /* preinstalledModulePath= */ apex_path_2,
2053       /* versionCode= */ 1, /* versionName= */ "1", /* isFactory= */ true,
2054       /* isActive= */ true, GetMTime(apex_path_2),
2055       /* provideSharedApexLibs= */ false);
2056 
2057   ASSERT_THAT(info_list->getApexInfo(),
2058               UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1),
2059                                    ApexInfoXmlEq(apex_info_xml_2)));
2060 }
2061 
TEST_F(ApexdMountTest,OnOtaChrootBootstrapDataHasSameVersionButDifferentKey)2062 TEST_F(ApexdMountTest, OnOtaChrootBootstrapDataHasSameVersionButDifferentKey) {
2063   std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex");
2064   std::string apex_path_2 =
2065       AddPreInstalledApex("apex.apexd_test_different_app.apex");
2066   AddDataApex("apex.apexd_test_different_key.apex");
2067 
2068   ASSERT_EQ(OnOtaChrootBootstrap(), 0);
2069 
2070   UnmountOnTearDown(apex_path_1);
2071   UnmountOnTearDown(apex_path_2);
2072 
2073   auto apex_mounts = GetApexMounts();
2074   ASSERT_THAT(apex_mounts,
2075               UnorderedElementsAre("/apex/com.android.apex.test_package",
2076                                    "/apex/com.android.apex.test_package@1",
2077                                    "/apex/com.android.apex.test_package_2",
2078                                    "/apex/com.android.apex.test_package_2@1"));
2079 
2080   ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
2081   auto info_list =
2082       com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
2083   ASSERT_TRUE(info_list.has_value());
2084   auto apex_info_xml_1 = com::android::apex::ApexInfo(
2085       /* moduleName= */ "com.android.apex.test_package",
2086       /* modulePath= */ apex_path_1,
2087       /* preinstalledModulePath= */ apex_path_1,
2088       /* versionCode= */ 1, /* versionName= */ "1",
2089       /* isFactory= */ true, /* isActive= */ true, GetMTime(apex_path_1),
2090       /* provideSharedApexLibs= */ false);
2091   auto apex_info_xml_2 = com::android::apex::ApexInfo(
2092       /* moduleName= */ "com.android.apex.test_package_2",
2093       /* modulePath= */ apex_path_2, /* preinstalledModulePath= */ apex_path_2,
2094       /* versionCode= */ 1, /* versionName= */ "1", /* isFactory= */ true,
2095       /* isActive= */ true, GetMTime(apex_path_2),
2096       /* provideSharedApexLibs= */ false);
2097 
2098   ASSERT_THAT(info_list->getApexInfo(),
2099               UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1),
2100                                    ApexInfoXmlEq(apex_info_xml_2)));
2101 }
2102 
TEST_F(ApexdMountTest,OnOtaChrootBootstrapDataHasHigherVersionButDifferentKey)2103 TEST_F(ApexdMountTest,
2104        OnOtaChrootBootstrapDataHasHigherVersionButDifferentKey) {
2105   std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex");
2106   std::string apex_path_2 =
2107       AddPreInstalledApex("apex.apexd_test_different_app.apex");
2108   std::string apex_path_3 =
2109       AddDataApex("apex.apexd_test_different_key_v2.apex");
2110 
2111   {
2112     auto apex = ApexFile::Open(apex_path_3);
2113     ASSERT_THAT(apex, Ok());
2114     ASSERT_EQ(static_cast<uint64_t>(apex->GetManifest().version()), 2ULL);
2115   }
2116 
2117   ASSERT_EQ(OnOtaChrootBootstrap(), 0);
2118 
2119   UnmountOnTearDown(apex_path_1);
2120   UnmountOnTearDown(apex_path_2);
2121 
2122   auto apex_mounts = GetApexMounts();
2123   ASSERT_THAT(apex_mounts,
2124               UnorderedElementsAre("/apex/com.android.apex.test_package",
2125                                    "/apex/com.android.apex.test_package@1",
2126                                    "/apex/com.android.apex.test_package_2",
2127                                    "/apex/com.android.apex.test_package_2@1"));
2128 
2129   ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
2130   auto info_list =
2131       com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
2132   ASSERT_TRUE(info_list.has_value());
2133   auto apex_info_xml_1 = com::android::apex::ApexInfo(
2134       /* moduleName= */ "com.android.apex.test_package",
2135       /* modulePath= */ apex_path_1,
2136       /* preinstalledModulePath= */ apex_path_1,
2137       /* versionCode= */ 1, /* versionName= */ "1",
2138       /* isFactory= */ true, /* isActive= */ true, GetMTime(apex_path_1),
2139       /* provideSharedApexLibs= */ false);
2140   auto apex_info_xml_2 = com::android::apex::ApexInfo(
2141       /* moduleName= */ "com.android.apex.test_package_2",
2142       /* modulePath= */ apex_path_2, /* preinstalledModulePath= */ apex_path_2,
2143       /* versionCode= */ 1, /* versionName= */ "1", /* isFactory= */ true,
2144       /* isActive= */ true, GetMTime(apex_path_2),
2145       /* provideSharedApexLibs= */ false);
2146 
2147   ASSERT_THAT(info_list->getApexInfo(),
2148               UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1),
2149                                    ApexInfoXmlEq(apex_info_xml_2)));
2150 }
2151 
TEST_F(ApexdMountTest,OnOtaChrootBootstrapDataApexWithoutPreInstalledApex)2152 TEST_F(ApexdMountTest, OnOtaChrootBootstrapDataApexWithoutPreInstalledApex) {
2153   std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex");
2154   AddDataApex("apex.apexd_test_different_app.apex");
2155 
2156   ASSERT_EQ(OnOtaChrootBootstrap(), 0);
2157 
2158   UnmountOnTearDown(apex_path_1);
2159 
2160   auto apex_mounts = GetApexMounts();
2161   ASSERT_THAT(apex_mounts,
2162               UnorderedElementsAre("/apex/com.android.apex.test_package",
2163                                    "/apex/com.android.apex.test_package@1"));
2164 
2165   ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
2166   auto info_list =
2167       com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
2168   ASSERT_TRUE(info_list.has_value());
2169   auto apex_info_xml_1 = com::android::apex::ApexInfo(
2170       /* moduleName= */ "com.android.apex.test_package",
2171       /* modulePath= */ apex_path_1,
2172       /* preinstalledModulePath= */ apex_path_1,
2173       /* versionCode= */ 1, /* versionName= */ "1",
2174       /* isFactory= */ true, /* isActive= */ true, GetMTime(apex_path_1),
2175       /* provideSharedApexLibs= */ false);
2176 
2177   ASSERT_THAT(info_list->getApexInfo(),
2178               UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1)));
2179 }
2180 
TEST_F(ApexdMountTest,OnOtaChrootBootstrapPreInstalledSharedLibsApex)2181 TEST_F(ApexdMountTest, OnOtaChrootBootstrapPreInstalledSharedLibsApex) {
2182   std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex");
2183   std::string apex_path_2 = AddPreInstalledApex(
2184       "com.android.apex.test.sharedlibs_generated.v1.libvX.apex");
2185   std::string apex_path_3 = AddDataApex("apex.apexd_test_v2.apex");
2186 
2187   ASSERT_EQ(OnOtaChrootBootstrap(), 0);
2188 
2189   UnmountOnTearDown(apex_path_2);
2190   UnmountOnTearDown(apex_path_3);
2191 
2192   auto apex_mounts = GetApexMounts();
2193   ASSERT_THAT(apex_mounts,
2194               UnorderedElementsAre("/apex/com.android.apex.test_package",
2195                                    "/apex/com.android.apex.test_package@2",
2196                                    "/apex/com.android.apex.test.sharedlibs@1"));
2197 
2198   ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
2199   auto info_list =
2200       com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
2201   ASSERT_TRUE(info_list.has_value());
2202   auto apex_info_xml_1 = com::android::apex::ApexInfo(
2203       /* moduleName= */ "com.android.apex.test_package",
2204       /* modulePath= */ apex_path_1,
2205       /* preinstalledModulePath= */ apex_path_1,
2206       /* versionCode= */ 1, /* versionName= */ "1",
2207       /* isFactory= */ true, /* isActive= */ false, GetMTime(apex_path_1),
2208       /* provideSharedApexLibs= */ false);
2209   auto apex_info_xml_2 = com::android::apex::ApexInfo(
2210       /* moduleName= */ "com.android.apex.test.sharedlibs",
2211       /* modulePath= */ apex_path_2,
2212       /* preinstalledModulePath= */ apex_path_2,
2213       /* versionCode= */ 1, /* versionName= */ "1",
2214       /* isFactory= */ true, /* isActive= */ true, GetMTime(apex_path_2),
2215       /* provideSharedApexLibs= */ false);
2216   auto apex_info_xml_3 = com::android::apex::ApexInfo(
2217       /* moduleName= */ "com.android.apex.test_package",
2218       /* modulePath= */ apex_path_3,
2219       /* preinstalledModulePath= */ apex_path_1,
2220       /* versionCode= */ 2, /* versionName= */ "2",
2221       /* isFactory= */ false, /* isActive= */ true, GetMTime(apex_path_3),
2222       /* provideSharedApexLibs= */ false);
2223 
2224   ASSERT_THAT(info_list->getApexInfo(),
2225               UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1),
2226                                    ApexInfoXmlEq(apex_info_xml_2),
2227                                    ApexInfoXmlEq(apex_info_xml_3)));
2228 
2229   ASSERT_EQ(access("/apex/sharedlibs", F_OK), 0);
2230 
2231   // Check /apex/sharedlibs is populated properly.
2232   std::vector<std::string> sharedlibs;
2233   for (const auto& p : fs::recursive_directory_iterator("/apex/sharedlibs")) {
2234     if (fs::is_symlink(p)) {
2235       auto src = fs::read_symlink(p.path());
2236       ASSERT_EQ(p.path().filename(), src.filename());
2237       sharedlibs.push_back(p.path().parent_path().string() + "->" +
2238                            src.parent_path().string());
2239     }
2240   }
2241 
2242   std::vector<std::string> expected = {
2243       "/apex/sharedlibs/lib/libsharedlibtest.so->"
2244       "/apex/com.android.apex.test.sharedlibs@1/lib/libsharedlibtest.so",
2245       "/apex/sharedlibs/lib/libc++.so->"
2246       "/apex/com.android.apex.test.sharedlibs@1/lib/libc++.so",
2247   };
2248 
2249   // On 64bit devices we also have lib64.
2250   if (!GetProperty("ro.product.cpu.abilist64", "").empty()) {
2251     expected.push_back(
2252         "/apex/sharedlibs/lib64/libsharedlibtest.so->"
2253         "/apex/com.android.apex.test.sharedlibs@1/lib64/libsharedlibtest.so");
2254     expected.push_back(
2255         "/apex/sharedlibs/lib64/libc++.so->"
2256         "/apex/com.android.apex.test.sharedlibs@1/lib64/libc++.so");
2257   }
2258   ASSERT_THAT(sharedlibs, UnorderedElementsAreArray(expected));
2259 }
2260 
TEST_F(ApexdMountTest,OnOtaChrootBootstrapSharedLibsApexBothVersions)2261 TEST_F(ApexdMountTest, OnOtaChrootBootstrapSharedLibsApexBothVersions) {
2262   std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex");
2263   std::string apex_path_2 = AddPreInstalledApex(
2264       "com.android.apex.test.sharedlibs_generated.v1.libvX.apex");
2265   std::string apex_path_3 = AddDataApex("apex.apexd_test_v2.apex");
2266   std::string apex_path_4 =
2267       AddDataApex("com.android.apex.test.sharedlibs_generated.v2.libvY.apex");
2268 
2269   ASSERT_EQ(OnOtaChrootBootstrap(), 0);
2270 
2271   UnmountOnTearDown(apex_path_2);
2272   UnmountOnTearDown(apex_path_3);
2273   UnmountOnTearDown(apex_path_4);
2274 
2275   auto apex_mounts = GetApexMounts();
2276   ASSERT_THAT(apex_mounts,
2277               UnorderedElementsAre("/apex/com.android.apex.test_package",
2278                                    "/apex/com.android.apex.test_package@2",
2279                                    "/apex/com.android.apex.test.sharedlibs@1",
2280                                    "/apex/com.android.apex.test.sharedlibs@2"));
2281 
2282   ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
2283   auto info_list =
2284       com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
2285   ASSERT_TRUE(info_list.has_value());
2286   auto apex_info_xml_1 = com::android::apex::ApexInfo(
2287       /* moduleName= */ "com.android.apex.test_package",
2288       /* modulePath= */ apex_path_1,
2289       /* preinstalledModulePath= */ apex_path_1,
2290       /* versionCode= */ 1, /* versionName= */ "1",
2291       /* isFactory= */ true, /* isActive= */ false, GetMTime(apex_path_1),
2292       /* provideSharedApexLibs= */ false);
2293   auto apex_info_xml_2 = com::android::apex::ApexInfo(
2294       /* moduleName= */ "com.android.apex.test.sharedlibs",
2295       /* modulePath= */ apex_path_2,
2296       /* preinstalledModulePath= */ apex_path_2,
2297       /* versionCode= */ 1, /* versionName= */ "1",
2298       /* isFactory= */ true, /* isActive= */ false, GetMTime(apex_path_2),
2299       /* provideSharedApexLibs= */ false);
2300   auto apex_info_xml_3 = com::android::apex::ApexInfo(
2301       /* moduleName= */ "com.android.apex.test_package",
2302       /* modulePath= */ apex_path_3,
2303       /* preinstalledModulePath= */ apex_path_1,
2304       /* versionCode= */ 2, /* versionName= */ "2",
2305       /* isFactory= */ false, /* isActive= */ true, GetMTime(apex_path_3),
2306       /* provideSharedApexLibs= */ false);
2307   auto apex_info_xml_4 = com::android::apex::ApexInfo(
2308       /* moduleName= */ "com.android.apex.test.sharedlibs",
2309       /* modulePath= */ apex_path_4,
2310       /* preinstalledModulePath= */ apex_path_2,
2311       /* versionCode= */ 2, /* versionName= */ "2",
2312       /* isFactory= */ false, /* isActive= */ true, GetMTime(apex_path_4),
2313       /* provideSharedApexLibs= */ false);
2314 
2315   ASSERT_THAT(info_list->getApexInfo(),
2316               UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1),
2317                                    ApexInfoXmlEq(apex_info_xml_2),
2318                                    ApexInfoXmlEq(apex_info_xml_3),
2319                                    ApexInfoXmlEq(apex_info_xml_4)));
2320 
2321   ASSERT_EQ(access("/apex/sharedlibs", F_OK), 0);
2322 
2323   // Check /apex/sharedlibs is populated properly.
2324   // Because we don't want to hardcode full paths (they are pretty long and have
2325   // a hash in them which might change if new prebuilts are dropped in), the
2326   // assertion logic is a little bit clunky.
2327   std::vector<std::string> sharedlibs;
2328   for (const auto& p : fs::recursive_directory_iterator("/apex/sharedlibs")) {
2329     if (fs::is_symlink(p)) {
2330       auto src = fs::read_symlink(p.path());
2331       ASSERT_EQ(p.path().filename(), src.filename());
2332       sharedlibs.push_back(p.path().parent_path().string() + "->" +
2333                            src.parent_path().string());
2334     }
2335   }
2336 
2337   std::vector<std::string> expected = {
2338       "/apex/sharedlibs/lib/libsharedlibtest.so->"
2339       "/apex/com.android.apex.test.sharedlibs@2/lib/libsharedlibtest.so",
2340       "/apex/sharedlibs/lib/libsharedlibtest.so->"
2341       "/apex/com.android.apex.test.sharedlibs@1/lib/libsharedlibtest.so",
2342       "/apex/sharedlibs/lib/libc++.so->"
2343       "/apex/com.android.apex.test.sharedlibs@2/lib/libc++.so",
2344   };
2345   // On 64bit devices we also have lib64.
2346   if (!GetProperty("ro.product.cpu.abilist64", "").empty()) {
2347     expected.push_back(
2348         "/apex/sharedlibs/lib64/libsharedlibtest.so->"
2349         "/apex/com.android.apex.test.sharedlibs@2/lib64/libsharedlibtest.so");
2350     expected.push_back(
2351         "/apex/sharedlibs/lib64/libsharedlibtest.so->"
2352         "/apex/com.android.apex.test.sharedlibs@1/lib64/libsharedlibtest.so");
2353     expected.push_back(
2354         "/apex/sharedlibs/lib64/libc++.so->"
2355         "/apex/com.android.apex.test.sharedlibs@2/lib64/libc++.so");
2356   }
2357 
2358   ASSERT_THAT(sharedlibs, UnorderedElementsAreArray(expected));
2359 }
2360 
2361 // Test when we move from uncompressed APEX to CAPEX via ota
TEST_F(ApexdMountTest,OnOtaChrootBootstrapOnlyCompressedApexes)2362 TEST_F(ApexdMountTest, OnOtaChrootBootstrapOnlyCompressedApexes) {
2363   std::string apex_path =
2364       AddPreInstalledApex("com.android.apex.compressed.v1.capex");
2365 
2366   ASSERT_EQ(OnOtaChrootBootstrap(), 0);
2367 
2368   // Decompressed APEX should be mounted from decompression_dir
2369   std::string decompressed_apex =
2370       StringPrintf("%s/com.android.apex.compressed@1%s",
2371                    GetDecompressionDir().c_str(), kOtaApexPackageSuffix);
2372   UnmountOnTearDown(decompressed_apex);
2373 
2374   auto apex_mounts = GetApexMounts();
2375   ASSERT_THAT(apex_mounts,
2376               UnorderedElementsAre("/apex/com.android.apex.compressed",
2377                                    "/apex/com.android.apex.compressed@1"));
2378 
2379   ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
2380   auto info_list =
2381       com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
2382   ASSERT_TRUE(info_list.has_value());
2383   auto apex_info_xml_decompressed = com::android::apex::ApexInfo(
2384       /* moduleName= */ "com.android.apex.compressed",
2385       /* modulePath= */ decompressed_apex,
2386       /* preinstalledModulePath= */ apex_path,
2387       /* versionCode= */ 1, /* versionName= */ "1",
2388       /* isFactory= */ true, /* isActive= */ true, GetMTime(decompressed_apex),
2389       /* provideSharedApexLibs= */ false);
2390   ASSERT_THAT(info_list->getApexInfo(),
2391               UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_decompressed)));
2392   auto& db = GetApexDatabaseForTesting();
2393   // Check that it was mounted from decompressed apex. It should also be mounted
2394   // on dm-verity device.
2395   db.ForallMountedApexes("com.android.apex.compressed",
2396                          [&](const MountedApexData& data, bool latest) {
2397                            ASSERT_TRUE(latest);
2398                            ASSERT_EQ(data.full_path, decompressed_apex);
2399                            ASSERT_EQ(data.device_name,
2400                                      "com.android.apex.compressed@1.chroot");
2401                          });
2402 }
2403 
2404 // Test we decompress only once even if OnOtaChrootBootstrap is called multiple
2405 // times
TEST_F(ApexdMountTest,OnOtaChrootBootstrapDecompressOnlyOnceMultipleCalls)2406 TEST_F(ApexdMountTest, OnOtaChrootBootstrapDecompressOnlyOnceMultipleCalls) {
2407   std::string apex_path =
2408       AddPreInstalledApex("com.android.apex.compressed.v1.capex");
2409 
2410   ASSERT_EQ(OnOtaChrootBootstrap(), 0);
2411 
2412   // Decompressed OTA APEX should be mounted
2413   std::string decompressed_ota_apex =
2414       StringPrintf("%s/com.android.apex.compressed@1%s",
2415                    GetDecompressionDir().c_str(), kOtaApexPackageSuffix);
2416   UnmountOnTearDown(decompressed_ota_apex);
2417 
2418   // Capture the creation time of the OTA APEX
2419   std::error_code ec;
2420   auto last_write_time_1 = fs::last_write_time(decompressed_ota_apex, ec);
2421   ASSERT_FALSE(ec) << "Failed to capture last write time of "
2422                    << decompressed_ota_apex;
2423 
2424   // Call OnOtaChrootBootstrap again. Since we do not hardlink decompressed APEX
2425   // to /data/apex/active directory when in chroot, when selecting apex for
2426   // activation, we will end up selecting compressed APEX again.
2427   ASSERT_EQ(OnOtaChrootBootstrap(), 0);
2428 
2429   // Compare write time to ensure we did not decompress again
2430   auto last_write_time_2 = fs::last_write_time(decompressed_ota_apex, ec);
2431   ASSERT_FALSE(ec) << "Failed to capture last write time of "
2432                    << decompressed_ota_apex << ec.message();
2433   ASSERT_EQ(last_write_time_1, last_write_time_2);
2434 }
2435 
2436 // Test when we upgrade existing CAPEX to higher version via OTA
TEST_F(ApexdMountTest,OnOtaChrootBootstrapUpgradeCapex)2437 TEST_F(ApexdMountTest, OnOtaChrootBootstrapUpgradeCapex) {
2438   TemporaryDir previous_built_in_dir;
2439   PrepareCompressedApex("com.android.apex.compressed.v1.capex",
2440                         previous_built_in_dir.path);
2441   // Place a higher version capex in current built_in_dir
2442   std::string apex_path =
2443       AddPreInstalledApex("com.android.apex.compressed.v2.capex");
2444 
2445   ASSERT_EQ(OnOtaChrootBootstrap(), 0);
2446 
2447   // Upgraded decompressed APEX should be mounted from decompression dir
2448   std::string decompressed_active_apex =
2449       StringPrintf("%s/com.android.apex.compressed@2%s",
2450                    GetDecompressionDir().c_str(), kOtaApexPackageSuffix);
2451   UnmountOnTearDown(decompressed_active_apex);
2452 
2453   auto apex_mounts = GetApexMounts();
2454   ASSERT_THAT(apex_mounts,
2455               UnorderedElementsAre("/apex/com.android.apex.compressed",
2456                                    "/apex/com.android.apex.compressed@2"));
2457 
2458   ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
2459   auto info_list =
2460       com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
2461   ASSERT_TRUE(info_list.has_value());
2462   auto apex_info_xml_decompressed = com::android::apex::ApexInfo(
2463       /* moduleName= */ "com.android.apex.compressed",
2464       /* modulePath= */ decompressed_active_apex,
2465       /* preinstalledModulePath= */ apex_path,
2466       /* versionCode= */ 2, /* versionName= */ "2",
2467       /* isFactory= */ true, /* isActive= */ true,
2468       GetMTime(decompressed_active_apex),
2469       /* provideSharedApexLibs= */ false);
2470   ASSERT_THAT(info_list->getApexInfo(),
2471               UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_decompressed)));
2472   auto& db = GetApexDatabaseForTesting();
2473   // Check that it was mounted from decompressed apex. It should also be mounted
2474   // on dm-verity device.
2475   db.ForallMountedApexes("com.android.apex.compressed",
2476                          [&](const MountedApexData& data, bool latest) {
2477                            ASSERT_TRUE(latest);
2478                            ASSERT_EQ(data.full_path, decompressed_active_apex);
2479                            ASSERT_EQ(data.device_name,
2480                                      "com.android.apex.compressed@2.chroot");
2481                          });
2482 }
2483 
2484 // Test when we update existing CAPEX to same version via OTA
TEST_F(ApexdMountTest,OnOtaChrootBootstrapSamegradeCapex)2485 TEST_F(ApexdMountTest, OnOtaChrootBootstrapSamegradeCapex) {
2486   TemporaryDir previous_built_in_dir;
2487   PrepareCompressedApex("com.android.apex.compressed.v1.capex",
2488                         previous_built_in_dir.path);
2489   // Place a same version capex in current built_in_dir, under a different name
2490   auto apex_path =
2491       StringPrintf("%s/different-name.capex", GetBuiltInDir().c_str());
2492   fs::copy(GetTestFile("com.android.apex.compressed.v1.capex"), apex_path);
2493 
2494   ASSERT_EQ(OnOtaChrootBootstrap(), 0);
2495 
2496   // Previously decompressed APEX should be mounted from decompression_dir
2497   std::string decompressed_active_apex = StringPrintf(
2498       "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(),
2499       kDecompressedApexPackageSuffix);
2500   UnmountOnTearDown(decompressed_active_apex);
2501 
2502   auto apex_mounts = GetApexMounts();
2503   ASSERT_THAT(apex_mounts,
2504               UnorderedElementsAre("/apex/com.android.apex.compressed",
2505                                    "/apex/com.android.apex.compressed@1"));
2506 
2507   ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
2508   auto info_list =
2509       com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
2510   ASSERT_TRUE(info_list.has_value());
2511   auto apex_info_xml_decompressed = com::android::apex::ApexInfo(
2512       /* moduleName= */ "com.android.apex.compressed",
2513       /* modulePath= */ decompressed_active_apex,
2514       /* preinstalledModulePath= */ apex_path,
2515       /* versionCode= */ 1, /* versionName= */ "1",
2516       /* isFactory= */ true, /* isActive= */ true,
2517       GetMTime(decompressed_active_apex),
2518       /* provideSharedApexLibs= */ false);
2519   ASSERT_THAT(info_list->getApexInfo(),
2520               UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_decompressed)));
2521   auto& db = GetApexDatabaseForTesting();
2522   // Check that it was mounted from decompressed apex. It should also be mounted
2523   // on dm-verity device.
2524   db.ForallMountedApexes("com.android.apex.compressed",
2525                          [&](const MountedApexData& data, bool latest) {
2526                            ASSERT_TRUE(latest);
2527                            ASSERT_EQ(data.full_path, decompressed_active_apex);
2528                            ASSERT_EQ(data.device_name,
2529                                      "com.android.apex.compressed@1.chroot");
2530                          });
2531 }
2532 
2533 // Test when we update existing CAPEX to same version, but different digest
TEST_F(ApexdMountTest,OnOtaChrootBootstrapSamegradeCapexDifferentDigest)2534 TEST_F(ApexdMountTest, OnOtaChrootBootstrapSamegradeCapexDifferentDigest) {
2535   TemporaryDir previous_built_in_dir;
2536   auto different_digest_apex_path = PrepareCompressedApex(
2537       "com.android.apex.compressed.v1_different_digest.capex",
2538       previous_built_in_dir.path);
2539   // Place a same version capex in current built_in_dir, which has different
2540   // digest
2541   auto apex_path = AddPreInstalledApex("com.android.apex.compressed.v1.capex");
2542 
2543   ASSERT_EQ(OnOtaChrootBootstrap(), 0);
2544 
2545   // New decompressed ota APEX should be mounted with kOtaApexPackageSuffix
2546   std::string decompressed_ota_apex =
2547       StringPrintf("%s/com.android.apex.compressed@1%s",
2548                    GetDecompressionDir().c_str(), kOtaApexPackageSuffix);
2549   UnmountOnTearDown(decompressed_ota_apex);
2550 
2551   auto apex_mounts = GetApexMounts();
2552   ASSERT_THAT(apex_mounts,
2553               UnorderedElementsAre("/apex/com.android.apex.compressed",
2554                                    "/apex/com.android.apex.compressed@1"));
2555 
2556   ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
2557   auto info_list =
2558       com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
2559   ASSERT_TRUE(info_list.has_value());
2560   auto apex_info_xml_decompressed = com::android::apex::ApexInfo(
2561       /* moduleName= */ "com.android.apex.compressed",
2562       /* modulePath= */ decompressed_ota_apex,
2563       /* preinstalledModulePath= */ apex_path,
2564       /* versionCode= */ 1, /* versionName= */ "1",
2565       /* isFactory= */ true, /* isActive= */ true,
2566       GetMTime(decompressed_ota_apex),
2567       /* provideSharedApexLibs= */ false);
2568   ASSERT_THAT(info_list->getApexInfo(),
2569               UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_decompressed)));
2570   auto& db = GetApexDatabaseForTesting();
2571   // Check that it was mounted from decompressed apex. It should also be mounted
2572   // on dm-verity device.
2573   db.ForallMountedApexes("com.android.apex.compressed",
2574                          [&](const MountedApexData& data, bool latest) {
2575                            ASSERT_TRUE(latest);
2576                            ASSERT_EQ(data.full_path, decompressed_ota_apex);
2577                            ASSERT_EQ(data.device_name,
2578                                      "com.android.apex.compressed@1.chroot");
2579                          });
2580 
2581   // Ensure decompressed apex has same digest as pre-installed
2582   auto pre_installed_apex = ApexFile::Open(apex_path);
2583   auto decompressed_apex = ApexFile::Open(decompressed_ota_apex);
2584   auto different_digest_apex = ApexFile::Open(different_digest_apex_path);
2585   ASSERT_EQ(
2586       pre_installed_apex->GetManifest().capexmetadata().originalapexdigest(),
2587       GetRootDigest(*decompressed_apex));
2588   ASSERT_NE(
2589       pre_installed_apex->GetManifest().capexmetadata().originalapexdigest(),
2590       GetRootDigest(*different_digest_apex));
2591 
2592   // Ensure we didn't remove previous decompressed APEX
2593   std::string previous_decompressed_apex = StringPrintf(
2594       "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(),
2595       kDecompressedApexPackageSuffix);
2596   auto path_exists = PathExists(previous_decompressed_apex);
2597   ASSERT_TRUE(*path_exists);
2598 }
2599 
2600 // Test when we update existing CAPEX to same version, but different key via OTA
TEST_F(ApexdMountTest,OnOtaChrootBootstrapSamegradeCapexDifferentKey)2601 TEST_F(ApexdMountTest, OnOtaChrootBootstrapSamegradeCapexDifferentKey) {
2602   TemporaryDir previous_built_in_dir;
2603   PrepareCompressedApex("com.android.apex.compressed_different_key.capex",
2604                         previous_built_in_dir.path);
2605   // Place a same version capex in current built_in_dir, which has different key
2606   auto apex_path = AddPreInstalledApex("com.android.apex.compressed.v1.capex");
2607 
2608   ASSERT_EQ(OnOtaChrootBootstrap(), 0);
2609 
2610   // New decompressed APEX should be mounted from ota_reserved directory
2611   std::string decompressed_active_apex =
2612       StringPrintf("%s/com.android.apex.compressed@1%s",
2613                    GetDecompressionDir().c_str(), kOtaApexPackageSuffix);
2614   UnmountOnTearDown(decompressed_active_apex);
2615 
2616   auto apex_mounts = GetApexMounts();
2617   ASSERT_THAT(apex_mounts,
2618               UnorderedElementsAre("/apex/com.android.apex.compressed",
2619                                    "/apex/com.android.apex.compressed@1"));
2620 
2621   ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
2622   auto info_list =
2623       com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
2624   ASSERT_TRUE(info_list.has_value());
2625   auto apex_info_xml_decompressed = com::android::apex::ApexInfo(
2626       /* moduleName= */ "com.android.apex.compressed",
2627       /* modulePath= */ decompressed_active_apex,
2628       /* preinstalledModulePath= */ apex_path,
2629       /* versionCode= */ 1, /* versionName= */ "1",
2630       /* isFactory= */ true, /* isActive= */ true,
2631       GetMTime(decompressed_active_apex),
2632       /* provideSharedApexLibs= */ false);
2633   ASSERT_THAT(info_list->getApexInfo(),
2634               UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_decompressed)));
2635   auto& db = GetApexDatabaseForTesting();
2636   // Check that it was mounted from decompressed apex. It should also be mounted
2637   // on dm-verity device.
2638   db.ForallMountedApexes("com.android.apex.compressed",
2639                          [&](const MountedApexData& data, bool latest) {
2640                            ASSERT_TRUE(latest);
2641                            ASSERT_EQ(data.full_path, decompressed_active_apex);
2642                            ASSERT_EQ(data.device_name,
2643                                      "com.android.apex.compressed@1.chroot");
2644                          });
2645 }
2646 
2647 // Test when we remove CAPEX via OTA
TEST_F(ApexdMountTest,OnOtaChrootBootstrapCapexToApex)2648 TEST_F(ApexdMountTest, OnOtaChrootBootstrapCapexToApex) {
2649   TemporaryDir previous_built_in_dir;
2650   PrepareCompressedApex("com.android.apex.compressed.v1.capex",
2651                         previous_built_in_dir.path);
2652   // Place a uncompressed version apex in current built_in_dir
2653   std::string apex_path =
2654       AddPreInstalledApex("com.android.apex.compressed.v1_original.apex");
2655 
2656   ASSERT_EQ(OnOtaChrootBootstrap(), 0);
2657 
2658   // New uncompressed APEX should be mounted
2659   UnmountOnTearDown(apex_path);
2660 
2661   auto apex_mounts = GetApexMounts();
2662   ASSERT_THAT(apex_mounts,
2663               UnorderedElementsAre("/apex/com.android.apex.compressed",
2664                                    "/apex/com.android.apex.compressed@1"));
2665 
2666   ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
2667   auto info_list =
2668       com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
2669   ASSERT_TRUE(info_list.has_value());
2670   auto apex_info_xml_uncompressed = com::android::apex::ApexInfo(
2671       /* moduleName= */ "com.android.apex.compressed",
2672       /* modulePath= */ apex_path,
2673       /* preinstalledModulePath= */ apex_path,
2674       /* versionCode= */ 1, /* versionName= */ "1",
2675       /* isFactory= */ true, /* isActive= */ true, GetMTime(apex_path),
2676       /* provideSharedApexLibs= */ false);
2677   ASSERT_THAT(info_list->getApexInfo(),
2678               UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_uncompressed)));
2679 }
2680 
TEST_F(ApexdMountTest,OnOtaChrootBootstrapDecompressedApexVersionDifferentThanCapex)2681 TEST_F(ApexdMountTest,
2682        OnOtaChrootBootstrapDecompressedApexVersionDifferentThanCapex) {
2683   TemporaryDir previous_built_in_dir;
2684   PrepareCompressedApex("com.android.apex.compressed.v2.capex",
2685                         previous_built_in_dir.path);
2686   // Place a lower version capex in current built_in_dir, so that previously
2687   // decompressed APEX has higher version but still doesn't get picked during
2688   // selection.
2689   std::string apex_path =
2690       AddPreInstalledApex("com.android.apex.compressed.v1.capex");
2691 
2692   ASSERT_EQ(OnOtaChrootBootstrap(), 0);
2693 
2694   // Pre-installed CAPEX should be decompressed again and mounted from
2695   // decompression_dir
2696   std::string decompressed_active_apex =
2697       StringPrintf("%s/com.android.apex.compressed@1%s",
2698                    GetDecompressionDir().c_str(), kOtaApexPackageSuffix);
2699   UnmountOnTearDown(decompressed_active_apex);
2700 
2701   auto apex_mounts = GetApexMounts();
2702   ASSERT_THAT(apex_mounts,
2703               UnorderedElementsAre("/apex/com.android.apex.compressed",
2704                                    "/apex/com.android.apex.compressed@1"));
2705 
2706   ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
2707   auto info_list =
2708       com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
2709   ASSERT_TRUE(info_list.has_value());
2710   auto apex_info_xml_decompressed = com::android::apex::ApexInfo(
2711       /* moduleName= */ "com.android.apex.compressed",
2712       /* modulePath= */ decompressed_active_apex,
2713       /* preinstalledModulePath= */ apex_path,
2714       /* versionCode= */ 1, /* versionName= */ "1",
2715       /* isFactory= */ true, /* isActive= */ true,
2716       GetMTime(decompressed_active_apex),
2717       /* provideSharedApexLibs= */ false);
2718   ASSERT_THAT(info_list->getApexInfo(),
2719               UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_decompressed)));
2720 }
2721 
2722 // Test when we update CAPEX and there is a higher version present in data
TEST_F(ApexdMountTest,OnOtaChrootBootstrapDataHigherThanCapex)2723 TEST_F(ApexdMountTest, OnOtaChrootBootstrapDataHigherThanCapex) {
2724   auto system_apex_path =
2725       PrepareCompressedApex("com.android.apex.compressed.v1.capex");
2726   auto data_apex_path =
2727       AddDataApex("com.android.apex.compressed.v2_original.apex");
2728 
2729   ASSERT_EQ(OnOtaChrootBootstrap(), 0);
2730 
2731   // Data APEX should be mounted
2732   UnmountOnTearDown(data_apex_path);
2733 
2734   auto apex_mounts = GetApexMounts();
2735   ASSERT_THAT(apex_mounts,
2736               UnorderedElementsAre("/apex/com.android.apex.compressed",
2737                                    "/apex/com.android.apex.compressed@2"));
2738 
2739   ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
2740   auto info_list =
2741       com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
2742   ASSERT_TRUE(info_list.has_value());
2743   auto apex_info_xml_data = com::android::apex::ApexInfo(
2744       /* moduleName= */ "com.android.apex.compressed",
2745       /* modulePath= */ data_apex_path,
2746       /* preinstalledModulePath= */ system_apex_path,
2747       /* versionCode= */ 2, /* versionName= */ "2",
2748       /* isFactory= */ false, /* isActive= */ true, GetMTime(data_apex_path),
2749       /* provideSharedApexLibs= */ false);
2750   auto apex_info_xml_system = com::android::apex::ApexInfo(
2751       /* moduleName= */ "com.android.apex.compressed",
2752       /* modulePath= */ system_apex_path,
2753       /* preinstalledModulePath= */ system_apex_path,
2754       /* versionCode= */ 1, /* versionName= */ "1",
2755       /* isFactory= */ true, /* isActive= */ false, GetMTime(system_apex_path),
2756       /* provideSharedApexLibs= */ false);
2757   ASSERT_THAT(info_list->getApexInfo(),
2758               UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_data),
2759                                    ApexInfoXmlEq(apex_info_xml_system)));
2760   auto& db = GetApexDatabaseForTesting();
2761   // Check that it was mounted from decompressed apex. It should also be mounted
2762   // on dm-verity device.
2763   db.ForallMountedApexes("com.android.apex.compressed",
2764                          [&](const MountedApexData& data, bool latest) {
2765                            ASSERT_TRUE(latest);
2766                            ASSERT_EQ(data.full_path, data_apex_path);
2767                            ASSERT_EQ(data.device_name,
2768                                      "com.android.apex.compressed@2.chroot");
2769                          });
2770 }
2771 
2772 // Test when we update CAPEX and there is a lower version present in data
TEST_F(ApexdMountTest,OnOtaChrootBootstrapDataLowerThanCapex)2773 TEST_F(ApexdMountTest, OnOtaChrootBootstrapDataLowerThanCapex) {
2774   auto apex_path = AddPreInstalledApex("com.android.apex.compressed.v2.capex");
2775   AddDataApex("com.android.apex.compressed.v1_original.apex");
2776 
2777   ASSERT_EQ(OnOtaChrootBootstrap(), 0);
2778 
2779   // Decompressed APEX should be mounted from reserved dir
2780   std::string decompressed_active_apex =
2781       StringPrintf("%s/com.android.apex.compressed@2%s",
2782                    GetDecompressionDir().c_str(), kOtaApexPackageSuffix);
2783   UnmountOnTearDown(decompressed_active_apex);
2784 
2785   auto apex_mounts = GetApexMounts();
2786   ASSERT_THAT(apex_mounts,
2787               UnorderedElementsAre("/apex/com.android.apex.compressed",
2788                                    "/apex/com.android.apex.compressed@2"));
2789 
2790   ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
2791   auto info_list =
2792       com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
2793   ASSERT_TRUE(info_list.has_value());
2794   auto apex_info_xml = com::android::apex::ApexInfo(
2795       /* moduleName= */ "com.android.apex.compressed",
2796       /* modulePath= */ decompressed_active_apex,
2797       /* preinstalledModulePath= */ apex_path,
2798       /* versionCode= */ 2, /* versionName= */ "2",
2799       /* isFactory= */ true, /* isActive= */ true,
2800       GetMTime(decompressed_active_apex),
2801       /* provideSharedApexLibs= */ false);
2802   ASSERT_THAT(info_list->getApexInfo(),
2803               UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml)));
2804   auto& db = GetApexDatabaseForTesting();
2805   // Check that it was mounted from decompressed apex. It should also be mounted
2806   // on dm-verity device.
2807   db.ForallMountedApexes("com.android.apex.compressed",
2808                          [&](const MountedApexData& data, bool latest) {
2809                            ASSERT_TRUE(latest);
2810                            ASSERT_EQ(data.full_path, decompressed_active_apex);
2811                            ASSERT_EQ(data.device_name,
2812                                      "com.android.apex.compressed@2.chroot");
2813                          });
2814 }
2815 
2816 // Test when we update CAPEX and there is a same version present in data
TEST_F(ApexdMountTest,OnOtaChrootBootstrapDataSameAsCapex)2817 TEST_F(ApexdMountTest, OnOtaChrootBootstrapDataSameAsCapex) {
2818   auto system_apex_path =
2819       PrepareCompressedApex("com.android.apex.compressed.v1.capex");
2820   auto data_apex_path =
2821       AddDataApex("com.android.apex.compressed.v1_original.apex");
2822 
2823   ASSERT_EQ(OnOtaChrootBootstrap(), 0);
2824 
2825   // Data APEX should be mounted
2826   UnmountOnTearDown(data_apex_path);
2827 
2828   auto apex_mounts = GetApexMounts();
2829   ASSERT_THAT(apex_mounts,
2830               UnorderedElementsAre("/apex/com.android.apex.compressed",
2831                                    "/apex/com.android.apex.compressed@1"));
2832 
2833   ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
2834   auto info_list =
2835       com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
2836   ASSERT_TRUE(info_list.has_value());
2837   auto apex_info_xml_data = com::android::apex::ApexInfo(
2838       /* moduleName= */ "com.android.apex.compressed",
2839       /* modulePath= */ data_apex_path,
2840       /* preinstalledModulePath= */ system_apex_path,
2841       /* versionCode= */ 1, /* versionName= */ "1",
2842       /* isFactory= */ false, /* isActive= */ true, GetMTime(data_apex_path),
2843       /* provideSharedApexLibs= */ false);
2844   auto apex_info_xml_system = com::android::apex::ApexInfo(
2845       /* moduleName= */ "com.android.apex.compressed",
2846       /* modulePath= */ system_apex_path,
2847       /* preinstalledModulePath= */ system_apex_path,
2848       /* versionCode= */ 1, /* versionName= */ "1",
2849       /* isFactory= */ true, /* isActive= */ false, GetMTime(system_apex_path),
2850       /* provideSharedApexLibs= */ false);
2851   ASSERT_THAT(info_list->getApexInfo(),
2852               UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_data),
2853                                    ApexInfoXmlEq(apex_info_xml_system)));
2854   auto& db = GetApexDatabaseForTesting();
2855   // Check that it was mounted from decompressed apex. It should also be mounted
2856   // on dm-verity device.
2857   db.ForallMountedApexes("com.android.apex.compressed",
2858                          [&](const MountedApexData& data, bool latest) {
2859                            ASSERT_TRUE(latest);
2860                            ASSERT_EQ(data.full_path, data_apex_path);
2861                            ASSERT_EQ(data.device_name,
2862                                      "com.android.apex.compressed@1.chroot");
2863                          });
2864 }
2865 
TEST_F(ApexdMountTest,OnOtaChrootBootstrapDataHasDifferentKeyThanCapex)2866 TEST_F(ApexdMountTest, OnOtaChrootBootstrapDataHasDifferentKeyThanCapex) {
2867   AddDataApex("com.android.apex.compressed_different_key.capex");
2868   // Place a same version capex in current built_in_dir, which has different key
2869   auto apex_path = AddPreInstalledApex("com.android.apex.compressed.v1.capex");
2870 
2871   ASSERT_EQ(OnOtaChrootBootstrap(), 0);
2872 
2873   // New decompressed APEX should be mounted from ota_reserved directory
2874   std::string decompressed_active_apex =
2875       StringPrintf("%s/com.android.apex.compressed@1%s",
2876                    GetDecompressionDir().c_str(), kOtaApexPackageSuffix);
2877   UnmountOnTearDown(decompressed_active_apex);
2878 
2879   auto apex_mounts = GetApexMounts();
2880   ASSERT_THAT(apex_mounts,
2881               UnorderedElementsAre("/apex/com.android.apex.compressed",
2882                                    "/apex/com.android.apex.compressed@1"));
2883 
2884   ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
2885   auto info_list =
2886       com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
2887   ASSERT_TRUE(info_list.has_value());
2888   auto apex_info_xml_decompressed = com::android::apex::ApexInfo(
2889       /* moduleName= */ "com.android.apex.compressed",
2890       /* modulePath= */ decompressed_active_apex,
2891       /* preinstalledModulePath= */ apex_path,
2892       /* versionCode= */ 1, /* versionName= */ "1",
2893       /* isFactory= */ true, /* isActive= */ true,
2894       GetMTime(decompressed_active_apex),
2895       /* provideSharedApexLibs= */ false);
2896   ASSERT_THAT(info_list->getApexInfo(),
2897               UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_decompressed)));
2898   auto& db = GetApexDatabaseForTesting();
2899   // Check that it was mounted from decompressed apex. It should also be mounted
2900   // on dm-verity device.
2901   db.ForallMountedApexes("com.android.apex.compressed",
2902                          [&](const MountedApexData& data, bool latest) {
2903                            ASSERT_TRUE(latest);
2904                            ASSERT_EQ(data.full_path, decompressed_active_apex);
2905                            ASSERT_EQ(data.device_name,
2906                                      "com.android.apex.compressed@1.chroot");
2907                          });
2908 }
2909 
GetSelinuxContext(const std::string & file)2910 static std::string GetSelinuxContext(const std::string& file) {
2911   char* ctx;
2912   if (getfilecon(file.c_str(), &ctx) < 0) {
2913     PLOG(ERROR) << "Failed to getfilecon " << file;
2914     return "";
2915   }
2916   std::string result(ctx);
2917   freecon(ctx);
2918   return result;
2919 }
2920 
TEST_F(ApexdMountTest,OnOtaChrootBootstrapSelinuxLabelsAreCorrect)2921 TEST_F(ApexdMountTest, OnOtaChrootBootstrapSelinuxLabelsAreCorrect) {
2922   std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex");
2923   std::string apex_path_2 = AddPreInstalledApex(
2924       "com.android.apex.test.sharedlibs_generated.v1.libvX.apex");
2925   std::string apex_path_3 = AddDataApex("apex.apexd_test_v2.apex");
2926 
2927   UnmountOnTearDown(apex_path_2);
2928   UnmountOnTearDown(apex_path_3);
2929   ASSERT_EQ(OnOtaChrootBootstrap(), 0);
2930 
2931   EXPECT_EQ(GetSelinuxContext("/apex/apex-info-list.xml"),
2932             "u:object_r:apex_info_file:s0");
2933 
2934   EXPECT_EQ(GetSelinuxContext("/apex/sharedlibs"),
2935             "u:object_r:apex_mnt_dir:s0");
2936 
2937   EXPECT_EQ(GetSelinuxContext("/apex/com.android.apex.test_package"),
2938             "u:object_r:system_file:s0");
2939   EXPECT_EQ(GetSelinuxContext("/apex/com.android.apex.test_package@2"),
2940             "u:object_r:system_file:s0");
2941 }
2942 
TEST_F(ApexdMountTest,OnOtaChrootBootstrapDmDevicesHaveCorrectName)2943 TEST_F(ApexdMountTest, OnOtaChrootBootstrapDmDevicesHaveCorrectName) {
2944   std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex");
2945   std::string apex_path_2 =
2946       AddPreInstalledApex("apex.apexd_test_different_app.apex");
2947   std::string apex_path_3 = AddDataApex("apex.apexd_test_v2.apex");
2948 
2949   ASSERT_EQ(OnOtaChrootBootstrap(), 0);
2950   UnmountOnTearDown(apex_path_2);
2951   UnmountOnTearDown(apex_path_3);
2952 
2953   MountedApexDatabase& db = GetApexDatabaseForTesting();
2954   // com.android.apex.test_package_2 should be mounted directly on top of loop
2955   // device.
2956   db.ForallMountedApexes("com.android.apex.test_package_2",
2957                          [&](const MountedApexData& data, bool latest) {
2958                            ASSERT_TRUE(latest);
2959                            ASSERT_THAT(data.device_name, IsEmpty());
2960                            ASSERT_THAT(data.loop_name, StartsWith("/dev"));
2961                          });
2962   // com.android.apex.test_package should be mounted on top of dm-verity device.
2963   db.ForallMountedApexes("com.android.apex.test_package",
2964                          [&](const MountedApexData& data, bool latest) {
2965                            ASSERT_TRUE(latest);
2966                            ASSERT_EQ(data.device_name,
2967                                      "com.android.apex.test_package@2.chroot");
2968                            ASSERT_THAT(data.loop_name, StartsWith("/dev"));
2969                          });
2970 }
2971 
TEST_F(ApexdMountTest,OnOtaChrootBootstrapFailsToActivatePreInstalledApexKeepsGoing)2972 TEST_F(ApexdMountTest,
2973        OnOtaChrootBootstrapFailsToActivatePreInstalledApexKeepsGoing) {
2974   std::string apex_path_1 =
2975       AddPreInstalledApex("apex.apexd_test_manifest_mismatch.apex");
2976   std::string apex_path_2 =
2977       AddPreInstalledApex("apex.apexd_test_different_app.apex");
2978 
2979   ASSERT_EQ(OnOtaChrootBootstrap(), 0);
2980   UnmountOnTearDown(apex_path_2);
2981 
2982   auto apex_mounts = GetApexMounts();
2983   ASSERT_THAT(apex_mounts,
2984               UnorderedElementsAre("/apex/com.android.apex.test_package_2",
2985                                    "/apex/com.android.apex.test_package_2@1"));
2986 
2987   ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
2988   auto info_list =
2989       com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
2990   ASSERT_TRUE(info_list.has_value());
2991   auto apex_info_xml_1 = com::android::apex::ApexInfo(
2992       /* moduleName= */ "com.android.apex.test_package",
2993       /* modulePath= */ apex_path_1,
2994       /* preinstalledModulePath= */ apex_path_1,
2995       /* versionCode= */ 137, /* versionName= */ "1",
2996       /* isFactory= */ true, /* isActive= */ false, GetMTime(apex_path_1),
2997       /* provideSharedApexLibs= */ false);
2998   auto apex_info_xml_2 = com::android::apex::ApexInfo(
2999       /* moduleName= */ "com.android.apex.test_package_2",
3000       /* modulePath= */ apex_path_2, /* preinstalledModulePath= */ apex_path_2,
3001       /* versionCode= */ 1, /* versionName= */ "1", /* isFactory= */ true,
3002       /* isActive= */ true, GetMTime(apex_path_2),
3003       /* provideSharedApexLibs= */ false);
3004 
3005   ASSERT_THAT(info_list->getApexInfo(),
3006               UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1),
3007                                    ApexInfoXmlEq(apex_info_xml_2)));
3008 }
3009 
TEST_F(ApexdMountTest,OnOtaChrootBootstrapFailsToActivateDataApexFallsBackToPreInstalled)3010 TEST_F(ApexdMountTest,
3011        OnOtaChrootBootstrapFailsToActivateDataApexFallsBackToPreInstalled) {
3012   std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex");
3013   std::string apex_path_2 =
3014       AddPreInstalledApex("apex.apexd_test_different_app.apex");
3015   std::string apex_path_3 =
3016       AddDataApex("apex.apexd_test_manifest_mismatch.apex");
3017 
3018   ASSERT_EQ(OnOtaChrootBootstrap(), 0);
3019   UnmountOnTearDown(apex_path_1);
3020   UnmountOnTearDown(apex_path_2);
3021 
3022   auto apex_mounts = GetApexMounts();
3023   ASSERT_THAT(apex_mounts,
3024               UnorderedElementsAre("/apex/com.android.apex.test_package",
3025                                    "/apex/com.android.apex.test_package@1",
3026                                    "/apex/com.android.apex.test_package_2",
3027                                    "/apex/com.android.apex.test_package_2@1"));
3028 
3029   ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
3030   auto info_list =
3031       com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
3032   ASSERT_TRUE(info_list.has_value());
3033   auto apex_info_xml_1 = com::android::apex::ApexInfo(
3034       /* moduleName= */ "com.android.apex.test_package",
3035       /* modulePath= */ apex_path_1,
3036       /* preinstalledModulePath= */ apex_path_1,
3037       /* versionCode= */ 1, /* versionName= */ "1",
3038       /* isFactory= */ true, /* isActive= */ true, GetMTime(apex_path_1),
3039       /* provideSharedApexLibs= */ false);
3040   auto apex_info_xml_2 = com::android::apex::ApexInfo(
3041       /* moduleName= */ "com.android.apex.test_package_2",
3042       /* modulePath= */ apex_path_2, /* preinstalledModulePath= */ apex_path_2,
3043       /* versionCode= */ 1, /* versionName= */ "1", /* isFactory= */ true,
3044       /* isActive= */ true, GetMTime(apex_path_2),
3045       /* provideSharedApexLibs= */ false);
3046 
3047   ASSERT_THAT(info_list->getApexInfo(),
3048               UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1),
3049                                    ApexInfoXmlEq(apex_info_xml_2)));
3050 }
3051 
PrepareFlattenedApex(const std::string & apex_dir,const std::string & apex_name,int version)3052 void PrepareFlattenedApex(const std::string& apex_dir,
3053                           const std::string& apex_name, int version) {
3054   ASSERT_EQ(mkdir(apex_dir.c_str(), 0755), 0);
3055 
3056   ::apex::proto::ApexManifest manifest;
3057   manifest.set_name(apex_name);
3058   manifest.set_version(version);
3059   manifest.set_versionname(std::to_string(version));
3060 
3061   std::string out;
3062   manifest.SerializeToString(&out);
3063   ASSERT_TRUE(WriteStringToFile(out, apex_dir + "/apex_manifest.pb"));
3064 }
3065 
TEST_F(ApexdMountTest,ActivateFlattenedApex)3066 TEST_F(ApexdMountTest, ActivateFlattenedApex) {
3067   std::string apex_dir_1 = GetBuiltInDir() + "/com.android.apex.test_package";
3068   std::string apex_dir_2 = GetBuiltInDir() + "/com.android.apex.test_package_2";
3069   PrepareFlattenedApex(apex_dir_1, "com.android.apex.test_package", 2);
3070   PrepareFlattenedApex(apex_dir_2, "com.android.apex.test_package_2", 1);
3071 
3072   ASSERT_EQ(ActivateFlattenedApex(), 0);
3073 
3074   auto apex_mounts = GetApexMounts();
3075   ASSERT_THAT(apex_mounts,
3076               UnorderedElementsAre("/apex/com.android.apex.test_package",
3077                                    "/apex/com.android.apex.test_package_2"));
3078 
3079   ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
3080   ASSERT_EQ(GetSelinuxContext("/apex/apex-info-list.xml"),
3081             "u:object_r:apex_info_file:s0");
3082 
3083   auto info_list =
3084       com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
3085   ASSERT_TRUE(info_list.has_value());
3086   auto apex_info_xml_1 = com::android::apex::ApexInfo(
3087       /* moduleName= */ "com.android.apex.test_package",
3088       /* modulePath= */ apex_dir_1,
3089       /* preinstalledModulePath= */ apex_dir_1,
3090       /* versionCode= */ 2, /* versionName= */ "2",
3091       /* isFactory= */ true, /* isActive= */ true,
3092       /* lastUpdateMillis= */ 0,
3093       /* provideSharedApexLibs= */ false);
3094   auto apex_info_xml_2 = com::android::apex::ApexInfo(
3095       /* moduleName= */ "com.android.apex.test_package_2",
3096       /* modulePath= */ apex_dir_2,
3097       /* preinstalledModulePath= */ apex_dir_2,
3098       /* versionCode= */ 1, /* versionName= */ "1",
3099       /* isFactory= */ true, /* isActive= */ true,
3100       /* lastUpdateMillis= */ 0,
3101       /* provideSharedApexLibs= */ false);
3102 
3103   ASSERT_THAT(info_list->getApexInfo(),
3104               UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1),
3105                                    ApexInfoXmlEq(apex_info_xml_2)));
3106 }
3107 
TEST_F(ApexdMountTest,ActivateFlattenedApexShouldFailWithDuplicate)3108 TEST_F(ApexdMountTest, ActivateFlattenedApexShouldFailWithDuplicate) {
3109   // Two flattened APEXes with the same name
3110   PrepareFlattenedApex(GetBuiltInDir() + "/com.android.apex.test_package",
3111                        "com.android.apex.test_package", 1);
3112   PrepareFlattenedApex(GetBuiltInDir() + "/com.android.apex.test_package_2",
3113                        "com.android.apex.test_package", 1);
3114 
3115   CaptureStderr();
3116   ASSERT_EQ(ActivateFlattenedApex(), 1);
3117   std::string error = GetCapturedStderr();
3118   ASSERT_THAT(error,
3119               HasSubstr("duplicate of com.android.apex.test_package found"));
3120 }
3121 
TEST_F(ApexdMountTest,ActivateFlattenedApexSupportsMultiApex)3122 TEST_F(ApexdMountTest, ActivateFlattenedApexSupportsMultiApex) {
3123   auto apex_dir = GetBuiltInDir() + "/com.android.apex.test_package";
3124   // Two flattened APEXes with the same name
3125   PrepareFlattenedApex(apex_dir, "com.android.apex.test_package", 1);
3126   PrepareFlattenedApex(apex_dir + "_something_else",
3127                        "com.android.apex.test_package", 1);
3128 
3129   // With sysprop indicating multi-apex
3130   std::string property_prefix = "debug.apexd.test.persistprefix.";
3131   android::base::SetProperty(property_prefix + "com.android.apex.test_package",
3132                              "com.android.apex.test_package");
3133 
3134   ASSERT_EQ(ActivateFlattenedApex({property_prefix}), 0);  // Succeeds
3135 
3136   // apex-info-list.xml should have original paths (realpaths) not symlinks
3137   auto info_list =
3138       com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
3139   ASSERT_TRUE(info_list.has_value());
3140   auto apex_info = com::android::apex::ApexInfo(
3141       /* moduleName= */ "com.android.apex.test_package",
3142       /* modulePath= */ apex_dir,
3143       /* preinstalledModulePath= */ apex_dir,
3144       /* versionCode= */ 1, /* versionName= */ "1",
3145       /* isFactory= */ true, /* isActive= */ true,
3146       /* lastUpdateMillis= */ 0,
3147       /* provideSharedApexLibs= */ false);
3148   ASSERT_THAT(info_list->getApexInfo(), ElementsAre(ApexInfoXmlEq(apex_info)));
3149 
3150   android::base::SetProperty(property_prefix + "com.android.apex.test_package",
3151                              "");
3152 }
3153 
TEST_F(ApexdMountTest,ActivateFlattenedApexShouldHaveRealPaths)3154 TEST_F(ApexdMountTest, ActivateFlattenedApexShouldHaveRealPaths) {
3155   // Prepare flattened apexes somewhere else
3156   TemporaryDir dir;
3157   auto apex_dir_1 = fmt::format("{}/apex1", dir.path);
3158   auto apex_dir_2 = fmt::format("{}/apex2", dir.path);
3159   PrepareFlattenedApex(apex_dir_1, "com.android.apex.test_package", 2);
3160   PrepareFlattenedApex(apex_dir_2, "com.android.apex.test_package_2", 1);
3161 
3162   // Symlink flattened apexes under builtin dir.
3163   auto symlink_apex_dir1 = fmt::format("{}/apex1", GetBuiltInDir());
3164   auto symlink_apex_dir2 = fmt::format("{}/apex2", GetBuiltInDir());
3165   ASSERT_EQ(0, symlink(apex_dir_1.c_str(), symlink_apex_dir1.c_str()));
3166   ASSERT_EQ(0, symlink(apex_dir_2.c_str(), symlink_apex_dir2.c_str()));
3167 
3168   ASSERT_EQ(ActivateFlattenedApex(), 0);
3169 
3170   // apex-info-list.xml should have original paths (realpaths) not symlinks
3171   auto info_list =
3172       com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
3173   ASSERT_TRUE(info_list.has_value());
3174   auto apex_info_xml_1 = com::android::apex::ApexInfo(
3175       /* moduleName= */ "com.android.apex.test_package",
3176       /* modulePath= */ apex_dir_1,
3177       /* preinstalledModulePath= */ apex_dir_1,
3178       /* versionCode= */ 2, /* versionName= */ "2",
3179       /* isFactory= */ true, /* isActive= */ true,
3180       /* lastUpdateMillis= */ 0,
3181       /* provideSharedApexLibs= */ false);
3182   auto apex_info_xml_2 = com::android::apex::ApexInfo(
3183       /* moduleName= */ "com.android.apex.test_package_2",
3184       /* modulePath= */ apex_dir_2,
3185       /* preinstalledModulePath= */ apex_dir_2,
3186       /* versionCode= */ 1, /* versionName= */ "1",
3187       /* isFactory= */ true, /* isActive= */ true,
3188       /* lastUpdateMillis= */ 0,
3189       /* provideSharedApexLibs= */ false);
3190 
3191   ASSERT_THAT(info_list->getApexInfo(),
3192               UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1),
3193                                    ApexInfoXmlEq(apex_info_xml_2)));
3194 }
3195 
TEST_F(ApexdMountTest,OnStartOnlyPreInstalledApexes)3196 TEST_F(ApexdMountTest, OnStartOnlyPreInstalledApexes) {
3197   MockCheckpointInterface checkpoint_interface;
3198   // Need to call InitializeVold before calling OnStart
3199   InitializeVold(&checkpoint_interface);
3200 
3201   std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex");
3202   std::string apex_path_2 =
3203       AddPreInstalledApex("apex.apexd_test_different_app.apex");
3204 
3205   ASSERT_THAT(
3206       ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}),
3207       Ok());
3208 
3209   OnStart();
3210 
3211   UnmountOnTearDown(apex_path_1);
3212   UnmountOnTearDown(apex_path_2);
3213 
3214   ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting");
3215   auto apex_mounts = GetApexMounts();
3216   ASSERT_THAT(apex_mounts,
3217               UnorderedElementsAre("/apex/com.android.apex.test_package",
3218                                    "/apex/com.android.apex.test_package@1",
3219                                    "/apex/com.android.apex.test_package_2",
3220                                    "/apex/com.android.apex.test_package_2@1"));
3221 }
3222 
TEST_F(ApexdMountTest,OnStartDataHasHigherVersion)3223 TEST_F(ApexdMountTest, OnStartDataHasHigherVersion) {
3224   MockCheckpointInterface checkpoint_interface;
3225   // Need to call InitializeVold before calling OnStart
3226   InitializeVold(&checkpoint_interface);
3227 
3228   AddPreInstalledApex("apex.apexd_test.apex");
3229   std::string apex_path_2 =
3230       AddPreInstalledApex("apex.apexd_test_different_app.apex");
3231   std::string apex_path_3 = AddDataApex("apex.apexd_test_v2.apex");
3232 
3233   ASSERT_THAT(
3234       ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}),
3235       Ok());
3236 
3237   OnStart();
3238 
3239   UnmountOnTearDown(apex_path_2);
3240   UnmountOnTearDown(apex_path_3);
3241 
3242   ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting");
3243   auto apex_mounts = GetApexMounts();
3244   ASSERT_THAT(apex_mounts,
3245               UnorderedElementsAre("/apex/com.android.apex.test_package",
3246                                    "/apex/com.android.apex.test_package@2",
3247                                    "/apex/com.android.apex.test_package_2",
3248                                    "/apex/com.android.apex.test_package_2@1"));
3249 }
3250 
TEST_F(ApexdMountTest,OnStartDataHasWrongSHA)3251 TEST_F(ApexdMountTest, OnStartDataHasWrongSHA) {
3252   MockCheckpointInterface checkpoint_interface;
3253   // Need to call InitializeVold before calling OnStart
3254   InitializeVold(&checkpoint_interface);
3255 
3256   std::string apex_path = AddPreInstalledApex("com.android.apex.cts.shim.apex");
3257   AddDataApex("com.android.apex.cts.shim.v2_wrong_sha.apex");
3258 
3259   ASSERT_THAT(
3260       ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}),
3261       Ok());
3262 
3263   UnmountOnTearDown(apex_path);
3264   OnStart();
3265 
3266   // Check system shim apex is activated instead of the data one.
3267   auto apex_mounts = GetApexMounts();
3268   ASSERT_THAT(apex_mounts,
3269               UnorderedElementsAre("/apex/com.android.apex.cts.shim",
3270                                    "/apex/com.android.apex.cts.shim@1"));
3271 }
3272 
TEST_F(ApexdMountTest,OnStartDataHasSameVersion)3273 TEST_F(ApexdMountTest, OnStartDataHasSameVersion) {
3274   MockCheckpointInterface checkpoint_interface;
3275   // Need to call InitializeVold before calling OnStart
3276   InitializeVold(&checkpoint_interface);
3277 
3278   AddPreInstalledApex("apex.apexd_test.apex");
3279   std::string apex_path_2 =
3280       AddPreInstalledApex("apex.apexd_test_different_app.apex");
3281   std::string apex_path_3 = AddDataApex("apex.apexd_test.apex");
3282 
3283   ASSERT_THAT(
3284       ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}),
3285       Ok());
3286 
3287   OnStart();
3288 
3289   UnmountOnTearDown(apex_path_2);
3290   UnmountOnTearDown(apex_path_3);
3291 
3292   ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting");
3293   auto apex_mounts = GetApexMounts();
3294   ASSERT_THAT(apex_mounts,
3295               UnorderedElementsAre("/apex/com.android.apex.test_package",
3296                                    "/apex/com.android.apex.test_package@1",
3297                                    "/apex/com.android.apex.test_package_2",
3298                                    "/apex/com.android.apex.test_package_2@1"));
3299 
3300   auto& db = GetApexDatabaseForTesting();
3301   // Check that it was mounted from data apex, not pre-installed one.
3302   db.ForallMountedApexes("com.android.apex.test_package",
3303                          [&](const MountedApexData& data, bool latest) {
3304                            ASSERT_TRUE(latest);
3305                            ASSERT_EQ(data.full_path, apex_path_3);
3306                          });
3307 }
3308 
TEST_F(ApexdMountTest,OnStartSystemHasHigherVersion)3309 TEST_F(ApexdMountTest, OnStartSystemHasHigherVersion) {
3310   MockCheckpointInterface checkpoint_interface;
3311   // Need to call InitializeVold before calling OnStart
3312   InitializeVold(&checkpoint_interface);
3313 
3314   std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test_v2.apex");
3315   std::string apex_path_2 =
3316       AddPreInstalledApex("apex.apexd_test_different_app.apex");
3317   AddDataApex("apex.apexd_test.apex");
3318 
3319   ASSERT_THAT(
3320       ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}),
3321       Ok());
3322 
3323   OnStart();
3324 
3325   UnmountOnTearDown(apex_path_1);
3326   UnmountOnTearDown(apex_path_2);
3327 
3328   ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting");
3329   auto apex_mounts = GetApexMounts();
3330   ASSERT_THAT(apex_mounts,
3331               UnorderedElementsAre("/apex/com.android.apex.test_package",
3332                                    "/apex/com.android.apex.test_package@2",
3333                                    "/apex/com.android.apex.test_package_2",
3334                                    "/apex/com.android.apex.test_package_2@1"));
3335 
3336   auto& db = GetApexDatabaseForTesting();
3337   // Check that it was mounted from pre-installed one.
3338   db.ForallMountedApexes("com.android.apex.test_package",
3339                          [&](const MountedApexData& data, bool latest) {
3340                            ASSERT_TRUE(latest);
3341                            ASSERT_EQ(data.full_path, apex_path_1);
3342                          });
3343 }
3344 
TEST_F(ApexdMountTest,OnStartFailsToActivateApexOnDataFallsBackToBuiltIn)3345 TEST_F(ApexdMountTest, OnStartFailsToActivateApexOnDataFallsBackToBuiltIn) {
3346   MockCheckpointInterface checkpoint_interface;
3347   // Need to call InitializeVold before calling OnStart
3348   InitializeVold(&checkpoint_interface);
3349 
3350   std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex");
3351   std::string apex_path_2 =
3352       AddPreInstalledApex("apex.apexd_test_different_app.apex");
3353   AddDataApex("apex.apexd_test_manifest_mismatch.apex");
3354 
3355   ASSERT_THAT(
3356       ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}),
3357       Ok());
3358 
3359   OnStart();
3360 
3361   UnmountOnTearDown(apex_path_1);
3362   UnmountOnTearDown(apex_path_2);
3363 
3364   ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting");
3365   auto apex_mounts = GetApexMounts();
3366   ASSERT_THAT(apex_mounts,
3367               UnorderedElementsAre("/apex/com.android.apex.test_package",
3368                                    "/apex/com.android.apex.test_package@1",
3369                                    "/apex/com.android.apex.test_package_2",
3370                                    "/apex/com.android.apex.test_package_2@1"));
3371 
3372   auto& db = GetApexDatabaseForTesting();
3373   // Check that it was mounted from pre-installed apex.
3374   db.ForallMountedApexes("com.android.apex.test_package",
3375                          [&](const MountedApexData& data, bool latest) {
3376                            ASSERT_TRUE(latest);
3377                            ASSERT_EQ(data.full_path, apex_path_1);
3378                          });
3379 }
3380 
TEST_F(ApexdMountTest,OnStartApexOnDataHasWrongKeyFallsBackToBuiltIn)3381 TEST_F(ApexdMountTest, OnStartApexOnDataHasWrongKeyFallsBackToBuiltIn) {
3382   MockCheckpointInterface checkpoint_interface;
3383   // Need to call InitializeVold before calling OnStart
3384   InitializeVold(&checkpoint_interface);
3385 
3386   std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex");
3387   std::string apex_path_2 =
3388       AddPreInstalledApex("apex.apexd_test_different_app.apex");
3389   std::string apex_path_3 =
3390       AddDataApex("apex.apexd_test_different_key_v2.apex");
3391 
3392   {
3393     auto apex = ApexFile::Open(apex_path_3);
3394     ASSERT_THAT(apex, Ok());
3395     ASSERT_EQ(static_cast<uint64_t>(apex->GetManifest().version()), 2ULL);
3396   }
3397 
3398   ASSERT_THAT(
3399       ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}),
3400       Ok());
3401 
3402   OnStart();
3403 
3404   UnmountOnTearDown(apex_path_1);
3405   UnmountOnTearDown(apex_path_2);
3406 
3407   ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting");
3408   auto apex_mounts = GetApexMounts();
3409   ASSERT_THAT(apex_mounts,
3410               UnorderedElementsAre("/apex/com.android.apex.test_package",
3411                                    "/apex/com.android.apex.test_package@1",
3412                                    "/apex/com.android.apex.test_package_2",
3413                                    "/apex/com.android.apex.test_package_2@1"));
3414 
3415   auto& db = GetApexDatabaseForTesting();
3416   // Check that it was mounted from pre-installed apex.
3417   db.ForallMountedApexes("com.android.apex.test_package",
3418                          [&](const MountedApexData& data, bool latest) {
3419                            ASSERT_TRUE(latest);
3420                            ASSERT_EQ(data.full_path, apex_path_1);
3421                          });
3422 }
3423 
TEST_F(ApexdMountTest,OnStartOnlyPreInstalledCapexes)3424 TEST_F(ApexdMountTest, OnStartOnlyPreInstalledCapexes) {
3425   MockCheckpointInterface checkpoint_interface;
3426   // Need to call InitializeVold before calling OnStart
3427   InitializeVold(&checkpoint_interface);
3428 
3429   std::string apex_path_1 =
3430       AddPreInstalledApex("com.android.apex.compressed.v1.capex");
3431 
3432   ASSERT_THAT(
3433       ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}),
3434       Ok());
3435 
3436   OnStart();
3437 
3438   // Decompressed APEX should be mounted
3439   std::string decompressed_active_apex = StringPrintf(
3440       "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(),
3441       kDecompressedApexPackageSuffix);
3442   UnmountOnTearDown(decompressed_active_apex);
3443 
3444   ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting");
3445   auto apex_mounts = GetApexMounts();
3446   ASSERT_THAT(apex_mounts,
3447               UnorderedElementsAre("/apex/com.android.apex.compressed",
3448                                    "/apex/com.android.apex.compressed@1"));
3449   auto& db = GetApexDatabaseForTesting();
3450   // Check that it was mounted from decompressed apex.
3451   db.ForallMountedApexes("com.android.apex.compressed",
3452                          [&](const MountedApexData& data, bool latest) {
3453                            ASSERT_TRUE(latest);
3454                            ASSERT_EQ(data.full_path, decompressed_active_apex);
3455                            ASSERT_EQ(data.device_name,
3456                                      "com.android.apex.compressed");
3457                          });
3458 }
3459 
TEST_F(ApexdMountTest,OnStartDataHasHigherVersionThanCapex)3460 TEST_F(ApexdMountTest, OnStartDataHasHigherVersionThanCapex) {
3461   MockCheckpointInterface checkpoint_interface;
3462   // Need to call InitializeVold before calling OnStart
3463   InitializeVold(&checkpoint_interface);
3464 
3465   AddPreInstalledApex("com.android.apex.compressed.v1.capex");
3466   std::string apex_path_2 =
3467       AddDataApex("com.android.apex.compressed.v2_original.apex");
3468 
3469   ASSERT_THAT(
3470       ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}),
3471       Ok());
3472 
3473   OnStart();
3474 
3475   UnmountOnTearDown(apex_path_2);
3476 
3477   ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting");
3478   auto apex_mounts = GetApexMounts();
3479   ASSERT_THAT(apex_mounts,
3480               UnorderedElementsAre("/apex/com.android.apex.compressed",
3481                                    "/apex/com.android.apex.compressed@2"));
3482   auto& db = GetApexDatabaseForTesting();
3483   // Check that it was mounted from data apex.
3484   db.ForallMountedApexes("com.android.apex.compressed",
3485                          [&](const MountedApexData& data, bool latest) {
3486                            ASSERT_TRUE(latest);
3487                            ASSERT_EQ(data.full_path, apex_path_2);
3488                            ASSERT_EQ(data.device_name,
3489                                      "com.android.apex.compressed");
3490                          });
3491 }
3492 
TEST_F(ApexdMountTest,OnStartDataHasSameVersionAsCapex)3493 TEST_F(ApexdMountTest, OnStartDataHasSameVersionAsCapex) {
3494   MockCheckpointInterface checkpoint_interface;
3495   // Need to call InitializeVold before calling OnStart
3496   InitializeVold(&checkpoint_interface);
3497 
3498   AddPreInstalledApex("com.android.apex.compressed.v1.capex");
3499   std::string apex_path_2 =
3500       AddDataApex("com.android.apex.compressed.v1_original.apex");
3501 
3502   ASSERT_THAT(
3503       ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}),
3504       Ok());
3505 
3506   OnStart();
3507 
3508   // Data APEX should be mounted
3509   UnmountOnTearDown(apex_path_2);
3510 
3511   ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting");
3512   auto apex_mounts = GetApexMounts();
3513   ASSERT_THAT(apex_mounts,
3514               UnorderedElementsAre("/apex/com.android.apex.compressed",
3515                                    "/apex/com.android.apex.compressed@1"));
3516 
3517   auto& db = GetApexDatabaseForTesting();
3518   // Check that it was mounted from data apex, not pre-installed one.
3519   db.ForallMountedApexes("com.android.apex.compressed",
3520                          [&](const MountedApexData& data, bool latest) {
3521                            ASSERT_TRUE(latest);
3522                            ASSERT_EQ(data.full_path, apex_path_2);
3523                            ASSERT_EQ(data.device_name,
3524                                      "com.android.apex.compressed");
3525                          });
3526 }
3527 
TEST_F(ApexdMountTest,OnStartSystemHasHigherVersionCapexThanData)3528 TEST_F(ApexdMountTest, OnStartSystemHasHigherVersionCapexThanData) {
3529   MockCheckpointInterface checkpoint_interface;
3530   // Need to call InitializeVold before calling OnStart
3531   InitializeVold(&checkpoint_interface);
3532 
3533   std::string apex_path_1 =
3534       AddPreInstalledApex("com.android.apex.compressed.v2.capex");
3535   AddDataApex("com.android.apex.compressed.v1_original.apex");
3536 
3537   ASSERT_THAT(
3538       ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}),
3539       Ok());
3540 
3541   OnStart();
3542 
3543   // Decompressed APEX should be mounted
3544   std::string decompressed_active_apex = StringPrintf(
3545       "%s/com.android.apex.compressed@2%s", GetDecompressionDir().c_str(),
3546       kDecompressedApexPackageSuffix);
3547   UnmountOnTearDown(decompressed_active_apex);
3548 
3549   ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting");
3550   auto apex_mounts = GetApexMounts();
3551   ASSERT_THAT(apex_mounts,
3552               UnorderedElementsAre("/apex/com.android.apex.compressed",
3553                                    "/apex/com.android.apex.compressed@2"));
3554 
3555   auto& db = GetApexDatabaseForTesting();
3556   // Check that it was mounted from compressed apex
3557   db.ForallMountedApexes("com.android.apex.compressed",
3558                          [&](const MountedApexData& data, bool latest) {
3559                            ASSERT_TRUE(latest);
3560                            ASSERT_EQ(data.full_path, decompressed_active_apex);
3561                            ASSERT_EQ(data.device_name,
3562                                      "com.android.apex.compressed");
3563                          });
3564 }
3565 
TEST_F(ApexdMountTest,OnStartFailsToActivateApexOnDataFallsBackToCapex)3566 TEST_F(ApexdMountTest, OnStartFailsToActivateApexOnDataFallsBackToCapex) {
3567   MockCheckpointInterface checkpoint_interface;
3568   // Need to call InitializeVold before calling OnStart
3569   InitializeVold(&checkpoint_interface);
3570 
3571   AddPreInstalledApex("com.android.apex.compressed.v1.capex");
3572   AddDataApex("com.android.apex.compressed.v2_manifest_mismatch.apex");
3573 
3574   ASSERT_THAT(
3575       ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}),
3576       Ok());
3577 
3578   OnStart();
3579 
3580   // Decompressed APEX should be mounted
3581   std::string decompressed_active_apex = StringPrintf(
3582       "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(),
3583       kDecompressedApexPackageSuffix);
3584   UnmountOnTearDown(decompressed_active_apex);
3585 
3586   ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting");
3587   auto apex_mounts = GetApexMounts();
3588   ASSERT_THAT(apex_mounts,
3589               UnorderedElementsAre("/apex/com.android.apex.compressed",
3590                                    "/apex/com.android.apex.compressed@1"));
3591   auto& db = GetApexDatabaseForTesting();
3592   // Check that it was mounted from decompressed apex. It should also be mounted
3593   // on dm-verity device.
3594   db.ForallMountedApexes("com.android.apex.compressed",
3595                          [&](const MountedApexData& data, bool latest) {
3596                            ASSERT_TRUE(latest);
3597                            ASSERT_EQ(data.full_path, decompressed_active_apex);
3598                            ASSERT_EQ(data.device_name,
3599                                      "com.android.apex.compressed");
3600                          });
3601 }
3602 
3603 // Test scenario when we fallback to capex but it already has a decompressed
3604 // version on data
TEST_F(ApexdMountTest,OnStartFallbackToAlreadyDecompressedCapex)3605 TEST_F(ApexdMountTest, OnStartFallbackToAlreadyDecompressedCapex) {
3606   MockCheckpointInterface checkpoint_interface;
3607   // Need to call InitializeVold before calling OnStart
3608   InitializeVold(&checkpoint_interface);
3609 
3610   PrepareCompressedApex("com.android.apex.compressed.v1.capex");
3611   AddDataApex("com.android.apex.compressed.v2_manifest_mismatch.apex");
3612 
3613   ASSERT_THAT(
3614       ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}),
3615       Ok());
3616 
3617   OnStart();
3618 
3619   // Decompressed APEX should be mounted
3620   std::string decompressed_active_apex = StringPrintf(
3621       "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(),
3622       kDecompressedApexPackageSuffix);
3623   UnmountOnTearDown(decompressed_active_apex);
3624 
3625   ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting");
3626   auto apex_mounts = GetApexMounts();
3627   ASSERT_THAT(apex_mounts,
3628               UnorderedElementsAre("/apex/com.android.apex.compressed",
3629                                    "/apex/com.android.apex.compressed@1"));
3630   auto& db = GetApexDatabaseForTesting();
3631   // Check that it was mounted from decompressed apex.
3632   db.ForallMountedApexes("com.android.apex.compressed",
3633                          [&](const MountedApexData& data, bool latest) {
3634                            ASSERT_TRUE(latest);
3635                            ASSERT_EQ(data.full_path, decompressed_active_apex);
3636                            ASSERT_EQ(data.device_name,
3637                                      "com.android.apex.compressed");
3638                          });
3639 }
3640 
3641 // Test scenario when we fallback to capex but it has same version as corrupt
3642 // data apex
TEST_F(ApexdMountTest,OnStartFallbackToCapexSameVersion)3643 TEST_F(ApexdMountTest, OnStartFallbackToCapexSameVersion) {
3644   MockCheckpointInterface checkpoint_interface;
3645   // Need to call InitializeVold before calling OnStart
3646   InitializeVold(&checkpoint_interface);
3647 
3648   AddPreInstalledApex("com.android.apex.compressed.v2.capex");
3649   // Add data apex using the common naming convention for /data/apex/active
3650   // directory
3651   fs::copy(GetTestFile("com.android.apex.compressed.v2_manifest_mismatch.apex"),
3652            GetDataDir() + "/com.android.apex.compressed@2.apex");
3653 
3654   ASSERT_THAT(
3655       ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}),
3656       Ok());
3657 
3658   OnStart();
3659 
3660   // Decompressed APEX should be mounted
3661   std::string decompressed_active_apex = StringPrintf(
3662       "%s/com.android.apex.compressed@2%s", GetDecompressionDir().c_str(),
3663       kDecompressedApexPackageSuffix);
3664   UnmountOnTearDown(decompressed_active_apex);
3665 
3666   ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting");
3667   auto apex_mounts = GetApexMounts();
3668   ASSERT_THAT(apex_mounts,
3669               UnorderedElementsAre("/apex/com.android.apex.compressed",
3670                                    "/apex/com.android.apex.compressed@2"));
3671   auto& db = GetApexDatabaseForTesting();
3672   // Check that it was mounted from decompressed apex.
3673   db.ForallMountedApexes("com.android.apex.compressed",
3674                          [&](const MountedApexData& data, bool latest) {
3675                            ASSERT_TRUE(latest);
3676                            ASSERT_EQ(data.full_path, decompressed_active_apex);
3677                            ASSERT_EQ(data.device_name,
3678                                      "com.android.apex.compressed");
3679                          });
3680 }
3681 
TEST_F(ApexdMountTest,OnStartCapexToApex)3682 TEST_F(ApexdMountTest, OnStartCapexToApex) {
3683   MockCheckpointInterface checkpoint_interface;
3684   // Need to call InitializeVold before calling OnStart
3685   InitializeVold(&checkpoint_interface);
3686 
3687   TemporaryDir previous_built_in_dir;
3688   PrepareCompressedApex("com.android.apex.compressed.v1.capex",
3689                         previous_built_in_dir.path);
3690   auto apex_path =
3691       AddPreInstalledApex("com.android.apex.compressed.v1_original.apex");
3692 
3693   ASSERT_THAT(
3694       ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}),
3695       Ok());
3696 
3697   OnStart();
3698 
3699   // Uncompressed APEX should be mounted
3700   UnmountOnTearDown(apex_path);
3701 
3702   ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting");
3703   auto apex_mounts = GetApexMounts();
3704   ASSERT_THAT(apex_mounts,
3705               UnorderedElementsAre("/apex/com.android.apex.compressed",
3706                                    "/apex/com.android.apex.compressed@1"));
3707   auto& db = GetApexDatabaseForTesting();
3708   // Check that it was mounted from decompressed apex.
3709   db.ForallMountedApexes("com.android.apex.compressed",
3710                          [&](const MountedApexData& data, bool latest) {
3711                            ASSERT_TRUE(latest);
3712                            ASSERT_EQ(data.full_path, apex_path);
3713                            ASSERT_THAT(data.device_name, IsEmpty());
3714                          });
3715 }
3716 
3717 // Test to ensure we do not mount decompressed APEX from /data/apex/active
TEST_F(ApexdMountTest,OnStartOrphanedDecompressedApexInActiveDirectory)3718 TEST_F(ApexdMountTest, OnStartOrphanedDecompressedApexInActiveDirectory) {
3719   MockCheckpointInterface checkpoint_interface;
3720   // Need to call InitializeVold before calling OnStart
3721   InitializeVold(&checkpoint_interface);
3722 
3723   // Place a decompressed APEX in /data/apex/active. This apex should not
3724   // be mounted since it's not in correct location. Instead, the
3725   // pre-installed APEX should be mounted.
3726   auto decompressed_apex_in_active_dir =
3727       StringPrintf("%s/com.android.apex.compressed@1%s", GetDataDir().c_str(),
3728                    kDecompressedApexPackageSuffix);
3729   fs::copy(GetTestFile("com.android.apex.compressed.v1_original.apex"),
3730            decompressed_apex_in_active_dir);
3731   auto apex_path =
3732       AddPreInstalledApex("com.android.apex.compressed.v1_original.apex");
3733 
3734   ASSERT_THAT(
3735       ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}),
3736       Ok());
3737 
3738   OnStart();
3739 
3740   // Pre-installed APEX should be mounted
3741   UnmountOnTearDown(apex_path);
3742   auto& db = GetApexDatabaseForTesting();
3743   // Check that pre-installed APEX has been activated
3744   db.ForallMountedApexes("com.android.apex.compressed",
3745                          [&](const MountedApexData& data, bool latest) {
3746                            ASSERT_TRUE(latest);
3747                            ASSERT_EQ(data.full_path, apex_path);
3748                            ASSERT_THAT(data.device_name, IsEmpty());
3749                          });
3750 }
3751 
3752 // Test scenario when decompressed version has different version than
3753 // pre-installed CAPEX
TEST_F(ApexdMountTest,OnStartDecompressedApexVersionDifferentThanCapex)3754 TEST_F(ApexdMountTest, OnStartDecompressedApexVersionDifferentThanCapex) {
3755   MockCheckpointInterface checkpoint_interface;
3756   // Need to call InitializeVold before calling OnStart
3757   InitializeVold(&checkpoint_interface);
3758 
3759   TemporaryDir previous_built_in_dir;
3760   PrepareCompressedApex("com.android.apex.compressed.v2.capex",
3761                         previous_built_in_dir.path);
3762   auto apex_path = AddPreInstalledApex("com.android.apex.compressed.v1.capex");
3763 
3764   ASSERT_THAT(
3765       ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}),
3766       Ok());
3767 
3768   OnStart();
3769 
3770   // Existing higher version decompressed APEX should be ignored and new
3771   // pre-installed CAPEX should be decompressed and mounted
3772   std::string decompressed_active_apex = StringPrintf(
3773       "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(),
3774       kDecompressedApexPackageSuffix);
3775   UnmountOnTearDown(decompressed_active_apex);
3776 
3777   ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting");
3778   auto apex_mounts = GetApexMounts();
3779   ASSERT_THAT(apex_mounts,
3780               UnorderedElementsAre("/apex/com.android.apex.compressed",
3781                                    "/apex/com.android.apex.compressed@1"));
3782   auto& db = GetApexDatabaseForTesting();
3783   // Check that it was mounted from newly decompressed apex.
3784   db.ForallMountedApexes("com.android.apex.compressed",
3785                          [&](const MountedApexData& data, bool latest) {
3786                            ASSERT_TRUE(latest);
3787                            ASSERT_EQ(data.full_path, decompressed_active_apex);
3788                            ASSERT_EQ(data.device_name,
3789                                      "com.android.apex.compressed");
3790                          });
3791 }
3792 
3793 // Test that ota_apex is persisted until slot switch
TEST_F(ApexdMountTest,OnStartOtaApexKeptUntilSlotSwitch)3794 TEST_F(ApexdMountTest, OnStartOtaApexKeptUntilSlotSwitch) {
3795   MockCheckpointInterface checkpoint_interface;
3796   // Need to call InitializeVold before calling OnStart
3797   InitializeVold(&checkpoint_interface);
3798 
3799   // Imagine current system has v1 capex and we have v2 incoming via ota
3800   auto old_capex = AddPreInstalledApex("com.android.apex.compressed.v1.capex");
3801   auto ota_apex_path =
3802       StringPrintf("%s/com.android.apex.compressed@2%s",
3803                    GetDecompressionDir().c_str(), kOtaApexPackageSuffix);
3804   fs::copy(GetTestFile("com.android.apex.compressed.v2_original.apex"),
3805            ota_apex_path.c_str());
3806 
3807   ASSERT_THAT(
3808       ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}),
3809       Ok());
3810 
3811   // When we call OnStart for the first time, it will decompress v1 capex and
3812   // activate it, while after second call it will decompress v2 capex and
3813   // activate it. We need to make sure that activated APEXes are cleaned up
3814   // after test finishes.
3815   auto old_decompressed_apex = StringPrintf(
3816       "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(),
3817       kDecompressedApexPackageSuffix);
3818   auto new_decompressed_apex = StringPrintf(
3819       "%s/com.android.apex.compressed@2%s", GetDecompressionDir().c_str(),
3820       kDecompressedApexPackageSuffix);
3821   UnmountOnTearDown(old_decompressed_apex);
3822   UnmountOnTearDown(new_decompressed_apex);
3823 
3824   // First try starting without slot switch. Since we are booting with
3825   // old pre-installed capex, ota_apex should not be deleted
3826   OnStart();
3827   auto path_exists = PathExists(ota_apex_path);
3828   ASSERT_TRUE(*path_exists);
3829 
3830   // When we switch slot, the pre-installed APEX will match ota_apex
3831   // and the ota_apex will end up getting renamed.
3832   RemoveFileIfExists(old_capex);
3833   AddPreInstalledApex("com.android.apex.compressed.v2.capex");
3834   ApexFileRepository::GetInstance().Reset(GetDecompressionDir());
3835   ASSERT_THAT(
3836       ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}),
3837       Ok());
3838   OnStart();
3839   path_exists = PathExists(ota_apex_path);
3840   ASSERT_FALSE(*path_exists);
3841 }
3842 
3843 // Test scenario when decompressed version has same version but different
3844 // digest
TEST_F(ApexdMountTest,OnStartDecompressedApexVersionSameAsCapexDifferentDigest)3845 TEST_F(ApexdMountTest,
3846        OnStartDecompressedApexVersionSameAsCapexDifferentDigest) {
3847   MockCheckpointInterface checkpoint_interface;
3848   // Need to call InitializeVold before calling OnStart
3849   InitializeVold(&checkpoint_interface);
3850 
3851   // Push a CAPEX to system without decompressing it
3852   auto apex_path = AddPreInstalledApex("com.android.apex.compressed.v1.capex");
3853   auto pre_installed_apex = ApexFile::Open(apex_path);
3854   // Now push an APEX with different root digest as decompressed APEX
3855   auto decompressed_apex_path = StringPrintf(
3856       "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(),
3857       kDecompressedApexPackageSuffix);
3858   fs::copy(GetTestFile(
3859                "com.android.apex.compressed.v1_different_digest_original.apex"),
3860            decompressed_apex_path);
3861   auto different_digest_apex = ApexFile::Open(decompressed_apex_path);
3862   auto different_digest = GetRootDigest(*different_digest_apex);
3863   ASSERT_NE(
3864       pre_installed_apex->GetManifest().capexmetadata().originalapexdigest(),
3865       different_digest);
3866 
3867   ASSERT_THAT(
3868       ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}),
3869       Ok());
3870 
3871   OnStart();
3872 
3873   // Existing same version decompressed APEX with different root digest should
3874   // be ignored and the pre-installed CAPEX should be decompressed again.
3875   UnmountOnTearDown(decompressed_apex_path);
3876 
3877   // Ensure decompressed apex has same digest as pre-installed
3878   auto decompressed_apex = ApexFile::Open(decompressed_apex_path);
3879   ASSERT_EQ(
3880       pre_installed_apex->GetManifest().capexmetadata().originalapexdigest(),
3881       GetRootDigest(*decompressed_apex));
3882   ASSERT_NE(GetRootDigest(*decompressed_apex), different_digest);
3883 }
3884 
3885 // Test when decompressed APEX has different key than CAPEX
TEST_F(ApexdMountTest,OnStartDecompressedApexVersionSameAsCapexDifferentKey)3886 TEST_F(ApexdMountTest, OnStartDecompressedApexVersionSameAsCapexDifferentKey) {
3887   MockCheckpointInterface checkpoint_interface;
3888   // Need to call InitializeVold before calling OnStart
3889   InitializeVold(&checkpoint_interface);
3890 
3891   TemporaryDir previous_built_in_dir;
3892   auto different_key_apex_path =
3893       PrepareCompressedApex("com.android.apex.compressed_different_key.capex",
3894                             previous_built_in_dir.path);
3895   // Place a same version capex in current built_in_dir, which has different key
3896   auto apex_path = AddPreInstalledApex("com.android.apex.compressed.v1.capex");
3897 
3898   ASSERT_THAT(
3899       ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}),
3900       Ok());
3901 
3902   OnStart();
3903 
3904   // Existing same version decompressed APEX should be ignored and new
3905   // pre-installed CAPEX should be decompressed and mounted
3906   std::string decompressed_active_apex = StringPrintf(
3907       "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(),
3908       kDecompressedApexPackageSuffix);
3909   UnmountOnTearDown(decompressed_active_apex);
3910 
3911   // Ensure decompressed apex has same digest as pre-installed
3912   auto pre_installed_apex = ApexFile::Open(apex_path);
3913   auto decompressed_apex = ApexFile::Open(decompressed_active_apex);
3914   auto different_key_apex = ApexFile::Open(different_key_apex_path);
3915   ASSERT_EQ(
3916       pre_installed_apex->GetManifest().capexmetadata().originalapexdigest(),
3917       GetRootDigest(*decompressed_apex));
3918   ASSERT_NE(
3919       pre_installed_apex->GetManifest().capexmetadata().originalapexdigest(),
3920       GetRootDigest(*different_key_apex));
3921 }
3922 
TEST_F(ApexdMountTest,PopulateFromMountsChecksPathPrefix)3923 TEST_F(ApexdMountTest, PopulateFromMountsChecksPathPrefix) {
3924   AddPreInstalledApex("apex.apexd_test.apex");
3925   std::string apex_path = AddDataApex("apex.apexd_test_v2.apex");
3926 
3927   // Mount an apex from decomrpession_dir
3928   PrepareCompressedApex("com.android.apex.compressed.v1.capex");
3929   std::string decompressed_apex =
3930       StringPrintf("%s/com.android.apex.compressed@1.decompressed.apex",
3931                    GetDecompressionDir().c_str());
3932 
3933   // Mount an apex from some other directory
3934   TemporaryDir td;
3935   AddPreInstalledApex("apex.apexd_test_different_app.apex");
3936   fs::copy(GetTestFile("apex.apexd_test_different_app.apex"), td.path);
3937   std::string other_apex =
3938       StringPrintf("%s/apex.apexd_test_different_app.apex", td.path);
3939 
3940   auto& instance = ApexFileRepository::GetInstance();
3941   ASSERT_THAT(instance.AddPreInstalledApex({GetBuiltInDir()}), Ok());
3942 
3943   ASSERT_THAT(ActivatePackage(apex_path), Ok());
3944   ASSERT_THAT(ActivatePackage(decompressed_apex), Ok());
3945   ASSERT_THAT(ActivatePackage(other_apex), Ok());
3946 
3947   auto& db = GetApexDatabaseForTesting();
3948   // Remember mount information for |other_apex|, since it won't be available in
3949   // the database. We will need to tear it down manually.
3950   std::optional<MountedApexData> other_apex_mount_data;
3951   db.ForallMountedApexes(
3952       "com.android.apex.test_package_2",
3953       [&other_apex_mount_data](const MountedApexData& data, bool latest) {
3954         if (latest) {
3955           other_apex_mount_data.emplace(data);
3956         }
3957       });
3958   UnmountOnTearDown(apex_path);
3959   UnmountOnTearDown(decompressed_apex);
3960   ASSERT_TRUE(other_apex_mount_data.has_value());
3961   auto deleter = make_scope_guard([&other_apex_mount_data]() {
3962     if (!other_apex_mount_data.has_value()) {
3963       return;
3964     }
3965     if (umount2("/apex/com.android.apex.test_package_2", 0) != 0) {
3966       PLOG(ERROR) << "Failed to unmount /apex/com.android.apex.test_package_2";
3967     }
3968     auto res = Unmount(*other_apex_mount_data, /* deferred= */ false);
3969     if (!res.ok()) {
3970       LOG(ERROR) << res.error();
3971     }
3972   });
3973 
3974   auto apex_mounts = GetApexMounts();
3975   ASSERT_THAT(apex_mounts,
3976               UnorderedElementsAre("/apex/com.android.apex.test_package",
3977                                    "/apex/com.android.apex.test_package@2",
3978                                    "/apex/com.android.apex.compressed",
3979                                    "/apex/com.android.apex.compressed@1",
3980                                    "/apex/com.android.apex.test_package_2",
3981                                    "/apex/com.android.apex.test_package_2@1"));
3982 
3983   // Clear the database before calling PopulateFromMounts
3984   db.Reset();
3985 
3986   // Populate from mount
3987   db.PopulateFromMounts(GetDataDir(), GetDecompressionDir(), GetHashTreeDir());
3988 
3989   // Count number of package and collect package names
3990   int package_count = 0;
3991   std::vector<std::string> mounted_paths;
3992   db.ForallMountedApexes([&](const std::string& package,
3993                              const MountedApexData& data, bool latest) {
3994     package_count++;
3995     mounted_paths.push_back(data.full_path);
3996   });
3997   ASSERT_EQ(package_count, 2);
3998   ASSERT_THAT(mounted_paths,
3999               UnorderedElementsAre(apex_path, decompressed_apex));
4000 }
4001 
TEST_F(ApexdMountTest,UnmountAll)4002 TEST_F(ApexdMountTest, UnmountAll) {
4003   AddPreInstalledApex("apex.apexd_test.apex");
4004   std::string apex_path_2 =
4005       AddPreInstalledApex("apex.apexd_test_different_app.apex");
4006   std::string apex_path_3 = AddDataApex("apex.apexd_test_v2.apex");
4007 
4008   // Mount an apex from decomrpession_dir
4009   PrepareCompressedApex("com.android.apex.compressed.v1.capex");
4010   std::string decompressed_apex =
4011       StringPrintf("%s/com.android.apex.compressed@1.decompressed.apex",
4012                    GetDecompressionDir().c_str());
4013 
4014   auto& instance = ApexFileRepository::GetInstance();
4015   ASSERT_THAT(instance.AddPreInstalledApex({GetBuiltInDir()}), Ok());
4016 
4017   ASSERT_THAT(ActivatePackage(apex_path_2), Ok());
4018   ASSERT_THAT(ActivatePackage(apex_path_3), Ok());
4019   ASSERT_THAT(ActivatePackage(decompressed_apex), Ok());
4020   UnmountOnTearDown(apex_path_2);
4021   UnmountOnTearDown(apex_path_3);
4022   UnmountOnTearDown(decompressed_apex);
4023 
4024   auto apex_mounts = GetApexMounts();
4025   ASSERT_THAT(apex_mounts,
4026               UnorderedElementsAre("/apex/com.android.apex.test_package",
4027                                    "/apex/com.android.apex.test_package@2",
4028                                    "/apex/com.android.apex.compressed",
4029                                    "/apex/com.android.apex.compressed@1",
4030                                    "/apex/com.android.apex.test_package_2",
4031                                    "/apex/com.android.apex.test_package_2@1"));
4032 
4033   auto& db = GetApexDatabaseForTesting();
4034   // UnmountAll expects apex database to empty, hence this reset.
4035   db.Reset();
4036 
4037   ASSERT_EQ(0, UnmountAll());
4038 
4039   auto new_apex_mounts = GetApexMounts();
4040   ASSERT_EQ(new_apex_mounts.size(), 0u);
4041 }
4042 
TEST_F(ApexdMountTest,UnmountAllSharedLibsApex)4043 TEST_F(ApexdMountTest, UnmountAllSharedLibsApex) {
4044   ASSERT_EQ(mkdir("/apex/sharedlibs", 0755), 0);
4045   ASSERT_EQ(mkdir("/apex/sharedlibs/lib", 0755), 0);
4046   ASSERT_EQ(mkdir("/apex/sharedlibs/lib64", 0755), 0);
4047   auto deleter = make_scope_guard([]() {
4048     std::error_code ec;
4049     fs::remove_all("/apex/sharedlibs", ec);
4050     if (ec) {
4051       LOG(ERROR) << "Failed to delete /apex/sharedlibs : " << ec;
4052     }
4053   });
4054 
4055   std::string apex_path_1 = AddPreInstalledApex(
4056       "com.android.apex.test.sharedlibs_generated.v1.libvX.apex");
4057   std::string apex_path_2 =
4058       AddDataApex("com.android.apex.test.sharedlibs_generated.v2.libvY.apex");
4059 
4060   auto& instance = ApexFileRepository::GetInstance();
4061   ASSERT_THAT(instance.AddPreInstalledApex({GetBuiltInDir()}), Ok());
4062 
4063   ASSERT_THAT(ActivatePackage(apex_path_1), Ok());
4064   ASSERT_THAT(ActivatePackage(apex_path_2), Ok());
4065   UnmountOnTearDown(apex_path_1);
4066   UnmountOnTearDown(apex_path_2);
4067 
4068   auto apex_mounts = GetApexMounts();
4069   ASSERT_THAT(apex_mounts,
4070               UnorderedElementsAre("/apex/com.android.apex.test.sharedlibs@1",
4071                                    "/apex/com.android.apex.test.sharedlibs@2"));
4072 
4073   auto& db = GetApexDatabaseForTesting();
4074   // UnmountAll expects apex database to empty, hence this reset.
4075   db.Reset();
4076 
4077   ASSERT_EQ(0, UnmountAll());
4078 
4079   auto new_apex_mounts = GetApexMounts();
4080   ASSERT_EQ(new_apex_mounts.size(), 0u);
4081 }
4082 
TEST_F(ApexdMountTest,OnStartInVmModeActivatesPreInstalled)4083 TEST_F(ApexdMountTest, OnStartInVmModeActivatesPreInstalled) {
4084   MockCheckpointInterface checkpoint_interface;
4085   // Need to call InitializeVold before calling OnStart
4086   InitializeVold(&checkpoint_interface);
4087 
4088   auto path1 = AddPreInstalledApex("apex.apexd_test.apex");
4089   auto path2 = AddPreInstalledApex("apex.apexd_test_different_app.apex");
4090   // In VM mode, we don't scan /data/apex
4091   AddDataApex("apex.apexd_test_v2.apex");
4092 
4093   ASSERT_EQ(0, OnStartInVmMode());
4094   UnmountOnTearDown(path1);
4095   UnmountOnTearDown(path2);
4096 
4097   auto apex_mounts = GetApexMounts();
4098   ASSERT_THAT(apex_mounts,
4099               UnorderedElementsAre("/apex/com.android.apex.test_package",
4100                                    "/apex/com.android.apex.test_package@1",
4101                                    "/apex/com.android.apex.test_package_2",
4102                                    "/apex/com.android.apex.test_package_2@1",
4103                                    // Emits apex-info-list as well
4104                                    "/apex/apex-info-list.xml"));
4105 
4106   ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "ready");
4107 }
4108 
TEST_F(ApexdMountTest,OnStartInVmModeFailsWithCapex)4109 TEST_F(ApexdMountTest, OnStartInVmModeFailsWithCapex) {
4110   MockCheckpointInterface checkpoint_interface;
4111   // Need to call InitializeVold before calling OnStart
4112   InitializeVold(&checkpoint_interface);
4113 
4114   AddPreInstalledApex("com.android.apex.compressed.v2.capex");
4115 
4116   ASSERT_EQ(1, OnStartInVmMode());
4117 }
4118 
TEST_F(ApexdMountTest,OnStartInVmModeActivatesBlockDevicesAsWell)4119 TEST_F(ApexdMountTest, OnStartInVmModeActivatesBlockDevicesAsWell) {
4120   MockCheckpointInterface checkpoint_interface;
4121   // Need to call InitializeVold before calling OnStart
4122   InitializeVold(&checkpoint_interface);
4123 
4124   // Set system property to enable block apexes
4125   SetBlockApexEnabled(true);
4126 
4127   auto path1 = AddBlockApex("apex.apexd_test.apex");
4128 
4129   ASSERT_EQ(0, OnStartInVmMode());
4130   UnmountOnTearDown(path1);
4131 
4132   auto apex_mounts = GetApexMounts();
4133   ASSERT_THAT(apex_mounts,
4134               UnorderedElementsAre("/apex/com.android.apex.test_package",
4135                                    "/apex/com.android.apex.test_package@1",
4136                                    // Emits apex-info-list as well
4137                                    "/apex/apex-info-list.xml"));
4138 
4139   ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
4140   auto info_list =
4141       com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
4142   ASSERT_TRUE(info_list.has_value());
4143   auto apex_info_xml_1 = com::android::apex::ApexInfo(
4144       /* moduleName= */ "com.android.apex.test_package",
4145       /* modulePath= */ path1,
4146       /* preinstalledModulePath= */ path1,
4147       /* versionCode= */ 1, /* versionName= */ "1",
4148       /* isFactory= */ true, /* isActive= */ true, GetMTime(path1),
4149       /* provideSharedApexLibs= */ false);
4150   ASSERT_THAT(info_list->getApexInfo(),
4151               UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1)));
4152 }
4153 
TEST_F(ApexdMountTest,OnStartInVmModeFailsWithDuplicateNames)4154 TEST_F(ApexdMountTest, OnStartInVmModeFailsWithDuplicateNames) {
4155   MockCheckpointInterface checkpoint_interface;
4156   // Need to call InitializeVold before calling OnStart
4157   InitializeVold(&checkpoint_interface);
4158 
4159   // Set system property to enable block apexes
4160   SetBlockApexEnabled(true);
4161 
4162   AddPreInstalledApex("apex.apexd_test.apex");
4163   AddBlockApex("apex.apexd_test_v2.apex");
4164 
4165   ASSERT_EQ(1, OnStartInVmMode());
4166 }
4167 
TEST_F(ApexdMountTest,OnStartInVmSupportsMultipleSharedLibsApexes)4168 TEST_F(ApexdMountTest, OnStartInVmSupportsMultipleSharedLibsApexes) {
4169   MockCheckpointInterface checkpoint_interface;
4170   InitializeVold(&checkpoint_interface);
4171   SetBlockApexEnabled(true);
4172 
4173   auto path1 =
4174       AddBlockApex("com.android.apex.test.sharedlibs_generated.v1.libvX.apex",
4175                    /*public_key=*/"", /*root_digest=*/"", /*is_factory=*/true);
4176   auto path2 =
4177       AddBlockApex("com.android.apex.test.sharedlibs_generated.v2.libvY.apex",
4178                    /*public_key=*/"", /*root_digest=*/"", /*is_factory=*/false);
4179 
4180   ASSERT_EQ(0, OnStartInVmMode());
4181   UnmountOnTearDown(path1);
4182   UnmountOnTearDown(path2);
4183 
4184   // Btw, in case duplicates are sharedlibs apexes, both should be activated
4185   auto apex_mounts = GetApexMounts();
4186   ASSERT_THAT(apex_mounts,
4187               UnorderedElementsAre("/apex/com.android.apex.test.sharedlibs@1",
4188                                    "/apex/com.android.apex.test.sharedlibs@2",
4189                                    // Emits apex-info-list as well
4190                                    "/apex/apex-info-list.xml"));
4191 }
4192 
TEST_F(ApexdMountTest,OnStartInVmShouldRejectInDuplicateFactoryApexes)4193 TEST_F(ApexdMountTest, OnStartInVmShouldRejectInDuplicateFactoryApexes) {
4194   MockCheckpointInterface checkpoint_interface;
4195   InitializeVold(&checkpoint_interface);
4196   SetBlockApexEnabled(true);
4197 
4198   auto path1 =
4199       AddBlockApex("com.android.apex.test.sharedlibs_generated.v1.libvX.apex",
4200                    /*public_key=*/"", /*root_digest=*/"", /*is_factory=*/true);
4201   auto path2 =
4202       AddBlockApex("com.android.apex.test.sharedlibs_generated.v2.libvY.apex",
4203                    /*public_key=*/"", /*root_digest=*/"", /*is_factory=*/true);
4204 
4205   ASSERT_EQ(1, OnStartInVmMode());
4206   UnmountOnTearDown(path1);
4207   UnmountOnTearDown(path2);
4208 }
4209 
TEST_F(ApexdMountTest,OnStartInVmShouldRejectInDuplicateNonFactoryApexes)4210 TEST_F(ApexdMountTest, OnStartInVmShouldRejectInDuplicateNonFactoryApexes) {
4211   MockCheckpointInterface checkpoint_interface;
4212   InitializeVold(&checkpoint_interface);
4213   SetBlockApexEnabled(true);
4214 
4215   auto path1 =
4216       AddBlockApex("com.android.apex.test.sharedlibs_generated.v1.libvX.apex",
4217                    /*public_key=*/"", /*root_digest=*/"", /*is_factory=*/false);
4218   auto path2 =
4219       AddBlockApex("com.android.apex.test.sharedlibs_generated.v2.libvY.apex",
4220                    /*public_key=*/"", /*root_digest=*/"", /*is_factory=*/false);
4221 
4222   ASSERT_EQ(1, OnStartInVmMode());
4223   UnmountOnTearDown(path1);
4224   UnmountOnTearDown(path2);
4225 }
4226 
TEST_F(ApexdMountTest,OnStartInVmModeFailsWithWrongPubkey)4227 TEST_F(ApexdMountTest, OnStartInVmModeFailsWithWrongPubkey) {
4228   MockCheckpointInterface checkpoint_interface;
4229   // Need to call InitializeVold before calling OnStart
4230   InitializeVold(&checkpoint_interface);
4231 
4232   // Set system property to enable block apexes
4233   SetBlockApexEnabled(true);
4234 
4235   AddBlockApex("apex.apexd_test.apex", /*public_key=*/"wrong pubkey");
4236 
4237   ASSERT_EQ(1, OnStartInVmMode());
4238 }
4239 
TEST_F(ApexdMountTest,GetActivePackagesReturningBlockApexesAsWell)4240 TEST_F(ApexdMountTest, GetActivePackagesReturningBlockApexesAsWell) {
4241   MockCheckpointInterface checkpoint_interface;
4242   // Need to call InitializeVold before calling OnStart
4243   InitializeVold(&checkpoint_interface);
4244 
4245   // Set system property to enable block apexes
4246   SetBlockApexEnabled(true);
4247 
4248   auto path1 = AddBlockApex("apex.apexd_test.apex");
4249 
4250   ASSERT_EQ(0, OnStartInVmMode());
4251   UnmountOnTearDown(path1);
4252 
4253   auto active_apexes = GetActivePackages();
4254   ASSERT_EQ(1u, active_apexes.size());
4255   ASSERT_EQ(path1, active_apexes[0].GetPath());
4256 }
4257 
TEST_F(ApexdMountTest,OnStartInVmModeFailsWithWrongRootDigest)4258 TEST_F(ApexdMountTest, OnStartInVmModeFailsWithWrongRootDigest) {
4259   MockCheckpointInterface checkpoint_interface;
4260   // Need to call InitializeVold before calling OnStart
4261   InitializeVold(&checkpoint_interface);
4262 
4263   // Set system property to enable block apexes
4264   SetBlockApexEnabled(true);
4265 
4266   AddBlockApex("apex.apexd_test.apex", /*public_key=*/"",
4267                /*root_digest=*/"wrong root digest");
4268 
4269   ASSERT_EQ(1, OnStartInVmMode());
4270 }
4271 
4272 // Test that OnStart works with only block devices
TEST_F(ApexdMountTest,OnStartOnlyBlockDevices)4273 TEST_F(ApexdMountTest, OnStartOnlyBlockDevices) {
4274   MockCheckpointInterface checkpoint_interface;
4275   // Need to call InitializeVold before calling OnStart
4276   InitializeVold(&checkpoint_interface);
4277 
4278   // Set system property to enable block apexes
4279   SetBlockApexEnabled(true);
4280 
4281   auto path1 = AddBlockApex("apex.apexd_test.apex");
4282 
4283   ASSERT_THAT(android::apex::AddBlockApex(ApexFileRepository::GetInstance()),
4284               Ok());
4285 
4286   OnStart();
4287   UnmountOnTearDown(path1);
4288 
4289   ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting");
4290   auto apex_mounts = GetApexMounts();
4291 
4292   ASSERT_THAT(apex_mounts,
4293               UnorderedElementsAre("/apex/com.android.apex.test_package",
4294                                    "/apex/com.android.apex.test_package@1"));
4295 }
4296 
4297 // Test that we can have a mix of both block and system apexes
TEST_F(ApexdMountTest,OnStartBlockAndSystemInstalled)4298 TEST_F(ApexdMountTest, OnStartBlockAndSystemInstalled) {
4299   MockCheckpointInterface checkpoint_interface;
4300   // Need to call InitializeVold before calling OnStart
4301   InitializeVold(&checkpoint_interface);
4302 
4303   // Set system property to enable block apexes
4304   SetBlockApexEnabled(true);
4305 
4306   auto path1 = AddPreInstalledApex("apex.apexd_test.apex");
4307   auto path2 = AddBlockApex("apex.apexd_test_different_app.apex");
4308 
4309   auto& instance = ApexFileRepository::GetInstance();
4310 
4311   ASSERT_THAT(instance.AddPreInstalledApex({GetBuiltInDir()}), Ok());
4312   ASSERT_THAT(android::apex::AddBlockApex(instance), Ok());
4313 
4314   OnStart();
4315   UnmountOnTearDown(path1);
4316   UnmountOnTearDown(path2);
4317 
4318   ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting");
4319   auto apex_mounts = GetApexMounts();
4320 
4321   ASSERT_THAT(apex_mounts,
4322               UnorderedElementsAre("/apex/com.android.apex.test_package",
4323                                    "/apex/com.android.apex.test_package@1",
4324                                    "/apex/com.android.apex.test_package_2",
4325                                    "/apex/com.android.apex.test_package_2@1"));
4326 }
4327 
TEST_F(ApexdMountTest,OnStartBlockAndCompressedInstalled)4328 TEST_F(ApexdMountTest, OnStartBlockAndCompressedInstalled) {
4329   MockCheckpointInterface checkpoint_interface;
4330   // Need to call InitializeVold before calling OnStart
4331   InitializeVold(&checkpoint_interface);
4332 
4333   // Set system property to enable block apexes
4334   SetBlockApexEnabled(true);
4335 
4336   auto path1 = AddPreInstalledApex("com.android.apex.compressed.v1.capex");
4337   auto path2 = AddBlockApex("apex.apexd_test.apex");
4338 
4339   auto& instance = ApexFileRepository::GetInstance();
4340 
4341   ASSERT_THAT(instance.AddPreInstalledApex({GetBuiltInDir()}), Ok());
4342   ASSERT_THAT(android::apex::AddBlockApex(instance), Ok());
4343 
4344   OnStart();
4345   UnmountOnTearDown(path1);
4346   UnmountOnTearDown(path2);
4347 
4348   // Decompressed APEX should be mounted
4349   std::string decompressed_active_apex = StringPrintf(
4350       "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(),
4351       kDecompressedApexPackageSuffix);
4352   UnmountOnTearDown(decompressed_active_apex);
4353 
4354   ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting");
4355   auto apex_mounts = GetApexMounts();
4356   ASSERT_THAT(apex_mounts,
4357               UnorderedElementsAre("/apex/com.android.apex.compressed",
4358                                    "/apex/com.android.apex.compressed@1",
4359                                    "/apex/com.android.apex.test_package",
4360                                    "/apex/com.android.apex.test_package@1"));
4361 }
4362 
4363 // Test that data version of apex is used if newer
TEST_F(ApexdMountTest,BlockAndNewerData)4364 TEST_F(ApexdMountTest, BlockAndNewerData) {
4365   // MockCheckpointInterface checkpoint_interface;
4366   //// Need to call InitializeVold before calling OnStart
4367   // InitializeVold(&checkpoint_interface);
4368 
4369   // Set system property to enable block apexes
4370   SetBlockApexEnabled(true);
4371 
4372   auto& instance = ApexFileRepository::GetInstance();
4373   AddBlockApex("apex.apexd_test.apex");
4374   ASSERT_THAT(android::apex::AddBlockApex(instance), Ok());
4375 
4376   TemporaryDir data_dir;
4377   auto apexd_test_file_v2 =
4378       ApexFile::Open(AddDataApex("apex.apexd_test_v2.apex"));
4379   ASSERT_THAT(instance.AddDataApex(GetDataDir()), Ok());
4380 
4381   auto all_apex = instance.AllApexFilesByName();
4382   auto result = SelectApexForActivation(all_apex, instance);
4383   ASSERT_EQ(result.size(), 1u);
4384 
4385   ASSERT_THAT(result,
4386               UnorderedElementsAre(ApexFileEq(ByRef(*apexd_test_file_v2))));
4387 }
4388 
4389 // Test that data version of apex not is used if older
TEST_F(ApexdMountTest,BlockApexAndOlderData)4390 TEST_F(ApexdMountTest, BlockApexAndOlderData) {
4391   MockCheckpointInterface checkpoint_interface;
4392   // Need to call InitializeVold before calling OnStart
4393   InitializeVold(&checkpoint_interface);
4394 
4395   // Set system property to enable block apexes
4396   SetBlockApexEnabled(true);
4397 
4398   auto& instance = ApexFileRepository::GetInstance();
4399   auto apexd_test_file_v2 =
4400       ApexFile::Open(AddBlockApex("apex.apexd_test_v2.apex"));
4401   ASSERT_THAT(android::apex::AddBlockApex(instance), Ok());
4402 
4403   TemporaryDir data_dir;
4404   AddDataApex("apex.apexd_test.apex");
4405   ASSERT_THAT(instance.AddDataApex(GetDataDir()), Ok());
4406 
4407   auto all_apex = instance.AllApexFilesByName();
4408   auto result = SelectApexForActivation(all_apex, instance);
4409   ASSERT_EQ(result.size(), 1u);
4410 
4411   ASSERT_THAT(result,
4412               UnorderedElementsAre(ApexFileEq(ByRef(*apexd_test_file_v2))));
4413 }
4414 
4415 // Test that AddBlockApex does nothing if system property not set.
TEST_F(ApexdMountTest,AddBlockApexWithoutSystemProp)4416 TEST_F(ApexdMountTest, AddBlockApexWithoutSystemProp) {
4417   MockCheckpointInterface checkpoint_interface;
4418   // Need to call InitializeVold before calling OnStart
4419   InitializeVold(&checkpoint_interface);
4420 
4421   auto& instance = ApexFileRepository::GetInstance();
4422   AddBlockApex("apex.apexd_test.apex");
4423   ASSERT_THAT(android::apex::AddBlockApex(instance), Ok());
4424   ASSERT_EQ(instance.AllApexFilesByName().size(), 0ul);
4425 }
4426 
4427 // Test that adding block apex fails if preinstalled version exists
TEST_F(ApexdMountTest,AddBlockApexFailsWithDuplicate)4428 TEST_F(ApexdMountTest, AddBlockApexFailsWithDuplicate) {
4429   MockCheckpointInterface checkpoint_interface;
4430   // Need to call InitializeVold before calling OnStart
4431   InitializeVold(&checkpoint_interface);
4432 
4433   // Set system property to enable block apexes
4434   SetBlockApexEnabled(true);
4435 
4436   AddPreInstalledApex("apex.apexd_test.apex");
4437   AddBlockApex("apex.apexd_test_v2.apex");
4438 
4439   auto& instance = ApexFileRepository::GetInstance();
4440 
4441   ASSERT_THAT(instance.AddPreInstalledApex({GetBuiltInDir()}), Ok());
4442   ASSERT_THAT(android::apex::AddBlockApex(instance),
4443               HasError(WithMessage(HasSubstr(
4444                   "duplicate of com.android.apex.test_package found"))));
4445 }
4446 
4447 // Test that adding block apex fails if preinstalled compressed version exists
TEST_F(ApexdMountTest,AddBlockApexFailsWithCompressedDuplicate)4448 TEST_F(ApexdMountTest, AddBlockApexFailsWithCompressedDuplicate) {
4449   MockCheckpointInterface checkpoint_interface;
4450   // Need to call InitializeVold before calling OnStart
4451   InitializeVold(&checkpoint_interface);
4452 
4453   // Set system property to enable block apexes
4454   SetBlockApexEnabled(true);
4455 
4456   auto path1 = AddPreInstalledApex("com.android.apex.compressed.v1.capex");
4457   auto path2 = AddBlockApex("com.android.apex.compressed.v1_original.apex");
4458 
4459   auto& instance = ApexFileRepository::GetInstance();
4460 
4461   ASSERT_THAT(instance.AddPreInstalledApex({GetBuiltInDir()}), Ok());
4462   ASSERT_THAT(android::apex::AddBlockApex(instance),
4463               HasError(WithMessage(HasSubstr(
4464                   "duplicate of com.android.apex.compressed found"))));
4465 }
4466 
TEST_F(ApexdMountTest,CopySepolicyToMetadata)4467 TEST_F(ApexdMountTest, CopySepolicyToMetadata) {
4468   std::string file_path = AddPreInstalledApex("com.android.sepolicy.apex");
4469   ASSERT_THAT(
4470       ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}),
4471       Ok());
4472   ASSERT_THAT(ActivatePackage(file_path), Ok());
4473   UnmountOnTearDown(file_path);
4474   ASSERT_THAT(CreateStagedSession("com.android.sepolicy.apex", 666), Ok());
4475 
4476   ASSERT_THAT(
4477       SubmitStagedSession(666, {}, /* has_rollback_enabled= */ false,
4478                           /* is_rollback= */ false, /* rollback_id= */ -1),
4479       Ok());
4480 
4481   auto staged_dir = GetMetadataSepolicyStagedDir();
4482   ASSERT_THAT(PathExists(staged_dir + "/SEPolicy.zip"), HasValue(true));
4483   ASSERT_THAT(PathExists(staged_dir + "/SEPolicy.zip.sig"), HasValue(true));
4484 }
4485 
TEST_F(ApexdMountTest,AbortSepolicyApexInstall)4486 TEST_F(ApexdMountTest, AbortSepolicyApexInstall) {
4487   std::string file_path = AddPreInstalledApex("com.android.sepolicy.apex");
4488   ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
4489   ASSERT_THAT(CreateStagedSession("com.android.sepolicy.apex", 666), Ok());
4490   ASSERT_THAT(
4491       SubmitStagedSession(666, {}, /* has_rollback_enabled= */ false,
4492                           /* is_rollback= */ false, /* rollback_id= */ -1),
4493       Ok());
4494 
4495   auto staged_dir = GetMetadataSepolicyStagedDir();
4496   ASSERT_THAT(PathExists(staged_dir), HasValue(true));
4497   ASSERT_FALSE(IsEmptyDirectory(staged_dir));
4498 
4499   ASSERT_THAT(AbortStagedSession(666), Ok());
4500   ASSERT_THAT(PathExists(staged_dir), HasValue(false));
4501 }
4502 
4503 class ApexActivationFailureTests : public ApexdMountTest {};
4504 
TEST_F(ApexActivationFailureTests,BuildFingerprintDifferent)4505 TEST_F(ApexActivationFailureTests, BuildFingerprintDifferent) {
4506   auto apex_session = CreateStagedSession("apex.apexd_test.apex", 123);
4507   apex_session->SetBuildFingerprint("wrong fingerprint");
4508   apex_session->UpdateStateAndCommit(SessionState::STAGED);
4509 
4510   OnStart();
4511 
4512   apex_session = ApexSession::GetSession(123);
4513   ASSERT_THAT(apex_session->GetErrorMessage(),
4514               HasSubstr("APEX build fingerprint has changed"));
4515 }
4516 
TEST_F(ApexActivationFailureTests,ApexFileMissingInStagingDirectory)4517 TEST_F(ApexActivationFailureTests, ApexFileMissingInStagingDirectory) {
4518   auto apex_session = CreateStagedSession("apex.apexd_test.apex", 123);
4519   apex_session->UpdateStateAndCommit(SessionState::STAGED);
4520   // Delete the apex file in staging directory
4521   DeleteDirContent(GetStagedDir(123));
4522 
4523   OnStart();
4524 
4525   apex_session = ApexSession::GetSession(123);
4526   ASSERT_THAT(apex_session->GetErrorMessage(),
4527               HasSubstr("No APEX packages found"));
4528 }
4529 
TEST_F(ApexActivationFailureTests,MultipleApexFileInStagingDirectory)4530 TEST_F(ApexActivationFailureTests, MultipleApexFileInStagingDirectory) {
4531   auto apex_session = CreateStagedSession("apex.apexd_test.apex", 123);
4532   CreateStagedSession("com.android.apex.compressed.v1_original.apex", 123);
4533   apex_session->UpdateStateAndCommit(SessionState::STAGED);
4534 
4535   OnStart();
4536 
4537   apex_session = ApexSession::GetSession(123);
4538   ASSERT_THAT(apex_session->GetErrorMessage(),
4539               HasSubstr("More than one APEX package found"));
4540 }
4541 
TEST_F(ApexActivationFailureTests,CorruptedSuperblockApexCannotBeStaged)4542 TEST_F(ApexActivationFailureTests, CorruptedSuperblockApexCannotBeStaged) {
4543   auto apex_session =
4544       CreateStagedSession("apex.apexd_test_corrupt_superblock_apex.apex", 123);
4545   apex_session->UpdateStateAndCommit(SessionState::STAGED);
4546 
4547   OnStart();
4548 
4549   apex_session = ApexSession::GetSession(123);
4550   ASSERT_THAT(apex_session->GetErrorMessage(),
4551               HasSubstr("Couldn't find filesystem magic"));
4552 }
4553 
TEST_F(ApexActivationFailureTests,CorruptedApexCannotBeStaged)4554 TEST_F(ApexActivationFailureTests, CorruptedApexCannotBeStaged) {
4555   auto apex_session = CreateStagedSession("corrupted_b146895998.apex", 123);
4556   apex_session->UpdateStateAndCommit(SessionState::STAGED);
4557 
4558   OnStart();
4559 
4560   apex_session = ApexSession::GetSession(123);
4561   ASSERT_THAT(apex_session->GetErrorMessage(),
4562               HasSubstr("Activation failed for packages"));
4563 }
4564 
TEST_F(ApexActivationFailureTests,ActivatePackageImplFails)4565 TEST_F(ApexActivationFailureTests, ActivatePackageImplFails) {
4566   auto shim_path = AddPreInstalledApex("com.android.apex.cts.shim.apex");
4567   auto& instance = ApexFileRepository::GetInstance();
4568   ASSERT_THAT(instance.AddPreInstalledApex({GetBuiltInDir()}), Ok());
4569 
4570   auto apex_session =
4571       CreateStagedSession("com.android.apex.cts.shim.v2_wrong_sha.apex", 123);
4572   apex_session->UpdateStateAndCommit(SessionState::STAGED);
4573 
4574   UnmountOnTearDown(shim_path);
4575   OnStart();
4576 
4577   apex_session = ApexSession::GetSession(123);
4578   ASSERT_THAT(apex_session->GetErrorMessage(),
4579               HasSubstr("Failed to activate packages"));
4580   ASSERT_THAT(apex_session->GetErrorMessage(),
4581               HasSubstr("has unexpected SHA512 hash"));
4582 }
4583 
TEST_F(ApexActivationFailureTests,StagedSessionFailsWhenNotInFsCheckpointMode)4584 TEST_F(ApexActivationFailureTests,
4585        StagedSessionFailsWhenNotInFsCheckpointMode) {
4586   MockCheckpointInterface checkpoint_interface;
4587   checkpoint_interface.SetSupportsCheckpoint(true);
4588   // Need to call InitializeVold before calling OnStart
4589   InitializeVold(&checkpoint_interface);
4590 
4591   auto pre_installed_apex = AddPreInstalledApex("apex.apexd_test.apex");
4592   auto& instance = ApexFileRepository::GetInstance();
4593   ASSERT_THAT(instance.AddPreInstalledApex({GetBuiltInDir()}), Ok());
4594 
4595   auto apex_session = CreateStagedSession("apex.apexd_test.apex", 123);
4596   apex_session->UpdateStateAndCommit(SessionState::STAGED);
4597 
4598   UnmountOnTearDown(pre_installed_apex);
4599   OnStart();
4600 
4601   apex_session = ApexSession::GetSession(123);
4602   ASSERT_EQ(apex_session->GetState(), SessionState::ACTIVATION_FAILED);
4603   ASSERT_THAT(
4604       apex_session->GetErrorMessage(),
4605       HasSubstr("Cannot install apex session if not in fs-checkpoint mode"));
4606 }
4607 
TEST_F(ApexActivationFailureTests,StagedSessionRevertsWhenInFsRollbackMode)4608 TEST_F(ApexActivationFailureTests, StagedSessionRevertsWhenInFsRollbackMode) {
4609   MockCheckpointInterface checkpoint_interface;
4610   checkpoint_interface.SetSupportsCheckpoint(true);
4611   checkpoint_interface.SetNeedsRollback(true);
4612   // Need to call InitializeVold before calling OnStart
4613   InitializeVold(&checkpoint_interface);
4614 
4615   auto pre_installed_apex = AddPreInstalledApex("apex.apexd_test.apex");
4616   auto& instance = ApexFileRepository::GetInstance();
4617   ASSERT_THAT(instance.AddPreInstalledApex({GetBuiltInDir()}), Ok());
4618 
4619   auto apex_session = CreateStagedSession("apex.apexd_test.apex", 123);
4620   apex_session->UpdateStateAndCommit(SessionState::STAGED);
4621 
4622   UnmountOnTearDown(pre_installed_apex);
4623   OnStart();
4624 
4625   apex_session = ApexSession::GetSession(123);
4626   ASSERT_EQ(apex_session->GetState(), SessionState::REVERTED);
4627 }
4628 
TEST_F(ApexdMountTest,OnBootstrapCreatesEmptyDmDevices)4629 TEST_F(ApexdMountTest, OnBootstrapCreatesEmptyDmDevices) {
4630   AddPreInstalledApex("apex.apexd_test.apex");
4631   AddPreInstalledApex("com.android.apex.compressed.v1.capex");
4632 
4633   DeviceMapper& dm = DeviceMapper::Instance();
4634 
4635   auto cleaner = make_scope_guard([&]() {
4636     dm.DeleteDeviceIfExists("com.android.apex.test_package", 1s);
4637     dm.DeleteDeviceIfExists("com.android.apex.compressed", 1s);
4638   });
4639 
4640   ASSERT_EQ(0, OnBootstrap());
4641 
4642   ASSERT_EQ(dm::DmDeviceState::SUSPENDED,
4643             dm.GetState("com.android.apex.test_package"));
4644   ASSERT_EQ(dm::DmDeviceState::SUSPENDED,
4645             dm.GetState("com.android.apex.compressed"));
4646 }
4647 
TEST_F(ApexdUnitTest,StagePackagesFailKey)4648 TEST_F(ApexdUnitTest, StagePackagesFailKey) {
4649   auto status =
4650       StagePackages({GetTestFile("apex.apexd_test_no_inst_key.apex")});
4651 
4652   ASSERT_THAT(
4653       status,
4654       HasError(WithMessage(("No preinstalled apex found for package "
4655                             "com.android.apex.test_package.no_inst_key"))));
4656 }
4657 
TEST_F(ApexdUnitTest,StagePackagesSuccess)4658 TEST_F(ApexdUnitTest, StagePackagesSuccess) {
4659   AddPreInstalledApex("apex.apexd_test.apex");
4660   auto& instance = ApexFileRepository::GetInstance();
4661   ASSERT_THAT(instance.AddPreInstalledApex({GetBuiltInDir()}), Ok());
4662 
4663   auto status = StagePackages({GetTestFile("apex.apexd_test.apex")});
4664   ASSERT_THAT(status, Ok());
4665 
4666   auto staged_path = StringPrintf("%s/com.android.apex.test_package@1.apex",
4667                                   GetDataDir().c_str());
4668   ASSERT_EQ(0, access(staged_path.c_str(), F_OK));
4669 }
4670 
TEST_F(ApexdUnitTest,StagePackagesClearsPreviouslyActivePackage)4671 TEST_F(ApexdUnitTest, StagePackagesClearsPreviouslyActivePackage) {
4672   AddPreInstalledApex("apex.apexd_test.apex");
4673   auto& instance = ApexFileRepository::GetInstance();
4674   ASSERT_THAT(instance.AddPreInstalledApex({GetBuiltInDir()}), Ok());
4675 
4676   auto current_apex = AddDataApex("apex.apexd_test.apex");
4677   ASSERT_EQ(0, access(current_apex.c_str(), F_OK));
4678 
4679   auto status = StagePackages({GetTestFile("apex.apexd_test_v2.apex")});
4680   ASSERT_THAT(status, Ok());
4681 
4682   auto staged_path = StringPrintf("%s/com.android.apex.test_package@2.apex",
4683                                   GetDataDir().c_str());
4684   ASSERT_EQ(0, access(staged_path.c_str(), F_OK));
4685   ASSERT_EQ(-1, access(current_apex.c_str(), F_OK));
4686   ASSERT_EQ(ENOENT, errno);
4687 }
4688 
TEST_F(ApexdUnitTest,StagePackagesClearsPreviouslyActivePackageDowngrade)4689 TEST_F(ApexdUnitTest, StagePackagesClearsPreviouslyActivePackageDowngrade) {
4690   AddPreInstalledApex("apex.apexd_test.apex");
4691   auto& instance = ApexFileRepository::GetInstance();
4692   ASSERT_THAT(instance.AddPreInstalledApex({GetBuiltInDir()}), Ok());
4693 
4694   auto current_apex = AddDataApex("apex.apexd_test_v2.apex");
4695   ASSERT_EQ(0, access(current_apex.c_str(), F_OK));
4696 
4697   auto status = StagePackages({GetTestFile("apex.apexd_test.apex")});
4698   ASSERT_THAT(status, Ok());
4699 
4700   auto staged_path = StringPrintf("%s/com.android.apex.test_package@1.apex",
4701                                   GetDataDir().c_str());
4702   ASSERT_EQ(0, access(staged_path.c_str(), F_OK));
4703   ASSERT_EQ(-1, access(current_apex.c_str(), F_OK));
4704   ASSERT_EQ(ENOENT, errno);
4705 }
4706 
TEST_F(ApexdUnitTest,StagePackagesAlreadyStagedPackage)4707 TEST_F(ApexdUnitTest, StagePackagesAlreadyStagedPackage) {
4708   AddPreInstalledApex("apex.apexd_test.apex");
4709   auto& instance = ApexFileRepository::GetInstance();
4710   ASSERT_THAT(instance.AddPreInstalledApex({GetBuiltInDir()}), Ok());
4711 
4712   auto status = StagePackages({GetTestFile("apex.apexd_test.apex")});
4713   ASSERT_THAT(status, Ok());
4714 
4715   auto staged_path = StringPrintf("%s/com.android.apex.test_package@1.apex",
4716                                   GetDataDir().c_str());
4717   struct stat stat1;
4718   ASSERT_EQ(0, stat(staged_path.c_str(), &stat1));
4719   ASSERT_TRUE(S_ISREG(stat1.st_mode));
4720 
4721   {
4722     auto apex = ApexFile::Open(staged_path);
4723     ASSERT_THAT(apex, Ok());
4724     ASSERT_FALSE(apex->GetManifest().nocode());
4725   }
4726 
4727   auto status2 = StagePackages({GetTestFile("apex.apexd_test_nocode.apex")});
4728   ASSERT_THAT(status2, Ok());
4729 
4730   struct stat stat2;
4731   ASSERT_EQ(0, stat(staged_path.c_str(), &stat2));
4732   ASSERT_TRUE(S_ISREG(stat2.st_mode));
4733 
4734   ASSERT_NE(stat1.st_ino, stat2.st_ino);
4735 
4736   {
4737     auto apex = ApexFile::Open(staged_path);
4738     ASSERT_THAT(apex, Ok());
4739     ASSERT_TRUE(apex->GetManifest().nocode());
4740   }
4741 }
4742 
TEST_F(ApexdUnitTest,StagePackagesMultiplePackages)4743 TEST_F(ApexdUnitTest, StagePackagesMultiplePackages) {
4744   AddPreInstalledApex("apex.apexd_test.apex");
4745   AddPreInstalledApex("apex.apexd_test_different_app.apex");
4746   auto& instance = ApexFileRepository::GetInstance();
4747   ASSERT_THAT(instance.AddPreInstalledApex({GetBuiltInDir()}), Ok());
4748 
4749   auto status =
4750       StagePackages({GetTestFile("apex.apexd_test_v2.apex"),
4751                      GetTestFile("apex.apexd_test_different_app.apex")});
4752   ASSERT_THAT(status, Ok());
4753 
4754   auto staged_path1 = StringPrintf("%s/com.android.apex.test_package@2.apex",
4755                                    GetDataDir().c_str());
4756   auto staged_path2 = StringPrintf("%s/com.android.apex.test_package_2@1.apex",
4757                                    GetDataDir().c_str());
4758   ASSERT_EQ(0, access(staged_path1.c_str(), F_OK));
4759   ASSERT_EQ(0, access(staged_path2.c_str(), F_OK));
4760 }
4761 
TEST_F(ApexdUnitTest,UnstagePackages)4762 TEST_F(ApexdUnitTest, UnstagePackages) {
4763   auto file_path1 = AddDataApex("apex.apexd_test.apex");
4764   auto file_path2 = AddDataApex("apex.apexd_test_different_app.apex");
4765 
4766   ASSERT_THAT(UnstagePackages({file_path1}), Ok());
4767   ASSERT_EQ(-1, access(file_path1.c_str(), F_OK));
4768   ASSERT_EQ(errno, ENOENT);
4769   ASSERT_EQ(0, access(file_path2.c_str(), F_OK));
4770 }
4771 
TEST_F(ApexdUnitTest,UnstagePackagesEmptyInput)4772 TEST_F(ApexdUnitTest, UnstagePackagesEmptyInput) {
4773   auto file_path1 = AddDataApex("apex.apexd_test.apex");
4774   auto file_path2 = AddDataApex("apex.apexd_test_different_app.apex");
4775 
4776   ASSERT_THAT(UnstagePackages({}),
4777               HasError(WithMessage("Empty set of inputs")));
4778   ASSERT_EQ(0, access(file_path1.c_str(), F_OK));
4779   ASSERT_EQ(0, access(file_path2.c_str(), F_OK));
4780 }
4781 
TEST_F(ApexdUnitTest,UnstagePackagesFail)4782 TEST_F(ApexdUnitTest, UnstagePackagesFail) {
4783   auto file_path1 = AddDataApex("apex.apexd_test.apex");
4784   auto bad_path = GetDataDir() + "/missing.apex";
4785 
4786   ASSERT_THAT(UnstagePackages({file_path1, bad_path}), Not(Ok()));
4787   ASSERT_EQ(0, access(file_path1.c_str(), F_OK));
4788 }
4789 
TEST_F(ApexdUnitTest,UnstagePackagesFailPreInstalledApex)4790 TEST_F(ApexdUnitTest, UnstagePackagesFailPreInstalledApex) {
4791   auto file_path1 = AddPreInstalledApex("apex.apexd_test.apex");
4792   auto file_path2 = AddDataApex("apex.apexd_test_different_app.apex");
4793 
4794   auto& instance = ApexFileRepository::GetInstance();
4795   ASSERT_THAT(instance.AddPreInstalledApex({GetBuiltInDir()}), Ok());
4796 
4797   ASSERT_THAT(UnstagePackages({file_path1, file_path2}),
4798               HasError(WithMessage("Can't uninstall pre-installed apex " +
4799                                    file_path1)));
4800   ASSERT_EQ(0, access(file_path1.c_str(), F_OK));
4801   ASSERT_EQ(0, access(file_path2.c_str(), F_OK));
4802 }
4803 
TEST_F(ApexdUnitTest,RevertStoresCrashingNativeProcess)4804 TEST_F(ApexdUnitTest, RevertStoresCrashingNativeProcess) {
4805   MockCheckpointInterface checkpoint_interface;
4806   checkpoint_interface.SetSupportsCheckpoint(true);
4807   InitializeVold(&checkpoint_interface);
4808 
4809   auto apex_session = CreateStagedSession("apex.apexd_test.apex", 1543);
4810   ASSERT_THAT(apex_session, Ok());
4811   ASSERT_THAT(apex_session->UpdateStateAndCommit(SessionState::ACTIVATED),
4812               Ok());
4813 
4814   ASSERT_THAT(RevertActiveSessions("test_process", ""), Ok());
4815   apex_session = ApexSession::GetSession(1543);
4816   ASSERT_THAT(apex_session, Ok());
4817   ASSERT_EQ(apex_session->GetCrashingNativeProcess(), "test_process");
4818 }
4819 
TEST_F(ApexdUnitTest,MountAndDeriveClasspathNoJar)4820 TEST_F(ApexdUnitTest, MountAndDeriveClasspathNoJar) {
4821   AddPreInstalledApex("apex.apexd_test_classpath.apex");
4822   ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
4823 
4824   // Call MountAndDeriveClassPath
4825   auto apex_file = ApexFile::Open(GetTestFile("apex.apexd_test.apex"));
4826   auto package_name = apex_file->GetManifest().name();
4827   std::vector<ApexFile> apex_files;
4828   apex_files.emplace_back(std::move(*apex_file));
4829   auto class_path = MountAndDeriveClassPath(apex_files);
4830   ASSERT_THAT(class_path, Ok());
4831   ASSERT_THAT(class_path->HasClassPathJars(package_name), false);
4832 }
4833 
TEST_F(ApexdUnitTest,MountAndDeriveClassPathJarsPresent)4834 TEST_F(ApexdUnitTest, MountAndDeriveClassPathJarsPresent) {
4835   AddPreInstalledApex("apex.apexd_test_classpath.apex");
4836   ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
4837 
4838   // Call MountAndDeriveClassPath
4839   auto apex_file =
4840       ApexFile::Open(GetTestFile("apex.apexd_test_classpath.apex"));
4841   auto package_name = apex_file->GetManifest().name();
4842   std::vector<ApexFile> apex_files;
4843   apex_files.emplace_back(std::move(*apex_file));
4844   auto class_path = MountAndDeriveClassPath(apex_files);
4845   ASSERT_THAT(class_path, Ok());
4846   ASSERT_THAT(class_path->HasClassPathJars(package_name), true);
4847 }
4848 
TEST_F(ApexdUnitTest,ProcessCompressedApexWrongSELinuxContext)4849 TEST_F(ApexdUnitTest, ProcessCompressedApexWrongSELinuxContext) {
4850   auto compressed_apex = ApexFile::Open(
4851       AddPreInstalledApex("com.android.apex.compressed.v1.capex"));
4852 
4853   std::vector<ApexFileRef> compressed_apex_list;
4854   compressed_apex_list.emplace_back(std::cref(*compressed_apex));
4855   auto return_value =
4856       ProcessCompressedApex(compressed_apex_list, /* is_ota_chroot= */ false);
4857   ASSERT_EQ(return_value.size(), 1u);
4858 
4859   auto decompressed_apex_path = StringPrintf(
4860       "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(),
4861       kDecompressedApexPackageSuffix);
4862   // Verify that so far it has correct context.
4863   ASSERT_EQ(kTestActiveApexSelinuxCtx,
4864             GetSelinuxContext(decompressed_apex_path));
4865 
4866   // Manually mess up the context
4867   ASSERT_EQ(0, setfilecon(decompressed_apex_path.c_str(),
4868                           "u:object_r:apex_data_file:s0"));
4869   ASSERT_EQ("u:object_r:apex_data_file:s0",
4870             GetSelinuxContext(decompressed_apex_path));
4871 
4872   auto attempt_2 =
4873       ProcessCompressedApex(compressed_apex_list, /* is_ota_chroot= */ false);
4874   ASSERT_EQ(attempt_2.size(), 1u);
4875   // Verify that it again has correct context.
4876   ASSERT_EQ(kTestActiveApexSelinuxCtx,
4877             GetSelinuxContext(decompressed_apex_path));
4878 }
4879 
TEST_F(ApexdMountTest,OnStartNoApexUpdated)4880 TEST_F(ApexdMountTest, OnStartNoApexUpdated) {
4881   MockCheckpointInterface checkpoint_interface;
4882   // Need to call InitializeVold before calling OnStart
4883   InitializeVold(&checkpoint_interface);
4884 
4885   AddPreInstalledApex("com.android.apex.compressed.v1.capex");
4886   std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex");
4887   std::string apex_path_2 =
4888       AddPreInstalledApex("apex.apexd_test_different_app.apex");
4889   std::string apex_path_3 = AddDataApex("apex.apexd_test_v2.apex");
4890   std::string apex_path_4 =
4891       AddDecompressedApex("com.android.apex.compressed.v1_original.apex");
4892 
4893   ASSERT_THAT(
4894       ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}),
4895       Ok());
4896 
4897   OnStart();
4898 
4899   UnmountOnTearDown(apex_path_2);
4900   UnmountOnTearDown(apex_path_3);
4901   UnmountOnTearDown(apex_path_4);
4902 
4903   auto updated_apexes = GetChangedActiveApexesForTesting();
4904   ASSERT_EQ(updated_apexes.size(), 0u);
4905   // Quick check that all apexes were mounted
4906   auto apex_mounts = GetApexMounts();
4907   ASSERT_EQ(apex_mounts.size(), 6u);
4908 }
4909 
TEST_F(ApexdMountTest,OnStartDecompressingConsideredApexUpdate)4910 TEST_F(ApexdMountTest, OnStartDecompressingConsideredApexUpdate) {
4911   MockCheckpointInterface checkpoint_interface;
4912   // Need to call InitializeVold before calling OnStart
4913   InitializeVold(&checkpoint_interface);
4914 
4915   AddPreInstalledApex("com.android.apex.compressed.v1.capex");
4916   std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex");
4917   std::string decompressed_active_apex = StringPrintf(
4918       "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(),
4919       kDecompressedApexPackageSuffix);
4920   UnmountOnTearDown(decompressed_active_apex);
4921 
4922   ASSERT_THAT(
4923       ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}),
4924       Ok());
4925 
4926   OnStart();
4927 
4928   UnmountOnTearDown(apex_path_1);
4929   UnmountOnTearDown(decompressed_active_apex);
4930 
4931   auto updated_apexes = GetChangedActiveApexesForTesting();
4932   ASSERT_EQ(updated_apexes.size(), 1u);
4933   auto apex_file = ApexFile::Open(decompressed_active_apex);
4934   ASSERT_THAT(apex_file, Ok());
4935   ASSERT_TRUE(IsActiveApexChanged(*apex_file));
4936 }
4937 
TEST_F(ApexdMountTest,ActivatesStagedSession)4938 TEST_F(ApexdMountTest, ActivatesStagedSession) {
4939   MockCheckpointInterface checkpoint_interface;
4940   // Need to call InitializeVold before calling OnStart
4941   InitializeVold(&checkpoint_interface);
4942 
4943   std::string preinstalled_apex = AddPreInstalledApex("apex.apexd_test.apex");
4944   auto apex_session = CreateStagedSession("apex.apexd_test_v2.apex", 37);
4945   apex_session->UpdateStateAndCommit(SessionState::STAGED);
4946 
4947   ASSERT_THAT(
4948       ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}),
4949       Ok());
4950 
4951   std::string active_apex =
4952       GetDataDir() + "/" + "com.android.apex.test_package@2.apex";
4953 
4954   UnmountOnTearDown(preinstalled_apex);
4955   UnmountOnTearDown(active_apex);
4956   OnStart();
4957 
4958   // Quick check that session was activated
4959   {
4960     auto session = ApexSession::GetSession(37);
4961     ASSERT_THAT(session, Ok());
4962     ASSERT_EQ(session->GetState(), SessionState::ACTIVATED);
4963   }
4964 
4965   auto updated_apexes = GetChangedActiveApexesForTesting();
4966   ASSERT_EQ(updated_apexes.size(), 1u);
4967   auto apex_file = ApexFile::Open(active_apex);
4968   ASSERT_THAT(apex_file, Ok());
4969   ASSERT_TRUE(IsActiveApexChanged(*apex_file));
4970 }
4971 
TEST_F(ApexdMountTest,FailsToActivateStagedSession)4972 TEST_F(ApexdMountTest, FailsToActivateStagedSession) {
4973   MockCheckpointInterface checkpoint_interface;
4974   // Need to call InitializeVold before calling OnStart
4975   InitializeVold(&checkpoint_interface);
4976 
4977   std::string preinstalled_apex = AddPreInstalledApex("apex.apexd_test.apex");
4978   auto apex_session =
4979       CreateStagedSession("apex.apexd_test_manifest_mismatch.apex", 73);
4980   apex_session->UpdateStateAndCommit(SessionState::STAGED);
4981 
4982   ASSERT_THAT(
4983       ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}),
4984       Ok());
4985 
4986   UnmountOnTearDown(preinstalled_apex);
4987   OnStart();
4988 
4989   // Quick check that session was activated
4990   {
4991     auto session = ApexSession::GetSession(73);
4992     ASSERT_THAT(session, Ok());
4993     ASSERT_NE(session->GetState(), SessionState::ACTIVATED);
4994   }
4995 
4996   auto updated_apexes = GetChangedActiveApexesForTesting();
4997   ASSERT_EQ(updated_apexes.size(), 1u);
4998 
4999   auto apex_file = ApexFile::Open(preinstalled_apex);
5000   ASSERT_THAT(apex_file, Ok());
5001   ASSERT_TRUE(IsActiveApexChanged(*apex_file));
5002 }
5003 
TEST_F(ApexdMountTest,FailsToActivateApexFallbacksToSystemOne)5004 TEST_F(ApexdMountTest, FailsToActivateApexFallbacksToSystemOne) {
5005   MockCheckpointInterface checkpoint_interface;
5006   // Need to call InitializeVold before calling OnStart
5007   InitializeVold(&checkpoint_interface);
5008 
5009   std::string preinstalled_apex = AddPreInstalledApex("apex.apexd_test.apex");
5010   AddDataApex("apex.apexd_test_manifest_mismatch.apex");
5011 
5012   ASSERT_THAT(
5013       ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}),
5014       Ok());
5015 
5016   UnmountOnTearDown(preinstalled_apex);
5017   OnStart();
5018 
5019   auto updated_apexes = GetChangedActiveApexesForTesting();
5020   ASSERT_EQ(updated_apexes.size(), 1u);
5021 
5022   auto apex_file = ApexFile::Open(preinstalled_apex);
5023   ASSERT_THAT(apex_file, Ok());
5024   ASSERT_TRUE(IsActiveApexChanged(*apex_file));
5025 }
5026 
5027 class LogTestToLogcat : public ::testing::EmptyTestEventListener {
OnTestStart(const::testing::TestInfo & test_info)5028   void OnTestStart(const ::testing::TestInfo& test_info) override {
5029 #ifdef __ANDROID__
5030     using base::LogId;
5031     using base::LogSeverity;
5032     using base::StringPrintf;
5033     base::LogdLogger l;
5034     std::string msg =
5035         StringPrintf("=== %s::%s (%s:%d)", test_info.test_suite_name(),
5036                      test_info.name(), test_info.file(), test_info.line());
5037     l(LogId::MAIN, LogSeverity::INFO, "ApexTestCases", __FILE__, __LINE__,
5038       msg.c_str());
5039 #else
5040     UNUSED(test_info);
5041 #endif
5042   }
5043 };
5044 
5045 }  // namespace apex
5046 }  // namespace android
5047 
main(int argc,char ** argv)5048 int main(int argc, char** argv) {
5049   ::testing::InitGoogleTest(&argc, argv);
5050   android::base::InitLogging(argv, &android::base::StderrLogger);
5051   android::base::SetMinimumLogSeverity(android::base::VERBOSE);
5052   ::testing::UnitTest::GetInstance()->listeners().Append(
5053       new android::apex::LogTestToLogcat());
5054   return RUN_ALL_TESTS();
5055 }
5056