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