diff options
| -rw-r--r-- | cmds/installd/InstalldNativeService.cpp | 12 | ||||
| -rw-r--r-- | cmds/installd/InstalldNativeService.h | 5 | ||||
| -rw-r--r-- | cmds/installd/binder/android/os/IInstalld.aidl | 4 | ||||
| -rw-r--r-- | cmds/installd/dexopt.cpp | 50 | ||||
| -rw-r--r-- | cmds/installd/dexopt.h | 11 | ||||
| -rw-r--r-- | cmds/installd/tests/installd_dexopt_test.cpp | 45 |
6 files changed, 127 insertions, 0 deletions
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp index 8a60754b5b..0653af6e10 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp @@ -2582,5 +2582,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<std::string>& dexMetadata, bool* _aidl_return) { + ENFORCE_UID(AID_SYSTEM); + CHECK_ARGUMENT_PACKAGE_NAME(packageName); + std::lock_guard<std::recursive_mutex> 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 887a2fc372..7bd9c213be 100644 --- a/cmds/installd/InstalldNativeService.h +++ b/cmds/installd/InstalldNativeService.h @@ -133,6 +133,11 @@ public: binder::Status isQuotaSupported(const std::unique_ptr<std::string>& 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<std::string>& 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 3725fc01eb..ba7efd03bf 100644 --- a/cmds/installd/binder/android/os/IInstalld.aidl +++ b/cmds/installd/binder/android/os/IInstalld.aidl @@ -93,4 +93,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 b85744fee9..0c836e812f 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<std::string>& 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<unique_fd> 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<std::string>& 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 |