diff options
| -rw-r--r-- | cmds/installd/Android.bp | 25 | ||||
| -rw-r--r-- | cmds/installd/Android.mk | 3 | ||||
| -rw-r--r-- | cmds/installd/InstalldNativeService.cpp | 96 | ||||
| -rw-r--r-- | cmds/installd/InstalldNativeService.h | 25 | ||||
| -rw-r--r-- | cmds/installd/binder/android/os/IInstalld.aidl | 24 | ||||
| -rw-r--r-- | cmds/installd/dexopt.cpp | 523 | ||||
| -rw-r--r-- | cmds/installd/dexopt.h | 64 | ||||
| -rw-r--r-- | cmds/installd/otapreopt.cpp | 501 | ||||
| -rw-r--r-- | cmds/installd/otapreopt_parameters.cpp | 356 | ||||
| -rw-r--r-- | cmds/installd/otapreopt_parameters.h | 59 | ||||
| -rw-r--r-- | cmds/installd/tests/Android.bp | 20 | ||||
| -rw-r--r-- | cmds/installd/tests/installd_dexopt_test.cpp | 441 | ||||
| -rw-r--r-- | cmds/installd/tests/installd_otapreopt_test.cpp | 210 | ||||
| -rw-r--r-- | cmds/installd/tests/installd_service_test.cpp | 83 | ||||
| -rw-r--r-- | cmds/installd/tests/installd_utils_test.cpp | 36 | ||||
| -rw-r--r-- | cmds/installd/utils.cpp | 98 | ||||
| -rw-r--r-- | cmds/installd/utils.h | 19 |
17 files changed, 1899 insertions, 684 deletions
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp index 7d1537a63f..352cf0ad74 100644 --- a/cmds/installd/Android.bp +++ b/cmds/installd/Android.bp @@ -18,6 +18,7 @@ cc_defaults { shared_libs: [ "libbase", "libbinder", + "libcrypto", "libcutils", "liblog", "liblogwrap", @@ -91,4 +92,28 @@ filegroup { ], } +// +// Static library for otapreopt used in testing +// +cc_library_static { + name: "libotapreoptparameters", + cflags: [ + "-Wall", + "-Werror" + ], + clang: true, + + srcs: [ + "otapreopt_parameters.cpp"], + + export_include_dirs: ["."], + + shared_libs: [ + "libbase", + "libcutils", + "liblog", + "libutils", + ], +} + subdirs = ["tests"] diff --git a/cmds/installd/Android.mk b/cmds/installd/Android.mk index 1d21b3ce1b..a4f95da939 100644 --- a/cmds/installd/Android.mk +++ b/cmds/installd/Android.mk @@ -23,10 +23,11 @@ LOCAL_CFLAGS += -DART_BASE_ADDRESS=$(LIBART_IMG_HOST_BASE_ADDRESS) LOCAL_CFLAGS += -DART_BASE_ADDRESS_MIN_DELTA=$(LOCAL_LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA) LOCAL_CFLAGS += -DART_BASE_ADDRESS_MAX_DELTA=$(LOCAL_LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA) -LOCAL_SRC_FILES := otapreopt.cpp globals.cpp utils.cpp dexopt.cpp +LOCAL_SRC_FILES := otapreopt.cpp otapreopt_parameters.cpp globals.cpp utils.cpp dexopt.cpp LOCAL_HEADER_LIBRARIES := dex2oat_headers LOCAL_SHARED_LIBRARIES := \ libbase \ + libcrypto \ libcutils \ liblog \ liblogwrap \ diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp index b0661c5ffd..c52255ae73 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp @@ -366,13 +366,6 @@ static bool prepare_app_profile_dir(const std::string& packageName, int32_t appI PLOG(ERROR) << "Failed to prepare " << profile_dir; return false; } - const std::string profile_file = create_current_profile_path(userId, packageName, - /*is_secondary_dex*/false); - // read-write only for the app user. - if (fs_prepare_file_strict(profile_file.c_str(), 0600, uid, uid) != 0) { - PLOG(ERROR) << "Failed to prepare " << profile_file; - return false; - } const std::string ref_profile_path = create_primary_reference_profile_package_dir_path(packageName); @@ -522,16 +515,17 @@ binder::Status InstalldNativeService::migrateAppData(const std::unique_ptr<std:: } -binder::Status InstalldNativeService::clearAppProfiles(const std::string& packageName) { +binder::Status InstalldNativeService::clearAppProfiles(const std::string& packageName, + const std::string& profileName) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); std::lock_guard<std::recursive_mutex> lock(mLock); binder::Status res = ok(); - if (!clear_primary_reference_profile(packageName)) { + if (!clear_primary_reference_profile(packageName, profileName)) { res = error("Failed to clear reference profile for " + packageName); } - if (!clear_primary_current_profiles(packageName)) { + if (!clear_primary_current_profiles(packageName, profileName)) { res = error("Failed to clear current profiles for " + packageName); } return res; @@ -581,11 +575,6 @@ binder::Status InstalldNativeService::clearAppData(const std::unique_ptr<std::st res = error("Failed to delete contents of " + path); } } - if (!only_cache) { - if (!clear_primary_current_profile(packageName, userId)) { - res = error("Failed to clear current profile for " + packageName); - } - } } return res; } @@ -1833,68 +1822,73 @@ binder::Status InstalldNativeService::setAppQuota(const std::unique_ptr<std::str // Dumps the contents of a profile file, using pkgname's dex files for pretty // printing the result. binder::Status InstalldNativeService::dumpProfiles(int32_t uid, const std::string& packageName, - const std::string& codePaths, bool* _aidl_return) { + const std::string& profileName, const std::string& codePath, bool* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); std::lock_guard<std::recursive_mutex> lock(mLock); - const char* pkgname = packageName.c_str(); - const char* code_paths = codePaths.c_str(); - - *_aidl_return = dump_profiles(uid, pkgname, code_paths); + *_aidl_return = dump_profiles(uid, packageName, profileName, codePath); return ok(); } // Copy the contents of a system profile over the data profile. binder::Status InstalldNativeService::copySystemProfile(const std::string& systemProfile, - int32_t packageUid, const std::string& packageName, bool* _aidl_return) { + int32_t packageUid, const std::string& packageName, const std::string& profileName, + bool* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); std::lock_guard<std::recursive_mutex> lock(mLock); - *_aidl_return = copy_system_profile(systemProfile, packageUid, packageName); + *_aidl_return = copy_system_profile(systemProfile, packageUid, packageName, profileName); return ok(); } // TODO: Consider returning error codes. binder::Status InstalldNativeService::mergeProfiles(int32_t uid, const std::string& packageName, - bool* _aidl_return) { + const std::string& profileName, bool* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); std::lock_guard<std::recursive_mutex> lock(mLock); - *_aidl_return = analyze_primary_profiles(uid, packageName); + *_aidl_return = analyze_primary_profiles(uid, packageName, profileName); return ok(); } binder::Status InstalldNativeService::createProfileSnapshot(int32_t appId, - const std::string& packageName, const std::string& codePath, bool* _aidl_return) { + const std::string& packageName, const std::string& profileName, + const std::string& classpath, bool* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); std::lock_guard<std::recursive_mutex> lock(mLock); - *_aidl_return = create_profile_snapshot(appId, packageName, codePath); + *_aidl_return = create_profile_snapshot(appId, packageName, profileName, classpath); return ok(); } binder::Status InstalldNativeService::destroyProfileSnapshot(const std::string& packageName, - const std::string& codePath) { + const std::string& profileName) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); std::lock_guard<std::recursive_mutex> lock(mLock); - std::string snapshot = create_snapshot_profile_path(packageName, codePath); + std::string snapshot = create_snapshot_profile_path(packageName, profileName); if ((unlink(snapshot.c_str()) != 0) && (errno != ENOENT)) { - return error("Failed to destroy profile snapshot for " + packageName + ":" + codePath); + return error("Failed to destroy profile snapshot for " + packageName + ":" + profileName); } return ok(); } +static const char* getCStr(const std::unique_ptr<std::string>& data, + const char* default_value = nullptr) { + return data == nullptr ? default_value : data->c_str(); +} binder::Status InstalldNativeService::dexopt(const std::string& apkPath, int32_t uid, const std::unique_ptr<std::string>& packageName, const std::string& instructionSet, int32_t dexoptNeeded, const std::unique_ptr<std::string>& outputPath, int32_t dexFlags, const std::string& compilerFilter, const std::unique_ptr<std::string>& uuid, const std::unique_ptr<std::string>& classLoaderContext, - const std::unique_ptr<std::string>& seInfo, bool downgrade, int32_t targetSdkVersion) { + const std::unique_ptr<std::string>& seInfo, bool downgrade, int32_t targetSdkVersion, + const std::unique_ptr<std::string>& profileName, + const std::unique_ptr<std::string>& dexMetadataPath) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); if (packageName && *packageName != "*") { @@ -1903,16 +1897,18 @@ binder::Status InstalldNativeService::dexopt(const std::string& apkPath, int32_t std::lock_guard<std::recursive_mutex> lock(mLock); const char* apk_path = apkPath.c_str(); - const char* pkgname = packageName ? packageName->c_str() : "*"; + const char* pkgname = getCStr(packageName, "*"); const char* instruction_set = instructionSet.c_str(); - const char* oat_dir = outputPath ? outputPath->c_str() : nullptr; + const char* oat_dir = getCStr(outputPath); const char* compiler_filter = compilerFilter.c_str(); - const char* volume_uuid = uuid ? uuid->c_str() : nullptr; - const char* class_loader_context = classLoaderContext ? classLoaderContext->c_str() : nullptr; - const char* se_info = seInfo ? seInfo->c_str() : nullptr; + const char* volume_uuid = getCStr(uuid); + const char* class_loader_context = getCStr(classLoaderContext); + const char* se_info = getCStr(seInfo); + const char* profile_name = getCStr(profileName); + const char* dm_path = getCStr(dexMetadataPath); int res = android::installd::dexopt(apk_path, uid, pkgname, instruction_set, dexoptNeeded, oat_dir, dexFlags, compiler_filter, volume_uuid, class_loader_context, se_info, - downgrade, targetSdkVersion); + downgrade, targetSdkVersion, profile_name, dm_path); return res ? error(res, "Failed to dexopt") : ok(); } @@ -2379,6 +2375,22 @@ binder::Status InstalldNativeService::reconcileSecondaryDexFile( return result ? ok() : error(); } +binder::Status InstalldNativeService::hashSecondaryDexFile( + const std::string& dexPath, const std::string& packageName, int32_t uid, + const std::unique_ptr<std::string>& volumeUuid, int32_t storageFlag, + std::vector<uint8_t>* _aidl_return) { + ENFORCE_UID(AID_SYSTEM); + CHECK_ARGUMENT_UUID(volumeUuid); + CHECK_ARGUMENT_PACKAGE_NAME(packageName); + + // mLock is not taken here since we will never modify the file system. + // If a file is modified just as we are reading it this may result in an + // anomalous hash, but that's ok. + bool result = android::installd::hash_secondary_dex_file( + dexPath, packageName, uid, volumeUuid, storageFlag, _aidl_return); + return result ? ok() : error(); +} + binder::Status InstalldNativeService::invalidateMounts() { ENFORCE_UID(AID_SYSTEM); std::lock_guard<std::recursive_mutex> lock(mMountsLock); @@ -2457,5 +2469,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 93b59ed700..e40b74ea1c 100644 --- a/cmds/installd/InstalldNativeService.h +++ b/cmds/installd/InstalldNativeService.h @@ -85,22 +85,25 @@ public: const std::string& compilerFilter, const std::unique_ptr<std::string>& uuid, const std::unique_ptr<std::string>& classLoaderContext, const std::unique_ptr<std::string>& seInfo, bool downgrade, - int32_t targetSdkVersion); + int32_t targetSdkVersion, const std::unique_ptr<std::string>& profileName, + const std::unique_ptr<std::string>& dexMetadataPath); binder::Status rmdex(const std::string& codePath, const std::string& instructionSet); - binder::Status mergeProfiles(int32_t uid, const std::string& packageName, bool* _aidl_return); + binder::Status mergeProfiles(int32_t uid, const std::string& packageName, + const std::string& profileName, bool* _aidl_return); binder::Status dumpProfiles(int32_t uid, const std::string& packageName, - const std::string& codePaths, bool* _aidl_return); + const std::string& profileName, const std::string& codePath, bool* _aidl_return); binder::Status copySystemProfile(const std::string& systemProfile, - int32_t uid, const std::string& packageName, bool* _aidl_return); - binder::Status clearAppProfiles(const std::string& packageName); + int32_t uid, const std::string& packageName, const std::string& profileName, + bool* _aidl_return); + binder::Status clearAppProfiles(const std::string& packageName, const std::string& profileName); binder::Status destroyAppProfiles(const std::string& packageName); binder::Status createProfileSnapshot(int32_t appId, const std::string& packageName, - const std::string& codePath, bool* _aidl_return); + const std::string& profileName, const std::string& classpath, bool* _aidl_return); binder::Status destroyProfileSnapshot(const std::string& packageName, - const std::string& codePath); + const std::string& profileName); binder::Status idmap(const std::string& targetApkPath, const std::string& overlayApkPath, int32_t uid); @@ -123,11 +126,19 @@ public: binder::Status reconcileSecondaryDexFile(const std::string& dexPath, const std::string& packageName, int32_t uid, const std::vector<std::string>& isa, const std::unique_ptr<std::string>& volumeUuid, int32_t storage_flag, bool* _aidl_return); + binder::Status hashSecondaryDexFile(const std::string& dexPath, + const std::string& packageName, int32_t uid, const std::unique_ptr<std::string>& volumeUuid, + int32_t storageFlag, std::vector<uint8_t>* _aidl_return); binder::Status invalidateMounts(); 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 2c9c6bd238..1106734160 100644 --- a/cmds/installd/binder/android/os/IInstalld.aidl +++ b/cmds/installd/binder/android/os/IInstalld.aidl @@ -51,20 +51,23 @@ interface IInstalld { @nullable @utf8InCpp String outputPath, int dexFlags, @utf8InCpp String compilerFilter, @nullable @utf8InCpp String uuid, @nullable @utf8InCpp String sharedLibraries, - @nullable @utf8InCpp String seInfo, boolean downgrade, int targetSdkVersion); + @nullable @utf8InCpp String seInfo, boolean downgrade, int targetSdkVersion, + @nullable @utf8InCpp String profileName, + @nullable @utf8InCpp String dexMetadataPath); void rmdex(@utf8InCpp String codePath, @utf8InCpp String instructionSet); - boolean mergeProfiles(int uid, @utf8InCpp String packageName); - boolean dumpProfiles(int uid, @utf8InCpp String packageName, @utf8InCpp String codePaths); + boolean mergeProfiles(int uid, @utf8InCpp String packageName, @utf8InCpp String profileName); + boolean dumpProfiles(int uid, @utf8InCpp String packageName, @utf8InCpp String profileName, + @utf8InCpp String codePath); boolean copySystemProfile(@utf8InCpp String systemProfile, int uid, - @utf8InCpp String packageName); - void clearAppProfiles(@utf8InCpp String packageName); + @utf8InCpp String packageName, @utf8InCpp String profileName); + void clearAppProfiles(@utf8InCpp String packageName, @utf8InCpp String profileName); void destroyAppProfiles(@utf8InCpp String packageName); boolean createProfileSnapshot(int appId, @utf8InCpp String packageName, - @utf8InCpp String codePath); - void destroyProfileSnapshot(@utf8InCpp String packageName, @utf8InCpp String codePath); + @utf8InCpp String profileName, @utf8InCpp String classpath); + void destroyProfileSnapshot(@utf8InCpp String packageName, @utf8InCpp String profileName); void idmap(@utf8InCpp String targetApkPath, @utf8InCpp String overlayApkPath, int uid); void removeIdmap(@utf8InCpp String overlayApkPath); @@ -87,6 +90,13 @@ interface IInstalld { int uid, in @utf8InCpp String[] isas, @nullable @utf8InCpp String volume_uuid, int storage_flag); + byte[] hashSecondaryDexFile(@utf8InCpp String dexPath, @utf8InCpp String pkgName, + int uid, @nullable @utf8InCpp String volumeUuid, int storageFlag); + 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 a2f74baec4..2a7ad614fd 100644 --- a/cmds/installd/dexopt.cpp +++ b/cmds/installd/dexopt.cpp @@ -15,6 +15,7 @@ */ #define LOG_TAG "installed" +#include <array> #include <fcntl.h> #include <stdlib.h> #include <string.h> @@ -27,6 +28,7 @@ #include <sys/wait.h> #include <unistd.h> +#include <android-base/file.h> #include <android-base/logging.h> #include <android-base/properties.h> #include <android-base/stringprintf.h> @@ -36,6 +38,7 @@ #include <cutils/properties.h> #include <cutils/sched_policy.h> #include <log/log.h> // TODO: Move everything to base/logging. +#include <openssl/sha.h> #include <private/android_filesystem_config.h> #include <selinux/android.h> #include <system/thread_defs.h> @@ -46,8 +49,10 @@ #include "otapreopt_utils.h" #include "utils.h" -using android::base::StringPrintf; using android::base::EndsWith; +using android::base::ReadFully; +using android::base::StringPrintf; +using android::base::WriteFully; using android::base::unique_fd; namespace android { @@ -132,37 +137,43 @@ static bool clear_profile(const std::string& profile) { } // Clear the reference profile for the given location. -// The location is the package name for primary apks or the dex path for secondary dex files. -static bool clear_reference_profile(const std::string& location, bool is_secondary_dex) { - return clear_profile(create_reference_profile_path(location, is_secondary_dex)); +// The location is the profile name for primary apks or the dex path for secondary dex files. +static bool clear_reference_profile(const std::string& package_name, const std::string& location, + bool is_secondary_dex) { + return clear_profile(create_reference_profile_path(package_name, location, is_secondary_dex)); } // Clear the reference profile for the given location. -// The location is the package name for primary apks or the dex path for secondary dex files. -static bool clear_current_profile(const std::string& pkgname, userid_t user, - bool is_secondary_dex) { - return clear_profile(create_current_profile_path(user, pkgname, is_secondary_dex)); +// The location is the profile name for primary apks or the dex path for secondary dex files. +static bool clear_current_profile(const std::string& package_name, const std::string& location, + userid_t user, bool is_secondary_dex) { + return clear_profile(create_current_profile_path(user, package_name, location, + is_secondary_dex)); } // Clear the reference profile for the primary apk of the given package. -bool clear_primary_reference_profile(const std::string& pkgname) { - return clear_reference_profile(pkgname, /*is_secondary_dex*/false); +// The location is the profile name for primary apks or the dex path for secondary dex files. +bool clear_primary_reference_profile(const std::string& package_name, + const std::string& location) { + return clear_reference_profile(package_name, location, /*is_secondary_dex*/false); } // Clear all current profile for the primary apk of the given package. -bool clear_primary_current_profiles(const std::string& pkgname) { +// The location is the profile name for primary apks or the dex path for secondary dex files. +bool clear_primary_current_profiles(const std::string& package_name, const std::string& location) { bool success = true; // For secondary dex files, we don't really need the user but we use it for sanity checks. std::vector<userid_t> users = get_known_users(/*volume_uuid*/ nullptr); for (auto user : users) { - success &= clear_current_profile(pkgname, user, /*is_secondary_dex*/false); + success &= clear_current_profile(package_name, location, user, /*is_secondary_dex*/false); } return success; } // Clear the current profile for the primary apk of the given package and user. -bool clear_primary_current_profile(const std::string& pkgname, userid_t user) { - return clear_current_profile(pkgname, user, /*is_secondary_dex*/false); +bool clear_primary_current_profile(const std::string& package_name, const std::string& location, + userid_t user) { + return clear_current_profile(package_name, location, user, /*is_secondary_dex*/false); } static int split_count(const char *str) @@ -212,7 +223,8 @@ static void run_dex2oat(int zip_fd, int oat_fd, int input_vdex_fd, int output_vd const char* input_file_name, const char* output_file_name, int swap_fd, const char* instruction_set, const char* compiler_filter, bool debuggable, bool post_bootcomplete, bool background_job_compile, int profile_fd, - const char* class_loader_context, int target_sdk_version, bool enable_hidden_api_checks) { + const char* class_loader_context, int target_sdk_version, bool enable_hidden_api_checks, + int dex_metadata_fd) { static const unsigned int MAX_INSTRUCTION_SET_LEN = 7; if (strlen(instruction_set) >= MAX_INSTRUCTION_SET_LEN) { @@ -409,6 +421,7 @@ static void run_dex2oat(int zip_fd, int oat_fd, int input_vdex_fd, int output_vd sprintf(base_dir, "--classpath-dir=%s", apk_dir.c_str()); } + std::string dex_metadata_fd_arg = "--dm-fd=" + std::to_string(dex_metadata_fd); ALOGV("Running %s in=%s out=%s\n", dex2oat_bin, relative_input_file_name, output_file_name); @@ -439,7 +452,8 @@ static void run_dex2oat(int zip_fd, int oat_fd, int input_vdex_fd, int output_vd + (disable_cdex ? 1 : 0) + (generate_minidebug_info ? 1 : 0) + (target_sdk_version != 0 ? 2 : 0) - + (enable_hidden_api_checks ? 2 : 0)]; + + (enable_hidden_api_checks ? 2 : 0) + + (dex_metadata_fd > -1 ? 1 : 0)]; int i = 0; argv[i++] = dex2oat_bin; argv[i++] = zip_fd_arg; @@ -518,6 +532,9 @@ static void run_dex2oat(int zip_fd, int oat_fd, int input_vdex_fd, int output_vd argv[i++] = "-Xhidden-api-checks"; } + if (dex_metadata_fd > -1) { + argv[i++] = dex_metadata_fd_arg.c_str(); + } // Do not add after dex2oat_flags, they should override others for debugging. argv[i] = NULL; @@ -633,29 +650,31 @@ static unique_fd open_profile(uid_t uid, const std::string& profile, int32_t fla return fd; } -static unique_fd open_current_profile(uid_t uid, userid_t user, const std::string& location, - bool is_secondary_dex) { - std::string profile = create_current_profile_path(user, location, is_secondary_dex); +static unique_fd open_current_profile(uid_t uid, userid_t user, const std::string& package_name, + const std::string& location, bool is_secondary_dex) { + std::string profile = create_current_profile_path(user, package_name, location, + is_secondary_dex); return open_profile(uid, profile, O_RDONLY); } -static unique_fd open_reference_profile(uid_t uid, const std::string& location, bool read_write, - bool is_secondary_dex) { - std::string profile = create_reference_profile_path(location, is_secondary_dex); +static unique_fd open_reference_profile(uid_t uid, const std::string& package_name, + const std::string& location, bool read_write, bool is_secondary_dex) { + std::string profile = create_reference_profile_path(package_name, location, is_secondary_dex); return open_profile(uid, profile, read_write ? (O_CREAT | O_RDWR) : O_RDONLY); } static unique_fd open_spnashot_profile(uid_t uid, const std::string& package_name, - const std::string& code_path) { - std::string profile = create_snapshot_profile_path(package_name, code_path); + const std::string& location) { + std::string profile = create_snapshot_profile_path(package_name, location); return open_profile(uid, profile, O_CREAT | O_RDWR | O_TRUNC); } -static void open_profile_files(uid_t uid, const std::string& location, bool is_secondary_dex, +static void open_profile_files(uid_t uid, const std::string& package_name, + const std::string& location, bool is_secondary_dex, /*out*/ std::vector<unique_fd>* profiles_fd, /*out*/ unique_fd* reference_profile_fd) { // Open the reference profile in read-write mode as profman might need to save the merge. - *reference_profile_fd = open_reference_profile(uid, location, /*read_write*/ true, - is_secondary_dex); + *reference_profile_fd = open_reference_profile(uid, package_name, location, + /*read_write*/ true, is_secondary_dex); // For secondary dex files, we don't really need the user but we use it for sanity checks. // Note: the user owning the dex file should be the current user. @@ -666,7 +685,8 @@ static void open_profile_files(uid_t uid, const std::string& location, bool is_s users = get_known_users(/*volume_uuid*/ nullptr); } for (auto user : users) { - unique_fd profile_fd = open_current_profile(uid, user, location, is_secondary_dex); + unique_fd profile_fd = open_current_profile(uid, user, package_name, location, + is_secondary_dex); // Add to the lists only if both fds are valid. if (profile_fd.get() >= 0) { profiles_fd->push_back(std::move(profile_fd)); @@ -701,28 +721,45 @@ static constexpr int PROFMAN_BIN_RETURN_CODE_BAD_PROFILES = 2; static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_IO = 3; static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_LOCKING = 4; -static void run_profman_merge(const std::vector<unique_fd>& profiles_fd, - const unique_fd& reference_profile_fd) { - static const size_t MAX_INT_LEN = 32; +static void run_profman(const std::vector<unique_fd>& profile_fds, + const unique_fd& reference_profile_fd, + const std::vector<unique_fd>* apk_fds, + bool copy_and_update) { const char* profman_bin = is_debug_runtime() ? "/system/bin/profmand" : "/system/bin/profman"; - std::vector<std::string> profile_args(profiles_fd.size()); - char profile_buf[strlen("--profile-file-fd=") + MAX_INT_LEN]; - for (size_t k = 0; k < profiles_fd.size(); k++) { - sprintf(profile_buf, "--profile-file-fd=%d", profiles_fd[k].get()); - profile_args[k].assign(profile_buf); + if (copy_and_update) { + CHECK_EQ(1u, profile_fds.size()); + CHECK(apk_fds != nullptr); + CHECK_EQ(1u, apk_fds->size()); + } + std::vector<std::string> profile_args(profile_fds.size()); + for (size_t k = 0; k < profile_fds.size(); k++) { + profile_args[k] = "--profile-file-fd=" + std::to_string(profile_fds[k].get()); + } + std::string reference_profile_arg = "--reference-profile-file-fd=" + + std::to_string(reference_profile_fd.get()); + + std::vector<std::string> apk_args; + if (apk_fds != nullptr) { + for (size_t k = 0; k < apk_fds->size(); k++) { + apk_args.push_back("--apk-fd=" + std::to_string((*apk_fds)[k].get())); + } } - char reference_profile_arg[strlen("--reference-profile-file-fd=") + MAX_INT_LEN]; - sprintf(reference_profile_arg, "--reference-profile-file-fd=%d", reference_profile_fd.get()); // program name, reference profile fd, the final NULL and the profile fds - const char* argv[3 + profiles_fd.size()]; + const char* argv[3 + profile_args.size() + apk_args.size() + (copy_and_update ? 1 : 0)]; int i = 0; argv[i++] = profman_bin; - argv[i++] = reference_profile_arg; + argv[i++] = reference_profile_arg.c_str(); for (size_t k = 0; k < profile_args.size(); k++) { argv[i++] = profile_args[k].c_str(); } + for (size_t k = 0; k < apk_args.size(); k++) { + argv[i++] = apk_args[k].c_str(); + } + if (copy_and_update) { + argv[i++] = "--copy-and-update-profile-key"; + } // Do not add after dex2oat_flags, they should override others for debugging. argv[i] = NULL; @@ -731,16 +768,37 @@ static void run_profman_merge(const std::vector<unique_fd>& profiles_fd, exit(68); /* only get here on exec failure */ } + +static void run_profman_merge(const std::vector<unique_fd>& profiles_fd, + const unique_fd& reference_profile_fd, + const std::vector<unique_fd>* apk_fds = nullptr) { + run_profman(profiles_fd, reference_profile_fd, apk_fds, /*copy_and_update*/false); +} + + +static void run_profman_copy_and_update(unique_fd&& profile_fd, + unique_fd&& reference_profile_fd, + unique_fd&& apk_fd) { + std::vector<unique_fd> profiles_fd; + profiles_fd.push_back(std::move(profile_fd)); + std::vector<unique_fd> apk_fds; + apk_fds.push_back(std::move(apk_fd)); + + run_profman(profiles_fd, reference_profile_fd, &apk_fds, /*copy_and_update*/true); +} + // Decides if profile guided compilation is needed or not based on existing profiles. // The location is the package name for primary apks or the dex path for secondary dex files. // Returns true if there is enough information in the current profiles that makes it // worth to recompile the given location. // If the return value is true all the current profiles would have been merged into // the reference profiles accessible with open_reference_profile(). -static bool analyze_profiles(uid_t uid, const std::string& location, bool is_secondary_dex) { +static bool analyze_profiles(uid_t uid, const std::string& package_name, + const std::string& location, bool is_secondary_dex) { std::vector<unique_fd> profiles_fd; unique_fd reference_profile_fd; - open_profile_files(uid, location, is_secondary_dex, &profiles_fd, &reference_profile_fd); + open_profile_files(uid, package_name, location, is_secondary_dex, + &profiles_fd, &reference_profile_fd); if (profiles_fd.empty() || (reference_profile_fd.get() < 0)) { // Skip profile guided compilation because no profiles were found. // Or if the reference profile info couldn't be opened. @@ -802,13 +860,14 @@ static bool analyze_profiles(uid_t uid, const std::string& location, bool is_sec if (should_clear_current_profiles) { if (is_secondary_dex) { // For secondary dex files, the owning user is the current user. - clear_current_profile(location, multiuser_get_user_id(uid), is_secondary_dex); + clear_current_profile(package_name, location, multiuser_get_user_id(uid), + is_secondary_dex); } else { - clear_primary_current_profiles(location); + clear_primary_current_profiles(package_name, location); } } if (should_clear_reference_profile) { - clear_reference_profile(location, is_secondary_dex); + clear_reference_profile(package_name, location, is_secondary_dex); } return need_to_compile; } @@ -819,8 +878,9 @@ static bool analyze_profiles(uid_t uid, const std::string& location, bool is_sec // worth to recompile the package. // If the return value is true all the current profiles would have been merged into // the reference profiles accessible with open_reference_profile(). -bool analyze_primary_profiles(uid_t uid, const std::string& pkgname) { - return analyze_profiles(uid, pkgname, /*is_secondary_dex*/false); +bool analyze_primary_profiles(uid_t uid, const std::string& package_name, + const std::string& profile_name) { + return analyze_profiles(uid, package_name, profile_name, /*is_secondary_dex*/false); } static void run_profman_dump(const std::vector<unique_fd>& profile_fds, @@ -858,12 +918,14 @@ static void run_profman_dump(const std::vector<unique_fd>& profile_fds, exit(68); /* only get here on exec failure */ } -bool dump_profiles(int32_t uid, const std::string& pkgname, const char* code_paths) { +bool dump_profiles(int32_t uid, const std::string& pkgname, const std::string& profile_name, + const std::string& code_path) { std::vector<unique_fd> profile_fds; unique_fd reference_profile_fd; - std::string out_file_name = StringPrintf("/data/misc/profman/%s.txt", pkgname.c_str()); + std::string out_file_name = StringPrintf("/data/misc/profman/%s-%s.txt", + pkgname.c_str(), profile_name.c_str()); - open_profile_files(uid, pkgname, /*is_secondary_dex*/false, + open_profile_files(uid, pkgname, profile_name, /*is_secondary_dex*/false, &profile_fds, &reference_profile_fd); const bool has_reference_profile = (reference_profile_fd.get() != -1); @@ -877,22 +939,20 @@ bool dump_profiles(int32_t uid, const std::string& pkgname, const char* code_pat unique_fd output_fd(open(out_file_name.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW, 0644)); if (fchmod(output_fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) { - ALOGE("installd cannot chmod '%s' dump_profile\n", out_file_name.c_str()); + LOG(ERROR) << "installd cannot chmod file for dump_profile" << out_file_name; return false; } - std::vector<std::string> code_full_paths = base::Split(code_paths, ";"); + std::vector<std::string> dex_locations; std::vector<unique_fd> apk_fds; - for (const std::string& code_full_path : code_full_paths) { - const char* full_path = code_full_path.c_str(); - unique_fd apk_fd(open(full_path, O_RDONLY | O_NOFOLLOW)); - if (apk_fd == -1) { - ALOGE("installd cannot open '%s'\n", full_path); - return false; - } - dex_locations.push_back(get_location_from_path(full_path)); - apk_fds.push_back(std::move(apk_fd)); + unique_fd apk_fd(open(code_path.c_str(), O_RDONLY | O_NOFOLLOW)); + if (apk_fd == -1) { + PLOG(ERROR) << "installd cannot open " << code_path.c_str(); + return false; } + dex_locations.push_back(get_location_from_path(code_path.c_str())); + apk_fds.push_back(std::move(apk_fd)); + pid_t pid = fork(); if (pid == 0) { @@ -913,10 +973,11 @@ bool dump_profiles(int32_t uid, const std::string& pkgname, const char* code_pat } bool copy_system_profile(const std::string& system_profile, - uid_t packageUid, const std::string& data_profile_location) { + uid_t packageUid, const std::string& package_name, const std::string& profile_name) { unique_fd in_fd(open(system_profile.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC)); unique_fd out_fd(open_reference_profile(packageUid, - data_profile_location, + package_name, + profile_name, /*read_write*/ true, /*secondary*/ false)); if (in_fd.get() < 0) { @@ -924,7 +985,7 @@ bool copy_system_profile(const std::string& system_profile, return false; } if (out_fd.get() < 0) { - PLOG(WARNING) << "Could not open profile " << data_profile_location; + PLOG(WARNING) << "Could not open profile " << package_name; return false; } @@ -937,7 +998,7 @@ bool copy_system_profile(const std::string& system_profile, if (flock(out_fd.get(), LOCK_EX | LOCK_NB) != 0) { if (errno != EWOULDBLOCK) { - PLOG(WARNING) << "Error locking profile " << data_profile_location; + PLOG(WARNING) << "Error locking profile " << package_name; } // This implies that the app owning this profile is running // (and has acquired the lock). @@ -945,13 +1006,13 @@ bool copy_system_profile(const std::string& system_profile, // The app never acquires the lock for the reference profiles of primary apks. // Only dex2oat from installd will do that. Since installd is single threaded // we should not see this case. Nevertheless be prepared for it. - PLOG(WARNING) << "Failed to flock " << data_profile_location; + PLOG(WARNING) << "Failed to flock " << package_name; return false; } bool truncated = ftruncate(out_fd.get(), 0) == 0; if (!truncated) { - PLOG(WARNING) << "Could not truncate " << data_profile_location; + PLOG(WARNING) << "Could not truncate " << package_name; } // Copy over data. @@ -965,7 +1026,7 @@ bool copy_system_profile(const std::string& system_profile, write(out_fd.get(), buffer, bytes); } if (flock(out_fd.get(), LOCK_UN) != 0) { - PLOG(WARNING) << "Error unlocking profile " << data_profile_location; + PLOG(WARNING) << "Error unlocking profile " << package_name; } // Use _exit since we don't want to run the global destructors in the child. // b/62597429 @@ -1264,19 +1325,42 @@ unique_fd maybe_open_dexopt_swap_file(const char* out_oat_path) { // Opens the reference profiles if needed. // Note that the reference profile might not exist so it's OK if the fd will be -1. Dex2oatFileWrapper maybe_open_reference_profile(const std::string& pkgname, - const std::string& dex_path, bool profile_guided, bool is_public, int uid, - bool is_secondary_dex) { - // Public apps should not be compiled with profile information ever. Same goes for the special - // package '*' used for the system server. - if (!profile_guided || is_public || (pkgname[0] == '*')) { + const std::string& dex_path, const char* profile_name, bool profile_guided, + bool is_public, int uid, bool is_secondary_dex) { + // If we are not profile guided compilation, or we are compiling system server + // do not bother to open the profiles; we won't be using them. + if (!profile_guided || (pkgname[0] == '*')) { + return Dex2oatFileWrapper(); + } + + // If this is a secondary dex path which is public do not open the profile. + // We cannot compile public secondary dex paths with profiles. That's because + // it will expose how the dex files are used by their owner. + // + // Note that the PackageManager is responsible to set the is_public flag for + // primary apks and we do not check it here. In some cases, e.g. when + // compiling with a public profile from the .dm file the PackageManager will + // set is_public toghether with the profile guided compilation. + if (is_secondary_dex && is_public) { return Dex2oatFileWrapper(); } // Open reference profile in read only mode as dex2oat does not get write permissions. - const std::string location = is_secondary_dex ? dex_path : pkgname; - unique_fd ufd = open_reference_profile(uid, location, /*read_write*/false, is_secondary_dex); - const auto& cleanup = [location, is_secondary_dex]() { - clear_reference_profile(location.c_str(), is_secondary_dex); + std::string location; + if (is_secondary_dex) { + location = dex_path; + } else { + if (profile_name == nullptr) { + // This path is taken for system server re-compilation lunched from ZygoteInit. + return Dex2oatFileWrapper(); + } else { + location = profile_name; + } + } + unique_fd ufd = open_reference_profile(uid, pkgname, location, /*read_write*/false, + is_secondary_dex); + const auto& cleanup = [pkgname, location, is_secondary_dex]() { + clear_reference_profile(pkgname, location, is_secondary_dex); }; return Dex2oatFileWrapper(ufd.release(), cleanup); } @@ -1732,7 +1816,8 @@ static bool process_secondary_dex_dexopt(const std::string& dex_path, const char } // Analyze profiles. - bool profile_was_updated = analyze_profiles(uid, dex_path, /*is_secondary_dex*/true); + bool profile_was_updated = analyze_profiles(uid, pkgname, dex_path, + /*is_secondary_dex*/true); // Run dexoptanalyzer to get dexopt_needed code. This is not expected to return. exec_dexoptanalyzer(dex_path, @@ -1779,7 +1864,8 @@ static bool process_secondary_dex_dexopt(const std::string& dex_path, const char int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* instruction_set, int dexopt_needed, const char* oat_dir, int dexopt_flags, const char* compiler_filter, const char* volume_uuid, const char* class_loader_context, const char* se_info, - bool downgrade, int target_sdk_version) { + bool downgrade, int target_sdk_version, const char* profile_name, + const char* dex_metadata_path) { CHECK(pkgname != nullptr); CHECK(pkgname[0] != 0); if ((dexopt_flags & ~DEXOPT_MASK) != 0) { @@ -1868,7 +1954,15 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins // Open the reference profile if needed. Dex2oatFileWrapper reference_profile_fd = maybe_open_reference_profile( - pkgname, dex_path, profile_guided, is_public, uid, is_secondary_dex); + pkgname, dex_path, profile_name, profile_guided, is_public, uid, is_secondary_dex); + + unique_fd dex_metadata_fd; + if (dex_metadata_path != nullptr) { + dex_metadata_fd.reset(TEMP_FAILURE_RETRY(open(dex_metadata_path, O_RDONLY | O_NOFOLLOW))); + if (dex_metadata_fd.get() < 0) { + PLOG(ERROR) << "Failed to open dex metadata file " << dex_metadata_path; + } + } ALOGV("DexInv: --- BEGIN '%s' ---\n", dex_path); @@ -1899,7 +1993,8 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins reference_profile_fd.get(), class_loader_context, target_sdk_version, - enable_hidden_api_checks); + enable_hidden_api_checks, + dex_metadata_fd.get()); _exit(68); /* only get here on exec failure */ } else { int res = wait_child(pid); @@ -2027,9 +2122,9 @@ bool reconcile_secondary_dex_file(const std::string& dex_path, // Delete profiles. std::string current_profile = create_current_profile_path( - multiuser_get_user_id(uid), dex_path, /*is_secondary*/true); + multiuser_get_user_id(uid), pkgname, dex_path, /*is_secondary*/true); std::string reference_profile = create_reference_profile_path( - dex_path, /*is_secondary*/true); + pkgname, dex_path, /*is_secondary*/true); result = unlink_if_exists(current_profile) && result; result = unlink_if_exists(reference_profile) && result; @@ -2081,6 +2176,90 @@ bool reconcile_secondary_dex_file(const std::string& dex_path, } } +// Compute and return the hash (SHA-256) of the secondary dex file at dex_path. +// Returns true if all parameters are valid and the hash successfully computed and stored in +// out_secondary_dex_hash. +// Also returns true with an empty hash if the file does not currently exist or is not accessible to +// the app. +// For any other errors (e.g. if any of the parameters are invalid) returns false. +bool hash_secondary_dex_file(const std::string& dex_path, const std::string& pkgname, int uid, + const std::unique_ptr<std::string>& volume_uuid, int storage_flag, + std::vector<uint8_t>* out_secondary_dex_hash) { + out_secondary_dex_hash->clear(); + + const char* volume_uuid_cstr = volume_uuid == nullptr ? nullptr : volume_uuid->c_str(); + + if (storage_flag != FLAG_STORAGE_CE && storage_flag != FLAG_STORAGE_DE) { + LOG(ERROR) << "hash_secondary_dex_file called with invalid storage_flag: " + << storage_flag; + return false; + } + + // Pipe to get the hash result back from our child process. + unique_fd pipe_read, pipe_write; + if (!Pipe(&pipe_read, &pipe_write)) { + PLOG(ERROR) << "Failed to create pipe"; + return false; + } + + // Fork so that actual access to the files is done in the app's own UID, to ensure we only + // access data the app itself can access. + pid_t pid = fork(); + if (pid == 0) { + // child -- drop privileges before continuing + drop_capabilities(uid); + pipe_read.reset(); + + if (!validate_secondary_dex_path(pkgname, dex_path, volume_uuid_cstr, uid, storage_flag)) { + LOG(ERROR) << "Could not validate secondary dex path " << dex_path; + _exit(1); + } + + unique_fd fd(TEMP_FAILURE_RETRY(open(dex_path.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW))); + if (fd == -1) { + if (errno == EACCES || errno == ENOENT) { + // Not treated as an error. + _exit(0); + } + PLOG(ERROR) << "Failed to open secondary dex " << dex_path; + _exit(1); + } + + SHA256_CTX ctx; + SHA256_Init(&ctx); + + std::vector<uint8_t> buffer(65536); + while (true) { + ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer.data(), buffer.size())); + if (bytes_read == 0) { + break; + } else if (bytes_read == -1) { + PLOG(ERROR) << "Failed to read secondary dex " << dex_path; + _exit(1); + } + + SHA256_Update(&ctx, buffer.data(), bytes_read); + } + + std::array<uint8_t, SHA256_DIGEST_LENGTH> hash; + SHA256_Final(hash.data(), &ctx); + if (!WriteFully(pipe_write, hash.data(), hash.size())) { + _exit(1); + } + + _exit(0); + } + + // parent + pipe_write.reset(); + + out_secondary_dex_hash->resize(SHA256_DIGEST_LENGTH); + if (!ReadFully(pipe_read, out_secondary_dex_hash->data(), out_secondary_dex_hash->size())) { + out_secondary_dex_hash->clear(); + } + return wait_child(pid) == 0; +} + // Helper for move_ab, so that we can have common failure-case cleanup. static bool unlink_and_rename(const char* from, const char* to) { // Check whether "from" exists, and if so whether it's regular. If it is, unlink. Otherwise, @@ -2336,40 +2515,204 @@ bool create_cache_path_default(char path[PKG_PATH_MAX], const char *src, } } -bool create_profile_snapshot(int32_t app_id, const std::string& package_name, - const std::string& code_path) { +bool open_classpath_files(const std::string& classpath, std::vector<unique_fd>* apk_fds) { + std::vector<std::string> classpaths_elems = base::Split(classpath, ":"); + for (const std::string& elem : classpaths_elems) { + unique_fd fd(TEMP_FAILURE_RETRY(open(elem.c_str(), O_RDONLY))); + if (fd < 0) { + PLOG(ERROR) << "Could not open classpath elem " << elem; + return false; + } else { + apk_fds->push_back(std::move(fd)); + } + } + return true; +} + +static bool create_app_profile_snapshot(int32_t app_id, + const std::string& package_name, + const std::string& profile_name, + const std::string& classpath) { int app_shared_gid = multiuser_get_shared_gid(/*user_id*/ 0, app_id); - unique_fd snapshot_fd = open_spnashot_profile(AID_SYSTEM, package_name, code_path); + unique_fd snapshot_fd = open_spnashot_profile(AID_SYSTEM, package_name, profile_name); if (snapshot_fd < 0) { return false; } std::vector<unique_fd> profiles_fd; unique_fd reference_profile_fd; - open_profile_files(app_shared_gid, package_name, /*is_secondary_dex*/ false, &profiles_fd, - &reference_profile_fd); + open_profile_files(app_shared_gid, package_name, profile_name, /*is_secondary_dex*/ false, + &profiles_fd, &reference_profile_fd); if (profiles_fd.empty() || (reference_profile_fd.get() < 0)) { return false; } profiles_fd.push_back(std::move(reference_profile_fd)); + // Open the class paths elements. These will be used to filter out profile data that does + // not belong to the classpath during merge. + std::vector<unique_fd> apk_fds; + if (!open_classpath_files(classpath, &apk_fds)) { + return false; + } + pid_t pid = fork(); if (pid == 0) { /* child -- drop privileges before continuing */ drop_capabilities(app_shared_gid); - run_profman_merge(profiles_fd, snapshot_fd); + run_profman_merge(profiles_fd, snapshot_fd, &apk_fds); exit(42); /* only get here on exec failure */ } /* parent */ int return_code = wait_child(pid); if (!WIFEXITED(return_code)) { - LOG(WARNING) << "profman failed for " << package_name << ":" << code_path; + LOG(WARNING) << "profman failed for " << package_name << ":" << profile_name; + return false; + } + + return true; +} + +static bool create_boot_image_profile_snapshot(const std::string& package_name, + const std::string& profile_name, + const std::string& classpath) { + // The reference profile directory for the android package might not be prepared. Do it now. + const std::string ref_profile_dir = + create_primary_reference_profile_package_dir_path(package_name); + if (fs_prepare_dir(ref_profile_dir.c_str(), 0770, AID_SYSTEM, AID_SYSTEM) != 0) { + PLOG(ERROR) << "Failed to prepare " << ref_profile_dir; + return false; + } + + // Open and create the snapshot profile. + unique_fd snapshot_fd = open_spnashot_profile(AID_SYSTEM, package_name, profile_name); + + // Collect all non empty profiles. + // The collection will traverse all applications profiles and find the non empty files. + // This has the potential of inspecting a large number of files and directories (depending + // on the number of applications and users). So there is a slight increase in the chance + // to get get occasionally I/O errors (e.g. for opening the file). When that happens do not + // fail the snapshot and aggregate whatever profile we could open. + // + // The profile snapshot is a best effort based on available data it's ok if some data + // from some apps is missing. It will be counter productive for the snapshot to fail + // because we could not open or read some of the files. + std::vector<std::string> profiles; + if (!collect_profiles(&profiles)) { + LOG(WARNING) << "There were errors while collecting the profiles for the boot image."; + } + + // If we have no profiles return early. + if (profiles.empty()) { + return true; + } + + // Open the classpath elements. These will be used to filter out profile data that does + // not belong to the classpath during merge. + std::vector<unique_fd> apk_fds; + if (!open_classpath_files(classpath, &apk_fds)) { + return false; + } + + // If we could not open any files from the classpath return an error. + if (apk_fds.empty()) { + LOG(ERROR) << "Could not open any of the classpath elements."; + return false; + } + + // Aggregate the profiles in batches of kAggregationBatchSize. + // We do this to avoid opening a huge a amount of files. + static constexpr size_t kAggregationBatchSize = 10; + + std::vector<unique_fd> profiles_fd; + for (size_t i = 0; i < profiles.size(); ) { + for (size_t k = 0; k < kAggregationBatchSize && i < profiles.size(); k++, i++) { + unique_fd fd = open_profile(AID_SYSTEM, profiles[i], O_RDONLY); + if (fd.get() >= 0) { + profiles_fd.push_back(std::move(fd)); + } + } + pid_t pid = fork(); + if (pid == 0) { + /* child -- drop privileges before continuing */ + drop_capabilities(AID_SYSTEM); + + run_profman_merge(profiles_fd, snapshot_fd, &apk_fds); + 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; + } + return true; +} + +bool create_profile_snapshot(int32_t app_id, const std::string& package_name, + const std::string& profile_name, const std::string& classpath) { + if (app_id == -1) { + return create_boot_image_profile_snapshot(package_name, profile_name, classpath); + } else { + return create_app_profile_snapshot(app_id, package_name, profile_name, classpath); + } +} + +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) { + // 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))); + unique_fd apk_fd(TEMP_FAILURE_RETRY(open(code_path.c_str(), O_RDONLY | O_NOFOLLOW))); + if (apk_fd < 0) { + PLOG(ERROR) << "Could not open code path " << code_path; + return false; + } + + 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); + + // The copy and update takes ownership over the fds. + run_profman_copy_and_update(std::move(dex_metadata_fd), + std::move(ref_profile_fd), + std::move(apk_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; } diff --git a/cmds/installd/dexopt.h b/cmds/installd/dexopt.h index 4923a43d4d..ae1412e854 100644 --- a/cmds/installd/dexopt.h +++ b/cmds/installd/dexopt.h @@ -33,24 +33,27 @@ static constexpr int DEX2OAT_FOR_BOOT_IMAGE = 2; static constexpr int DEX2OAT_FOR_FILTER = 3; static constexpr int DEX2OAT_FOR_RELOCATION = 4; -// Clear the reference profile for the primary apk of the given package. -bool clear_primary_reference_profile(const std::string& pkgname); -// Clear the current profile for the primary apk of the given package and user. -bool clear_primary_current_profile(const std::string& pkgname, userid_t user); -// Clear all current profile for the primary apk of the given package. -bool clear_primary_current_profiles(const std::string& pkgname); - -bool move_ab(const char* apk_path, const char* instruction_set, const char* output_path); +// Clear the reference profile identified by the given profile name. +bool clear_primary_reference_profile(const std::string& pkgname, const std::string& profile_name); +// Clear the current profile identified by the given profile name (for single user). +bool clear_primary_current_profile(const std::string& pkgname, const std::string& profile_name, + userid_t user); +// Clear all current profiles identified by the given profile name (all users). +bool clear_primary_current_profiles(const std::string& pkgname, const std::string& profile_name); // Decide if profile guided compilation is needed or not based on existing profiles. -// The analysis is done for the primary apks (base + splits) of the given package. +// The analysis is done for a single profile name (which corresponds to a single code path). // Returns true if there is enough information in the current profiles that makes it // worth to recompile the package. // If the return value is true all the current profiles would have been merged into // the reference profiles accessible with open_reference_profile(). -bool analyze_primary_profiles(uid_t uid, const std::string& pkgname); +bool analyze_primary_profiles(uid_t uid, + const std::string& pkgname, + const std::string& profile_name); -// Create a snapshot of the profile information for the given package and code path. +// Create a snapshot of the profile information for the given package profile. +// If appId is -1, the method creates the profile snapshot for the boot image. +// // The profile snapshot is the aggregation of all existing profiles (all current user // profiles & the reference profile) and is meant to capture the all the profile information // without performing a merge into the reference profile which might impact future dex2oat @@ -59,14 +62,34 @@ bool analyze_primary_profiles(uid_t uid, const std::string& pkgname); // ownership is assigned to AID_SYSTEM. // The snapshot location is reference_profile_location.snapshot. If a snapshot is already // there, it will be truncated and overwritten. -bool create_profile_snapshot(int32_t app_id, const std::string& package, - const std::string& code_path); - -bool dump_profiles(int32_t uid, const std::string& pkgname, const char* code_paths); +// +// The classpath acts as filter: only profiling data belonging to elements of the classpath +// will end up in the snapshot. +bool create_profile_snapshot(int32_t app_id, + const std::string& package, + const std::string& profile_name, + const std::string& classpath); + +bool dump_profiles(int32_t uid, + const std::string& pkgname, + const std::string& profile_name, + const std::string& code_path); bool copy_system_profile(const std::string& system_profile, uid_t packageUid, - const std::string& data_profile_location); + 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); @@ -75,10 +98,15 @@ bool reconcile_secondary_dex_file(const std::string& dex_path, const std::unique_ptr<std::string>& volumeUuid, int storage_flag, /*out*/bool* out_secondary_dex_exists); +bool hash_secondary_dex_file(const std::string& dex_path, + const std::string& pkgname, int uid, const std::unique_ptr<std::string>& volume_uuid, + int storage_flag, std::vector<uint8_t>* out_secondary_dex_hash); + int dexopt(const char *apk_path, uid_t uid, const char *pkgName, const char *instruction_set, int dexopt_needed, const char* oat_dir, int dexopt_flags, const char* compiler_filter, const char* volume_uuid, const char* class_loader_context, const char* se_info, - bool downgrade, int target_sdk_version); + bool downgrade, int target_sdk_version, const char* profile_name, + const char* dexMetadataPath); bool calculate_oat_file_path_default(char path[PKG_PATH_MAX], const char *oat_dir, const char *apk_path, const char *instruction_set); @@ -89,6 +117,8 @@ bool calculate_odex_file_path_default(char path[PKG_PATH_MAX], const char *apk_p bool create_cache_path_default(char path[PKG_PATH_MAX], const char *src, const char *instruction_set); +bool move_ab(const char* apk_path, const char* instruction_set, const char* output_path); + } // namespace installd } // namespace android diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp index fe49e707c1..b3e87f2be2 100644 --- a/cmds/installd/otapreopt.cpp +++ b/cmds/installd/otapreopt.cpp @@ -43,6 +43,7 @@ #include "globals.h" #include "installd_constants.h" #include "installd_deps.h" // Need to fill in requirements of commands. +#include "otapreopt_parameters.h" #include "otapreopt_utils.h" #include "system_properties.h" #include "utils.h" @@ -158,31 +159,15 @@ class OTAPreoptService { } std::string GetOTADataDirectory() const { - return StringPrintf("%s/%s", GetOtaDirectoryPrefix().c_str(), target_slot_.c_str()); + return StringPrintf("%s/%s", GetOtaDirectoryPrefix().c_str(), GetTargetSlot().c_str()); } const std::string& GetTargetSlot() const { - return target_slot_; + return parameters_.target_slot; } private: - struct Parameters { - const char *apk_path; - uid_t uid; - const char *pkgName; - const char *instruction_set; - int dexopt_needed; - const char* oat_dir; - int dexopt_flags; - const char* compiler_filter; - const char* volume_uuid; - const char* shared_libraries; - const char* se_info; - bool downgrade; - int target_sdk_version; - }; - bool ReadSystemProperties() { static constexpr const char* kPropertyFiles[] = { "/default.prop", "/system/build.prop" @@ -306,439 +291,7 @@ private: } bool ReadArguments(int argc, char** argv) { - // Expected command line: - // target-slot [version] dexopt {DEXOPT_PARAMETERS} - - const char* target_slot_arg = argv[1]; - if (target_slot_arg == nullptr) { - LOG(ERROR) << "Missing parameters"; - return false; - } - // Sanitize value. Only allow (a-zA-Z0-9_)+. - target_slot_ = target_slot_arg; - if (!ValidateTargetSlotSuffix(target_slot_)) { - LOG(ERROR) << "Target slot suffix not legal: " << target_slot_; - return false; - } - - // Check for version or "dexopt" next. - if (argv[2] == nullptr) { - LOG(ERROR) << "Missing parameters"; - return false; - } - - if (std::string("dexopt").compare(argv[2]) == 0) { - // This is version 1 (N) or pre-versioning version 2. - constexpr int kV2ArgCount = 1 // "otapreopt" - + 1 // slot - + 1 // "dexopt" - + 1 // apk_path - + 1 // uid - + 1 // pkg - + 1 // isa - + 1 // dexopt_needed - + 1 // oat_dir - + 1 // dexopt_flags - + 1 // filter - + 1 // volume - + 1 // libs - + 1; // seinfo - if (argc == kV2ArgCount) { - return ReadArgumentsV2(argc, argv, false); - } else { - return ReadArgumentsV1(argc, argv); - } - } - - uint32_t version; - if (!ParseUInt(argv[2], &version)) { - LOG(ERROR) << "Could not parse version: " << argv[2]; - return false; - } - - switch (version) { - case 2: - return ReadArgumentsV2(argc, argv, true); - case 3: - return ReadArgumentsV3(argc, argv); - case 4: - return ReadArgumentsV4(argc, argv); - - default: - LOG(ERROR) << "Unsupported version " << version; - return false; - } - } - - bool ReadArgumentsV2(int argc ATTRIBUTE_UNUSED, char** argv, bool versioned) { - size_t dexopt_index = versioned ? 3 : 2; - - // Check for "dexopt". - if (argv[dexopt_index] == nullptr) { - LOG(ERROR) << "Missing parameters"; - return false; - } - if (std::string("dexopt").compare(argv[dexopt_index]) != 0) { - LOG(ERROR) << "Expected \"dexopt\""; - return false; - } - - size_t param_index = 0; - for (;; ++param_index) { - const char* param = argv[dexopt_index + 1 + param_index]; - if (param == nullptr) { - break; - } - - switch (param_index) { - case 0: - package_parameters_.apk_path = param; - break; - - case 1: - package_parameters_.uid = atoi(param); - break; - - case 2: - package_parameters_.pkgName = param; - break; - - case 3: - package_parameters_.instruction_set = param; - break; - - case 4: - package_parameters_.dexopt_needed = atoi(param); - break; - - case 5: - package_parameters_.oat_dir = param; - break; - - case 6: - package_parameters_.dexopt_flags = atoi(param); - break; - - case 7: - package_parameters_.compiler_filter = param; - break; - - case 8: - package_parameters_.volume_uuid = ParseNull(param); - break; - - case 9: - package_parameters_.shared_libraries = ParseNull(param); - break; - - case 10: - package_parameters_.se_info = ParseNull(param); - break; - - default: - LOG(ERROR) << "Too many arguments, got " << param; - return false; - } - } - - // Set downgrade to false. It is only relevant when downgrading compiler - // filter, which is not the case during ota. - package_parameters_.downgrade = false; - - // Set target_sdk_version to 0, ie the platform SDK version. This is - // conservative and may force some classes to verify at runtime. - package_parameters_.target_sdk_version = 0; - - if (param_index != 11) { - LOG(ERROR) << "Not enough parameters"; - return false; - } - - return true; - } - - bool ReadArgumentsV3(int argc ATTRIBUTE_UNUSED, char** argv) { - size_t dexopt_index = 3; - - // Check for "dexopt". - if (argv[dexopt_index] == nullptr) { - LOG(ERROR) << "Missing parameters"; - return false; - } - if (std::string("dexopt").compare(argv[dexopt_index]) != 0) { - LOG(ERROR) << "Expected \"dexopt\""; - return false; - } - - size_t param_index = 0; - for (;; ++param_index) { - const char* param = argv[dexopt_index + 1 + param_index]; - if (param == nullptr) { - break; - } - - switch (param_index) { - case 0: - package_parameters_.apk_path = param; - break; - - case 1: - package_parameters_.uid = atoi(param); - break; - - case 2: - package_parameters_.pkgName = param; - break; - - case 3: - package_parameters_.instruction_set = param; - break; - - case 4: - package_parameters_.dexopt_needed = atoi(param); - break; - - case 5: - package_parameters_.oat_dir = param; - break; - - case 6: - package_parameters_.dexopt_flags = atoi(param); - break; - - case 7: - package_parameters_.compiler_filter = param; - break; - - case 8: - package_parameters_.volume_uuid = ParseNull(param); - break; - - case 9: - package_parameters_.shared_libraries = ParseNull(param); - break; - - case 10: - package_parameters_.se_info = ParseNull(param); - break; - - case 11: - package_parameters_.downgrade = ParseBool(param); - break; - - default: - LOG(ERROR) << "Too many arguments, got " << param; - return false; - } - } - - // Set target_sdk_version to 0, ie the platform SDK version. This is - // conservative and may force some classes to verify at runtime. - package_parameters_.target_sdk_version = 0; - - if (param_index != 12) { - LOG(ERROR) << "Not enough parameters"; - return false; - } - - return true; - } - - bool ReadArgumentsV4(int argc ATTRIBUTE_UNUSED, char** argv) { - size_t dexopt_index = 3; - - // Check for "dexopt". - if (argv[dexopt_index] == nullptr) { - LOG(ERROR) << "Missing parameters"; - return false; - } - if (std::string("dexopt").compare(argv[dexopt_index]) != 0) { - LOG(ERROR) << "Expected \"dexopt\""; - return false; - } - - size_t param_index = 0; - for (;; ++param_index) { - const char* param = argv[dexopt_index + 1 + param_index]; - if (param == nullptr) { - break; - } - - switch (param_index) { - case 0: - package_parameters_.apk_path = param; - break; - - case 1: - package_parameters_.uid = atoi(param); - break; - - case 2: - package_parameters_.pkgName = param; - break; - - case 3: - package_parameters_.instruction_set = param; - break; - - case 4: - package_parameters_.dexopt_needed = atoi(param); - break; - - case 5: - package_parameters_.oat_dir = param; - break; - - case 6: - package_parameters_.dexopt_flags = atoi(param); - break; - - case 7: - package_parameters_.compiler_filter = param; - break; - - case 8: - package_parameters_.volume_uuid = ParseNull(param); - break; - - case 9: - package_parameters_.shared_libraries = ParseNull(param); - break; - - case 10: - package_parameters_.se_info = ParseNull(param); - break; - - case 11: - package_parameters_.downgrade = ParseBool(param); - break; - - case 12: - package_parameters_.target_sdk_version = atoi(param); - break; - - default: - LOG(ERROR) << "Too many arguments, got " << param; - return false; - } - } - - if (param_index != 13) { - LOG(ERROR) << "Not enough parameters"; - return false; - } - - return true; - } - - static int ReplaceMask(int input, int old_mask, int new_mask) { - return (input & old_mask) != 0 ? new_mask : 0; - } - - bool ReadArgumentsV1(int argc ATTRIBUTE_UNUSED, char** argv) { - // Check for "dexopt". - if (argv[2] == nullptr) { - LOG(ERROR) << "Missing parameters"; - return false; - } - if (std::string("dexopt").compare(argv[2]) != 0) { - LOG(ERROR) << "Expected \"dexopt\""; - return false; - } - - size_t param_index = 0; - for (;; ++param_index) { - const char* param = argv[3 + param_index]; - if (param == nullptr) { - break; - } - - switch (param_index) { - case 0: - package_parameters_.apk_path = param; - break; - - case 1: - package_parameters_.uid = atoi(param); - break; - - case 2: - package_parameters_.pkgName = param; - break; - - case 3: - package_parameters_.instruction_set = param; - break; - - case 4: { - // Version 1 had: - // DEXOPT_DEX2OAT_NEEDED = 1 - // DEXOPT_PATCHOAT_NEEDED = 2 - // DEXOPT_SELF_PATCHOAT_NEEDED = 3 - // We will simply use DEX2OAT_FROM_SCRATCH. - package_parameters_.dexopt_needed = DEX2OAT_FROM_SCRATCH; - break; - } - - case 5: - package_parameters_.oat_dir = param; - break; - - case 6: { - // Version 1 had: - constexpr int OLD_DEXOPT_PUBLIC = 1 << 1; - // Note: DEXOPT_SAFEMODE has been removed. - // constexpr int OLD_DEXOPT_SAFEMODE = 1 << 2; - constexpr int OLD_DEXOPT_DEBUGGABLE = 1 << 3; - constexpr int OLD_DEXOPT_BOOTCOMPLETE = 1 << 4; - constexpr int OLD_DEXOPT_PROFILE_GUIDED = 1 << 5; - constexpr int OLD_DEXOPT_OTA = 1 << 6; - int input = atoi(param); - package_parameters_.dexopt_flags = - ReplaceMask(input, OLD_DEXOPT_PUBLIC, DEXOPT_PUBLIC) | - ReplaceMask(input, OLD_DEXOPT_DEBUGGABLE, DEXOPT_DEBUGGABLE) | - ReplaceMask(input, OLD_DEXOPT_BOOTCOMPLETE, DEXOPT_BOOTCOMPLETE) | - ReplaceMask(input, OLD_DEXOPT_PROFILE_GUIDED, DEXOPT_PROFILE_GUIDED) | - ReplaceMask(input, OLD_DEXOPT_OTA, 0); - break; - } - - case 7: - package_parameters_.compiler_filter = param; - break; - - case 8: - package_parameters_.volume_uuid = ParseNull(param); - break; - - case 9: - package_parameters_.shared_libraries = ParseNull(param); - break; - - default: - LOG(ERROR) << "Too many arguments, got " << param; - return false; - } - } - - if (param_index != 10) { - LOG(ERROR) << "Not enough parameters"; - return false; - } - - // Set se_info to null. It is only relevant for secondary dex files, which we won't - // receive from a v1 A side. - package_parameters_.se_info = nullptr; - - // Set downgrade to false. It is only relevant when downgrading compiler - // filter, which is not the case during ota. - package_parameters_.downgrade = false; - - // Set target_sdk_version to 0, ie the platform SDK version. This is - // conservative and may force some classes to verify at runtime. - package_parameters_.target_sdk_version = 0; - - return true; + return parameters_.ReadArguments(argc, const_cast<const char**>(argv)); } void PrepareEnvironment() { @@ -754,11 +307,11 @@ private: // Ensure that we have the right boot image. The first time any app is // compiled, we'll try to generate it. bool PrepareBootImage(bool force) const { - if (package_parameters_.instruction_set == nullptr) { + if (parameters_.instruction_set == nullptr) { LOG(ERROR) << "Instruction set missing."; return false; } - const char* isa = package_parameters_.instruction_set; + const char* isa = parameters_.instruction_set; // Check whether the file exists where expected. std::string dalvik_cache = GetOTADataDirectory() + "/" + DALVIK_CACHE; @@ -982,9 +535,9 @@ private: // jar content must be exactly the same). // (This is ugly as it's the only thing where we need to understand the contents - // of package_parameters_, but it beats postponing the decision or using the call- + // of parameters_, but it beats postponing the decision or using the call- // backs to do weird things.) - const char* apk_path = package_parameters_.apk_path; + const char* apk_path = parameters_.apk_path; CHECK(apk_path != nullptr); if (StartsWith(apk_path, android_root_)) { const char* last_slash = strrchr(apk_path, '/'); @@ -1011,21 +564,24 @@ private: return false; } - // Run dexopt with the parameters of package_parameters_. + // Run dexopt with the parameters of parameters_. + // TODO(calin): embed the profile name in the parameters. int Dexopt() { - return dexopt(package_parameters_.apk_path, - package_parameters_.uid, - package_parameters_.pkgName, - package_parameters_.instruction_set, - package_parameters_.dexopt_needed, - package_parameters_.oat_dir, - package_parameters_.dexopt_flags, - package_parameters_.compiler_filter, - package_parameters_.volume_uuid, - package_parameters_.shared_libraries, - package_parameters_.se_info, - package_parameters_.downgrade, - package_parameters_.target_sdk_version); + return dexopt(parameters_.apk_path, + parameters_.uid, + parameters_.pkgName, + parameters_.instruction_set, + parameters_.dexopt_needed, + parameters_.oat_dir, + parameters_.dexopt_flags, + parameters_.compiler_filter, + parameters_.volume_uuid, + parameters_.shared_libraries, + parameters_.se_info, + parameters_.downgrade, + parameters_.target_sdk_version, + parameters_.profile_name, + parameters_.dex_metadata_path); } int RunPreopt() { @@ -1056,12 +612,12 @@ private: // If this was a profile-guided run, we may have profile version issues. Try to downgrade, // if possible. - if ((package_parameters_.dexopt_flags & DEXOPT_PROFILE_GUIDED) == 0) { + if ((parameters_.dexopt_flags & DEXOPT_PROFILE_GUIDED) == 0) { return dexopt_result; } LOG(WARNING) << "Downgrading compiler filter in an attempt to progress compilation"; - package_parameters_.dexopt_flags &= ~DEXOPT_PROFILE_GUIDED; + parameters_.dexopt_flags &= ~DEXOPT_PROFILE_GUIDED; return Dexopt(); } @@ -1186,13 +742,12 @@ private: SystemProperties system_properties_; // Some select properties that are always needed. - std::string target_slot_; std::string android_root_; std::string android_data_; std::string boot_classpath_; std::string asec_mountpoint_; - Parameters package_parameters_; + OTAPreoptParameters parameters_; // Store environment values we need to set. std::vector<std::string> environ_; diff --git a/cmds/installd/otapreopt_parameters.cpp b/cmds/installd/otapreopt_parameters.cpp new file mode 100644 index 0000000000..1f857280b9 --- /dev/null +++ b/cmds/installd/otapreopt_parameters.cpp @@ -0,0 +1,356 @@ +/* + ** Copyright 2016, 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 "otapreopt_parameters.h" + +#include <android-base/logging.h> + +#include "dexopt.h" +#include "installd_constants.h" +#include "otapreopt_utils.h" + +#ifndef LOG_TAG +#define LOG_TAG "otapreopt" +#endif + +namespace android { +namespace installd { + +static bool ParseBool(const char* in) { + if (strcmp(in, "true") == 0) { + return true; + } + return false; +} + +static const char* ParseNull(const char* arg) { + return (strcmp(arg, "!") == 0) ? nullptr : arg; +} + +static bool ParseUInt(const char* in, uint32_t* out) { + char* end; + long long int result = strtoll(in, &end, 0); + if (in == end || *end != '\0') { + return false; + } + if (result < std::numeric_limits<uint32_t>::min() || + std::numeric_limits<uint32_t>::max() < result) { + return false; + } + *out = static_cast<uint32_t>(result); + return true; +} + +bool OTAPreoptParameters::ReadArguments(int argc, const char** argv) { + // Expected command line: + // target-slot [version] dexopt {DEXOPT_PARAMETERS} + + const char* target_slot_arg = argv[1]; + if (target_slot_arg == nullptr) { + LOG(ERROR) << "Missing parameters"; + return false; + } + // Sanitize value. Only allow (a-zA-Z0-9_)+. + target_slot = target_slot_arg; + if (!ValidateTargetSlotSuffix(target_slot)) { + LOG(ERROR) << "Target slot suffix not legal: " << target_slot; + return false; + } + + // Check for version or "dexopt" next. + if (argv[2] == nullptr) { + LOG(ERROR) << "Missing parameters"; + return false; + } + + if (std::string("dexopt").compare(argv[2]) == 0) { + // This is version 1 (N) or pre-versioning version 2. + constexpr int kV2ArgCount = 1 // "otapreopt" + + 1 // slot + + 1 // "dexopt" + + 1 // apk_path + + 1 // uid + + 1 // pkg + + 1 // isa + + 1 // dexopt_needed + + 1 // oat_dir + + 1 // dexopt_flags + + 1 // filter + + 1 // volume + + 1 // libs + + 1; // seinfo + if (argc == kV2ArgCount) { + return ReadArgumentsPostV1(2, argv, false); + } else { + return ReadArgumentsV1(argv); + } + } + + uint32_t version; + if (!ParseUInt(argv[2], &version)) { + LOG(ERROR) << "Could not parse version: " << argv[2]; + return false; + } + + return ReadArgumentsPostV1(version, argv, true); +} + +static int ReplaceMask(int input, int old_mask, int new_mask) { + return (input & old_mask) != 0 ? new_mask : 0; +} + +bool OTAPreoptParameters::ReadArgumentsV1(const char** argv) { + // Check for "dexopt". + if (argv[2] == nullptr) { + LOG(ERROR) << "Missing parameters"; + return false; + } + if (std::string("dexopt").compare(argv[2]) != 0) { + LOG(ERROR) << "Expected \"dexopt\" but found: " << argv[2]; + return false; + } + + size_t param_index = 0; + for (;; ++param_index) { + const char* param = argv[3 + param_index]; + if (param == nullptr) { + break; + } + + switch (param_index) { + case 0: + apk_path = param; + break; + + case 1: + uid = atoi(param); + break; + + case 2: + pkgName = param; + break; + + case 3: + instruction_set = param; + break; + + case 4: { + // Version 1 had: + // DEXOPT_DEX2OAT_NEEDED = 1 + // DEXOPT_PATCHOAT_NEEDED = 2 + // DEXOPT_SELF_PATCHOAT_NEEDED = 3 + // We will simply use DEX2OAT_FROM_SCRATCH. + dexopt_needed = DEX2OAT_FROM_SCRATCH; + break; + } + + case 5: + oat_dir = param; + break; + + case 6: { + // Version 1 had: + constexpr int OLD_DEXOPT_PUBLIC = 1 << 1; + // Note: DEXOPT_SAFEMODE has been removed. + // constexpr int OLD_DEXOPT_SAFEMODE = 1 << 2; + constexpr int OLD_DEXOPT_DEBUGGABLE = 1 << 3; + constexpr int OLD_DEXOPT_BOOTCOMPLETE = 1 << 4; + constexpr int OLD_DEXOPT_PROFILE_GUIDED = 1 << 5; + constexpr int OLD_DEXOPT_OTA = 1 << 6; + int input = atoi(param); + dexopt_flags = + ReplaceMask(input, OLD_DEXOPT_PUBLIC, DEXOPT_PUBLIC) | + ReplaceMask(input, OLD_DEXOPT_DEBUGGABLE, DEXOPT_DEBUGGABLE) | + ReplaceMask(input, OLD_DEXOPT_BOOTCOMPLETE, DEXOPT_BOOTCOMPLETE) | + ReplaceMask(input, OLD_DEXOPT_PROFILE_GUIDED, DEXOPT_PROFILE_GUIDED) | + ReplaceMask(input, OLD_DEXOPT_OTA, 0); + break; + } + + case 7: + compiler_filter = param; + break; + + case 8: + volume_uuid = ParseNull(param); + break; + + case 9: + shared_libraries = ParseNull(param); + break; + + default: + LOG(ERROR) << "Too many arguments, got " << param; + return false; + } + } + + if (param_index != 10) { + LOG(ERROR) << "Not enough parameters"; + return false; + } + + // Set se_info to null. It is only relevant for secondary dex files, which we won't + // receive from a v1 A side. + se_info = nullptr; + + // Set downgrade to false. It is only relevant when downgrading compiler + // filter, which is not the case during ota. + downgrade = false; + + // Set target_sdk_version to 0, ie the platform SDK version. This is + // conservative and may force some classes to verify at runtime. + target_sdk_version = 0; + + // Set the profile name to the primary apk profile. + profile_name = "primary.prof"; + + // By default we don't have a dex metadata file. + dex_metadata_path = nullptr; + + return true; +} + +bool OTAPreoptParameters::ReadArgumentsPostV1(uint32_t version, const char** argv, bool versioned) { + size_t num_args_expected = 0; + switch (version) { + case 2: num_args_expected = 11; break; + case 3: num_args_expected = 12; break; + case 4: num_args_expected = 13; break; + case 5: num_args_expected = 14; break; + case 6: num_args_expected = 15; break; + default: + LOG(ERROR) << "Don't know how to read arguments for version " << version; + return false; + } + size_t dexopt_index = versioned ? 3 : 2; + + // Check for "dexopt". + if (argv[dexopt_index] == nullptr) { + LOG(ERROR) << "Missing parameters"; + return false; + } + if (std::string("dexopt").compare(argv[dexopt_index]) != 0) { + LOG(ERROR) << "Expected \"dexopt\" but found: " << argv[dexopt_index]; + return false; + } + + // Validate the number of arguments. + size_t num_args_actual = 0; + while (argv[dexopt_index + 1 + num_args_actual] != nullptr) { + num_args_actual++; + } + + if (num_args_actual != num_args_expected) { + LOG(ERROR) << "Invalid number of arguments. expected=" + << num_args_expected << " actual=" << num_args_actual; + return false; + } + + // The number of arguments is OK. + // Configure the default values for the parameters that were added after V1. + // The default values will be overwritten in case they are passed as arguments. + + // Set downgrade to false. It is only relevant when downgrading compiler + // filter, which is not the case during ota. + downgrade = false; + + // Set target_sdk_version to 0, ie the platform SDK version. This is + // conservative and may force some classes to verify at runtime. + target_sdk_version = 0; + + // Set the profile name to the primary apk profile. + profile_name = "primary.prof"; + + // By default we don't have a dex metadata file. + dex_metadata_path = nullptr; + + for (size_t param_index = 0; param_index < num_args_actual; ++param_index) { + const char* param = argv[dexopt_index + 1 + param_index]; + switch (param_index) { + case 0: + apk_path = param; + break; + + case 1: + uid = atoi(param); + break; + + case 2: + pkgName = param; + break; + + case 3: + instruction_set = param; + break; + + case 4: + dexopt_needed = atoi(param); + break; + + case 5: + oat_dir = param; + break; + + case 6: + dexopt_flags = atoi(param); + break; + + case 7: + compiler_filter = param; + break; + + case 8: + volume_uuid = ParseNull(param); + break; + + case 9: + shared_libraries = ParseNull(param); + break; + + case 10: + se_info = ParseNull(param); + break; + + case 11: + downgrade = ParseBool(param); + break; + + case 12: + target_sdk_version = atoi(param); + break; + + case 13: + profile_name = ParseNull(param); + break; + + case 14: + dex_metadata_path = ParseNull(param); + break; + + default: + LOG(FATAL) << "Should not get here. Did you call ReadArguments " + << "with the right expectation? index=" << param_index + << " num_args=" << num_args_actual; + return false; + } + } + + return true; +} + +} // namespace installd +} // namespace android diff --git a/cmds/installd/otapreopt_parameters.h b/cmds/installd/otapreopt_parameters.h new file mode 100644 index 0000000000..0f3bb8c981 --- /dev/null +++ b/cmds/installd/otapreopt_parameters.h @@ -0,0 +1,59 @@ +/* + ** Copyright 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. + */ + +#ifndef OTAPREOPT_PARAMETERS_H_ +#define OTAPREOPT_PARAMETERS_H_ + +#include <string> +#include <sys/types.h> + +namespace android { +namespace installd { + +class OTAPreoptParameters { + public: + bool ReadArguments(int argc, const char** argv); + + private: + bool ReadArgumentsV1(const char** argv); + bool ReadArgumentsPostV1(uint32_t version, const char** argv, bool versioned); + + const char* apk_path; + uid_t uid; + const char* pkgName; + const char* instruction_set; + int dexopt_needed; + const char* oat_dir; + int dexopt_flags; + const char* compiler_filter; + const char* volume_uuid; + const char* shared_libraries; + const char* se_info; + bool downgrade; + int target_sdk_version; + const char* profile_name; + const char* dex_metadata_path; + + std::string target_slot; + + friend class OTAPreoptService; + friend class OTAPreoptTest; +}; + +} // namespace installd +} // namespace android + +#endif // OTAPREOPT_PARAMETERS_H_
\ No newline at end of file diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp index 1a22992cb8..7438d3d9b4 100644 --- a/cmds/installd/tests/Android.bp +++ b/cmds/installd/tests/Android.bp @@ -24,6 +24,7 @@ cc_test { shared_libs: [ "libbase", "libbinder", + "libcrypto", "libcutils", "libselinux", "libutils", @@ -44,6 +45,7 @@ cc_test { shared_libs: [ "libbase", "libbinder", + "libcrypto", "libcutils", "libselinux", "libutils", @@ -64,6 +66,7 @@ cc_test { shared_libs: [ "libbase", "libbinder", + "libcrypto", "libcutils", "libselinux", "libutils", @@ -75,3 +78,20 @@ cc_test { "liblogwrap", ], } + +cc_test { + name: "installd_otapreopt_test", + clang: true, + srcs: ["installd_otapreopt_test.cpp"], + cflags: ["-Wall", "-Werror"], + shared_libs: [ + "libbase", + "libcutils", + "libutils", + ], + static_libs: [ + "liblog", + "libotapreoptparameters" + ], +} + diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp index ff2950631f..5a829652ee 100644 --- a/cmds/installd/tests/installd_dexopt_test.cpp +++ b/cmds/installd/tests/installd_dexopt_test.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include <cstdlib> #include <fcntl.h> #include <stdlib.h> #include <string.h> @@ -153,10 +154,12 @@ protected: InstalldNativeService* service_; std::unique_ptr<std::string> volume_uuid_; std::string package_name_; + std::string apk_path_; std::string app_apk_dir_; std::string app_private_dir_ce_; std::string app_private_dir_de_; std::string se_info_; + std::string app_oat_dir_; int64_t ce_data_inode_; @@ -197,13 +200,13 @@ protected: void create_mock_app() { // Create the oat dir. - std::string app_oat_dir = app_apk_dir_ + "/oat"; + app_oat_dir_ = app_apk_dir_ + "/oat"; mkdir(app_apk_dir_, kSystemUid, kSystemGid, 0755); - service_->createOatDir(app_oat_dir, kRuntimeIsa); + service_->createOatDir(app_oat_dir_, kRuntimeIsa); // Copy the primary apk. - std::string apk_path = app_apk_dir_ + "/base.jar"; - ASSERT_TRUE(WriteBase64ToFile(kDexFile, apk_path, kSystemUid, kSystemGid, 0644)); + apk_path_ = app_apk_dir_ + "/base.jar"; + ASSERT_TRUE(WriteBase64ToFile(kDexFile, apk_path_, kSystemUid, kSystemGid, 0644)); // Create the app user data. ASSERT_TRUE(service_->createAppData( @@ -259,6 +262,8 @@ protected: std::unique_ptr<std::string> se_info_ptr(new std::string(se_info_)); bool downgrade = false; int32_t target_sdk_version = 0; // default + std::unique_ptr<std::string> profile_name_ptr = nullptr; + std::unique_ptr<std::string> dm_path_ptr = nullptr; binder::Status result = service_->dexopt(path, uid, @@ -272,7 +277,9 @@ protected: class_loader_context_ptr, se_info_ptr, downgrade, - target_sdk_version); + target_sdk_version, + profile_name_ptr, + dm_path_ptr); ASSERT_EQ(should_binder_call_succeed, result.isOk()); int expected_access = should_dex_be_compiled ? 0 : -1; std::string odex = GetSecondaryDexArtifact(path, "odex"); @@ -320,6 +327,113 @@ protected: ASSERT_EQ(gid, st.st_gid); ASSERT_EQ(mode, st.st_mode); } + + void CompilePrimaryDexOk(std::string compiler_filter, + int32_t dex_flags, + const char* oat_dir, + int32_t uid, + int32_t dexopt_needed, + const char* dm_path = nullptr, + bool downgrade = false) { + return CompilePrimaryDex( + compiler_filter, dex_flags, oat_dir, uid, dexopt_needed, dm_path, downgrade, true); + } + + void CompilePrimaryDexFail(std::string compiler_filter, + int32_t dex_flags, + const char* oat_dir, + int32_t uid, + int32_t dexopt_needed, + const char* dm_path = nullptr, + bool downgrade = false) { + return CompilePrimaryDex( + compiler_filter, dex_flags, oat_dir, uid, dexopt_needed, dm_path, downgrade, false); + } + + void CompilePrimaryDex(std::string compiler_filter, + int32_t dex_flags, + const char* oat_dir, + int32_t uid, + int32_t dexopt_needed, + const char* dm_path, + bool downgrade, + bool should_binder_call_succeed) { + std::unique_ptr<std::string> package_name_ptr(new std::string(package_name_)); + std::unique_ptr<std::string> out_path( + oat_dir == nullptr ? nullptr : new std::string(oat_dir)); + std::unique_ptr<std::string> class_loader_context_ptr(new std::string("&")); + std::unique_ptr<std::string> se_info_ptr(new std::string(se_info_)); + int32_t target_sdk_version = 0; // default + std::unique_ptr<std::string> profile_name_ptr(new std::string("primary.prof")); + std::unique_ptr<std::string> dm_path_ptr = nullptr; + if (dm_path != nullptr) { + dm_path_ptr.reset(new std::string(dm_path)); + } + + bool prof_result; + binder::Status prof_binder_result = service_->prepareAppProfile( + package_name_, kTestUserId, kTestAppId, *profile_name_ptr, /*code path*/ "base.apk", + /*dex_metadata*/ nullptr, &prof_result); + + ASSERT_TRUE(prof_binder_result.isOk()); + ASSERT_TRUE(prof_result); + + binder::Status result = service_->dexopt(apk_path_, + uid, + package_name_ptr, + kRuntimeIsa, + dexopt_needed, + out_path, + dex_flags, + compiler_filter, + volume_uuid_, + class_loader_context_ptr, + se_info_ptr, + downgrade, + target_sdk_version, + profile_name_ptr, + dm_path_ptr); + ASSERT_EQ(should_binder_call_succeed, result.isOk()); + + if (!should_binder_call_succeed) { + return; + } + // Check the access to the compiler output. + // - speed-profile artifacts are not world-wide readable. + // - files are owned by the system uid. + std::string odex = GetPrimaryDexArtifact(oat_dir, apk_path_, "odex"); + std::string vdex = GetPrimaryDexArtifact(oat_dir, apk_path_, "vdex"); + std::string art = GetPrimaryDexArtifact(oat_dir, apk_path_, "art"); + + bool is_public = (dex_flags & DEXOPT_PUBLIC) != 0; + mode_t mode = S_IFREG | (is_public ? 0644 : 0640); + CheckFileAccess(odex, kSystemUid, uid, mode); + CheckFileAccess(vdex, kSystemUid, uid, mode); + + if (compiler_filter == "speed-profile") { + CheckFileAccess(art, kSystemUid, uid, mode); + } + } + + std::string GetPrimaryDexArtifact(const char* oat_dir, + const std::string& dex_path, + const std::string& type) { + if (oat_dir == nullptr) { + std::string path = dex_path; + for (auto it = path.begin() + 1; it < path.end(); ++it) { + if (*it == '/') { + *it = '@'; + } + } + return android_data_dir + DALVIK_CACHE + '/' + kRuntimeIsa + "/" + path + + "@classes.dex"; + } else { + std::string::size_type name_end = dex_path.rfind('.'); + std::string::size_type name_start = dex_path.rfind('/'); + return std::string(oat_dir) + "/" + kRuntimeIsa + "/" + + dex_path.substr(name_start + 1, name_end - name_start) + type; + } + } }; @@ -366,6 +480,87 @@ TEST_F(DexoptTest, DexoptSecondaryAcessViaDifferentUidError) { /*binder_ok*/ false, /*compile_ok*/ false, kSystemUid); } +TEST_F(DexoptTest, DexoptPrimaryPublic) { + LOG(INFO) << "DexoptPrimaryPublic"; + CompilePrimaryDexOk("verify", + DEXOPT_BOOTCOMPLETE | DEXOPT_PUBLIC, + app_oat_dir_.c_str(), + kTestAppGid, + DEX2OAT_FROM_SCRATCH); +} + +TEST_F(DexoptTest, DexoptPrimaryFailedInvalidFilter) { + LOG(INFO) << "DexoptPrimaryFailedInvalidFilter"; + CompilePrimaryDexFail("awesome-filter", + DEXOPT_IDLE_BACKGROUND_JOB | DEXOPT_PUBLIC, + app_oat_dir_.c_str(), + kTestAppGid, + DEX2OAT_FROM_SCRATCH); +} + +TEST_F(DexoptTest, DexoptPrimaryProfileNonPublic) { + LOG(INFO) << "DexoptPrimaryProfileNonPublic"; + CompilePrimaryDexOk("speed-profile", + DEXOPT_BOOTCOMPLETE | DEXOPT_PROFILE_GUIDED, + app_oat_dir_.c_str(), + kTestAppGid, + DEX2OAT_FROM_SCRATCH); +} + +TEST_F(DexoptTest, DexoptPrimaryProfilePublic) { + LOG(INFO) << "DexoptPrimaryProfilePublic"; + CompilePrimaryDexOk("speed-profile", + DEXOPT_BOOTCOMPLETE | DEXOPT_PROFILE_GUIDED | DEXOPT_PUBLIC, + app_oat_dir_.c_str(), + kTestAppGid, + DEX2OAT_FROM_SCRATCH); +} + +TEST_F(DexoptTest, DexoptPrimaryBackgroundOk) { + LOG(INFO) << "DexoptPrimaryBackgroundOk"; + CompilePrimaryDexOk("speed-profile", + DEXOPT_IDLE_BACKGROUND_JOB | DEXOPT_PROFILE_GUIDED, + app_oat_dir_.c_str(), + kTestAppGid, + DEX2OAT_FROM_SCRATCH); +} + +class PrimaryDexReCompilationTest : public DexoptTest { + public: + virtual void SetUp() { + DexoptTest::SetUp(); + CompilePrimaryDexOk("verify", + DEXOPT_BOOTCOMPLETE | DEXOPT_PUBLIC, + app_oat_dir_.c_str(), + kTestAppGid, + DEX2OAT_FROM_SCRATCH); + std::string odex = GetSecondaryDexArtifact(apk_path_, "odex"); + std::string vdex = GetSecondaryDexArtifact(apk_path_, "vdex"); + + first_compilation_odex_fd_.reset(open(odex.c_str(), O_RDONLY)); + first_compilation_vdex_fd_.reset(open(vdex.c_str(), O_RDONLY)); + } + + virtual void TearDown() { + first_compilation_odex_fd_.reset(-1); + first_compilation_vdex_fd_.reset(-1); + DexoptTest::TearDown(); + } + + protected: + unique_fd first_compilation_odex_fd_; + unique_fd first_compilation_vdex_fd_; +}; + +TEST_F(PrimaryDexReCompilationTest, DexoptPrimaryUpdateInPlaceVdex) { + LOG(INFO) << "DexoptPrimaryUpdateInPlaceVdex"; + + CompilePrimaryDexOk("verify", + DEXOPT_IDLE_BACKGROUND_JOB | DEXOPT_PUBLIC, + app_oat_dir_.c_str(), + kTestAppGid, + DEX2OAT_FOR_BOOT_IMAGE); +} class ReconcileTest : public DexoptTest { virtual void SetUp() { @@ -431,17 +626,22 @@ class ProfileTest : public DexoptTest { std::string ref_profile_; std::string snap_profile_; + static constexpr const char* kPrimaryProfile = "primary.prof"; + virtual void SetUp() { DexoptTest::SetUp(); cur_profile_ = create_current_profile_path( - kTestUserId, package_name_, /*is_secondary_dex*/ false); - ref_profile_ = create_reference_profile_path(package_name_, /*is_secondary_dex*/ false); - snap_profile_ = create_snapshot_profile_path(package_name_, "base.jar"); + kTestUserId, package_name_, kPrimaryProfile, /*is_secondary_dex*/ false); + ref_profile_ = create_reference_profile_path(package_name_, kPrimaryProfile, + /*is_secondary_dex*/ false); + snap_profile_ = create_snapshot_profile_path(package_name_, kPrimaryProfile); } - void SetupProfile(const std::string& path, uid_t uid, gid_t gid, mode_t mode, int32_t seed) { - run_cmd("profman --generate-test-profile-seed=" + std::to_string(seed) + - " --generate-test-profile-num-dex=2 --generate-test-profile=" + path); + void SetupProfile(const std::string& path, uid_t uid, gid_t gid, mode_t mode, + int32_t num_dex) { + run_cmd("profman --generate-test-profile-seed=" + std::to_string(num_dex) + + " --generate-test-profile-num-dex=" + std::to_string(num_dex) + + " --generate-test-profile=" + path); ::chmod(path.c_str(), mode); ::chown(path.c_str(), uid, gid); } @@ -449,7 +649,7 @@ class ProfileTest : public DexoptTest { void SetupProfiles(bool setup_ref) { SetupProfile(cur_profile_, kTestAppUid, kTestAppGid, 0600, 1); if (setup_ref) { - SetupProfile(ref_profile_, kTestAppUid, kTestAppGid, 0060, 2); + SetupProfile(ref_profile_, kTestAppUid, kTestAppGid, 0600, 2); } } @@ -457,7 +657,7 @@ class ProfileTest : public DexoptTest { bool expected_result) { bool result; binder::Status binder_result = service_->createProfileSnapshot( - appid, package_name, "base.jar", &result); + appid, package_name, kPrimaryProfile, apk_path_, &result); ASSERT_TRUE(binder_result.isOk()); ASSERT_EQ(expected_result, result); @@ -475,7 +675,8 @@ class ProfileTest : public DexoptTest { run_cmd("touch " + expected_profile_content); run_cmd("profman --profile-file=" + cur_profile_ + " --profile-file=" + ref_profile_ + - " --reference-profile-file=" + expected_profile_content); + " --reference-profile-file=" + expected_profile_content + + " --apk=" + apk_path_); ASSERT_TRUE(AreFilesEqual(expected_profile_content, snap_profile_)); @@ -493,7 +694,62 @@ class ProfileTest : public DexoptTest { ASSERT_TRUE(WIFEXITED(wait_child(pid))); } - private: + void mergePackageProfiles(const std::string& package_name, + const std::string& code_path, + bool expected_result) { + bool result; + binder::Status binder_result = service_->mergeProfiles( + kTestAppUid, package_name, code_path, &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; + } + + // Check that the snapshot was created witht he expected acess flags. + CheckFileAccess(ref_profile_, kTestAppUid, kTestAppUid, 0600 | S_IFREG); + + // The snapshot should be equivalent to the merge of profiles. + std::string ref_profile_content = ref_profile_ + ".expected"; + run_cmd("rm -f " + ref_profile_content); + run_cmd("touch " + ref_profile_content); + run_cmd("profman --profile-file=" + cur_profile_ + + " --profile-file=" + ref_profile_ + + " --reference-profile-file=" + ref_profile_content); + + 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)); + } + + protected: void TransitionToSystemServer() { ASSERT_TRUE(DropCapabilities(kSystemUid, kSystemGid)); int32_t res = selinux_android_setcontext( @@ -559,24 +815,46 @@ TEST_F(ProfileTest, ProfileSnapshotDestroySnapshot) { SetupProfiles(/*setup_ref*/ true); createProfileSnapshot(kTestAppId, package_name_, /*expected_result*/ true); - binder::Status binder_result = service_->destroyProfileSnapshot(package_name_, "base.jar"); + binder::Status binder_result = service_->destroyProfileSnapshot(package_name_, kPrimaryProfile); ASSERT_TRUE(binder_result.isOk()); struct stat st; ASSERT_EQ(-1, stat(snap_profile_.c_str(), &st)); ASSERT_EQ(ENOENT, errno); } +TEST_F(ProfileTest, ProfileMergeOk) { + LOG(INFO) << "ProfileMergeOk"; + + SetupProfiles(/*setup_ref*/ true); + mergePackageProfiles(package_name_, "primary.prof", /*expected_result*/ true); +} + +// The reference profile is created on the fly. We need to be able to +// merge without one. +TEST_F(ProfileTest, ProfileMergeOkNoReference) { + LOG(INFO) << "ProfileMergeOkNoReference"; + + SetupProfiles(/*setup_ref*/ false); + mergePackageProfiles(package_name_, "primary.prof", /*expected_result*/ true); +} + +TEST_F(ProfileTest, ProfileMergeFailWrongPackage) { + LOG(INFO) << "ProfileMergeFailWrongPackage"; + + SetupProfiles(/*setup_ref*/ true); + mergePackageProfiles("not.there", "primary.prof", /*expected_result*/ false); +} + TEST_F(ProfileTest, ProfileDirOk) { LOG(INFO) << "ProfileDirOk"; std::string cur_profile_dir = create_primary_current_profile_package_dir_path( kTestUserId, package_name_); std::string cur_profile_file = create_current_profile_path(kTestUserId, package_name_, - /*is_secondary_dex*/false); + kPrimaryProfile, /*is_secondary_dex*/false); std::string ref_profile_dir = create_primary_reference_profile_package_dir_path(package_name_); CheckFileAccess(cur_profile_dir, kTestAppUid, kTestAppUid, 0700 | S_IFDIR); - CheckFileAccess(cur_profile_file, kTestAppUid, kTestAppUid, 0600 | S_IFREG); CheckFileAccess(ref_profile_dir, kSystemUid, kTestAppGid, 0770 | S_IFDIR); } @@ -588,7 +866,7 @@ TEST_F(ProfileTest, ProfileDirOkAfterFixup) { std::string cur_profile_dir = create_primary_current_profile_package_dir_path( kTestUserId, package_name_); std::string cur_profile_file = create_current_profile_path(kTestUserId, package_name_, - /*is_secondary_dex*/false); + kPrimaryProfile, /*is_secondary_dex*/false); std::string ref_profile_dir = create_primary_reference_profile_package_dir_path(package_name_); // Simulate a pre-P setup by changing the owner to kTestAppGid and permissions to 0700. @@ -608,9 +886,132 @@ TEST_F(ProfileTest, ProfileDirOkAfterFixup) { // Check the file access. CheckFileAccess(cur_profile_dir, kTestAppUid, kTestAppUid, 0700 | S_IFDIR); - CheckFileAccess(cur_profile_file, kTestAppUid, kTestAppUid, 0600 | S_IFREG); 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); +} + + +class BootProfileTest : public ProfileTest { + public: + virtual void setup() { + ProfileTest::SetUp(); + intial_android_profiles_dir = android_profiles_dir; + } + + virtual void TearDown() { + android_profiles_dir = intial_android_profiles_dir; + ProfileTest::TearDown(); + } + + void UpdateAndroidProfilesDir(const std::string& profile_dir) { + android_profiles_dir = profile_dir; + // We need to create the reference profile directory in the new profile dir. + run_cmd("mkdir -p " + profile_dir + "/ref"); + } + + void createBootImageProfileSnapshot(const std::string& classpath, bool expected_result) { + bool result; + binder::Status binder_result = service_->createProfileSnapshot( + -1, "android", "android.prof", classpath, &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; + } + + // Check that the snapshot was created with he expected access flags. + const std::string boot_profile = create_snapshot_profile_path("android", "android.prof"); + CheckFileAccess(boot_profile, kSystemUid, kSystemGid, 0600 | S_IFREG); + + pid_t pid = fork(); + if (pid == 0) { + /* child */ + TransitionToSystemServer(); + + // System server should be able to open the snapshot. + unique_fd fd(open(boot_profile.c_str(), O_RDONLY)); + ASSERT_TRUE(fd > -1) << "Failed to open profile as kSystemUid: " << strerror(errno); + _exit(0); + } + /* parent */ + ASSERT_TRUE(WIFEXITED(wait_child(pid))); + } + protected: + std::string intial_android_profiles_dir; +}; + +TEST_F(BootProfileTest, BootProfileSnapshotOk) { + LOG(INFO) << "BootProfileSnapshotOk"; + char* boot_classpath = getenv("BOOTCLASSPATH"); + ASSERT_TRUE(boot_classpath != nullptr); + createBootImageProfileSnapshot(boot_classpath, /*expected_result*/ true); +} + +TEST_F(BootProfileTest, BootProfileSnapshotFailEmptyClasspath) { + LOG(INFO) << "BootProfileSnapshotFailEmptyClasspath"; + + createBootImageProfileSnapshot(/*boot_classpath*/ "", /*expected_result*/ false); +} + +TEST_F(BootProfileTest, BootProfileSnapshotOkNoProfiles) { + LOG(INFO) << "BootProfileSnapshotOkNoProfiles"; + char* boot_classpath = getenv("BOOTCLASSPATH"); + ASSERT_TRUE(boot_classpath != nullptr); + + // The app_apk_dir has no profiles. So we shouldn't be able to merge anything. + // Still, this is not a failure case. + UpdateAndroidProfilesDir(app_apk_dir_); + createBootImageProfileSnapshot(boot_classpath, /*expected_result*/ true); +} + +// Verify that profile collection. +TEST_F(BootProfileTest, CollectProfiles) { + LOG(INFO) << "CollectProfiles"; + + // Create some profile directories mimicking the real profile structure. + run_cmd("mkdir -p " + app_private_dir_de_ + "/profiles/ref"); + run_cmd("mkdir -p " + app_private_dir_de_ + "/profiles/cur/0/"); + run_cmd("mkdir -p " + app_private_dir_de_ + "/profiles/cur/1/"); + // Create an empty profile. + run_cmd("touch " + app_private_dir_de_ + "/profiles/cur/1/primary.prof"); + // Create a random file. + run_cmd("touch " + app_private_dir_de_ + "/profiles/cur/0/non.profile.file"); + + // Create some non-empty profiles. + std::string current_prof = app_private_dir_de_ + "/profiles/cur/0/primary.prof"; + run_cmd("echo 1 > " + current_prof); + std::string ref_prof = app_private_dir_de_ + "/profiles/ref/primary.prof"; + run_cmd("echo 1 > " + ref_prof); + + UpdateAndroidProfilesDir(app_private_dir_de_ + "/profiles"); + + std::vector<std::string> profiles; + collect_profiles(&profiles); + + // Only two profiles should be in the output. + ASSERT_EQ(2u, profiles.size()); + ASSERT_TRUE(std::find(profiles.begin(), profiles.end(), current_prof) != profiles.end()); + ASSERT_TRUE(std::find(profiles.begin(), profiles.end(), ref_prof) != profiles.end()); +} + } // namespace installd } // namespace android diff --git a/cmds/installd/tests/installd_otapreopt_test.cpp b/cmds/installd/tests/installd_otapreopt_test.cpp new file mode 100644 index 0000000000..8b8dde16cf --- /dev/null +++ b/cmds/installd/tests/installd_otapreopt_test.cpp @@ -0,0 +1,210 @@ +/** + * 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 <stdlib.h> +#include <string.h> + +#include <android-base/logging.h> +#include <gtest/gtest.h> + +#include "otapreopt_parameters.h" + +namespace android { +namespace installd { + +static bool ParseBool(const char* in) { + if (strcmp(in, "true") == 0) { + return true; + } + return false; +} + +static const char* ParseNull(const char* arg) { + return (strcmp(arg, "!") == 0) ? nullptr : arg; +} + +class OTAPreoptTest : public testing::Test { +protected: + virtual void SetUp() { + setenv("ANDROID_LOG_TAGS", "*:f", 1); + android::base::InitLogging(nullptr, android::base::StderrLogger); + } + + void verifyPackageParameters(const OTAPreoptParameters& params, + uint32_t version, + bool versioned, + const char** args) { + // otapreopt target-slot [version] dexopt {DEXOPT_PARAMETERS} + int i = 0; + if (version > 2 || (version == 2 && versioned)) { + i += 4; + } else { + i += 3; + } + ASSERT_STREQ(params.target_slot.c_str(), args[1]); + ASSERT_STREQ(params.apk_path, args[i++]); + ASSERT_EQ(params.uid, static_cast<uid_t>(atoi(args[i++]))); + ASSERT_STREQ(params.pkgName, args[i++]); + ASSERT_STREQ(params.instruction_set, args[i++]); + ASSERT_EQ(params.dexopt_needed, atoi(args[i++])); + ASSERT_STREQ(params.oat_dir, args[i++]); + ASSERT_EQ(params.dexopt_flags, atoi(args[i++])); + ASSERT_STREQ(params.compiler_filter, args[i++]); + ASSERT_STREQ(params.volume_uuid, ParseNull(args[i++])); + ASSERT_STREQ(params.shared_libraries, ParseNull(args[i++])); + if (version > 1) { + ASSERT_STREQ(params.se_info, ParseNull(args[i++])); + } else { + ASSERT_EQ(params.se_info, nullptr); + } + if (version > 2) { + ASSERT_EQ(params.downgrade, ParseBool(args[i++])); + } else { + ASSERT_FALSE(params.downgrade); + } + if (version > 3) { + ASSERT_EQ(params.target_sdk_version, atoi(args[i++])); + } else { + ASSERT_EQ(params.target_sdk_version, 0); + } + if (version > 4) { + ASSERT_STREQ(params.profile_name, ParseNull(args[i++])); + } else { + ASSERT_STREQ(params.profile_name, "primary.prof"); + } + if (version > 5) { + ASSERT_STREQ(params.dex_metadata_path, ParseNull(args[i++])); + } else { + ASSERT_EQ(params.dex_metadata_path, nullptr); + } + } + + const char* getVersionCStr(uint32_t version) { + switch (version) { + case 1: return "1"; + case 2: return "2"; + case 3: return "3"; + case 4: return "4"; + case 5: return "5"; + case 6: return "6"; + } + return nullptr; + } + + std::vector<const char*> getArgs(uint32_t version, bool versioned) { + std::vector<const char*> args; + args.push_back("otapreopt"); // "otapreopt" + args.push_back("a"); // slot + if (versioned) { + args.push_back(getVersionCStr(version)); + } + args.push_back("dexopt"); // "dexopt" + args.push_back("foo.apk"); // apk_path + args.push_back("123"); // uid + args.push_back("pkgname"); // pkg + args.push_back("arm"); // isa + args.push_back("1"); // dexopt_needed (DEX2OAT_FROM_SCRATCH) + args.push_back("oat_dir"); // oat_dir + args.push_back("0"); // dexopt_flags + args.push_back("speed"); // filter + args.push_back("!"); // volume + args.push_back("shared.lib"); // libs + + if (version > 1) { + args.push_back("!"); // seinfo + } + if (version > 2) { + args.push_back("true"); // downgrade + } + if (version > 3) { + args.push_back("28"); // sdk_version + } + if (version > 4) { + args.push_back("split_a.prof"); // profile_name + } + if (version > 5) { + args.push_back("dex_metadata.dm"); // dex_metadata_path + } + args.push_back(nullptr); // we have to end with null. + return args; + } + + void VerifyReadArguments(uint32_t version, bool versioned) { + OTAPreoptParameters params; + std::vector<const char*> args = getArgs(version, versioned); + ASSERT_TRUE(params.ReadArguments(args.size() - 1, args.data())); + verifyPackageParameters(params, version, versioned, args.data()); + } +}; + +TEST_F(OTAPreoptTest, ReadArgumentsV1) { + VerifyReadArguments(1, false); +} + +TEST_F(OTAPreoptTest, ReadArgumentsV2Unversioned) { + VerifyReadArguments(2, false); +} + +TEST_F(OTAPreoptTest, ReadArgumentsV2) { + VerifyReadArguments(2, true); +} + +TEST_F(OTAPreoptTest, ReadArgumentsV3) { + VerifyReadArguments(3, true); +} + +TEST_F(OTAPreoptTest, ReadArgumentsV4) { + VerifyReadArguments(4, true); +} + +TEST_F(OTAPreoptTest, ReadArgumentsV5) { + VerifyReadArguments(5, true); +} + +TEST_F(OTAPreoptTest, ReadArgumentsV6) { + VerifyReadArguments(6, true); +} + +TEST_F(OTAPreoptTest, ReadArgumentsFailToManyArgs) { + OTAPreoptParameters params; + std::vector<const char*> args = getArgs(5, true); + args[2] = "3"; // pretend it's version 3. It should fail since there are too many args. + ASSERT_FALSE(params.ReadArguments(args.size() - 1, args.data())); +} + +TEST_F(OTAPreoptTest, ReadArgumentsFailInsufficientArgs) { + OTAPreoptParameters params; + std::vector<const char*> args = getArgs(4, true); + args[2] = "5"; // pretend it's version 5. It should fail since there are insufficient args. + ASSERT_FALSE(params.ReadArguments(args.size() - 1, args.data())); +} + +TEST_F(OTAPreoptTest, ReadArgumentsFailInvalidDexopt) { + OTAPreoptParameters params; + std::vector<const char*> args = getArgs(4, true); + args[3] = "dexopt-invalid"; + ASSERT_FALSE(params.ReadArguments(args.size() - 1, args.data())); +} + +TEST_F(OTAPreoptTest, ReadArgumentsFailInvalidSlot) { + OTAPreoptParameters params; + std::vector<const char*> args = getArgs(3, true); + args[1] = "invalid-slot???"; + ASSERT_FALSE(params.ReadArguments(args.size() - 1, args.data())); +} + +} // namespace installd +} // namespace android diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp index ca812bdeeb..a5af5d740e 100644 --- a/cmds/installd/tests/installd_service_test.cpp +++ b/cmds/installd/tests/installd_service_test.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include <sstream> #include <stdlib.h> #include <string.h> #include <sys/statvfs.h> @@ -56,16 +57,19 @@ bool create_cache_path(char path[PKG_PATH_MAX], const char *src, const char *ins return create_cache_path_default(path, src, instruction_set); } +static std::string get_full_path(const char* path) { + return StringPrintf("/data/local/tmp/user/0/%s", path); +} + static void mkdir(const char* path, uid_t owner, gid_t group, mode_t mode) { - const char* fullPath = StringPrintf("/data/local/tmp/user/0/%s", path).c_str(); - ::mkdir(fullPath, mode); - ::chown(fullPath, owner, group); - ::chmod(fullPath, mode); + const std::string fullPath = get_full_path(path); + ::mkdir(fullPath.c_str(), mode); + ::chown(fullPath.c_str(), owner, group); + ::chmod(fullPath.c_str(), mode); } static void touch(const char* path, uid_t owner, gid_t group, mode_t mode) { - int fd = ::open(StringPrintf("/data/local/tmp/user/0/%s", path).c_str(), - O_RDWR | O_CREAT, mode); + int fd = ::open(get_full_path(path).c_str(), O_RDWR | O_CREAT, mode); ::fchown(fd, owner, group); ::fchmod(fd, mode); ::close(fd); @@ -73,13 +77,13 @@ static void touch(const char* path, uid_t owner, gid_t group, mode_t mode) { static int stat_gid(const char* path) { struct stat buf; - ::stat(StringPrintf("/data/local/tmp/user/0/%s", path).c_str(), &buf); + ::stat(get_full_path(path).c_str(), &buf); return buf.st_gid; } static int stat_mode(const char* path) { struct stat buf; - ::stat(StringPrintf("/data/local/tmp/user/0/%s", path).c_str(), &buf); + ::stat(get_full_path(path).c_str(), &buf); return buf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISGID); } @@ -149,6 +153,69 @@ TEST_F(ServiceTest, FixupAppData_Moved) { EXPECT_EQ(10000, stat_gid("com.example/bar/file")); } +TEST_F(ServiceTest, HashSecondaryDex) { + LOG(INFO) << "HashSecondaryDex"; + + mkdir("com.example", 10000, 10000, 0700); + mkdir("com.example/foo", 10000, 10000, 0700); + touch("com.example/foo/file", 10000, 20000, 0700); + + std::vector<uint8_t> result; + std::string dexPath = get_full_path("com.example/foo/file"); + EXPECT_TRUE(service->hashSecondaryDexFile( + dexPath, "com.example", 10000, testUuid, FLAG_STORAGE_CE, &result).isOk()); + + EXPECT_EQ(result.size(), 32U); + + std::ostringstream output; + output << std::hex << std::setfill('0'); + for (auto b : result) { + output << std::setw(2) << +b; + } + + // This is the SHA256 of an empty string (sha256sum /dev/null) + EXPECT_EQ(output.str(), "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); +} + +TEST_F(ServiceTest, HashSecondaryDex_NoSuch) { + LOG(INFO) << "HashSecondaryDex_NoSuch"; + + std::vector<uint8_t> result; + std::string dexPath = get_full_path("com.example/foo/file"); + EXPECT_TRUE(service->hashSecondaryDexFile( + dexPath, "com.example", 10000, testUuid, FLAG_STORAGE_CE, &result).isOk()); + + EXPECT_EQ(result.size(), 0U); +} + +TEST_F(ServiceTest, HashSecondaryDex_Unreadable) { + LOG(INFO) << "HashSecondaryDex_Unreadable"; + + mkdir("com.example", 10000, 10000, 0700); + mkdir("com.example/foo", 10000, 10000, 0700); + touch("com.example/foo/file", 10000, 20000, 0300); + + std::vector<uint8_t> result; + std::string dexPath = get_full_path("com.example/foo/file"); + EXPECT_TRUE(service->hashSecondaryDexFile( + dexPath, "com.example", 10000, testUuid, FLAG_STORAGE_CE, &result).isOk()); + + EXPECT_EQ(result.size(), 0U); +} + +TEST_F(ServiceTest, HashSecondaryDex_WrongApp) { + LOG(INFO) << "HashSecondaryDex_WrongApp"; + + mkdir("com.example", 10000, 10000, 0700); + mkdir("com.example/foo", 10000, 10000, 0700); + touch("com.example/foo/file", 10000, 20000, 0700); + + std::vector<uint8_t> result; + std::string dexPath = get_full_path("com.example/foo/file"); + EXPECT_FALSE(service->hashSecondaryDexFile( + dexPath, "com.wrong", 10000, testUuid, FLAG_STORAGE_CE, &result).isOk()); +} + TEST_F(ServiceTest, CalculateOat) { char buf[PKG_PATH_MAX]; diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp index 49da85dcfc..bbff6fb0d7 100644 --- a/cmds/installd/tests/installd_utils_test.cpp +++ b/cmds/installd/tests/installd_utils_test.cpp @@ -337,34 +337,50 @@ TEST_F(UtilsTest, CreateDataRefProfilePackagePath) { } TEST_F(UtilsTest, CreatePrimaryCurrentProfile) { - std::string expected = + std::string expected_base = create_primary_current_profile_package_dir_path(0, "com.example") + "/primary.prof"; - EXPECT_EQ(expected, - create_current_profile_path(/*user*/0, "com.example", /*is_secondary*/false)); + EXPECT_EQ(expected_base, + create_current_profile_path(/*user*/0, "com.example", "primary.prof", + /*is_secondary*/false)); + + std::string expected_split = + create_primary_current_profile_package_dir_path(0, "com.example") + "/split.prof"; + EXPECT_EQ(expected_split, + create_current_profile_path(/*user*/0, "com.example", "split.prof", + /*is_secondary*/false)); } TEST_F(UtilsTest, CreatePrimaryReferenceProfile) { - std::string expected = + std::string expected_base = create_primary_reference_profile_package_dir_path("com.example") + "/primary.prof"; - EXPECT_EQ(expected, - create_reference_profile_path("com.example", /*is_secondary*/false)); + EXPECT_EQ(expected_base, + create_reference_profile_path("com.example", "primary.prof", /*is_secondary*/false)); + + std::string expected_split = + create_primary_reference_profile_package_dir_path("com.example") + "/split.prof"; + EXPECT_EQ(expected_split, + create_reference_profile_path("com.example", "split.prof", /*is_secondary*/false)); } TEST_F(UtilsTest, CreateProfileSnapshot) { - std::string expected = + std::string expected_base = create_primary_reference_profile_package_dir_path("com.example") + "/primary.prof.snapshot"; - EXPECT_EQ(expected, create_snapshot_profile_path("com.example", "base.apk")); + EXPECT_EQ(expected_base, create_snapshot_profile_path("com.example", "primary.prof")); + + std::string expected_split = + create_primary_reference_profile_package_dir_path("com.example") + "/split.prof.snapshot"; + EXPECT_EQ(expected_split, create_snapshot_profile_path("com.example", "split.prof")); } TEST_F(UtilsTest, CreateSecondaryCurrentProfile) { EXPECT_EQ("/data/user/0/com.example/oat/secondary.dex.cur.prof", - create_current_profile_path(/*user*/0, + create_current_profile_path(/*user*/0, "com.example", "/data/user/0/com.example/secondary.dex", /*is_secondary*/true)); } TEST_F(UtilsTest, CreateSecondaryReferenceProfile) { EXPECT_EQ("/data/user/0/com.example/oat/secondary.dex.prof", - create_reference_profile_path( + create_reference_profile_path("com.example", "/data/user/0/com.example/secondary.dex", /*is_secondary*/true)); } diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp index 61c9c8ff08..a8c32ed2a5 100644 --- a/cmds/installd/utils.cpp +++ b/cmds/installd/utils.cpp @@ -26,7 +26,9 @@ #include <sys/statvfs.h> #include <android-base/logging.h> +#include <android-base/strings.h> #include <android-base/stringprintf.h> +#include <android-base/unique_fd.h> #include <cutils/fs.h> #include <cutils/properties.h> #include <log/log.h> @@ -40,7 +42,9 @@ #define DEBUG_XATTRS 0 +using android::base::EndsWith; using android::base::StringPrintf; +using android::base::unique_fd; namespace android { namespace installd { @@ -235,7 +239,6 @@ std::string create_data_dalvik_cache_path() { // Keep profile paths in sync with ActivityThread and LoadedApk. const std::string PROFILE_EXT = ".prof"; const std::string CURRENT_PROFILE_EXT = ".cur"; -const std::string PRIMARY_PROFILE_NAME = "primary" + PROFILE_EXT; const std::string SNAPSHOT_PROFILE_EXT = ".snapshot"; // Gets the parent directory and the file name for the given secondary dex path. @@ -256,8 +259,8 @@ static bool get_secondary_dex_location(const std::string& dex_path, return true; } -std::string create_current_profile_path(userid_t user, const std::string& location, - bool is_secondary_dex) { +std::string create_current_profile_path(userid_t user, const std::string& package_name, + const std::string& location, bool is_secondary_dex) { if (is_secondary_dex) { // Secondary dex current profiles are stored next to the dex files under the oat folder. std::string dex_dir; @@ -269,12 +272,14 @@ std::string create_current_profile_path(userid_t user, const std::string& locati PROFILE_EXT.c_str()); } else { // Profiles for primary apks are under /data/misc/profiles/cur. - std::string profile_dir = create_primary_current_profile_package_dir_path(user, location); - return StringPrintf("%s/%s", profile_dir.c_str(), PRIMARY_PROFILE_NAME.c_str()); + std::string profile_dir = create_primary_current_profile_package_dir_path( + user, package_name); + return StringPrintf("%s/%s", profile_dir.c_str(), location.c_str()); } } -std::string create_reference_profile_path(const std::string& location, bool is_secondary_dex) { +std::string create_reference_profile_path(const std::string& package_name, + const std::string& location, bool is_secondary_dex) { if (is_secondary_dex) { // Secondary dex reference profiles are stored next to the dex files under the oat folder. std::string dex_dir; @@ -285,16 +290,15 @@ std::string create_reference_profile_path(const std::string& location, bool is_s dex_dir.c_str(), dex_name.c_str(), PROFILE_EXT.c_str()); } else { // Reference profiles for primary apks are stored in /data/misc/profile/ref. - std::string profile_dir = create_primary_reference_profile_package_dir_path(location); - return StringPrintf("%s/%s", profile_dir.c_str(), PRIMARY_PROFILE_NAME.c_str()); + std::string profile_dir = create_primary_reference_profile_package_dir_path(package_name); + return StringPrintf("%s/%s", profile_dir.c_str(), location.c_str()); } } std::string create_snapshot_profile_path(const std::string& package, - const std::string& code_path ATTRIBUTE_UNUSED) { - // TODD(calin): code_path is ignored for now. It will be used when each split gets its own - // profile file. - std::string ref_profile = create_reference_profile_path(package, /*is_secondary_dex*/ false); + const std::string& profile_name) { + std::string ref_profile = create_reference_profile_path(package, profile_name, + /*is_secondary_dex*/ false); return ref_profile + SNAPSHOT_PROFILE_EXT; } @@ -984,5 +988,75 @@ int prepare_app_cache_dir(const std::string& parent, const char* name, mode_t ta return 0; } +// Collect all non empty profiles from the given directory and puts then into profile_paths. +// The profiles are identified based on PROFILE_EXT extension. +// If a subdirectory or profile file cannot be opened the method logs a warning and moves on. +// It returns true if there were no errors at all, and false otherwise. +static bool collect_profiles(DIR* d, + const std::string& current_path, + std::vector<std::string>* profiles_paths) { + int32_t dir_fd = dirfd(d); + if (dir_fd < 0) { + return false; + } + + bool result = true; + struct dirent* dir_entry; + while ((dir_entry = readdir(d))) { + std::string name = dir_entry->d_name; + std::string local_path = current_path + "/" + name; + + if (dir_entry->d_type == DT_REG) { + // Check if this is a non empty profile file. + if (EndsWith(name, PROFILE_EXT)) { + struct stat st; + if (stat(local_path.c_str(), &st) != 0) { + PLOG(WARNING) << "Cannot stat local path " << local_path; + result = false; + continue; + } else if (st.st_size > 0) { + profiles_paths->push_back(local_path); + } + } + } else if (dir_entry->d_type == DT_DIR) { + // always skip "." and ".." + if (name == "." || name == "..") { + continue; + } + + unique_fd subdir_fd(openat(dir_fd, name.c_str(), + O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC)); + if (subdir_fd < 0) { + PLOG(WARNING) << "Could not open dir path " << local_path; + result = false; + continue; + } + + DIR* subdir = fdopendir(subdir_fd); + if (subdir == NULL) { + PLOG(WARNING) << "Could not open dir path " << local_path; + result = false; + continue; + } + bool new_result = collect_profiles(subdir, local_path, profiles_paths); + result = result && new_result; + if (closedir(subdir) != 0) { + PLOG(WARNING) << "Could not close dir path " << local_path; + } + } + } + + return result; +} + +bool collect_profiles(std::vector<std::string>* profiles_paths) { + DIR* d = opendir(android_profiles_dir.c_str()); + if (d == NULL) { + return false; + } else { + return collect_profiles(d, android_profiles_dir, profiles_paths); + } +} + } // namespace installd } // namespace android diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h index 53910615d9..b74073cb29 100644 --- a/cmds/installd/utils.h +++ b/cmds/installd/utils.h @@ -80,10 +80,17 @@ std::string create_primary_ref_profile_dir_path(); std::string create_primary_reference_profile_package_dir_path(const std::string& package_name); std::string create_current_profile_path( - userid_t user, const std::string& package_name, bool is_secondary_dex); + userid_t user, + const std::string& package_name, + const std::string& location, + bool is_secondary_dex); std::string create_reference_profile_path( - const std::string& package_name, bool is_secondary_dex); -std::string create_snapshot_profile_path(const std::string& package, const std::string& code_path); + const std::string& package_name, + const std::string& location, + bool is_secondary_dex); +std::string create_snapshot_profile_path( + const std::string& package, + const std::string& profile_name); std::vector<userid_t> get_known_users(const char* volume_uuid); @@ -129,6 +136,12 @@ int wait_child(pid_t pid); int prepare_app_cache_dir(const std::string& parent, const char* name, mode_t target_mode, uid_t uid, gid_t gid); +// Collect all non empty profiles from the global profile directory and +// put then into profile_paths. The profiles are identified based on PROFILE_EXT extension. +// If a subdirectory or profile file cannot be opened the method logs a warning and moves on. +// It returns true if there were no errors at all, and false otherwise. +bool collect_profiles(std::vector<std::string>* profiles_paths); + } // namespace installd } // namespace android |