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