| /* |
| * Copyright (C) 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <algorithm> |
| #include <fstream> |
| #include <memory> |
| #include <string> |
| #include <vector> |
| |
| #include <grp.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| |
| #include <android-base/file.h> |
| #include <android-base/logging.h> |
| #include <android-base/macros.h> |
| #include <android-base/scopeguard.h> |
| #include <android-base/stringprintf.h> |
| #include <android-base/strings.h> |
| #include <binder/IServiceManager.h> |
| #include <gtest/gtest.h> |
| #include <selinux/selinux.h> |
| |
| #include <android/apex/ApexInfo.h> |
| #include <android/apex/IApexService.h> |
| |
| #include "apex_file.h" |
| #include "apex_manifest.h" |
| #include "apexd.h" |
| #include "apexd_private.h" |
| #include "apexd_utils.h" |
| #include "status_or.h" |
| |
| namespace android { |
| namespace apex { |
| |
| using android::sp; |
| using android::String16; |
| using android::base::Join; |
| |
| class ApexServiceTest : public ::testing::Test { |
| public: |
| ApexServiceTest() { |
| using android::IBinder; |
| using android::IServiceManager; |
| |
| sp<IServiceManager> sm = android::defaultServiceManager(); |
| sp<IBinder> binder = sm->getService(String16("apexservice")); |
| if (binder != nullptr) { |
| service_ = android::interface_cast<IApexService>(binder); |
| } |
| } |
| |
| void SetUp() override { ASSERT_NE(nullptr, service_.get()); } |
| |
| protected: |
| static std::string GetTestDataDir() { |
| return android::base::GetExecutableDirectory(); |
| } |
| static std::string GetTestFile(const std::string& name) { |
| return GetTestDataDir() + "/" + name; |
| } |
| |
| static bool HaveSelinux() { return 1 == is_selinux_enabled(); } |
| |
| static bool IsSelinuxEnforced() { return 0 != security_getenforce(); } |
| |
| StatusOr<bool> IsActive(const std::string& name, int64_t version) { |
| std::vector<ApexInfo> list; |
| android::binder::Status status = service_->getActivePackages(&list); |
| if (status.isOk()) { |
| for (const ApexInfo& p : list) { |
| if (p.packageName == name && p.versionCode == version) { |
| return StatusOr<bool>(true); |
| } |
| } |
| return StatusOr<bool>(false); |
| } |
| return StatusOr<bool>::MakeError(status.toString8().c_str()); |
| } |
| |
| StatusOr<std::vector<ApexInfo>> GetActivePackages() { |
| std::vector<ApexInfo> list; |
| android::binder::Status status = service_->getActivePackages(&list); |
| if (status.isOk()) { |
| return StatusOr<std::vector<ApexInfo>>(list); |
| } |
| |
| return StatusOr<std::vector<ApexInfo>>::MakeError( |
| status.toString8().c_str()); |
| } |
| |
| StatusOr<ApexInfo> GetActivePackage(const std::string& name) { |
| ApexInfo package; |
| android::binder::Status status = service_->getActivePackage(name, &package); |
| if (status.isOk()) { |
| return StatusOr<ApexInfo>(package); |
| } |
| |
| return StatusOr<ApexInfo>::MakeError(status.toString8().c_str()); |
| } |
| |
| std::vector<std::string> GetActivePackagesStrings() { |
| std::vector<ApexInfo> list; |
| android::binder::Status status = service_->getActivePackages(&list); |
| if (status.isOk()) { |
| std::vector<std::string> ret; |
| for (const ApexInfo& p : list) { |
| ret.push_back(p.packageName + "@" + std::to_string(p.versionCode) + |
| " [path=" + p.packagePath + "]"); |
| } |
| return ret; |
| } |
| |
| std::vector<std::string> error; |
| error.push_back("ERROR"); |
| return error; |
| } |
| |
| static std::vector<std::string> ListDir(const std::string& path) { |
| std::vector<std::string> ret; |
| auto d = |
| std::unique_ptr<DIR, int (*)(DIR*)>(opendir(path.c_str()), closedir); |
| if (d == nullptr) { |
| return ret; |
| } |
| |
| struct dirent* dp; |
| while ((dp = readdir(d.get())) != nullptr) { |
| std::string tmp; |
| switch (dp->d_type) { |
| case DT_DIR: |
| tmp = "[dir]"; |
| break; |
| case DT_LNK: |
| tmp = "[lnk]"; |
| break; |
| case DT_REG: |
| tmp = "[reg]"; |
| break; |
| default: |
| tmp = "[other]"; |
| break; |
| } |
| tmp = tmp.append(dp->d_name); |
| ret.push_back(tmp); |
| } |
| std::sort(ret.begin(), ret.end()); |
| return ret; |
| } |
| |
| static std::string GetLogcat() { |
| // For simplicity, log to file and read it. |
| std::string file = GetTestFile("logcat.tmp.txt"); |
| std::vector<std::string> args{ |
| "/system/bin/logcat", |
| "-d", |
| "-f", |
| file, |
| }; |
| std::string error_msg; |
| int res = ForkAndRun(args, &error_msg); |
| CHECK_EQ(0, res) << error_msg; |
| |
| std::string data; |
| CHECK(android::base::ReadFileToString(file, &data)); |
| |
| unlink(file.c_str()); |
| |
| return data; |
| } |
| |
| struct PrepareTestApexForInstall { |
| static constexpr const char* kTestDir = "/data/local/apexservice_tmp"; |
| |
| // This is given to the constructor. |
| std::string test_input; // Original test file. |
| std::string selinux_label_input; // SELinux label to apply. |
| std::string test_dir_input; |
| |
| // This is derived from the input. |
| std::string test_file; // Prepared path. Under test_dir_input. |
| std::string test_installed_file; // Where apexd will store it. |
| |
| std::string package; // APEX package name. |
| uint64_t version; // APEX version |
| |
| explicit PrepareTestApexForInstall( |
| const std::string& test, |
| const std::string& test_dir = std::string(kTestDir), |
| const std::string& selinux_label = "apex_data_file") { |
| test_input = test; |
| selinux_label_input = selinux_label; |
| test_dir_input = test_dir; |
| |
| test_file = test_dir_input + "/" + android::base::Basename(test); |
| |
| package = ""; // Explicitly mark as not initialized. |
| |
| StatusOr<ApexFile> apex_file = ApexFile::Open(test); |
| if (!apex_file.Ok()) { |
| return; |
| } |
| |
| const ApexManifest& manifest = apex_file->GetManifest(); |
| package = manifest.name(); |
| version = manifest.version(); |
| |
| test_installed_file = std::string(kApexPackageDataDir) + "/" + package + |
| "@" + std::to_string(version) + ".apex"; |
| } |
| |
| bool Prepare() { |
| if (package.empty()) { |
| // Failure in constructor. Redo work to get error message. |
| auto fail_fn = [&]() { |
| StatusOr<ApexFile> apex_file = ApexFile::Open(test_input); |
| ASSERT_FALSE(apex_file.Ok()); |
| ASSERT_TRUE(apex_file.Ok()) |
| << test_input << " failed to load: " << apex_file.ErrorMessage(); |
| }; |
| fail_fn(); |
| return false; |
| } |
| |
| auto prepare = [](const std::string& src, const std::string& trg, |
| const std::string& selinux_label) { |
| ASSERT_EQ(0, access(src.c_str(), F_OK)) |
| << src << ": " << strerror(errno); |
| const std::string trg_dir = android::base::Dirname(trg); |
| if (0 != mkdir(trg_dir.c_str(), 0777)) { |
| int saved_errno = errno; |
| ASSERT_EQ(saved_errno, EEXIST) << trg << ":" << strerror(saved_errno); |
| } |
| |
| // Do not use a hardlink, even though it's the simplest solution. |
| // b/119569101. |
| { |
| std::ifstream src_stream(src, std::ios::binary); |
| ASSERT_TRUE(src_stream.good()); |
| std::ofstream trg_stream(trg, std::ios::binary); |
| ASSERT_TRUE(trg_stream.good()); |
| |
| trg_stream << src_stream.rdbuf(); |
| } |
| |
| ASSERT_EQ(0, chmod(trg.c_str(), 0666)) << strerror(errno); |
| struct group* g = getgrnam("system"); |
| ASSERT_NE(nullptr, g); |
| ASSERT_EQ(0, chown(trg.c_str(), /* root uid */ 0, g->gr_gid)) |
| << strerror(errno); |
| |
| int rc = setfilecon( |
| trg_dir.c_str(), |
| std::string("u:object_r:" + selinux_label + ":s0").c_str()); |
| ASSERT_TRUE(0 == rc || !HaveSelinux()) << strerror(errno); |
| rc = setfilecon( |
| trg.c_str(), |
| std::string("u:object_r:" + selinux_label + ":s0").c_str()); |
| ASSERT_TRUE(0 == rc || !HaveSelinux()) << strerror(errno); |
| }; |
| prepare(test_input, test_file, selinux_label_input); |
| return !HasFatalFailure(); |
| } |
| |
| ~PrepareTestApexForInstall() { |
| if (unlink(test_file.c_str()) != 0) { |
| PLOG(ERROR) << "Unable to unlink " << test_file; |
| } |
| if (rmdir(test_dir_input.c_str()) != 0) { |
| PLOG(ERROR) << "Unable to rmdir " << test_dir_input; |
| } |
| |
| if (!package.empty()) { |
| // For cleanliness, also attempt to delete apexd's file. |
| // TODO: to the unstaging using APIs |
| if (unlink(test_installed_file.c_str()) != 0) { |
| PLOG(ERROR) << "Unable to unlink " << test_installed_file; |
| } |
| } |
| } |
| }; |
| |
| std::string GetDebugStr(PrepareTestApexForInstall* installer) { |
| StringLog log; |
| |
| if (installer != nullptr) { |
| log << "test_input=" << installer->test_input << " "; |
| log << "test_file=" << installer->test_file << " "; |
| log << "test_installed_file=" << installer->test_installed_file << " "; |
| log << "package=" << installer->package << " "; |
| log << "version=" << installer->version << " "; |
| } |
| |
| log << "active=[" << Join(GetActivePackagesStrings(), ',') << "] "; |
| log << kApexPackageDataDir << "=[" |
| << Join(ListDir(kApexPackageDataDir), ',') << "] "; |
| log << kApexRoot << "=[" << Join(ListDir(kApexRoot), ',') << "]"; |
| |
| return log; |
| } |
| |
| sp<IApexService> service_; |
| }; |
| |
| namespace { |
| |
| bool RegularFileExists(const std::string& path) { |
| struct stat buf; |
| if (0 != stat(path.c_str(), &buf)) { |
| return false; |
| } |
| return S_ISREG(buf.st_mode); |
| } |
| |
| } // namespace |
| |
| TEST_F(ApexServiceTest, HaveSelinux) { |
| // We want to test under selinux. |
| EXPECT_TRUE(HaveSelinux()); |
| } |
| |
| // Skip for b/119032200. |
| TEST_F(ApexServiceTest, DISABLED_EnforceSelinux) { |
| // Crude cutout for virtual devices. |
| #if !defined(__i386__) && !defined(__x86_64__) |
| constexpr bool kIsX86 = false; |
| #else |
| constexpr bool kIsX86 = true; |
| #endif |
| EXPECT_TRUE(IsSelinuxEnforced() || kIsX86); |
| } |
| |
| TEST_F(ApexServiceTest, StageFailAccess) { |
| if (!IsSelinuxEnforced()) { |
| LOG(WARNING) << "Skipping InstallFailAccess because of selinux"; |
| return; |
| } |
| |
| // Use an extra copy, so that even if this test fails (incorrectly installs), |
| // we have the testdata file still around. |
| std::string orig_test_file = GetTestFile("apex.apexd_test.apex"); |
| std::string test_file = orig_test_file + ".2"; |
| ASSERT_EQ(0, link(orig_test_file.c_str(), test_file.c_str())) |
| << strerror(errno); |
| struct Deleter { |
| std::string to_delete; |
| explicit Deleter(const std::string& t) : to_delete(t) {} |
| ~Deleter() { |
| if (unlink(to_delete.c_str()) != 0) { |
| PLOG(ERROR) << "Could not unlink " << to_delete; |
| } |
| } |
| }; |
| Deleter del(test_file); |
| |
| bool success; |
| android::binder::Status st = service_->stagePackage(test_file, &success); |
| ASSERT_FALSE(st.isOk()); |
| std::string error = st.toString8().c_str(); |
| EXPECT_NE(std::string::npos, error.find("Failed to open package")) << error; |
| EXPECT_NE(std::string::npos, error.find("I/O error")) << error; |
| } |
| |
| TEST_F(ApexServiceTest, StageFailKey) { |
| PrepareTestApexForInstall installer( |
| GetTestFile("apex.apexd_test_no_inst_key.apex")); |
| if (!installer.Prepare()) { |
| return; |
| } |
| ASSERT_EQ(std::string("com.android.apex.test_package.no_inst_key"), |
| installer.package); |
| |
| bool success; |
| android::binder::Status st = |
| service_->stagePackage(installer.test_file, &success); |
| ASSERT_FALSE(st.isOk()); |
| |
| // May contain one of two errors. |
| std::string error = st.toString8().c_str(); |
| |
| constexpr const char* kExpectedError1 = "Failed to get realpath of "; |
| const size_t pos1 = error.find(kExpectedError1); |
| constexpr const char* kExpectedError2 = |
| "/etc/security/apex/com.android.apex.test_package.no_inst_key"; |
| const size_t pos2 = error.find(kExpectedError2); |
| |
| constexpr const char* kExpectedError3 = |
| "Error verifying " |
| "/data/local/apexservice_tmp/apex.apexd_test_no_inst_key.apex: " |
| "couldn't verify public key: Failed to compare the bundled public key " |
| "with key"; |
| const size_t pos3 = error.find(kExpectedError3); |
| |
| const size_t npos = std::string::npos; |
| EXPECT_TRUE((pos1 != npos && pos2 != npos) || pos3 != npos) << error; |
| } |
| |
| TEST_F(ApexServiceTest, StageSuccess) { |
| PrepareTestApexForInstall installer(GetTestFile("apex.apexd_test.apex")); |
| if (!installer.Prepare()) { |
| return; |
| } |
| ASSERT_EQ(std::string("com.android.apex.test_package"), installer.package); |
| |
| bool success; |
| android::binder::Status st = |
| service_->stagePackage(installer.test_file, &success); |
| ASSERT_TRUE(st.isOk()) << st.toString8().c_str(); |
| ASSERT_TRUE(success); |
| EXPECT_TRUE(RegularFileExists(installer.test_installed_file)); |
| } |
| |
| TEST_F(ApexServiceTest, MultiStageSuccess) { |
| PrepareTestApexForInstall installer(GetTestFile("apex.apexd_test.apex")); |
| if (!installer.Prepare()) { |
| return; |
| } |
| ASSERT_EQ(std::string("com.android.apex.test_package"), installer.package); |
| |
| // TODO: Add second test. Right now, just use a separate version. |
| PrepareTestApexForInstall installer2(GetTestFile("apex.apexd_test_v2.apex")); |
| if (!installer2.Prepare()) { |
| return; |
| } |
| ASSERT_EQ(std::string("com.android.apex.test_package"), installer2.package); |
| |
| std::vector<std::string> packages; |
| packages.push_back(installer.test_file); |
| packages.push_back(installer2.test_file); |
| |
| bool success; |
| android::binder::Status st = service_->stagePackages(packages, &success); |
| ASSERT_TRUE(st.isOk()) << st.toString8().c_str(); |
| ASSERT_TRUE(success); |
| EXPECT_TRUE(RegularFileExists(installer.test_installed_file)); |
| EXPECT_TRUE(RegularFileExists(installer2.test_installed_file)); |
| } |
| |
| template <typename NameProvider> |
| class ApexServiceActivationTest : public ApexServiceTest { |
| public: |
| void SetUp() override { |
| ApexServiceTest::SetUp(); |
| ASSERT_NE(nullptr, service_.get()); |
| |
| installer_ = std::make_unique<PrepareTestApexForInstall>( |
| GetTestFile(NameProvider::GetTestName())); |
| if (!installer_->Prepare()) { |
| return; |
| } |
| ASSERT_EQ(NameProvider::GetPackageName(), installer_->package); |
| |
| { |
| // Check package is not active. |
| StatusOr<bool> active = |
| IsActive(installer_->package, installer_->version); |
| ASSERT_TRUE(active.Ok()); |
| ASSERT_FALSE(*active); |
| } |
| |
| { |
| bool success; |
| android::binder::Status st = |
| service_->stagePackage(installer_->test_file, &success); |
| ASSERT_TRUE(st.isOk()) << st.toString8().c_str(); |
| ASSERT_TRUE(success); |
| } |
| } |
| |
| void TearDown() override { |
| // Attempt to deactivate. |
| if (installer_ != nullptr) { |
| service_->deactivatePackage(installer_->test_installed_file); |
| } |
| |
| installer_.reset(); |
| } |
| |
| std::unique_ptr<PrepareTestApexForInstall> installer_; |
| }; |
| |
| struct SuccessNameProvider { |
| static std::string GetTestName() { return "apex.apexd_test.apex"; } |
| static std::string GetPackageName() { |
| return "com.android.apex.test_package"; |
| } |
| }; |
| |
| class ApexServiceActivationSuccessTest |
| : public ApexServiceActivationTest<SuccessNameProvider> {}; |
| |
| TEST_F(ApexServiceActivationSuccessTest, Activate) { |
| android::binder::Status st = |
| service_->activatePackage(installer_->test_installed_file); |
| ASSERT_TRUE(st.isOk()) << st.toString8().c_str() << " " |
| << GetDebugStr(installer_.get()); |
| |
| { |
| // Check package is active. |
| StatusOr<bool> active = IsActive(installer_->package, installer_->version); |
| ASSERT_TRUE(active.Ok()); |
| ASSERT_TRUE(*active) << Join(GetActivePackagesStrings(), ','); |
| } |
| |
| { |
| // Check that the "latest" view exists. |
| std::string latest_path = |
| std::string(kApexRoot) + "/" + installer_->package; |
| struct stat buf; |
| ASSERT_EQ(0, stat(latest_path.c_str(), &buf)) << strerror(errno); |
| // Check that it is a folder. |
| EXPECT_TRUE(S_ISDIR(buf.st_mode)); |
| |
| // Collect direct entries of a folder. |
| auto collect_entries_fn = [](const std::string& path) { |
| std::vector<std::string> ret; |
| // Check that there is something in there. |
| auto d = |
| std::unique_ptr<DIR, int (*)(DIR*)>(opendir(path.c_str()), closedir); |
| if (d == nullptr) { |
| return ret; |
| } |
| |
| struct dirent* dp; |
| while ((dp = readdir(d.get())) != nullptr) { |
| if (dp->d_type != DT_DIR || (strcmp(dp->d_name, ".") == 0) || |
| (strcmp(dp->d_name, "..") == 0)) { |
| continue; |
| } |
| ret.emplace_back(dp->d_name); |
| } |
| std::sort(ret.begin(), ret.end()); |
| return ret; |
| }; |
| |
| std::string versioned_path = std::string(kApexRoot) + "/" + |
| installer_->package + "@" + |
| std::to_string(installer_->version); |
| std::vector<std::string> versioned_folder_entries = |
| collect_entries_fn(versioned_path); |
| std::vector<std::string> latest_folder_entries = |
| collect_entries_fn(latest_path); |
| |
| EXPECT_TRUE(versioned_folder_entries == latest_folder_entries) |
| << "Versioned: " << Join(versioned_folder_entries, ',') |
| << " Latest: " << Join(latest_folder_entries, ','); |
| } |
| } |
| |
| TEST_F(ApexServiceActivationSuccessTest, GetActivePackages) { |
| android::binder::Status st = |
| service_->activatePackage(installer_->test_installed_file); |
| ASSERT_TRUE(st.isOk()) << st.toString8().c_str() << " " |
| << GetDebugStr(installer_.get()); |
| |
| StatusOr<std::vector<ApexInfo>> active = GetActivePackages(); |
| ASSERT_TRUE(active.Ok()); |
| ApexInfo match; |
| |
| for (ApexInfo info : *active) { |
| if (info.packageName == installer_->package) { |
| match = info; |
| break; |
| } |
| } |
| |
| ASSERT_EQ(installer_->package, match.packageName); |
| ASSERT_EQ(installer_->version, static_cast<uint64_t>(match.versionCode)); |
| ASSERT_EQ(installer_->test_installed_file, match.packagePath); |
| } |
| |
| TEST_F(ApexServiceActivationSuccessTest, GetActivePackage) { |
| android::binder::Status st = |
| service_->activatePackage(installer_->test_installed_file); |
| ASSERT_TRUE(st.isOk()) << st.toString8().c_str() << " " |
| << GetDebugStr(installer_.get()); |
| |
| StatusOr<ApexInfo> active = GetActivePackage(installer_->package); |
| ASSERT_TRUE(active.Ok()); |
| |
| ASSERT_EQ(installer_->package, active->packageName); |
| ASSERT_EQ(installer_->version, static_cast<uint64_t>(active->versionCode)); |
| ASSERT_EQ(installer_->test_installed_file, active->packagePath); |
| } |
| |
| TEST_F(ApexServiceTest, StagePreinstall) { |
| PrepareTestApexForInstall installer( |
| GetTestFile("apex.apexd_test_preinstall.apex")); |
| if (!installer.Prepare()) { |
| return; |
| } |
| |
| bool success; |
| android::binder::Status st = |
| service_->stagePackage(installer.test_file, &success); |
| ASSERT_TRUE(st.isOk()) << st.toString8().c_str(); |
| ASSERT_TRUE(success); |
| |
| std::string logcat = GetLogcat(); |
| constexpr const char* kTestMessage = "sh : PreInstall Test\n"; |
| EXPECT_NE(std::string::npos, logcat.find(kTestMessage)) << logcat; |
| |
| // Ensure that the package is neither active nor mounted. |
| { |
| StatusOr<bool> active = IsActive(installer.package, installer.version); |
| ASSERT_TRUE(active.Ok()); |
| EXPECT_FALSE(*active); |
| } |
| { |
| StatusOr<ApexFile> apex = ApexFile::Open(installer.test_input); |
| ASSERT_TRUE(apex.Ok()); |
| std::string path = apexd_private::GetPackageMountPoint(apex->GetManifest()); |
| std::string entry = std::string("[dir]").append(path); |
| std::vector<std::string> slash_apex = ListDir(kApexRoot); |
| auto it = std::find(slash_apex.begin(), slash_apex.end(), entry); |
| EXPECT_TRUE(it == slash_apex.end()) << Join(slash_apex, ','); |
| } |
| } |
| |
| TEST_F(ApexServiceTest, MultiStagePreinstall) { |
| PrepareTestApexForInstall installer( |
| GetTestFile("apex.apexd_test_preinstall.apex")); |
| if (!installer.Prepare()) { |
| return; |
| } |
| PrepareTestApexForInstall installer2(GetTestFile("apex.apexd_test.apex")); |
| if (!installer2.Prepare()) { |
| return; |
| } |
| |
| std::vector<std::string> pkgs = { |
| installer.test_file, |
| installer2.test_file, |
| }; |
| bool success; |
| android::binder::Status st = service_->stagePackages(pkgs, &success); |
| ASSERT_TRUE(st.isOk()) << st.toString8().c_str(); |
| ASSERT_TRUE(success); |
| |
| std::string logcat = GetLogcat(); |
| constexpr const char* kTestMessage = |
| "sh : /apex/com.android.apex.test_package/etc/sample_prebuilt_file"; |
| EXPECT_NE(std::string::npos, logcat.find(kTestMessage)) << logcat; |
| |
| // Ensure that the package is neither active nor mounted. |
| { |
| StatusOr<bool> active = IsActive(installer.package, installer.version); |
| ASSERT_TRUE(active.Ok()); |
| EXPECT_FALSE(*active); |
| } |
| { |
| StatusOr<ApexFile> apex = ApexFile::Open(installer.test_input); |
| ASSERT_TRUE(apex.Ok()); |
| std::string path = apexd_private::GetPackageMountPoint(apex->GetManifest()); |
| std::string entry = std::string("[dir]").append(path); |
| std::vector<std::string> slash_apex = ListDir(kApexRoot); |
| auto it = std::find(slash_apex.begin(), slash_apex.end(), entry); |
| EXPECT_TRUE(it == slash_apex.end()) << Join(slash_apex, ','); |
| } |
| } |
| |
| TEST_F(ApexServiceTest, SubmitSessionTestSuccess) { |
| PrepareTestApexForInstall installer(GetTestFile("apex.apexd_test.apex"), |
| "/data/staging/session_123", |
| "staging_data_file"); |
| if (!installer.Prepare()) { |
| FAIL() << GetDebugStr(&installer); |
| } |
| |
| ApexInfoList list; |
| bool ret_value; |
| android::binder::Status status = |
| service_->submitStagedSession(123, &list, &ret_value); |
| |
| ASSERT_TRUE(status.isOk()) |
| << status.toString8().c_str() << " " << GetDebugStr(&installer); |
| EXPECT_TRUE(ret_value); |
| EXPECT_EQ(1u, list.apexInfos.size()); |
| ApexInfo match; |
| for (ApexInfo info : list.apexInfos) { |
| if (info.packageName == installer.package) { |
| match = info; |
| break; |
| } |
| } |
| |
| ASSERT_EQ(installer.package, match.packageName); |
| ASSERT_EQ(installer.version, static_cast<uint64_t>(match.versionCode)); |
| ASSERT_EQ(installer.test_file, match.packagePath); |
| |
| ApexSessionInfo session; |
| status = service_->getStagedSessionInfo(123, &session); |
| ASSERT_TRUE(status.isOk()) |
| << status.toString8().c_str() << " " << GetDebugStr(&installer); |
| EXPECT_FALSE(session.isUnknown); |
| EXPECT_FALSE(session.isVerified); |
| EXPECT_TRUE(session.isStaged); |
| EXPECT_FALSE(session.isActivated); |
| EXPECT_FALSE(session.isActivationPendingRetry); |
| EXPECT_FALSE(session.isActivationFailed); |
| } |
| |
| TEST_F(ApexServiceTest, SubmitSessionTestFail) { |
| PrepareTestApexForInstall installer( |
| GetTestFile("apex.apexd_test_no_inst_key.apex"), |
| "/data/staging/session_456", "staging_data_file"); |
| if (!installer.Prepare()) { |
| FAIL() << GetDebugStr(&installer); |
| } |
| |
| ApexInfoList list; |
| bool ret_value; |
| android::binder::Status status = |
| service_->submitStagedSession(456, &list, &ret_value); |
| |
| ASSERT_TRUE(status.isOk()) |
| << status.toString8().c_str() << " " << GetDebugStr(&installer); |
| EXPECT_FALSE(ret_value); |
| |
| ApexSessionInfo session; |
| status = service_->getStagedSessionInfo(456, &session); |
| ASSERT_TRUE(status.isOk()) |
| << status.toString8().c_str() << " " << GetDebugStr(&installer); |
| EXPECT_TRUE(session.isUnknown); |
| EXPECT_FALSE(session.isVerified); |
| EXPECT_FALSE(session.isStaged); |
| EXPECT_FALSE(session.isActivated); |
| EXPECT_FALSE(session.isActivationPendingRetry); |
| EXPECT_FALSE(session.isActivationFailed); |
| } |
| |
| class LogTestToLogcat : public testing::EmptyTestEventListener { |
| void OnTestStart(const testing::TestInfo& test_info) override { |
| #ifdef __ANDROID__ |
| using base::LogId; |
| using base::LogSeverity; |
| using base::StringPrintf; |
| base::LogdLogger l; |
| std::string msg = |
| StringPrintf("=== %s::%s (%s:%d)", test_info.test_case_name(), |
| test_info.name(), test_info.file(), test_info.line()); |
| l(LogId::MAIN, LogSeverity::INFO, "apexservice_test", __FILE__, __LINE__, |
| msg.c_str()); |
| #else |
| UNUSED(test_info); |
| #endif |
| } |
| }; |
| |
| } // namespace apex |
| } // namespace android |
| |
| int main(int argc, char** argv) { |
| android::base::InitLogging(argv, &android::base::StderrLogger); |
| ::testing::InitGoogleTest(&argc, argv); |
| testing::UnitTest::GetInstance()->listeners().Append( |
| new android::apex::LogTestToLogcat()); |
| return RUN_ALL_TESTS(); |
| } |