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