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