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