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