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 <android-base/file.h>
18 #include <android-base/logging.h>
19 #include <android-base/macros.h>
20 #include <android-base/properties.h>
21 #include <android-base/scopeguard.h>
22 #include <android-base/stringprintf.h>
23 #include <android-base/strings.h>
24 #include <android/apex/ApexInfo.h>
25 #include <android/apex/IApexService.h>
26 #include <android/os/IVold.h>
27 #include <binder/IServiceManager.h>
28 #include <fs_mgr_overlayfs.h>
29 #include <fstab/fstab.h>
30 #include <gmock/gmock.h>
31 #include <grp.h>
32 #include <gtest/gtest.h>
33 #include <libdm/dm.h>
34 #include <linux/loop.h>
35 #include <selinux/selinux.h>
36 #include <stdio.h>
37 #include <sys/ioctl.h>
38 #include <sys/stat.h>
39 #include <sys/types.h>
40 #include <sys/xattr.h>
41
42 #include <algorithm>
43 #include <filesystem>
44 #include <fstream>
45 #include <functional>
46 #include <memory>
47 #include <optional>
48 #include <string>
49 #include <unordered_set>
50 #include <vector>
51
52 #include "apex_constants.h"
53 #include "apex_database.h"
54 #include "apex_file.h"
55 #include "apex_manifest.h"
56 #include "apexd.h"
57 #include "apexd_private.h"
58 #include "apexd_session.h"
59 #include "apexd_test_utils.h"
60 #include "apexd_utils.h"
61 #include "session_state.pb.h"
62 #include "string_log.h"
63
64 using apex::proto::SessionState;
65
66 namespace android {
67 namespace apex {
68
69 using android::sp;
70 using android::String16;
71 using android::apex::testing::CreateSessionInfo;
72 using android::apex::testing::IsOk;
73 using android::apex::testing::SessionInfoEq;
74 using android::base::EndsWith;
75 using android::base::Error;
76 using android::base::Join;
77 using android::base::Result;
78 using android::base::SetProperty;
79 using android::base::StartsWith;
80 using android::base::StringPrintf;
81 using android::base::unique_fd;
82 using android::dm::DeviceMapper;
83 using ::apex::proto::ApexManifest;
84 using ::apex::proto::SessionState;
85 using ::testing::EndsWith;
86 using ::testing::Not;
87 using ::testing::SizeIs;
88 using ::testing::UnorderedElementsAre;
89 using ::testing::UnorderedElementsAreArray;
90
91 using MountedApexData = MountedApexDatabase::MountedApexData;
92
93 namespace fs = std::filesystem;
94
95 class ApexServiceTest : public ::testing::Test {
96 public:
ApexServiceTest()97 ApexServiceTest() {}
98
99 protected:
SetUp()100 void SetUp() override {
101 // TODO(b/136647373): Move this check to environment setup
102 if (!android::base::GetBoolProperty("ro.apex.updatable", false)) {
103 GTEST_SKIP() << "Skipping test because device doesn't support APEX";
104 }
105
106 // Enable VERBOSE logging to simplifying debugging
107 SetProperty("log.tag.apexd", "VERBOSE");
108
109 using android::IBinder;
110 using android::IServiceManager;
111
112 sp<IServiceManager> sm = android::defaultServiceManager();
113 sp<IBinder> binder = sm->waitForService(String16("apexservice"));
114 if (binder != nullptr) {
115 service_ = android::interface_cast<IApexService>(binder);
116 }
117 binder = sm->getService(String16("vold"));
118 if (binder != nullptr) {
119 vold_service_ = android::interface_cast<android::os::IVold>(binder);
120 }
121
122 ASSERT_NE(nullptr, service_.get());
123 ASSERT_NE(nullptr, vold_service_.get());
124 android::binder::Status status =
125 vold_service_->supportsCheckpoint(&supports_fs_checkpointing_);
126 ASSERT_TRUE(IsOk(status));
127 CleanUp();
128 service_->recollectPreinstalledData(kApexPackageBuiltinDirs);
129 }
130
TearDown()131 void TearDown() override { CleanUp(); }
132
GetTestDataDir()133 static std::string GetTestDataDir() {
134 return android::base::GetExecutableDirectory();
135 }
GetTestFile(const std::string & name)136 static std::string GetTestFile(const std::string& name) {
137 return GetTestDataDir() + "/" + name;
138 }
139
HaveSelinux()140 static bool HaveSelinux() { return 1 == is_selinux_enabled(); }
141
IsSelinuxEnforced()142 static bool IsSelinuxEnforced() { return 0 != security_getenforce(); }
143
GetAllPackages()144 Result<std::vector<ApexInfo>> GetAllPackages() {
145 std::vector<ApexInfo> list;
146 android::binder::Status status = service_->getAllPackages(&list);
147 if (status.isOk()) {
148 return list;
149 }
150
151 return Error() << status.toString8().c_str();
152 }
153
GetActivePackages()154 Result<std::vector<ApexInfo>> GetActivePackages() {
155 std::vector<ApexInfo> list;
156 android::binder::Status status = service_->getActivePackages(&list);
157 if (status.isOk()) {
158 return list;
159 }
160
161 return Error() << status.exceptionMessage().c_str();
162 }
163
GetInactivePackages()164 Result<std::vector<ApexInfo>> GetInactivePackages() {
165 std::vector<ApexInfo> list;
166 android::binder::Status status = service_->getAllPackages(&list);
167 list.erase(std::remove_if(
168 list.begin(), list.end(),
169 [](const ApexInfo& apexInfo) { return apexInfo.isActive; }),
170 list.end());
171 if (status.isOk()) {
172 return list;
173 }
174
175 return Error() << status.toString8().c_str();
176 }
177
GetPackageString(const ApexInfo & p)178 std::string GetPackageString(const ApexInfo& p) {
179 return p.moduleName + "@" + std::to_string(p.versionCode) +
180 " [path=" + p.moduleName + "]";
181 }
182
GetPackagesStrings(const std::vector<ApexInfo> & list)183 std::vector<std::string> GetPackagesStrings(
184 const std::vector<ApexInfo>& list) {
185 std::vector<std::string> ret;
186 ret.reserve(list.size());
187 for (const ApexInfo& p : list) {
188 ret.push_back(GetPackageString(p));
189 }
190 return ret;
191 }
192
GetActivePackagesStrings()193 std::vector<std::string> GetActivePackagesStrings() {
194 std::vector<ApexInfo> list;
195 android::binder::Status status = service_->getActivePackages(&list);
196 if (status.isOk()) {
197 std::vector<std::string> ret(list.size());
198 for (const ApexInfo& p : list) {
199 ret.push_back(GetPackageString(p));
200 }
201 return ret;
202 }
203
204 std::vector<std::string> error;
205 error.push_back("ERROR");
206 return error;
207 }
208
GetFactoryPackages()209 Result<std::vector<ApexInfo>> GetFactoryPackages() {
210 std::vector<ApexInfo> list;
211 android::binder::Status status = service_->getAllPackages(&list);
212 list.erase(
213 std::remove_if(list.begin(), list.end(),
214 [](ApexInfo& apexInfo) { return !apexInfo.isFactory; }),
215 list.end());
216 if (status.isOk()) {
217 return list;
218 }
219
220 return Error() << status.toString8().c_str();
221 }
222
ListDir(const std::string & path)223 static std::vector<std::string> ListDir(const std::string& path) {
224 std::vector<std::string> ret;
225 std::error_code ec;
226 if (!fs::is_directory(path, ec)) {
227 return ret;
228 }
229 auto status = WalkDir(path, [&](const fs::directory_entry& entry) {
230 std::string tmp;
231 switch (entry.symlink_status(ec).type()) {
232 case fs::file_type::directory:
233 tmp = "[dir]";
234 break;
235 case fs::file_type::symlink:
236 tmp = "[lnk]";
237 break;
238 case fs::file_type::regular:
239 tmp = "[reg]";
240 break;
241 default:
242 tmp = "[other]";
243 }
244 ret.push_back(tmp.append(entry.path().filename()));
245 });
246 CHECK(status.has_value())
247 << "Failed to list " << path << " : " << status.error();
248 std::sort(ret.begin(), ret.end());
249 return ret;
250 }
251
DeleteIfExists(const std::string & path)252 static void DeleteIfExists(const std::string& path) {
253 if (fs::exists(path)) {
254 std::error_code ec;
255 fs::remove_all(path, ec);
256 ASSERT_FALSE(ec) << "Failed to delete dir " << path << " : "
257 << ec.message();
258 }
259 }
260
261 struct PrepareTestApexForInstall {
262 static constexpr const char* kTestDir = "/data/app-staging/apexservice_tmp";
263
264 // This is given to the constructor.
265 std::string test_input; // Original test file.
266 std::string selinux_label_input; // SELinux label to apply.
267 std::string test_dir_input;
268
269 // This is derived from the input.
270 std::string test_file; // Prepared path. Under test_dir_input.
271 std::string test_installed_file; // Where apexd will store it.
272
273 std::string package; // APEX package name.
274 uint64_t version; // APEX version
275
PrepareTestApexForInstallandroid::apex::ApexServiceTest::PrepareTestApexForInstall276 explicit PrepareTestApexForInstall(
277 const std::string& test,
278 const std::string& test_dir = std::string(kTestDir),
279 const std::string& selinux_label = "staging_data_file") {
280 test_input = test;
281 selinux_label_input = selinux_label;
282 test_dir_input = test_dir;
283
284 test_file = test_dir_input + "/" + android::base::Basename(test);
285
286 package = ""; // Explicitly mark as not initialized.
287
288 Result<ApexFile> apex_file = ApexFile::Open(test);
289 if (!apex_file.ok()) {
290 return;
291 }
292
293 const ApexManifest& manifest = apex_file->GetManifest();
294 package = manifest.name();
295 version = manifest.version();
296
297 test_installed_file = std::string(kActiveApexPackagesDataDir) + "/" +
298 package + "@" + std::to_string(version) + ".apex";
299 }
300
Prepareandroid::apex::ApexServiceTest::PrepareTestApexForInstall301 bool Prepare() {
302 if (package.empty()) {
303 // Failure in constructor. Redo work to get error message.
304 auto fail_fn = [&]() {
305 Result<ApexFile> apex_file = ApexFile::Open(test_input);
306 ASSERT_FALSE(IsOk(apex_file));
307 ASSERT_TRUE(apex_file.ok())
308 << test_input << " failed to load: " << apex_file.error();
309 };
310 fail_fn();
311 return false;
312 }
313
314 auto prepare = [](const std::string& src, const std::string& trg,
315 const std::string& selinux_label) {
316 ASSERT_EQ(0, access(src.c_str(), F_OK))
317 << src << ": " << strerror(errno);
318 const std::string trg_dir = android::base::Dirname(trg);
319 if (0 != mkdir(trg_dir.c_str(), 0777)) {
320 int saved_errno = errno;
321 ASSERT_EQ(saved_errno, EEXIST) << trg << ":" << strerror(saved_errno);
322 }
323
324 // Do not use a hardlink, even though it's the simplest solution.
325 // b/119569101.
326 {
327 std::ifstream src_stream(src, std::ios::binary);
328 ASSERT_TRUE(src_stream.good());
329 std::ofstream trg_stream(trg, std::ios::binary);
330 ASSERT_TRUE(trg_stream.good());
331
332 trg_stream << src_stream.rdbuf();
333 }
334
335 ASSERT_EQ(0, chmod(trg.c_str(), 0666)) << strerror(errno);
336 struct group* g = getgrnam("system");
337 ASSERT_NE(nullptr, g);
338 ASSERT_EQ(0, chown(trg.c_str(), /* root uid */ 0, g->gr_gid))
339 << strerror(errno);
340
341 int rc = setfilecon(
342 trg_dir.c_str(),
343 std::string("u:object_r:" + selinux_label + ":s0").c_str());
344 ASSERT_TRUE(0 == rc || !HaveSelinux()) << strerror(errno);
345 rc = setfilecon(
346 trg.c_str(),
347 std::string("u:object_r:" + selinux_label + ":s0").c_str());
348 ASSERT_TRUE(0 == rc || !HaveSelinux()) << strerror(errno);
349 };
350 prepare(test_input, test_file, selinux_label_input);
351 return !HasFatalFailure();
352 }
353
~PrepareTestApexForInstallandroid::apex::ApexServiceTest::PrepareTestApexForInstall354 ~PrepareTestApexForInstall() {
355 LOG(INFO) << "Deleting file " << test_file;
356 if (unlink(test_file.c_str()) != 0) {
357 PLOG(ERROR) << "Unable to unlink " << test_file;
358 }
359 LOG(INFO) << "Deleting directory " << test_dir_input;
360 if (rmdir(test_dir_input.c_str()) != 0) {
361 PLOG(ERROR) << "Unable to rmdir " << test_dir_input;
362 }
363 }
364 };
365
GetDebugStr(PrepareTestApexForInstall * installer)366 std::string GetDebugStr(PrepareTestApexForInstall* installer) {
367 StringLog log;
368
369 if (installer != nullptr) {
370 log << "test_input=" << installer->test_input << " ";
371 log << "test_file=" << installer->test_file << " ";
372 log << "test_installed_file=" << installer->test_installed_file << " ";
373 log << "package=" << installer->package << " ";
374 log << "version=" << installer->version << " ";
375 }
376
377 log << "active=[" << Join(GetActivePackagesStrings(), ',') << "] ";
378 log << kActiveApexPackagesDataDir << "=["
379 << Join(ListDir(kActiveApexPackagesDataDir), ',') << "] ";
380 log << kApexRoot << "=[" << Join(ListDir(kApexRoot), ',') << "]";
381
382 return log;
383 }
384
385 sp<IApexService> service_;
386 sp<android::os::IVold> vold_service_;
387 bool supports_fs_checkpointing_;
388
389 private:
CleanUp()390 void CleanUp() {
391 DeleteDirContent(kActiveApexPackagesDataDir);
392 DeleteDirContent(kApexBackupDir);
393 DeleteDirContent(kApexHashTreeDir);
394 DeleteDirContent(ApexSession::GetSessionsDir());
395
396 DeleteIfExists("/data/misc_ce/0/apexdata/apex.apexd_test");
397 DeleteIfExists("/data/misc_ce/0/apexrollback/123456");
398 DeleteIfExists("/data/misc_ce/0/apexrollback/77777");
399 DeleteIfExists("/data/misc_ce/0/apexrollback/98765");
400 DeleteIfExists("/data/misc_de/0/apexrollback/123456");
401 DeleteIfExists("/data/misc/apexrollback/123456");
402 }
403 };
404
405 namespace {
406
RegularFileExists(const std::string & path)407 bool RegularFileExists(const std::string& path) {
408 struct stat buf;
409 if (0 != stat(path.c_str(), &buf)) {
410 return false;
411 }
412 return S_ISREG(buf.st_mode);
413 }
414
DirExists(const std::string & path)415 bool DirExists(const std::string& path) {
416 struct stat buf;
417 if (0 != stat(path.c_str(), &buf)) {
418 return false;
419 }
420 return S_ISDIR(buf.st_mode);
421 }
422
CreateDir(const std::string & path)423 void CreateDir(const std::string& path) {
424 std::error_code ec;
425 fs::create_directory(path, ec);
426 ASSERT_FALSE(ec) << "Failed to create rollback dir "
427 << " : " << ec.message();
428 }
429
CreateFile(const std::string & path)430 void CreateFile(const std::string& path) {
431 std::ofstream ofs(path);
432 ASSERT_TRUE(ofs.good());
433 ofs.close();
434 }
435
CreateFileWithExpectedProperties(const std::string & path)436 void CreateFileWithExpectedProperties(const std::string& path) {
437 CreateFile(path);
438 std::error_code ec;
439 fs::permissions(
440 path,
441 fs::perms::owner_read | fs::perms::group_write | fs::perms::others_exec,
442 fs::perm_options::replace, ec);
443 ASSERT_FALSE(ec) << "Failed to set permissions: " << ec.message();
444 ASSERT_EQ(0, chown(path.c_str(), 1007 /* log */, 3001 /* net_bt_admin */))
445 << "chown failed: " << strerror(errno);
446 ASSERT_TRUE(RegularFileExists(path));
447 char buf[65536]; // 64kB is max possible xattr list size. See "man 7 xattr".
448 ASSERT_EQ(0, setxattr(path.c_str(), "user.foo", "bar", 4, 0));
449 ASSERT_GE(listxattr(path.c_str(), buf, sizeof(buf)), 9);
450 ASSERT_TRUE(memmem(buf, sizeof(buf), "user.foo", 9) != nullptr);
451 ASSERT_EQ(4, getxattr(path.c_str(), "user.foo", buf, sizeof(buf)));
452 ASSERT_STREQ("bar", buf);
453 }
454
ExpectFileWithExpectedProperties(const std::string & path)455 void ExpectFileWithExpectedProperties(const std::string& path) {
456 EXPECT_TRUE(RegularFileExists(path));
457 EXPECT_EQ(fs::status(path).permissions(), fs::perms::owner_read |
458 fs::perms::group_write |
459 fs::perms::others_exec);
460 struct stat sd;
461 ASSERT_EQ(0, stat(path.c_str(), &sd));
462 EXPECT_EQ(1007u, sd.st_uid);
463 EXPECT_EQ(3001u, sd.st_gid);
464 char buf[65536]; // 64kB is max possible xattr list size. See "man 7 xattr".
465 EXPECT_GE(listxattr(path.c_str(), buf, sizeof(buf)), 9);
466 EXPECT_TRUE(memmem(buf, sizeof(buf), "user.foo", 9) != nullptr);
467 EXPECT_EQ(4, getxattr(path.c_str(), "user.foo", buf, sizeof(buf)));
468 EXPECT_STREQ("bar", buf);
469 }
470
ReadEntireDir(const std::string & path)471 Result<std::vector<std::string>> ReadEntireDir(const std::string& path) {
472 static const auto kAcceptAll = [](auto /*entry*/) { return true; };
473 return ReadDir(path, kAcceptAll);
474 }
475
476 } // namespace
477
TEST_F(ApexServiceTest,HaveSelinux)478 TEST_F(ApexServiceTest, HaveSelinux) {
479 // We want to test under selinux.
480 EXPECT_TRUE(HaveSelinux());
481 }
482
483 // Skip for b/119032200.
TEST_F(ApexServiceTest,DISABLED_EnforceSelinux)484 TEST_F(ApexServiceTest, DISABLED_EnforceSelinux) {
485 // Crude cutout for virtual devices.
486 #if !defined(__i386__) && !defined(__x86_64__)
487 constexpr bool kIsX86 = false;
488 #else
489 constexpr bool kIsX86 = true;
490 #endif
491 EXPECT_TRUE(IsSelinuxEnforced() || kIsX86);
492 }
493
TEST_F(ApexServiceTest,SubmitStagegSessionSuccessDoesNotLeakTempVerityDevices)494 TEST_F(ApexServiceTest,
495 SubmitStagegSessionSuccessDoesNotLeakTempVerityDevices) {
496 PrepareTestApexForInstall installer(GetTestFile("apex.apexd_test.apex"),
497 "/data/app-staging/session_1543",
498 "staging_data_file");
499 if (!installer.Prepare()) {
500 return;
501 }
502
503 ApexInfoList list;
504 ApexSessionParams params;
505 params.sessionId = 1543;
506 ASSERT_TRUE(IsOk(service_->submitStagedSession(params, &list)));
507
508 std::vector<DeviceMapper::DmBlockDevice> devices;
509 DeviceMapper& dm = DeviceMapper::Instance();
510 ASSERT_TRUE(dm.GetAvailableDevices(&devices));
511
512 for (const auto& device : devices) {
513 ASSERT_THAT(device.name(), Not(EndsWith(".tmp")));
514 }
515 }
516
TEST_F(ApexServiceTest,SubmitStagedSessionStoresBuildFingerprint)517 TEST_F(ApexServiceTest, SubmitStagedSessionStoresBuildFingerprint) {
518 PrepareTestApexForInstall installer(GetTestFile("apex.apexd_test.apex"),
519 "/data/app-staging/session_1547",
520 "staging_data_file");
521 if (!installer.Prepare()) {
522 return;
523 }
524 ApexInfoList list;
525 ApexSessionParams params;
526 params.sessionId = 1547;
527 ASSERT_TRUE(IsOk(service_->submitStagedSession(params, &list)));
528
529 auto session = ApexSession::GetSession(1547);
530 ASSERT_FALSE(session->GetBuildFingerprint().empty());
531 }
532
TEST_F(ApexServiceTest,SubmitStagedSessionFailDoesNotLeakTempVerityDevices)533 TEST_F(ApexServiceTest, SubmitStagedSessionFailDoesNotLeakTempVerityDevices) {
534 PrepareTestApexForInstall installer(
535 GetTestFile("apex.apexd_test_manifest_mismatch.apex"),
536 "/data/app-staging/session_239", "staging_data_file");
537 if (!installer.Prepare()) {
538 return;
539 }
540
541 ApexInfoList list;
542 ApexSessionParams params;
543 params.sessionId = 239;
544 ASSERT_FALSE(IsOk(service_->submitStagedSession(params, &list)));
545
546 std::vector<DeviceMapper::DmBlockDevice> devices;
547 DeviceMapper& dm = DeviceMapper::Instance();
548 ASSERT_TRUE(dm.GetAvailableDevices(&devices));
549
550 for (const auto& device : devices) {
551 ASSERT_THAT(device.name(), Not(EndsWith(".tmp")));
552 }
553 }
554
TEST_F(ApexServiceTest,CannotBeRollbackAndHaveRollbackEnabled)555 TEST_F(ApexServiceTest, CannotBeRollbackAndHaveRollbackEnabled) {
556 PrepareTestApexForInstall installer(GetTestFile("apex.apexd_test.apex"),
557 "/data/app-staging/session_1543",
558 "staging_data_file");
559 if (!installer.Prepare()) {
560 return;
561 }
562
563 ApexInfoList list;
564 ApexSessionParams params;
565 params.sessionId = 1543;
566 params.isRollback = true;
567 params.hasRollbackEnabled = true;
568 ASSERT_FALSE(IsOk(service_->submitStagedSession(params, &list)));
569 }
570
TEST_F(ApexServiceTest,SessionParamDefaults)571 TEST_F(ApexServiceTest, SessionParamDefaults) {
572 PrepareTestApexForInstall installer(GetTestFile("apex.apexd_test.apex"),
573 "/data/app-staging/session_1547",
574 "staging_data_file");
575 if (!installer.Prepare()) {
576 return;
577 }
578 ApexInfoList list;
579 ApexSessionParams params;
580 params.sessionId = 1547;
581 ASSERT_TRUE(IsOk(service_->submitStagedSession(params, &list)));
582
583 auto session = ApexSession::GetSession(1547);
584 ASSERT_TRUE(session->GetChildSessionIds().empty());
585 ASSERT_FALSE(session->IsRollback());
586 ASSERT_FALSE(session->HasRollbackEnabled());
587 ASSERT_EQ(0, session->GetRollbackId());
588 }
589
TEST_F(ApexServiceTest,SnapshotCeData)590 TEST_F(ApexServiceTest, SnapshotCeData) {
591 CreateDir("/data/misc_ce/0/apexdata/apex.apexd_test");
592 CreateFileWithExpectedProperties(
593 "/data/misc_ce/0/apexdata/apex.apexd_test/hello.txt");
594
595 service_->snapshotCeData(0, 123456, "apex.apexd_test");
596
597 ExpectFileWithExpectedProperties(
598 "/data/misc_ce/0/apexrollback/123456/apex.apexd_test/hello.txt");
599 }
600
TEST_F(ApexServiceTest,RestoreCeData)601 TEST_F(ApexServiceTest, RestoreCeData) {
602 CreateDir("/data/misc_ce/0/apexdata/apex.apexd_test");
603 CreateDir("/data/misc_ce/0/apexrollback/123456");
604 CreateDir("/data/misc_ce/0/apexrollback/123456/apex.apexd_test");
605
606 CreateFile("/data/misc_ce/0/apexdata/apex.apexd_test/newfile.txt");
607 CreateFileWithExpectedProperties(
608 "/data/misc_ce/0/apexrollback/123456/apex.apexd_test/oldfile.txt");
609
610 ASSERT_TRUE(RegularFileExists(
611 "/data/misc_ce/0/apexdata/apex.apexd_test/newfile.txt"));
612 ExpectFileWithExpectedProperties(
613 "/data/misc_ce/0/apexrollback/123456/apex.apexd_test/oldfile.txt");
614
615 service_->restoreCeData(0, 123456, "apex.apexd_test");
616
617 ExpectFileWithExpectedProperties(
618 "/data/misc_ce/0/apexdata/apex.apexd_test/oldfile.txt");
619 EXPECT_FALSE(RegularFileExists(
620 "/data/misc_ce/0/apexdata/apex.apexd_test/newfile.txt"));
621 // The snapshot should be deleted after restoration.
622 EXPECT_FALSE(
623 DirExists("/data/misc_ce/0/apexrollback/123456/apex.apexd_test"));
624 }
625
TEST_F(ApexServiceTest,DestroyDeSnapshotsDeSys)626 TEST_F(ApexServiceTest, DestroyDeSnapshotsDeSys) {
627 CreateDir("/data/misc/apexrollback/123456");
628 CreateDir("/data/misc/apexrollback/123456/my.apex");
629 CreateFile("/data/misc/apexrollback/123456/my.apex/hello.txt");
630
631 ASSERT_TRUE(
632 RegularFileExists("/data/misc/apexrollback/123456/my.apex/hello.txt"));
633
634 service_->destroyDeSnapshots(8975);
635 ASSERT_TRUE(
636 RegularFileExists("/data/misc/apexrollback/123456/my.apex/hello.txt"));
637
638 service_->destroyDeSnapshots(123456);
639 ASSERT_FALSE(
640 RegularFileExists("/data/misc/apexrollback/123456/my.apex/hello.txt"));
641 ASSERT_FALSE(DirExists("/data/misc/apexrollback/123456"));
642 }
643
TEST_F(ApexServiceTest,DestroyDeSnapshotsDeUser)644 TEST_F(ApexServiceTest, DestroyDeSnapshotsDeUser) {
645 CreateDir("/data/misc_de/0/apexrollback/123456");
646 CreateDir("/data/misc_de/0/apexrollback/123456/my.apex");
647 CreateFile("/data/misc_de/0/apexrollback/123456/my.apex/hello.txt");
648
649 ASSERT_TRUE(RegularFileExists(
650 "/data/misc_de/0/apexrollback/123456/my.apex/hello.txt"));
651
652 service_->destroyDeSnapshots(8975);
653 ASSERT_TRUE(RegularFileExists(
654 "/data/misc_de/0/apexrollback/123456/my.apex/hello.txt"));
655
656 service_->destroyDeSnapshots(123456);
657 ASSERT_FALSE(RegularFileExists(
658 "/data/misc_de/0/apexrollback/123456/my.apex/hello.txt"));
659 ASSERT_FALSE(DirExists("/data/misc_de/0/apexrollback/123456"));
660 }
661
TEST_F(ApexServiceTest,DestroyCeSnapshots)662 TEST_F(ApexServiceTest, DestroyCeSnapshots) {
663 CreateDir("/data/misc_ce/0/apexrollback/123456");
664 CreateDir("/data/misc_ce/0/apexrollback/123456/apex.apexd_test");
665 CreateFile("/data/misc_ce/0/apexrollback/123456/apex.apexd_test/file.txt");
666
667 CreateDir("/data/misc_ce/0/apexrollback/77777");
668 CreateDir("/data/misc_ce/0/apexrollback/77777/apex.apexd_test");
669 CreateFile("/data/misc_ce/0/apexrollback/77777/apex.apexd_test/thing.txt");
670
671 ASSERT_TRUE(RegularFileExists(
672 "/data/misc_ce/0/apexrollback/123456/apex.apexd_test/file.txt"));
673 ASSERT_TRUE(RegularFileExists(
674 "/data/misc_ce/0/apexrollback/77777/apex.apexd_test/thing.txt"));
675
676 android::binder::Status st = service_->destroyCeSnapshots(0, 123456);
677 ASSERT_TRUE(IsOk(st));
678 // Should be OK if the directory doesn't exist.
679 st = service_->destroyCeSnapshots(1, 123456);
680 ASSERT_TRUE(IsOk(st));
681
682 ASSERT_TRUE(RegularFileExists(
683 "/data/misc_ce/0/apexrollback/77777/apex.apexd_test/thing.txt"));
684 ASSERT_FALSE(DirExists("/data/misc_ce/0/apexrollback/123456"));
685 }
686
TEST_F(ApexServiceTest,DestroyCeSnapshotsNotSpecified)687 TEST_F(ApexServiceTest, DestroyCeSnapshotsNotSpecified) {
688 CreateDir("/data/misc_ce/0/apexrollback/123456");
689 CreateDir("/data/misc_ce/0/apexrollback/123456/apex.apexd_test");
690 CreateFile("/data/misc_ce/0/apexrollback/123456/apex.apexd_test/file.txt");
691
692 CreateDir("/data/misc_ce/0/apexrollback/77777");
693 CreateDir("/data/misc_ce/0/apexrollback/77777/apex.apexd_test");
694 CreateFile("/data/misc_ce/0/apexrollback/77777/apex.apexd_test/thing.txt");
695
696 CreateDir("/data/misc_ce/0/apexrollback/98765");
697 CreateDir("/data/misc_ce/0/apexrollback/98765/apex.apexd_test");
698 CreateFile("/data/misc_ce/0/apexrollback/98765/apex.apexd_test/test.txt");
699
700 ASSERT_TRUE(RegularFileExists(
701 "/data/misc_ce/0/apexrollback/123456/apex.apexd_test/file.txt"));
702 ASSERT_TRUE(RegularFileExists(
703 "/data/misc_ce/0/apexrollback/77777/apex.apexd_test/thing.txt"));
704 ASSERT_TRUE(RegularFileExists(
705 "/data/misc_ce/0/apexrollback/98765/apex.apexd_test/test.txt"));
706
707 std::vector<int> retain{123, 77777, 987654};
708 android::binder::Status st =
709 service_->destroyCeSnapshotsNotSpecified(0, retain);
710 ASSERT_TRUE(IsOk(st));
711
712 ASSERT_TRUE(RegularFileExists(
713 "/data/misc_ce/0/apexrollback/77777/apex.apexd_test/thing.txt"));
714 ASSERT_FALSE(DirExists("/data/misc_ce/0/apexrollback/123456"));
715 ASSERT_FALSE(DirExists("/data/misc_ce/0/apexrollback/98765"));
716 }
717
TEST_F(ApexServiceTest,SubmitStagedSessionCleanupsTempMountOnFailure)718 TEST_F(ApexServiceTest, SubmitStagedSessionCleanupsTempMountOnFailure) {
719 // Parent session id: 23
720 // Children session ids: 37 73
721 PrepareTestApexForInstall installer(
722 GetTestFile("apex.apexd_test_different_app.apex"),
723 "/data/app-staging/session_37", "staging_data_file");
724 PrepareTestApexForInstall installer2(
725 GetTestFile("apex.apexd_test_manifest_mismatch.apex"),
726 "/data/app-staging/session_73", "staging_data_file");
727 if (!installer.Prepare() || !installer2.Prepare()) {
728 FAIL() << GetDebugStr(&installer) << GetDebugStr(&installer2);
729 }
730 ApexInfoList list;
731 ApexSessionParams params;
732 params.sessionId = 23;
733 params.childSessionIds = {37, 73};
734 ASSERT_FALSE(IsOk(service_->submitStagedSession(params, &list)))
735 << GetDebugStr(&installer);
736
737 // Check that temp mounts were cleanded up.
738 for (const auto& mount : GetApexMounts()) {
739 EXPECT_FALSE(EndsWith(mount, ".tmp")) << "Found temp mount " << mount;
740 }
741 }
742
TEST_F(ApexServiceTest,GetFactoryPackages)743 TEST_F(ApexServiceTest, GetFactoryPackages) {
744 Result<std::vector<ApexInfo>> factory_packages = GetFactoryPackages();
745 ASSERT_TRUE(IsOk(factory_packages));
746 ASSERT_TRUE(factory_packages->size() > 0);
747
748 std::vector<std::string> builtin_dirs;
749 for (const auto& d : kApexPackageBuiltinDirs) {
750 std::string realpath;
751 if (android::base::Realpath(d, &realpath)) {
752 builtin_dirs.push_back(realpath);
753 }
754 // realpath might fail in case when dir is a non-existing path. We can
755 // ignore non-existing paths.
756 }
757
758 // Decompressed APEX is also considred factory package
759 builtin_dirs.push_back(kApexDecompressedDir);
760
761 for (const ApexInfo& package : *factory_packages) {
762 bool is_builtin = false;
763 for (const auto& dir : builtin_dirs) {
764 if (StartsWith(package.modulePath, dir)) {
765 is_builtin = true;
766 }
767 }
768 ASSERT_TRUE(is_builtin);
769 }
770 }
771
TEST_F(ApexServiceTest,NoPackagesAreBothActiveAndInactive)772 TEST_F(ApexServiceTest, NoPackagesAreBothActiveAndInactive) {
773 Result<std::vector<ApexInfo>> active_packages = GetActivePackages();
774 ASSERT_TRUE(IsOk(active_packages));
775 ASSERT_TRUE(active_packages->size() > 0);
776 Result<std::vector<ApexInfo>> inactive_packages = GetInactivePackages();
777 ASSERT_TRUE(IsOk(inactive_packages));
778 std::vector<std::string> active_packages_strings =
779 GetPackagesStrings(*active_packages);
780 std::vector<std::string> inactive_packages_strings =
781 GetPackagesStrings(*inactive_packages);
782 std::sort(active_packages_strings.begin(), active_packages_strings.end());
783 std::sort(inactive_packages_strings.begin(), inactive_packages_strings.end());
784 std::vector<std::string> intersection;
785 std::set_intersection(
786 active_packages_strings.begin(), active_packages_strings.end(),
787 inactive_packages_strings.begin(), inactive_packages_strings.end(),
788 std::back_inserter(intersection));
789 ASSERT_THAT(intersection, SizeIs(0));
790 }
791
TEST_F(ApexServiceTest,GetAllPackages)792 TEST_F(ApexServiceTest, GetAllPackages) {
793 Result<std::vector<ApexInfo>> all_packages = GetAllPackages();
794 ASSERT_TRUE(IsOk(all_packages));
795 ASSERT_TRUE(all_packages->size() > 0);
796 Result<std::vector<ApexInfo>> active_packages = GetActivePackages();
797 std::vector<std::string> active_strings =
798 GetPackagesStrings(*active_packages);
799 Result<std::vector<ApexInfo>> factory_packages = GetFactoryPackages();
800 std::vector<std::string> factory_strings =
801 GetPackagesStrings(*factory_packages);
802 for (ApexInfo& apexInfo : *all_packages) {
803 std::string package_string = GetPackageString(apexInfo);
804 bool should_be_active =
805 std::find(active_strings.begin(), active_strings.end(),
806 package_string) != active_strings.end();
807 bool should_be_factory =
808 std::find(factory_strings.begin(), factory_strings.end(),
809 package_string) != factory_strings.end();
810 ASSERT_EQ(should_be_active, apexInfo.isActive)
811 << package_string << " should " << (should_be_active ? "" : "not ")
812 << "be active";
813 ASSERT_EQ(should_be_factory, apexInfo.isFactory)
814 << package_string << " should " << (should_be_factory ? "" : "not ")
815 << "be factory";
816 }
817 }
818
TEST_F(ApexServiceTest,SubmitSingleSessionTestSuccess)819 TEST_F(ApexServiceTest, SubmitSingleSessionTestSuccess) {
820 PrepareTestApexForInstall installer(GetTestFile("apex.apexd_test.apex"),
821 "/data/app-staging/session_123",
822 "staging_data_file");
823 if (!installer.Prepare()) {
824 FAIL() << GetDebugStr(&installer);
825 }
826
827 ApexInfoList list;
828 ApexSessionParams params;
829 params.sessionId = 123;
830 ASSERT_TRUE(IsOk(service_->submitStagedSession(params, &list)))
831 << GetDebugStr(&installer);
832 EXPECT_EQ(1u, list.apexInfos.size());
833 ApexInfo match;
834 for (const ApexInfo& info : list.apexInfos) {
835 if (info.moduleName == installer.package) {
836 match = info;
837 break;
838 }
839 }
840
841 ASSERT_EQ(installer.package, match.moduleName);
842 ASSERT_EQ(installer.version, static_cast<uint64_t>(match.versionCode));
843 ASSERT_EQ(installer.test_file, match.modulePath);
844
845 ApexSessionInfo session;
846 ASSERT_TRUE(IsOk(service_->getStagedSessionInfo(123, &session)))
847 << GetDebugStr(&installer);
848 ApexSessionInfo expected = CreateSessionInfo(123);
849 expected.isVerified = true;
850 EXPECT_THAT(session, SessionInfoEq(expected));
851
852 ASSERT_TRUE(IsOk(service_->markStagedSessionReady(123)));
853 ASSERT_TRUE(IsOk(service_->getStagedSessionInfo(123, &session)))
854 << GetDebugStr(&installer);
855 expected.isVerified = false;
856 expected.isStaged = true;
857 EXPECT_THAT(session, SessionInfoEq(expected));
858
859 // Call markStagedSessionReady again. Should be a no-op.
860 ASSERT_TRUE(IsOk(service_->markStagedSessionReady(123)))
861 << GetDebugStr(&installer);
862
863 ASSERT_TRUE(IsOk(service_->getStagedSessionInfo(123, &session)))
864 << GetDebugStr(&installer);
865 EXPECT_THAT(session, SessionInfoEq(expected));
866
867 // See if the session is reported with getSessions() as well
868 std::vector<ApexSessionInfo> sessions;
869 ASSERT_TRUE(IsOk(service_->getSessions(&sessions)))
870 << GetDebugStr(&installer);
871 ASSERT_THAT(sessions, UnorderedElementsAre(SessionInfoEq(expected)));
872 }
873
TEST_F(ApexServiceTest,SubmitSingleStagedSessionKeepsPreviousSessions)874 TEST_F(ApexServiceTest, SubmitSingleStagedSessionKeepsPreviousSessions) {
875 PrepareTestApexForInstall installer(GetTestFile("apex.apexd_test.apex"),
876 "/data/app-staging/session_239",
877 "staging_data_file");
878 if (!installer.Prepare()) {
879 FAIL() << GetDebugStr(&installer);
880 }
881
882 // First simulate existence of a bunch of sessions.
883 auto session1 = ApexSession::CreateSession(37);
884 ASSERT_TRUE(IsOk(session1));
885 auto session2 = ApexSession::CreateSession(57);
886 ASSERT_TRUE(IsOk(session2));
887 auto session3 = ApexSession::CreateSession(73);
888 ASSERT_TRUE(IsOk(session3));
889 ASSERT_TRUE(IsOk(session1->UpdateStateAndCommit(SessionState::VERIFIED)));
890 ASSERT_TRUE(IsOk(session2->UpdateStateAndCommit(SessionState::STAGED)));
891 ASSERT_TRUE(IsOk(session3->UpdateStateAndCommit(SessionState::SUCCESS)));
892
893 std::vector<ApexSessionInfo> sessions;
894 ASSERT_TRUE(IsOk(service_->getSessions(&sessions)));
895
896 ApexSessionInfo expected_session1 = CreateSessionInfo(37);
897 expected_session1.isVerified = true;
898 ApexSessionInfo expected_session2 = CreateSessionInfo(57);
899 expected_session2.isStaged = true;
900 ApexSessionInfo expected_session3 = CreateSessionInfo(73);
901 expected_session3.isSuccess = true;
902 ASSERT_THAT(sessions, UnorderedElementsAre(SessionInfoEq(expected_session1),
903 SessionInfoEq(expected_session2),
904 SessionInfoEq(expected_session3)));
905
906 ApexInfoList list;
907 ApexSessionParams params;
908 params.sessionId = 239;
909 ASSERT_TRUE(IsOk(service_->submitStagedSession(params, &list)));
910
911 sessions.clear();
912 ASSERT_TRUE(IsOk(service_->getSessions(&sessions)));
913
914 ApexSessionInfo new_session = CreateSessionInfo(239);
915 new_session.isVerified = true;
916 ASSERT_THAT(sessions, UnorderedElementsAre(SessionInfoEq(new_session),
917 SessionInfoEq(expected_session1),
918 SessionInfoEq(expected_session2),
919 SessionInfoEq(expected_session3)));
920 }
921
TEST_F(ApexServiceTest,SubmitSingleSessionTestFail)922 TEST_F(ApexServiceTest, SubmitSingleSessionTestFail) {
923 PrepareTestApexForInstall installer(
924 GetTestFile("apex.apexd_test_corrupt_apex.apex"),
925 "/data/app-staging/session_456", "staging_data_file");
926 if (!installer.Prepare()) {
927 FAIL() << GetDebugStr(&installer);
928 }
929
930 ApexInfoList list;
931 ApexSessionParams params;
932 params.sessionId = 456;
933 ASSERT_FALSE(IsOk(service_->submitStagedSession(params, &list)))
934 << GetDebugStr(&installer);
935
936 ApexSessionInfo session;
937 ASSERT_TRUE(IsOk(service_->getStagedSessionInfo(456, &session)))
938 << GetDebugStr(&installer);
939 ApexSessionInfo expected = CreateSessionInfo(-1);
940 expected.isUnknown = true;
941 EXPECT_THAT(session, SessionInfoEq(expected));
942 }
943
TEST_F(ApexServiceTest,SubmitMultiSessionTestSuccess)944 TEST_F(ApexServiceTest, SubmitMultiSessionTestSuccess) {
945 // Parent session id: 10
946 // Children session ids: 20 30
947 PrepareTestApexForInstall installer(GetTestFile("apex.apexd_test.apex"),
948 "/data/app-staging/session_20",
949 "staging_data_file");
950 PrepareTestApexForInstall installer2(
951 GetTestFile("apex.apexd_test_different_app.apex"),
952 "/data/app-staging/session_30", "staging_data_file");
953 if (!installer.Prepare() || !installer2.Prepare()) {
954 FAIL() << GetDebugStr(&installer) << GetDebugStr(&installer2);
955 }
956
957 ApexInfoList list;
958 ApexSessionParams params;
959 params.sessionId = 10;
960 params.childSessionIds = {20, 30};
961 ASSERT_TRUE(IsOk(service_->submitStagedSession(params, &list)))
962 << GetDebugStr(&installer);
963 EXPECT_EQ(2u, list.apexInfos.size());
964 ApexInfo match;
965 bool package1_found = false;
966 bool package2_found = false;
967 for (const ApexInfo& info : list.apexInfos) {
968 if (info.moduleName == installer.package) {
969 ASSERT_EQ(installer.package, info.moduleName);
970 ASSERT_EQ(installer.version, static_cast<uint64_t>(info.versionCode));
971 ASSERT_EQ(installer.test_file, info.modulePath);
972 package1_found = true;
973 } else if (info.moduleName == installer2.package) {
974 ASSERT_EQ(installer2.package, info.moduleName);
975 ASSERT_EQ(installer2.version, static_cast<uint64_t>(info.versionCode));
976 ASSERT_EQ(installer2.test_file, info.modulePath);
977 package2_found = true;
978 } else {
979 FAIL() << "Unexpected package found " << info.moduleName
980 << GetDebugStr(&installer) << GetDebugStr(&installer2);
981 }
982 }
983 ASSERT_TRUE(package1_found);
984 ASSERT_TRUE(package2_found);
985
986 ApexSessionInfo session;
987 ASSERT_TRUE(IsOk(service_->getStagedSessionInfo(10, &session)))
988 << GetDebugStr(&installer);
989 ApexSessionInfo expected = CreateSessionInfo(10);
990 expected.isVerified = true;
991 ASSERT_THAT(session, SessionInfoEq(expected));
992
993 ASSERT_TRUE(IsOk(service_->markStagedSessionReady(10)))
994 << GetDebugStr(&installer);
995
996 ASSERT_TRUE(IsOk(service_->getStagedSessionInfo(10, &session)))
997 << GetDebugStr(&installer);
998 expected.isVerified = false;
999 expected.isStaged = true;
1000 ASSERT_THAT(session, SessionInfoEq(expected));
1001
1002 // Check that temp mounts were cleanded up.
1003 for (const auto& mount : GetApexMounts()) {
1004 EXPECT_FALSE(EndsWith(mount, ".tmp")) << "Found temp mount " << mount;
1005 }
1006 }
1007
TEST_F(ApexServiceTest,SubmitMultiSessionTestFail)1008 TEST_F(ApexServiceTest, SubmitMultiSessionTestFail) {
1009 // Parent session id: 11
1010 // Children session ids: 21 31
1011 PrepareTestApexForInstall installer(GetTestFile("apex.apexd_test.apex"),
1012 "/data/app-staging/session_21",
1013 "staging_data_file");
1014 PrepareTestApexForInstall installer2(
1015 GetTestFile("apex.apexd_test_corrupt_apex.apex"),
1016 "/data/app-staging/session_31", "staging_data_file");
1017 if (!installer.Prepare() || !installer2.Prepare()) {
1018 FAIL() << GetDebugStr(&installer) << GetDebugStr(&installer2);
1019 }
1020 ApexInfoList list;
1021 ApexSessionParams params;
1022 params.sessionId = 11;
1023 params.childSessionIds = {21, 31};
1024 ASSERT_FALSE(IsOk(service_->submitStagedSession(params, &list)))
1025 << GetDebugStr(&installer);
1026 }
1027
TEST_F(ApexServiceTest,MarkStagedSessionReadyFail)1028 TEST_F(ApexServiceTest, MarkStagedSessionReadyFail) {
1029 // We should fail if we ask information about a session we don't know.
1030 ASSERT_FALSE(IsOk(service_->markStagedSessionReady(666)));
1031
1032 ApexSessionInfo session;
1033 ASSERT_TRUE(IsOk(service_->getStagedSessionInfo(666, &session)));
1034 ApexSessionInfo expected = CreateSessionInfo(-1);
1035 expected.isUnknown = true;
1036 ASSERT_THAT(session, SessionInfoEq(expected));
1037 }
1038
TEST_F(ApexServiceTest,MarkStagedSessionSuccessfulFailsNoSession)1039 TEST_F(ApexServiceTest, MarkStagedSessionSuccessfulFailsNoSession) {
1040 ASSERT_FALSE(IsOk(service_->markStagedSessionSuccessful(37)));
1041
1042 ApexSessionInfo session_info;
1043 ASSERT_TRUE(IsOk(service_->getStagedSessionInfo(37, &session_info)));
1044 ApexSessionInfo expected = CreateSessionInfo(-1);
1045 expected.isUnknown = true;
1046 ASSERT_THAT(session_info, SessionInfoEq(expected));
1047 }
1048
TEST_F(ApexServiceTest,MarkStagedSessionSuccessfulFailsSessionInWrongState)1049 TEST_F(ApexServiceTest, MarkStagedSessionSuccessfulFailsSessionInWrongState) {
1050 auto session = ApexSession::CreateSession(73);
1051 ASSERT_TRUE(IsOk(session));
1052 ASSERT_TRUE(
1053 IsOk(session->UpdateStateAndCommit(::apex::proto::SessionState::STAGED)));
1054
1055 ASSERT_FALSE(IsOk(service_->markStagedSessionSuccessful(73)));
1056
1057 ApexSessionInfo session_info;
1058 ASSERT_TRUE(IsOk(service_->getStagedSessionInfo(73, &session_info)));
1059 ApexSessionInfo expected = CreateSessionInfo(73);
1060 expected.isStaged = true;
1061 ASSERT_THAT(session_info, SessionInfoEq(expected));
1062 }
1063
TEST_F(ApexServiceTest,MarkStagedSessionSuccessfulActivatedSession)1064 TEST_F(ApexServiceTest, MarkStagedSessionSuccessfulActivatedSession) {
1065 auto session = ApexSession::CreateSession(239);
1066 ASSERT_TRUE(IsOk(session));
1067 ASSERT_TRUE(IsOk(
1068 session->UpdateStateAndCommit(::apex::proto::SessionState::ACTIVATED)));
1069
1070 ASSERT_TRUE(IsOk(service_->markStagedSessionSuccessful(239)));
1071
1072 ApexSessionInfo session_info;
1073 ASSERT_TRUE(IsOk(service_->getStagedSessionInfo(239, &session_info)));
1074 ApexSessionInfo expected = CreateSessionInfo(239);
1075 expected.isSuccess = true;
1076 ASSERT_THAT(session_info, SessionInfoEq(expected));
1077 }
1078
TEST_F(ApexServiceTest,MarkStagedSessionSuccessfulNoOp)1079 TEST_F(ApexServiceTest, MarkStagedSessionSuccessfulNoOp) {
1080 auto session = ApexSession::CreateSession(1543);
1081 ASSERT_TRUE(IsOk(session));
1082 ASSERT_TRUE(IsOk(
1083 session->UpdateStateAndCommit(::apex::proto::SessionState::SUCCESS)));
1084
1085 ASSERT_TRUE(IsOk(service_->markStagedSessionSuccessful(1543)));
1086
1087 ApexSessionInfo session_info;
1088 ASSERT_TRUE(IsOk(service_->getStagedSessionInfo(1543, &session_info)));
1089 ApexSessionInfo expected = CreateSessionInfo(1543);
1090 expected.isSuccess = true;
1091 ASSERT_THAT(session_info, SessionInfoEq(expected));
1092 }
1093
1094 // Should be able to abort individual staged session
TEST_F(ApexServiceTest,AbortStagedSession)1095 TEST_F(ApexServiceTest, AbortStagedSession) {
1096 auto session1 = ApexSession::CreateSession(239);
1097 ASSERT_TRUE(IsOk(session1->UpdateStateAndCommit(SessionState::VERIFIED)));
1098 auto session2 = ApexSession::CreateSession(240);
1099 ASSERT_TRUE(IsOk(session2->UpdateStateAndCommit(SessionState::STAGED)));
1100
1101 std::vector<ApexSessionInfo> sessions;
1102 ASSERT_TRUE(IsOk(service_->getSessions(&sessions)));
1103 ASSERT_EQ(2u, sessions.size());
1104
1105 ASSERT_TRUE(IsOk(service_->abortStagedSession(239)));
1106
1107 sessions.clear();
1108 ASSERT_TRUE(IsOk(service_->getSessions(&sessions)));
1109 ApexSessionInfo expected = CreateSessionInfo(240);
1110 expected.isStaged = true;
1111 ASSERT_THAT(sessions, UnorderedElementsAre(SessionInfoEq(expected)));
1112 }
1113
1114 // abortStagedSession should not abort activated session
TEST_F(ApexServiceTest,AbortStagedSessionActivatedFail)1115 TEST_F(ApexServiceTest, AbortStagedSessionActivatedFail) {
1116 auto session1 = ApexSession::CreateSession(239);
1117 ASSERT_TRUE(IsOk(session1->UpdateStateAndCommit(SessionState::ACTIVATED)));
1118 auto session2 = ApexSession::CreateSession(240);
1119 ASSERT_TRUE(IsOk(session2->UpdateStateAndCommit(SessionState::STAGED)));
1120
1121 std::vector<ApexSessionInfo> sessions;
1122 ASSERT_TRUE(IsOk(service_->getSessions(&sessions)));
1123 ASSERT_EQ(2u, sessions.size());
1124
1125 ASSERT_FALSE(IsOk(service_->abortStagedSession(239)));
1126
1127 sessions.clear();
1128 ASSERT_TRUE(IsOk(service_->getSessions(&sessions)));
1129 ApexSessionInfo expected1 = CreateSessionInfo(239);
1130 expected1.isActivated = true;
1131 ApexSessionInfo expected2 = CreateSessionInfo(240);
1132 expected2.isStaged = true;
1133 ASSERT_THAT(sessions, UnorderedElementsAre(SessionInfoEq(expected1),
1134 SessionInfoEq(expected2)));
1135 }
1136
1137 // Only finalized sessions should be deleted on DeleteFinalizedSessions()
TEST_F(ApexServiceTest,DeleteFinalizedSessions)1138 TEST_F(ApexServiceTest, DeleteFinalizedSessions) {
1139 // Fetch list of all session state
1140 std::vector<SessionState::State> states;
1141 for (int i = SessionState::State_MIN; i < SessionState::State_MAX; i++) {
1142 if (!SessionState::State_IsValid(i)) {
1143 continue;
1144 }
1145 states.push_back(SessionState::State(i));
1146 }
1147
1148 // For every session state, create a new session. This is to verify we only
1149 // delete sessions in final state.
1150 auto nonFinalSessions = 0u;
1151 for (auto i = 0u; i < states.size(); i++) {
1152 auto session = ApexSession::CreateSession(230 + i);
1153 SessionState::State state = states[i];
1154 ASSERT_TRUE(IsOk(session->UpdateStateAndCommit(state)));
1155 if (!session->IsFinalized()) {
1156 nonFinalSessions++;
1157 }
1158 }
1159 std::vector<ApexSession> sessions = ApexSession::GetSessions();
1160 ASSERT_EQ(states.size(), sessions.size());
1161
1162 // Now try cleaning up all finalized sessions
1163 ApexSession::DeleteFinalizedSessions();
1164 sessions = ApexSession::GetSessions();
1165 ASSERT_EQ(nonFinalSessions, sessions.size());
1166
1167 // Verify only finalized sessions have been deleted
1168 for (auto& session : sessions) {
1169 ASSERT_FALSE(session.IsFinalized());
1170 }
1171 }
1172
TEST_F(ApexServiceTest,BackupActivePackages)1173 TEST_F(ApexServiceTest, BackupActivePackages) {
1174 if (supports_fs_checkpointing_) {
1175 GTEST_SKIP() << "Can't run if filesystem checkpointing is enabled";
1176 }
1177 PrepareTestApexForInstall installer1(GetTestFile("apex.apexd_test.apex"));
1178 PrepareTestApexForInstall installer2(
1179 GetTestFile("apex.apexd_test_different_app.apex"));
1180 PrepareTestApexForInstall installer3(GetTestFile("apex.apexd_test_v2.apex"),
1181 "/data/app-staging/session_23",
1182 "staging_data_file");
1183
1184 if (!installer1.Prepare() || !installer2.Prepare() || !installer3.Prepare()) {
1185 return;
1186 }
1187
1188 // Activate some packages, in order to backup them later.
1189 std::vector<std::string> pkgs = {installer1.test_file, installer2.test_file};
1190 ASSERT_TRUE(IsOk(service_->stagePackages(pkgs)));
1191
1192 // Make sure that /data/apex/active has activated packages.
1193 auto active_pkgs = ReadEntireDir(kActiveApexPackagesDataDir);
1194 ASSERT_TRUE(IsOk(active_pkgs));
1195 ASSERT_THAT(*active_pkgs,
1196 UnorderedElementsAre(installer1.test_installed_file,
1197 installer2.test_installed_file));
1198
1199 ApexInfoList list;
1200 ApexSessionParams params;
1201 params.sessionId = 23;
1202 ASSERT_TRUE(IsOk(service_->submitStagedSession(params, &list)));
1203
1204 auto backups = ReadEntireDir(kApexBackupDir);
1205 ASSERT_TRUE(IsOk(backups));
1206 auto backup1 =
1207 StringPrintf("%s/com.android.apex.test_package@1.apex", kApexBackupDir);
1208 auto backup2 =
1209 StringPrintf("%s/com.android.apex.test_package_2@1.apex", kApexBackupDir);
1210 ASSERT_THAT(*backups, UnorderedElementsAre(backup1, backup2));
1211 }
1212
TEST_F(ApexServiceTest,BackupActivePackagesClearsPreviousBackup)1213 TEST_F(ApexServiceTest, BackupActivePackagesClearsPreviousBackup) {
1214 if (supports_fs_checkpointing_) {
1215 GTEST_SKIP() << "Can't run if filesystem checkpointing is enabled";
1216 }
1217 PrepareTestApexForInstall installer1(GetTestFile("apex.apexd_test.apex"));
1218 PrepareTestApexForInstall installer2(
1219 GetTestFile("apex.apexd_test_different_app.apex"));
1220 PrepareTestApexForInstall installer3(GetTestFile("apex.apexd_test_v2.apex"),
1221 "/data/app-staging/session_43",
1222 "staging_data_file");
1223
1224 if (!installer1.Prepare() || !installer2.Prepare() || !installer3.Prepare()) {
1225 return;
1226 }
1227
1228 // Make sure /data/apex/backups exists.
1229 ASSERT_TRUE(IsOk(CreateDirIfNeeded(std::string(kApexBackupDir), 0700)));
1230 // Create some bogus files in /data/apex/backups.
1231 std::ofstream old_backup(StringPrintf("%s/file1", kApexBackupDir));
1232 ASSERT_TRUE(old_backup.good());
1233 old_backup.close();
1234
1235 std::vector<std::string> pkgs = {installer1.test_file, installer2.test_file};
1236 ASSERT_TRUE(IsOk(service_->stagePackages(pkgs)));
1237
1238 // Make sure that /data/apex/active has activated packages.
1239 auto active_pkgs = ReadEntireDir(kActiveApexPackagesDataDir);
1240 ASSERT_TRUE(IsOk(active_pkgs));
1241 ASSERT_THAT(*active_pkgs,
1242 UnorderedElementsAre(installer1.test_installed_file,
1243 installer2.test_installed_file));
1244
1245 ApexInfoList list;
1246 ApexSessionParams params;
1247 params.sessionId = 43;
1248 ASSERT_TRUE(IsOk(service_->submitStagedSession(params, &list)));
1249
1250 auto backups = ReadEntireDir(kApexBackupDir);
1251 ASSERT_TRUE(IsOk(backups));
1252 auto backup1 =
1253 StringPrintf("%s/com.android.apex.test_package@1.apex", kApexBackupDir);
1254 auto backup2 =
1255 StringPrintf("%s/com.android.apex.test_package_2@1.apex", kApexBackupDir);
1256 ASSERT_THAT(*backups, UnorderedElementsAre(backup1, backup2));
1257 }
1258
TEST_F(ApexServiceTest,BackupActivePackagesZeroActivePackages)1259 TEST_F(ApexServiceTest, BackupActivePackagesZeroActivePackages) {
1260 if (supports_fs_checkpointing_) {
1261 GTEST_SKIP() << "Can't run if filesystem checkpointing is enabled";
1262 }
1263 PrepareTestApexForInstall installer(GetTestFile("apex.apexd_test_v2.apex"),
1264 "/data/app-staging/session_41",
1265 "staging_data_file");
1266
1267 if (!installer.Prepare()) {
1268 return;
1269 }
1270
1271 // Make sure that /data/apex/active exists and is empty
1272 ASSERT_TRUE(
1273 IsOk(CreateDirIfNeeded(std::string(kActiveApexPackagesDataDir), 0755)));
1274 auto active_pkgs = ReadEntireDir(kActiveApexPackagesDataDir);
1275 ASSERT_TRUE(IsOk(active_pkgs));
1276 ASSERT_EQ(0u, active_pkgs->size());
1277
1278 ApexInfoList list;
1279 ApexSessionParams params;
1280 params.sessionId = 41;
1281 ASSERT_TRUE(IsOk(service_->submitStagedSession(params, &list)));
1282
1283 auto backups = ReadEntireDir(kApexBackupDir);
1284 ASSERT_TRUE(IsOk(backups));
1285 ASSERT_EQ(0u, backups->size());
1286 }
1287
TEST_F(ApexServiceTest,ActivePackagesDirEmpty)1288 TEST_F(ApexServiceTest, ActivePackagesDirEmpty) {
1289 PrepareTestApexForInstall installer(GetTestFile("apex.apexd_test_v2.apex"),
1290 "/data/app-staging/session_41",
1291 "staging_data_file");
1292
1293 if (!installer.Prepare()) {
1294 return;
1295 }
1296
1297 // Make sure that /data/apex/active is empty
1298 DeleteDirContent(kActiveApexPackagesDataDir);
1299
1300 ApexInfoList list;
1301 ApexSessionParams params;
1302 params.sessionId = 41;
1303 ASSERT_TRUE(IsOk(service_->submitStagedSession(params, &list)));
1304
1305 if (!supports_fs_checkpointing_) {
1306 auto backups = ReadEntireDir(kApexBackupDir);
1307 ASSERT_TRUE(IsOk(backups));
1308 ASSERT_EQ(0u, backups->size());
1309 }
1310 }
1311
1312 class ApexServiceRevertTest : public ApexServiceTest {
1313 protected:
SetUp()1314 void SetUp() override { ApexServiceTest::SetUp(); }
1315
PrepareBackup(const std::vector<std::string> & pkgs)1316 void PrepareBackup(const std::vector<std::string>& pkgs) {
1317 ASSERT_TRUE(IsOk(CreateDirIfNeeded(std::string(kApexBackupDir), 0700)));
1318 for (const auto& pkg : pkgs) {
1319 PrepareTestApexForInstall installer(pkg);
1320 ASSERT_TRUE(installer.Prepare()) << " failed to prepare " << pkg;
1321 const std::string& from = installer.test_file;
1322 std::string to = std::string(kApexBackupDir) + "/" + installer.package +
1323 "@" + std::to_string(installer.version) + ".apex";
1324 std::error_code ec;
1325 fs::copy(fs::path(from), fs::path(to),
1326 fs::copy_options::create_hard_links, ec);
1327 ASSERT_FALSE(ec) << "Failed to copy " << from << " to " << to << " : "
1328 << ec;
1329 }
1330 }
1331
CheckActiveApexContents(const std::vector<std::string> & expected_pkgs)1332 void CheckActiveApexContents(const std::vector<std::string>& expected_pkgs) {
1333 // First check that /data/apex/active exists and has correct permissions.
1334 struct stat sd;
1335 ASSERT_EQ(0, stat(kActiveApexPackagesDataDir, &sd));
1336 ASSERT_EQ(0755u, sd.st_mode & ALLPERMS);
1337
1338 // Now read content and check it contains expected values.
1339 auto active_pkgs = ReadEntireDir(kActiveApexPackagesDataDir);
1340 ASSERT_TRUE(IsOk(active_pkgs));
1341 ASSERT_THAT(*active_pkgs, UnorderedElementsAreArray(expected_pkgs));
1342 }
1343 };
1344
1345 // Should be able to revert activated sessions
TEST_F(ApexServiceRevertTest,RevertActiveSessionsSuccessful)1346 TEST_F(ApexServiceRevertTest, RevertActiveSessionsSuccessful) {
1347 if (supports_fs_checkpointing_) {
1348 GTEST_SKIP() << "Can't run if filesystem checkpointing is enabled";
1349 }
1350
1351 PrepareTestApexForInstall installer(GetTestFile("apex.apexd_test_v2.apex"));
1352 if (!installer.Prepare()) {
1353 return;
1354 }
1355
1356 auto session = ApexSession::CreateSession(1543);
1357 ASSERT_TRUE(IsOk(session));
1358 ASSERT_TRUE(IsOk(session->UpdateStateAndCommit(SessionState::ACTIVATED)));
1359
1360 // Make sure /data/apex/active is non-empty.
1361 ASSERT_TRUE(IsOk(service_->stagePackages({installer.test_file})));
1362
1363 PrepareBackup({GetTestFile("apex.apexd_test.apex")});
1364
1365 ASSERT_TRUE(IsOk(service_->revertActiveSessions()));
1366
1367 auto pkg = StringPrintf("%s/com.android.apex.test_package@1.apex",
1368 kActiveApexPackagesDataDir);
1369 SCOPED_TRACE("");
1370 CheckActiveApexContents({pkg});
1371 }
1372
1373 // Calling revertActiveSessions should not restore backup on checkpointing
1374 // devices
TEST_F(ApexServiceRevertTest,RevertActiveSessionsDoesNotRestoreBackupIfCheckpointingSupported)1375 TEST_F(ApexServiceRevertTest,
1376 RevertActiveSessionsDoesNotRestoreBackupIfCheckpointingSupported) {
1377 if (!supports_fs_checkpointing_) {
1378 GTEST_SKIP() << "Can't run if filesystem checkpointing is not supported";
1379 }
1380
1381 PrepareTestApexForInstall installer(GetTestFile("apex.apexd_test_v2.apex"));
1382 if (!installer.Prepare()) {
1383 return;
1384 }
1385
1386 auto session = ApexSession::CreateSession(1543);
1387 ASSERT_TRUE(IsOk(session));
1388 ASSERT_TRUE(IsOk(session->UpdateStateAndCommit(SessionState::ACTIVATED)));
1389
1390 // Make sure /data/apex/active is non-empty.
1391 ASSERT_TRUE(IsOk(service_->stagePackages({installer.test_file})));
1392
1393 PrepareBackup({GetTestFile("apex.apexd_test.apex")});
1394
1395 ASSERT_TRUE(IsOk(service_->revertActiveSessions()));
1396
1397 // Check that active apexes were not reverted.
1398 auto pkg = StringPrintf("%s/com.android.apex.test_package@2.apex",
1399 kActiveApexPackagesDataDir);
1400 SCOPED_TRACE("");
1401 CheckActiveApexContents({pkg});
1402 }
1403
1404 // Should fail to revert active sessions when there are none
TEST_F(ApexServiceRevertTest,RevertActiveSessionsWithoutActiveSessions)1405 TEST_F(ApexServiceRevertTest, RevertActiveSessionsWithoutActiveSessions) {
1406 // This test simulates a situation that should never happen on user builds:
1407 // revertActiveSessions was called, but there were no active sessions.
1408 PrepareTestApexForInstall installer(GetTestFile("apex.apexd_test_v2.apex"));
1409 if (!installer.Prepare()) {
1410 return;
1411 }
1412
1413 // Make sure /data/apex/active is non-empty.
1414 ASSERT_TRUE(IsOk(service_->stagePackages({installer.test_file})));
1415
1416 PrepareBackup({GetTestFile("apex.apexd_test.apex")});
1417
1418 // Even though backup is there, no sessions are active, hence revert request
1419 // should fail.
1420 ASSERT_FALSE(IsOk(service_->revertActiveSessions()));
1421 }
1422
TEST_F(ApexServiceRevertTest,RevertFailsNoBackupFolder)1423 TEST_F(ApexServiceRevertTest, RevertFailsNoBackupFolder) {
1424 ASSERT_FALSE(IsOk(service_->revertActiveSessions()));
1425 }
1426
TEST_F(ApexServiceRevertTest,RevertFailsNoActivePackagesFolder)1427 TEST_F(ApexServiceRevertTest, RevertFailsNoActivePackagesFolder) {
1428 PrepareTestApexForInstall installer(GetTestFile("apex.apexd_test.apex"));
1429 ASSERT_FALSE(IsOk(service_->revertActiveSessions()));
1430 }
1431
TEST_F(ApexServiceRevertTest,MarkStagedSessionSuccessfulCleanupBackup)1432 TEST_F(ApexServiceRevertTest, MarkStagedSessionSuccessfulCleanupBackup) {
1433 PrepareBackup({GetTestFile("apex.apexd_test.apex"),
1434 GetTestFile("apex.apexd_test_different_app.apex")});
1435
1436 auto session = ApexSession::CreateSession(101);
1437 ASSERT_TRUE(IsOk(session));
1438 ASSERT_TRUE(IsOk(session->UpdateStateAndCommit(SessionState::ACTIVATED)));
1439
1440 ASSERT_TRUE(IsOk(service_->markStagedSessionSuccessful(101)));
1441
1442 ASSERT_TRUE(fs::is_empty(fs::path(kApexBackupDir)));
1443 }
1444
TEST_F(ApexServiceRevertTest,ResumesRevert)1445 TEST_F(ApexServiceRevertTest, ResumesRevert) {
1446 if (supports_fs_checkpointing_) {
1447 GTEST_SKIP() << "Can't run if filesystem checkpointing is enabled";
1448 }
1449 PrepareBackup({GetTestFile("apex.apexd_test.apex"),
1450 GetTestFile("apex.apexd_test_different_app.apex")});
1451
1452 PrepareTestApexForInstall installer(GetTestFile("apex.apexd_test_v2.apex"));
1453 if (!installer.Prepare()) {
1454 return;
1455 }
1456
1457 // Make sure /data/apex/active is non-empty.
1458 ASSERT_TRUE(IsOk(service_->stagePackages({installer.test_file})));
1459
1460 auto session = ApexSession::CreateSession(17239);
1461 ASSERT_TRUE(IsOk(session));
1462 ASSERT_TRUE(
1463 IsOk(session->UpdateStateAndCommit(SessionState::REVERT_IN_PROGRESS)));
1464
1465 ASSERT_TRUE(IsOk(service_->resumeRevertIfNeeded()));
1466
1467 auto pkg1 = StringPrintf("%s/com.android.apex.test_package@1.apex",
1468 kActiveApexPackagesDataDir);
1469 auto pkg2 = StringPrintf("%s/com.android.apex.test_package_2@1.apex",
1470 kActiveApexPackagesDataDir);
1471 SCOPED_TRACE("");
1472 CheckActiveApexContents({pkg1, pkg2});
1473
1474 std::vector<ApexSessionInfo> sessions;
1475 ASSERT_TRUE(IsOk(service_->getSessions(&sessions)));
1476 ApexSessionInfo expected = CreateSessionInfo(17239);
1477 expected.isReverted = true;
1478 ASSERT_THAT(sessions, UnorderedElementsAre(SessionInfoEq(expected)));
1479 }
1480
TEST_F(ApexServiceRevertTest,DoesNotResumeRevert)1481 TEST_F(ApexServiceRevertTest, DoesNotResumeRevert) {
1482 if (supports_fs_checkpointing_) {
1483 GTEST_SKIP() << "Can't run if filesystem checkpointing is enabled";
1484 }
1485 PrepareTestApexForInstall installer(GetTestFile("apex.apexd_test_v2.apex"));
1486 if (!installer.Prepare()) {
1487 return;
1488 }
1489
1490 // Make sure /data/apex/active is non-empty.
1491 ASSERT_TRUE(IsOk(service_->stagePackages({installer.test_file})));
1492
1493 auto session = ApexSession::CreateSession(53);
1494 ASSERT_TRUE(IsOk(session));
1495 ASSERT_TRUE(IsOk(session->UpdateStateAndCommit(SessionState::SUCCESS)));
1496
1497 ASSERT_TRUE(IsOk(service_->resumeRevertIfNeeded()));
1498
1499 // Check that revert wasn't resumed.
1500 auto active_pkgs = ReadEntireDir(kActiveApexPackagesDataDir);
1501 ASSERT_TRUE(IsOk(active_pkgs));
1502 ASSERT_THAT(*active_pkgs,
1503 UnorderedElementsAre(installer.test_installed_file));
1504
1505 std::vector<ApexSessionInfo> sessions;
1506 ASSERT_TRUE(IsOk(service_->getSessions(&sessions)));
1507 ApexSessionInfo expected = CreateSessionInfo(53);
1508 expected.isSuccess = true;
1509 ASSERT_THAT(sessions, UnorderedElementsAre(SessionInfoEq(expected)));
1510 }
1511
1512 // Should mark sessions as REVERT_FAILED on failed revert
TEST_F(ApexServiceRevertTest,SessionsMarkedAsRevertFailed)1513 TEST_F(ApexServiceRevertTest, SessionsMarkedAsRevertFailed) {
1514 if (supports_fs_checkpointing_) {
1515 GTEST_SKIP() << "Can't run if filesystem checkpointing is enabled";
1516 }
1517
1518 auto session = ApexSession::CreateSession(53);
1519 ASSERT_TRUE(IsOk(session));
1520 ASSERT_TRUE(IsOk(session->UpdateStateAndCommit(SessionState::ACTIVATED)));
1521
1522 ASSERT_FALSE(IsOk(service_->revertActiveSessions()));
1523 ApexSessionInfo session_info;
1524 ASSERT_TRUE(IsOk(service_->getStagedSessionInfo(53, &session_info)));
1525 ApexSessionInfo expected = CreateSessionInfo(53);
1526 expected.isRevertFailed = true;
1527 ASSERT_THAT(session_info, SessionInfoEq(expected));
1528 }
1529
TEST_F(ApexServiceRevertTest,RevertFailedStateRevertAttemptFails)1530 TEST_F(ApexServiceRevertTest, RevertFailedStateRevertAttemptFails) {
1531 if (supports_fs_checkpointing_) {
1532 GTEST_SKIP() << "Can't run if filesystem checkpointing is enabled";
1533 }
1534
1535 auto session = ApexSession::CreateSession(17239);
1536 ASSERT_TRUE(IsOk(session));
1537 ASSERT_TRUE(IsOk(session->UpdateStateAndCommit(SessionState::REVERT_FAILED)));
1538
1539 ASSERT_FALSE(IsOk(service_->revertActiveSessions()));
1540 ApexSessionInfo session_info;
1541 ASSERT_TRUE(IsOk(service_->getStagedSessionInfo(17239, &session_info)));
1542 ApexSessionInfo expected = CreateSessionInfo(17239);
1543 expected.isRevertFailed = true;
1544 ASSERT_THAT(session_info, SessionInfoEq(expected));
1545 }
1546
GetPidOf(const std::string & name)1547 static pid_t GetPidOf(const std::string& name) {
1548 char buf[1024];
1549 const std::string cmd = std::string("pidof -s ") + name;
1550 FILE* cmd_pipe = popen(cmd.c_str(), "r"); // NOLINT(cert-env33-c): test code
1551 if (cmd_pipe == nullptr) {
1552 PLOG(ERROR) << "Cannot open pipe for " << cmd;
1553 return 0;
1554 }
1555 if (fgets(buf, 1024, cmd_pipe) == nullptr) {
1556 PLOG(ERROR) << "Cannot read pipe for " << cmd;
1557 pclose(cmd_pipe);
1558 return 0;
1559 }
1560
1561 pclose(cmd_pipe);
1562 return strtoul(buf, nullptr, 10);
1563 }
1564
ExecInMountNamespaceOf(pid_t pid,const std::function<void (pid_t)> & func)1565 static void ExecInMountNamespaceOf(pid_t pid,
1566 const std::function<void(pid_t)>& func) {
1567 const std::string my_path = "/proc/self/ns/mnt";
1568 android::base::unique_fd my_fd(open(my_path.c_str(), O_RDONLY | O_CLOEXEC));
1569 ASSERT_TRUE(my_fd.get() >= 0);
1570
1571 const std::string target_path =
1572 std::string("/proc/") + std::to_string(pid) + "/ns/mnt";
1573 android::base::unique_fd target_fd(
1574 open(target_path.c_str(), O_RDONLY | O_CLOEXEC));
1575 ASSERT_TRUE(target_fd.get() >= 0);
1576
1577 int res = setns(target_fd.get(), CLONE_NEWNS);
1578 ASSERT_NE(-1, res);
1579
1580 func(pid);
1581
1582 res = setns(my_fd.get(), CLONE_NEWNS);
1583 ASSERT_NE(-1, res);
1584 }
1585
1586 // This test case is part of the ApexServiceTest suite to ensure that apexd is
1587 // running when this test is executed.
TEST_F(ApexServiceTest,ApexdIsInSameMountNamespaceAsInit)1588 TEST_F(ApexServiceTest, ApexdIsInSameMountNamespaceAsInit) {
1589 // TODO(b/136647373): Move this check to environment setup
1590 if (!android::base::GetBoolProperty("ro.apex.updatable", false)) {
1591 GTEST_SKIP() << "Skipping test because device doesn't support APEX";
1592 }
1593 std::string ns_apexd;
1594 std::string ns_init;
1595
1596 ExecInMountNamespaceOf(GetPidOf("apexd"), [&](pid_t /*pid*/) {
1597 bool res = android::base::Readlink("/proc/self/ns/mnt", &ns_apexd);
1598 ASSERT_TRUE(res);
1599 });
1600
1601 ExecInMountNamespaceOf(1, [&](pid_t /*pid*/) {
1602 bool res = android::base::Readlink("/proc/self/ns/mnt", &ns_init);
1603 ASSERT_TRUE(res);
1604 });
1605
1606 ASSERT_EQ(ns_apexd, ns_init);
1607 }
1608
1609 // These are NOT exhaustive list of early processes be should be enough
1610 static const std::vector<const std::string> kEarlyProcesses = {
1611 "vold",
1612 "logd",
1613 };
1614
1615 // This test case is part of the ApexServiceTest suite to ensure that apexd is
1616 // running when this test is executed.
TEST_F(ApexServiceTest,EarlyProcessesAreInDifferentMountNamespace)1617 TEST_F(ApexServiceTest, EarlyProcessesAreInDifferentMountNamespace) {
1618 // TODO(b/136647373): Move this check to environment setup
1619 if (!android::base::GetBoolProperty("ro.apex.updatable", false)) {
1620 GTEST_SKIP() << "Skipping test because device doesn't support APEX";
1621 }
1622 std::string ns_apexd;
1623
1624 ExecInMountNamespaceOf(GetPidOf("apexd"), [&](pid_t /*pid*/) {
1625 bool res = android::base::Readlink("/proc/self/ns/mnt", &ns_apexd);
1626 ASSERT_TRUE(res);
1627 });
1628
1629 for (const auto& name : kEarlyProcesses) {
1630 std::string ns_early_process;
1631 ExecInMountNamespaceOf(GetPidOf(name), [&](pid_t /*pid*/) {
1632 bool res =
1633 android::base::Readlink("/proc/self/ns/mnt", &ns_early_process);
1634 ASSERT_TRUE(res);
1635 });
1636 ASSERT_NE(ns_apexd, ns_early_process);
1637 }
1638 }
1639
TEST(ApexdTest,ApexIsAPrivateMountPoint)1640 TEST(ApexdTest, ApexIsAPrivateMountPoint) {
1641 // TODO(b/136647373): Move this check to environment setup
1642 if (!android::base::GetBoolProperty("ro.apex.updatable", false)) {
1643 GTEST_SKIP() << "Skipping test because device doesn't support APEX";
1644 }
1645 std::string mountinfo;
1646 ASSERT_TRUE(
1647 android::base::ReadFileToString("/proc/self/mountinfo", &mountinfo));
1648 bool found_apex_mountpoint = false;
1649 for (const auto& line : android::base::Split(mountinfo, "\n")) {
1650 std::vector<std::string> tokens = android::base::Split(line, " ");
1651 // line format:
1652 // mnt_id parent_mnt_id major:minor source target option propagation_type
1653 // ex) 33 260:19 / /apex rw,nosuid,nodev -
1654 if (tokens.size() >= 7 && tokens[4] == "/apex") {
1655 found_apex_mountpoint = true;
1656 // Make sure that propagation type is set to - which means private
1657 ASSERT_EQ("-", tokens[6]);
1658 }
1659 }
1660 ASSERT_TRUE(found_apex_mountpoint);
1661 }
1662
1663 static const std::vector<const std::string> kEarlyApexes = {
1664 "/apex/com.android.runtime",
1665 "/apex/com.android.tzdata",
1666 };
1667
TEST(ApexdTest,ApexesAreActivatedForEarlyProcesses)1668 TEST(ApexdTest, ApexesAreActivatedForEarlyProcesses) {
1669 // TODO(b/136647373): Move this check to environment setup
1670 if (!android::base::GetBoolProperty("ro.apex.updatable", false)) {
1671 GTEST_SKIP() << "Skipping test because device doesn't support APEX";
1672 }
1673 for (const auto& name : kEarlyProcesses) {
1674 pid_t pid = GetPidOf(name);
1675 const std::string path =
1676 std::string("/proc/") + std::to_string(pid) + "/mountinfo";
1677 std::string mountinfo;
1678 ASSERT_TRUE(android::base::ReadFileToString(path.c_str(), &mountinfo));
1679
1680 std::unordered_set<std::string> mountpoints;
1681 for (const auto& line : android::base::Split(mountinfo, "\n")) {
1682 std::vector<std::string> tokens = android::base::Split(line, " ");
1683 // line format:
1684 // mnt_id parent_mnt_id major:minor source target option propagation_type
1685 // ex) 69 33 7:40 / /apex/com.android.conscrypt ro,nodev,noatime -
1686 if (tokens.size() >= 5) {
1687 // token[4] is the target mount point
1688 mountpoints.emplace(tokens[4]);
1689 }
1690 }
1691 for (const auto& apex_name : kEarlyApexes) {
1692 ASSERT_NE(mountpoints.end(), mountpoints.find(apex_name));
1693 }
1694 }
1695 }
1696
1697 class ApexShimUpdateTest : public ApexServiceTest {
1698 protected:
SetUp()1699 void SetUp() override {
1700 // TODO(b/136647373): Move this check to environment setup
1701 if (!android::base::GetBoolProperty("ro.apex.updatable", false)) {
1702 GTEST_SKIP() << "Skipping test because device doesn't support APEX";
1703 }
1704 ApexServiceTest::SetUp();
1705
1706 // Skip test if for some reason shim APEX is missing.
1707 std::vector<ApexInfo> list;
1708 ASSERT_TRUE(IsOk(service_->getAllPackages(&list)));
1709 bool found = std::any_of(list.begin(), list.end(), [](const auto& apex) {
1710 return apex.moduleName == "com.android.apex.cts.shim";
1711 });
1712 if (!found) {
1713 GTEST_SKIP() << "Can't find com.android.apex.cts.shim";
1714 }
1715 }
1716 };
1717
TEST_F(ApexShimUpdateTest,UpdateToV2Success)1718 TEST_F(ApexShimUpdateTest, UpdateToV2Success) {
1719 PrepareTestApexForInstall installer(
1720 GetTestFile("com.android.apex.cts.shim.v2.apex"));
1721
1722 if (!installer.Prepare()) {
1723 FAIL() << GetDebugStr(&installer);
1724 }
1725
1726 ASSERT_TRUE(IsOk(service_->stagePackages({installer.test_file})));
1727 }
1728
TEST_F(ApexShimUpdateTest,SubmitStagedSessionFailureHasPreInstallHook)1729 TEST_F(ApexShimUpdateTest, SubmitStagedSessionFailureHasPreInstallHook) {
1730 PrepareTestApexForInstall installer(
1731 GetTestFile("com.android.apex.cts.shim.v2_with_pre_install_hook.apex"),
1732 "/data/app-staging/session_23", "staging_data_file");
1733
1734 if (!installer.Prepare()) {
1735 FAIL() << GetDebugStr(&installer);
1736 }
1737
1738 ApexInfoList list;
1739 ApexSessionParams params;
1740 params.sessionId = 23;
1741 ASSERT_FALSE(IsOk(service_->submitStagedSession(params, &list)));
1742 }
1743
TEST_F(ApexShimUpdateTest,SubmitStagedSessionFailureHasPostInstallHook)1744 TEST_F(ApexShimUpdateTest, SubmitStagedSessionFailureHasPostInstallHook) {
1745 PrepareTestApexForInstall installer(
1746 GetTestFile("com.android.apex.cts.shim.v2_with_post_install_hook.apex"),
1747 "/data/app-staging/session_43", "staging_data_file");
1748
1749 if (!installer.Prepare()) {
1750 FAIL() << GetDebugStr(&installer);
1751 }
1752
1753 ApexInfoList list;
1754 ApexSessionParams params;
1755 params.sessionId = 43;
1756 ASSERT_FALSE(IsOk(service_->submitStagedSession(params, &list)));
1757 }
1758
TEST_F(ApexShimUpdateTest,SubmitStagedSessionFailureAdditionalFile)1759 TEST_F(ApexShimUpdateTest, SubmitStagedSessionFailureAdditionalFile) {
1760 PrepareTestApexForInstall installer(
1761 GetTestFile("com.android.apex.cts.shim.v2_additional_file.apex"),
1762 "/data/app-staging/session_41", "staging_data_file");
1763 if (!installer.Prepare()) {
1764 FAIL() << GetDebugStr(&installer);
1765 }
1766
1767 ApexInfoList list;
1768 ApexSessionParams params;
1769 params.sessionId = 41;
1770 ASSERT_FALSE(IsOk(service_->submitStagedSession(params, &list)));
1771 }
1772
TEST_F(ApexShimUpdateTest,SubmitStagedSessionFailureAdditionalFolder)1773 TEST_F(ApexShimUpdateTest, SubmitStagedSessionFailureAdditionalFolder) {
1774 PrepareTestApexForInstall installer(
1775 GetTestFile("com.android.apex.cts.shim.v2_additional_folder.apex"),
1776 "/data/app-staging/session_42", "staging_data_file");
1777 if (!installer.Prepare()) {
1778 FAIL() << GetDebugStr(&installer);
1779 }
1780
1781 ApexInfoList list;
1782 ApexSessionParams params;
1783 params.sessionId = 42;
1784 ASSERT_FALSE(IsOk(service_->submitStagedSession(params, &list)));
1785 }
1786
TEST_F(ApexShimUpdateTest,UpdateToV1Success)1787 TEST_F(ApexShimUpdateTest, UpdateToV1Success) {
1788 PrepareTestApexForInstall installer(
1789 GetTestFile("com.android.apex.cts.shim.apex"));
1790
1791 if (!installer.Prepare()) {
1792 FAIL() << GetDebugStr(&installer);
1793 }
1794
1795 ASSERT_TRUE(IsOk(service_->stagePackages({installer.test_file})));
1796 }
1797
TEST_F(ApexShimUpdateTest,SubmitStagedSessionV1ShimApexSuccess)1798 TEST_F(ApexShimUpdateTest, SubmitStagedSessionV1ShimApexSuccess) {
1799 PrepareTestApexForInstall installer(
1800 GetTestFile("com.android.apex.cts.shim.apex"),
1801 "/data/app-staging/session_97", "staging_data_file");
1802 if (!installer.Prepare()) {
1803 FAIL() << GetDebugStr(&installer);
1804 }
1805
1806 ApexInfoList list;
1807 ApexSessionParams params;
1808 params.sessionId = 97;
1809 ASSERT_TRUE(IsOk(service_->submitStagedSession(params, &list)));
1810 }
1811
TEST_F(ApexServiceTest,SubmitStagedSessionCorruptApexFails)1812 TEST_F(ApexServiceTest, SubmitStagedSessionCorruptApexFails) {
1813 PrepareTestApexForInstall installer(
1814 GetTestFile("apex.apexd_test_corrupt_apex.apex"),
1815 "/data/app-staging/session_57", "staging_data_file");
1816
1817 if (!installer.Prepare()) {
1818 FAIL() << GetDebugStr(&installer);
1819 }
1820
1821 ApexInfoList list;
1822 ApexSessionParams params;
1823 params.sessionId = 57;
1824 ASSERT_FALSE(IsOk(service_->submitStagedSession(params, &list)));
1825 }
1826
TEST_F(ApexServiceTest,SubmitStagedSessionCorruptApexFailsB146895998)1827 TEST_F(ApexServiceTest, SubmitStagedSessionCorruptApexFailsB146895998) {
1828 PrepareTestApexForInstall installer(GetTestFile("corrupted_b146895998.apex"),
1829 "/data/app-staging/session_71",
1830 "staging_data_file");
1831
1832 if (!installer.Prepare()) {
1833 FAIL() << GetDebugStr(&installer);
1834 }
1835
1836 ApexInfoList list;
1837 ApexSessionParams params;
1838 params.sessionId = 71;
1839 ASSERT_FALSE(IsOk(service_->submitStagedSession(params, &list)));
1840 }
1841
TEST_F(ApexServiceTest,StageCorruptApexFailsB146895998)1842 TEST_F(ApexServiceTest, StageCorruptApexFailsB146895998) {
1843 PrepareTestApexForInstall installer(GetTestFile("corrupted_b146895998.apex"));
1844
1845 if (!installer.Prepare()) {
1846 FAIL() << GetDebugStr(&installer);
1847 }
1848
1849 ASSERT_FALSE(IsOk(service_->stagePackages({installer.test_file})));
1850 }
1851
TEST_F(ApexServiceTest,SubmitStagedSessionFailsManifestMismatchCleansUpHashtree)1852 TEST_F(ApexServiceTest,
1853 SubmitStagedSessionFailsManifestMismatchCleansUpHashtree) {
1854 PrepareTestApexForInstall installer(
1855 GetTestFile("apex.apexd_test_no_hashtree_manifest_mismatch.apex"),
1856 "/data/app-staging/session_83", "staging_data_file");
1857 if (!installer.Prepare()) {
1858 return;
1859 }
1860
1861 ApexInfoList list;
1862 ApexSessionParams params;
1863 params.sessionId = 83;
1864 ASSERT_FALSE(IsOk(service_->submitStagedSession(params, &list)));
1865 std::string hashtree_file = std::string(kApexHashTreeDir) + "/" +
1866 installer.package + "@" +
1867 std::to_string(installer.version) + ".new";
1868 ASSERT_FALSE(RegularFileExists(hashtree_file));
1869 }
1870
1871 class LogTestToLogcat : public ::testing::EmptyTestEventListener {
OnTestStart(const::testing::TestInfo & test_info)1872 void OnTestStart(const ::testing::TestInfo& test_info) override {
1873 #ifdef __ANDROID__
1874 using base::LogId;
1875 using base::LogSeverity;
1876 using base::StringPrintf;
1877 base::LogdLogger l;
1878 std::string msg =
1879 StringPrintf("=== %s::%s (%s:%d)", test_info.test_suite_name(),
1880 test_info.name(), test_info.file(), test_info.line());
1881 l(LogId::MAIN, LogSeverity::INFO, "ApexServiceTestCases", __FILE__,
1882 __LINE__, msg.c_str());
1883 #else
1884 UNUSED(test_info);
1885 #endif
1886 }
1887 };
1888
1889 } // namespace apex
1890 } // namespace android
1891
main(int argc,char ** argv)1892 int main(int argc, char** argv) {
1893 ::testing::InitGoogleTest(&argc, argv);
1894 android::base::InitLogging(argv, &android::base::StderrLogger);
1895 android::base::SetMinimumLogSeverity(android::base::VERBOSE);
1896 ::testing::UnitTest::GetInstance()->listeners().Append(
1897 new android::apex::LogTestToLogcat());
1898 return RUN_ALL_TESTS();
1899 }
1900