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