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