From c3b049e0294a5884b18f0f29f3af14e6de0803c7 Mon Sep 17 00:00:00 2001 From: Calin Juravle Date: Thu, 18 Jan 2018 22:32:58 -0800 Subject: [installd] Prepare profiles for app code paths Implement profile preparation for individual application code paths. The preparation is: - create the current profile - merge the profile from the dex metadata file (if present) into the reference profile. Note: currently the current profile is created as part of InstalldNativeService::createAppData for the entire package. That logic does not support dex metadata or individual code paths and will be removed once the PackageManager switches over the new method. (cherry picked from commit bc5ab878b56bcd3a300cc5fc0facc864421dfcb6) Test: installd_dexopt_test Bug: 30934496 Merged-In: I2aeddcda7b78017bd46838985bef5f92a79d4573 Change-Id: I2aeddcda7b78017bd46838985bef5f92a79d4573 --- cmds/installd/InstalldNativeService.cpp | 12 +++++++ cmds/installd/InstalldNativeService.h | 5 +++ cmds/installd/binder/android/os/IInstalld.aidl | 4 +++ cmds/installd/dexopt.cpp | 50 ++++++++++++++++++++++++++ cmds/installd/dexopt.h | 11 ++++++ cmds/installd/tests/installd_dexopt_test.cpp | 45 +++++++++++++++++++++++ 6 files changed, 127 insertions(+) diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp index 9ab54470ae..c6eee0d249 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp @@ -2480,5 +2480,17 @@ binder::Status InstalldNativeService::isQuotaSupported( return ok(); } +binder::Status InstalldNativeService::prepareAppProfile(const std::string& packageName, + int32_t userId, int32_t appId, const std::string& profileName, const std::string& codePath, + const std::unique_ptr& dexMetadata, bool* _aidl_return) { + ENFORCE_UID(AID_SYSTEM); + CHECK_ARGUMENT_PACKAGE_NAME(packageName); + std::lock_guard lock(mLock); + + *_aidl_return = prepare_app_profile(packageName, userId, appId, profileName, codePath, + dexMetadata); + return ok(); +} + } // namespace installd } // namespace android diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h index cef62cde5e..8af6e2442a 100644 --- a/cmds/installd/InstalldNativeService.h +++ b/cmds/installd/InstalldNativeService.h @@ -131,6 +131,11 @@ public: binder::Status isQuotaSupported(const std::unique_ptr& volumeUuid, bool* _aidl_return); + binder::Status prepareAppProfile(const std::string& packageName, + int32_t userId, int32_t appId, const std::string& profileName, + const std::string& codePath, const std::unique_ptr& dexMetadata, + bool* _aidl_return); + private: std::recursive_mutex mLock; diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl index c819e98d89..2123998319 100644 --- a/cmds/installd/binder/android/os/IInstalld.aidl +++ b/cmds/installd/binder/android/os/IInstalld.aidl @@ -92,4 +92,8 @@ interface IInstalld { void invalidateMounts(); boolean isQuotaSupported(@nullable @utf8InCpp String uuid); + + boolean prepareAppProfile(@utf8InCpp String packageName, + int userId, int appId, @utf8InCpp String profileName, @utf8InCpp String codePath, + @nullable @utf8InCpp String dexMetadata); } diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp index df74d50b31..98adc167a0 100644 --- a/cmds/installd/dexopt.cpp +++ b/cmds/installd/dexopt.cpp @@ -2479,5 +2479,55 @@ bool create_profile_snapshot(int32_t app_id, const std::string& package_name, return true; } +bool prepare_app_profile(const std::string& package_name, + userid_t user_id, + appid_t app_id, + const std::string& profile_name, + const std::string& code_path ATTRIBUTE_UNUSED, + const std::unique_ptr& dex_metadata) { + // Prepare the current profile. + std::string cur_profile = create_current_profile_path(user_id, package_name, profile_name, + /*is_secondary_dex*/ false); + uid_t uid = multiuser_get_uid(user_id, app_id); + if (fs_prepare_file_strict(cur_profile.c_str(), 0600, uid, uid) != 0) { + PLOG(ERROR) << "Failed to prepare " << cur_profile; + return false; + } + + // Check if we need to install the profile from the dex metadata. + if (dex_metadata == nullptr) { + return true; + } + + // We have a dex metdata. Merge the profile into the reference profile. + unique_fd ref_profile_fd = open_reference_profile(uid, package_name, profile_name, + /*read_write*/ true, /*is_secondary_dex*/ false); + unique_fd dex_metadata_fd(TEMP_FAILURE_RETRY( + open(dex_metadata->c_str(), O_RDONLY | O_NOFOLLOW))); + std::vector profiles_fd; + profiles_fd.push_back(std::move(dex_metadata_fd)); + + pid_t pid = fork(); + if (pid == 0) { + /* child -- drop privileges before continuing */ + gid_t app_shared_gid = multiuser_get_shared_gid(user_id, app_id); + drop_capabilities(app_shared_gid); + + // TODO(calin): the dex metadata profile might embed different names for the + // same code path (e.g. YouTube.apk or base.apk, depending on how the initial + // profile was captured). We should pass the code path to adjust the names in the profile. + run_profman_merge(profiles_fd, ref_profile_fd); + exit(42); /* only get here on exec failure */ + } + + /* parent */ + int return_code = wait_child(pid); + if (!WIFEXITED(return_code)) { + PLOG(WARNING) << "profman failed for " << package_name << ":" << profile_name; + return false; + } + return true; +} + } // namespace installd } // namespace android diff --git a/cmds/installd/dexopt.h b/cmds/installd/dexopt.h index a2baf60e92..29312d2a8e 100644 --- a/cmds/installd/dexopt.h +++ b/cmds/installd/dexopt.h @@ -70,6 +70,17 @@ bool copy_system_profile(const std::string& system_profile, const std::string& pkgname, const std::string& profile_name); +// Prepare the app profile for the given code path: +// - create the current profile using profile_name +// - merge the profile from the dex metadata file (if present) into +// the reference profile. +bool prepare_app_profile(const std::string& package_name, + userid_t user_id, + appid_t app_id, + const std::string& profile_name, + const std::string& code_path, + const std::unique_ptr& dex_metadata); + bool delete_odex(const char* apk_path, const char* instruction_set, const char* output_path); bool reconcile_secondary_dex_file(const std::string& dex_path, diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp index 729ac3c378..4d6d234615 100644 --- a/cmds/installd/tests/installd_dexopt_test.cpp +++ b/cmds/installd/tests/installd_dexopt_test.cpp @@ -524,6 +524,33 @@ class ProfileTest : public DexoptTest { ASSERT_TRUE(AreFilesEqual(ref_profile_content, ref_profile_)); } + // TODO(calin): add dex metadata tests once the ART change is merged. + void preparePackageProfile(const std::string& package_name, const std::string& profile_name, + bool expected_result) { + bool result; + binder::Status binder_result = service_->prepareAppProfile( + package_name, kTestUserId, kTestAppId, profile_name, /*code path*/ "base.apk", + /*dex_metadata*/ nullptr, &result); + ASSERT_TRUE(binder_result.isOk()); + ASSERT_EQ(expected_result, result); + + if (!expected_result) { + // Do not check the files if we expect to fail. + return; + } + + std::string code_path_cur_prof = create_current_profile_path( + kTestUserId, package_name, profile_name, /*is_secondary_dex*/ false); + std::string code_path_ref_profile = create_reference_profile_path(package_name, + profile_name, /*is_secondary_dex*/ false); + + // Check that we created the current profile. + CheckFileAccess(code_path_cur_prof, kTestAppUid, kTestAppUid, 0600 | S_IFREG); + + // Without dex metadata we don't generate a reference profile. + ASSERT_EQ(-1, access(code_path_ref_profile.c_str(), R_OK)); + } + private: void TransitionToSystemServer() { ASSERT_TRUE(DropCapabilities(kSystemUid, kSystemGid)); @@ -666,5 +693,23 @@ TEST_F(ProfileTest, ProfileDirOkAfterFixup) { CheckFileAccess(ref_profile_dir, kSystemUid, kTestAppGid, 0770 | S_IFDIR); } +TEST_F(ProfileTest, ProfilePrepareOk) { + LOG(INFO) << "ProfilePrepareOk"; + preparePackageProfile(package_name_, "split.prof", /*expected_result*/ true); +} + +TEST_F(ProfileTest, ProfilePrepareFailInvalidPackage) { + LOG(INFO) << "ProfilePrepareFailInvalidPackage"; + preparePackageProfile("not.there.package", "split.prof", /*expected_result*/ false); +} + +TEST_F(ProfileTest, ProfilePrepareFailProfileChangedUid) { + LOG(INFO) << "ProfilePrepareFailProfileChangedUid"; + SetupProfiles(/*setup_ref*/ false); + // Change the uid on the profile to trigger a failure. + ::chown(cur_profile_.c_str(), kTestAppUid + 1, kTestAppGid + 1); + preparePackageProfile(package_name_, "primary.prof", /*expected_result*/ false); +} + } // namespace installd } // namespace android -- cgit v1.2.3-59-g8ed1b