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 <string>
18 #include <vector>
19
20 #include <android-base/file.h>
21 #include <android-base/properties.h>
22 #include <android-base/scopeguard.h>
23 #include <android-base/stringprintf.h>
24 #include <gmock/gmock.h>
25 #include <gtest/gtest.h>
26
27 #include "apex_database.h"
28 #include "apex_file_repository.h"
29 #include "apexd.h"
30 #include "apexd_checkpoint.h"
31 #include "apexd_session.h"
32 #include "apexd_test_utils.h"
33 #include "apexd_utils.h"
34
35 #include "apex_manifest.pb.h"
36 #include "com_android_apex.h"
37 #include "gmock/gmock-matchers.h"
38
39 namespace android {
40 namespace apex {
41
42 namespace fs = std::filesystem;
43
44 using MountedApexData = MountedApexDatabase::MountedApexData;
45 using android::apex::testing::ApexFileEq;
46 using android::apex::testing::IsOk;
47 using android::base::GetExecutableDirectory;
48 using android::base::GetProperty;
49 using android::base::make_scope_guard;
50 using android::base::RemoveFileIfExists;
51 using android::base::Result;
52 using android::base::StringPrintf;
53 using android::base::unique_fd;
54 using android::base::WriteStringToFile;
55 using com::android::apex::testing::ApexInfoXmlEq;
56 using ::testing::ByRef;
57 using ::testing::HasSubstr;
58 using ::testing::IsEmpty;
59 using ::testing::StartsWith;
60 using ::testing::UnorderedElementsAre;
61 using ::testing::UnorderedElementsAreArray;
62
GetTestDataDir()63 static std::string GetTestDataDir() { return GetExecutableDirectory(); }
GetTestFile(const std::string & name)64 static std::string GetTestFile(const std::string& name) {
65 return GetTestDataDir() + "/" + name;
66 }
67
GetMTime(const std::string & path)68 static int64_t GetMTime(const std::string& path) {
69 struct stat st_buf;
70 if (stat(path.c_str(), &st_buf) != 0) {
71 PLOG(ERROR) << "Failed to stat " << path;
72 return 0;
73 }
74 return st_buf.st_mtime;
75 }
76
77 // A very basic mock of CheckpointInterface.
78 class MockCheckpointInterface : public CheckpointInterface {
79 public:
SupportsFsCheckpoints()80 Result<bool> SupportsFsCheckpoints() override {
81 return supports_fs_checkpoint_;
82 }
83
NeedsCheckpoint()84 Result<bool> NeedsCheckpoint() override { return needs_checkpoint_; }
85
NeedsRollback()86 Result<bool> NeedsRollback() override { return needs_rollback_; }
87
StartCheckpoint(int32_t num_retries)88 Result<void> StartCheckpoint(int32_t num_retries) override { return {}; }
89
AbortChanges(const std::string & msg,bool retry)90 Result<void> AbortChanges(const std::string& msg, bool retry) override {
91 return {};
92 }
93
SetSupportsCheckpoint(bool value)94 void SetSupportsCheckpoint(bool value) { supports_fs_checkpoint_ = value; }
95
SetNeedsCheckpoint(bool value)96 void SetNeedsCheckpoint(bool value) { needs_checkpoint_ = value; }
97
SetNeedsRollback(bool value)98 void SetNeedsRollback(bool value) { needs_rollback_ = value; }
99
100 private:
101 bool supports_fs_checkpoint_, needs_checkpoint_, needs_rollback_;
102 };
103
104 static constexpr const char* kTestApexdStatusSysprop = "apexd.status.test";
105
106 // A test fixture that provides frequently required temp directories for tests
107 class ApexdUnitTest : public ::testing::Test {
108 public:
ApexdUnitTest()109 ApexdUnitTest() {
110 built_in_dir_ = StringPrintf("%s/pre-installed-apex", td_.path);
111 data_dir_ = StringPrintf("%s/data-apex", td_.path);
112 decompression_dir_ = StringPrintf("%s/decompressed-apex", td_.path);
113 ota_reserved_dir_ = StringPrintf("%s/ota-reserved", td_.path);
114 hash_tree_dir_ = StringPrintf("%s/apex-hash-tree", td_.path);
115 staged_session_dir_ = StringPrintf("%s/staged-session-dir", td_.path);
116 config_ = {kTestApexdStatusSysprop, {built_in_dir_},
117 data_dir_.c_str(), decompression_dir_.c_str(),
118 ota_reserved_dir_.c_str(), hash_tree_dir_.c_str(),
119 staged_session_dir_.c_str()};
120 }
121
GetBuiltInDir()122 const std::string& GetBuiltInDir() { return built_in_dir_; }
GetDataDir()123 const std::string& GetDataDir() { return data_dir_; }
GetDecompressionDir()124 const std::string& GetDecompressionDir() { return decompression_dir_; }
GetOtaReservedDir()125 const std::string& GetOtaReservedDir() { return ota_reserved_dir_; }
GetHashTreeDir()126 const std::string& GetHashTreeDir() { return hash_tree_dir_; }
GetStagedDir(int session_id)127 const std::string GetStagedDir(int session_id) {
128 return StringPrintf("%s/session_%d", staged_session_dir_.c_str(),
129 session_id);
130 }
131
GetRootDigest(const ApexFile & apex)132 std::string GetRootDigest(const ApexFile& apex) {
133 if (apex.IsCompressed()) {
134 return "";
135 }
136 auto digest = apex.VerifyApexVerity(apex.GetBundledPublicKey());
137 if (!digest.ok()) {
138 return "";
139 }
140 return digest->root_digest;
141 }
142
AddPreInstalledApex(const std::string & apex_name)143 std::string AddPreInstalledApex(const std::string& apex_name) {
144 fs::copy(GetTestFile(apex_name), built_in_dir_);
145 return StringPrintf("%s/%s", built_in_dir_.c_str(), apex_name.c_str());
146 }
147
AddDataApex(const std::string & apex_name)148 std::string AddDataApex(const std::string& apex_name) {
149 fs::copy(GetTestFile(apex_name), data_dir_);
150 return StringPrintf("%s/%s", data_dir_.c_str(), apex_name.c_str());
151 }
152
AddDataApex(const std::string & apex_name,const std::string & target_name)153 std::string AddDataApex(const std::string& apex_name,
154 const std::string& target_name) {
155 fs::copy(GetTestFile(apex_name), data_dir_ + "/" + target_name);
156 return StringPrintf("%s/%s", data_dir_.c_str(), target_name.c_str());
157 }
158
159 // Copies the compressed apex to |built_in_dir| and decompresses it to
160 // |decompressed_dir| and then hard links to |target_dir|
PrepareCompressedApex(const std::string & name,const std::string & built_in_dir)161 std::string PrepareCompressedApex(const std::string& name,
162 const std::string& built_in_dir) {
163 fs::copy(GetTestFile(name), built_in_dir);
164 auto compressed_apex = ApexFile::Open(
165 StringPrintf("%s/%s", built_in_dir.c_str(), name.c_str()));
166 std::vector<ApexFileRef> compressed_apex_list;
167 compressed_apex_list.emplace_back(std::cref(*compressed_apex));
168 auto return_value =
169 ProcessCompressedApex(compressed_apex_list, /*is_ota_chroot*/ false);
170 return StringPrintf("%s/%s", built_in_dir.c_str(), name.c_str());
171 }
172
PrepareCompressedApex(const std::string & name)173 std::string PrepareCompressedApex(const std::string& name) {
174 return PrepareCompressedApex(name, built_in_dir_);
175 }
176
CreateStagedSession(const std::string & apex_name,int session_id)177 Result<ApexSession> CreateStagedSession(const std::string& apex_name,
178 int session_id) {
179 CreateDirIfNeeded(GetStagedDir(session_id), 0755);
180 fs::copy(GetTestFile(apex_name), GetStagedDir(session_id));
181 auto result = ApexSession::CreateSession(session_id);
182 result->SetBuildFingerprint(GetProperty("ro.build.fingerprint", ""));
183 return result;
184 }
185
186 protected:
SetUp()187 void SetUp() override {
188 SetConfig(config_);
189 ApexFileRepository::GetInstance().Reset(decompression_dir_);
190 ASSERT_EQ(mkdir(built_in_dir_.c_str(), 0755), 0);
191 ASSERT_EQ(mkdir(data_dir_.c_str(), 0755), 0);
192 ASSERT_EQ(mkdir(decompression_dir_.c_str(), 0755), 0);
193 ASSERT_EQ(mkdir(ota_reserved_dir_.c_str(), 0755), 0);
194 ASSERT_EQ(mkdir(hash_tree_dir_.c_str(), 0755), 0);
195 ASSERT_EQ(mkdir(staged_session_dir_.c_str(), 0755), 0);
196
197 DeleteDirContent(ApexSession::GetSessionsDir());
198 }
199
TearDown()200 void TearDown() override { DeleteDirContent(ApexSession::GetSessionsDir()); }
201
202 private:
203 TemporaryDir td_;
204 std::string built_in_dir_;
205 std::string data_dir_;
206 std::string decompression_dir_;
207 std::string ota_reserved_dir_;
208 std::string hash_tree_dir_;
209 std::string staged_session_dir_;
210 ApexdConfig config_;
211 };
212
213 // Apex that does not have pre-installed version, does not get selected
TEST_F(ApexdUnitTest,ApexMustHavePreInstalledVersionForSelection)214 TEST_F(ApexdUnitTest, ApexMustHavePreInstalledVersionForSelection) {
215 AddPreInstalledApex("apex.apexd_test.apex");
216 AddPreInstalledApex("com.android.apex.cts.shim.apex");
217 auto shared_lib_1 = ApexFile::Open(AddPreInstalledApex(
218 "com.android.apex.test.sharedlibs_generated.v1.libvX.apex"));
219 auto& instance = ApexFileRepository::GetInstance();
220 // Pre-installed data needs to be present so that we can add data apex
221 ASSERT_TRUE(IsOk(instance.AddPreInstalledApex({GetBuiltInDir()})));
222
223 auto apexd_test_file = ApexFile::Open(AddDataApex("apex.apexd_test.apex"));
224 auto shim_v1 = ApexFile::Open(AddDataApex("com.android.apex.cts.shim.apex"));
225 // Normally both pre-installed and data apex would be activated for a shared
226 // libs apex, but if they are the same version only the data apex will be.
227 auto shared_lib_2 = ApexFile::Open(
228 AddDataApex("com.android.apex.test.sharedlibs_generated.v1.libvX.apex"));
229 ASSERT_TRUE(IsOk(instance.AddDataApex(GetDataDir())));
230
231 const auto all_apex = instance.AllApexFilesByName();
232 // Pass a blank instance so that the data apex files are not considered
233 // pre-installed
234 const ApexFileRepository instance_blank;
235 auto result = SelectApexForActivation(all_apex, instance_blank);
236 ASSERT_EQ(result.size(), 0u);
237 // When passed proper instance they should get selected
238 result = SelectApexForActivation(all_apex, instance);
239 ASSERT_EQ(result.size(), 3u);
240 ASSERT_THAT(result, UnorderedElementsAre(ApexFileEq(ByRef(*apexd_test_file)),
241 ApexFileEq(ByRef(*shim_v1)),
242 ApexFileEq(ByRef(*shared_lib_2))));
243 }
244
245 // Higher version gets priority when selecting for activation
TEST_F(ApexdUnitTest,HigherVersionOfApexIsSelected)246 TEST_F(ApexdUnitTest, HigherVersionOfApexIsSelected) {
247 auto apexd_test_file_v2 =
248 ApexFile::Open(AddPreInstalledApex("apex.apexd_test_v2.apex"));
249 AddPreInstalledApex("com.android.apex.cts.shim.apex");
250 auto& instance = ApexFileRepository::GetInstance();
251 ASSERT_TRUE(IsOk(instance.AddPreInstalledApex({GetBuiltInDir()})));
252
253 TemporaryDir data_dir;
254 AddDataApex("apex.apexd_test.apex");
255 auto shim_v2 =
256 ApexFile::Open(AddDataApex("com.android.apex.cts.shim.v2.apex"));
257 ASSERT_TRUE(IsOk(instance.AddDataApex(GetDataDir())));
258
259 auto all_apex = instance.AllApexFilesByName();
260 auto result = SelectApexForActivation(all_apex, instance);
261 ASSERT_EQ(result.size(), 2u);
262
263 ASSERT_THAT(result,
264 UnorderedElementsAre(ApexFileEq(ByRef(*apexd_test_file_v2)),
265 ApexFileEq(ByRef(*shim_v2))));
266 }
267
268 // When versions are equal, non-pre-installed version gets priority
TEST_F(ApexdUnitTest,DataApexGetsPriorityForSameVersions)269 TEST_F(ApexdUnitTest, DataApexGetsPriorityForSameVersions) {
270 AddPreInstalledApex("apex.apexd_test.apex");
271 AddPreInstalledApex("com.android.apex.cts.shim.apex");
272 // Initialize pre-installed APEX information
273 auto& instance = ApexFileRepository::GetInstance();
274 ASSERT_TRUE(IsOk(instance.AddPreInstalledApex({GetBuiltInDir()})));
275
276 auto apexd_test_file = ApexFile::Open(AddDataApex("apex.apexd_test.apex"));
277 auto shim_v1 = ApexFile::Open(AddDataApex("com.android.apex.cts.shim.apex"));
278 // Initialize ApexFile repo
279 ASSERT_TRUE(IsOk(instance.AddDataApex(GetDataDir())));
280
281 auto all_apex = instance.AllApexFilesByName();
282 auto result = SelectApexForActivation(all_apex, instance);
283 ASSERT_EQ(result.size(), 2u);
284
285 ASSERT_THAT(result, UnorderedElementsAre(ApexFileEq(ByRef(*apexd_test_file)),
286 ApexFileEq(ByRef(*shim_v1))));
287 }
288
289 // Both versions of shared libs can be selected when preinstalled version is
290 // lower than data version
TEST_F(ApexdUnitTest,SharedLibsCanHaveBothVersionSelected)291 TEST_F(ApexdUnitTest, SharedLibsCanHaveBothVersionSelected) {
292 auto shared_lib_v1 = ApexFile::Open(AddPreInstalledApex(
293 "com.android.apex.test.sharedlibs_generated.v1.libvX.apex"));
294 // Initialize pre-installed APEX information
295 auto& instance = ApexFileRepository::GetInstance();
296 ASSERT_TRUE(IsOk(instance.AddPreInstalledApex({GetBuiltInDir()})));
297
298 auto shared_lib_v2 = ApexFile::Open(
299 AddDataApex("com.android.apex.test.sharedlibs_generated.v2.libvY.apex"));
300 // Initialize data APEX information
301 ASSERT_TRUE(IsOk(instance.AddDataApex(GetDataDir())));
302
303 auto all_apex = instance.AllApexFilesByName();
304 auto result = SelectApexForActivation(all_apex, instance);
305 ASSERT_EQ(result.size(), 2u);
306
307 ASSERT_THAT(result, UnorderedElementsAre(ApexFileEq(ByRef(*shared_lib_v1)),
308 ApexFileEq(ByRef(*shared_lib_v2))));
309 }
310
311 // Data version of shared libs should not be selected if lower than
312 // preinstalled version
TEST_F(ApexdUnitTest,SharedLibsDataVersionDeletedIfLower)313 TEST_F(ApexdUnitTest, SharedLibsDataVersionDeletedIfLower) {
314 auto shared_lib_v2 = ApexFile::Open(AddPreInstalledApex(
315 "com.android.apex.test.sharedlibs_generated.v2.libvY.apex"));
316 // Initialize pre-installed APEX information
317 auto& instance = ApexFileRepository::GetInstance();
318 ASSERT_TRUE(IsOk(instance.AddPreInstalledApex({GetBuiltInDir()})));
319
320 auto shared_lib_v1 = ApexFile::Open(
321 AddDataApex("com.android.apex.test.sharedlibs_generated.v1.libvX.apex"));
322 // Initialize data APEX information
323 ASSERT_TRUE(IsOk(instance.AddDataApex(GetDataDir())));
324
325 auto all_apex = instance.AllApexFilesByName();
326 auto result = SelectApexForActivation(all_apex, instance);
327 ASSERT_EQ(result.size(), 1u);
328
329 ASSERT_THAT(result, UnorderedElementsAre(ApexFileEq(ByRef(*shared_lib_v2))));
330 }
331
TEST_F(ApexdUnitTest,ProcessCompressedApex)332 TEST_F(ApexdUnitTest, ProcessCompressedApex) {
333 auto compressed_apex = ApexFile::Open(
334 AddPreInstalledApex("com.android.apex.compressed.v1.capex"));
335
336 std::vector<ApexFileRef> compressed_apex_list;
337 compressed_apex_list.emplace_back(std::cref(*compressed_apex));
338 auto return_value =
339 ProcessCompressedApex(compressed_apex_list, /* is_ota_chroot= */ false);
340
341 std::string decompressed_file_path = StringPrintf(
342 "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(),
343 kDecompressedApexPackageSuffix);
344 // Assert output path is not empty
345 auto exists = PathExists(decompressed_file_path);
346 ASSERT_TRUE(IsOk(exists));
347 ASSERT_TRUE(*exists) << decompressed_file_path << " does not exist";
348
349 // Assert that decompressed apex is same as original apex
350 const std::string original_apex_file_path =
351 GetTestFile("com.android.apex.compressed.v1_original.apex");
352 auto comparison_result =
353 CompareFiles(original_apex_file_path, decompressed_file_path);
354 ASSERT_TRUE(IsOk(comparison_result));
355 ASSERT_TRUE(*comparison_result);
356
357 // Assert that return value contains decompressed APEX
358 auto decompressed_apex = ApexFile::Open(decompressed_file_path);
359 ASSERT_THAT(return_value,
360 UnorderedElementsAre(ApexFileEq(ByRef(*decompressed_apex))));
361 }
362
TEST_F(ApexdUnitTest,ProcessCompressedApexRunsVerification)363 TEST_F(ApexdUnitTest, ProcessCompressedApexRunsVerification) {
364 auto compressed_apex_mismatch_key = ApexFile::Open(AddPreInstalledApex(
365 "com.android.apex.compressed_key_mismatch_with_original.capex"));
366 auto compressed_apex_version_mismatch = ApexFile::Open(
367 AddPreInstalledApex("com.android.apex.compressed.v1_with_v2_apex.capex"));
368
369 std::vector<ApexFileRef> compressed_apex_list;
370 compressed_apex_list.emplace_back(std::cref(*compressed_apex_mismatch_key));
371 compressed_apex_list.emplace_back(
372 std::cref(*compressed_apex_version_mismatch));
373 auto return_value =
374 ProcessCompressedApex(compressed_apex_list, /* is_ota_chroot= */ false);
375 ASSERT_EQ(return_value.size(), 0u);
376 }
377
TEST_F(ApexdUnitTest,ValidateDecompressedApex)378 TEST_F(ApexdUnitTest, ValidateDecompressedApex) {
379 auto capex = ApexFile::Open(
380 AddPreInstalledApex("com.android.apex.compressed.v1.capex"));
381 auto decompressed_v1 = ApexFile::Open(
382 AddDataApex("com.android.apex.compressed.v1_original.apex"));
383
384 auto result =
385 ValidateDecompressedApex(std::cref(*capex), std::cref(*decompressed_v1));
386 ASSERT_TRUE(IsOk(result));
387
388 // Validation checks version
389 auto decompressed_v2 = ApexFile::Open(
390 AddDataApex("com.android.apex.compressed.v2_original.apex"));
391 result =
392 ValidateDecompressedApex(std::cref(*capex), std::cref(*decompressed_v2));
393 ASSERT_FALSE(IsOk(result));
394 ASSERT_THAT(
395 result.error().message(),
396 HasSubstr(
397 "Compressed APEX has different version than decompressed APEX"));
398
399 // Validation check root digest
400 auto decompressed_v1_different_digest = ApexFile::Open(AddDataApex(
401 "com.android.apex.compressed.v1_different_digest_original.apex"));
402 result = ValidateDecompressedApex(
403 std::cref(*capex), std::cref(*decompressed_v1_different_digest));
404 ASSERT_FALSE(IsOk(result));
405 ASSERT_THAT(result.error().message(),
406 HasSubstr("does not match with expected root digest"));
407
408 // Validation checks key
409 auto capex_different_key = ApexFile::Open(
410 AddDataApex("com.android.apex.compressed_different_key.capex"));
411 result = ValidateDecompressedApex(std::cref(*capex_different_key),
412 std::cref(*decompressed_v1));
413 ASSERT_FALSE(IsOk(result));
414 ASSERT_THAT(
415 result.error().message(),
416 HasSubstr("Public key of compressed APEX is different than original"));
417 }
418
TEST_F(ApexdUnitTest,ProcessCompressedApexCanBeCalledMultipleTimes)419 TEST_F(ApexdUnitTest, ProcessCompressedApexCanBeCalledMultipleTimes) {
420 auto compressed_apex = ApexFile::Open(
421 AddPreInstalledApex("com.android.apex.compressed.v1.capex"));
422
423 std::vector<ApexFileRef> compressed_apex_list;
424 compressed_apex_list.emplace_back(std::cref(*compressed_apex));
425 auto return_value =
426 ProcessCompressedApex(compressed_apex_list, /* is_ota_chroot= */ false);
427 ASSERT_EQ(return_value.size(), 1u);
428
429 // Capture the creation time of the decompressed APEX
430 std::error_code ec;
431 auto decompressed_apex_path = StringPrintf(
432 "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(),
433 kDecompressedApexPackageSuffix);
434 auto last_write_time_1 = fs::last_write_time(decompressed_apex_path, ec);
435 ASSERT_FALSE(ec) << "Failed to capture last write time of "
436 << decompressed_apex_path;
437
438 // Now try to decompress the same capex again. It should not fail.
439 return_value =
440 ProcessCompressedApex(compressed_apex_list, /* is_ota_chroot= */ false);
441 ASSERT_EQ(return_value.size(), 1u);
442
443 // Ensure the decompressed APEX file did not change
444 auto last_write_time_2 = fs::last_write_time(decompressed_apex_path, ec);
445 ASSERT_FALSE(ec) << "Failed to capture last write time of "
446 << decompressed_apex_path;
447 ASSERT_EQ(last_write_time_1, last_write_time_2);
448 }
449
450 // Test behavior of ProcessCompressedApex when is_ota_chroot is true
TEST_F(ApexdUnitTest,ProcessCompressedApexOnOtaChroot)451 TEST_F(ApexdUnitTest, ProcessCompressedApexOnOtaChroot) {
452 auto compressed_apex = ApexFile::Open(
453 AddPreInstalledApex("com.android.apex.compressed.v1.capex"));
454
455 std::vector<ApexFileRef> compressed_apex_list;
456 compressed_apex_list.emplace_back(std::cref(*compressed_apex));
457 auto return_value =
458 ProcessCompressedApex(compressed_apex_list, /* is_ota_chroot= */ true);
459 ASSERT_EQ(return_value.size(), 1u);
460
461 // Decompressed APEX should be located in decompression_dir
462 std::string decompressed_file_path =
463 StringPrintf("%s/com.android.apex.compressed@1%s",
464 GetDecompressionDir().c_str(), kOtaApexPackageSuffix);
465 // Assert output path is not empty
466 auto exists = PathExists(decompressed_file_path);
467 ASSERT_TRUE(IsOk(exists));
468 ASSERT_TRUE(*exists) << decompressed_file_path << " does not exist";
469
470 // Assert that decompressed apex is same as original apex
471 const std::string original_apex_file_path =
472 GetTestFile("com.android.apex.compressed.v1_original.apex");
473 auto comparison_result =
474 CompareFiles(original_apex_file_path, decompressed_file_path);
475 ASSERT_TRUE(IsOk(comparison_result));
476 ASSERT_TRUE(*comparison_result);
477
478 // Assert that return value contains the decompressed APEX
479 auto apex_file = ApexFile::Open(decompressed_file_path);
480 ASSERT_THAT(return_value,
481 UnorderedElementsAre(ApexFileEq(ByRef(*apex_file))));
482 }
483
484 // When decompressing APEX, reuse existing OTA APEX
TEST_F(ApexdUnitTest,ProcessCompressedApexReuseOtaApex)485 TEST_F(ApexdUnitTest, ProcessCompressedApexReuseOtaApex) {
486 // Push a compressed APEX that will fail to decompress
487 auto compressed_apex = ApexFile::Open(AddPreInstalledApex(
488 "com.android.apex.compressed.v1_not_decompressible.capex"));
489
490 std::vector<ApexFileRef> compressed_apex_list;
491 compressed_apex_list.emplace_back(std::cref(*compressed_apex));
492
493 // If we try to decompress capex directly, it should fail since the capex
494 // pushed is faulty and cannot be decompressed
495 auto return_value =
496 ProcessCompressedApex(compressed_apex_list, /* is_ota_chroot= */ false);
497 ASSERT_EQ(return_value.size(), 0u);
498
499 // But, if there is an ota_apex present for reuse, it should reuse that
500 // and avoid decompressing the faulty capex
501
502 // Push an OTA apex that should be reused to skip decompression
503 auto ota_apex_path =
504 StringPrintf("%s/com.android.apex.compressed@1%s",
505 GetDecompressionDir().c_str(), kOtaApexPackageSuffix);
506 fs::copy(GetTestFile("com.android.apex.compressed.v1_original.apex"),
507 ota_apex_path);
508 return_value =
509 ProcessCompressedApex(compressed_apex_list, /* is_ota_chroot= */ false);
510 ASSERT_EQ(return_value.size(), 1u);
511
512 // Ota Apex should be cleaned up
513 ASSERT_FALSE(*PathExists(ota_apex_path));
514 ASSERT_EQ(return_value[0].GetPath(),
515 StringPrintf("%s/com.android.apex.compressed@1%s",
516 GetDecompressionDir().c_str(),
517 kDecompressedApexPackageSuffix));
518 }
519
TEST_F(ApexdUnitTest,ShouldAllocateSpaceForDecompressionNewApex)520 TEST_F(ApexdUnitTest, ShouldAllocateSpaceForDecompressionNewApex) {
521 auto& instance = ApexFileRepository::GetInstance();
522 ASSERT_TRUE(IsOk(instance.AddPreInstalledApex({GetBuiltInDir()})));
523
524 // A brand new compressed APEX is being introduced: selected
525 auto result =
526 ShouldAllocateSpaceForDecompression("com.android.brand.new", 1, instance);
527 ASSERT_TRUE(IsOk(result));
528 ASSERT_TRUE(*result);
529 }
530
TEST_F(ApexdUnitTest,ShouldAllocateSpaceForDecompressionWasNotCompressedBefore)531 TEST_F(ApexdUnitTest,
532 ShouldAllocateSpaceForDecompressionWasNotCompressedBefore) {
533 // Prepare fake pre-installed apex
534 AddPreInstalledApex("apex.apexd_test.apex");
535 auto& instance = ApexFileRepository::GetInstance();
536 ASSERT_TRUE(IsOk(instance.AddPreInstalledApex({GetBuiltInDir()})));
537
538 // An existing pre-installed APEX is now compressed in the OTA: selected
539 {
540 auto result = ShouldAllocateSpaceForDecompression(
541 "com.android.apex.test_package", 1, instance);
542 ASSERT_TRUE(IsOk(result));
543 ASSERT_TRUE(*result);
544 }
545
546 // Even if there is a data apex (lower version)
547 // Include data apex within calculation now
548 AddDataApex("apex.apexd_test_v2.apex");
549 ASSERT_TRUE(IsOk(instance.AddDataApex(GetDataDir())));
550 {
551 auto result = ShouldAllocateSpaceForDecompression(
552 "com.android.apex.test_package", 3, instance);
553 ASSERT_TRUE(IsOk(result));
554 ASSERT_TRUE(*result);
555 }
556
557 // But not if data apex has equal or higher version
558 {
559 auto result = ShouldAllocateSpaceForDecompression(
560 "com.android.apex.test_package", 2, instance);
561 ASSERT_TRUE(IsOk(result));
562 ASSERT_FALSE(*result);
563 }
564 }
565
TEST_F(ApexdUnitTest,ShouldAllocateSpaceForDecompressionVersionCompare)566 TEST_F(ApexdUnitTest, ShouldAllocateSpaceForDecompressionVersionCompare) {
567 // Prepare fake pre-installed apex
568 PrepareCompressedApex("com.android.apex.compressed.v1.capex");
569 auto& instance = ApexFileRepository::GetInstance();
570 ASSERT_TRUE(IsOk(instance.AddPreInstalledApex({GetBuiltInDir()})));
571 ASSERT_TRUE(IsOk(instance.AddDataApex(GetDataDir())));
572
573 {
574 // New Compressed apex has higher version than decompressed data apex:
575 // selected
576 auto result = ShouldAllocateSpaceForDecompression(
577 "com.android.apex.compressed", 2, instance);
578 ASSERT_TRUE(IsOk(result));
579 ASSERT_TRUE(*result)
580 << "Higher version test with decompressed data returned false";
581 }
582
583 // Compare against decompressed data apex
584 {
585 // New Compressed apex has same version as decompressed data apex: not
586 // selected
587 auto result = ShouldAllocateSpaceForDecompression(
588 "com.android.apex.compressed", 1, instance);
589 ASSERT_TRUE(IsOk(result));
590 ASSERT_FALSE(*result)
591 << "Same version test with decompressed data returned true";
592 }
593
594 {
595 // New Compressed apex has lower version than decompressed data apex:
596 // selected
597 auto result = ShouldAllocateSpaceForDecompression(
598 "com.android.apex.compressed", 0, instance);
599 ASSERT_TRUE(IsOk(result));
600 ASSERT_TRUE(*result)
601 << "lower version test with decompressed data returned false";
602 }
603
604 // Replace decompressed data apex with a higher version
605 ApexFileRepository instance_new(GetDecompressionDir());
606 ASSERT_TRUE(IsOk(instance_new.AddPreInstalledApex({GetBuiltInDir()})));
607 TemporaryDir data_dir_new;
608 fs::copy(GetTestFile("com.android.apex.compressed.v2_original.apex"),
609 data_dir_new.path);
610 ASSERT_TRUE(IsOk(instance_new.AddDataApex(data_dir_new.path)));
611
612 {
613 // New Compressed apex has higher version as data apex: selected
614 auto result = ShouldAllocateSpaceForDecompression(
615 "com.android.apex.compressed", 3, instance_new);
616 ASSERT_TRUE(IsOk(result));
617 ASSERT_TRUE(*result) << "Higher version test with new data returned false";
618 }
619
620 {
621 // New Compressed apex has same version as data apex: not selected
622 auto result = ShouldAllocateSpaceForDecompression(
623 "com.android.apex.compressed", 2, instance_new);
624 ASSERT_TRUE(IsOk(result));
625 ASSERT_FALSE(*result) << "Same version test with new data returned true";
626 }
627
628 {
629 // New Compressed apex has lower version than data apex: not selected
630 auto result = ShouldAllocateSpaceForDecompression(
631 "com.android.apex.compressed", 1, instance_new);
632 ASSERT_TRUE(IsOk(result));
633 ASSERT_FALSE(*result) << "lower version test with new data returned true";
634 }
635 }
636
TEST_F(ApexdUnitTest,ReserveSpaceForCompressedApexCreatesSingleFile)637 TEST_F(ApexdUnitTest, ReserveSpaceForCompressedApexCreatesSingleFile) {
638 TemporaryDir dest_dir;
639 // Reserving space should create a single file in dest_dir with exact size
640
641 ASSERT_TRUE(IsOk(ReserveSpaceForCompressedApex(100, dest_dir.path)));
642 auto files = ReadDir(dest_dir.path, [](auto _) { return true; });
643 ASSERT_TRUE(IsOk(files));
644 ASSERT_EQ(files->size(), 1u);
645 EXPECT_EQ(fs::file_size((*files)[0]), 100u);
646 }
647
TEST_F(ApexdUnitTest,ReserveSpaceForCompressedApexSafeToCallMultipleTimes)648 TEST_F(ApexdUnitTest, ReserveSpaceForCompressedApexSafeToCallMultipleTimes) {
649 TemporaryDir dest_dir;
650 // Calling ReserveSpaceForCompressedApex multiple times should still create
651 // a single file
652 ASSERT_TRUE(IsOk(ReserveSpaceForCompressedApex(100, dest_dir.path)));
653 ASSERT_TRUE(IsOk(ReserveSpaceForCompressedApex(100, dest_dir.path)));
654 auto files = ReadDir(dest_dir.path, [](auto _) { return true; });
655 ASSERT_TRUE(IsOk(files));
656 ASSERT_EQ(files->size(), 1u);
657 EXPECT_EQ(fs::file_size((*files)[0]), 100u);
658 }
659
TEST_F(ApexdUnitTest,ReserveSpaceForCompressedApexShrinkAndGrow)660 TEST_F(ApexdUnitTest, ReserveSpaceForCompressedApexShrinkAndGrow) {
661 TemporaryDir dest_dir;
662
663 // Create a 100 byte file
664 ASSERT_TRUE(IsOk(ReserveSpaceForCompressedApex(100, dest_dir.path)));
665
666 // Should be able to shrink and grow the reserved space
667 ASSERT_TRUE(IsOk(ReserveSpaceForCompressedApex(1000, dest_dir.path)));
668 auto files = ReadDir(dest_dir.path, [](auto _) { return true; });
669 ASSERT_TRUE(IsOk(files));
670 ASSERT_EQ(files->size(), 1u);
671 EXPECT_EQ(fs::file_size((*files)[0]), 1000u);
672
673 ASSERT_TRUE(IsOk(ReserveSpaceForCompressedApex(10, dest_dir.path)));
674 files = ReadDir(dest_dir.path, [](auto _) { return true; });
675 ASSERT_TRUE(IsOk(files));
676 ASSERT_EQ(files->size(), 1u);
677 EXPECT_EQ(fs::file_size((*files)[0]), 10u);
678 }
679
TEST_F(ApexdUnitTest,ReserveSpaceForCompressedApexDeallocateIfPassedZero)680 TEST_F(ApexdUnitTest, ReserveSpaceForCompressedApexDeallocateIfPassedZero) {
681 TemporaryDir dest_dir;
682
683 // Create a file first
684 ASSERT_TRUE(IsOk(ReserveSpaceForCompressedApex(100, dest_dir.path)));
685 auto files = ReadDir(dest_dir.path, [](auto _) { return true; });
686 ASSERT_TRUE(IsOk(files));
687 ASSERT_EQ(files->size(), 1u);
688
689 // Should delete the reserved file if size passed is 0
690 ASSERT_TRUE(IsOk(ReserveSpaceForCompressedApex(0, dest_dir.path)));
691 files = ReadDir(dest_dir.path, [](auto _) { return true; });
692 ASSERT_TRUE(IsOk(files));
693 ASSERT_EQ(files->size(), 0u);
694 }
695
TEST_F(ApexdUnitTest,ReserveSpaceForCapexCleansOtaApex)696 TEST_F(ApexdUnitTest, ReserveSpaceForCapexCleansOtaApex) {
697 TemporaryDir dest_dir;
698
699 auto ota_apex_path = StringPrintf(
700 "%s/ota_apex%s", GetDecompressionDir().c_str(), kOtaApexPackageSuffix);
701 auto create_ota_apex = [&]() {
702 // Create an ota_apex first
703 fs::copy(GetTestFile("com.android.apex.compressed.v1_original.apex"),
704 ota_apex_path);
705 auto path_exists = PathExists(ota_apex_path);
706 ASSERT_TRUE(*path_exists);
707 };
708 create_ota_apex();
709
710 // Should not delete the reserved file if size passed is negative
711 ASSERT_FALSE(IsOk(ReserveSpaceForCompressedApex(-1, dest_dir.path)));
712 auto path_exists = PathExists(ota_apex_path);
713 ASSERT_TRUE(*path_exists);
714
715 // Should delete the reserved file if size passed is 0
716 ASSERT_TRUE(IsOk(ReserveSpaceForCompressedApex(0, dest_dir.path)));
717 path_exists = PathExists(ota_apex_path);
718 ASSERT_FALSE(*path_exists);
719
720 create_ota_apex();
721 // Should delete the reserved file if size passed is positive
722 ASSERT_TRUE(IsOk(ReserveSpaceForCompressedApex(10, dest_dir.path)));
723 path_exists = PathExists(ota_apex_path);
724 ASSERT_FALSE(*path_exists);
725 }
726
TEST_F(ApexdUnitTest,ReserveSpaceForCompressedApexErrorForNegativeValue)727 TEST_F(ApexdUnitTest, ReserveSpaceForCompressedApexErrorForNegativeValue) {
728 TemporaryDir dest_dir;
729 // Should return error if negative value is passed
730 ASSERT_FALSE(IsOk(ReserveSpaceForCompressedApex(-1, dest_dir.path)));
731 }
732
733 // A test fixture to use for tests that mount/unmount apexes.
734 class ApexdMountTest : public ApexdUnitTest {
735 public:
736
UnmountOnTearDown(const std::string & apex_file)737 void UnmountOnTearDown(const std::string& apex_file) {
738 to_unmount_.push_back(apex_file);
739 }
740
741 protected:
SetUp()742 void SetUp() final {
743 ApexdUnitTest::SetUp();
744 GetApexDatabaseForTesting().Reset();
745 ASSERT_TRUE(IsOk(SetUpApexTestEnvironment()));
746 }
747
TearDown()748 void TearDown() final {
749 ApexdUnitTest::TearDown();
750 for (const auto& apex : to_unmount_) {
751 if (auto status = DeactivatePackage(apex); !status.ok()) {
752 LOG(ERROR) << "Failed to unmount " << apex << " : " << status.error();
753 }
754 }
755 }
756
757 private:
758 MountNamespaceRestorer restorer_;
759 std::vector<std::string> to_unmount_;
760 };
761
762 // TODO(b/187864524): cover other negative scenarios.
TEST_F(ApexdMountTest,InstallPackageRejectsApexWithoutRebootlessSupport)763 TEST_F(ApexdMountTest, InstallPackageRejectsApexWithoutRebootlessSupport) {
764 std::string file_path = AddPreInstalledApex("apex.apexd_test.apex");
765 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
766
767 ASSERT_TRUE(IsOk(ActivatePackage(file_path)));
768 UnmountOnTearDown(file_path);
769
770 auto ret = InstallPackage(GetTestFile("apex.apexd_test.apex"));
771 ASSERT_FALSE(IsOk(ret));
772 ASSERT_THAT(ret.error().message(),
773 HasSubstr("does not support non-staged update"));
774 }
775
TEST_F(ApexdMountTest,InstallPackageRejectsNoPreInstalledApex)776 TEST_F(ApexdMountTest, InstallPackageRejectsNoPreInstalledApex) {
777 auto ret = InstallPackage(GetTestFile("test.rebootless_apex_v1.apex"));
778 ASSERT_FALSE(IsOk(ret));
779 ASSERT_THAT(
780 ret.error().message(),
781 HasSubstr("No active version found for package test.apex.rebootless"));
782 }
783
TEST_F(ApexdMountTest,InstallPackageRejectsNoHashtree)784 TEST_F(ApexdMountTest, InstallPackageRejectsNoHashtree) {
785 std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex");
786 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
787
788 ASSERT_TRUE(IsOk(ActivatePackage(file_path)));
789 UnmountOnTearDown(file_path);
790
791 auto ret =
792 InstallPackage(GetTestFile("test.rebootless_apex_v2_no_hashtree.apex"));
793 ASSERT_FALSE(IsOk(ret));
794 ASSERT_THAT(ret.error().message(),
795 HasSubstr(" does not have an embedded hash tree"));
796 }
797
TEST_F(ApexdMountTest,InstallPackageRejectsNoActiveApex)798 TEST_F(ApexdMountTest, InstallPackageRejectsNoActiveApex) {
799 std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex");
800 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
801
802 auto ret = InstallPackage(GetTestFile("test.rebootless_apex_v2.apex"));
803 ASSERT_FALSE(IsOk(ret));
804 ASSERT_THAT(
805 ret.error().message(),
806 HasSubstr("No active version found for package test.apex.rebootless"));
807 }
808
TEST_F(ApexdMountTest,InstallPackageRejectsManifestMismatch)809 TEST_F(ApexdMountTest, InstallPackageRejectsManifestMismatch) {
810 std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex");
811 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
812
813 ASSERT_TRUE(IsOk(ActivatePackage(file_path)));
814 UnmountOnTearDown(file_path);
815
816 auto ret = InstallPackage(
817 GetTestFile("test.rebootless_apex_manifest_mismatch.apex"));
818 ASSERT_FALSE(IsOk(ret));
819 ASSERT_THAT(
820 ret.error().message(),
821 HasSubstr(
822 "Manifest inside filesystem does not match manifest outside it"));
823 }
824
TEST_F(ApexdMountTest,InstallPackageRejectsCorrupted)825 TEST_F(ApexdMountTest, InstallPackageRejectsCorrupted) {
826 std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex");
827 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
828
829 ASSERT_TRUE(IsOk(ActivatePackage(file_path)));
830 UnmountOnTearDown(file_path);
831
832 auto ret = InstallPackage(GetTestFile("test.rebootless_apex_corrupted.apex"));
833 ASSERT_FALSE(IsOk(ret));
834 ASSERT_THAT(ret.error().message(), HasSubstr("Can't verify /dev/block/dm-"));
835 }
836
TEST_F(ApexdMountTest,InstallPackageRejectsProvidesSharedLibs)837 TEST_F(ApexdMountTest, InstallPackageRejectsProvidesSharedLibs) {
838 std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex");
839 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
840
841 ASSERT_TRUE(IsOk(ActivatePackage(file_path)));
842 UnmountOnTearDown(file_path);
843
844 auto ret = InstallPackage(
845 GetTestFile("test.rebootless_apex_provides_sharedlibs.apex"));
846 ASSERT_FALSE(IsOk(ret));
847 ASSERT_THAT(ret.error().message(), HasSubstr(" is a shared libs APEX"));
848 }
849
TEST_F(ApexdMountTest,InstallPackageRejectsProvidesNativeLibs)850 TEST_F(ApexdMountTest, InstallPackageRejectsProvidesNativeLibs) {
851 std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex");
852 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
853
854 ASSERT_TRUE(IsOk(ActivatePackage(file_path)));
855 UnmountOnTearDown(file_path);
856
857 auto ret = InstallPackage(
858 GetTestFile("test.rebootless_apex_provides_native_libs.apex"));
859 ASSERT_FALSE(IsOk(ret));
860 ASSERT_THAT(ret.error().message(), HasSubstr(" provides native libs"));
861 }
862
TEST_F(ApexdMountTest,InstallPackageRejectsRequiresSharedApexLibs)863 TEST_F(ApexdMountTest, InstallPackageRejectsRequiresSharedApexLibs) {
864 std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex");
865 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
866
867 ASSERT_TRUE(IsOk(ActivatePackage(file_path)));
868 UnmountOnTearDown(file_path);
869
870 auto ret = InstallPackage(
871 GetTestFile("test.rebootless_apex_requires_shared_apex_libs.apex"));
872 ASSERT_FALSE(IsOk(ret));
873 ASSERT_THAT(ret.error().message(), HasSubstr(" requires shared apex libs"));
874 }
875
TEST_F(ApexdMountTest,InstallPackageRejectsJniLibs)876 TEST_F(ApexdMountTest, InstallPackageRejectsJniLibs) {
877 std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex");
878 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
879
880 ASSERT_TRUE(IsOk(ActivatePackage(file_path)));
881 UnmountOnTearDown(file_path);
882
883 auto ret = InstallPackage(GetTestFile("test.rebootless_apex_jni_libs.apex"));
884 ASSERT_FALSE(IsOk(ret));
885 ASSERT_THAT(ret.error().message(), HasSubstr(" requires JNI libs"));
886 }
887
TEST_F(ApexdMountTest,InstallPackageRejectsAddRequiredNativeLib)888 TEST_F(ApexdMountTest, InstallPackageRejectsAddRequiredNativeLib) {
889 std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex");
890 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
891
892 ASSERT_TRUE(IsOk(ActivatePackage(file_path)));
893 UnmountOnTearDown(file_path);
894
895 auto ret =
896 InstallPackage(GetTestFile("test.rebootless_apex_add_native_lib.apex"));
897 ASSERT_FALSE(IsOk(ret));
898 ASSERT_THAT(ret.error().message(),
899 HasSubstr("Set of native libs required by"));
900 ASSERT_THAT(
901 ret.error().message(),
902 HasSubstr("differs from the one required by the currently active"));
903 }
904
TEST_F(ApexdMountTest,InstallPackageRejectsRemovesRequiredNativeLib)905 TEST_F(ApexdMountTest, InstallPackageRejectsRemovesRequiredNativeLib) {
906 std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex");
907 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
908
909 ASSERT_TRUE(IsOk(ActivatePackage(file_path)));
910 UnmountOnTearDown(file_path);
911
912 auto ret = InstallPackage(
913 GetTestFile("test.rebootless_apex_remove_native_lib.apex"));
914 ASSERT_FALSE(IsOk(ret));
915 ASSERT_THAT(ret.error().message(),
916 HasSubstr("Set of native libs required by"));
917 ASSERT_THAT(
918 ret.error().message(),
919 HasSubstr("differs from the one required by the currently active"));
920 }
921
TEST_F(ApexdMountTest,InstallPackageRejectsAppInApex)922 TEST_F(ApexdMountTest, InstallPackageRejectsAppInApex) {
923 std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex");
924 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
925
926 ASSERT_TRUE(IsOk(ActivatePackage(file_path)));
927 UnmountOnTearDown(file_path);
928
929 auto ret =
930 InstallPackage(GetTestFile("test.rebootless_apex_app_in_apex.apex"));
931 ASSERT_FALSE(IsOk(ret));
932 ASSERT_THAT(ret.error().message(), HasSubstr("contains app inside"));
933 }
934
TEST_F(ApexdMountTest,InstallPackageRejectsPrivAppInApex)935 TEST_F(ApexdMountTest, InstallPackageRejectsPrivAppInApex) {
936 std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex");
937 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
938
939 ASSERT_TRUE(IsOk(ActivatePackage(file_path)));
940 UnmountOnTearDown(file_path);
941
942 auto ret =
943 InstallPackage(GetTestFile("test.rebootless_apex_priv_app_in_apex.apex"));
944 ASSERT_FALSE(IsOk(ret));
945 ASSERT_THAT(ret.error().message(), HasSubstr("contains priv-app inside"));
946 }
947
TEST_F(ApexdMountTest,InstallPackagePreInstallVersionActive)948 TEST_F(ApexdMountTest, InstallPackagePreInstallVersionActive) {
949 std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex");
950 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
951
952 ASSERT_TRUE(IsOk(ActivatePackage(file_path)));
953 UnmountOnTearDown(file_path);
954
955 {
956 auto active_apex = GetActivePackage("test.apex.rebootless");
957 ASSERT_TRUE(IsOk(active_apex));
958 ASSERT_EQ(active_apex->GetPath(), file_path);
959 }
960
961 auto ret = InstallPackage(GetTestFile("test.rebootless_apex_v2.apex"));
962 ASSERT_TRUE(IsOk(ret));
963 UnmountOnTearDown(ret->GetPath());
964
965 auto apex_mounts = GetApexMounts();
966 ASSERT_THAT(apex_mounts,
967 UnorderedElementsAre("/apex/test.apex.rebootless",
968 "/apex/test.apex.rebootless@2"));
969
970 // Check that /apex/test.apex.rebootless is a bind mount of
971 // /apex/test.apex.rebootless@2.
972 auto manifest = ReadManifest("/apex/test.apex.rebootless/apex_manifest.pb");
973 ASSERT_TRUE(IsOk(manifest));
974 ASSERT_EQ(2u, manifest->version());
975
976 // Check that GetActivePackage correctly reports upgraded version.
977 auto active_apex = GetActivePackage("test.apex.rebootless");
978 ASSERT_TRUE(IsOk(active_apex));
979 ASSERT_EQ(active_apex->GetPath(), ret->GetPath());
980
981 // Check that pre-installed APEX is still around
982 ASSERT_EQ(0, access(file_path.c_str(), F_OK))
983 << "Can't access " << file_path << " : " << strerror(errno);
984
985 auto& db = GetApexDatabaseForTesting();
986 // Check that upgraded APEX is mounted on top of dm-verity device.
987 db.ForallMountedApexes(
988 "test.apex.rebootless", [&](const MountedApexData& data, bool latest) {
989 ASSERT_TRUE(latest);
990 ASSERT_EQ(data.full_path, ret->GetPath());
991 ASSERT_EQ(data.device_name, "test.apex.rebootless@2_1");
992 });
993 }
994
TEST_F(ApexdMountTest,InstallPackagePreInstallVersionActiveSamegrade)995 TEST_F(ApexdMountTest, InstallPackagePreInstallVersionActiveSamegrade) {
996 std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex");
997 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
998
999 ASSERT_TRUE(IsOk(ActivatePackage(file_path)));
1000 UnmountOnTearDown(file_path);
1001
1002 {
1003 auto active_apex = GetActivePackage("test.apex.rebootless");
1004 ASSERT_TRUE(IsOk(active_apex));
1005 ASSERT_EQ(active_apex->GetPath(), file_path);
1006 }
1007
1008 auto ret = InstallPackage(GetTestFile("test.rebootless_apex_v1.apex"));
1009 ASSERT_TRUE(IsOk(ret));
1010 UnmountOnTearDown(ret->GetPath());
1011
1012 auto apex_mounts = GetApexMounts();
1013 ASSERT_THAT(apex_mounts,
1014 UnorderedElementsAre("/apex/test.apex.rebootless",
1015 "/apex/test.apex.rebootless@1"));
1016
1017 // Check that GetActivePackage correctly reports upgraded version.
1018 auto active_apex = GetActivePackage("test.apex.rebootless");
1019 ASSERT_TRUE(IsOk(active_apex));
1020 ASSERT_EQ(active_apex->GetPath(), ret->GetPath());
1021
1022 // Check that pre-installed APEX is still around
1023 ASSERT_EQ(0, access(file_path.c_str(), F_OK))
1024 << "Can't access " << file_path << " : " << strerror(errno);
1025
1026 auto& db = GetApexDatabaseForTesting();
1027 // Check that upgraded APEX is mounted on top of dm-verity device.
1028 db.ForallMountedApexes(
1029 "test.apex.rebootless", [&](const MountedApexData& data, bool latest) {
1030 ASSERT_TRUE(latest);
1031 ASSERT_EQ(data.full_path, ret->GetPath());
1032 ASSERT_EQ(data.device_name, "test.apex.rebootless@1_1");
1033 });
1034 }
1035
TEST_F(ApexdMountTest,InstallPackageDataVersionActive)1036 TEST_F(ApexdMountTest, InstallPackageDataVersionActive) {
1037 AddPreInstalledApex("test.rebootless_apex_v1.apex");
1038 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
1039
1040 std::string file_path = AddDataApex("test.rebootless_apex_v1.apex");
1041 ASSERT_TRUE(IsOk(ActivatePackage(file_path)));
1042 UnmountOnTearDown(file_path);
1043
1044 {
1045 auto active_apex = GetActivePackage("test.apex.rebootless");
1046 ASSERT_TRUE(IsOk(active_apex));
1047 ASSERT_EQ(active_apex->GetPath(), file_path);
1048 }
1049
1050 auto ret = InstallPackage(GetTestFile("test.rebootless_apex_v2.apex"));
1051 ASSERT_TRUE(IsOk(ret));
1052 UnmountOnTearDown(ret->GetPath());
1053
1054 auto apex_mounts = GetApexMounts();
1055 ASSERT_THAT(apex_mounts,
1056 UnorderedElementsAre("/apex/test.apex.rebootless",
1057 "/apex/test.apex.rebootless@2"));
1058
1059 // Check that /apex/test.apex.rebootless is a bind mount of
1060 // /apex/test.apex.rebootless@2.
1061 auto manifest = ReadManifest("/apex/test.apex.rebootless/apex_manifest.pb");
1062 ASSERT_TRUE(IsOk(manifest));
1063 ASSERT_EQ(2u, manifest->version());
1064
1065 // Check that GetActivePackage correctly reports upgraded version.
1066 auto active_apex = GetActivePackage("test.apex.rebootless");
1067 ASSERT_TRUE(IsOk(active_apex));
1068 ASSERT_EQ(active_apex->GetPath(), ret->GetPath());
1069
1070 // Check that previously active APEX was deleted.
1071 ASSERT_EQ(-1, access(file_path.c_str(), F_OK));
1072 ASSERT_EQ(ENOENT, errno);
1073
1074 auto& db = GetApexDatabaseForTesting();
1075 // Check that upgraded APEX is mounted on top of dm-verity device.
1076 db.ForallMountedApexes(
1077 "test.apex.rebootless", [&](const MountedApexData& data, bool latest) {
1078 ASSERT_TRUE(latest);
1079 ASSERT_EQ(data.full_path, ret->GetPath());
1080 ASSERT_EQ(data.device_name, "test.apex.rebootless@2_1");
1081 });
1082 }
1083
TEST_F(ApexdMountTest,InstallPackageResolvesPathCollision)1084 TEST_F(ApexdMountTest, InstallPackageResolvesPathCollision) {
1085 AddPreInstalledApex("test.rebootless_apex_v1.apex");
1086 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
1087
1088 std::string file_path = AddDataApex("test.rebootless_apex_v1.apex",
1089 "test.apex.rebootless@1_1.apex");
1090 ASSERT_TRUE(IsOk(ActivatePackage(file_path)));
1091 UnmountOnTearDown(file_path);
1092
1093 {
1094 auto active_apex = GetActivePackage("test.apex.rebootless");
1095 ASSERT_TRUE(IsOk(active_apex));
1096 ASSERT_EQ(active_apex->GetPath(), file_path);
1097 }
1098
1099 auto ret = InstallPackage(GetTestFile("test.rebootless_apex_v1.apex"));
1100 ASSERT_TRUE(IsOk(ret));
1101 UnmountOnTearDown(ret->GetPath());
1102
1103 auto apex_mounts = GetApexMounts();
1104 ASSERT_THAT(apex_mounts,
1105 UnorderedElementsAre("/apex/test.apex.rebootless",
1106 "/apex/test.apex.rebootless@1"));
1107
1108 // Check that /apex/test.apex.rebootless is a bind mount of
1109 // /apex/test.apex.rebootless@2.
1110 auto manifest = ReadManifest("/apex/test.apex.rebootless/apex_manifest.pb");
1111 ASSERT_TRUE(IsOk(manifest));
1112 ASSERT_EQ(1u, manifest->version());
1113
1114 // Check that GetActivePackage correctly reports upgraded version.
1115 auto active_apex = GetActivePackage("test.apex.rebootless");
1116 ASSERT_TRUE(IsOk(active_apex));
1117 ASSERT_EQ(active_apex->GetPath(), ret->GetPath());
1118
1119 // Check that we correctly resolved active apex path collision.
1120 ASSERT_EQ(active_apex->GetPath(),
1121 GetDataDir() + "/test.apex.rebootless@1_2.apex");
1122
1123 // Check that previously active APEX was deleted.
1124 ASSERT_EQ(-1, access(file_path.c_str(), F_OK));
1125 ASSERT_EQ(ENOENT, errno);
1126
1127 auto& db = GetApexDatabaseForTesting();
1128 // Check that upgraded APEX is mounted on top of dm-verity device.
1129 db.ForallMountedApexes(
1130 "test.apex.rebootless", [&](const MountedApexData& data, bool latest) {
1131 ASSERT_TRUE(latest);
1132 ASSERT_EQ(data.full_path, ret->GetPath());
1133 ASSERT_EQ(data.device_name, "test.apex.rebootless@1_2");
1134 });
1135 }
1136
TEST_F(ApexdMountTest,InstallPackageDataVersionActiveSamegrade)1137 TEST_F(ApexdMountTest, InstallPackageDataVersionActiveSamegrade) {
1138 AddPreInstalledApex("test.rebootless_apex_v1.apex");
1139 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
1140
1141 std::string file_path = AddDataApex("test.rebootless_apex_v2.apex");
1142 ASSERT_TRUE(IsOk(ActivatePackage(file_path)));
1143 UnmountOnTearDown(file_path);
1144
1145 {
1146 auto active_apex = GetActivePackage("test.apex.rebootless");
1147 ASSERT_TRUE(IsOk(active_apex));
1148 ASSERT_EQ(active_apex->GetPath(), file_path);
1149 }
1150
1151 auto ret = InstallPackage(GetTestFile("test.rebootless_apex_v2.apex"));
1152 ASSERT_TRUE(IsOk(ret));
1153 UnmountOnTearDown(ret->GetPath());
1154
1155 auto apex_mounts = GetApexMounts();
1156 ASSERT_THAT(apex_mounts,
1157 UnorderedElementsAre("/apex/test.apex.rebootless",
1158 "/apex/test.apex.rebootless@2"));
1159
1160 // Check that /apex/test.apex.rebootless is a bind mount of
1161 // /apex/test.apex.rebootless@2.
1162 auto manifest = ReadManifest("/apex/test.apex.rebootless/apex_manifest.pb");
1163 ASSERT_TRUE(IsOk(manifest));
1164 ASSERT_EQ(2u, manifest->version());
1165
1166 // Check that GetActivePackage correctly reports upgraded version.
1167 auto active_apex = GetActivePackage("test.apex.rebootless");
1168 ASSERT_TRUE(IsOk(active_apex));
1169 ASSERT_EQ(active_apex->GetPath(), ret->GetPath());
1170
1171 // Check that previously active APEX was deleted.
1172 ASSERT_EQ(-1, access(file_path.c_str(), F_OK));
1173 ASSERT_EQ(ENOENT, errno);
1174
1175 auto& db = GetApexDatabaseForTesting();
1176 // Check that upgraded APEX is mounted on top of dm-verity device.
1177 db.ForallMountedApexes(
1178 "test.apex.rebootless", [&](const MountedApexData& data, bool latest) {
1179 ASSERT_TRUE(latest);
1180 ASSERT_EQ(data.full_path, ret->GetPath());
1181 ASSERT_EQ(data.device_name, "test.apex.rebootless@2_1");
1182 });
1183 }
1184
TEST_F(ApexdMountTest,InstallPackageUnmountFailsPreInstalledApexActive)1185 TEST_F(ApexdMountTest, InstallPackageUnmountFailsPreInstalledApexActive) {
1186 std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex");
1187 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
1188
1189 ASSERT_TRUE(IsOk(ActivatePackage(file_path)));
1190 UnmountOnTearDown(file_path);
1191
1192 {
1193 auto active_apex = GetActivePackage("test.apex.rebootless");
1194 ASSERT_TRUE(IsOk(active_apex));
1195 ASSERT_EQ(active_apex->GetPath(), file_path);
1196 }
1197
1198 unique_fd fd(open("/apex/test.apex.rebootless/apex_manifest.pb",
1199 O_RDONLY | O_CLOEXEC));
1200 ASSERT_NE(-1, fd.get());
1201
1202 auto ret = InstallPackage(GetTestFile("test.rebootless_apex_v2.apex"));
1203 ASSERT_FALSE(IsOk(ret));
1204
1205 auto apex_mounts = GetApexMounts();
1206 ASSERT_THAT(apex_mounts,
1207 UnorderedElementsAre("/apex/test.apex.rebootless",
1208 "/apex/test.apex.rebootless@1"));
1209
1210 // Check that GetActivePackage correctly reports upgraded version.
1211 auto active_apex = GetActivePackage("test.apex.rebootless");
1212 ASSERT_TRUE(IsOk(active_apex));
1213 ASSERT_EQ(active_apex->GetPath(), file_path);
1214
1215 // Check that old APEX is still around
1216 ASSERT_EQ(0, access(file_path.c_str(), F_OK))
1217 << "Can't access " << file_path << " : " << strerror(errno);
1218
1219 auto& db = GetApexDatabaseForTesting();
1220 // Check that upgraded APEX is mounted on top of dm-verity device.
1221 db.ForallMountedApexes("test.apex.rebootless",
1222 [&](const MountedApexData& data, bool latest) {
1223 ASSERT_TRUE(latest);
1224 ASSERT_EQ(data.full_path, file_path);
1225 });
1226 }
1227
TEST_F(ApexdMountTest,InstallPackageUnmountFailedUpdatedApexActive)1228 TEST_F(ApexdMountTest, InstallPackageUnmountFailedUpdatedApexActive) {
1229 AddPreInstalledApex("test.rebootless_apex_v1.apex");
1230 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
1231
1232 std::string file_path = AddDataApex("test.rebootless_apex_v1.apex");
1233
1234 ASSERT_TRUE(IsOk(ActivatePackage(file_path)));
1235 UnmountOnTearDown(file_path);
1236
1237 {
1238 auto active_apex = GetActivePackage("test.apex.rebootless");
1239 ASSERT_TRUE(IsOk(active_apex));
1240 ASSERT_EQ(active_apex->GetPath(), file_path);
1241 }
1242
1243 unique_fd fd(open("/apex/test.apex.rebootless/apex_manifest.pb",
1244 O_RDONLY | O_CLOEXEC));
1245 ASSERT_NE(-1, fd.get());
1246
1247 auto ret = InstallPackage(GetTestFile("test.rebootless_apex_v2.apex"));
1248 ASSERT_FALSE(IsOk(ret));
1249
1250 auto apex_mounts = GetApexMounts();
1251 ASSERT_THAT(apex_mounts,
1252 UnorderedElementsAre("/apex/test.apex.rebootless",
1253 "/apex/test.apex.rebootless@1"));
1254
1255 // Check that GetActivePackage correctly reports old apex.
1256 auto active_apex = GetActivePackage("test.apex.rebootless");
1257 ASSERT_TRUE(IsOk(active_apex));
1258 ASSERT_EQ(active_apex->GetPath(), file_path);
1259
1260 // Check that old APEX is still around
1261 ASSERT_EQ(0, access(file_path.c_str(), F_OK))
1262 << "Can't access " << file_path << " : " << strerror(errno);
1263
1264 auto& db = GetApexDatabaseForTesting();
1265 db.ForallMountedApexes(
1266 "test.apex.rebootless", [&](const MountedApexData& data, bool latest) {
1267 ASSERT_TRUE(latest);
1268 ASSERT_EQ(data.full_path, file_path);
1269 ASSERT_EQ(data.device_name, "test.apex.rebootless@1");
1270 });
1271 }
1272
TEST_F(ApexdMountTest,InstallPackageUpdatesApexInfoList)1273 TEST_F(ApexdMountTest, InstallPackageUpdatesApexInfoList) {
1274 auto apex_1 = AddPreInstalledApex("test.rebootless_apex_v1.apex");
1275 auto apex_2 = AddPreInstalledApex("apex.apexd_test.apex");
1276 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
1277
1278 UnmountOnTearDown(apex_1);
1279 UnmountOnTearDown(apex_2);
1280 ASSERT_TRUE(IsOk(ActivatePackage(apex_1)));
1281 ASSERT_TRUE(IsOk(ActivatePackage(apex_2)));
1282
1283 // Call OnAllPackagesActivated to create /apex/apex-info-list.xml.
1284 OnAllPackagesActivated(/* is_bootstrap= */ false);
1285 // Check /apex/apex-info-list.xml was created.
1286 ASSERT_EQ(0, access("/apex/apex-info-list.xml", F_OK));
1287
1288 auto ret = InstallPackage(GetTestFile("test.rebootless_apex_v2.apex"));
1289 ASSERT_TRUE(IsOk(ret));
1290 UnmountOnTearDown(ret->GetPath());
1291
1292 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
1293 auto info_list =
1294 com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
1295 ASSERT_TRUE(info_list.has_value());
1296 auto apex_info_xml_1 = com::android::apex::ApexInfo(
1297 /* moduleName= */ "test.apex.rebootless",
1298 /* modulePath= */ apex_1,
1299 /* preinstalledModulePath= */ apex_1,
1300 /* versionCode= */ 1, /* versionName= */ "1",
1301 /* isFactory= */ true, /* isActive= */ false, GetMTime(apex_1));
1302 auto apex_info_xml_2 = com::android::apex::ApexInfo(
1303 /* moduleName= */ "com.android.apex.test_package",
1304 /* modulePath= */ apex_2, /* preinstalledModulePath= */ apex_2,
1305 /* versionCode= */ 1, /* versionName= */ "1", /* isFactory= */ true,
1306 /* isActive= */ true, GetMTime(apex_2));
1307 auto apex_info_xml_3 = com::android::apex::ApexInfo(
1308 /* moduleName= */ "test.apex.rebootless",
1309 /* modulePath= */ ret->GetPath(),
1310 /* preinstalledModulePath= */ apex_1,
1311 /* versionCode= */ 2, /* versionName= */ "2",
1312 /* isFactory= */ false, /* isActive= */ true, GetMTime(ret->GetPath()));
1313 ASSERT_THAT(info_list->getApexInfo(),
1314 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1),
1315 ApexInfoXmlEq(apex_info_xml_2),
1316 ApexInfoXmlEq(apex_info_xml_3)));
1317 }
1318
TEST_F(ApexdMountTest,ActivatePackage)1319 TEST_F(ApexdMountTest, ActivatePackage) {
1320 std::string file_path = AddPreInstalledApex("apex.apexd_test.apex");
1321 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
1322
1323 ASSERT_TRUE(IsOk(ActivatePackage(file_path)));
1324 UnmountOnTearDown(file_path);
1325
1326 auto active_apex = GetActivePackage("com.android.apex.test_package");
1327 ASSERT_TRUE(IsOk(active_apex));
1328 ASSERT_EQ(active_apex->GetPath(), file_path);
1329
1330 auto apex_mounts = GetApexMounts();
1331 ASSERT_THAT(apex_mounts,
1332 UnorderedElementsAre("/apex/com.android.apex.test_package",
1333 "/apex/com.android.apex.test_package@1"));
1334
1335 ASSERT_TRUE(IsOk(DeactivatePackage(file_path)));
1336 ASSERT_FALSE(IsOk(GetActivePackage("com.android.apex.test_package")));
1337
1338 auto new_apex_mounts = GetApexMounts();
1339 ASSERT_EQ(new_apex_mounts.size(), 0u);
1340 }
1341
TEST_F(ApexdMountTest,ActivateDeactivateSharedLibsApex)1342 TEST_F(ApexdMountTest, ActivateDeactivateSharedLibsApex) {
1343 ASSERT_EQ(mkdir("/apex/sharedlibs", 0755), 0);
1344 ASSERT_EQ(mkdir("/apex/sharedlibs/lib", 0755), 0);
1345 ASSERT_EQ(mkdir("/apex/sharedlibs/lib64", 0755), 0);
1346 auto deleter = make_scope_guard([]() {
1347 std::error_code ec;
1348 fs::remove_all("/apex/sharedlibs", ec);
1349 if (ec) {
1350 LOG(ERROR) << "Failed to delete /apex/sharedlibs : " << ec;
1351 }
1352 });
1353
1354 std::string file_path = AddPreInstalledApex(
1355 "com.android.apex.test.sharedlibs_generated.v1.libvX.apex");
1356 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
1357
1358 UnmountOnTearDown(file_path);
1359 ASSERT_TRUE(IsOk(ActivatePackage(file_path)));
1360
1361 auto active_apex = GetActivePackage("com.android.apex.test.sharedlibs");
1362 ASSERT_TRUE(IsOk(active_apex));
1363 ASSERT_EQ(active_apex->GetPath(), file_path);
1364
1365 auto apex_mounts = GetApexMounts();
1366 ASSERT_THAT(apex_mounts,
1367 UnorderedElementsAre("/apex/com.android.apex.test.sharedlibs@1"));
1368
1369 ASSERT_TRUE(IsOk(DeactivatePackage(file_path)));
1370 ASSERT_FALSE(IsOk(GetActivePackage("com.android.apex.test.sharedlibs")));
1371
1372 auto new_apex_mounts = GetApexMounts();
1373 ASSERT_EQ(new_apex_mounts.size(), 0u);
1374 }
1375
TEST_F(ApexdMountTest,RemoveInactiveDataApex)1376 TEST_F(ApexdMountTest, RemoveInactiveDataApex) {
1377 AddPreInstalledApex("com.android.apex.compressed.v2.capex");
1378 // Add a decompressed apex that will not be mounted, so should be removed
1379 auto decompressed_apex = StringPrintf("%s/com.android.apex.compressed@1%s",
1380 GetDecompressionDir().c_str(),
1381 kDecompressedApexPackageSuffix);
1382 fs::copy(GetTestFile("com.android.apex.compressed.v1_original.apex"),
1383 decompressed_apex);
1384 // Add a decompressed apex that will be mounted, so should be not be removed
1385 auto active_decompressed_apex = StringPrintf(
1386 "%s/com.android.apex.compressed@2%s", GetDecompressionDir().c_str(),
1387 kDecompressedApexPackageSuffix);
1388 fs::copy(GetTestFile("com.android.apex.compressed.v2_original.apex"),
1389 active_decompressed_apex);
1390 // Apex that do not have kDecompressedApexPackageSuffix, should not be removed
1391 // from decompression_dir
1392 auto decompressed_different_suffix =
1393 StringPrintf("%s/com.android.apex.compressed@2%s",
1394 GetDecompressionDir().c_str(), kApexPackageSuffix);
1395 fs::copy(GetTestFile("com.android.apex.compressed.v2_original.apex"),
1396 decompressed_different_suffix);
1397
1398 AddPreInstalledApex("apex.apexd_test.apex");
1399 auto data_apex = AddDataApex("apex.apexd_test.apex");
1400 auto active_data_apex = AddDataApex("apex.apexd_test_v2.apex");
1401
1402 // Activate some of the apex
1403 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
1404 UnmountOnTearDown(active_decompressed_apex);
1405 UnmountOnTearDown(active_data_apex);
1406 ASSERT_TRUE(IsOk(ActivatePackage(active_decompressed_apex)));
1407 ASSERT_TRUE(IsOk(ActivatePackage(active_data_apex)));
1408 // Clean up inactive apex packages
1409 RemoveInactiveDataApex();
1410
1411 // Verify inactive apex packages have been deleted
1412 ASSERT_TRUE(*PathExists(active_decompressed_apex));
1413 ASSERT_TRUE(*PathExists(active_data_apex));
1414 ASSERT_TRUE(*PathExists(decompressed_different_suffix));
1415 ASSERT_FALSE(*PathExists(decompressed_apex));
1416 ASSERT_FALSE(*PathExists(data_apex));
1417 }
1418
TEST_F(ApexdMountTest,OnOtaChrootBootstrapOnlyPreInstalledApexes)1419 TEST_F(ApexdMountTest, OnOtaChrootBootstrapOnlyPreInstalledApexes) {
1420 std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex");
1421 std::string apex_path_2 =
1422 AddPreInstalledApex("apex.apexd_test_different_app.apex");
1423
1424 ASSERT_EQ(OnOtaChrootBootstrap(), 0);
1425 UnmountOnTearDown(apex_path_1);
1426 UnmountOnTearDown(apex_path_2);
1427
1428 auto apex_mounts = GetApexMounts();
1429 ASSERT_THAT(apex_mounts,
1430 UnorderedElementsAre("/apex/com.android.apex.test_package",
1431 "/apex/com.android.apex.test_package@1",
1432 "/apex/com.android.apex.test_package_2",
1433 "/apex/com.android.apex.test_package_2@1"));
1434
1435 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
1436 auto info_list =
1437 com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
1438 ASSERT_TRUE(info_list.has_value());
1439 auto apex_info_xml_1 = com::android::apex::ApexInfo(
1440 /* moduleName= */ "com.android.apex.test_package",
1441 /* modulePath= */ apex_path_1,
1442 /* preinstalledModulePath= */ apex_path_1,
1443 /* versionCode= */ 1, /* versionName= */ "1",
1444 /* isFactory= */ true, /* isActive= */ true, GetMTime(apex_path_1));
1445 auto apex_info_xml_2 = com::android::apex::ApexInfo(
1446 /* moduleName= */ "com.android.apex.test_package_2",
1447 /* modulePath= */ apex_path_2, /* preinstalledModulePath= */ apex_path_2,
1448 /* versionCode= */ 1, /* versionName= */ "1", /* isFactory= */ true,
1449 /* isActive= */ true, GetMTime(apex_path_2));
1450 ASSERT_THAT(info_list->getApexInfo(),
1451 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1),
1452 ApexInfoXmlEq(apex_info_xml_2)));
1453 }
1454
TEST_F(ApexdMountTest,OnOtaChrootBootstrapFailsToScanPreInstalledApexes)1455 TEST_F(ApexdMountTest, OnOtaChrootBootstrapFailsToScanPreInstalledApexes) {
1456 AddPreInstalledApex("apex.apexd_test.apex");
1457 AddPreInstalledApex("apex.apexd_test_corrupt_superblock_apex.apex");
1458
1459 ASSERT_EQ(OnOtaChrootBootstrap(), 1);
1460 }
1461
TEST_F(ApexdMountTest,OnOtaChrootBootstrapDataHasHigherVersion)1462 TEST_F(ApexdMountTest, OnOtaChrootBootstrapDataHasHigherVersion) {
1463 std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex");
1464 std::string apex_path_2 =
1465 AddPreInstalledApex("apex.apexd_test_different_app.apex");
1466 std::string apex_path_3 = AddDataApex("apex.apexd_test_v2.apex");
1467
1468 ASSERT_EQ(OnOtaChrootBootstrap(), 0);
1469
1470 UnmountOnTearDown(apex_path_2);
1471 UnmountOnTearDown(apex_path_3);
1472
1473 auto apex_mounts = GetApexMounts();
1474 ASSERT_THAT(apex_mounts,
1475 UnorderedElementsAre("/apex/com.android.apex.test_package",
1476 "/apex/com.android.apex.test_package@2",
1477 "/apex/com.android.apex.test_package_2",
1478 "/apex/com.android.apex.test_package_2@1"));
1479
1480 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
1481 auto info_list =
1482 com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
1483 ASSERT_TRUE(info_list.has_value());
1484 auto apex_info_xml_1 = com::android::apex::ApexInfo(
1485 /* moduleName= */ "com.android.apex.test_package",
1486 /* modulePath= */ apex_path_1,
1487 /* preinstalledModulePath= */ apex_path_1,
1488 /* versionCode= */ 1, /* versionName= */ "1",
1489 /* isFactory= */ true, /* isActive= */ false, GetMTime(apex_path_1));
1490 auto apex_info_xml_2 = com::android::apex::ApexInfo(
1491 /* moduleName= */ "com.android.apex.test_package_2",
1492 /* modulePath= */ apex_path_2, /* preinstalledModulePath= */ apex_path_2,
1493 /* versionCode= */ 1, /* versionName= */ "1", /* isFactory= */ true,
1494 /* isActive= */ true, GetMTime(apex_path_2));
1495 auto apex_info_xml_3 = com::android::apex::ApexInfo(
1496 /* moduleName= */ "com.android.apex.test_package",
1497 /* modulePath= */ apex_path_3,
1498 /* preinstalledModulePath= */ apex_path_1,
1499 /* versionCode= */ 2, /* versionName= */ "2",
1500 /* isFactory= */ false, /* isActive= */ true, GetMTime(apex_path_3));
1501 ASSERT_THAT(info_list->getApexInfo(),
1502 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1),
1503 ApexInfoXmlEq(apex_info_xml_2),
1504 ApexInfoXmlEq(apex_info_xml_3)));
1505 }
1506
TEST_F(ApexdMountTest,OnOtaChrootBootstrapDataHasSameVersion)1507 TEST_F(ApexdMountTest, OnOtaChrootBootstrapDataHasSameVersion) {
1508 std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex");
1509 std::string apex_path_2 =
1510 AddPreInstalledApex("apex.apexd_test_different_app.apex");
1511 std::string apex_path_3 = AddDataApex("apex.apexd_test.apex");
1512
1513 ASSERT_EQ(OnOtaChrootBootstrap(), 0);
1514
1515 UnmountOnTearDown(apex_path_2);
1516 UnmountOnTearDown(apex_path_3);
1517
1518 auto apex_mounts = GetApexMounts();
1519 ASSERT_THAT(apex_mounts,
1520 UnorderedElementsAre("/apex/com.android.apex.test_package",
1521 "/apex/com.android.apex.test_package@1",
1522 "/apex/com.android.apex.test_package_2",
1523 "/apex/com.android.apex.test_package_2@1"));
1524
1525 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
1526 auto info_list =
1527 com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
1528 ASSERT_TRUE(info_list.has_value());
1529 auto apex_info_xml_1 = com::android::apex::ApexInfo(
1530 /* moduleName= */ "com.android.apex.test_package",
1531 /* modulePath= */ apex_path_1,
1532 /* preinstalledModulePath= */ apex_path_1,
1533 /* versionCode= */ 1, /* versionName= */ "1",
1534 /* isFactory= */ true, /* isActive= */ false, GetMTime(apex_path_1));
1535 auto apex_info_xml_2 = com::android::apex::ApexInfo(
1536 /* moduleName= */ "com.android.apex.test_package_2",
1537 /* modulePath= */ apex_path_2, /* preinstalledModulePath= */ apex_path_2,
1538 /* versionCode= */ 1, /* versionName= */ "1", /* isFactory= */ true,
1539 /* isActive= */ true, GetMTime(apex_path_2));
1540 auto apex_info_xml_3 = com::android::apex::ApexInfo(
1541 /* moduleName= */ "com.android.apex.test_package",
1542 /* modulePath= */ apex_path_3,
1543 /* preinstalledModulePath= */ apex_path_1,
1544 /* versionCode= */ 1, /* versionName= */ "1",
1545 /* isFactory= */ false, /* isActive= */ true, GetMTime(apex_path_3));
1546 ASSERT_THAT(info_list->getApexInfo(),
1547 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1),
1548 ApexInfoXmlEq(apex_info_xml_2),
1549 ApexInfoXmlEq(apex_info_xml_3)));
1550 }
1551
TEST_F(ApexdMountTest,OnOtaChrootBootstrapSystemHasHigherVersion)1552 TEST_F(ApexdMountTest, OnOtaChrootBootstrapSystemHasHigherVersion) {
1553 std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test_v2.apex");
1554 std::string apex_path_2 =
1555 AddPreInstalledApex("apex.apexd_test_different_app.apex");
1556 AddDataApex("apex.apexd_test.apex");
1557
1558 ASSERT_EQ(OnOtaChrootBootstrap(), 0);
1559
1560 UnmountOnTearDown(apex_path_1);
1561 UnmountOnTearDown(apex_path_2);
1562
1563 auto apex_mounts = GetApexMounts();
1564 ASSERT_THAT(apex_mounts,
1565 UnorderedElementsAre("/apex/com.android.apex.test_package",
1566 "/apex/com.android.apex.test_package@2",
1567 "/apex/com.android.apex.test_package_2",
1568 "/apex/com.android.apex.test_package_2@1"));
1569
1570 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
1571 auto info_list =
1572 com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
1573 ASSERT_TRUE(info_list.has_value());
1574 auto apex_info_xml_1 = com::android::apex::ApexInfo(
1575 /* moduleName= */ "com.android.apex.test_package",
1576 /* modulePath= */ apex_path_1,
1577 /* preinstalledModulePath= */ apex_path_1,
1578 /* versionCode= */ 2, /* versionName= */ "2",
1579 /* isFactory= */ true, /* isActive= */ true, GetMTime(apex_path_1));
1580 auto apex_info_xml_2 = com::android::apex::ApexInfo(
1581 /* moduleName= */ "com.android.apex.test_package_2",
1582 /* modulePath= */ apex_path_2, /* preinstalledModulePath= */ apex_path_2,
1583 /* versionCode= */ 1, /* versionName= */ "1", /* isFactory= */ true,
1584 /* isActive= */ true, GetMTime(apex_path_2));
1585
1586 ASSERT_THAT(info_list->getApexInfo(),
1587 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1),
1588 ApexInfoXmlEq(apex_info_xml_2)));
1589 }
1590
TEST_F(ApexdMountTest,OnOtaChrootBootstrapDataHasSameVersionButDifferentKey)1591 TEST_F(ApexdMountTest, OnOtaChrootBootstrapDataHasSameVersionButDifferentKey) {
1592 std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex");
1593 std::string apex_path_2 =
1594 AddPreInstalledApex("apex.apexd_test_different_app.apex");
1595 AddDataApex("apex.apexd_test_different_key.apex");
1596
1597 ASSERT_EQ(OnOtaChrootBootstrap(), 0);
1598
1599 UnmountOnTearDown(apex_path_1);
1600 UnmountOnTearDown(apex_path_2);
1601
1602 auto apex_mounts = GetApexMounts();
1603 ASSERT_THAT(apex_mounts,
1604 UnorderedElementsAre("/apex/com.android.apex.test_package",
1605 "/apex/com.android.apex.test_package@1",
1606 "/apex/com.android.apex.test_package_2",
1607 "/apex/com.android.apex.test_package_2@1"));
1608
1609 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
1610 auto info_list =
1611 com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
1612 ASSERT_TRUE(info_list.has_value());
1613 auto apex_info_xml_1 = com::android::apex::ApexInfo(
1614 /* moduleName= */ "com.android.apex.test_package",
1615 /* modulePath= */ apex_path_1,
1616 /* preinstalledModulePath= */ apex_path_1,
1617 /* versionCode= */ 1, /* versionName= */ "1",
1618 /* isFactory= */ true, /* isActive= */ true, GetMTime(apex_path_1));
1619 auto apex_info_xml_2 = com::android::apex::ApexInfo(
1620 /* moduleName= */ "com.android.apex.test_package_2",
1621 /* modulePath= */ apex_path_2, /* preinstalledModulePath= */ apex_path_2,
1622 /* versionCode= */ 1, /* versionName= */ "1", /* isFactory= */ true,
1623 /* isActive= */ true, GetMTime(apex_path_2));
1624
1625 ASSERT_THAT(info_list->getApexInfo(),
1626 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1),
1627 ApexInfoXmlEq(apex_info_xml_2)));
1628 }
1629
TEST_F(ApexdMountTest,OnOtaChrootBootstrapDataHasHigherVersionButDifferentKey)1630 TEST_F(ApexdMountTest,
1631 OnOtaChrootBootstrapDataHasHigherVersionButDifferentKey) {
1632 std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex");
1633 std::string apex_path_2 =
1634 AddPreInstalledApex("apex.apexd_test_different_app.apex");
1635 std::string apex_path_3 =
1636 AddDataApex("apex.apexd_test_different_key_v2.apex");
1637
1638 {
1639 auto apex = ApexFile::Open(apex_path_3);
1640 ASSERT_TRUE(IsOk(apex));
1641 ASSERT_EQ(static_cast<uint64_t>(apex->GetManifest().version()), 2ULL);
1642 }
1643
1644 ASSERT_EQ(OnOtaChrootBootstrap(), 0);
1645
1646 UnmountOnTearDown(apex_path_1);
1647 UnmountOnTearDown(apex_path_2);
1648
1649 auto apex_mounts = GetApexMounts();
1650 ASSERT_THAT(apex_mounts,
1651 UnorderedElementsAre("/apex/com.android.apex.test_package",
1652 "/apex/com.android.apex.test_package@1",
1653 "/apex/com.android.apex.test_package_2",
1654 "/apex/com.android.apex.test_package_2@1"));
1655
1656 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
1657 auto info_list =
1658 com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
1659 ASSERT_TRUE(info_list.has_value());
1660 auto apex_info_xml_1 = com::android::apex::ApexInfo(
1661 /* moduleName= */ "com.android.apex.test_package",
1662 /* modulePath= */ apex_path_1,
1663 /* preinstalledModulePath= */ apex_path_1,
1664 /* versionCode= */ 1, /* versionName= */ "1",
1665 /* isFactory= */ true, /* isActive= */ true, GetMTime(apex_path_1));
1666 auto apex_info_xml_2 = com::android::apex::ApexInfo(
1667 /* moduleName= */ "com.android.apex.test_package_2",
1668 /* modulePath= */ apex_path_2, /* preinstalledModulePath= */ apex_path_2,
1669 /* versionCode= */ 1, /* versionName= */ "1", /* isFactory= */ true,
1670 /* isActive= */ true, GetMTime(apex_path_2));
1671
1672 ASSERT_THAT(info_list->getApexInfo(),
1673 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1),
1674 ApexInfoXmlEq(apex_info_xml_2)));
1675 }
1676
TEST_F(ApexdMountTest,OnOtaChrootBootstrapDataApexWithoutPreInstalledApex)1677 TEST_F(ApexdMountTest, OnOtaChrootBootstrapDataApexWithoutPreInstalledApex) {
1678 std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex");
1679 AddDataApex("apex.apexd_test_different_app.apex");
1680
1681 ASSERT_EQ(OnOtaChrootBootstrap(), 0);
1682
1683 UnmountOnTearDown(apex_path_1);
1684
1685 auto apex_mounts = GetApexMounts();
1686 ASSERT_THAT(apex_mounts,
1687 UnorderedElementsAre("/apex/com.android.apex.test_package",
1688 "/apex/com.android.apex.test_package@1"));
1689
1690 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
1691 auto info_list =
1692 com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
1693 ASSERT_TRUE(info_list.has_value());
1694 auto apex_info_xml_1 = com::android::apex::ApexInfo(
1695 /* moduleName= */ "com.android.apex.test_package",
1696 /* modulePath= */ apex_path_1,
1697 /* preinstalledModulePath= */ apex_path_1,
1698 /* versionCode= */ 1, /* versionName= */ "1",
1699 /* isFactory= */ true, /* isActive= */ true, GetMTime(apex_path_1));
1700
1701 ASSERT_THAT(info_list->getApexInfo(),
1702 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1)));
1703 }
1704
TEST_F(ApexdMountTest,OnOtaChrootBootstrapPreInstalledSharedLibsApex)1705 TEST_F(ApexdMountTest, OnOtaChrootBootstrapPreInstalledSharedLibsApex) {
1706 std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex");
1707 std::string apex_path_2 = AddPreInstalledApex(
1708 "com.android.apex.test.sharedlibs_generated.v1.libvX.apex");
1709 std::string apex_path_3 = AddDataApex("apex.apexd_test_v2.apex");
1710
1711 ASSERT_EQ(OnOtaChrootBootstrap(), 0);
1712
1713 UnmountOnTearDown(apex_path_2);
1714 UnmountOnTearDown(apex_path_3);
1715
1716 auto apex_mounts = GetApexMounts();
1717 ASSERT_THAT(apex_mounts,
1718 UnorderedElementsAre("/apex/com.android.apex.test_package",
1719 "/apex/com.android.apex.test_package@2",
1720 "/apex/com.android.apex.test.sharedlibs@1"));
1721
1722 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
1723 auto info_list =
1724 com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
1725 ASSERT_TRUE(info_list.has_value());
1726 auto apex_info_xml_1 = com::android::apex::ApexInfo(
1727 /* moduleName= */ "com.android.apex.test_package",
1728 /* modulePath= */ apex_path_1,
1729 /* preinstalledModulePath= */ apex_path_1,
1730 /* versionCode= */ 1, /* versionName= */ "1",
1731 /* isFactory= */ true, /* isActive= */ false, GetMTime(apex_path_1));
1732 auto apex_info_xml_2 = com::android::apex::ApexInfo(
1733 /* moduleName= */ "com.android.apex.test.sharedlibs",
1734 /* modulePath= */ apex_path_2,
1735 /* preinstalledModulePath= */ apex_path_2,
1736 /* versionCode= */ 1, /* versionName= */ "1",
1737 /* isFactory= */ true, /* isActive= */ true, GetMTime(apex_path_2));
1738 auto apex_info_xml_3 = com::android::apex::ApexInfo(
1739 /* moduleName= */ "com.android.apex.test_package",
1740 /* modulePath= */ apex_path_3,
1741 /* preinstalledModulePath= */ apex_path_1,
1742 /* versionCode= */ 2, /* versionName= */ "2",
1743 /* isFactory= */ false, /* isActive= */ true, GetMTime(apex_path_3));
1744
1745 ASSERT_THAT(info_list->getApexInfo(),
1746 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1),
1747 ApexInfoXmlEq(apex_info_xml_2),
1748 ApexInfoXmlEq(apex_info_xml_3)));
1749
1750 ASSERT_EQ(access("/apex/sharedlibs", F_OK), 0);
1751
1752 // Check /apex/sharedlibs is populated properly.
1753 std::vector<std::string> sharedlibs;
1754 for (const auto& p : fs::recursive_directory_iterator("/apex/sharedlibs")) {
1755 if (fs::is_symlink(p)) {
1756 auto src = fs::read_symlink(p.path());
1757 ASSERT_EQ(p.path().filename(), src.filename());
1758 sharedlibs.push_back(p.path().parent_path().string() + "->" +
1759 src.parent_path().string());
1760 }
1761 }
1762
1763 std::vector<std::string> expected = {
1764 "/apex/sharedlibs/lib/libsharedlibtest.so->"
1765 "/apex/com.android.apex.test.sharedlibs@1/lib/libsharedlibtest.so",
1766 "/apex/sharedlibs/lib/libc++.so->"
1767 "/apex/com.android.apex.test.sharedlibs@1/lib/libc++.so",
1768 };
1769
1770 // On 64bit devices we also have lib64.
1771 if (!GetProperty("ro.product.cpu.abilist64", "").empty()) {
1772 expected.push_back(
1773 "/apex/sharedlibs/lib64/libsharedlibtest.so->"
1774 "/apex/com.android.apex.test.sharedlibs@1/lib64/libsharedlibtest.so");
1775 expected.push_back(
1776 "/apex/sharedlibs/lib64/libc++.so->"
1777 "/apex/com.android.apex.test.sharedlibs@1/lib64/libc++.so");
1778 }
1779 ASSERT_THAT(sharedlibs, UnorderedElementsAreArray(expected));
1780 }
1781
TEST_F(ApexdMountTest,OnOtaChrootBootstrapSharedLibsApexBothVersions)1782 TEST_F(ApexdMountTest, OnOtaChrootBootstrapSharedLibsApexBothVersions) {
1783 std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex");
1784 std::string apex_path_2 = AddPreInstalledApex(
1785 "com.android.apex.test.sharedlibs_generated.v1.libvX.apex");
1786 std::string apex_path_3 = AddDataApex("apex.apexd_test_v2.apex");
1787 std::string apex_path_4 =
1788 AddDataApex("com.android.apex.test.sharedlibs_generated.v2.libvY.apex");
1789
1790 ASSERT_EQ(OnOtaChrootBootstrap(), 0);
1791
1792 UnmountOnTearDown(apex_path_2);
1793 UnmountOnTearDown(apex_path_3);
1794 UnmountOnTearDown(apex_path_4);
1795
1796 auto apex_mounts = GetApexMounts();
1797 ASSERT_THAT(apex_mounts,
1798 UnorderedElementsAre("/apex/com.android.apex.test_package",
1799 "/apex/com.android.apex.test_package@2",
1800 "/apex/com.android.apex.test.sharedlibs@1",
1801 "/apex/com.android.apex.test.sharedlibs@2"));
1802
1803 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
1804 auto info_list =
1805 com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
1806 ASSERT_TRUE(info_list.has_value());
1807 auto apex_info_xml_1 = com::android::apex::ApexInfo(
1808 /* moduleName= */ "com.android.apex.test_package",
1809 /* modulePath= */ apex_path_1,
1810 /* preinstalledModulePath= */ apex_path_1,
1811 /* versionCode= */ 1, /* versionName= */ "1",
1812 /* isFactory= */ true, /* isActive= */ false, GetMTime(apex_path_1));
1813 auto apex_info_xml_2 = com::android::apex::ApexInfo(
1814 /* moduleName= */ "com.android.apex.test.sharedlibs",
1815 /* modulePath= */ apex_path_2,
1816 /* preinstalledModulePath= */ apex_path_2,
1817 /* versionCode= */ 1, /* versionName= */ "1",
1818 /* isFactory= */ true, /* isActive= */ false, GetMTime(apex_path_2));
1819 auto apex_info_xml_3 = com::android::apex::ApexInfo(
1820 /* moduleName= */ "com.android.apex.test_package",
1821 /* modulePath= */ apex_path_3,
1822 /* preinstalledModulePath= */ apex_path_1,
1823 /* versionCode= */ 2, /* versionName= */ "2",
1824 /* isFactory= */ false, /* isActive= */ true, GetMTime(apex_path_3));
1825 auto apex_info_xml_4 = com::android::apex::ApexInfo(
1826 /* moduleName= */ "com.android.apex.test.sharedlibs",
1827 /* modulePath= */ apex_path_4,
1828 /* preinstalledModulePath= */ apex_path_2,
1829 /* versionCode= */ 2, /* versionName= */ "2",
1830 /* isFactory= */ false, /* isActive= */ true, GetMTime(apex_path_4));
1831
1832 ASSERT_THAT(info_list->getApexInfo(),
1833 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1),
1834 ApexInfoXmlEq(apex_info_xml_2),
1835 ApexInfoXmlEq(apex_info_xml_3),
1836 ApexInfoXmlEq(apex_info_xml_4)));
1837
1838 ASSERT_EQ(access("/apex/sharedlibs", F_OK), 0);
1839
1840 // Check /apex/sharedlibs is populated properly.
1841 // Because we don't want to hardcode full paths (they are pretty long and have
1842 // a hash in them which might change if new prebuilts are dropped in), the
1843 // assertion logic is a little bit clunky.
1844 std::vector<std::string> sharedlibs;
1845 for (const auto& p : fs::recursive_directory_iterator("/apex/sharedlibs")) {
1846 if (fs::is_symlink(p)) {
1847 auto src = fs::read_symlink(p.path());
1848 ASSERT_EQ(p.path().filename(), src.filename());
1849 sharedlibs.push_back(p.path().parent_path().string() + "->" +
1850 src.parent_path().string());
1851 }
1852 }
1853
1854 std::vector<std::string> expected = {
1855 "/apex/sharedlibs/lib/libsharedlibtest.so->"
1856 "/apex/com.android.apex.test.sharedlibs@2/lib/libsharedlibtest.so",
1857 "/apex/sharedlibs/lib/libsharedlibtest.so->"
1858 "/apex/com.android.apex.test.sharedlibs@1/lib/libsharedlibtest.so",
1859 "/apex/sharedlibs/lib/libc++.so->"
1860 "/apex/com.android.apex.test.sharedlibs@1/lib/libc++.so",
1861 };
1862 // On 64bit devices we also have lib64.
1863 if (!GetProperty("ro.product.cpu.abilist64", "").empty()) {
1864 expected.push_back(
1865 "/apex/sharedlibs/lib64/libsharedlibtest.so->"
1866 "/apex/com.android.apex.test.sharedlibs@2/lib64/libsharedlibtest.so");
1867 expected.push_back(
1868 "/apex/sharedlibs/lib64/libsharedlibtest.so->"
1869 "/apex/com.android.apex.test.sharedlibs@1/lib64/libsharedlibtest.so");
1870 expected.push_back(
1871 "/apex/sharedlibs/lib64/libc++.so->"
1872 "/apex/com.android.apex.test.sharedlibs@1/lib64/libc++.so");
1873 }
1874
1875 ASSERT_THAT(sharedlibs, UnorderedElementsAreArray(expected));
1876 }
1877
1878 // Test when we move from uncompressed APEX to CAPEX via ota
TEST_F(ApexdMountTest,OnOtaChrootBootstrapOnlyCompressedApexes)1879 TEST_F(ApexdMountTest, OnOtaChrootBootstrapOnlyCompressedApexes) {
1880 std::string apex_path =
1881 AddPreInstalledApex("com.android.apex.compressed.v1.capex");
1882
1883 ASSERT_EQ(OnOtaChrootBootstrap(), 0);
1884
1885 // Decompressed APEX should be mounted from decompression_dir
1886 std::string decompressed_apex =
1887 StringPrintf("%s/com.android.apex.compressed@1%s",
1888 GetDecompressionDir().c_str(), kOtaApexPackageSuffix);
1889 UnmountOnTearDown(decompressed_apex);
1890
1891 auto apex_mounts = GetApexMounts();
1892 ASSERT_THAT(apex_mounts,
1893 UnorderedElementsAre("/apex/com.android.apex.compressed",
1894 "/apex/com.android.apex.compressed@1"));
1895
1896 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
1897 auto info_list =
1898 com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
1899 ASSERT_TRUE(info_list.has_value());
1900 auto apex_info_xml_decompressed = com::android::apex::ApexInfo(
1901 /* moduleName= */ "com.android.apex.compressed",
1902 /* modulePath= */ decompressed_apex,
1903 /* preinstalledModulePath= */ apex_path,
1904 /* versionCode= */ 1, /* versionName= */ "1",
1905 /* isFactory= */ true, /* isActive= */ true, GetMTime(decompressed_apex));
1906 ASSERT_THAT(info_list->getApexInfo(),
1907 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_decompressed)));
1908 auto& db = GetApexDatabaseForTesting();
1909 // Check that it was mounted from decompressed apex. It should also be mounted
1910 // on dm-verity device.
1911 db.ForallMountedApexes("com.android.apex.compressed",
1912 [&](const MountedApexData& data, bool latest) {
1913 ASSERT_TRUE(latest);
1914 ASSERT_EQ(data.full_path, decompressed_apex);
1915 ASSERT_EQ(data.device_name,
1916 "com.android.apex.compressed@1.chroot");
1917 });
1918 }
1919
1920 // Test we decompress only once even if OnOtaChrootBootstrap is called multiple
1921 // times
TEST_F(ApexdMountTest,OnOtaChrootBootstrapDecompressOnlyOnceMultipleCalls)1922 TEST_F(ApexdMountTest, OnOtaChrootBootstrapDecompressOnlyOnceMultipleCalls) {
1923 std::string apex_path =
1924 AddPreInstalledApex("com.android.apex.compressed.v1.capex");
1925
1926 ASSERT_EQ(OnOtaChrootBootstrap(), 0);
1927
1928 // Decompressed OTA APEX should be mounted
1929 std::string decompressed_ota_apex =
1930 StringPrintf("%s/com.android.apex.compressed@1%s",
1931 GetDecompressionDir().c_str(), kOtaApexPackageSuffix);
1932 UnmountOnTearDown(decompressed_ota_apex);
1933
1934 // Capture the creation time of the OTA APEX
1935 std::error_code ec;
1936 auto last_write_time_1 = fs::last_write_time(decompressed_ota_apex, ec);
1937 ASSERT_FALSE(ec) << "Failed to capture last write time of "
1938 << decompressed_ota_apex;
1939
1940 // Call OnOtaChrootBootstrap again. Since we do not hardlink decompressed APEX
1941 // to /data/apex/active directory when in chroot, when selecting apex for
1942 // activation, we will end up selecting compressed APEX again.
1943 ASSERT_EQ(OnOtaChrootBootstrap(), 0);
1944
1945 // Compare write time to ensure we did not decompress again
1946 auto last_write_time_2 = fs::last_write_time(decompressed_ota_apex, ec);
1947 ASSERT_FALSE(ec) << "Failed to capture last write time of "
1948 << decompressed_ota_apex << ec.message();
1949 ASSERT_EQ(last_write_time_1, last_write_time_2);
1950 }
1951
1952 // Test when we upgrade existing CAPEX to higher version via OTA
TEST_F(ApexdMountTest,OnOtaChrootBootstrapUpgradeCapex)1953 TEST_F(ApexdMountTest, OnOtaChrootBootstrapUpgradeCapex) {
1954 TemporaryDir previous_built_in_dir;
1955 PrepareCompressedApex("com.android.apex.compressed.v1.capex",
1956 previous_built_in_dir.path);
1957 // Place a higher version capex in current built_in_dir
1958 std::string apex_path =
1959 AddPreInstalledApex("com.android.apex.compressed.v2.capex");
1960
1961 ASSERT_EQ(OnOtaChrootBootstrap(), 0);
1962
1963 // Upgraded decompressed APEX should be mounted from decompression dir
1964 std::string decompressed_active_apex =
1965 StringPrintf("%s/com.android.apex.compressed@2%s",
1966 GetDecompressionDir().c_str(), kOtaApexPackageSuffix);
1967 UnmountOnTearDown(decompressed_active_apex);
1968
1969 auto apex_mounts = GetApexMounts();
1970 ASSERT_THAT(apex_mounts,
1971 UnorderedElementsAre("/apex/com.android.apex.compressed",
1972 "/apex/com.android.apex.compressed@2"));
1973
1974 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
1975 auto info_list =
1976 com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
1977 ASSERT_TRUE(info_list.has_value());
1978 auto apex_info_xml_decompressed = com::android::apex::ApexInfo(
1979 /* moduleName= */ "com.android.apex.compressed",
1980 /* modulePath= */ decompressed_active_apex,
1981 /* preinstalledModulePath= */ apex_path,
1982 /* versionCode= */ 2, /* versionName= */ "2",
1983 /* isFactory= */ true, /* isActive= */ true,
1984 GetMTime(decompressed_active_apex));
1985 ASSERT_THAT(info_list->getApexInfo(),
1986 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_decompressed)));
1987 auto& db = GetApexDatabaseForTesting();
1988 // Check that it was mounted from decompressed apex. It should also be mounted
1989 // on dm-verity device.
1990 db.ForallMountedApexes("com.android.apex.compressed",
1991 [&](const MountedApexData& data, bool latest) {
1992 ASSERT_TRUE(latest);
1993 ASSERT_EQ(data.full_path, decompressed_active_apex);
1994 ASSERT_EQ(data.device_name,
1995 "com.android.apex.compressed@2.chroot");
1996 });
1997 }
1998
1999 // Test when we update existing CAPEX to same version via OTA
TEST_F(ApexdMountTest,OnOtaChrootBootstrapSamegradeCapex)2000 TEST_F(ApexdMountTest, OnOtaChrootBootstrapSamegradeCapex) {
2001 TemporaryDir previous_built_in_dir;
2002 PrepareCompressedApex("com.android.apex.compressed.v1.capex",
2003 previous_built_in_dir.path);
2004 // Place a same version capex in current built_in_dir, under a different name
2005 auto apex_path =
2006 StringPrintf("%s/different-name.capex", GetBuiltInDir().c_str());
2007 fs::copy(GetTestFile("com.android.apex.compressed.v1.capex"), apex_path);
2008
2009 ASSERT_EQ(OnOtaChrootBootstrap(), 0);
2010
2011 // Previously decompressed APEX should be mounted from decompression_dir
2012 std::string decompressed_active_apex = StringPrintf(
2013 "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(),
2014 kDecompressedApexPackageSuffix);
2015 UnmountOnTearDown(decompressed_active_apex);
2016
2017 auto apex_mounts = GetApexMounts();
2018 ASSERT_THAT(apex_mounts,
2019 UnorderedElementsAre("/apex/com.android.apex.compressed",
2020 "/apex/com.android.apex.compressed@1"));
2021
2022 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
2023 auto info_list =
2024 com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
2025 ASSERT_TRUE(info_list.has_value());
2026 auto apex_info_xml_decompressed = com::android::apex::ApexInfo(
2027 /* moduleName= */ "com.android.apex.compressed",
2028 /* modulePath= */ decompressed_active_apex,
2029 /* preinstalledModulePath= */ apex_path,
2030 /* versionCode= */ 1, /* versionName= */ "1",
2031 /* isFactory= */ true, /* isActive= */ true,
2032 GetMTime(decompressed_active_apex));
2033 ASSERT_THAT(info_list->getApexInfo(),
2034 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_decompressed)));
2035 auto& db = GetApexDatabaseForTesting();
2036 // Check that it was mounted from decompressed apex. It should also be mounted
2037 // on dm-verity device.
2038 db.ForallMountedApexes("com.android.apex.compressed",
2039 [&](const MountedApexData& data, bool latest) {
2040 ASSERT_TRUE(latest);
2041 ASSERT_EQ(data.full_path, decompressed_active_apex);
2042 ASSERT_EQ(data.device_name,
2043 "com.android.apex.compressed@1.chroot");
2044 });
2045 }
2046
2047 // Test when we update existing CAPEX to same version, but different digest
TEST_F(ApexdMountTest,OnOtaChrootBootstrapSamegradeCapexDifferentDigest)2048 TEST_F(ApexdMountTest, OnOtaChrootBootstrapSamegradeCapexDifferentDigest) {
2049 TemporaryDir previous_built_in_dir;
2050 auto different_digest_apex_path = PrepareCompressedApex(
2051 "com.android.apex.compressed.v1_different_digest.capex",
2052 previous_built_in_dir.path);
2053 // Place a same version capex in current built_in_dir, which has different
2054 // digest
2055 auto apex_path = AddPreInstalledApex("com.android.apex.compressed.v1.capex");
2056
2057 ASSERT_EQ(OnOtaChrootBootstrap(), 0);
2058
2059 // New decompressed ota APEX should be mounted with kOtaApexPackageSuffix
2060 std::string decompressed_ota_apex =
2061 StringPrintf("%s/com.android.apex.compressed@1%s",
2062 GetDecompressionDir().c_str(), kOtaApexPackageSuffix);
2063 UnmountOnTearDown(decompressed_ota_apex);
2064
2065 auto apex_mounts = GetApexMounts();
2066 ASSERT_THAT(apex_mounts,
2067 UnorderedElementsAre("/apex/com.android.apex.compressed",
2068 "/apex/com.android.apex.compressed@1"));
2069
2070 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
2071 auto info_list =
2072 com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
2073 ASSERT_TRUE(info_list.has_value());
2074 auto apex_info_xml_decompressed = com::android::apex::ApexInfo(
2075 /* moduleName= */ "com.android.apex.compressed",
2076 /* modulePath= */ decompressed_ota_apex,
2077 /* preinstalledModulePath= */ apex_path,
2078 /* versionCode= */ 1, /* versionName= */ "1",
2079 /* isFactory= */ true, /* isActive= */ true,
2080 GetMTime(decompressed_ota_apex));
2081 ASSERT_THAT(info_list->getApexInfo(),
2082 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_decompressed)));
2083 auto& db = GetApexDatabaseForTesting();
2084 // Check that it was mounted from decompressed apex. It should also be mounted
2085 // on dm-verity device.
2086 db.ForallMountedApexes("com.android.apex.compressed",
2087 [&](const MountedApexData& data, bool latest) {
2088 ASSERT_TRUE(latest);
2089 ASSERT_EQ(data.full_path, decompressed_ota_apex);
2090 ASSERT_EQ(data.device_name,
2091 "com.android.apex.compressed@1.chroot");
2092 });
2093
2094 // Ensure decompressed apex has same digest as pre-installed
2095 auto pre_installed_apex = ApexFile::Open(apex_path);
2096 auto decompressed_apex = ApexFile::Open(decompressed_ota_apex);
2097 auto different_digest_apex = ApexFile::Open(different_digest_apex_path);
2098 ASSERT_EQ(
2099 pre_installed_apex->GetManifest().capexmetadata().originalapexdigest(),
2100 GetRootDigest(*decompressed_apex));
2101 ASSERT_NE(
2102 pre_installed_apex->GetManifest().capexmetadata().originalapexdigest(),
2103 GetRootDigest(*different_digest_apex));
2104
2105 // Ensure we didn't remove previous decompressed APEX
2106 std::string previous_decompressed_apex = StringPrintf(
2107 "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(),
2108 kDecompressedApexPackageSuffix);
2109 auto path_exists = PathExists(previous_decompressed_apex);
2110 ASSERT_TRUE(*path_exists);
2111 }
2112
2113 // Test when we update existing CAPEX to same version, but different key via OTA
TEST_F(ApexdMountTest,OnOtaChrootBootstrapSamegradeCapexDifferentKey)2114 TEST_F(ApexdMountTest, OnOtaChrootBootstrapSamegradeCapexDifferentKey) {
2115 TemporaryDir previous_built_in_dir;
2116 PrepareCompressedApex("com.android.apex.compressed_different_key.capex",
2117 previous_built_in_dir.path);
2118 // Place a same version capex in current built_in_dir, which has different key
2119 auto apex_path = AddPreInstalledApex("com.android.apex.compressed.v1.capex");
2120
2121 ASSERT_EQ(OnOtaChrootBootstrap(), 0);
2122
2123 // New decompressed APEX should be mounted from ota_reserved directory
2124 std::string decompressed_active_apex =
2125 StringPrintf("%s/com.android.apex.compressed@1%s",
2126 GetDecompressionDir().c_str(), kOtaApexPackageSuffix);
2127 UnmountOnTearDown(decompressed_active_apex);
2128
2129 auto apex_mounts = GetApexMounts();
2130 ASSERT_THAT(apex_mounts,
2131 UnorderedElementsAre("/apex/com.android.apex.compressed",
2132 "/apex/com.android.apex.compressed@1"));
2133
2134 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
2135 auto info_list =
2136 com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
2137 ASSERT_TRUE(info_list.has_value());
2138 auto apex_info_xml_decompressed = com::android::apex::ApexInfo(
2139 /* moduleName= */ "com.android.apex.compressed",
2140 /* modulePath= */ decompressed_active_apex,
2141 /* preinstalledModulePath= */ apex_path,
2142 /* versionCode= */ 1, /* versionName= */ "1",
2143 /* isFactory= */ true, /* isActive= */ true,
2144 GetMTime(decompressed_active_apex));
2145 ASSERT_THAT(info_list->getApexInfo(),
2146 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_decompressed)));
2147 auto& db = GetApexDatabaseForTesting();
2148 // Check that it was mounted from decompressed apex. It should also be mounted
2149 // on dm-verity device.
2150 db.ForallMountedApexes("com.android.apex.compressed",
2151 [&](const MountedApexData& data, bool latest) {
2152 ASSERT_TRUE(latest);
2153 ASSERT_EQ(data.full_path, decompressed_active_apex);
2154 ASSERT_EQ(data.device_name,
2155 "com.android.apex.compressed@1.chroot");
2156 });
2157 }
2158
2159 // Test when we remove CAPEX via OTA
TEST_F(ApexdMountTest,OnOtaChrootBootstrapCapexToApex)2160 TEST_F(ApexdMountTest, OnOtaChrootBootstrapCapexToApex) {
2161 TemporaryDir previous_built_in_dir;
2162 PrepareCompressedApex("com.android.apex.compressed.v1.capex",
2163 previous_built_in_dir.path);
2164 // Place a uncompressed version apex in current built_in_dir
2165 std::string apex_path =
2166 AddPreInstalledApex("com.android.apex.compressed.v1_original.apex");
2167
2168 ASSERT_EQ(OnOtaChrootBootstrap(), 0);
2169
2170 // New uncompressed APEX should be mounted
2171 UnmountOnTearDown(apex_path);
2172
2173 auto apex_mounts = GetApexMounts();
2174 ASSERT_THAT(apex_mounts,
2175 UnorderedElementsAre("/apex/com.android.apex.compressed",
2176 "/apex/com.android.apex.compressed@1"));
2177
2178 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
2179 auto info_list =
2180 com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
2181 ASSERT_TRUE(info_list.has_value());
2182 auto apex_info_xml_uncompressed = com::android::apex::ApexInfo(
2183 /* moduleName= */ "com.android.apex.compressed",
2184 /* modulePath= */ apex_path,
2185 /* preinstalledModulePath= */ apex_path,
2186 /* versionCode= */ 1, /* versionName= */ "1",
2187 /* isFactory= */ true, /* isActive= */ true, GetMTime(apex_path));
2188 ASSERT_THAT(info_list->getApexInfo(),
2189 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_uncompressed)));
2190 }
2191
TEST_F(ApexdMountTest,OnOtaChrootBootstrapDecompressedApexVersionDifferentThanCapex)2192 TEST_F(ApexdMountTest,
2193 OnOtaChrootBootstrapDecompressedApexVersionDifferentThanCapex) {
2194 TemporaryDir previous_built_in_dir;
2195 PrepareCompressedApex("com.android.apex.compressed.v2.capex",
2196 previous_built_in_dir.path);
2197 // Place a lower version capex in current built_in_dir, so that previously
2198 // decompressed APEX has higher version but still doesn't get picked during
2199 // selection.
2200 std::string apex_path =
2201 AddPreInstalledApex("com.android.apex.compressed.v1.capex");
2202
2203 ASSERT_EQ(OnOtaChrootBootstrap(), 0);
2204
2205 // Pre-installed CAPEX should be decompressed again and mounted from
2206 // decompression_dir
2207 std::string decompressed_active_apex =
2208 StringPrintf("%s/com.android.apex.compressed@1%s",
2209 GetDecompressionDir().c_str(), kOtaApexPackageSuffix);
2210 UnmountOnTearDown(decompressed_active_apex);
2211
2212 auto apex_mounts = GetApexMounts();
2213 ASSERT_THAT(apex_mounts,
2214 UnorderedElementsAre("/apex/com.android.apex.compressed",
2215 "/apex/com.android.apex.compressed@1"));
2216
2217 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
2218 auto info_list =
2219 com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
2220 ASSERT_TRUE(info_list.has_value());
2221 auto apex_info_xml_decompressed = com::android::apex::ApexInfo(
2222 /* moduleName= */ "com.android.apex.compressed",
2223 /* modulePath= */ decompressed_active_apex,
2224 /* preinstalledModulePath= */ apex_path,
2225 /* versionCode= */ 1, /* versionName= */ "1",
2226 /* isFactory= */ true, /* isActive= */ true,
2227 GetMTime(decompressed_active_apex));
2228 ASSERT_THAT(info_list->getApexInfo(),
2229 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_decompressed)));
2230 }
2231
2232 // Test when we update CAPEX and there is a higher version present in data
TEST_F(ApexdMountTest,OnOtaChrootBootstrapDataHigherThanCapex)2233 TEST_F(ApexdMountTest, OnOtaChrootBootstrapDataHigherThanCapex) {
2234 auto system_apex_path =
2235 PrepareCompressedApex("com.android.apex.compressed.v1.capex");
2236 auto data_apex_path =
2237 AddDataApex("com.android.apex.compressed.v2_original.apex");
2238
2239 ASSERT_EQ(OnOtaChrootBootstrap(), 0);
2240
2241 // Data APEX should be mounted
2242 UnmountOnTearDown(data_apex_path);
2243
2244 auto apex_mounts = GetApexMounts();
2245 ASSERT_THAT(apex_mounts,
2246 UnorderedElementsAre("/apex/com.android.apex.compressed",
2247 "/apex/com.android.apex.compressed@2"));
2248
2249 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
2250 auto info_list =
2251 com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
2252 ASSERT_TRUE(info_list.has_value());
2253 auto apex_info_xml_data = com::android::apex::ApexInfo(
2254 /* moduleName= */ "com.android.apex.compressed",
2255 /* modulePath= */ data_apex_path,
2256 /* preinstalledModulePath= */ system_apex_path,
2257 /* versionCode= */ 2, /* versionName= */ "2",
2258 /* isFactory= */ false, /* isActive= */ true, GetMTime(data_apex_path));
2259 auto apex_info_xml_system = com::android::apex::ApexInfo(
2260 /* moduleName= */ "com.android.apex.compressed",
2261 /* modulePath= */ system_apex_path,
2262 /* preinstalledModulePath= */ system_apex_path,
2263 /* versionCode= */ 1, /* versionName= */ "1",
2264 /* isFactory= */ true, /* isActive= */ false, GetMTime(system_apex_path));
2265 ASSERT_THAT(info_list->getApexInfo(),
2266 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_data),
2267 ApexInfoXmlEq(apex_info_xml_system)));
2268 auto& db = GetApexDatabaseForTesting();
2269 // Check that it was mounted from decompressed apex. It should also be mounted
2270 // on dm-verity device.
2271 db.ForallMountedApexes("com.android.apex.compressed",
2272 [&](const MountedApexData& data, bool latest) {
2273 ASSERT_TRUE(latest);
2274 ASSERT_EQ(data.full_path, data_apex_path);
2275 ASSERT_EQ(data.device_name,
2276 "com.android.apex.compressed@2.chroot");
2277 });
2278 }
2279
2280 // Test when we update CAPEX and there is a lower version present in data
TEST_F(ApexdMountTest,OnOtaChrootBootstrapDataLowerThanCapex)2281 TEST_F(ApexdMountTest, OnOtaChrootBootstrapDataLowerThanCapex) {
2282 auto apex_path = AddPreInstalledApex("com.android.apex.compressed.v2.capex");
2283 AddDataApex("com.android.apex.compressed.v1_original.apex");
2284
2285 ASSERT_EQ(OnOtaChrootBootstrap(), 0);
2286
2287 // Decompressed APEX should be mounted from reserved dir
2288 std::string decompressed_active_apex =
2289 StringPrintf("%s/com.android.apex.compressed@2%s",
2290 GetDecompressionDir().c_str(), kOtaApexPackageSuffix);
2291 UnmountOnTearDown(decompressed_active_apex);
2292
2293 auto apex_mounts = GetApexMounts();
2294 ASSERT_THAT(apex_mounts,
2295 UnorderedElementsAre("/apex/com.android.apex.compressed",
2296 "/apex/com.android.apex.compressed@2"));
2297
2298 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
2299 auto info_list =
2300 com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
2301 ASSERT_TRUE(info_list.has_value());
2302 auto apex_info_xml = com::android::apex::ApexInfo(
2303 /* moduleName= */ "com.android.apex.compressed",
2304 /* modulePath= */ decompressed_active_apex,
2305 /* preinstalledModulePath= */ apex_path,
2306 /* versionCode= */ 2, /* versionName= */ "2",
2307 /* isFactory= */ true, /* isActive= */ true,
2308 GetMTime(decompressed_active_apex));
2309 ASSERT_THAT(info_list->getApexInfo(),
2310 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml)));
2311 auto& db = GetApexDatabaseForTesting();
2312 // Check that it was mounted from decompressed apex. It should also be mounted
2313 // on dm-verity device.
2314 db.ForallMountedApexes("com.android.apex.compressed",
2315 [&](const MountedApexData& data, bool latest) {
2316 ASSERT_TRUE(latest);
2317 ASSERT_EQ(data.full_path, decompressed_active_apex);
2318 ASSERT_EQ(data.device_name,
2319 "com.android.apex.compressed@2.chroot");
2320 });
2321 }
2322
2323 // Test when we update CAPEX and there is a same version present in data
TEST_F(ApexdMountTest,OnOtaChrootBootstrapDataSameAsCapex)2324 TEST_F(ApexdMountTest, OnOtaChrootBootstrapDataSameAsCapex) {
2325 auto system_apex_path =
2326 PrepareCompressedApex("com.android.apex.compressed.v1.capex");
2327 auto data_apex_path =
2328 AddDataApex("com.android.apex.compressed.v1_original.apex");
2329
2330 ASSERT_EQ(OnOtaChrootBootstrap(), 0);
2331
2332 // Data APEX should be mounted
2333 UnmountOnTearDown(data_apex_path);
2334
2335 auto apex_mounts = GetApexMounts();
2336 ASSERT_THAT(apex_mounts,
2337 UnorderedElementsAre("/apex/com.android.apex.compressed",
2338 "/apex/com.android.apex.compressed@1"));
2339
2340 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
2341 auto info_list =
2342 com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
2343 ASSERT_TRUE(info_list.has_value());
2344 auto apex_info_xml_data = com::android::apex::ApexInfo(
2345 /* moduleName= */ "com.android.apex.compressed",
2346 /* modulePath= */ data_apex_path,
2347 /* preinstalledModulePath= */ system_apex_path,
2348 /* versionCode= */ 1, /* versionName= */ "1",
2349 /* isFactory= */ false, /* isActive= */ true, GetMTime(data_apex_path));
2350 auto apex_info_xml_system = com::android::apex::ApexInfo(
2351 /* moduleName= */ "com.android.apex.compressed",
2352 /* modulePath= */ system_apex_path,
2353 /* preinstalledModulePath= */ system_apex_path,
2354 /* versionCode= */ 1, /* versionName= */ "1",
2355 /* isFactory= */ true, /* isActive= */ false, GetMTime(system_apex_path));
2356 ASSERT_THAT(info_list->getApexInfo(),
2357 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_data),
2358 ApexInfoXmlEq(apex_info_xml_system)));
2359 auto& db = GetApexDatabaseForTesting();
2360 // Check that it was mounted from decompressed apex. It should also be mounted
2361 // on dm-verity device.
2362 db.ForallMountedApexes("com.android.apex.compressed",
2363 [&](const MountedApexData& data, bool latest) {
2364 ASSERT_TRUE(latest);
2365 ASSERT_EQ(data.full_path, data_apex_path);
2366 ASSERT_EQ(data.device_name,
2367 "com.android.apex.compressed@1.chroot");
2368 });
2369 }
2370
TEST_F(ApexdMountTest,OnOtaChrootBootstrapDataHasDifferentKeyThanCapex)2371 TEST_F(ApexdMountTest, OnOtaChrootBootstrapDataHasDifferentKeyThanCapex) {
2372 AddDataApex("com.android.apex.compressed_different_key.capex");
2373 // Place a same version capex in current built_in_dir, which has different key
2374 auto apex_path = AddPreInstalledApex("com.android.apex.compressed.v1.capex");
2375
2376 ASSERT_EQ(OnOtaChrootBootstrap(), 0);
2377
2378 // New decompressed APEX should be mounted from ota_reserved directory
2379 std::string decompressed_active_apex =
2380 StringPrintf("%s/com.android.apex.compressed@1%s",
2381 GetDecompressionDir().c_str(), kOtaApexPackageSuffix);
2382 UnmountOnTearDown(decompressed_active_apex);
2383
2384 auto apex_mounts = GetApexMounts();
2385 ASSERT_THAT(apex_mounts,
2386 UnorderedElementsAre("/apex/com.android.apex.compressed",
2387 "/apex/com.android.apex.compressed@1"));
2388
2389 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
2390 auto info_list =
2391 com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
2392 ASSERT_TRUE(info_list.has_value());
2393 auto apex_info_xml_decompressed = com::android::apex::ApexInfo(
2394 /* moduleName= */ "com.android.apex.compressed",
2395 /* modulePath= */ decompressed_active_apex,
2396 /* preinstalledModulePath= */ apex_path,
2397 /* versionCode= */ 1, /* versionName= */ "1",
2398 /* isFactory= */ true, /* isActive= */ true,
2399 GetMTime(decompressed_active_apex));
2400 ASSERT_THAT(info_list->getApexInfo(),
2401 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_decompressed)));
2402 auto& db = GetApexDatabaseForTesting();
2403 // Check that it was mounted from decompressed apex. It should also be mounted
2404 // on dm-verity device.
2405 db.ForallMountedApexes("com.android.apex.compressed",
2406 [&](const MountedApexData& data, bool latest) {
2407 ASSERT_TRUE(latest);
2408 ASSERT_EQ(data.full_path, decompressed_active_apex);
2409 ASSERT_EQ(data.device_name,
2410 "com.android.apex.compressed@1.chroot");
2411 });
2412 }
2413
GetSelinuxContext(const std::string & file)2414 static std::string GetSelinuxContext(const std::string& file) {
2415 char* ctx;
2416 if (getfilecon(file.c_str(), &ctx) < 0) {
2417 PLOG(ERROR) << "Failed to getfilecon " << file;
2418 return "";
2419 }
2420 std::string result(ctx);
2421 freecon(ctx);
2422 return result;
2423 }
2424
TEST_F(ApexdMountTest,OnOtaChrootBootstrapSelinuxLabelsAreCorrect)2425 TEST_F(ApexdMountTest, OnOtaChrootBootstrapSelinuxLabelsAreCorrect) {
2426 std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex");
2427 std::string apex_path_2 = AddPreInstalledApex(
2428 "com.android.apex.test.sharedlibs_generated.v1.libvX.apex");
2429 std::string apex_path_3 = AddDataApex("apex.apexd_test_v2.apex");
2430
2431 UnmountOnTearDown(apex_path_2);
2432 UnmountOnTearDown(apex_path_3);
2433 ASSERT_EQ(OnOtaChrootBootstrap(), 0);
2434
2435 EXPECT_EQ(GetSelinuxContext("/apex/apex-info-list.xml"),
2436 "u:object_r:apex_info_file:s0");
2437
2438 EXPECT_EQ(GetSelinuxContext("/apex/sharedlibs"),
2439 "u:object_r:apex_mnt_dir:s0");
2440
2441 EXPECT_EQ(GetSelinuxContext("/apex/com.android.apex.test_package"),
2442 "u:object_r:system_file:s0");
2443 EXPECT_EQ(GetSelinuxContext("/apex/com.android.apex.test_package@2"),
2444 "u:object_r:system_file:s0");
2445 }
2446
TEST_F(ApexdMountTest,OnOtaChrootBootstrapDmDevicesHaveCorrectName)2447 TEST_F(ApexdMountTest, OnOtaChrootBootstrapDmDevicesHaveCorrectName) {
2448 std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex");
2449 std::string apex_path_2 =
2450 AddPreInstalledApex("apex.apexd_test_different_app.apex");
2451 std::string apex_path_3 = AddDataApex("apex.apexd_test_v2.apex");
2452
2453 ASSERT_EQ(OnOtaChrootBootstrap(), 0);
2454 UnmountOnTearDown(apex_path_2);
2455 UnmountOnTearDown(apex_path_3);
2456
2457 MountedApexDatabase& db = GetApexDatabaseForTesting();
2458 // com.android.apex.test_package_2 should be mounted directly on top of loop
2459 // device.
2460 db.ForallMountedApexes("com.android.apex.test_package_2",
2461 [&](const MountedApexData& data, bool latest) {
2462 ASSERT_TRUE(latest);
2463 ASSERT_THAT(data.device_name, IsEmpty());
2464 ASSERT_THAT(data.loop_name, StartsWith("/dev"));
2465 });
2466 // com.android.apex.test_package should be mounted on top of dm-verity device.
2467 db.ForallMountedApexes("com.android.apex.test_package",
2468 [&](const MountedApexData& data, bool latest) {
2469 ASSERT_TRUE(latest);
2470 ASSERT_EQ(data.device_name,
2471 "com.android.apex.test_package@2.chroot");
2472 ASSERT_THAT(data.loop_name, StartsWith("/dev"));
2473 });
2474 }
2475
TEST_F(ApexdMountTest,OnOtaChrootBootstrapFailsToActivatePreInstalledApexKeepsGoing)2476 TEST_F(ApexdMountTest,
2477 OnOtaChrootBootstrapFailsToActivatePreInstalledApexKeepsGoing) {
2478 std::string apex_path_1 =
2479 AddPreInstalledApex("apex.apexd_test_manifest_mismatch.apex");
2480 std::string apex_path_2 =
2481 AddPreInstalledApex("apex.apexd_test_different_app.apex");
2482
2483 ASSERT_EQ(OnOtaChrootBootstrap(), 0);
2484 UnmountOnTearDown(apex_path_2);
2485
2486 auto apex_mounts = GetApexMounts();
2487 ASSERT_THAT(apex_mounts,
2488 UnorderedElementsAre("/apex/com.android.apex.test_package_2",
2489 "/apex/com.android.apex.test_package_2@1"));
2490
2491 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
2492 auto info_list =
2493 com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
2494 ASSERT_TRUE(info_list.has_value());
2495 auto apex_info_xml_1 = com::android::apex::ApexInfo(
2496 /* moduleName= */ "com.android.apex.test_package",
2497 /* modulePath= */ apex_path_1,
2498 /* preinstalledModulePath= */ apex_path_1,
2499 /* versionCode= */ 137, /* versionName= */ "1",
2500 /* isFactory= */ true, /* isActive= */ false, GetMTime(apex_path_1));
2501 auto apex_info_xml_2 = com::android::apex::ApexInfo(
2502 /* moduleName= */ "com.android.apex.test_package_2",
2503 /* modulePath= */ apex_path_2, /* preinstalledModulePath= */ apex_path_2,
2504 /* versionCode= */ 1, /* versionName= */ "1", /* isFactory= */ true,
2505 /* isActive= */ true, GetMTime(apex_path_2));
2506
2507 ASSERT_THAT(info_list->getApexInfo(),
2508 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1),
2509 ApexInfoXmlEq(apex_info_xml_2)));
2510 }
2511
TEST_F(ApexdMountTest,OnOtaChrootBootstrapFailsToActivateDataApexFallsBackToPreInstalled)2512 TEST_F(ApexdMountTest,
2513 OnOtaChrootBootstrapFailsToActivateDataApexFallsBackToPreInstalled) {
2514 std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex");
2515 std::string apex_path_2 =
2516 AddPreInstalledApex("apex.apexd_test_different_app.apex");
2517 std::string apex_path_3 =
2518 AddDataApex("apex.apexd_test_manifest_mismatch.apex");
2519
2520 ASSERT_EQ(OnOtaChrootBootstrap(), 0);
2521 UnmountOnTearDown(apex_path_1);
2522 UnmountOnTearDown(apex_path_2);
2523
2524 auto apex_mounts = GetApexMounts();
2525 ASSERT_THAT(apex_mounts,
2526 UnorderedElementsAre("/apex/com.android.apex.test_package",
2527 "/apex/com.android.apex.test_package@1",
2528 "/apex/com.android.apex.test_package_2",
2529 "/apex/com.android.apex.test_package_2@1"));
2530
2531 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
2532 auto info_list =
2533 com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
2534 ASSERT_TRUE(info_list.has_value());
2535 auto apex_info_xml_1 = com::android::apex::ApexInfo(
2536 /* moduleName= */ "com.android.apex.test_package",
2537 /* modulePath= */ apex_path_1,
2538 /* preinstalledModulePath= */ apex_path_1,
2539 /* versionCode= */ 1, /* versionName= */ "1",
2540 /* isFactory= */ true, /* isActive= */ true, GetMTime(apex_path_1));
2541 auto apex_info_xml_2 = com::android::apex::ApexInfo(
2542 /* moduleName= */ "com.android.apex.test_package_2",
2543 /* modulePath= */ apex_path_2, /* preinstalledModulePath= */ apex_path_2,
2544 /* versionCode= */ 1, /* versionName= */ "1", /* isFactory= */ true,
2545 /* isActive= */ true, GetMTime(apex_path_2));
2546
2547 ASSERT_THAT(info_list->getApexInfo(),
2548 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1),
2549 ApexInfoXmlEq(apex_info_xml_2)));
2550 }
2551
TEST_F(ApexdMountTest,OnOtaChrootBootstrapFlattenedApex)2552 TEST_F(ApexdMountTest, OnOtaChrootBootstrapFlattenedApex) {
2553 std::string apex_dir_1 = GetBuiltInDir() + "/com.android.apex.test_package";
2554 std::string apex_dir_2 = GetBuiltInDir() + "/com.android.apex.test_package_2";
2555
2556 ASSERT_EQ(mkdir(apex_dir_1.c_str(), 0755), 0);
2557 ASSERT_EQ(mkdir(apex_dir_2.c_str(), 0755), 0);
2558
2559 auto write_manifest_fn = [&](const std::string& apex_dir,
2560 const std::string& module_name, int version) {
2561 using ::apex::proto::ApexManifest;
2562
2563 ApexManifest manifest;
2564 manifest.set_name(module_name);
2565 manifest.set_version(version);
2566 manifest.set_versionname(std::to_string(version));
2567
2568 std::string out;
2569 manifest.SerializeToString(&out);
2570 ASSERT_TRUE(WriteStringToFile(out, apex_dir + "/apex_manifest.pb"));
2571 };
2572
2573 write_manifest_fn(apex_dir_1, "com.android.apex.test_package", 2);
2574 write_manifest_fn(apex_dir_2, "com.android.apex.test_package_2", 1);
2575
2576 ASSERT_EQ(OnOtaChrootBootstrapFlattenedApex(), 0);
2577
2578 auto apex_mounts = GetApexMounts();
2579 ASSERT_THAT(apex_mounts,
2580 UnorderedElementsAre("/apex/com.android.apex.test_package",
2581 "/apex/com.android.apex.test_package_2"));
2582
2583 ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
2584 ASSERT_EQ(GetSelinuxContext("/apex/apex-info-list.xml"),
2585 "u:object_r:apex_info_file:s0");
2586
2587 auto info_list =
2588 com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
2589 ASSERT_TRUE(info_list.has_value());
2590 auto apex_info_xml_1 = com::android::apex::ApexInfo(
2591 /* moduleName= */ "com.android.apex.test_package",
2592 /* modulePath= */ apex_dir_1,
2593 /* preinstalledModulePath= */ apex_dir_1,
2594 /* versionCode= */ 2, /* versionName= */ "2",
2595 /* isFactory= */ true, /* isActive= */ true,
2596 /* lastUpdateMillis= */ 0);
2597 auto apex_info_xml_2 = com::android::apex::ApexInfo(
2598 /* moduleName= */ "com.android.apex.test_package_2",
2599 /* modulePath= */ apex_dir_2,
2600 /* preinstalledModulePath= */ apex_dir_2,
2601 /* versionCode= */ 1, /* versionName= */ "1",
2602 /* isFactory= */ true, /* isActive= */ true,
2603 /* lastUpdateMillis= */ 0);
2604
2605 ASSERT_THAT(info_list->getApexInfo(),
2606 UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1),
2607 ApexInfoXmlEq(apex_info_xml_2)));
2608 }
2609
TEST_F(ApexdMountTest,OnStartOnlyPreInstalledApexes)2610 TEST_F(ApexdMountTest, OnStartOnlyPreInstalledApexes) {
2611 MockCheckpointInterface checkpoint_interface;
2612 // Need to call InitializeVold before calling OnStart
2613 InitializeVold(&checkpoint_interface);
2614
2615 std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex");
2616 std::string apex_path_2 =
2617 AddPreInstalledApex("apex.apexd_test_different_app.apex");
2618
2619 ASSERT_RESULT_OK(
2620 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}));
2621
2622 OnStart();
2623
2624 UnmountOnTearDown(apex_path_1);
2625 UnmountOnTearDown(apex_path_2);
2626
2627 ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting");
2628 auto apex_mounts = GetApexMounts();
2629 ASSERT_THAT(apex_mounts,
2630 UnorderedElementsAre("/apex/com.android.apex.test_package",
2631 "/apex/com.android.apex.test_package@1",
2632 "/apex/com.android.apex.test_package_2",
2633 "/apex/com.android.apex.test_package_2@1"));
2634 }
2635
TEST_F(ApexdMountTest,OnStartDataHasHigherVersion)2636 TEST_F(ApexdMountTest, OnStartDataHasHigherVersion) {
2637 MockCheckpointInterface checkpoint_interface;
2638 // Need to call InitializeVold before calling OnStart
2639 InitializeVold(&checkpoint_interface);
2640
2641 AddPreInstalledApex("apex.apexd_test.apex");
2642 std::string apex_path_2 =
2643 AddPreInstalledApex("apex.apexd_test_different_app.apex");
2644 std::string apex_path_3 = AddDataApex("apex.apexd_test_v2.apex");
2645
2646 ASSERT_RESULT_OK(
2647 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}));
2648
2649 OnStart();
2650
2651 UnmountOnTearDown(apex_path_2);
2652 UnmountOnTearDown(apex_path_3);
2653
2654 ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting");
2655 auto apex_mounts = GetApexMounts();
2656 ASSERT_THAT(apex_mounts,
2657 UnorderedElementsAre("/apex/com.android.apex.test_package",
2658 "/apex/com.android.apex.test_package@2",
2659 "/apex/com.android.apex.test_package_2",
2660 "/apex/com.android.apex.test_package_2@1"));
2661 }
2662
TEST_F(ApexdMountTest,OnStartDataHasWrongSHA)2663 TEST_F(ApexdMountTest, OnStartDataHasWrongSHA) {
2664 MockCheckpointInterface checkpoint_interface;
2665 // Need to call InitializeVold before calling OnStart
2666 InitializeVold(&checkpoint_interface);
2667
2668 std::string apex_path = AddPreInstalledApex("com.android.apex.cts.shim.apex");
2669 AddDataApex("com.android.apex.cts.shim.v2_wrong_sha.apex");
2670
2671 ASSERT_RESULT_OK(
2672 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}));
2673
2674 UnmountOnTearDown(apex_path);
2675 OnStart();
2676
2677 // Check system shim apex is activated instead of the data one.
2678 auto apex_mounts = GetApexMounts();
2679 ASSERT_THAT(apex_mounts,
2680 UnorderedElementsAre("/apex/com.android.apex.cts.shim",
2681 "/apex/com.android.apex.cts.shim@1"));
2682 }
2683
TEST_F(ApexdMountTest,OnStartDataHasSameVersion)2684 TEST_F(ApexdMountTest, OnStartDataHasSameVersion) {
2685 MockCheckpointInterface checkpoint_interface;
2686 // Need to call InitializeVold before calling OnStart
2687 InitializeVold(&checkpoint_interface);
2688
2689 AddPreInstalledApex("apex.apexd_test.apex");
2690 std::string apex_path_2 =
2691 AddPreInstalledApex("apex.apexd_test_different_app.apex");
2692 std::string apex_path_3 = AddDataApex("apex.apexd_test.apex");
2693
2694 ASSERT_RESULT_OK(
2695 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}));
2696
2697 OnStart();
2698
2699 UnmountOnTearDown(apex_path_2);
2700 UnmountOnTearDown(apex_path_3);
2701
2702 ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting");
2703 auto apex_mounts = GetApexMounts();
2704 ASSERT_THAT(apex_mounts,
2705 UnorderedElementsAre("/apex/com.android.apex.test_package",
2706 "/apex/com.android.apex.test_package@1",
2707 "/apex/com.android.apex.test_package_2",
2708 "/apex/com.android.apex.test_package_2@1"));
2709
2710 auto& db = GetApexDatabaseForTesting();
2711 // Check that it was mounted from data apex, not pre-installed one.
2712 db.ForallMountedApexes("com.android.apex.test_package",
2713 [&](const MountedApexData& data, bool latest) {
2714 ASSERT_TRUE(latest);
2715 ASSERT_EQ(data.full_path, apex_path_3);
2716 });
2717 }
2718
TEST_F(ApexdMountTest,OnStartSystemHasHigherVersion)2719 TEST_F(ApexdMountTest, OnStartSystemHasHigherVersion) {
2720 MockCheckpointInterface checkpoint_interface;
2721 // Need to call InitializeVold before calling OnStart
2722 InitializeVold(&checkpoint_interface);
2723
2724 std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test_v2.apex");
2725 std::string apex_path_2 =
2726 AddPreInstalledApex("apex.apexd_test_different_app.apex");
2727 AddDataApex("apex.apexd_test.apex");
2728
2729 ASSERT_RESULT_OK(
2730 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}));
2731
2732 OnStart();
2733
2734 UnmountOnTearDown(apex_path_1);
2735 UnmountOnTearDown(apex_path_2);
2736
2737 ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting");
2738 auto apex_mounts = GetApexMounts();
2739 ASSERT_THAT(apex_mounts,
2740 UnorderedElementsAre("/apex/com.android.apex.test_package",
2741 "/apex/com.android.apex.test_package@2",
2742 "/apex/com.android.apex.test_package_2",
2743 "/apex/com.android.apex.test_package_2@1"));
2744
2745 auto& db = GetApexDatabaseForTesting();
2746 // Check that it was mounted from pre-installed one.
2747 db.ForallMountedApexes("com.android.apex.test_package",
2748 [&](const MountedApexData& data, bool latest) {
2749 ASSERT_TRUE(latest);
2750 ASSERT_EQ(data.full_path, apex_path_1);
2751 });
2752 }
2753
TEST_F(ApexdMountTest,OnStartFailsToActivateApexOnDataFallsBackToBuiltIn)2754 TEST_F(ApexdMountTest, OnStartFailsToActivateApexOnDataFallsBackToBuiltIn) {
2755 MockCheckpointInterface checkpoint_interface;
2756 // Need to call InitializeVold before calling OnStart
2757 InitializeVold(&checkpoint_interface);
2758
2759 std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex");
2760 std::string apex_path_2 =
2761 AddPreInstalledApex("apex.apexd_test_different_app.apex");
2762 AddDataApex("apex.apexd_test_manifest_mismatch.apex");
2763
2764 ASSERT_RESULT_OK(
2765 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}));
2766
2767 OnStart();
2768
2769 UnmountOnTearDown(apex_path_1);
2770 UnmountOnTearDown(apex_path_2);
2771
2772 ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting");
2773 auto apex_mounts = GetApexMounts();
2774 ASSERT_THAT(apex_mounts,
2775 UnorderedElementsAre("/apex/com.android.apex.test_package",
2776 "/apex/com.android.apex.test_package@1",
2777 "/apex/com.android.apex.test_package_2",
2778 "/apex/com.android.apex.test_package_2@1"));
2779
2780 auto& db = GetApexDatabaseForTesting();
2781 // Check that it was mounted from pre-installed apex.
2782 db.ForallMountedApexes("com.android.apex.test_package",
2783 [&](const MountedApexData& data, bool latest) {
2784 ASSERT_TRUE(latest);
2785 ASSERT_EQ(data.full_path, apex_path_1);
2786 });
2787 }
2788
TEST_F(ApexdMountTest,OnStartApexOnDataHasWrongKeyFallsBackToBuiltIn)2789 TEST_F(ApexdMountTest, OnStartApexOnDataHasWrongKeyFallsBackToBuiltIn) {
2790 MockCheckpointInterface checkpoint_interface;
2791 // Need to call InitializeVold before calling OnStart
2792 InitializeVold(&checkpoint_interface);
2793
2794 std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex");
2795 std::string apex_path_2 =
2796 AddPreInstalledApex("apex.apexd_test_different_app.apex");
2797 std::string apex_path_3 =
2798 AddDataApex("apex.apexd_test_different_key_v2.apex");
2799
2800 {
2801 auto apex = ApexFile::Open(apex_path_3);
2802 ASSERT_TRUE(IsOk(apex));
2803 ASSERT_EQ(static_cast<uint64_t>(apex->GetManifest().version()), 2ULL);
2804 }
2805
2806 ASSERT_RESULT_OK(
2807 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}));
2808
2809 OnStart();
2810
2811 UnmountOnTearDown(apex_path_1);
2812 UnmountOnTearDown(apex_path_2);
2813
2814 ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting");
2815 auto apex_mounts = GetApexMounts();
2816 ASSERT_THAT(apex_mounts,
2817 UnorderedElementsAre("/apex/com.android.apex.test_package",
2818 "/apex/com.android.apex.test_package@1",
2819 "/apex/com.android.apex.test_package_2",
2820 "/apex/com.android.apex.test_package_2@1"));
2821
2822 auto& db = GetApexDatabaseForTesting();
2823 // Check that it was mounted from pre-installed apex.
2824 db.ForallMountedApexes("com.android.apex.test_package",
2825 [&](const MountedApexData& data, bool latest) {
2826 ASSERT_TRUE(latest);
2827 ASSERT_EQ(data.full_path, apex_path_1);
2828 });
2829 }
2830
TEST_F(ApexdMountTest,OnStartOnlyPreInstalledCapexes)2831 TEST_F(ApexdMountTest, OnStartOnlyPreInstalledCapexes) {
2832 MockCheckpointInterface checkpoint_interface;
2833 // Need to call InitializeVold before calling OnStart
2834 InitializeVold(&checkpoint_interface);
2835
2836 std::string apex_path_1 =
2837 AddPreInstalledApex("com.android.apex.compressed.v1.capex");
2838
2839 ASSERT_RESULT_OK(
2840 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}));
2841
2842 OnStart();
2843
2844 // Decompressed APEX should be mounted
2845 std::string decompressed_active_apex = StringPrintf(
2846 "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(),
2847 kDecompressedApexPackageSuffix);
2848 UnmountOnTearDown(decompressed_active_apex);
2849
2850 ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting");
2851 auto apex_mounts = GetApexMounts();
2852 ASSERT_THAT(apex_mounts,
2853 UnorderedElementsAre("/apex/com.android.apex.compressed",
2854 "/apex/com.android.apex.compressed@1"));
2855 auto& db = GetApexDatabaseForTesting();
2856 // Check that it was mounted from decompressed apex.
2857 db.ForallMountedApexes("com.android.apex.compressed",
2858 [&](const MountedApexData& data, bool latest) {
2859 ASSERT_TRUE(latest);
2860 ASSERT_EQ(data.full_path, decompressed_active_apex);
2861 ASSERT_EQ(data.device_name,
2862 "com.android.apex.compressed@1");
2863 });
2864 }
2865
TEST_F(ApexdMountTest,OnStartDataHasHigherVersionThanCapex)2866 TEST_F(ApexdMountTest, OnStartDataHasHigherVersionThanCapex) {
2867 MockCheckpointInterface checkpoint_interface;
2868 // Need to call InitializeVold before calling OnStart
2869 InitializeVold(&checkpoint_interface);
2870
2871 AddPreInstalledApex("com.android.apex.compressed.v1.capex");
2872 std::string apex_path_2 =
2873 AddDataApex("com.android.apex.compressed.v2_original.apex");
2874
2875 ASSERT_RESULT_OK(
2876 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}));
2877
2878 OnStart();
2879
2880 UnmountOnTearDown(apex_path_2);
2881
2882 ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting");
2883 auto apex_mounts = GetApexMounts();
2884 ASSERT_THAT(apex_mounts,
2885 UnorderedElementsAre("/apex/com.android.apex.compressed",
2886 "/apex/com.android.apex.compressed@2"));
2887 auto& db = GetApexDatabaseForTesting();
2888 // Check that it was mounted from data apex.
2889 db.ForallMountedApexes("com.android.apex.compressed",
2890 [&](const MountedApexData& data, bool latest) {
2891 ASSERT_TRUE(latest);
2892 ASSERT_EQ(data.full_path, apex_path_2);
2893 ASSERT_EQ(data.device_name,
2894 "com.android.apex.compressed@2");
2895 });
2896 }
2897
TEST_F(ApexdMountTest,OnStartDataHasSameVersionAsCapex)2898 TEST_F(ApexdMountTest, OnStartDataHasSameVersionAsCapex) {
2899 MockCheckpointInterface checkpoint_interface;
2900 // Need to call InitializeVold before calling OnStart
2901 InitializeVold(&checkpoint_interface);
2902
2903 AddPreInstalledApex("com.android.apex.compressed.v1.capex");
2904 std::string apex_path_2 =
2905 AddDataApex("com.android.apex.compressed.v1_original.apex");
2906
2907 ASSERT_RESULT_OK(
2908 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}));
2909
2910 OnStart();
2911
2912 // Data APEX should be mounted
2913 UnmountOnTearDown(apex_path_2);
2914
2915 ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting");
2916 auto apex_mounts = GetApexMounts();
2917 ASSERT_THAT(apex_mounts,
2918 UnorderedElementsAre("/apex/com.android.apex.compressed",
2919 "/apex/com.android.apex.compressed@1"));
2920
2921 auto& db = GetApexDatabaseForTesting();
2922 // Check that it was mounted from data apex, not pre-installed one.
2923 db.ForallMountedApexes("com.android.apex.compressed",
2924 [&](const MountedApexData& data, bool latest) {
2925 ASSERT_TRUE(latest);
2926 ASSERT_EQ(data.full_path, apex_path_2);
2927 ASSERT_EQ(data.device_name,
2928 "com.android.apex.compressed@1");
2929 });
2930 }
2931
TEST_F(ApexdMountTest,OnStartSystemHasHigherVersionCapexThanData)2932 TEST_F(ApexdMountTest, OnStartSystemHasHigherVersionCapexThanData) {
2933 MockCheckpointInterface checkpoint_interface;
2934 // Need to call InitializeVold before calling OnStart
2935 InitializeVold(&checkpoint_interface);
2936
2937 std::string apex_path_1 =
2938 AddPreInstalledApex("com.android.apex.compressed.v2.capex");
2939 AddDataApex("com.android.apex.compressed.v1_original.apex");
2940
2941 ASSERT_RESULT_OK(
2942 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}));
2943
2944 OnStart();
2945
2946 // Decompressed APEX should be mounted
2947 std::string decompressed_active_apex = StringPrintf(
2948 "%s/com.android.apex.compressed@2%s", GetDecompressionDir().c_str(),
2949 kDecompressedApexPackageSuffix);
2950 UnmountOnTearDown(decompressed_active_apex);
2951
2952 ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting");
2953 auto apex_mounts = GetApexMounts();
2954 ASSERT_THAT(apex_mounts,
2955 UnorderedElementsAre("/apex/com.android.apex.compressed",
2956 "/apex/com.android.apex.compressed@2"));
2957
2958 auto& db = GetApexDatabaseForTesting();
2959 // Check that it was mounted from compressed apex
2960 db.ForallMountedApexes("com.android.apex.compressed",
2961 [&](const MountedApexData& data, bool latest) {
2962 ASSERT_TRUE(latest);
2963 ASSERT_EQ(data.full_path, decompressed_active_apex);
2964 ASSERT_EQ(data.device_name,
2965 "com.android.apex.compressed@2");
2966 });
2967 }
2968
TEST_F(ApexdMountTest,OnStartFailsToActivateApexOnDataFallsBackToCapex)2969 TEST_F(ApexdMountTest, OnStartFailsToActivateApexOnDataFallsBackToCapex) {
2970 MockCheckpointInterface checkpoint_interface;
2971 // Need to call InitializeVold before calling OnStart
2972 InitializeVold(&checkpoint_interface);
2973
2974 AddPreInstalledApex("com.android.apex.compressed.v1.capex");
2975 AddDataApex("com.android.apex.compressed.v2_manifest_mismatch.apex");
2976
2977 ASSERT_RESULT_OK(
2978 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}));
2979
2980 OnStart();
2981
2982 // Decompressed APEX should be mounted
2983 std::string decompressed_active_apex = StringPrintf(
2984 "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(),
2985 kDecompressedApexPackageSuffix);
2986 UnmountOnTearDown(decompressed_active_apex);
2987
2988 ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting");
2989 auto apex_mounts = GetApexMounts();
2990 ASSERT_THAT(apex_mounts,
2991 UnorderedElementsAre("/apex/com.android.apex.compressed",
2992 "/apex/com.android.apex.compressed@1"));
2993 auto& db = GetApexDatabaseForTesting();
2994 // Check that it was mounted from decompressed apex. It should also be mounted
2995 // on dm-verity device.
2996 db.ForallMountedApexes("com.android.apex.compressed",
2997 [&](const MountedApexData& data, bool latest) {
2998 ASSERT_TRUE(latest);
2999 ASSERT_EQ(data.full_path, decompressed_active_apex);
3000 ASSERT_EQ(data.device_name,
3001 "com.android.apex.compressed@1");
3002 });
3003 }
3004
3005 // Test scenario when we fallback to capex but it already has a decompressed
3006 // version on data
TEST_F(ApexdMountTest,OnStartFallbackToAlreadyDecompressedCapex)3007 TEST_F(ApexdMountTest, OnStartFallbackToAlreadyDecompressedCapex) {
3008 MockCheckpointInterface checkpoint_interface;
3009 // Need to call InitializeVold before calling OnStart
3010 InitializeVold(&checkpoint_interface);
3011
3012 PrepareCompressedApex("com.android.apex.compressed.v1.capex");
3013 AddDataApex("com.android.apex.compressed.v2_manifest_mismatch.apex");
3014
3015 ASSERT_RESULT_OK(
3016 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}));
3017
3018 OnStart();
3019
3020 // Decompressed APEX should be mounted
3021 std::string decompressed_active_apex = StringPrintf(
3022 "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(),
3023 kDecompressedApexPackageSuffix);
3024 UnmountOnTearDown(decompressed_active_apex);
3025
3026 ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting");
3027 auto apex_mounts = GetApexMounts();
3028 ASSERT_THAT(apex_mounts,
3029 UnorderedElementsAre("/apex/com.android.apex.compressed",
3030 "/apex/com.android.apex.compressed@1"));
3031 auto& db = GetApexDatabaseForTesting();
3032 // Check that it was mounted from decompressed apex.
3033 db.ForallMountedApexes("com.android.apex.compressed",
3034 [&](const MountedApexData& data, bool latest) {
3035 ASSERT_TRUE(latest);
3036 ASSERT_EQ(data.full_path, decompressed_active_apex);
3037 ASSERT_EQ(data.device_name,
3038 "com.android.apex.compressed@1");
3039 });
3040 }
3041
3042 // Test scenario when we fallback to capex but it has same version as corrupt
3043 // data apex
TEST_F(ApexdMountTest,OnStartFallbackToCapexSameVersion)3044 TEST_F(ApexdMountTest, OnStartFallbackToCapexSameVersion) {
3045 MockCheckpointInterface checkpoint_interface;
3046 // Need to call InitializeVold before calling OnStart
3047 InitializeVold(&checkpoint_interface);
3048
3049 AddPreInstalledApex("com.android.apex.compressed.v2.capex");
3050 // Add data apex using the common naming convention for /data/apex/active
3051 // directory
3052 fs::copy(GetTestFile("com.android.apex.compressed.v2_manifest_mismatch.apex"),
3053 GetDataDir() + "/com.android.apex.compressed@2.apex");
3054
3055 ASSERT_RESULT_OK(
3056 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}));
3057
3058 OnStart();
3059
3060 // Decompressed APEX should be mounted
3061 std::string decompressed_active_apex = StringPrintf(
3062 "%s/com.android.apex.compressed@2%s", GetDecompressionDir().c_str(),
3063 kDecompressedApexPackageSuffix);
3064 UnmountOnTearDown(decompressed_active_apex);
3065
3066 ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting");
3067 auto apex_mounts = GetApexMounts();
3068 ASSERT_THAT(apex_mounts,
3069 UnorderedElementsAre("/apex/com.android.apex.compressed",
3070 "/apex/com.android.apex.compressed@2"));
3071 auto& db = GetApexDatabaseForTesting();
3072 // Check that it was mounted from decompressed apex.
3073 db.ForallMountedApexes("com.android.apex.compressed",
3074 [&](const MountedApexData& data, bool latest) {
3075 ASSERT_TRUE(latest);
3076 ASSERT_EQ(data.full_path, decompressed_active_apex);
3077 ASSERT_EQ(data.device_name,
3078 "com.android.apex.compressed@2");
3079 });
3080 }
3081
TEST_F(ApexdMountTest,OnStartCapexToApex)3082 TEST_F(ApexdMountTest, OnStartCapexToApex) {
3083 MockCheckpointInterface checkpoint_interface;
3084 // Need to call InitializeVold before calling OnStart
3085 InitializeVold(&checkpoint_interface);
3086
3087 TemporaryDir previous_built_in_dir;
3088 PrepareCompressedApex("com.android.apex.compressed.v1.capex",
3089 previous_built_in_dir.path);
3090 auto apex_path =
3091 AddPreInstalledApex("com.android.apex.compressed.v1_original.apex");
3092
3093 ASSERT_RESULT_OK(
3094 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}));
3095
3096 OnStart();
3097
3098 // Uncompressed APEX should be mounted
3099 UnmountOnTearDown(apex_path);
3100
3101 ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting");
3102 auto apex_mounts = GetApexMounts();
3103 ASSERT_THAT(apex_mounts,
3104 UnorderedElementsAre("/apex/com.android.apex.compressed",
3105 "/apex/com.android.apex.compressed@1"));
3106 auto& db = GetApexDatabaseForTesting();
3107 // Check that it was mounted from decompressed apex.
3108 db.ForallMountedApexes("com.android.apex.compressed",
3109 [&](const MountedApexData& data, bool latest) {
3110 ASSERT_TRUE(latest);
3111 ASSERT_EQ(data.full_path, apex_path);
3112 ASSERT_THAT(data.device_name, IsEmpty());
3113 });
3114 }
3115
3116 // Test to ensure we do not mount decompressed APEX from /data/apex/active
TEST_F(ApexdMountTest,OnStartOrphanedDecompressedApexInActiveDirectory)3117 TEST_F(ApexdMountTest, OnStartOrphanedDecompressedApexInActiveDirectory) {
3118 MockCheckpointInterface checkpoint_interface;
3119 // Need to call InitializeVold before calling OnStart
3120 InitializeVold(&checkpoint_interface);
3121
3122 // Place a decompressed APEX in /data/apex/active. This apex should not
3123 // be mounted since it's not in correct location. Instead, the
3124 // pre-installed APEX should be mounted.
3125 auto decompressed_apex_in_active_dir =
3126 StringPrintf("%s/com.android.apex.compressed@1%s", GetDataDir().c_str(),
3127 kDecompressedApexPackageSuffix);
3128 fs::copy(GetTestFile("com.android.apex.compressed.v1_original.apex"),
3129 decompressed_apex_in_active_dir);
3130 auto apex_path =
3131 AddPreInstalledApex("com.android.apex.compressed.v1_original.apex");
3132
3133 ASSERT_RESULT_OK(
3134 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}));
3135
3136 OnStart();
3137
3138 // Pre-installed APEX should be mounted
3139 UnmountOnTearDown(apex_path);
3140 auto& db = GetApexDatabaseForTesting();
3141 // Check that pre-installed APEX has been activated
3142 db.ForallMountedApexes("com.android.apex.compressed",
3143 [&](const MountedApexData& data, bool latest) {
3144 ASSERT_TRUE(latest);
3145 ASSERT_EQ(data.full_path, apex_path);
3146 ASSERT_THAT(data.device_name, IsEmpty());
3147 });
3148 }
3149
3150 // Test scenario when decompressed version has different version than
3151 // pre-installed CAPEX
TEST_F(ApexdMountTest,OnStartDecompressedApexVersionDifferentThanCapex)3152 TEST_F(ApexdMountTest, OnStartDecompressedApexVersionDifferentThanCapex) {
3153 MockCheckpointInterface checkpoint_interface;
3154 // Need to call InitializeVold before calling OnStart
3155 InitializeVold(&checkpoint_interface);
3156
3157 TemporaryDir previous_built_in_dir;
3158 PrepareCompressedApex("com.android.apex.compressed.v2.capex",
3159 previous_built_in_dir.path);
3160 auto apex_path = AddPreInstalledApex("com.android.apex.compressed.v1.capex");
3161
3162 ASSERT_RESULT_OK(
3163 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}));
3164
3165 OnStart();
3166
3167 // Existing higher version decompressed APEX should be ignored and new
3168 // pre-installed CAPEX should be decompressed and mounted
3169 std::string decompressed_active_apex = StringPrintf(
3170 "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(),
3171 kDecompressedApexPackageSuffix);
3172 UnmountOnTearDown(decompressed_active_apex);
3173
3174 ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting");
3175 auto apex_mounts = GetApexMounts();
3176 ASSERT_THAT(apex_mounts,
3177 UnorderedElementsAre("/apex/com.android.apex.compressed",
3178 "/apex/com.android.apex.compressed@1"));
3179 auto& db = GetApexDatabaseForTesting();
3180 // Check that it was mounted from newly decompressed apex.
3181 db.ForallMountedApexes("com.android.apex.compressed",
3182 [&](const MountedApexData& data, bool latest) {
3183 ASSERT_TRUE(latest);
3184 ASSERT_EQ(data.full_path, decompressed_active_apex);
3185 ASSERT_EQ(data.device_name,
3186 "com.android.apex.compressed@1");
3187 });
3188 }
3189
3190 // Test that ota_apex is persisted until slot switch
TEST_F(ApexdMountTest,OnStartOtaApexKeptUntilSlotSwitch)3191 TEST_F(ApexdMountTest, OnStartOtaApexKeptUntilSlotSwitch) {
3192 MockCheckpointInterface checkpoint_interface;
3193 // Need to call InitializeVold before calling OnStart
3194 InitializeVold(&checkpoint_interface);
3195
3196 // Imagine current system has v1 capex and we have v2 incoming via ota
3197 auto old_capex = AddPreInstalledApex("com.android.apex.compressed.v1.capex");
3198 auto ota_apex_path =
3199 StringPrintf("%s/com.android.apex.compressed@2%s",
3200 GetDecompressionDir().c_str(), kOtaApexPackageSuffix);
3201 fs::copy(GetTestFile("com.android.apex.compressed.v2_original.apex"),
3202 ota_apex_path.c_str());
3203
3204 ASSERT_RESULT_OK(
3205 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}));
3206
3207 // When we call OnStart for the first time, it will decompress v1 capex and
3208 // activate it, while after second call it will decompress v2 capex and
3209 // activate it. We need to make sure that activated APEXes are cleaned up
3210 // after test finishes.
3211 auto old_decompressed_apex = StringPrintf(
3212 "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(),
3213 kDecompressedApexPackageSuffix);
3214 auto new_decompressed_apex = StringPrintf(
3215 "%s/com.android.apex.compressed@2%s", GetDecompressionDir().c_str(),
3216 kDecompressedApexPackageSuffix);
3217 UnmountOnTearDown(old_decompressed_apex);
3218 UnmountOnTearDown(new_decompressed_apex);
3219
3220 // First try starting without slot switch. Since we are booting with
3221 // old pre-installed capex, ota_apex should not be deleted
3222 OnStart();
3223 auto path_exists = PathExists(ota_apex_path);
3224 ASSERT_TRUE(*path_exists);
3225
3226 // When we switch slot, the pre-installed APEX will match ota_apex
3227 // and the ota_apex will end up getting renamed.
3228 RemoveFileIfExists(old_capex);
3229 AddPreInstalledApex("com.android.apex.compressed.v2.capex");
3230 ApexFileRepository::GetInstance().Reset(GetDecompressionDir());
3231 ASSERT_RESULT_OK(
3232 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}));
3233 OnStart();
3234 path_exists = PathExists(ota_apex_path);
3235 ASSERT_FALSE(*path_exists);
3236 }
3237
3238 // Test scenario when decompressed version has same version but different
3239 // digest
TEST_F(ApexdMountTest,OnStartDecompressedApexVersionSameAsCapexDifferentDigest)3240 TEST_F(ApexdMountTest,
3241 OnStartDecompressedApexVersionSameAsCapexDifferentDigest) {
3242 MockCheckpointInterface checkpoint_interface;
3243 // Need to call InitializeVold before calling OnStart
3244 InitializeVold(&checkpoint_interface);
3245
3246 // Push a CAPEX to system without decompressing it
3247 auto apex_path = AddPreInstalledApex("com.android.apex.compressed.v1.capex");
3248 auto pre_installed_apex = ApexFile::Open(apex_path);
3249 // Now push an APEX with different root digest as decompressed APEX
3250 auto decompressed_apex_path = StringPrintf(
3251 "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(),
3252 kDecompressedApexPackageSuffix);
3253 fs::copy(GetTestFile(
3254 "com.android.apex.compressed.v1_different_digest_original.apex"),
3255 decompressed_apex_path);
3256 auto different_digest_apex = ApexFile::Open(decompressed_apex_path);
3257 auto different_digest = GetRootDigest(*different_digest_apex);
3258 ASSERT_NE(
3259 pre_installed_apex->GetManifest().capexmetadata().originalapexdigest(),
3260 different_digest);
3261
3262 ASSERT_RESULT_OK(
3263 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}));
3264
3265 OnStart();
3266
3267 // Existing same version decompressed APEX with different root digest should
3268 // be ignored and the pre-installed CAPEX should be decompressed again.
3269 UnmountOnTearDown(decompressed_apex_path);
3270
3271 // Ensure decompressed apex has same digest as pre-installed
3272 auto decompressed_apex = ApexFile::Open(decompressed_apex_path);
3273 ASSERT_EQ(
3274 pre_installed_apex->GetManifest().capexmetadata().originalapexdigest(),
3275 GetRootDigest(*decompressed_apex));
3276 ASSERT_NE(GetRootDigest(*decompressed_apex), different_digest);
3277 }
3278
3279 // Test when decompressed APEX has different key than CAPEX
TEST_F(ApexdMountTest,OnStartDecompressedApexVersionSameAsCapexDifferentKey)3280 TEST_F(ApexdMountTest, OnStartDecompressedApexVersionSameAsCapexDifferentKey) {
3281 MockCheckpointInterface checkpoint_interface;
3282 // Need to call InitializeVold before calling OnStart
3283 InitializeVold(&checkpoint_interface);
3284
3285 TemporaryDir previous_built_in_dir;
3286 auto different_key_apex_path =
3287 PrepareCompressedApex("com.android.apex.compressed_different_key.capex",
3288 previous_built_in_dir.path);
3289 // Place a same version capex in current built_in_dir, which has different key
3290 auto apex_path = AddPreInstalledApex("com.android.apex.compressed.v1.capex");
3291
3292 ASSERT_RESULT_OK(
3293 ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}));
3294
3295 OnStart();
3296
3297 // Existing same version decompressed APEX should be ignored and new
3298 // pre-installed CAPEX should be decompressed and mounted
3299 std::string decompressed_active_apex = StringPrintf(
3300 "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(),
3301 kDecompressedApexPackageSuffix);
3302 UnmountOnTearDown(decompressed_active_apex);
3303
3304 // Ensure decompressed apex has same digest as pre-installed
3305 auto pre_installed_apex = ApexFile::Open(apex_path);
3306 auto decompressed_apex = ApexFile::Open(decompressed_active_apex);
3307 auto different_key_apex = ApexFile::Open(different_key_apex_path);
3308 ASSERT_EQ(
3309 pre_installed_apex->GetManifest().capexmetadata().originalapexdigest(),
3310 GetRootDigest(*decompressed_apex));
3311 ASSERT_NE(
3312 pre_installed_apex->GetManifest().capexmetadata().originalapexdigest(),
3313 GetRootDigest(*different_key_apex));
3314 }
3315
TEST_F(ApexdMountTest,PopulateFromMountsChecksPathPrefix)3316 TEST_F(ApexdMountTest, PopulateFromMountsChecksPathPrefix) {
3317 AddPreInstalledApex("apex.apexd_test.apex");
3318 std::string apex_path = AddDataApex("apex.apexd_test_v2.apex");
3319
3320 // Mount an apex from decomrpession_dir
3321 PrepareCompressedApex("com.android.apex.compressed.v1.capex");
3322 std::string decompressed_apex =
3323 StringPrintf("%s/com.android.apex.compressed@1.decompressed.apex",
3324 GetDecompressionDir().c_str());
3325
3326 // Mount an apex from some other directory
3327 TemporaryDir td;
3328 AddPreInstalledApex("apex.apexd_test_different_app.apex");
3329 fs::copy(GetTestFile("apex.apexd_test_different_app.apex"), td.path);
3330 std::string other_apex =
3331 StringPrintf("%s/apex.apexd_test_different_app.apex", td.path);
3332
3333 auto& instance = ApexFileRepository::GetInstance();
3334 ASSERT_RESULT_OK(instance.AddPreInstalledApex({GetBuiltInDir()}));
3335
3336 ASSERT_TRUE(IsOk(ActivatePackage(apex_path)));
3337 ASSERT_TRUE(IsOk(ActivatePackage(decompressed_apex)));
3338 ASSERT_TRUE(IsOk(ActivatePackage(other_apex)));
3339
3340 auto& db = GetApexDatabaseForTesting();
3341 // Remember mount information for |other_apex|, since it won't be available in
3342 // the database. We will need to tear it down manually.
3343 std::optional<MountedApexData> other_apex_mount_data;
3344 db.ForallMountedApexes(
3345 "com.android.apex.test_package_2",
3346 [&other_apex_mount_data](const MountedApexData& data, bool latest) {
3347 if (latest) {
3348 other_apex_mount_data.emplace(data);
3349 }
3350 });
3351 UnmountOnTearDown(apex_path);
3352 UnmountOnTearDown(decompressed_apex);
3353 ASSERT_TRUE(other_apex_mount_data.has_value());
3354 auto deleter = make_scope_guard([&other_apex_mount_data]() {
3355 if (!other_apex_mount_data.has_value()) {
3356 return;
3357 }
3358 if (umount2("/apex/com.android.apex.test_package_2", 0) != 0) {
3359 PLOG(ERROR) << "Failed to unmount /apex/com.android.apex.test_package_2";
3360 }
3361 auto res = Unmount(*other_apex_mount_data, /* deferred= */ false);
3362 if (!res.ok()) {
3363 LOG(ERROR) << res.error();
3364 }
3365 });
3366
3367 auto apex_mounts = GetApexMounts();
3368 ASSERT_THAT(apex_mounts,
3369 UnorderedElementsAre("/apex/com.android.apex.test_package",
3370 "/apex/com.android.apex.test_package@2",
3371 "/apex/com.android.apex.compressed",
3372 "/apex/com.android.apex.compressed@1",
3373 "/apex/com.android.apex.test_package_2",
3374 "/apex/com.android.apex.test_package_2@1"));
3375
3376 // Clear the database before calling PopulateFromMounts
3377 db.Reset();
3378
3379 // Populate from mount
3380 db.PopulateFromMounts(GetDataDir(), GetDecompressionDir(), GetHashTreeDir());
3381
3382 // Count number of package and collect package names
3383 int package_count = 0;
3384 std::vector<std::string> mounted_paths;
3385 db.ForallMountedApexes([&](const std::string& package,
3386 const MountedApexData& data, bool latest) {
3387 package_count++;
3388 mounted_paths.push_back(data.full_path);
3389 });
3390 ASSERT_EQ(package_count, 2);
3391 ASSERT_THAT(mounted_paths,
3392 UnorderedElementsAre(apex_path, decompressed_apex));
3393 }
3394
TEST_F(ApexdMountTest,UnmountAll)3395 TEST_F(ApexdMountTest, UnmountAll) {
3396 AddPreInstalledApex("apex.apexd_test.apex");
3397 std::string apex_path_2 =
3398 AddPreInstalledApex("apex.apexd_test_different_app.apex");
3399 std::string apex_path_3 = AddDataApex("apex.apexd_test_v2.apex");
3400
3401 // Mount an apex from decomrpession_dir
3402 PrepareCompressedApex("com.android.apex.compressed.v1.capex");
3403 std::string decompressed_apex =
3404 StringPrintf("%s/com.android.apex.compressed@1.decompressed.apex",
3405 GetDecompressionDir().c_str());
3406
3407 auto& instance = ApexFileRepository::GetInstance();
3408 ASSERT_RESULT_OK(instance.AddPreInstalledApex({GetBuiltInDir()}));
3409
3410 ASSERT_TRUE(IsOk(ActivatePackage(apex_path_2)));
3411 ASSERT_TRUE(IsOk(ActivatePackage(apex_path_3)));
3412 ASSERT_TRUE(IsOk(ActivatePackage(decompressed_apex)));
3413 UnmountOnTearDown(apex_path_2);
3414 UnmountOnTearDown(apex_path_3);
3415 UnmountOnTearDown(decompressed_apex);
3416
3417 auto apex_mounts = GetApexMounts();
3418 ASSERT_THAT(apex_mounts,
3419 UnorderedElementsAre("/apex/com.android.apex.test_package",
3420 "/apex/com.android.apex.test_package@2",
3421 "/apex/com.android.apex.compressed",
3422 "/apex/com.android.apex.compressed@1",
3423 "/apex/com.android.apex.test_package_2",
3424 "/apex/com.android.apex.test_package_2@1"));
3425
3426 auto& db = GetApexDatabaseForTesting();
3427 // UnmountAll expects apex database to empty, hence this reset.
3428 db.Reset();
3429
3430 ASSERT_EQ(0, UnmountAll());
3431
3432 auto new_apex_mounts = GetApexMounts();
3433 ASSERT_EQ(new_apex_mounts.size(), 0u);
3434 }
3435
TEST_F(ApexdMountTest,UnmountAllSharedLibsApex)3436 TEST_F(ApexdMountTest, UnmountAllSharedLibsApex) {
3437 ASSERT_EQ(mkdir("/apex/sharedlibs", 0755), 0);
3438 ASSERT_EQ(mkdir("/apex/sharedlibs/lib", 0755), 0);
3439 ASSERT_EQ(mkdir("/apex/sharedlibs/lib64", 0755), 0);
3440 auto deleter = make_scope_guard([]() {
3441 std::error_code ec;
3442 fs::remove_all("/apex/sharedlibs", ec);
3443 if (ec) {
3444 LOG(ERROR) << "Failed to delete /apex/sharedlibs : " << ec;
3445 }
3446 });
3447
3448 std::string apex_path_1 = AddPreInstalledApex(
3449 "com.android.apex.test.sharedlibs_generated.v1.libvX.apex");
3450 std::string apex_path_2 =
3451 AddDataApex("com.android.apex.test.sharedlibs_generated.v2.libvY.apex");
3452
3453 auto& instance = ApexFileRepository::GetInstance();
3454 ASSERT_RESULT_OK(instance.AddPreInstalledApex({GetBuiltInDir()}));
3455
3456 ASSERT_TRUE(IsOk(ActivatePackage(apex_path_1)));
3457 ASSERT_TRUE(IsOk(ActivatePackage(apex_path_2)));
3458 UnmountOnTearDown(apex_path_1);
3459 UnmountOnTearDown(apex_path_2);
3460
3461 auto apex_mounts = GetApexMounts();
3462 ASSERT_THAT(apex_mounts,
3463 UnorderedElementsAre("/apex/com.android.apex.test.sharedlibs@1",
3464 "/apex/com.android.apex.test.sharedlibs@2"));
3465
3466 auto& db = GetApexDatabaseForTesting();
3467 // UnmountAll expects apex database to empty, hence this reset.
3468 db.Reset();
3469
3470 ASSERT_EQ(0, UnmountAll());
3471
3472 auto new_apex_mounts = GetApexMounts();
3473 ASSERT_EQ(new_apex_mounts.size(), 0u);
3474 }
3475
3476 class ApexActivationFailureTests : public ApexdMountTest {};
3477
TEST_F(ApexActivationFailureTests,BuildFingerprintDifferent)3478 TEST_F(ApexActivationFailureTests, BuildFingerprintDifferent) {
3479 auto apex_session = CreateStagedSession("apex.apexd_test.apex", 123);
3480 apex_session->SetBuildFingerprint("wrong fingerprint");
3481 apex_session->UpdateStateAndCommit(SessionState::STAGED);
3482
3483 OnStart();
3484
3485 apex_session = ApexSession::GetSession(123);
3486 ASSERT_THAT(apex_session->GetErrorMessage(),
3487 HasSubstr("APEX build fingerprint has changed"));
3488 }
3489
TEST_F(ApexActivationFailureTests,ApexFileMissingInStagingDirectory)3490 TEST_F(ApexActivationFailureTests, ApexFileMissingInStagingDirectory) {
3491 auto apex_session = CreateStagedSession("apex.apexd_test.apex", 123);
3492 apex_session->UpdateStateAndCommit(SessionState::STAGED);
3493 // Delete the apex file in staging directory
3494 DeleteDirContent(GetStagedDir(123));
3495
3496 OnStart();
3497
3498 apex_session = ApexSession::GetSession(123);
3499 ASSERT_THAT(apex_session->GetErrorMessage(),
3500 HasSubstr("No APEX packages found"));
3501 }
3502
TEST_F(ApexActivationFailureTests,MultipleApexFileInStagingDirectory)3503 TEST_F(ApexActivationFailureTests, MultipleApexFileInStagingDirectory) {
3504 auto apex_session = CreateStagedSession("apex.apexd_test.apex", 123);
3505 CreateStagedSession("com.android.apex.compressed.v1_original.apex", 123);
3506 apex_session->UpdateStateAndCommit(SessionState::STAGED);
3507
3508 OnStart();
3509
3510 apex_session = ApexSession::GetSession(123);
3511 ASSERT_THAT(apex_session->GetErrorMessage(),
3512 HasSubstr("More than one APEX package found"));
3513 }
3514
TEST_F(ApexActivationFailureTests,PostInstallFailsForApex)3515 TEST_F(ApexActivationFailureTests, PostInstallFailsForApex) {
3516 auto apex_session =
3517 CreateStagedSession("apex.apexd_test_corrupt_superblock_apex.apex", 123);
3518 apex_session->UpdateStateAndCommit(SessionState::STAGED);
3519
3520 OnStart();
3521
3522 apex_session = ApexSession::GetSession(123);
3523 ASSERT_THAT(apex_session->GetErrorMessage(),
3524 HasSubstr("Postinstall failed for session"));
3525 }
3526
TEST_F(ApexActivationFailureTests,CorruptedApexCannotBeStaged)3527 TEST_F(ApexActivationFailureTests, CorruptedApexCannotBeStaged) {
3528 auto apex_session = CreateStagedSession("corrupted_b146895998.apex", 123);
3529 apex_session->UpdateStateAndCommit(SessionState::STAGED);
3530
3531 OnStart();
3532
3533 apex_session = ApexSession::GetSession(123);
3534 ASSERT_THAT(apex_session->GetErrorMessage(),
3535 HasSubstr("Activation failed for packages"));
3536 }
3537
TEST_F(ApexActivationFailureTests,ActivatePackageImplFails)3538 TEST_F(ApexActivationFailureTests, ActivatePackageImplFails) {
3539 auto shim_path = AddPreInstalledApex("com.android.apex.cts.shim.apex");
3540 auto& instance = ApexFileRepository::GetInstance();
3541 ASSERT_RESULT_OK(instance.AddPreInstalledApex({GetBuiltInDir()}));
3542
3543 auto apex_session =
3544 CreateStagedSession("com.android.apex.cts.shim.v2_wrong_sha.apex", 123);
3545 apex_session->UpdateStateAndCommit(SessionState::STAGED);
3546
3547 UnmountOnTearDown(shim_path);
3548 OnStart();
3549
3550 apex_session = ApexSession::GetSession(123);
3551 ASSERT_THAT(apex_session->GetErrorMessage(),
3552 HasSubstr("Failed to activate packages"));
3553 ASSERT_THAT(apex_session->GetErrorMessage(),
3554 HasSubstr("has unexpected SHA512 hash"));
3555 }
3556
TEST_F(ApexActivationFailureTests,StagedSessionFailsWhenNotInFsCheckpointMode)3557 TEST_F(ApexActivationFailureTests,
3558 StagedSessionFailsWhenNotInFsCheckpointMode) {
3559 MockCheckpointInterface checkpoint_interface;
3560 checkpoint_interface.SetSupportsCheckpoint(true);
3561 // Need to call InitializeVold before calling OnStart
3562 InitializeVold(&checkpoint_interface);
3563
3564 auto pre_installed_apex = AddPreInstalledApex("apex.apexd_test.apex");
3565 auto& instance = ApexFileRepository::GetInstance();
3566 ASSERT_RESULT_OK(instance.AddPreInstalledApex({GetBuiltInDir()}));
3567
3568 auto apex_session = CreateStagedSession("apex.apexd_test.apex", 123);
3569 apex_session->UpdateStateAndCommit(SessionState::STAGED);
3570
3571 UnmountOnTearDown(pre_installed_apex);
3572 OnStart();
3573
3574 apex_session = ApexSession::GetSession(123);
3575 ASSERT_EQ(apex_session->GetState(), SessionState::ACTIVATION_FAILED);
3576 ASSERT_THAT(
3577 apex_session->GetErrorMessage(),
3578 HasSubstr("Cannot install apex session if not in fs-checkpoint mode"));
3579 }
3580
TEST_F(ApexActivationFailureTests,StagedSessionRevertsWhenInFsRollbackMode)3581 TEST_F(ApexActivationFailureTests, StagedSessionRevertsWhenInFsRollbackMode) {
3582 MockCheckpointInterface checkpoint_interface;
3583 checkpoint_interface.SetSupportsCheckpoint(true);
3584 checkpoint_interface.SetNeedsRollback(true);
3585 // Need to call InitializeVold before calling OnStart
3586 InitializeVold(&checkpoint_interface);
3587
3588 auto pre_installed_apex = AddPreInstalledApex("apex.apexd_test.apex");
3589 auto& instance = ApexFileRepository::GetInstance();
3590 ASSERT_RESULT_OK(instance.AddPreInstalledApex({GetBuiltInDir()}));
3591
3592 auto apex_session = CreateStagedSession("apex.apexd_test.apex", 123);
3593 apex_session->UpdateStateAndCommit(SessionState::STAGED);
3594
3595 UnmountOnTearDown(pre_installed_apex);
3596 OnStart();
3597
3598 apex_session = ApexSession::GetSession(123);
3599 ASSERT_EQ(apex_session->GetState(), SessionState::REVERTED);
3600 }
3601
3602 } // namespace apex
3603 } // namespace android
3604