1 /*
2 * Copyright (C) 2018 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 <algorithm>
18 #include <filesystem>
19 #include <fstream>
20 #include <functional>
21 #include <memory>
22 #include <optional>
23 #include <string>
24 #include <unordered_set>
25 #include <vector>
26
27 #include <grp.h>
28 #include <linux/loop.h>
29 #include <stdio.h>
30 #include <sys/ioctl.h>
31 #include <sys/stat.h>
32 #include <sys/types.h>
33
34 #include <android-base/file.h>
35 #include <android-base/logging.h>
36 #include <android-base/macros.h>
37 #include <android-base/properties.h>
38 #include <android-base/scopeguard.h>
39 #include <android-base/stringprintf.h>
40 #include <android-base/strings.h>
41 #include <android/os/IVold.h>
42 #include <binder/IServiceManager.h>
43 #include <fs_mgr_overlayfs.h>
44 #include <fstab/fstab.h>
45 #include <gmock/gmock.h>
46 #include <gtest/gtest.h>
47 #include <libdm/dm.h>
48 #include <selinux/selinux.h>
49
50 #include <android/apex/ApexInfo.h>
51 #include <android/apex/IApexService.h>
52
53 #include "apex_constants.h"
54 #include "apex_database.h"
55 #include "apex_file.h"
56 #include "apex_manifest.h"
57 #include "apexd.h"
58 #include "apexd_private.h"
59 #include "apexd_session.h"
60 #include "apexd_test_utils.h"
61 #include "apexd_utils.h"
62 #include "session_state.pb.h"
63 #include "string_log.h"
64
65 using apex::proto::SessionState;
66
67 namespace android {
68 namespace apex {
69
70 using android::sp;
71 using android::String16;
72 using android::apex::testing::ApexInfoEq;
73 using android::apex::testing::CreateSessionInfo;
74 using android::apex::testing::IsOk;
75 using android::apex::testing::SessionInfoEq;
76 using android::base::EndsWith;
77 using android::base::ErrnoError;
78 using android::base::Join;
79 using android::base::ReadFully;
80 using android::base::StartsWith;
81 using android::base::StringPrintf;
82 using android::base::unique_fd;
83 using android::dm::DeviceMapper;
84 using android::fs_mgr::Fstab;
85 using android::fs_mgr::GetEntryForMountPoint;
86 using android::fs_mgr::ReadFstabFromFile;
87 using ::apex::proto::ApexManifest;
88 using ::testing::Contains;
89 using ::testing::EndsWith;
90 using ::testing::HasSubstr;
91 using ::testing::Not;
92 using ::testing::SizeIs;
93 using ::testing::UnorderedElementsAre;
94 using ::testing::UnorderedElementsAreArray;
95
96 using MountedApexData = MountedApexDatabase::MountedApexData;
97
98 namespace fs = std::filesystem;
99
100 class ApexServiceTest : public ::testing::Test {
101 public:
ApexServiceTest()102 ApexServiceTest() {
103 using android::IBinder;
104 using android::IServiceManager;
105
106 sp<IServiceManager> sm = android::defaultServiceManager();
107 sp<IBinder> binder = sm->waitForService(String16("apexservice"));
108 if (binder != nullptr) {
109 service_ = android::interface_cast<IApexService>(binder);
110 }
111 binder = sm->getService(String16("vold"));
112 if (binder != nullptr) {
113 vold_service_ = android::interface_cast<android::os::IVold>(binder);
114 }
115 }
116
117 protected:
SetUp()118 void SetUp() override {
119 // TODO(b/136647373): Move this check to environment setup
120 if (!android::base::GetBoolProperty("ro.apex.updatable", false)) {
121 GTEST_SKIP() << "Skipping test because device doesn't support APEX";
122 }
123 ASSERT_NE(nullptr, service_.get());
124 ASSERT_NE(nullptr, vold_service_.get());
125 android::binder::Status status =
126 vold_service_->supportsCheckpoint(&supports_fs_checkpointing_);
127 ASSERT_TRUE(IsOk(status));
128 CleanUp();
129 service_->recollectPreinstalledData(kApexPackageBuiltinDirs);
130 }
131
TearDown()132 void TearDown() override { CleanUp(); }
133
GetTestDataDir()134 static std::string GetTestDataDir() {
135 return android::base::GetExecutableDirectory();
136 }
GetTestFile(const std::string & name)137 static std::string GetTestFile(const std::string& name) {
138 return GetTestDataDir() + "/" + name;
139 }
140
HaveSelinux()141 static bool HaveSelinux() { return 1 == is_selinux_enabled(); }
142
IsSelinuxEnforced()143 static bool IsSelinuxEnforced() { return 0 != security_getenforce(); }
144
IsActive(const std::string & name)145 Result<bool> IsActive(const std::string& name) {
146 std::vector<ApexInfo> list;
147 android::binder::Status status = service_->getActivePackages(&list);
148 if (!status.isOk()) {
149 return Error() << "Failed to check if " << name
150 << " is active : " << status.exceptionMessage().c_str();
151 }
152 for (const ApexInfo& apex : list) {
153 if (apex.moduleName == name) {
154 return true;
155 }
156 }
157 return false;
158 }
159
IsActive(const std::string & name,int64_t version,const std::string & path)160 Result<bool> IsActive(const std::string& name, int64_t version,
161 const std::string& path) {
162 std::vector<ApexInfo> list;
163 android::binder::Status status = service_->getActivePackages(&list);
164 if (status.isOk()) {
165 for (const ApexInfo& p : list) {
166 if (p.moduleName == name && p.versionCode == version &&
167 p.modulePath == path) {
168 return true;
169 }
170 }
171 return false;
172 }
173 return Error() << status.exceptionMessage().c_str();
174 }
175
GetAllPackages()176 Result<std::vector<ApexInfo>> GetAllPackages() {
177 std::vector<ApexInfo> list;
178 android::binder::Status status = service_->getAllPackages(&list);
179 if (status.isOk()) {
180 return list;
181 }
182
183 return Error() << status.toString8().c_str();
184 }
185
GetActivePackages()186 Result<std::vector<ApexInfo>> GetActivePackages() {
187 std::vector<ApexInfo> list;
188 android::binder::Status status = service_->getActivePackages(&list);
189 if (status.isOk()) {
190 return list;
191 }
192
193 return Error() << status.exceptionMessage().c_str();
194 }
195
GetInactivePackages()196 Result<std::vector<ApexInfo>> GetInactivePackages() {
197 std::vector<ApexInfo> list;
198 android::binder::Status status = service_->getAllPackages(&list);
199 list.erase(std::remove_if(
200 list.begin(), list.end(),
201 [](const ApexInfo& apexInfo) { return apexInfo.isActive; }),
202 list.end());
203 if (status.isOk()) {
204 return list;
205 }
206
207 return Error() << status.toString8().c_str();
208 }
209
GetActivePackage(const std::string & name)210 Result<ApexInfo> GetActivePackage(const std::string& name) {
211 ApexInfo package;
212 android::binder::Status status = service_->getActivePackage(name, &package);
213 if (status.isOk()) {
214 return package;
215 }
216
217 return Error() << status.exceptionMessage().c_str();
218 }
219
GetPackageString(const ApexInfo & p)220 std::string GetPackageString(const ApexInfo& p) {
221 return p.moduleName + "@" + std::to_string(p.versionCode) +
222 " [path=" + p.moduleName + "]";
223 }
224
GetPackagesStrings(const std::vector<ApexInfo> & list)225 std::vector<std::string> GetPackagesStrings(
226 const std::vector<ApexInfo>& list) {
227 std::vector<std::string> ret;
228 ret.reserve(list.size());
229 for (const ApexInfo& p : list) {
230 ret.push_back(GetPackageString(p));
231 }
232 return ret;
233 }
234
GetActivePackagesStrings()235 std::vector<std::string> GetActivePackagesStrings() {
236 std::vector<ApexInfo> list;
237 android::binder::Status status = service_->getActivePackages(&list);
238 if (status.isOk()) {
239 std::vector<std::string> ret(list.size());
240 for (const ApexInfo& p : list) {
241 ret.push_back(GetPackageString(p));
242 }
243 return ret;
244 }
245
246 std::vector<std::string> error;
247 error.push_back("ERROR");
248 return error;
249 }
250
GetFactoryPackages()251 Result<std::vector<ApexInfo>> GetFactoryPackages() {
252 std::vector<ApexInfo> list;
253 android::binder::Status status = service_->getAllPackages(&list);
254 list.erase(
255 std::remove_if(list.begin(), list.end(),
256 [](ApexInfo& apexInfo) { return !apexInfo.isFactory; }),
257 list.end());
258 if (status.isOk()) {
259 return list;
260 }
261
262 return Error() << status.toString8().c_str();
263 }
264
ListDir(const std::string & path)265 static std::vector<std::string> ListDir(const std::string& path) {
266 std::vector<std::string> ret;
267 std::error_code ec;
268 if (!fs::is_directory(path, ec)) {
269 return ret;
270 }
271 auto status = WalkDir(path, [&](const fs::directory_entry& entry) {
272 std::string tmp;
273 switch (entry.symlink_status(ec).type()) {
274 case fs::file_type::directory:
275 tmp = "[dir]";
276 break;
277 case fs::file_type::symlink:
278 tmp = "[lnk]";
279 break;
280 case fs::file_type::regular:
281 tmp = "[reg]";
282 break;
283 default:
284 tmp = "[other]";
285 }
286 ret.push_back(tmp.append(entry.path().filename()));
287 });
288 CHECK(status.has_value())
289 << "Failed to list " << path << " : " << status.error();
290 std::sort(ret.begin(), ret.end());
291 return ret;
292 }
293
GetLogcat()294 static std::string GetLogcat() {
295 // For simplicity, log to file and read it.
296 std::string file = GetTestFile("logcat.tmp.txt");
297 std::vector<std::string> args{
298 "/system/bin/logcat",
299 "-d",
300 "-f",
301 file,
302 };
303 auto res = ForkAndRun(args);
304 CHECK(res.ok()) << res.error();
305
306 std::string data;
307 CHECK(android::base::ReadFileToString(file, &data));
308
309 unlink(file.c_str());
310
311 return data;
312 }
313
DeleteIfExists(const std::string & path)314 static void DeleteIfExists(const std::string& path) {
315 if (fs::exists(path)) {
316 std::error_code ec;
317 fs::remove_all(path, ec);
318 ASSERT_FALSE(ec) << "Failed to delete dir " << path << " : "
319 << ec.message();
320 }
321 }
322
323 struct PrepareTestApexForInstall {
324 static constexpr const char* kTestDir = "/data/app-staging/apexservice_tmp";
325
326 // This is given to the constructor.
327 std::string test_input; // Original test file.
328 std::string selinux_label_input; // SELinux label to apply.
329 std::string test_dir_input;
330
331 // This is derived from the input.
332 std::string test_file; // Prepared path. Under test_dir_input.
333 std::string test_installed_file; // Where apexd will store it.
334
335 std::string package; // APEX package name.
336 uint64_t version; // APEX version
337
PrepareTestApexForInstallandroid::apex::ApexServiceTest::PrepareTestApexForInstall338 explicit PrepareTestApexForInstall(
339 const std::string& test,
340 const std::string& test_dir = std::string(kTestDir),
341 const std::string& selinux_label = "staging_data_file") {
342 test_input = test;
343 selinux_label_input = selinux_label;
344 test_dir_input = test_dir;
345
346 test_file = test_dir_input + "/" + android::base::Basename(test);
347
348 package = ""; // Explicitly mark as not initialized.
349
350 Result<ApexFile> apex_file = ApexFile::Open(test);
351 if (!apex_file.ok()) {
352 return;
353 }
354
355 const ApexManifest& manifest = apex_file->GetManifest();
356 package = manifest.name();
357 version = manifest.version();
358
359 test_installed_file = std::string(kActiveApexPackagesDataDir) + "/" +
360 package + "@" + std::to_string(version) + ".apex";
361 }
362
Prepareandroid::apex::ApexServiceTest::PrepareTestApexForInstall363 bool Prepare() {
364 if (package.empty()) {
365 // Failure in constructor. Redo work to get error message.
366 auto fail_fn = [&]() {
367 Result<ApexFile> apex_file = ApexFile::Open(test_input);
368 ASSERT_FALSE(IsOk(apex_file));
369 ASSERT_TRUE(apex_file.ok())
370 << test_input << " failed to load: " << apex_file.error();
371 };
372 fail_fn();
373 return false;
374 }
375
376 auto prepare = [](const std::string& src, const std::string& trg,
377 const std::string& selinux_label) {
378 ASSERT_EQ(0, access(src.c_str(), F_OK))
379 << src << ": " << strerror(errno);
380 const std::string trg_dir = android::base::Dirname(trg);
381 if (0 != mkdir(trg_dir.c_str(), 0777)) {
382 int saved_errno = errno;
383 ASSERT_EQ(saved_errno, EEXIST) << trg << ":" << strerror(saved_errno);
384 }
385
386 // Do not use a hardlink, even though it's the simplest solution.
387 // b/119569101.
388 {
389 std::ifstream src_stream(src, std::ios::binary);
390 ASSERT_TRUE(src_stream.good());
391 std::ofstream trg_stream(trg, std::ios::binary);
392 ASSERT_TRUE(trg_stream.good());
393
394 trg_stream << src_stream.rdbuf();
395 }
396
397 ASSERT_EQ(0, chmod(trg.c_str(), 0666)) << strerror(errno);
398 struct group* g = getgrnam("system");
399 ASSERT_NE(nullptr, g);
400 ASSERT_EQ(0, chown(trg.c_str(), /* root uid */ 0, g->gr_gid))
401 << strerror(errno);
402
403 int rc = setfilecon(
404 trg_dir.c_str(),
405 std::string("u:object_r:" + selinux_label + ":s0").c_str());
406 ASSERT_TRUE(0 == rc || !HaveSelinux()) << strerror(errno);
407 rc = setfilecon(
408 trg.c_str(),
409 std::string("u:object_r:" + selinux_label + ":s0").c_str());
410 ASSERT_TRUE(0 == rc || !HaveSelinux()) << strerror(errno);
411 };
412 prepare(test_input, test_file, selinux_label_input);
413 return !HasFatalFailure();
414 }
415
~PrepareTestApexForInstallandroid::apex::ApexServiceTest::PrepareTestApexForInstall416 ~PrepareTestApexForInstall() {
417 LOG(INFO) << "Deleting file " << test_file;
418 if (unlink(test_file.c_str()) != 0) {
419 PLOG(ERROR) << "Unable to unlink " << test_file;
420 }
421 LOG(INFO) << "Deleting directory " << test_dir_input;
422 if (rmdir(test_dir_input.c_str()) != 0) {
423 PLOG(ERROR) << "Unable to rmdir " << test_dir_input;
424 }
425 }
426 };
427
GetDebugStr(PrepareTestApexForInstall * installer)428 std::string GetDebugStr(PrepareTestApexForInstall* installer) {
429 StringLog log;
430
431 if (installer != nullptr) {
432 log << "test_input=" << installer->test_input << " ";
433 log << "test_file=" << installer->test_file << " ";
434 log << "test_installed_file=" << installer->test_installed_file << " ";
435 log << "package=" << installer->package << " ";
436 log << "version=" << installer->version << " ";
437 }
438
439 log << "active=[" << Join(GetActivePackagesStrings(), ',') << "] ";
440 log << kActiveApexPackagesDataDir << "=["
441 << Join(ListDir(kActiveApexPackagesDataDir), ',') << "] ";
442 log << kApexRoot << "=[" << Join(ListDir(kApexRoot), ',') << "]";
443
444 return log;
445 }
446
447 sp<IApexService> service_;
448 sp<android::os::IVold> vold_service_;
449 bool supports_fs_checkpointing_;
450
451 private:
CleanUp()452 void CleanUp() {
453 DeleteDirContent(kActiveApexPackagesDataDir);
454 DeleteDirContent(kApexBackupDir);
455 DeleteDirContent(kApexHashTreeDir);
456 DeleteDirContent(ApexSession::GetSessionsDir());
457
458 DeleteIfExists("/data/misc_ce/0/apexdata/apex.apexd_test");
459 DeleteIfExists("/data/misc_ce/0/apexrollback/123456");
460 DeleteIfExists("/data/misc_ce/0/apexrollback/77777");
461 DeleteIfExists("/data/misc_ce/0/apexrollback/98765");
462 DeleteIfExists("/data/misc_de/0/apexrollback/123456");
463 DeleteIfExists("/data/misc/apexrollback/123456");
464 }
465 };
466
467 namespace {
468
RegularFileExists(const std::string & path)469 bool RegularFileExists(const std::string& path) {
470 struct stat buf;
471 if (0 != stat(path.c_str(), &buf)) {
472 return false;
473 }
474 return S_ISREG(buf.st_mode);
475 }
476
DirExists(const std::string & path)477 bool DirExists(const std::string& path) {
478 struct stat buf;
479 if (0 != stat(path.c_str(), &buf)) {
480 return false;
481 }
482 return S_ISDIR(buf.st_mode);
483 }
484
CreateDir(const std::string & path)485 void CreateDir(const std::string& path) {
486 std::error_code ec;
487 fs::create_directory(path, ec);
488 ASSERT_FALSE(ec) << "Failed to create rollback dir "
489 << " : " << ec.message();
490 }
491
CreateFile(const std::string & path)492 void CreateFile(const std::string& path) {
493 std::ofstream ofs(path);
494 ASSERT_TRUE(ofs.good());
495 ofs.close();
496 }
497
ReadEntireDir(const std::string & path)498 Result<std::vector<std::string>> ReadEntireDir(const std::string& path) {
499 static const auto kAcceptAll = [](auto /*entry*/) { return true; };
500 return ReadDir(path, kAcceptAll);
501 }
502
GetBlockDeviceForApex(const std::string & package_id)503 Result<std::string> GetBlockDeviceForApex(const std::string& package_id) {
504 std::string mount_point = std::string(kApexRoot) + "/" + package_id;
505 Fstab fstab;
506 if (!ReadFstabFromFile("/proc/mounts", &fstab)) {
507 return Error() << "Failed to read /proc/mounts";
508 }
509 auto entry = GetEntryForMountPoint(&fstab, mount_point);
510 if (entry == nullptr) {
511 return Error() << "Can't find " << mount_point << " in /proc/mounts";
512 }
513 return entry->blk_device;
514 }
515
ReadDevice(const std::string & block_device)516 Result<void> ReadDevice(const std::string& block_device) {
517 static constexpr int kBlockSize = 4096;
518 static constexpr size_t kBufSize = 1024 * kBlockSize;
519 std::vector<uint8_t> buffer(kBufSize);
520
521 unique_fd fd(
522 TEMP_FAILURE_RETRY(open(block_device.c_str(), O_RDONLY | O_CLOEXEC)));
523 if (fd.get() == -1) {
524 return ErrnoError() << "Can't open " << block_device;
525 }
526
527 while (true) {
528 int n = read(fd.get(), buffer.data(), kBufSize);
529 if (n < 0) {
530 return ErrnoError() << "Failed to read " << block_device;
531 }
532 if (n == 0) {
533 break;
534 }
535 }
536 return {};
537 }
538
ListSlavesOfDmDevice(const std::string & name)539 std::vector<std::string> ListSlavesOfDmDevice(const std::string& name) {
540 DeviceMapper& dm = DeviceMapper::Instance();
541 std::string dm_path;
542 EXPECT_TRUE(dm.GetDmDevicePathByName(name, &dm_path))
543 << "Failed to get path of dm device " << name;
544 // It's a little bit sad we can't use ConsumePrefix here :(
545 constexpr std::string_view kDevPrefix = "/dev/";
546 EXPECT_TRUE(StartsWith(dm_path, kDevPrefix)) << "Illegal path " << dm_path;
547 dm_path = dm_path.substr(kDevPrefix.length());
548 std::vector<std::string> slaves;
549 {
550 std::string slaves_dir = "/sys/" + dm_path + "/slaves";
551 auto st = WalkDir(slaves_dir, [&](const auto& entry) {
552 std::error_code ec;
553 if (entry.is_symlink(ec)) {
554 slaves.push_back("/dev/block/" + entry.path().filename().string());
555 }
556 if (ec) {
557 ADD_FAILURE() << "Failed to scan " << slaves_dir << " : " << ec;
558 }
559 });
560 EXPECT_TRUE(IsOk(st));
561 }
562 return slaves;
563 }
564
CopyFile(const std::string & from,const std::string & to,const fs::copy_options & options)565 Result<void> CopyFile(const std::string& from, const std::string& to,
566 const fs::copy_options& options) {
567 std::error_code ec;
568 if (!fs::copy_file(from, to, options)) {
569 return Error() << "Failed to copy file " << from << " to " << to << " : "
570 << ec.message();
571 }
572 return {};
573 }
574
575 } // namespace
576
TEST_F(ApexServiceTest,HaveSelinux)577 TEST_F(ApexServiceTest, HaveSelinux) {
578 // We want to test under selinux.
579 EXPECT_TRUE(HaveSelinux());
580 }
581
582 // Skip for b/119032200.
TEST_F(ApexServiceTest,DISABLED_EnforceSelinux)583 TEST_F(ApexServiceTest, DISABLED_EnforceSelinux) {
584 // Crude cutout for virtual devices.
585 #if !defined(__i386__) && !defined(__x86_64__)
586 constexpr bool kIsX86 = false;
587 #else
588 constexpr bool kIsX86 = true;
589 #endif
590 EXPECT_TRUE(IsSelinuxEnforced() || kIsX86);
591 }
592
TEST_F(ApexServiceTest,StageFailAccess)593 TEST_F(ApexServiceTest, StageFailAccess) {
594 if (!IsSelinuxEnforced()) {
595 LOG(WARNING) << "Skipping InstallFailAccess because of selinux";
596 return;
597 }
598
599 // Use an extra copy, so that even if this test fails (incorrectly installs),
600 // we have the testdata file still around.
601 std::string orig_test_file = GetTestFile("apex.apexd_test.apex");
602 std::string test_file = orig_test_file + ".2";
603 ASSERT_EQ(0, link(orig_test_file.c_str(), test_file.c_str()))
604 << strerror(errno);
605 struct Deleter {
606 std::string to_delete;
607 explicit Deleter(std::string t) : to_delete(std::move(t)) {}
608 ~Deleter() {
609 if (unlink(to_delete.c_str()) != 0) {
610 PLOG(ERROR) << "Could not unlink " << to_delete;
611 }
612 }
613 };
614 Deleter del(test_file);
615
616 android::binder::Status st = service_->stagePackages({test_file});
617 ASSERT_FALSE(IsOk(st));
618 std::string error = st.exceptionMessage().c_str();
619 EXPECT_NE(std::string::npos, error.find("Failed to open package")) << error;
620 EXPECT_NE(std::string::npos, error.find("I/O error")) << error;
621 }
622
TEST_F(ApexServiceTest,StageFailKey)623 TEST_F(ApexServiceTest, StageFailKey) {
624 PrepareTestApexForInstall installer(
625 GetTestFile("apex.apexd_test_no_inst_key.apex"));
626 if (!installer.Prepare()) {
627 return;
628 }
629 ASSERT_EQ(std::string("com.android.apex.test_package.no_inst_key"),
630 installer.package);
631
632 android::binder::Status st = service_->stagePackages({installer.test_file});
633 ASSERT_FALSE(IsOk(st));
634
635 // May contain one of two errors.
636 std::string error = st.exceptionMessage().c_str();
637
638 ASSERT_THAT(error, HasSubstr("No preinstalled apex found for package "
639 "com.android.apex.test_package.no_inst_key"));
640 }
641
TEST_F(ApexServiceTest,StageSuccess)642 TEST_F(ApexServiceTest, StageSuccess) {
643 PrepareTestApexForInstall installer(GetTestFile("apex.apexd_test.apex"));
644 if (!installer.Prepare()) {
645 return;
646 }
647 ASSERT_EQ(std::string("com.android.apex.test_package"), installer.package);
648
649 ASSERT_TRUE(IsOk(service_->stagePackages({installer.test_file})));
650 EXPECT_TRUE(RegularFileExists(installer.test_installed_file));
651 }
652
TEST_F(ApexServiceTest,SubmitStagegSessionSuccessDoesNotLeakTempVerityDevices)653 TEST_F(ApexServiceTest,
654 SubmitStagegSessionSuccessDoesNotLeakTempVerityDevices) {
655 PrepareTestApexForInstall installer(GetTestFile("apex.apexd_test.apex"),
656 "/data/app-staging/session_1543",
657 "staging_data_file");
658 if (!installer.Prepare()) {
659 return;
660 }
661
662 ApexInfoList list;
663 ApexSessionParams params;
664 params.sessionId = 1543;
665 ASSERT_TRUE(IsOk(service_->submitStagedSession(params, &list)));
666
667 std::vector<DeviceMapper::DmBlockDevice> devices;
668 DeviceMapper& dm = DeviceMapper::Instance();
669 ASSERT_TRUE(dm.GetAvailableDevices(&devices));
670
671 for (const auto& device : devices) {
672 ASSERT_THAT(device.name(), Not(EndsWith(".tmp")));
673 }
674 }
675
TEST_F(ApexServiceTest,SubmitStagedSessionStoresBuildFingerprint)676 TEST_F(ApexServiceTest, SubmitStagedSessionStoresBuildFingerprint) {
677 PrepareTestApexForInstall installer(GetTestFile("apex.apexd_test.apex"),
678 "/data/app-staging/session_1547",
679 "staging_data_file");
680 if (!installer.Prepare()) {
681 return;
682 }
683 ApexInfoList list;
684 ApexSessionParams params;
685 params.sessionId = 1547;
686 ASSERT_TRUE(IsOk(service_->submitStagedSession(params, &list)));
687
688 auto session = ApexSession::GetSession(1547);
689 ASSERT_FALSE(session->GetBuildFingerprint().empty());
690 }
691
TEST_F(ApexServiceTest,SubmitStagedSessionFailDoesNotLeakTempVerityDevices)692 TEST_F(ApexServiceTest, SubmitStagedSessionFailDoesNotLeakTempVerityDevices) {
693 PrepareTestApexForInstall installer(
694 GetTestFile("apex.apexd_test_manifest_mismatch.apex"),
695 "/data/app-staging/session_239", "staging_data_file");
696 if (!installer.Prepare()) {
697 return;
698 }
699
700 ApexInfoList list;
701 ApexSessionParams params;
702 params.sessionId = 239;
703 ASSERT_FALSE(IsOk(service_->submitStagedSession(params, &list)));
704
705 std::vector<DeviceMapper::DmBlockDevice> devices;
706 DeviceMapper& dm = DeviceMapper::Instance();
707 ASSERT_TRUE(dm.GetAvailableDevices(&devices));
708
709 for (const auto& device : devices) {
710 ASSERT_THAT(device.name(), Not(EndsWith(".tmp")));
711 }
712 }
713
TEST_F(ApexServiceTest,StageSuccessClearsPreviouslyActivePackage)714 TEST_F(ApexServiceTest, StageSuccessClearsPreviouslyActivePackage) {
715 PrepareTestApexForInstall installer1(GetTestFile("apex.apexd_test_v2.apex"));
716 PrepareTestApexForInstall installer2(
717 GetTestFile("apex.apexd_test_different_app.apex"));
718 PrepareTestApexForInstall installer3(GetTestFile("apex.apexd_test.apex"));
719 auto install_fn = [&](PrepareTestApexForInstall& installer) {
720 if (!installer.Prepare()) {
721 return;
722 }
723 ASSERT_TRUE(IsOk(service_->stagePackages({installer.test_file})));
724 EXPECT_TRUE(RegularFileExists(installer.test_installed_file));
725 };
726 install_fn(installer1);
727 install_fn(installer2);
728 // Simulating a revert. After this call test_v2_apex_path should be removed.
729 install_fn(installer3);
730
731 EXPECT_FALSE(RegularFileExists(installer1.test_installed_file));
732 EXPECT_TRUE(RegularFileExists(installer2.test_installed_file));
733 EXPECT_TRUE(RegularFileExists(installer3.test_installed_file));
734 }
735
TEST_F(ApexServiceTest,StageAlreadyStagedPackageSuccess)736 TEST_F(ApexServiceTest, StageAlreadyStagedPackageSuccess) {
737 PrepareTestApexForInstall installer(GetTestFile("apex.apexd_test.apex"));
738 if (!installer.Prepare()) {
739 return;
740 }
741 ASSERT_EQ(std::string("com.android.apex.test_package"), installer.package);
742
743 ASSERT_TRUE(IsOk(service_->stagePackages({installer.test_file})));
744 ASSERT_TRUE(RegularFileExists(installer.test_installed_file));
745
746 ASSERT_TRUE(IsOk(service_->stagePackages({installer.test_file})));
747 ASSERT_TRUE(RegularFileExists(installer.test_installed_file));
748 }
749
TEST_F(ApexServiceTest,StageAlreadyStagedPackageSuccessNewWins)750 TEST_F(ApexServiceTest, StageAlreadyStagedPackageSuccessNewWins) {
751 PrepareTestApexForInstall installer(GetTestFile("apex.apexd_test.apex"));
752 PrepareTestApexForInstall installer2(
753 GetTestFile("apex.apexd_test_nocode.apex"));
754 if (!installer.Prepare() || !installer2.Prepare()) {
755 return;
756 }
757 ASSERT_EQ(std::string("com.android.apex.test_package"), installer.package);
758 ASSERT_EQ(installer.test_installed_file, installer2.test_installed_file);
759
760 ASSERT_TRUE(IsOk(service_->stagePackages({installer.test_file})));
761 const auto& apex = ApexFile::Open(installer.test_installed_file);
762 ASSERT_TRUE(IsOk(apex));
763 ASSERT_FALSE(apex->GetManifest().nocode());
764
765 ASSERT_TRUE(IsOk(service_->stagePackages({installer2.test_file})));
766 const auto& new_apex = ApexFile::Open(installer.test_installed_file);
767 ASSERT_TRUE(IsOk(new_apex));
768 ASSERT_TRUE(new_apex->GetManifest().nocode());
769 }
770
TEST_F(ApexServiceTest,MultiStageSuccess)771 TEST_F(ApexServiceTest, MultiStageSuccess) {
772 PrepareTestApexForInstall installer(GetTestFile("apex.apexd_test.apex"));
773 if (!installer.Prepare()) {
774 return;
775 }
776 ASSERT_EQ(std::string("com.android.apex.test_package"), installer.package);
777
778 PrepareTestApexForInstall installer2(GetTestFile("apex.apexd_test_v2.apex"));
779 if (!installer2.Prepare()) {
780 return;
781 }
782 ASSERT_EQ(std::string("com.android.apex.test_package"), installer2.package);
783
784 std::vector<std::string> packages;
785 packages.push_back(installer.test_file);
786 packages.push_back(installer2.test_file);
787
788 ASSERT_TRUE(IsOk(service_->stagePackages(packages)));
789 EXPECT_TRUE(RegularFileExists(installer.test_installed_file));
790 EXPECT_TRUE(RegularFileExists(installer2.test_installed_file));
791 }
792
TEST_F(ApexServiceTest,CannotBeRollbackAndHaveRollbackEnabled)793 TEST_F(ApexServiceTest, CannotBeRollbackAndHaveRollbackEnabled) {
794 PrepareTestApexForInstall installer(GetTestFile("apex.apexd_test.apex"),
795 "/data/app-staging/session_1543",
796 "staging_data_file");
797 if (!installer.Prepare()) {
798 return;
799 }
800
801 ApexInfoList list;
802 ApexSessionParams params;
803 params.sessionId = 1543;
804 params.isRollback = true;
805 params.hasRollbackEnabled = true;
806 ASSERT_FALSE(IsOk(service_->submitStagedSession(params, &list)));
807 }
808
TEST_F(ApexServiceTest,SessionParamDefaults)809 TEST_F(ApexServiceTest, SessionParamDefaults) {
810 PrepareTestApexForInstall installer(GetTestFile("apex.apexd_test.apex"),
811 "/data/app-staging/session_1547",
812 "staging_data_file");
813 if (!installer.Prepare()) {
814 return;
815 }
816 ApexInfoList list;
817 ApexSessionParams params;
818 params.sessionId = 1547;
819 ASSERT_TRUE(IsOk(service_->submitStagedSession(params, &list)));
820
821 auto session = ApexSession::GetSession(1547);
822 ASSERT_TRUE(session->GetChildSessionIds().empty());
823 ASSERT_FALSE(session->IsRollback());
824 ASSERT_FALSE(session->HasRollbackEnabled());
825 ASSERT_EQ(0, session->GetRollbackId());
826 }
827
TEST_F(ApexServiceTest,SnapshotCeData)828 TEST_F(ApexServiceTest, SnapshotCeData) {
829 CreateDir("/data/misc_ce/0/apexdata/apex.apexd_test");
830 CreateFile("/data/misc_ce/0/apexdata/apex.apexd_test/hello.txt");
831
832 ASSERT_TRUE(
833 RegularFileExists("/data/misc_ce/0/apexdata/apex.apexd_test/hello.txt"));
834
835 service_->snapshotCeData(0, 123456, "apex.apexd_test");
836
837 ASSERT_TRUE(RegularFileExists(
838 "/data/misc_ce/0/apexrollback/123456/apex.apexd_test/hello.txt"));
839 }
840
TEST_F(ApexServiceTest,RestoreCeData)841 TEST_F(ApexServiceTest, RestoreCeData) {
842 CreateDir("/data/misc_ce/0/apexdata/apex.apexd_test");
843 CreateDir("/data/misc_ce/0/apexrollback/123456");
844 CreateDir("/data/misc_ce/0/apexrollback/123456/apex.apexd_test");
845
846 CreateFile("/data/misc_ce/0/apexdata/apex.apexd_test/newfile.txt");
847 CreateFile("/data/misc_ce/0/apexrollback/123456/apex.apexd_test/oldfile.txt");
848
849 ASSERT_TRUE(RegularFileExists(
850 "/data/misc_ce/0/apexdata/apex.apexd_test/newfile.txt"));
851 ASSERT_TRUE(RegularFileExists(
852 "/data/misc_ce/0/apexrollback/123456/apex.apexd_test/oldfile.txt"));
853
854 service_->restoreCeData(0, 123456, "apex.apexd_test");
855
856 ASSERT_TRUE(RegularFileExists(
857 "/data/misc_ce/0/apexdata/apex.apexd_test/oldfile.txt"));
858 ASSERT_FALSE(RegularFileExists(
859 "/data/misc_ce/0/apexdata/apex.apexd_test/newfile.txt"));
860 // The snapshot should be deleted after restoration.
861 ASSERT_FALSE(
862 DirExists("/data/misc_ce/0/apexrollback/123456/apex.apexd_test"));
863 }
864
TEST_F(ApexServiceTest,DestroyDeSnapshotsDeSys)865 TEST_F(ApexServiceTest, DestroyDeSnapshotsDeSys) {
866 CreateDir("/data/misc/apexrollback/123456");
867 CreateDir("/data/misc/apexrollback/123456/my.apex");
868 CreateFile("/data/misc/apexrollback/123456/my.apex/hello.txt");
869
870 ASSERT_TRUE(
871 RegularFileExists("/data/misc/apexrollback/123456/my.apex/hello.txt"));
872
873 service_->destroyDeSnapshots(8975);
874 ASSERT_TRUE(
875 RegularFileExists("/data/misc/apexrollback/123456/my.apex/hello.txt"));
876
877 service_->destroyDeSnapshots(123456);
878 ASSERT_FALSE(
879 RegularFileExists("/data/misc/apexrollback/123456/my.apex/hello.txt"));
880 ASSERT_FALSE(DirExists("/data/misc/apexrollback/123456"));
881 }
882
TEST_F(ApexServiceTest,DestroyDeSnapshotsDeUser)883 TEST_F(ApexServiceTest, DestroyDeSnapshotsDeUser) {
884 CreateDir("/data/misc_de/0/apexrollback/123456");
885 CreateDir("/data/misc_de/0/apexrollback/123456/my.apex");
886 CreateFile("/data/misc_de/0/apexrollback/123456/my.apex/hello.txt");
887
888 ASSERT_TRUE(RegularFileExists(
889 "/data/misc_de/0/apexrollback/123456/my.apex/hello.txt"));
890
891 service_->destroyDeSnapshots(8975);
892 ASSERT_TRUE(RegularFileExists(
893 "/data/misc_de/0/apexrollback/123456/my.apex/hello.txt"));
894
895 service_->destroyDeSnapshots(123456);
896 ASSERT_FALSE(RegularFileExists(
897 "/data/misc_de/0/apexrollback/123456/my.apex/hello.txt"));
898 ASSERT_FALSE(DirExists("/data/misc_de/0/apexrollback/123456"));
899 }
900
TEST_F(ApexServiceTest,DestroyCeSnapshots)901 TEST_F(ApexServiceTest, DestroyCeSnapshots) {
902 CreateDir("/data/misc_ce/0/apexrollback/123456");
903 CreateDir("/data/misc_ce/0/apexrollback/123456/apex.apexd_test");
904 CreateFile("/data/misc_ce/0/apexrollback/123456/apex.apexd_test/file.txt");
905
906 CreateDir("/data/misc_ce/0/apexrollback/77777");
907 CreateDir("/data/misc_ce/0/apexrollback/77777/apex.apexd_test");
908 CreateFile("/data/misc_ce/0/apexrollback/77777/apex.apexd_test/thing.txt");
909
910 ASSERT_TRUE(RegularFileExists(
911 "/data/misc_ce/0/apexrollback/123456/apex.apexd_test/file.txt"));
912 ASSERT_TRUE(RegularFileExists(
913 "/data/misc_ce/0/apexrollback/77777/apex.apexd_test/thing.txt"));
914
915 android::binder::Status st = service_->destroyCeSnapshots(0, 123456);
916 ASSERT_TRUE(IsOk(st));
917 // Should be OK if the directory doesn't exist.
918 st = service_->destroyCeSnapshots(1, 123456);
919 ASSERT_TRUE(IsOk(st));
920
921 ASSERT_TRUE(RegularFileExists(
922 "/data/misc_ce/0/apexrollback/77777/apex.apexd_test/thing.txt"));
923 ASSERT_FALSE(DirExists("/data/misc_ce/0/apexrollback/123456"));
924 }
925
TEST_F(ApexServiceTest,DestroyCeSnapshotsNotSpecified)926 TEST_F(ApexServiceTest, DestroyCeSnapshotsNotSpecified) {
927 CreateDir("/data/misc_ce/0/apexrollback/123456");
928 CreateDir("/data/misc_ce/0/apexrollback/123456/apex.apexd_test");
929 CreateFile("/data/misc_ce/0/apexrollback/123456/apex.apexd_test/file.txt");
930
931 CreateDir("/data/misc_ce/0/apexrollback/77777");
932 CreateDir("/data/misc_ce/0/apexrollback/77777/apex.apexd_test");
933 CreateFile("/data/misc_ce/0/apexrollback/77777/apex.apexd_test/thing.txt");
934
935 CreateDir("/data/misc_ce/0/apexrollback/98765");
936 CreateDir("/data/misc_ce/0/apexrollback/98765/apex.apexd_test");
937 CreateFile("/data/misc_ce/0/apexrollback/98765/apex.apexd_test/test.txt");
938
939 ASSERT_TRUE(RegularFileExists(
940 "/data/misc_ce/0/apexrollback/123456/apex.apexd_test/file.txt"));
941 ASSERT_TRUE(RegularFileExists(
942 "/data/misc_ce/0/apexrollback/77777/apex.apexd_test/thing.txt"));
943 ASSERT_TRUE(RegularFileExists(
944 "/data/misc_ce/0/apexrollback/98765/apex.apexd_test/test.txt"));
945
946 std::vector<int> retain{123, 77777, 987654};
947 android::binder::Status st =
948 service_->destroyCeSnapshotsNotSpecified(0, retain);
949 ASSERT_TRUE(IsOk(st));
950
951 ASSERT_TRUE(RegularFileExists(
952 "/data/misc_ce/0/apexrollback/77777/apex.apexd_test/thing.txt"));
953 ASSERT_FALSE(DirExists("/data/misc_ce/0/apexrollback/123456"));
954 ASSERT_FALSE(DirExists("/data/misc_ce/0/apexrollback/98765"));
955 }
956
TEST_F(ApexServiceTest,SubmitStagedSessionCleanupsTempMountOnFailure)957 TEST_F(ApexServiceTest, SubmitStagedSessionCleanupsTempMountOnFailure) {
958 // Parent session id: 23
959 // Children session ids: 37 73
960 PrepareTestApexForInstall installer(
961 GetTestFile("apex.apexd_test_different_app.apex"),
962 "/data/app-staging/session_37", "staging_data_file");
963 PrepareTestApexForInstall installer2(
964 GetTestFile("apex.apexd_test_manifest_mismatch.apex"),
965 "/data/app-staging/session_73", "staging_data_file");
966 if (!installer.Prepare() || !installer2.Prepare()) {
967 FAIL() << GetDebugStr(&installer) << GetDebugStr(&installer2);
968 }
969 ApexInfoList list;
970 ApexSessionParams params;
971 params.sessionId = 23;
972 params.childSessionIds = {37, 73};
973 ASSERT_FALSE(IsOk(service_->submitStagedSession(params, &list)))
974 << GetDebugStr(&installer);
975
976 // Check that temp mounts were cleanded up.
977 for (const auto& mount : GetApexMounts()) {
978 EXPECT_FALSE(EndsWith(mount, ".tmp")) << "Found temp mount " << mount;
979 }
980 }
981
982 template <typename NameProvider>
983 class ApexServiceActivationTest : public ApexServiceTest {
984 public:
ApexServiceActivationTest()985 ApexServiceActivationTest() : stage_package(true) {}
986
ApexServiceActivationTest(bool stage_package)987 explicit ApexServiceActivationTest(bool stage_package)
988 : stage_package(stage_package) {}
989
SetUp()990 void SetUp() override {
991 // TODO(b/136647373): Move this check to environment setup
992 if (!android::base::GetBoolProperty("ro.apex.updatable", false)) {
993 GTEST_SKIP() << "Skipping test because device doesn't support APEX";
994 }
995 ApexServiceTest::SetUp();
996 ASSERT_NE(nullptr, service_.get());
997
998 installer_ = std::make_unique<PrepareTestApexForInstall>(
999 GetTestFile(NameProvider::GetTestName()));
1000 if (!installer_->Prepare()) {
1001 return;
1002 }
1003 ASSERT_EQ(NameProvider::GetPackageName(), installer_->package);
1004
1005 {
1006 // Check package is not active.
1007 std::string path = stage_package ? installer_->test_installed_file
1008 : installer_->test_file;
1009 Result<bool> active =
1010 IsActive(installer_->package, installer_->version, path);
1011 ASSERT_TRUE(IsOk(active));
1012 ASSERT_FALSE(*active);
1013 }
1014
1015 if (stage_package) {
1016 ASSERT_TRUE(IsOk(service_->stagePackages({installer_->test_file})));
1017 }
1018 }
1019
TearDown()1020 void TearDown() override {
1021 // Attempt to deactivate.
1022 if (installer_ != nullptr) {
1023 if (stage_package) {
1024 service_->deactivatePackage(installer_->test_installed_file);
1025 } else {
1026 service_->deactivatePackage(installer_->test_file);
1027 }
1028 }
1029
1030 installer_.reset();
1031 // ApexServiceTest::TearDown will wipe out everything under /data/apex.
1032 // Since some of that information is required for deactivatePackage binder
1033 // call, it's required to be called after deactivating package.
1034 ApexServiceTest::TearDown();
1035 }
1036
1037 std::unique_ptr<PrepareTestApexForInstall> installer_;
1038
1039 private:
1040 bool stage_package;
1041 };
1042
1043 struct SuccessNameProvider {
GetTestNameandroid::apex::SuccessNameProvider1044 static std::string GetTestName() { return "apex.apexd_test.apex"; }
GetPackageNameandroid::apex::SuccessNameProvider1045 static std::string GetPackageName() {
1046 return "com.android.apex.test_package";
1047 }
1048 };
1049
1050 struct ManifestMismatchNameProvider {
GetTestNameandroid::apex::ManifestMismatchNameProvider1051 static std::string GetTestName() {
1052 return "apex.apexd_test_manifest_mismatch.apex";
1053 }
GetPackageNameandroid::apex::ManifestMismatchNameProvider1054 static std::string GetPackageName() {
1055 return "com.android.apex.test_package";
1056 }
1057 };
1058
1059 class ApexServiceActivationManifestMismatchFailure
1060 : public ApexServiceActivationTest<ManifestMismatchNameProvider> {
1061 public:
ApexServiceActivationManifestMismatchFailure()1062 ApexServiceActivationManifestMismatchFailure()
1063 : ApexServiceActivationTest(false) {}
1064 };
1065
TEST_F(ApexServiceActivationManifestMismatchFailure,ActivateFailsWithManifestMismatch)1066 TEST_F(ApexServiceActivationManifestMismatchFailure,
1067 ActivateFailsWithManifestMismatch) {
1068 android::binder::Status st = service_->activatePackage(installer_->test_file);
1069 ASSERT_FALSE(IsOk(st));
1070
1071 std::string error = st.exceptionMessage().c_str();
1072 ASSERT_THAT(
1073 error,
1074 HasSubstr(
1075 "Manifest inside filesystem does not match manifest outside it"));
1076 }
1077
1078 class ApexServiceActivationSuccessTest
1079 : public ApexServiceActivationTest<SuccessNameProvider> {};
1080
TEST_F(ApexServiceActivationSuccessTest,Activate)1081 TEST_F(ApexServiceActivationSuccessTest, Activate) {
1082 ASSERT_TRUE(IsOk(service_->activatePackage(installer_->test_installed_file)))
1083 << GetDebugStr(installer_.get());
1084
1085 {
1086 // Check package is active.
1087 Result<bool> active = IsActive(installer_->package, installer_->version,
1088 installer_->test_installed_file);
1089 ASSERT_TRUE(IsOk(active));
1090 ASSERT_TRUE(*active) << Join(GetActivePackagesStrings(), ',');
1091 }
1092
1093 {
1094 // Check that the "latest" view exists.
1095 std::string latest_path =
1096 std::string(kApexRoot) + "/" + installer_->package;
1097 struct stat buf;
1098 ASSERT_EQ(0, stat(latest_path.c_str(), &buf)) << strerror(errno);
1099 // Check that it is a folder.
1100 EXPECT_TRUE(S_ISDIR(buf.st_mode));
1101
1102 // Collect direct entries of a folder.
1103 auto collect_entries_fn = [&](const std::string& path) {
1104 std::vector<std::string> ret;
1105 auto status = WalkDir(path, [&](const fs::directory_entry& entry) {
1106 if (!entry.is_directory()) {
1107 return;
1108 }
1109 ret.emplace_back(entry.path().filename());
1110 });
1111 CHECK(status.has_value())
1112 << "Failed to list " << path << " : " << status.error();
1113 std::sort(ret.begin(), ret.end());
1114 return ret;
1115 };
1116
1117 std::string versioned_path = std::string(kApexRoot) + "/" +
1118 installer_->package + "@" +
1119 std::to_string(installer_->version);
1120 std::vector<std::string> versioned_folder_entries =
1121 collect_entries_fn(versioned_path);
1122 std::vector<std::string> latest_folder_entries =
1123 collect_entries_fn(latest_path);
1124
1125 EXPECT_TRUE(versioned_folder_entries == latest_folder_entries)
1126 << "Versioned: " << Join(versioned_folder_entries, ',')
1127 << " Latest: " << Join(latest_folder_entries, ',');
1128 }
1129 }
1130
TEST_F(ApexServiceActivationSuccessTest,GetActivePackages)1131 TEST_F(ApexServiceActivationSuccessTest, GetActivePackages) {
1132 ASSERT_TRUE(IsOk(service_->activatePackage(installer_->test_installed_file)))
1133 << GetDebugStr(installer_.get());
1134
1135 Result<std::vector<ApexInfo>> active = GetActivePackages();
1136 ASSERT_TRUE(IsOk(active));
1137 ApexInfo match;
1138
1139 for (const ApexInfo& info : *active) {
1140 if (info.moduleName == installer_->package) {
1141 match = info;
1142 break;
1143 }
1144 }
1145
1146 ASSERT_EQ(installer_->package, match.moduleName);
1147 ASSERT_EQ(installer_->version, static_cast<uint64_t>(match.versionCode));
1148 ASSERT_EQ(installer_->test_installed_file, match.modulePath);
1149 }
1150
TEST_F(ApexServiceActivationSuccessTest,GetActivePackage)1151 TEST_F(ApexServiceActivationSuccessTest, GetActivePackage) {
1152 ASSERT_TRUE(IsOk(service_->activatePackage(installer_->test_installed_file)))
1153 << GetDebugStr(installer_.get());
1154
1155 Result<ApexInfo> active = GetActivePackage(installer_->package);
1156 ASSERT_TRUE(IsOk(active));
1157
1158 ASSERT_EQ(installer_->package, active->moduleName);
1159 ASSERT_EQ(installer_->version, static_cast<uint64_t>(active->versionCode));
1160 ASSERT_EQ(installer_->test_installed_file, active->modulePath);
1161 }
1162
TEST_F(ApexServiceActivationSuccessTest,ShowsUpInMountedApexDatabase)1163 TEST_F(ApexServiceActivationSuccessTest, ShowsUpInMountedApexDatabase) {
1164 ASSERT_TRUE(IsOk(service_->activatePackage(installer_->test_installed_file)))
1165 << GetDebugStr(installer_.get());
1166
1167 MountedApexDatabase db;
1168 db.PopulateFromMounts(kActiveApexPackagesDataDir, kApexDecompressedDir,
1169 kApexHashTreeDir);
1170
1171 std::optional<MountedApexData> mounted_apex;
1172 db.ForallMountedApexes(installer_->package,
1173 [&](const MountedApexData& d, bool active) {
1174 if (active) {
1175 mounted_apex.emplace(d);
1176 }
1177 });
1178 ASSERT_TRUE(mounted_apex)
1179 << "Haven't found " << installer_->test_installed_file
1180 << " in the database of mounted apexes";
1181
1182 // Get all necessary data for assertions on mounted_apex.
1183 std::string package_id =
1184 installer_->package + "@" + std::to_string(installer_->version);
1185 DeviceMapper& dm = DeviceMapper::Instance();
1186 std::string dm_path;
1187 ASSERT_TRUE(dm.GetDmDevicePathByName(package_id, &dm_path))
1188 << "Failed to get path of dm device " << package_id;
1189 auto loop_device = dm.GetParentBlockDeviceByPath(dm_path);
1190 ASSERT_TRUE(loop_device) << "Failed to find parent block device of "
1191 << dm_path;
1192
1193 // Now we are ready to assert on mounted_apex.
1194 ASSERT_EQ(*loop_device, mounted_apex->loop_name);
1195 ASSERT_EQ(installer_->test_installed_file, mounted_apex->full_path);
1196 std::string expected_mount = std::string(kApexRoot) + "/" + package_id;
1197 ASSERT_EQ(expected_mount, mounted_apex->mount_point);
1198 ASSERT_EQ(package_id, mounted_apex->device_name);
1199 ASSERT_EQ("", mounted_apex->hashtree_loop_name);
1200 }
1201
1202 struct NoHashtreeApexNameProvider {
GetTestNameandroid::apex::NoHashtreeApexNameProvider1203 static std::string GetTestName() {
1204 return "apex.apexd_test_no_hashtree.apex";
1205 }
GetPackageNameandroid::apex::NoHashtreeApexNameProvider1206 static std::string GetPackageName() {
1207 return "com.android.apex.test_package";
1208 }
1209 };
1210
1211 class ApexServiceNoHashtreeApexActivationTest
1212 : public ApexServiceActivationTest<NoHashtreeApexNameProvider> {};
1213
TEST_F(ApexServiceNoHashtreeApexActivationTest,Activate)1214 TEST_F(ApexServiceNoHashtreeApexActivationTest, Activate) {
1215 ASSERT_TRUE(IsOk(service_->activatePackage(installer_->test_installed_file)))
1216 << GetDebugStr(installer_.get());
1217 {
1218 // Check package is active.
1219 Result<bool> active = IsActive(installer_->package, installer_->version,
1220 installer_->test_installed_file);
1221 ASSERT_TRUE(IsOk(active));
1222 ASSERT_TRUE(*active) << Join(GetActivePackagesStrings(), ',');
1223 }
1224
1225 std::string package_id =
1226 installer_->package + "@" + std::to_string(installer_->version);
1227 // Check that hashtree file was created.
1228 {
1229 std::string hashtree_path =
1230 std::string(kApexHashTreeDir) + "/" + package_id;
1231 auto exists = PathExists(hashtree_path);
1232 ASSERT_TRUE(IsOk(exists));
1233 ASSERT_TRUE(*exists);
1234 }
1235
1236 // Check that block device can be read.
1237 auto block_device = GetBlockDeviceForApex(package_id);
1238 ASSERT_TRUE(IsOk(block_device));
1239 ASSERT_TRUE(IsOk(ReadDevice(*block_device)));
1240 }
1241
TEST_F(ApexServiceNoHashtreeApexActivationTest,NewSessionDoesNotImpactActivePackage)1242 TEST_F(ApexServiceNoHashtreeApexActivationTest,
1243 NewSessionDoesNotImpactActivePackage) {
1244 ASSERT_TRUE(IsOk(service_->activatePackage(installer_->test_installed_file)))
1245 << GetDebugStr(installer_.get());
1246 {
1247 // Check package is active.
1248 Result<bool> active = IsActive(installer_->package, installer_->version,
1249 installer_->test_installed_file);
1250 ASSERT_TRUE(IsOk(active));
1251 ASSERT_TRUE(*active) << Join(GetActivePackagesStrings(), ',');
1252 }
1253
1254 PrepareTestApexForInstall installer2(
1255 GetTestFile("apex.apexd_test_no_hashtree_2.apex"),
1256 "/data/app-staging/session_123", "staging_data_file");
1257 if (!installer2.Prepare()) {
1258 FAIL();
1259 }
1260
1261 ApexInfoList list;
1262 ApexSessionParams params;
1263 params.sessionId = 123;
1264 ASSERT_TRUE(IsOk(service_->submitStagedSession(params, &list)));
1265
1266 std::string package_id =
1267 installer_->package + "@" + std::to_string(installer_->version);
1268 // Check that new hashtree file was created.
1269 {
1270 std::string hashtree_path =
1271 std::string(kApexHashTreeDir) + "/" + package_id + ".new";
1272 auto exists = PathExists(hashtree_path);
1273 ASSERT_TRUE(IsOk(exists));
1274 ASSERT_TRUE(*exists) << hashtree_path << " does not exist";
1275 }
1276 // Check that active hashtree is still there.
1277 {
1278 std::string hashtree_path =
1279 std::string(kApexHashTreeDir) + "/" + package_id;
1280 auto exists = PathExists(hashtree_path);
1281 ASSERT_TRUE(IsOk(exists));
1282 ASSERT_TRUE(*exists) << hashtree_path << " does not exist";
1283 }
1284
1285 // Check that block device of active APEX can still be read.
1286 auto block_device = GetBlockDeviceForApex(package_id);
1287 ASSERT_TRUE(IsOk(block_device));
1288 }
1289
TEST_F(ApexServiceNoHashtreeApexActivationTest,ShowsUpInMountedApexDatabase)1290 TEST_F(ApexServiceNoHashtreeApexActivationTest, ShowsUpInMountedApexDatabase) {
1291 ASSERT_TRUE(IsOk(service_->activatePackage(installer_->test_installed_file)))
1292 << GetDebugStr(installer_.get());
1293
1294 MountedApexDatabase db;
1295 db.PopulateFromMounts(kActiveApexPackagesDataDir, kApexDecompressedDir,
1296 kApexHashTreeDir);
1297
1298 std::optional<MountedApexData> mounted_apex;
1299 db.ForallMountedApexes(installer_->package,
1300 [&](const MountedApexData& d, bool active) {
1301 if (active) {
1302 mounted_apex.emplace(d);
1303 }
1304 });
1305 ASSERT_TRUE(mounted_apex)
1306 << "Haven't found " << installer_->test_installed_file
1307 << " in the database of mounted apexes";
1308
1309 // Get all necessary data for assertions on mounted_apex.
1310 std::string package_id =
1311 installer_->package + "@" + std::to_string(installer_->version);
1312 std::vector<std::string> slaves = ListSlavesOfDmDevice(package_id);
1313 ASSERT_EQ(2u, slaves.size())
1314 << "Unexpected number of slaves: " << Join(slaves, ",");
1315
1316 // Now we are ready to assert on mounted_apex.
1317 ASSERT_EQ(installer_->test_installed_file, mounted_apex->full_path);
1318 std::string expected_mount = std::string(kApexRoot) + "/" + package_id;
1319 ASSERT_EQ(expected_mount, mounted_apex->mount_point);
1320 ASSERT_EQ(package_id, mounted_apex->device_name);
1321 // For loops we only check that both loop_name and hashtree_loop_name are
1322 // slaves of the top device mapper device.
1323 ASSERT_THAT(slaves, Contains(mounted_apex->loop_name));
1324 ASSERT_THAT(slaves, Contains(mounted_apex->hashtree_loop_name));
1325 ASSERT_NE(mounted_apex->loop_name, mounted_apex->hashtree_loop_name);
1326 }
1327
TEST_F(ApexServiceNoHashtreeApexActivationTest,DeactivateFreesLoopDevices)1328 TEST_F(ApexServiceNoHashtreeApexActivationTest, DeactivateFreesLoopDevices) {
1329 ASSERT_TRUE(IsOk(service_->activatePackage(installer_->test_installed_file)))
1330 << GetDebugStr(installer_.get());
1331
1332 std::string package_id =
1333 installer_->package + "@" + std::to_string(installer_->version);
1334 std::vector<std::string> slaves = ListSlavesOfDmDevice(package_id);
1335 ASSERT_EQ(2u, slaves.size())
1336 << "Unexpected number of slaves: " << Join(slaves, ",");
1337
1338 ASSERT_TRUE(
1339 IsOk(service_->deactivatePackage(installer_->test_installed_file)));
1340
1341 for (const auto& loop : slaves) {
1342 struct loop_info li;
1343 unique_fd fd(TEMP_FAILURE_RETRY(open(loop.c_str(), O_RDWR | O_CLOEXEC)));
1344 ASSERT_NE(-1, fd.get())
1345 << "Failed to open " << loop << " : " << strerror(errno);
1346 ASSERT_EQ(-1, ioctl(fd.get(), LOOP_GET_STATUS, &li))
1347 << loop << " is still alive";
1348 ASSERT_EQ(ENXIO, errno) << "Unexpected errno : " << strerror(errno);
1349 }
1350
1351 // Skip deactivatePackage on TearDown.
1352 installer_.reset();
1353 }
1354
TEST_F(ApexServiceTest,NoHashtreeApexStagePackagesMovesHashtree)1355 TEST_F(ApexServiceTest, NoHashtreeApexStagePackagesMovesHashtree) {
1356 PrepareTestApexForInstall installer(
1357 GetTestFile("apex.apexd_test_no_hashtree.apex"),
1358 "/data/app-staging/session_239", "staging_data_file");
1359 if (!installer.Prepare()) {
1360 FAIL();
1361 }
1362
1363 auto read_fn = [](const std::string& path) -> std::vector<uint8_t> {
1364 static constexpr size_t kBufSize = 4096;
1365 std::vector<uint8_t> buffer(kBufSize);
1366 unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
1367 if (fd.get() == -1) {
1368 PLOG(ERROR) << "Failed to open " << path;
1369 ADD_FAILURE();
1370 return buffer;
1371 }
1372 if (!ReadFully(fd.get(), buffer.data(), kBufSize)) {
1373 PLOG(ERROR) << "Failed to read " << path;
1374 ADD_FAILURE();
1375 }
1376 return buffer;
1377 };
1378
1379 ApexInfoList list;
1380 ApexSessionParams params;
1381 params.sessionId = 239;
1382 ASSERT_TRUE(IsOk(service_->submitStagedSession(params, &list)));
1383
1384 std::string package_id =
1385 installer.package + "@" + std::to_string(installer.version);
1386 // Check that new hashtree file was created.
1387 std::vector<uint8_t> original_hashtree_data;
1388 {
1389 std::string hashtree_path =
1390 std::string(kApexHashTreeDir) + "/" + package_id + ".new";
1391 auto exists = PathExists(hashtree_path);
1392 ASSERT_TRUE(IsOk(exists));
1393 ASSERT_TRUE(*exists);
1394 original_hashtree_data = read_fn(hashtree_path);
1395 }
1396
1397 ASSERT_TRUE(IsOk(service_->stagePackages({installer.test_file})));
1398 // Check that hashtree file was moved.
1399 {
1400 std::string hashtree_path =
1401 std::string(kApexHashTreeDir) + "/" + package_id + ".new";
1402 auto exists = PathExists(hashtree_path);
1403 ASSERT_TRUE(IsOk(exists));
1404 ASSERT_FALSE(*exists);
1405 }
1406 {
1407 std::string hashtree_path =
1408 std::string(kApexHashTreeDir) + "/" + package_id;
1409 auto exists = PathExists(hashtree_path);
1410 ASSERT_TRUE(IsOk(exists));
1411 ASSERT_TRUE(*exists);
1412 std::vector<uint8_t> moved_hashtree_data = read_fn(hashtree_path);
1413 ASSERT_EQ(moved_hashtree_data, original_hashtree_data);
1414 }
1415 }
1416
TEST_F(ApexServiceTest,GetFactoryPackages)1417 TEST_F(ApexServiceTest, GetFactoryPackages) {
1418 Result<std::vector<ApexInfo>> factory_packages = GetFactoryPackages();
1419 ASSERT_TRUE(IsOk(factory_packages));
1420 ASSERT_TRUE(factory_packages->size() > 0);
1421
1422 std::vector<std::string> builtin_dirs;
1423 for (const auto& d : kApexPackageBuiltinDirs) {
1424 std::string realpath;
1425 if (android::base::Realpath(d, &realpath)) {
1426 builtin_dirs.push_back(realpath);
1427 }
1428 // realpath might fail in case when dir is a non-existing path. We can
1429 // ignore non-existing paths.
1430 }
1431
1432 // Decompressed APEX is also considred factory package
1433 builtin_dirs.push_back(kApexDecompressedDir);
1434
1435 for (const ApexInfo& package : *factory_packages) {
1436 bool is_builtin = false;
1437 for (const auto& dir : builtin_dirs) {
1438 if (StartsWith(package.modulePath, dir)) {
1439 is_builtin = true;
1440 }
1441 }
1442 ASSERT_TRUE(is_builtin);
1443 }
1444 }
1445
TEST_F(ApexServiceTest,NoPackagesAreBothActiveAndInactive)1446 TEST_F(ApexServiceTest, NoPackagesAreBothActiveAndInactive) {
1447 Result<std::vector<ApexInfo>> active_packages = GetActivePackages();
1448 ASSERT_TRUE(IsOk(active_packages));
1449 ASSERT_TRUE(active_packages->size() > 0);
1450 Result<std::vector<ApexInfo>> inactive_packages = GetInactivePackages();
1451 ASSERT_TRUE(IsOk(inactive_packages));
1452 std::vector<std::string> active_packages_strings =
1453 GetPackagesStrings(*active_packages);
1454 std::vector<std::string> inactive_packages_strings =
1455 GetPackagesStrings(*inactive_packages);
1456 std::sort(active_packages_strings.begin(), active_packages_strings.end());
1457 std::sort(inactive_packages_strings.begin(), inactive_packages_strings.end());
1458 std::vector<std::string> intersection;
1459 std::set_intersection(
1460 active_packages_strings.begin(), active_packages_strings.end(),
1461 inactive_packages_strings.begin(), inactive_packages_strings.end(),
1462 std::back_inserter(intersection));
1463 ASSERT_THAT(intersection, SizeIs(0));
1464 }
1465
TEST_F(ApexServiceTest,GetAllPackages)1466 TEST_F(ApexServiceTest, GetAllPackages) {
1467 Result<std::vector<ApexInfo>> all_packages = GetAllPackages();
1468 ASSERT_TRUE(IsOk(all_packages));
1469 ASSERT_TRUE(all_packages->size() > 0);
1470 Result<std::vector<ApexInfo>> active_packages = GetActivePackages();
1471 std::vector<std::string> active_strings =
1472 GetPackagesStrings(*active_packages);
1473 Result<std::vector<ApexInfo>> factory_packages = GetFactoryPackages();
1474 std::vector<std::string> factory_strings =
1475 GetPackagesStrings(*factory_packages);
1476 for (ApexInfo& apexInfo : *all_packages) {
1477 std::string package_string = GetPackageString(apexInfo);
1478 bool should_be_active =
1479 std::find(active_strings.begin(), active_strings.end(),
1480 package_string) != active_strings.end();
1481 bool should_be_factory =
1482 std::find(factory_strings.begin(), factory_strings.end(),
1483 package_string) != factory_strings.end();
1484 ASSERT_EQ(should_be_active, apexInfo.isActive)
1485 << package_string << " should " << (should_be_active ? "" : "not ")
1486 << "be active";
1487 ASSERT_EQ(should_be_factory, apexInfo.isFactory)
1488 << package_string << " should " << (should_be_factory ? "" : "not ")
1489 << "be factory";
1490 }
1491 }
1492
1493 class ApexSameGradeOfPreInstalledVersionTest : public ApexServiceTest {
1494 public:
SetUp()1495 void SetUp() override {
1496 // TODO(b/136647373): Move this check to environment setup
1497 if (!android::base::GetBoolProperty("ro.apex.updatable", false)) {
1498 GTEST_SKIP() << "Skipping test because device doesn't support APEX";
1499 }
1500 ApexServiceTest::SetUp();
1501 ASSERT_NE(nullptr, service_.get());
1502
1503 installer_ = std::make_unique<PrepareTestApexForInstall>(
1504 GetTestFile("com.android.apex.cts.shim.apex"));
1505 if (!installer_->Prepare()) {
1506 return;
1507 }
1508 ASSERT_EQ("com.android.apex.cts.shim", installer_->package);
1509 // First deactivate currently active shim, otherwise activatePackage will be
1510 // no-op.
1511 {
1512 ApexInfo system_shim;
1513 ASSERT_TRUE(IsOk(service_->getActivePackage("com.android.apex.cts.shim",
1514 &system_shim)));
1515 ASSERT_TRUE(IsOk(service_->deactivatePackage(system_shim.modulePath)));
1516 }
1517 ASSERT_TRUE(IsOk(service_->stagePackages({installer_->test_file})));
1518 ASSERT_TRUE(
1519 IsOk(service_->activatePackage(installer_->test_installed_file)));
1520 }
1521
TearDown()1522 void TearDown() override {
1523 // Attempt to deactivate.
1524 service_->deactivatePackage(installer_->test_installed_file);
1525 installer_.reset();
1526 // ApexServiceTest::TearDown will wipe out everything under /data/apex.
1527 // Since some of that information is required for deactivatePackage binder
1528 // call, it's required to be called after deactivating package.
1529 ApexServiceTest::TearDown();
1530 ASSERT_TRUE(IsOk(service_->activatePackage(
1531 "/system/apex/com.android.apex.cts.shim.apex")));
1532 }
1533
1534 std::unique_ptr<PrepareTestApexForInstall> installer_;
1535 };
1536
TEST_F(ApexSameGradeOfPreInstalledVersionTest,VersionOnDataWins)1537 TEST_F(ApexSameGradeOfPreInstalledVersionTest, VersionOnDataWins) {
1538 std::vector<ApexInfo> all;
1539 ASSERT_TRUE(IsOk(service_->getAllPackages(&all)));
1540
1541 ApexInfo on_data;
1542 on_data.moduleName = "com.android.apex.cts.shim";
1543 on_data.modulePath = "/data/apex/active/com.android.apex.cts.shim@1.apex";
1544 on_data.preinstalledModulePath =
1545 "/system/apex/com.android.apex.cts.shim.apex";
1546 on_data.versionCode = 1;
1547 on_data.isFactory = false;
1548 on_data.isActive = true;
1549
1550 ApexInfo preinstalled;
1551 preinstalled.moduleName = "com.android.apex.cts.shim";
1552 preinstalled.modulePath = "/system/apex/com.android.apex.cts.shim.apex";
1553 preinstalled.preinstalledModulePath =
1554 "/system/apex/com.android.apex.cts.shim.apex";
1555 preinstalled.versionCode = 1;
1556 preinstalled.isFactory = true;
1557 preinstalled.isActive = false;
1558
1559 ASSERT_THAT(all, Contains(ApexInfoEq(on_data)));
1560 ASSERT_THAT(all, Contains(ApexInfoEq(preinstalled)));
1561 }
1562
1563 class ApexServiceDeactivationTest : public ApexServiceActivationSuccessTest {
1564 public:
SetUp()1565 void SetUp() override {
1566 ApexServiceActivationSuccessTest::SetUp();
1567
1568 ASSERT_TRUE(installer_ != nullptr);
1569 }
1570
TearDown()1571 void TearDown() override {
1572 installer_.reset();
1573 ApexServiceActivationSuccessTest::TearDown();
1574 }
1575
1576 std::unique_ptr<PrepareTestApexForInstall> installer_;
1577 };
1578
TEST_F(ApexServiceActivationSuccessTest,DmDeviceTearDown)1579 TEST_F(ApexServiceActivationSuccessTest, DmDeviceTearDown) {
1580 std::string package_id =
1581 installer_->package + "@" + std::to_string(installer_->version);
1582
1583 auto find_fn = [](const std::string& name) {
1584 auto& dm = DeviceMapper::Instance();
1585 std::vector<DeviceMapper::DmBlockDevice> devices;
1586 if (!dm.GetAvailableDevices(&devices)) {
1587 return Result<bool>(Errorf("GetAvailableDevices failed"));
1588 }
1589 for (const auto& device : devices) {
1590 if (device.name() == name) {
1591 return Result<bool>(true);
1592 }
1593 }
1594 return Result<bool>(false);
1595 };
1596
1597 #define ASSERT_FIND(type) \
1598 { \
1599 Result<bool> res = find_fn(package_id); \
1600 ASSERT_RESULT_OK(res); \
1601 ASSERT_##type(*res); \
1602 }
1603
1604 ASSERT_FIND(FALSE);
1605
1606 ASSERT_TRUE(IsOk(service_->activatePackage(installer_->test_installed_file)))
1607 << GetDebugStr(installer_.get());
1608
1609 ASSERT_FIND(TRUE);
1610
1611 ASSERT_TRUE(
1612 IsOk(service_->deactivatePackage(installer_->test_installed_file)));
1613
1614 ASSERT_FIND(FALSE);
1615
1616 installer_.reset(); // Skip TearDown deactivatePackage.
1617 }
1618
TEST_F(ApexServiceActivationSuccessTest,DeactivateFreesLoopDevices)1619 TEST_F(ApexServiceActivationSuccessTest, DeactivateFreesLoopDevices) {
1620 ASSERT_TRUE(IsOk(service_->activatePackage(installer_->test_installed_file)))
1621 << GetDebugStr(installer_.get());
1622
1623 std::string package_id =
1624 installer_->package + "@" + std::to_string(installer_->version);
1625 std::vector<std::string> slaves = ListSlavesOfDmDevice(package_id);
1626 ASSERT_EQ(1u, slaves.size())
1627 << "Unexpected number of slaves: " << Join(slaves, ",");
1628 const std::string& loop = slaves[0];
1629
1630 ASSERT_TRUE(
1631 IsOk(service_->deactivatePackage(installer_->test_installed_file)));
1632
1633 struct loop_info li;
1634 unique_fd fd(TEMP_FAILURE_RETRY(open(loop.c_str(), O_RDWR | O_CLOEXEC)));
1635 ASSERT_NE(-1, fd.get()) << "Failed to open " << loop << " : "
1636 << strerror(errno);
1637 ASSERT_EQ(-1, ioctl(fd.get(), LOOP_GET_STATUS, &li))
1638 << loop << " is still alive";
1639 ASSERT_EQ(ENXIO, errno) << "Unexpected errno : " << strerror(errno);
1640
1641 installer_.reset(); // Skip TearDown deactivatePackage.
1642 }
1643
1644 class ApexServicePrePostInstallTest : public ApexServiceTest {
1645 public:
1646 template <typename Fn>
RunPrePost(Fn fn,const std::vector<std::string> & apex_names,const char * test_message,bool expect_success=true)1647 void RunPrePost(Fn fn, const std::vector<std::string>& apex_names,
1648 const char* test_message, bool expect_success = true) {
1649 // Using unique_ptr is just the easiest here.
1650 using InstallerUPtr = std::unique_ptr<PrepareTestApexForInstall>;
1651 std::vector<InstallerUPtr> installers;
1652 std::vector<std::string> pkgs;
1653
1654 for (const std::string& apex_name : apex_names) {
1655 InstallerUPtr installer(
1656 new PrepareTestApexForInstall(GetTestFile(apex_name)));
1657 if (!installer->Prepare()) {
1658 return;
1659 }
1660 pkgs.push_back(installer->test_file);
1661 installers.emplace_back(std::move(installer));
1662 }
1663 android::binder::Status st = (service_.get()->*fn)(pkgs);
1664 if (expect_success) {
1665 ASSERT_TRUE(IsOk(st));
1666 } else {
1667 ASSERT_FALSE(IsOk(st));
1668 }
1669
1670 if (test_message != nullptr) {
1671 std::string logcat = GetLogcat();
1672 EXPECT_THAT(logcat, HasSubstr(test_message));
1673 }
1674
1675 // Ensure that the package is neither active nor mounted.
1676 for (const InstallerUPtr& installer : installers) {
1677 Result<bool> active = IsActive(installer->package, installer->version,
1678 installer->test_file);
1679 ASSERT_TRUE(IsOk(active));
1680 EXPECT_FALSE(*active);
1681 }
1682 for (const InstallerUPtr& installer : installers) {
1683 Result<ApexFile> apex = ApexFile::Open(installer->test_input);
1684 ASSERT_TRUE(IsOk(apex));
1685 std::string path =
1686 apexd_private::GetPackageMountPoint(apex->GetManifest());
1687 std::string entry = std::string("[dir]").append(path);
1688 std::vector<std::string> slash_apex = ListDir(kApexRoot);
1689 auto it = std::find(slash_apex.begin(), slash_apex.end(), entry);
1690 EXPECT_TRUE(it == slash_apex.end()) << Join(slash_apex, ',');
1691 }
1692 }
1693 };
1694
TEST_F(ApexServicePrePostInstallTest,Preinstall)1695 TEST_F(ApexServicePrePostInstallTest, Preinstall) {
1696 RunPrePost(&IApexService::preinstallPackages,
1697 {"apex.apexd_test_preinstall.apex"}, "sh : PreInstall Test");
1698 }
1699
TEST_F(ApexServicePrePostInstallTest,MultiPreinstall)1700 TEST_F(ApexServicePrePostInstallTest, MultiPreinstall) {
1701 constexpr const char* kLogcatText =
1702 "sh : /apex/com.android.apex.test_package/etc/sample_prebuilt_file";
1703 RunPrePost(&IApexService::preinstallPackages,
1704 {"apex.apexd_test_preinstall.apex", "apex.apexd_test.apex"},
1705 kLogcatText);
1706 }
1707
TEST_F(ApexServicePrePostInstallTest,PreinstallFail)1708 TEST_F(ApexServicePrePostInstallTest, PreinstallFail) {
1709 RunPrePost(&IApexService::preinstallPackages,
1710 {"apex.apexd_test_prepostinstall.fail.apex"},
1711 /* test_message= */ nullptr, /* expect_success= */ false);
1712 }
1713
TEST_F(ApexServicePrePostInstallTest,Postinstall)1714 TEST_F(ApexServicePrePostInstallTest, Postinstall) {
1715 RunPrePost(&IApexService::postinstallPackages,
1716 {"apex.apexd_test_postinstall.apex"},
1717 "sh : PostInstall Test");
1718 }
1719
TEST_F(ApexServicePrePostInstallTest,MultiPostinstall)1720 TEST_F(ApexServicePrePostInstallTest, MultiPostinstall) {
1721 constexpr const char* kLogcatText =
1722 "sh : /apex/com.android.apex.test_package/etc/sample_prebuilt_file";
1723 RunPrePost(&IApexService::postinstallPackages,
1724 {"apex.apexd_test_postinstall.apex", "apex.apexd_test.apex"},
1725 kLogcatText);
1726 }
1727
TEST_F(ApexServicePrePostInstallTest,PostinstallFail)1728 TEST_F(ApexServicePrePostInstallTest, PostinstallFail) {
1729 RunPrePost(&IApexService::postinstallPackages,
1730 {"apex.apexd_test_prepostinstall.fail.apex"},
1731 /* test_message= */ nullptr, /* expect_success= */ false);
1732 }
1733
TEST_F(ApexServiceTest,SubmitSingleSessionTestSuccess)1734 TEST_F(ApexServiceTest, SubmitSingleSessionTestSuccess) {
1735 PrepareTestApexForInstall installer(GetTestFile("apex.apexd_test.apex"),
1736 "/data/app-staging/session_123",
1737 "staging_data_file");
1738 if (!installer.Prepare()) {
1739 FAIL() << GetDebugStr(&installer);
1740 }
1741
1742 ApexInfoList list;
1743 ApexSessionParams params;
1744 params.sessionId = 123;
1745 ASSERT_TRUE(IsOk(service_->submitStagedSession(params, &list)))
1746 << GetDebugStr(&installer);
1747 EXPECT_EQ(1u, list.apexInfos.size());
1748 ApexInfo match;
1749 for (const ApexInfo& info : list.apexInfos) {
1750 if (info.moduleName == installer.package) {
1751 match = info;
1752 break;
1753 }
1754 }
1755
1756 ASSERT_EQ(installer.package, match.moduleName);
1757 ASSERT_EQ(installer.version, static_cast<uint64_t>(match.versionCode));
1758 ASSERT_EQ(installer.test_file, match.modulePath);
1759
1760 ApexSessionInfo session;
1761 ASSERT_TRUE(IsOk(service_->getStagedSessionInfo(123, &session)))
1762 << GetDebugStr(&installer);
1763 ApexSessionInfo expected = CreateSessionInfo(123);
1764 expected.isVerified = true;
1765 EXPECT_THAT(session, SessionInfoEq(expected));
1766
1767 ASSERT_TRUE(IsOk(service_->markStagedSessionReady(123)));
1768 ASSERT_TRUE(IsOk(service_->getStagedSessionInfo(123, &session)))
1769 << GetDebugStr(&installer);
1770 expected.isVerified = false;
1771 expected.isStaged = true;
1772 EXPECT_THAT(session, SessionInfoEq(expected));
1773
1774 // Call markStagedSessionReady again. Should be a no-op.
1775 ASSERT_TRUE(IsOk(service_->markStagedSessionReady(123)))
1776 << GetDebugStr(&installer);
1777
1778 ASSERT_TRUE(IsOk(service_->getStagedSessionInfo(123, &session)))
1779 << GetDebugStr(&installer);
1780 EXPECT_THAT(session, SessionInfoEq(expected));
1781
1782 // See if the session is reported with getSessions() as well
1783 std::vector<ApexSessionInfo> sessions;
1784 ASSERT_TRUE(IsOk(service_->getSessions(&sessions)))
1785 << GetDebugStr(&installer);
1786 ASSERT_THAT(sessions, UnorderedElementsAre(SessionInfoEq(expected)));
1787 }
1788
TEST_F(ApexServiceTest,SubmitSingleStagedSessionKeepsPreviousSessions)1789 TEST_F(ApexServiceTest, SubmitSingleStagedSessionKeepsPreviousSessions) {
1790 PrepareTestApexForInstall installer(GetTestFile("apex.apexd_test.apex"),
1791 "/data/app-staging/session_239",
1792 "staging_data_file");
1793 if (!installer.Prepare()) {
1794 FAIL() << GetDebugStr(&installer);
1795 }
1796
1797 // First simulate existence of a bunch of sessions.
1798 auto session1 = ApexSession::CreateSession(37);
1799 ASSERT_TRUE(IsOk(session1));
1800 auto session2 = ApexSession::CreateSession(57);
1801 ASSERT_TRUE(IsOk(session2));
1802 auto session3 = ApexSession::CreateSession(73);
1803 ASSERT_TRUE(IsOk(session3));
1804 ASSERT_TRUE(IsOk(session1->UpdateStateAndCommit(SessionState::VERIFIED)));
1805 ASSERT_TRUE(IsOk(session2->UpdateStateAndCommit(SessionState::STAGED)));
1806 ASSERT_TRUE(IsOk(session3->UpdateStateAndCommit(SessionState::SUCCESS)));
1807
1808 std::vector<ApexSessionInfo> sessions;
1809 ASSERT_TRUE(IsOk(service_->getSessions(&sessions)));
1810
1811 ApexSessionInfo expected_session1 = CreateSessionInfo(37);
1812 expected_session1.isVerified = true;
1813 ApexSessionInfo expected_session2 = CreateSessionInfo(57);
1814 expected_session2.isStaged = true;
1815 ApexSessionInfo expected_session3 = CreateSessionInfo(73);
1816 expected_session3.isSuccess = true;
1817 ASSERT_THAT(sessions, UnorderedElementsAre(SessionInfoEq(expected_session1),
1818 SessionInfoEq(expected_session2),
1819 SessionInfoEq(expected_session3)));
1820
1821 ApexInfoList list;
1822 ApexSessionParams params;
1823 params.sessionId = 239;
1824 ASSERT_TRUE(IsOk(service_->submitStagedSession(params, &list)));
1825
1826 sessions.clear();
1827 ASSERT_TRUE(IsOk(service_->getSessions(&sessions)));
1828
1829 ApexSessionInfo new_session = CreateSessionInfo(239);
1830 new_session.isVerified = true;
1831 ASSERT_THAT(sessions, UnorderedElementsAre(SessionInfoEq(new_session),
1832 SessionInfoEq(expected_session1),
1833 SessionInfoEq(expected_session2),
1834 SessionInfoEq(expected_session3)));
1835 }
1836
TEST_F(ApexServiceTest,SubmitSingleSessionTestFail)1837 TEST_F(ApexServiceTest, SubmitSingleSessionTestFail) {
1838 PrepareTestApexForInstall installer(
1839 GetTestFile("apex.apexd_test_corrupt_apex.apex"),
1840 "/data/app-staging/session_456", "staging_data_file");
1841 if (!installer.Prepare()) {
1842 FAIL() << GetDebugStr(&installer);
1843 }
1844
1845 ApexInfoList list;
1846 ApexSessionParams params;
1847 params.sessionId = 456;
1848 ASSERT_FALSE(IsOk(service_->submitStagedSession(params, &list)))
1849 << GetDebugStr(&installer);
1850
1851 ApexSessionInfo session;
1852 ASSERT_TRUE(IsOk(service_->getStagedSessionInfo(456, &session)))
1853 << GetDebugStr(&installer);
1854 ApexSessionInfo expected = CreateSessionInfo(-1);
1855 expected.isUnknown = true;
1856 EXPECT_THAT(session, SessionInfoEq(expected));
1857 }
1858
TEST_F(ApexServiceTest,SubmitMultiSessionTestSuccess)1859 TEST_F(ApexServiceTest, SubmitMultiSessionTestSuccess) {
1860 // Parent session id: 10
1861 // Children session ids: 20 30
1862 PrepareTestApexForInstall installer(GetTestFile("apex.apexd_test.apex"),
1863 "/data/app-staging/session_20",
1864 "staging_data_file");
1865 PrepareTestApexForInstall installer2(
1866 GetTestFile("apex.apexd_test_different_app.apex"),
1867 "/data/app-staging/session_30", "staging_data_file");
1868 if (!installer.Prepare() || !installer2.Prepare()) {
1869 FAIL() << GetDebugStr(&installer) << GetDebugStr(&installer2);
1870 }
1871
1872 ApexInfoList list;
1873 ApexSessionParams params;
1874 params.sessionId = 10;
1875 params.childSessionIds = {20, 30};
1876 ASSERT_TRUE(IsOk(service_->submitStagedSession(params, &list)))
1877 << GetDebugStr(&installer);
1878 EXPECT_EQ(2u, list.apexInfos.size());
1879 ApexInfo match;
1880 bool package1_found = false;
1881 bool package2_found = false;
1882 for (const ApexInfo& info : list.apexInfos) {
1883 if (info.moduleName == installer.package) {
1884 ASSERT_EQ(installer.package, info.moduleName);
1885 ASSERT_EQ(installer.version, static_cast<uint64_t>(info.versionCode));
1886 ASSERT_EQ(installer.test_file, info.modulePath);
1887 package1_found = true;
1888 } else if (info.moduleName == installer2.package) {
1889 ASSERT_EQ(installer2.package, info.moduleName);
1890 ASSERT_EQ(installer2.version, static_cast<uint64_t>(info.versionCode));
1891 ASSERT_EQ(installer2.test_file, info.modulePath);
1892 package2_found = true;
1893 } else {
1894 FAIL() << "Unexpected package found " << info.moduleName
1895 << GetDebugStr(&installer) << GetDebugStr(&installer2);
1896 }
1897 }
1898 ASSERT_TRUE(package1_found);
1899 ASSERT_TRUE(package2_found);
1900
1901 ApexSessionInfo session;
1902 ASSERT_TRUE(IsOk(service_->getStagedSessionInfo(10, &session)))
1903 << GetDebugStr(&installer);
1904 ApexSessionInfo expected = CreateSessionInfo(10);
1905 expected.isVerified = true;
1906 ASSERT_THAT(session, SessionInfoEq(expected));
1907
1908 ASSERT_TRUE(IsOk(service_->markStagedSessionReady(10)))
1909 << GetDebugStr(&installer);
1910
1911 ASSERT_TRUE(IsOk(service_->getStagedSessionInfo(10, &session)))
1912 << GetDebugStr(&installer);
1913 expected.isVerified = false;
1914 expected.isStaged = true;
1915 ASSERT_THAT(session, SessionInfoEq(expected));
1916
1917 // Check that temp mounts were cleanded up.
1918 for (const auto& mount : GetApexMounts()) {
1919 EXPECT_FALSE(EndsWith(mount, ".tmp")) << "Found temp mount " << mount;
1920 }
1921 }
1922
TEST_F(ApexServiceTest,SubmitMultiSessionTestFail)1923 TEST_F(ApexServiceTest, SubmitMultiSessionTestFail) {
1924 // Parent session id: 11
1925 // Children session ids: 21 31
1926 PrepareTestApexForInstall installer(GetTestFile("apex.apexd_test.apex"),
1927 "/data/app-staging/session_21",
1928 "staging_data_file");
1929 PrepareTestApexForInstall installer2(
1930 GetTestFile("apex.apexd_test_corrupt_apex.apex"),
1931 "/data/app-staging/session_31", "staging_data_file");
1932 if (!installer.Prepare() || !installer2.Prepare()) {
1933 FAIL() << GetDebugStr(&installer) << GetDebugStr(&installer2);
1934 }
1935 ApexInfoList list;
1936 ApexSessionParams params;
1937 params.sessionId = 11;
1938 params.childSessionIds = {21, 31};
1939 ASSERT_FALSE(IsOk(service_->submitStagedSession(params, &list)))
1940 << GetDebugStr(&installer);
1941 }
1942
TEST_F(ApexServiceTest,MarkStagedSessionReadyFail)1943 TEST_F(ApexServiceTest, MarkStagedSessionReadyFail) {
1944 // We should fail if we ask information about a session we don't know.
1945 ASSERT_FALSE(IsOk(service_->markStagedSessionReady(666)));
1946
1947 ApexSessionInfo session;
1948 ASSERT_TRUE(IsOk(service_->getStagedSessionInfo(666, &session)));
1949 ApexSessionInfo expected = CreateSessionInfo(-1);
1950 expected.isUnknown = true;
1951 ASSERT_THAT(session, SessionInfoEq(expected));
1952 }
1953
TEST_F(ApexServiceTest,MarkStagedSessionSuccessfulFailsNoSession)1954 TEST_F(ApexServiceTest, MarkStagedSessionSuccessfulFailsNoSession) {
1955 ASSERT_FALSE(IsOk(service_->markStagedSessionSuccessful(37)));
1956
1957 ApexSessionInfo session_info;
1958 ASSERT_TRUE(IsOk(service_->getStagedSessionInfo(37, &session_info)));
1959 ApexSessionInfo expected = CreateSessionInfo(-1);
1960 expected.isUnknown = true;
1961 ASSERT_THAT(session_info, SessionInfoEq(expected));
1962 }
1963
TEST_F(ApexServiceTest,MarkStagedSessionSuccessfulFailsSessionInWrongState)1964 TEST_F(ApexServiceTest, MarkStagedSessionSuccessfulFailsSessionInWrongState) {
1965 auto session = ApexSession::CreateSession(73);
1966 ASSERT_TRUE(IsOk(session));
1967 ASSERT_TRUE(
1968 IsOk(session->UpdateStateAndCommit(::apex::proto::SessionState::STAGED)));
1969
1970 ASSERT_FALSE(IsOk(service_->markStagedSessionSuccessful(73)));
1971
1972 ApexSessionInfo session_info;
1973 ASSERT_TRUE(IsOk(service_->getStagedSessionInfo(73, &session_info)));
1974 ApexSessionInfo expected = CreateSessionInfo(73);
1975 expected.isStaged = true;
1976 ASSERT_THAT(session_info, SessionInfoEq(expected));
1977 }
1978
TEST_F(ApexServiceTest,MarkStagedSessionSuccessfulActivatedSession)1979 TEST_F(ApexServiceTest, MarkStagedSessionSuccessfulActivatedSession) {
1980 auto session = ApexSession::CreateSession(239);
1981 ASSERT_TRUE(IsOk(session));
1982 ASSERT_TRUE(IsOk(
1983 session->UpdateStateAndCommit(::apex::proto::SessionState::ACTIVATED)));
1984
1985 ASSERT_TRUE(IsOk(service_->markStagedSessionSuccessful(239)));
1986
1987 ApexSessionInfo session_info;
1988 ASSERT_TRUE(IsOk(service_->getStagedSessionInfo(239, &session_info)));
1989 ApexSessionInfo expected = CreateSessionInfo(239);
1990 expected.isSuccess = true;
1991 ASSERT_THAT(session_info, SessionInfoEq(expected));
1992 }
1993
TEST_F(ApexServiceTest,MarkStagedSessionSuccessfulNoOp)1994 TEST_F(ApexServiceTest, MarkStagedSessionSuccessfulNoOp) {
1995 auto session = ApexSession::CreateSession(1543);
1996 ASSERT_TRUE(IsOk(session));
1997 ASSERT_TRUE(IsOk(
1998 session->UpdateStateAndCommit(::apex::proto::SessionState::SUCCESS)));
1999
2000 ASSERT_TRUE(IsOk(service_->markStagedSessionSuccessful(1543)));
2001
2002 ApexSessionInfo session_info;
2003 ASSERT_TRUE(IsOk(service_->getStagedSessionInfo(1543, &session_info)));
2004 ApexSessionInfo expected = CreateSessionInfo(1543);
2005 expected.isSuccess = true;
2006 ASSERT_THAT(session_info, SessionInfoEq(expected));
2007 }
2008
2009 // Should be able to abort individual staged session
TEST_F(ApexServiceTest,AbortStagedSession)2010 TEST_F(ApexServiceTest, AbortStagedSession) {
2011 auto session1 = ApexSession::CreateSession(239);
2012 ASSERT_TRUE(IsOk(session1->UpdateStateAndCommit(SessionState::VERIFIED)));
2013 auto session2 = ApexSession::CreateSession(240);
2014 ASSERT_TRUE(IsOk(session2->UpdateStateAndCommit(SessionState::STAGED)));
2015
2016 std::vector<ApexSessionInfo> sessions;
2017 ASSERT_TRUE(IsOk(service_->getSessions(&sessions)));
2018 ASSERT_EQ(2u, sessions.size());
2019
2020 ASSERT_TRUE(IsOk(service_->abortStagedSession(239)));
2021
2022 sessions.clear();
2023 ASSERT_TRUE(IsOk(service_->getSessions(&sessions)));
2024 ApexSessionInfo expected = CreateSessionInfo(240);
2025 expected.isStaged = true;
2026 ASSERT_THAT(sessions, UnorderedElementsAre(SessionInfoEq(expected)));
2027 }
2028
2029 // abortStagedSession should not abort activated session
TEST_F(ApexServiceTest,AbortStagedSessionActivatedFail)2030 TEST_F(ApexServiceTest, AbortStagedSessionActivatedFail) {
2031 auto session1 = ApexSession::CreateSession(239);
2032 ASSERT_TRUE(IsOk(session1->UpdateStateAndCommit(SessionState::ACTIVATED)));
2033 auto session2 = ApexSession::CreateSession(240);
2034 ASSERT_TRUE(IsOk(session2->UpdateStateAndCommit(SessionState::STAGED)));
2035
2036 std::vector<ApexSessionInfo> sessions;
2037 ASSERT_TRUE(IsOk(service_->getSessions(&sessions)));
2038 ASSERT_EQ(2u, sessions.size());
2039
2040 ASSERT_FALSE(IsOk(service_->abortStagedSession(239)));
2041
2042 sessions.clear();
2043 ASSERT_TRUE(IsOk(service_->getSessions(&sessions)));
2044 ApexSessionInfo expected1 = CreateSessionInfo(239);
2045 expected1.isActivated = true;
2046 ApexSessionInfo expected2 = CreateSessionInfo(240);
2047 expected2.isStaged = true;
2048 ASSERT_THAT(sessions, UnorderedElementsAre(SessionInfoEq(expected1),
2049 SessionInfoEq(expected2)));
2050 }
2051
2052 // Only finalized sessions should be deleted on DeleteFinalizedSessions()
TEST_F(ApexServiceTest,DeleteFinalizedSessions)2053 TEST_F(ApexServiceTest, DeleteFinalizedSessions) {
2054 // Fetch list of all session state
2055 std::vector<SessionState::State> states;
2056 for (int i = SessionState::State_MIN; i < SessionState::State_MAX; i++) {
2057 if (!SessionState::State_IsValid(i)) {
2058 continue;
2059 }
2060 states.push_back(SessionState::State(i));
2061 }
2062
2063 // For every session state, create a new session. This is to verify we only
2064 // delete sessions in final state.
2065 auto nonFinalSessions = 0u;
2066 for (auto i = 0u; i < states.size(); i++) {
2067 auto session = ApexSession::CreateSession(230 + i);
2068 SessionState::State state = states[i];
2069 ASSERT_TRUE(IsOk(session->UpdateStateAndCommit(state)));
2070 if (!session->IsFinalized()) {
2071 nonFinalSessions++;
2072 }
2073 }
2074 std::vector<ApexSession> sessions = ApexSession::GetSessions();
2075 ASSERT_EQ(states.size(), sessions.size());
2076
2077 // Now try cleaning up all finalized sessions
2078 ApexSession::DeleteFinalizedSessions();
2079 sessions = ApexSession::GetSessions();
2080 ASSERT_EQ(nonFinalSessions, sessions.size());
2081
2082 // Verify only finalized sessions have been deleted
2083 for (auto& session : sessions) {
2084 ASSERT_FALSE(session.IsFinalized());
2085 }
2086 }
2087
TEST_F(ApexServiceTest,BackupActivePackages)2088 TEST_F(ApexServiceTest, BackupActivePackages) {
2089 if (supports_fs_checkpointing_) {
2090 GTEST_SKIP() << "Can't run if filesystem checkpointing is enabled";
2091 }
2092 PrepareTestApexForInstall installer1(GetTestFile("apex.apexd_test.apex"));
2093 PrepareTestApexForInstall installer2(
2094 GetTestFile("apex.apexd_test_different_app.apex"));
2095 PrepareTestApexForInstall installer3(GetTestFile("apex.apexd_test_v2.apex"),
2096 "/data/app-staging/session_23",
2097 "staging_data_file");
2098
2099 if (!installer1.Prepare() || !installer2.Prepare() || !installer3.Prepare()) {
2100 return;
2101 }
2102
2103 // Activate some packages, in order to backup them later.
2104 std::vector<std::string> pkgs = {installer1.test_file, installer2.test_file};
2105 ASSERT_TRUE(IsOk(service_->stagePackages(pkgs)));
2106
2107 // Make sure that /data/apex/active has activated packages.
2108 auto active_pkgs = ReadEntireDir(kActiveApexPackagesDataDir);
2109 ASSERT_TRUE(IsOk(active_pkgs));
2110 ASSERT_THAT(*active_pkgs,
2111 UnorderedElementsAre(installer1.test_installed_file,
2112 installer2.test_installed_file));
2113
2114 ApexInfoList list;
2115 ApexSessionParams params;
2116 params.sessionId = 23;
2117 ASSERT_TRUE(IsOk(service_->submitStagedSession(params, &list)));
2118
2119 auto backups = ReadEntireDir(kApexBackupDir);
2120 ASSERT_TRUE(IsOk(backups));
2121 auto backup1 =
2122 StringPrintf("%s/com.android.apex.test_package@1.apex", kApexBackupDir);
2123 auto backup2 =
2124 StringPrintf("%s/com.android.apex.test_package_2@1.apex", kApexBackupDir);
2125 ASSERT_THAT(*backups, UnorderedElementsAre(backup1, backup2));
2126 }
2127
TEST_F(ApexServiceTest,BackupActivePackagesClearsPreviousBackup)2128 TEST_F(ApexServiceTest, BackupActivePackagesClearsPreviousBackup) {
2129 if (supports_fs_checkpointing_) {
2130 GTEST_SKIP() << "Can't run if filesystem checkpointing is enabled";
2131 }
2132 PrepareTestApexForInstall installer1(GetTestFile("apex.apexd_test.apex"));
2133 PrepareTestApexForInstall installer2(
2134 GetTestFile("apex.apexd_test_different_app.apex"));
2135 PrepareTestApexForInstall installer3(GetTestFile("apex.apexd_test_v2.apex"),
2136 "/data/app-staging/session_43",
2137 "staging_data_file");
2138
2139 if (!installer1.Prepare() || !installer2.Prepare() || !installer3.Prepare()) {
2140 return;
2141 }
2142
2143 // Make sure /data/apex/backups exists.
2144 ASSERT_TRUE(IsOk(CreateDirIfNeeded(std::string(kApexBackupDir), 0700)));
2145 // Create some bogus files in /data/apex/backups.
2146 std::ofstream old_backup(StringPrintf("%s/file1", kApexBackupDir));
2147 ASSERT_TRUE(old_backup.good());
2148 old_backup.close();
2149
2150 std::vector<std::string> pkgs = {installer1.test_file, installer2.test_file};
2151 ASSERT_TRUE(IsOk(service_->stagePackages(pkgs)));
2152
2153 // Make sure that /data/apex/active has activated packages.
2154 auto active_pkgs = ReadEntireDir(kActiveApexPackagesDataDir);
2155 ASSERT_TRUE(IsOk(active_pkgs));
2156 ASSERT_THAT(*active_pkgs,
2157 UnorderedElementsAre(installer1.test_installed_file,
2158 installer2.test_installed_file));
2159
2160 ApexInfoList list;
2161 ApexSessionParams params;
2162 params.sessionId = 43;
2163 ASSERT_TRUE(IsOk(service_->submitStagedSession(params, &list)));
2164
2165 auto backups = ReadEntireDir(kApexBackupDir);
2166 ASSERT_TRUE(IsOk(backups));
2167 auto backup1 =
2168 StringPrintf("%s/com.android.apex.test_package@1.apex", kApexBackupDir);
2169 auto backup2 =
2170 StringPrintf("%s/com.android.apex.test_package_2@1.apex", kApexBackupDir);
2171 ASSERT_THAT(*backups, UnorderedElementsAre(backup1, backup2));
2172 }
2173
TEST_F(ApexServiceTest,BackupActivePackagesZeroActivePackages)2174 TEST_F(ApexServiceTest, BackupActivePackagesZeroActivePackages) {
2175 if (supports_fs_checkpointing_) {
2176 GTEST_SKIP() << "Can't run if filesystem checkpointing is enabled";
2177 }
2178 PrepareTestApexForInstall installer(GetTestFile("apex.apexd_test_v2.apex"),
2179 "/data/app-staging/session_41",
2180 "staging_data_file");
2181
2182 if (!installer.Prepare()) {
2183 return;
2184 }
2185
2186 // Make sure that /data/apex/active exists and is empty
2187 ASSERT_TRUE(
2188 IsOk(CreateDirIfNeeded(std::string(kActiveApexPackagesDataDir), 0755)));
2189 auto active_pkgs = ReadEntireDir(kActiveApexPackagesDataDir);
2190 ASSERT_TRUE(IsOk(active_pkgs));
2191 ASSERT_EQ(0u, active_pkgs->size());
2192
2193 ApexInfoList list;
2194 ApexSessionParams params;
2195 params.sessionId = 41;
2196 ASSERT_TRUE(IsOk(service_->submitStagedSession(params, &list)));
2197
2198 auto backups = ReadEntireDir(kApexBackupDir);
2199 ASSERT_TRUE(IsOk(backups));
2200 ASSERT_EQ(0u, backups->size());
2201 }
2202
TEST_F(ApexServiceTest,ActivePackagesDirEmpty)2203 TEST_F(ApexServiceTest, ActivePackagesDirEmpty) {
2204 PrepareTestApexForInstall installer(GetTestFile("apex.apexd_test_v2.apex"),
2205 "/data/app-staging/session_41",
2206 "staging_data_file");
2207
2208 if (!installer.Prepare()) {
2209 return;
2210 }
2211
2212 // Make sure that /data/apex/active is empty
2213 DeleteDirContent(kActiveApexPackagesDataDir);
2214
2215 ApexInfoList list;
2216 ApexSessionParams params;
2217 params.sessionId = 41;
2218 ASSERT_TRUE(IsOk(service_->submitStagedSession(params, &list)));
2219
2220 if (!supports_fs_checkpointing_) {
2221 auto backups = ReadEntireDir(kApexBackupDir);
2222 ASSERT_TRUE(IsOk(backups));
2223 ASSERT_EQ(0u, backups->size());
2224 }
2225 }
2226
TEST_F(ApexServiceTest,UnstagePackagesSuccess)2227 TEST_F(ApexServiceTest, UnstagePackagesSuccess) {
2228 PrepareTestApexForInstall installer1(GetTestFile("apex.apexd_test.apex"));
2229 PrepareTestApexForInstall installer2(
2230 GetTestFile("apex.apexd_test_different_app.apex"));
2231
2232 if (!installer1.Prepare() || !installer2.Prepare()) {
2233 return;
2234 }
2235
2236 std::vector<std::string> pkgs = {installer1.test_file, installer2.test_file};
2237 ASSERT_TRUE(IsOk(service_->stagePackages(pkgs)));
2238
2239 pkgs = {installer2.test_installed_file};
2240 ASSERT_TRUE(IsOk(service_->unstagePackages(pkgs)));
2241
2242 auto active_packages = ReadEntireDir(kActiveApexPackagesDataDir);
2243 ASSERT_TRUE(IsOk(active_packages));
2244 ASSERT_THAT(*active_packages,
2245 UnorderedElementsAre(installer1.test_installed_file));
2246 }
2247
TEST_F(ApexServiceTest,UnstagePackagesFail)2248 TEST_F(ApexServiceTest, UnstagePackagesFail) {
2249 PrepareTestApexForInstall installer1(GetTestFile("apex.apexd_test.apex"));
2250 PrepareTestApexForInstall installer2(
2251 GetTestFile("apex.apexd_test_different_app.apex"));
2252
2253 if (!installer1.Prepare() || !installer2.Prepare()) {
2254 return;
2255 }
2256
2257 std::vector<std::string> pkgs = {installer1.test_file};
2258 ASSERT_TRUE(IsOk(service_->stagePackages(pkgs)));
2259
2260 pkgs = {installer1.test_installed_file, installer2.test_installed_file};
2261 ASSERT_FALSE(IsOk(service_->unstagePackages(pkgs)));
2262
2263 // Check that first package wasn't unstaged.
2264 auto active_packages = ReadEntireDir(kActiveApexPackagesDataDir);
2265 ASSERT_TRUE(IsOk(active_packages));
2266 ASSERT_THAT(*active_packages,
2267 UnorderedElementsAre(installer1.test_installed_file));
2268 }
2269
TEST_F(ApexServiceTest,UnstagePackagesFailPreInstalledApex)2270 TEST_F(ApexServiceTest, UnstagePackagesFailPreInstalledApex) {
2271 auto status = service_->unstagePackages(
2272 {"/system/apex/com.android.apex.cts.shim.apex"});
2273 ASSERT_FALSE(IsOk(status));
2274 const std::string& error_message =
2275 std::string(status.exceptionMessage().c_str());
2276 ASSERT_THAT(error_message,
2277 HasSubstr("Can't uninstall pre-installed apex "
2278 "/system/apex/com.android.apex.cts.shim.apex"));
2279 ASSERT_TRUE(RegularFileExists("/system/apex/com.android.apex.cts.shim.apex"));
2280 }
2281
2282 class ApexServiceRevertTest : public ApexServiceTest {
2283 protected:
SetUp()2284 void SetUp() override { ApexServiceTest::SetUp(); }
2285
PrepareBackup(const std::vector<std::string> & pkgs)2286 void PrepareBackup(const std::vector<std::string>& pkgs) {
2287 ASSERT_TRUE(IsOk(CreateDirIfNeeded(std::string(kApexBackupDir), 0700)));
2288 for (const auto& pkg : pkgs) {
2289 PrepareTestApexForInstall installer(pkg);
2290 ASSERT_TRUE(installer.Prepare()) << " failed to prepare " << pkg;
2291 const std::string& from = installer.test_file;
2292 std::string to = std::string(kApexBackupDir) + "/" + installer.package +
2293 "@" + std::to_string(installer.version) + ".apex";
2294 std::error_code ec;
2295 fs::copy(fs::path(from), fs::path(to),
2296 fs::copy_options::create_hard_links, ec);
2297 ASSERT_FALSE(ec) << "Failed to copy " << from << " to " << to << " : "
2298 << ec;
2299 }
2300 }
2301
CheckActiveApexContents(const std::vector<std::string> & expected_pkgs)2302 void CheckActiveApexContents(const std::vector<std::string>& expected_pkgs) {
2303 // First check that /data/apex/active exists and has correct permissions.
2304 struct stat sd;
2305 ASSERT_EQ(0, stat(kActiveApexPackagesDataDir, &sd));
2306 ASSERT_EQ(0755u, sd.st_mode & ALLPERMS);
2307
2308 // Now read content and check it contains expected values.
2309 auto active_pkgs = ReadEntireDir(kActiveApexPackagesDataDir);
2310 ASSERT_TRUE(IsOk(active_pkgs));
2311 ASSERT_THAT(*active_pkgs, UnorderedElementsAreArray(expected_pkgs));
2312 }
2313 };
2314
2315 // Should be able to revert activated sessions
TEST_F(ApexServiceRevertTest,RevertActiveSessionsSuccessful)2316 TEST_F(ApexServiceRevertTest, RevertActiveSessionsSuccessful) {
2317 if (supports_fs_checkpointing_) {
2318 GTEST_SKIP() << "Can't run if filesystem checkpointing is enabled";
2319 }
2320
2321 PrepareTestApexForInstall installer(GetTestFile("apex.apexd_test_v2.apex"));
2322 if (!installer.Prepare()) {
2323 return;
2324 }
2325
2326 auto session = ApexSession::CreateSession(1543);
2327 ASSERT_TRUE(IsOk(session));
2328 ASSERT_TRUE(IsOk(session->UpdateStateAndCommit(SessionState::ACTIVATED)));
2329
2330 // Make sure /data/apex/active is non-empty.
2331 ASSERT_TRUE(IsOk(service_->stagePackages({installer.test_file})));
2332
2333 PrepareBackup({GetTestFile("apex.apexd_test.apex")});
2334
2335 ASSERT_TRUE(IsOk(service_->revertActiveSessions()));
2336
2337 auto pkg = StringPrintf("%s/com.android.apex.test_package@1.apex",
2338 kActiveApexPackagesDataDir);
2339 SCOPED_TRACE("");
2340 CheckActiveApexContents({pkg});
2341 }
2342
2343 // Calling revertActiveSessions should not restore backup on checkpointing
2344 // devices
TEST_F(ApexServiceRevertTest,RevertActiveSessionsDoesNotRestoreBackupIfCheckpointingSupported)2345 TEST_F(ApexServiceRevertTest,
2346 RevertActiveSessionsDoesNotRestoreBackupIfCheckpointingSupported) {
2347 if (!supports_fs_checkpointing_) {
2348 GTEST_SKIP() << "Can't run if filesystem checkpointing is not supported";
2349 }
2350
2351 PrepareTestApexForInstall installer(GetTestFile("apex.apexd_test_v2.apex"));
2352 if (!installer.Prepare()) {
2353 return;
2354 }
2355
2356 auto session = ApexSession::CreateSession(1543);
2357 ASSERT_TRUE(IsOk(session));
2358 ASSERT_TRUE(IsOk(session->UpdateStateAndCommit(SessionState::ACTIVATED)));
2359
2360 // Make sure /data/apex/active is non-empty.
2361 ASSERT_TRUE(IsOk(service_->stagePackages({installer.test_file})));
2362
2363 PrepareBackup({GetTestFile("apex.apexd_test.apex")});
2364
2365 ASSERT_TRUE(IsOk(service_->revertActiveSessions()));
2366
2367 // Check that active apexes were not reverted.
2368 auto pkg = StringPrintf("%s/com.android.apex.test_package@2.apex",
2369 kActiveApexPackagesDataDir);
2370 SCOPED_TRACE("");
2371 CheckActiveApexContents({pkg});
2372 }
2373
2374 // Should fail to revert active sessions when there are none
TEST_F(ApexServiceRevertTest,RevertActiveSessionsWithoutActiveSessions)2375 TEST_F(ApexServiceRevertTest, RevertActiveSessionsWithoutActiveSessions) {
2376 // This test simulates a situation that should never happen on user builds:
2377 // revertActiveSessions was called, but there were no active sessions.
2378 PrepareTestApexForInstall installer(GetTestFile("apex.apexd_test_v2.apex"));
2379 if (!installer.Prepare()) {
2380 return;
2381 }
2382
2383 // Make sure /data/apex/active is non-empty.
2384 ASSERT_TRUE(IsOk(service_->stagePackages({installer.test_file})));
2385
2386 PrepareBackup({GetTestFile("apex.apexd_test.apex")});
2387
2388 // Even though backup is there, no sessions are active, hence revert request
2389 // should fail.
2390 ASSERT_FALSE(IsOk(service_->revertActiveSessions()));
2391 }
2392
TEST_F(ApexServiceRevertTest,RevertFailsNoBackupFolder)2393 TEST_F(ApexServiceRevertTest, RevertFailsNoBackupFolder) {
2394 ASSERT_FALSE(IsOk(service_->revertActiveSessions()));
2395 }
2396
TEST_F(ApexServiceRevertTest,RevertFailsNoActivePackagesFolder)2397 TEST_F(ApexServiceRevertTest, RevertFailsNoActivePackagesFolder) {
2398 PrepareTestApexForInstall installer(GetTestFile("apex.apexd_test.apex"));
2399 ASSERT_FALSE(IsOk(service_->revertActiveSessions()));
2400 }
2401
TEST_F(ApexServiceRevertTest,MarkStagedSessionSuccessfulCleanupBackup)2402 TEST_F(ApexServiceRevertTest, MarkStagedSessionSuccessfulCleanupBackup) {
2403 PrepareBackup({GetTestFile("apex.apexd_test.apex"),
2404 GetTestFile("apex.apexd_test_different_app.apex")});
2405
2406 auto session = ApexSession::CreateSession(101);
2407 ASSERT_TRUE(IsOk(session));
2408 ASSERT_TRUE(IsOk(session->UpdateStateAndCommit(SessionState::ACTIVATED)));
2409
2410 ASSERT_TRUE(IsOk(service_->markStagedSessionSuccessful(101)));
2411
2412 ASSERT_TRUE(fs::is_empty(fs::path(kApexBackupDir)));
2413 }
2414
TEST_F(ApexServiceRevertTest,ResumesRevert)2415 TEST_F(ApexServiceRevertTest, ResumesRevert) {
2416 if (supports_fs_checkpointing_) {
2417 GTEST_SKIP() << "Can't run if filesystem checkpointing is enabled";
2418 }
2419 PrepareBackup({GetTestFile("apex.apexd_test.apex"),
2420 GetTestFile("apex.apexd_test_different_app.apex")});
2421
2422 PrepareTestApexForInstall installer(GetTestFile("apex.apexd_test_v2.apex"));
2423 if (!installer.Prepare()) {
2424 return;
2425 }
2426
2427 // Make sure /data/apex/active is non-empty.
2428 ASSERT_TRUE(IsOk(service_->stagePackages({installer.test_file})));
2429
2430 auto session = ApexSession::CreateSession(17239);
2431 ASSERT_TRUE(IsOk(session));
2432 ASSERT_TRUE(
2433 IsOk(session->UpdateStateAndCommit(SessionState::REVERT_IN_PROGRESS)));
2434
2435 ASSERT_TRUE(IsOk(service_->resumeRevertIfNeeded()));
2436
2437 auto pkg1 = StringPrintf("%s/com.android.apex.test_package@1.apex",
2438 kActiveApexPackagesDataDir);
2439 auto pkg2 = StringPrintf("%s/com.android.apex.test_package_2@1.apex",
2440 kActiveApexPackagesDataDir);
2441 SCOPED_TRACE("");
2442 CheckActiveApexContents({pkg1, pkg2});
2443
2444 std::vector<ApexSessionInfo> sessions;
2445 ASSERT_TRUE(IsOk(service_->getSessions(&sessions)));
2446 ApexSessionInfo expected = CreateSessionInfo(17239);
2447 expected.isReverted = true;
2448 ASSERT_THAT(sessions, UnorderedElementsAre(SessionInfoEq(expected)));
2449 }
2450
TEST_F(ApexServiceRevertTest,DoesNotResumeRevert)2451 TEST_F(ApexServiceRevertTest, DoesNotResumeRevert) {
2452 if (supports_fs_checkpointing_) {
2453 GTEST_SKIP() << "Can't run if filesystem checkpointing is enabled";
2454 }
2455 PrepareTestApexForInstall installer(GetTestFile("apex.apexd_test_v2.apex"));
2456 if (!installer.Prepare()) {
2457 return;
2458 }
2459
2460 // Make sure /data/apex/active is non-empty.
2461 ASSERT_TRUE(IsOk(service_->stagePackages({installer.test_file})));
2462
2463 auto session = ApexSession::CreateSession(53);
2464 ASSERT_TRUE(IsOk(session));
2465 ASSERT_TRUE(IsOk(session->UpdateStateAndCommit(SessionState::SUCCESS)));
2466
2467 ASSERT_TRUE(IsOk(service_->resumeRevertIfNeeded()));
2468
2469 // Check that revert wasn't resumed.
2470 auto active_pkgs = ReadEntireDir(kActiveApexPackagesDataDir);
2471 ASSERT_TRUE(IsOk(active_pkgs));
2472 ASSERT_THAT(*active_pkgs,
2473 UnorderedElementsAre(installer.test_installed_file));
2474
2475 std::vector<ApexSessionInfo> sessions;
2476 ASSERT_TRUE(IsOk(service_->getSessions(&sessions)));
2477 ApexSessionInfo expected = CreateSessionInfo(53);
2478 expected.isSuccess = true;
2479 ASSERT_THAT(sessions, UnorderedElementsAre(SessionInfoEq(expected)));
2480 }
2481
2482 // Should mark sessions as REVERT_FAILED on failed revert
TEST_F(ApexServiceRevertTest,SessionsMarkedAsRevertFailed)2483 TEST_F(ApexServiceRevertTest, SessionsMarkedAsRevertFailed) {
2484 if (supports_fs_checkpointing_) {
2485 GTEST_SKIP() << "Can't run if filesystem checkpointing is enabled";
2486 }
2487
2488 auto session = ApexSession::CreateSession(53);
2489 ASSERT_TRUE(IsOk(session));
2490 ASSERT_TRUE(IsOk(session->UpdateStateAndCommit(SessionState::ACTIVATED)));
2491
2492 ASSERT_FALSE(IsOk(service_->revertActiveSessions()));
2493 ApexSessionInfo session_info;
2494 ASSERT_TRUE(IsOk(service_->getStagedSessionInfo(53, &session_info)));
2495 ApexSessionInfo expected = CreateSessionInfo(53);
2496 expected.isRevertFailed = true;
2497 ASSERT_THAT(session_info, SessionInfoEq(expected));
2498 }
2499
TEST_F(ApexServiceRevertTest,RevertFailedStateRevertAttemptFails)2500 TEST_F(ApexServiceRevertTest, RevertFailedStateRevertAttemptFails) {
2501 if (supports_fs_checkpointing_) {
2502 GTEST_SKIP() << "Can't run if filesystem checkpointing is enabled";
2503 }
2504
2505 auto session = ApexSession::CreateSession(17239);
2506 ASSERT_TRUE(IsOk(session));
2507 ASSERT_TRUE(IsOk(session->UpdateStateAndCommit(SessionState::REVERT_FAILED)));
2508
2509 ASSERT_FALSE(IsOk(service_->revertActiveSessions()));
2510 ApexSessionInfo session_info;
2511 ASSERT_TRUE(IsOk(service_->getStagedSessionInfo(17239, &session_info)));
2512 ApexSessionInfo expected = CreateSessionInfo(17239);
2513 expected.isRevertFailed = true;
2514 ASSERT_THAT(session_info, SessionInfoEq(expected));
2515 }
2516
TEST_F(ApexServiceRevertTest,RevertStoresCrashingNativeProcess)2517 TEST_F(ApexServiceRevertTest, RevertStoresCrashingNativeProcess) {
2518 PrepareTestApexForInstall installer(GetTestFile("apex.apexd_test_v2.apex"));
2519 if (!installer.Prepare()) {
2520 return;
2521 }
2522 auto session = ApexSession::CreateSession(1543);
2523 ASSERT_TRUE(IsOk(session));
2524 ASSERT_TRUE(IsOk(session->UpdateStateAndCommit(SessionState::ACTIVATED)));
2525
2526 // Make sure /data/apex/active is non-empty.
2527 ASSERT_TRUE(IsOk(service_->stagePackages({installer.test_file})));
2528 std::string native_process = "test_process";
2529 // TODO(ioffe): this is calling into internals of apexd which makes test quite
2530 // britle. With some refactoring we should be able to call binder api, or
2531 // make this a unit test of apexd.cpp.
2532 Result<void> res = ::android::apex::RevertActiveSessions(native_process, "");
2533 session = ApexSession::GetSession(1543);
2534 ASSERT_EQ(session->GetCrashingNativeProcess(), native_process);
2535 }
2536
GetPidOf(const std::string & name)2537 static pid_t GetPidOf(const std::string& name) {
2538 char buf[1024];
2539 const std::string cmd = std::string("pidof -s ") + name;
2540 FILE* cmd_pipe = popen(cmd.c_str(), "r");
2541 if (cmd_pipe == nullptr) {
2542 PLOG(ERROR) << "Cannot open pipe for " << cmd;
2543 return 0;
2544 }
2545 if (fgets(buf, 1024, cmd_pipe) == nullptr) {
2546 PLOG(ERROR) << "Cannot read pipe for " << cmd;
2547 pclose(cmd_pipe);
2548 return 0;
2549 }
2550
2551 pclose(cmd_pipe);
2552 return strtoul(buf, nullptr, 10);
2553 }
2554
ExecInMountNamespaceOf(pid_t pid,const std::function<void (pid_t)> & func)2555 static void ExecInMountNamespaceOf(pid_t pid,
2556 const std::function<void(pid_t)>& func) {
2557 const std::string my_path = "/proc/self/ns/mnt";
2558 android::base::unique_fd my_fd(open(my_path.c_str(), O_RDONLY | O_CLOEXEC));
2559 ASSERT_TRUE(my_fd.get() >= 0);
2560
2561 const std::string target_path =
2562 std::string("/proc/") + std::to_string(pid) + "/ns/mnt";
2563 android::base::unique_fd target_fd(
2564 open(target_path.c_str(), O_RDONLY | O_CLOEXEC));
2565 ASSERT_TRUE(target_fd.get() >= 0);
2566
2567 int res = setns(target_fd.get(), CLONE_NEWNS);
2568 ASSERT_NE(-1, res);
2569
2570 func(pid);
2571
2572 res = setns(my_fd.get(), CLONE_NEWNS);
2573 ASSERT_NE(-1, res);
2574 }
2575
TEST(ApexdTest,ApexdIsInSameMountNamespaceAsInit)2576 TEST(ApexdTest, ApexdIsInSameMountNamespaceAsInit) {
2577 // TODO(b/136647373): Move this check to environment setup
2578 if (!android::base::GetBoolProperty("ro.apex.updatable", false)) {
2579 GTEST_SKIP() << "Skipping test because device doesn't support APEX";
2580 }
2581 std::string ns_apexd;
2582 std::string ns_init;
2583
2584 ExecInMountNamespaceOf(GetPidOf("apexd"), [&](pid_t /*pid*/) {
2585 bool res = android::base::Readlink("/proc/self/ns/mnt", &ns_apexd);
2586 ASSERT_TRUE(res);
2587 });
2588
2589 ExecInMountNamespaceOf(1, [&](pid_t /*pid*/) {
2590 bool res = android::base::Readlink("/proc/self/ns/mnt", &ns_init);
2591 ASSERT_TRUE(res);
2592 });
2593
2594 ASSERT_EQ(ns_apexd, ns_init);
2595 }
2596
2597 // These are NOT exhaustive list of early processes be should be enough
2598 static const std::vector<const std::string> kEarlyProcesses = {
2599 "servicemanager",
2600 "hwservicemanager",
2601 "vold",
2602 "logd",
2603 };
2604
TEST(ApexdTest,EarlyProcessesAreInDifferentMountNamespace)2605 TEST(ApexdTest, EarlyProcessesAreInDifferentMountNamespace) {
2606 // TODO(b/136647373): Move this check to environment setup
2607 if (!android::base::GetBoolProperty("ro.apex.updatable", false)) {
2608 GTEST_SKIP() << "Skipping test because device doesn't support APEX";
2609 }
2610 std::string ns_apexd;
2611
2612 ExecInMountNamespaceOf(GetPidOf("apexd"), [&](pid_t /*pid*/) {
2613 bool res = android::base::Readlink("/proc/self/ns/mnt", &ns_apexd);
2614 ASSERT_TRUE(res);
2615 });
2616
2617 for (const auto& name : kEarlyProcesses) {
2618 std::string ns_early_process;
2619 ExecInMountNamespaceOf(GetPidOf(name), [&](pid_t /*pid*/) {
2620 bool res =
2621 android::base::Readlink("/proc/self/ns/mnt", &ns_early_process);
2622 ASSERT_TRUE(res);
2623 });
2624 ASSERT_NE(ns_apexd, ns_early_process);
2625 }
2626 }
2627
TEST(ApexdTest,ApexIsAPrivateMountPoint)2628 TEST(ApexdTest, ApexIsAPrivateMountPoint) {
2629 // TODO(b/136647373): Move this check to environment setup
2630 if (!android::base::GetBoolProperty("ro.apex.updatable", false)) {
2631 GTEST_SKIP() << "Skipping test because device doesn't support APEX";
2632 }
2633 std::string mountinfo;
2634 ASSERT_TRUE(
2635 android::base::ReadFileToString("/proc/self/mountinfo", &mountinfo));
2636 bool found_apex_mountpoint = false;
2637 for (const auto& line : android::base::Split(mountinfo, "\n")) {
2638 std::vector<std::string> tokens = android::base::Split(line, " ");
2639 // line format:
2640 // mnt_id parent_mnt_id major:minor source target option propagation_type
2641 // ex) 33 260:19 / /apex rw,nosuid,nodev -
2642 if (tokens.size() >= 7 && tokens[4] == "/apex") {
2643 found_apex_mountpoint = true;
2644 // Make sure that propagation type is set to - which means private
2645 ASSERT_EQ("-", tokens[6]);
2646 }
2647 }
2648 ASSERT_TRUE(found_apex_mountpoint);
2649 }
2650
2651 static const std::vector<const std::string> kEarlyApexes = {
2652 "/apex/com.android.runtime",
2653 "/apex/com.android.tzdata",
2654 };
2655
TEST(ApexdTest,ApexesAreActivatedForEarlyProcesses)2656 TEST(ApexdTest, ApexesAreActivatedForEarlyProcesses) {
2657 // TODO(b/136647373): Move this check to environment setup
2658 if (!android::base::GetBoolProperty("ro.apex.updatable", false)) {
2659 GTEST_SKIP() << "Skipping test because device doesn't support APEX";
2660 }
2661 for (const auto& name : kEarlyProcesses) {
2662 pid_t pid = GetPidOf(name);
2663 const std::string path =
2664 std::string("/proc/") + std::to_string(pid) + "/mountinfo";
2665 std::string mountinfo;
2666 ASSERT_TRUE(android::base::ReadFileToString(path.c_str(), &mountinfo));
2667
2668 std::unordered_set<std::string> mountpoints;
2669 for (const auto& line : android::base::Split(mountinfo, "\n")) {
2670 std::vector<std::string> tokens = android::base::Split(line, " ");
2671 // line format:
2672 // mnt_id parent_mnt_id major:minor source target option propagation_type
2673 // ex) 69 33 7:40 / /apex/com.android.conscrypt ro,nodev,noatime -
2674 if (tokens.size() >= 5) {
2675 // token[4] is the target mount point
2676 mountpoints.emplace(tokens[4]);
2677 }
2678 }
2679 for (const auto& apex_name : kEarlyApexes) {
2680 ASSERT_NE(mountpoints.end(), mountpoints.find(apex_name));
2681 }
2682 }
2683 }
2684
2685 class ApexShimUpdateTest : public ApexServiceTest {
2686 protected:
SetUp()2687 void SetUp() override {
2688 // TODO(b/136647373): Move this check to environment setup
2689 if (!android::base::GetBoolProperty("ro.apex.updatable", false)) {
2690 GTEST_SKIP() << "Skipping test because device doesn't support APEX";
2691 }
2692 ApexServiceTest::SetUp();
2693
2694 // Assert that shim apex is pre-installed.
2695 std::vector<ApexInfo> list;
2696 ASSERT_TRUE(IsOk(service_->getAllPackages(&list)));
2697 ApexInfo expected;
2698 expected.moduleName = "com.android.apex.cts.shim";
2699 expected.modulePath = "/system/apex/com.android.apex.cts.shim.apex";
2700 expected.preinstalledModulePath =
2701 "/system/apex/com.android.apex.cts.shim.apex";
2702 expected.versionCode = 1;
2703 expected.isFactory = true;
2704 expected.isActive = true;
2705 ASSERT_THAT(list, Contains(ApexInfoEq(expected)));
2706 }
2707 };
2708
TEST_F(ApexShimUpdateTest,UpdateToV2Success)2709 TEST_F(ApexShimUpdateTest, UpdateToV2Success) {
2710 PrepareTestApexForInstall installer(
2711 GetTestFile("com.android.apex.cts.shim.v2.apex"));
2712
2713 if (!installer.Prepare()) {
2714 FAIL() << GetDebugStr(&installer);
2715 }
2716
2717 ASSERT_TRUE(IsOk(service_->stagePackages({installer.test_file})));
2718 }
2719
TEST_F(ApexShimUpdateTest,SubmitStagedSessionFailureHasPreInstallHook)2720 TEST_F(ApexShimUpdateTest, SubmitStagedSessionFailureHasPreInstallHook) {
2721 PrepareTestApexForInstall installer(
2722 GetTestFile("com.android.apex.cts.shim.v2_with_pre_install_hook.apex"),
2723 "/data/app-staging/session_23", "staging_data_file");
2724
2725 if (!installer.Prepare()) {
2726 FAIL() << GetDebugStr(&installer);
2727 }
2728
2729 ApexInfoList list;
2730 ApexSessionParams params;
2731 params.sessionId = 23;
2732 ASSERT_FALSE(IsOk(service_->submitStagedSession(params, &list)));
2733 }
2734
TEST_F(ApexShimUpdateTest,SubmitStagedSessionFailureHasPostInstallHook)2735 TEST_F(ApexShimUpdateTest, SubmitStagedSessionFailureHasPostInstallHook) {
2736 PrepareTestApexForInstall installer(
2737 GetTestFile("com.android.apex.cts.shim.v2_with_post_install_hook.apex"),
2738 "/data/app-staging/session_43", "staging_data_file");
2739
2740 if (!installer.Prepare()) {
2741 FAIL() << GetDebugStr(&installer);
2742 }
2743
2744 ApexInfoList list;
2745 ApexSessionParams params;
2746 params.sessionId = 43;
2747 ASSERT_FALSE(IsOk(service_->submitStagedSession(params, &list)));
2748 }
2749
TEST_F(ApexShimUpdateTest,SubmitStagedSessionFailureAdditionalFile)2750 TEST_F(ApexShimUpdateTest, SubmitStagedSessionFailureAdditionalFile) {
2751 PrepareTestApexForInstall installer(
2752 GetTestFile("com.android.apex.cts.shim.v2_additional_file.apex"),
2753 "/data/app-staging/session_41", "staging_data_file");
2754 if (!installer.Prepare()) {
2755 FAIL() << GetDebugStr(&installer);
2756 }
2757
2758 ApexInfoList list;
2759 ApexSessionParams params;
2760 params.sessionId = 41;
2761 ASSERT_FALSE(IsOk(service_->submitStagedSession(params, &list)));
2762 }
2763
TEST_F(ApexShimUpdateTest,SubmitStagedSessionFailureAdditionalFolder)2764 TEST_F(ApexShimUpdateTest, SubmitStagedSessionFailureAdditionalFolder) {
2765 PrepareTestApexForInstall installer(
2766 GetTestFile("com.android.apex.cts.shim.v2_additional_folder.apex"),
2767 "/data/app-staging/session_42", "staging_data_file");
2768 if (!installer.Prepare()) {
2769 FAIL() << GetDebugStr(&installer);
2770 }
2771
2772 ApexInfoList list;
2773 ApexSessionParams params;
2774 params.sessionId = 42;
2775 ASSERT_FALSE(IsOk(service_->submitStagedSession(params, &list)));
2776 }
2777
TEST_F(ApexShimUpdateTest,UpdateToV1Success)2778 TEST_F(ApexShimUpdateTest, UpdateToV1Success) {
2779 PrepareTestApexForInstall installer(
2780 GetTestFile("com.android.apex.cts.shim.apex"));
2781
2782 if (!installer.Prepare()) {
2783 FAIL() << GetDebugStr(&installer);
2784 }
2785
2786 ASSERT_TRUE(IsOk(service_->stagePackages({installer.test_file})));
2787 }
2788
TEST_F(ApexShimUpdateTest,SubmitStagedSessionV1ShimApexSuccess)2789 TEST_F(ApexShimUpdateTest, SubmitStagedSessionV1ShimApexSuccess) {
2790 PrepareTestApexForInstall installer(
2791 GetTestFile("com.android.apex.cts.shim.apex"),
2792 "/data/app-staging/session_97", "staging_data_file");
2793 if (!installer.Prepare()) {
2794 FAIL() << GetDebugStr(&installer);
2795 }
2796
2797 ApexInfoList list;
2798 ApexSessionParams params;
2799 params.sessionId = 97;
2800 ASSERT_TRUE(IsOk(service_->submitStagedSession(params, &list)));
2801 }
2802
TEST_F(ApexServiceTest,SubmitStagedSessionCorruptApexFails)2803 TEST_F(ApexServiceTest, SubmitStagedSessionCorruptApexFails) {
2804 PrepareTestApexForInstall installer(
2805 GetTestFile("apex.apexd_test_corrupt_apex.apex"),
2806 "/data/app-staging/session_57", "staging_data_file");
2807
2808 if (!installer.Prepare()) {
2809 FAIL() << GetDebugStr(&installer);
2810 }
2811
2812 ApexInfoList list;
2813 ApexSessionParams params;
2814 params.sessionId = 57;
2815 ASSERT_FALSE(IsOk(service_->submitStagedSession(params, &list)));
2816 }
2817
TEST_F(ApexServiceTest,SubmitStagedSessionCorruptApexFailsB146895998)2818 TEST_F(ApexServiceTest, SubmitStagedSessionCorruptApexFailsB146895998) {
2819 PrepareTestApexForInstall installer(GetTestFile("corrupted_b146895998.apex"),
2820 "/data/app-staging/session_71",
2821 "staging_data_file");
2822
2823 if (!installer.Prepare()) {
2824 FAIL() << GetDebugStr(&installer);
2825 }
2826
2827 ApexInfoList list;
2828 ApexSessionParams params;
2829 params.sessionId = 71;
2830 ASSERT_FALSE(IsOk(service_->submitStagedSession(params, &list)));
2831 }
2832
TEST_F(ApexServiceTest,StageCorruptApexFailsB146895998)2833 TEST_F(ApexServiceTest, StageCorruptApexFailsB146895998) {
2834 PrepareTestApexForInstall installer(GetTestFile("corrupted_b146895998.apex"));
2835
2836 if (!installer.Prepare()) {
2837 FAIL() << GetDebugStr(&installer);
2838 }
2839
2840 ASSERT_FALSE(IsOk(service_->stagePackages({installer.test_file})));
2841 }
2842
TEST_F(ApexServiceTest,RemountPackagesPackageOnSystemChanged)2843 TEST_F(ApexServiceTest, RemountPackagesPackageOnSystemChanged) {
2844 static constexpr const char* kSystemPath =
2845 "/system_ext/apex/apex.apexd_test.apex";
2846 static constexpr const char* kPackageName = "com.android.apex.test_package";
2847 if (!fs_mgr_overlayfs_is_setup()) {
2848 GTEST_SKIP() << "/system_ext is not overlayed into read-write";
2849 }
2850 if (auto res = IsActive(kPackageName); !res.ok()) {
2851 FAIL() << res.error();
2852 } else {
2853 ASSERT_FALSE(*res) << kPackageName << " is active";
2854 }
2855 ASSERT_EQ(0, access(kSystemPath, F_OK))
2856 << "Failed to stat " << kSystemPath << " : " << strerror(errno);
2857 ASSERT_TRUE(IsOk(service_->activatePackage(kSystemPath)));
2858 std::string backup_path = GetTestFile("apex.apexd_test.apexd.bak");
2859 // Copy original /system_ext apex file. We will need to restore it after test
2860 // runs.
2861 ASSERT_RESULT_OK(CopyFile(kSystemPath, backup_path, fs::copy_options::none));
2862
2863 // Make sure we cleanup after ourselves.
2864 auto deleter = android::base::make_scope_guard([&]() {
2865 if (auto ret = service_->deactivatePackage(kSystemPath); !ret.isOk()) {
2866 LOG(ERROR) << ret.exceptionMessage();
2867 }
2868 auto ret = CopyFile(backup_path, kSystemPath,
2869 fs::copy_options::overwrite_existing);
2870 if (!ret.ok()) {
2871 LOG(ERROR) << ret.error();
2872 }
2873 });
2874
2875 // Copy v2 version to /system_ext/apex/ and then call remountPackages.
2876 PrepareTestApexForInstall installer(GetTestFile("apex.apexd_test_v2.apex"));
2877 if (!installer.Prepare()) {
2878 FAIL() << GetDebugStr(&installer);
2879 }
2880 ASSERT_RESULT_OK(CopyFile(installer.test_file, kSystemPath,
2881 fs::copy_options::overwrite_existing));
2882 // Don't check that remountPackages succeeded. Most likely it will fail, but
2883 // it should still remount our test apex.
2884 service_->remountPackages();
2885
2886 // Check that v2 is now active.
2887 auto active_apex = GetActivePackage("com.android.apex.test_package");
2888 ASSERT_RESULT_OK(active_apex);
2889 ASSERT_EQ(2u, active_apex->versionCode);
2890 // Check that module path didn't change, modulo symlink.
2891 std::string realSystemPath;
2892 ASSERT_TRUE(android::base::Realpath(kSystemPath, &realSystemPath));
2893 ASSERT_EQ(realSystemPath, active_apex->modulePath);
2894 }
2895
TEST_F(ApexServiceActivationSuccessTest,RemountPackagesPackageOnDataChanged)2896 TEST_F(ApexServiceActivationSuccessTest, RemountPackagesPackageOnDataChanged) {
2897 ASSERT_TRUE(IsOk(service_->activatePackage(installer_->test_installed_file)))
2898 << GetDebugStr(installer_.get());
2899 // Copy v2 version to /data/apex/active and then call remountPackages.
2900 PrepareTestApexForInstall installer2(GetTestFile("apex.apexd_test_v2.apex"));
2901 if (!installer2.Prepare()) {
2902 FAIL() << GetDebugStr(&installer2);
2903 }
2904 ASSERT_RESULT_OK(CopyFile(installer2.test_file,
2905 installer_->test_installed_file,
2906 fs::copy_options::overwrite_existing));
2907 // Don't check that remountPackages succeeded. Most likely it will fail, but
2908 // it should still remount our test apex.
2909 service_->remountPackages();
2910
2911 // Check that v2 is now active.
2912 auto active_apex = GetActivePackage("com.android.apex.test_package");
2913 ASSERT_RESULT_OK(active_apex);
2914 ASSERT_EQ(2u, active_apex->versionCode);
2915 // Sanity check that module path didn't change.
2916 ASSERT_EQ(installer_->test_installed_file, active_apex->modulePath);
2917 }
2918
TEST_F(ApexServiceTest,SubmitStagedSessionFailsManifestMismatchCleansUpHashtree)2919 TEST_F(ApexServiceTest,
2920 SubmitStagedSessionFailsManifestMismatchCleansUpHashtree) {
2921 PrepareTestApexForInstall installer(
2922 GetTestFile("apex.apexd_test_no_hashtree_manifest_mismatch.apex"),
2923 "/data/app-staging/session_83", "staging_data_file");
2924 if (!installer.Prepare()) {
2925 return;
2926 }
2927
2928 ApexInfoList list;
2929 ApexSessionParams params;
2930 params.sessionId = 83;
2931 ASSERT_FALSE(IsOk(service_->submitStagedSession(params, &list)));
2932 std::string hashtree_file = std::string(kApexHashTreeDir) + "/" +
2933 installer.package + "@" +
2934 std::to_string(installer.version) + ".new";
2935 ASSERT_FALSE(RegularFileExists(hashtree_file));
2936 }
2937
2938 class LogTestToLogcat : public ::testing::EmptyTestEventListener {
OnTestStart(const::testing::TestInfo & test_info)2939 void OnTestStart(const ::testing::TestInfo& test_info) override {
2940 #ifdef __ANDROID__
2941 using base::LogId;
2942 using base::LogSeverity;
2943 using base::StringPrintf;
2944 base::LogdLogger l;
2945 std::string msg =
2946 StringPrintf("=== %s::%s (%s:%d)", test_info.test_suite_name(),
2947 test_info.name(), test_info.file(), test_info.line());
2948 l(LogId::MAIN, LogSeverity::INFO, "ApexTestCases", __FILE__, __LINE__,
2949 msg.c_str());
2950 #else
2951 UNUSED(test_info);
2952 #endif
2953 }
2954 };
2955
2956 struct NoCodeApexNameProvider {
GetTestNameandroid::apex::NoCodeApexNameProvider2957 static std::string GetTestName() { return "apex.apexd_test_nocode.apex"; }
GetPackageNameandroid::apex::NoCodeApexNameProvider2958 static std::string GetPackageName() {
2959 return "com.android.apex.test_package";
2960 }
2961 };
2962
2963 class ApexServiceActivationNoCode
2964 : public ApexServiceActivationTest<NoCodeApexNameProvider> {};
2965
TEST_F(ApexServiceActivationNoCode,NoCodeApexIsNotExecutable)2966 TEST_F(ApexServiceActivationNoCode, NoCodeApexIsNotExecutable) {
2967 ASSERT_TRUE(IsOk(service_->activatePackage(installer_->test_installed_file)))
2968 << GetDebugStr(installer_.get());
2969
2970 std::string mountinfo;
2971 ASSERT_TRUE(
2972 android::base::ReadFileToString("/proc/self/mountinfo", &mountinfo));
2973 bool found_apex_mountpoint = false;
2974 for (const auto& line : android::base::Split(mountinfo, "\n")) {
2975 std::vector<std::string> tokens = android::base::Split(line, " ");
2976 // line format:
2977 // mnt_id parent_mnt_id major:minor source target option propagation_type
2978 // ex) 33 260:19 / /apex rw,nosuid,nodev -
2979 if (tokens.size() >= 7 &&
2980 tokens[4] ==
2981 "/apex/" + NoCodeApexNameProvider::GetPackageName() + "@1") {
2982 found_apex_mountpoint = true;
2983 // Make sure that option contains noexec
2984 std::vector<std::string> options = android::base::Split(tokens[5], ",");
2985 EXPECT_NE(options.end(),
2986 std::find(options.begin(), options.end(), "noexec"));
2987 break;
2988 }
2989 }
2990 EXPECT_TRUE(found_apex_mountpoint);
2991 }
2992
2993 struct BannedNameProvider {
GetTestNameandroid::apex::BannedNameProvider2994 static std::string GetTestName() { return "sharedlibs.apex"; }
GetPackageNameandroid::apex::BannedNameProvider2995 static std::string GetPackageName() { return "sharedlibs"; }
2996 };
2997
2998 class ApexServiceActivationBannedName
2999 : public ApexServiceActivationTest<BannedNameProvider> {
3000 public:
ApexServiceActivationBannedName()3001 ApexServiceActivationBannedName() : ApexServiceActivationTest(false) {}
3002 };
3003
TEST_F(ApexServiceActivationBannedName,ApexWithBannedNameCannotBeActivated)3004 TEST_F(ApexServiceActivationBannedName, ApexWithBannedNameCannotBeActivated) {
3005 ASSERT_FALSE(
3006 IsOk(service_->activatePackage(installer_->test_installed_file)));
3007 }
3008
3009 namespace {
PrepareCompressedTestApex(const std::string & input_apex,const std::string & builtin_dir,const std::string & decompressed_dir,const std::string & active_apex_dir)3010 void PrepareCompressedTestApex(const std::string& input_apex,
3011 const std::string& builtin_dir,
3012 const std::string& decompressed_dir,
3013 const std::string& active_apex_dir) {
3014 const Result<ApexFile>& apex_file = ApexFile::Open(input_apex);
3015 ASSERT_TRUE(apex_file.ok());
3016 ASSERT_TRUE(apex_file->IsCompressed()) << "Not a compressed APEX";
3017
3018 auto prebuilt_file_path =
3019 builtin_dir + "/" + android::base::Basename(input_apex);
3020 fs::copy(input_apex, prebuilt_file_path);
3021
3022 const ApexManifest& manifest = apex_file->GetManifest();
3023 const std::string& package = manifest.name();
3024 const int64_t& version = manifest.version();
3025
3026 auto decompressed_file_path = decompressed_dir + "/" + package + "@" +
3027 std::to_string(version) + ".apex";
3028 auto result = apex_file->Decompress(decompressed_file_path);
3029 ASSERT_TRUE(result.ok()) << "Failed to decompress " << result.error();
3030 auto active_apex_file_path =
3031 active_apex_dir + "/" + package + "@" + std::to_string(version) + ".apex";
3032 auto error =
3033 link(decompressed_file_path.c_str(), active_apex_file_path.c_str());
3034 ASSERT_EQ(error, 0) << "Failed to hardlink decompressed APEX";
3035 }
3036
CreateCompressedApex(const std::string & name,const int version,const int size)3037 CompressedApexInfo CreateCompressedApex(const std::string& name,
3038 const int version, const int size) {
3039 CompressedApexInfo result;
3040 result.moduleName = name;
3041 result.versionCode = version;
3042 result.decompressedSize = size;
3043 return result;
3044 }
3045 } // namespace
3046
3047 class ApexServiceTestForCompressedApex : public ApexServiceTest {
3048 public:
3049 static constexpr const char* kTempPrebuiltDir = "/data/apex/temp_prebuilt";
3050
SetUp()3051 void SetUp() override {
3052 ApexServiceTest::SetUp();
3053 ASSERT_NE(nullptr, service_.get());
3054
3055 TemporaryDir decompression_dir, active_apex_dir;
3056 if (0 != mkdir(kTempPrebuiltDir, 0777)) {
3057 int saved_errno = errno;
3058 ASSERT_EQ(saved_errno, EEXIST)
3059 << kTempPrebuiltDir << ":" << strerror(saved_errno);
3060 }
3061 PrepareCompressedTestApex(
3062 GetTestFile("com.android.apex.compressed.v1.capex"), kTempPrebuiltDir,
3063 kApexDecompressedDir, kActiveApexPackagesDataDir);
3064 service_->recollectPreinstalledData({kTempPrebuiltDir});
3065 service_->recollectDataApex(kActiveApexPackagesDataDir,
3066 kApexDecompressedDir);
3067 }
3068
TearDown()3069 void TearDown() override {
3070 ApexServiceTest::TearDown();
3071 DeleteDirContent(kTempPrebuiltDir);
3072 rmdir(kTempPrebuiltDir);
3073 DeleteDirContent(kApexDecompressedDir);
3074 DeleteDirContent(kActiveApexPackagesDataDir);
3075 }
3076 };
3077
TEST_F(ApexServiceTestForCompressedApex,CalculateSizeForCompressedApex)3078 TEST_F(ApexServiceTestForCompressedApex, CalculateSizeForCompressedApex) {
3079 int64_t result;
3080 // Empty list of compressed apex info
3081 {
3082 CompressedApexInfoList empty_list;
3083 ASSERT_TRUE(
3084 IsOk(service_->calculateSizeForCompressedApex(empty_list, &result)));
3085 ASSERT_EQ(result, 0ll);
3086 }
3087
3088 // Multiple compressed APEX should get summed
3089 {
3090 CompressedApexInfoList non_empty_list;
3091 CompressedApexInfo new_apex = CreateCompressedApex("new_apex", 1, 1);
3092 CompressedApexInfo new_apex_2 = CreateCompressedApex("new_apex_2", 1, 2);
3093 CompressedApexInfo compressed_apex_same_version =
3094 CreateCompressedApex("com.android.apex.compressed", 1, 4);
3095 CompressedApexInfo compressed_apex_higher_version =
3096 CreateCompressedApex("com.android.apex.compressed", 2, 8);
3097 non_empty_list.apexInfos.push_back(new_apex);
3098 non_empty_list.apexInfos.push_back(new_apex_2);
3099 non_empty_list.apexInfos.push_back(compressed_apex_same_version);
3100 non_empty_list.apexInfos.push_back(compressed_apex_higher_version);
3101 ASSERT_TRUE(IsOk(
3102 service_->calculateSizeForCompressedApex(non_empty_list, &result)));
3103 ASSERT_EQ(result, 11ll); // 1+2+8. compressed_apex_same_version is ignored
3104 }
3105 }
3106
TEST_F(ApexServiceTestForCompressedApex,ReserveSpaceForCompressedApex)3107 TEST_F(ApexServiceTestForCompressedApex, ReserveSpaceForCompressedApex) {
3108 // Multiple compressed APEX should reserve equal to
3109 // CalculateSizeForCompressedApex
3110 {
3111 CompressedApexInfoList non_empty_list;
3112 CompressedApexInfo new_apex = CreateCompressedApex("new_apex", 1, 1);
3113 CompressedApexInfo new_apex_2 = CreateCompressedApex("new_apex_2", 1, 2);
3114 CompressedApexInfo compressed_apex_same_version =
3115 CreateCompressedApex("com.android.apex.compressed", 1, 4);
3116 CompressedApexInfo compressed_apex_higher_version =
3117 CreateCompressedApex("com.android.apex.compressed", 2, 8);
3118 non_empty_list.apexInfos.push_back(new_apex);
3119 non_empty_list.apexInfos.push_back(new_apex_2);
3120 non_empty_list.apexInfos.push_back(compressed_apex_same_version);
3121 non_empty_list.apexInfos.push_back(compressed_apex_higher_version);
3122 int64_t required_size;
3123 ASSERT_TRUE(IsOk(service_->calculateSizeForCompressedApex(non_empty_list,
3124 &required_size)));
3125 ASSERT_EQ(required_size,
3126 11ll); // 1+2+8. compressed_apex_same_version is ignored
3127
3128 ASSERT_TRUE(IsOk(service_->reserveSpaceForCompressedApex(non_empty_list)));
3129 auto files = ReadDir(kOtaReservedDir, [](auto _) { return true; });
3130 ASSERT_TRUE(IsOk(files));
3131 ASSERT_EQ(files->size(), 1u);
3132 EXPECT_EQ((int64_t)fs::file_size((*files)[0]), required_size);
3133 }
3134
3135 // Sending empty list should delete reserved file
3136 {
3137 CompressedApexInfoList empty_list;
3138 ASSERT_TRUE(IsOk(service_->reserveSpaceForCompressedApex(empty_list)));
3139 auto files = ReadDir(kOtaReservedDir, [](auto _) { return true; });
3140 ASSERT_TRUE(IsOk(files));
3141 ASSERT_EQ(files->size(), 0u);
3142 }
3143 }
3144
3145 } // namespace apex
3146 } // namespace android
3147
main(int argc,char ** argv)3148 int main(int argc, char** argv) {
3149 android::base::InitLogging(argv, &android::base::StderrLogger);
3150 android::base::SetMinimumLogSeverity(android::base::VERBOSE);
3151 ::testing::InitGoogleTest(&argc, argv);
3152 ::testing::UnitTest::GetInstance()->listeners().Append(
3153 new android::apex::LogTestToLogcat());
3154 return RUN_ALL_TESTS();
3155 }
3156