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