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