diff options
| -rw-r--r-- | cmds/installd/Android.bp | 24 | ||||
| -rw-r--r-- | cmds/installd/Android.mk | 2 | ||||
| -rw-r--r-- | cmds/installd/otapreopt.cpp | 608 | ||||
| -rw-r--r-- | cmds/installd/otapreopt_parameters.cpp | 601 | ||||
| -rw-r--r-- | cmds/installd/otapreopt_parameters.h | 61 | ||||
| -rw-r--r-- | cmds/installd/tests/Android.bp | 17 | ||||
| -rw-r--r-- | cmds/installd/tests/installd_otapreopt_test.cpp | 197 |
7 files changed, 927 insertions, 583 deletions
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp index 90c7da47b1..352cf0ad74 100644 --- a/cmds/installd/Android.bp +++ b/cmds/installd/Android.bp @@ -92,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 b3dc5ec997..a4f95da939 100644 --- a/cmds/installd/Android.mk +++ b/cmds/installd/Android.mk @@ -23,7 +23,7 @@ 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 \ diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp index 223b17a422..c163f2c402 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,32 +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; - const char* profile_name; - }; - bool ReadSystemProperties() { static constexpr const char* kPropertyFiles[] = { "/default.prop", "/system/build.prop" @@ -307,546 +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); - case 5: - return ReadArgumentsV5(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; - - // Set the profile name to the primary apk profile. - package_parameters_.profile_name = "primary.prof"; - - 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; - - // Set the profile name to the primary apk profile. - package_parameters_.profile_name = "primary.prof"; - - 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; - } - } - - // Set the profile name to the primary apk profile. - package_parameters_.profile_name = "primary.prof"; - - if (param_index != 13) { - LOG(ERROR) << "Not enough parameters"; - return false; - } - - return true; - } - - // TODO: this pattern does not scale and result in a lot of code duplication. - // Either find a better pattern or refactor the code to eliminate the duplication. - bool ReadArgumentsV5(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; - - case 13: - package_parameters_.profile_name = ParseNull(param); - break; - - default: - LOG(ERROR) << "Too many arguments, got " << param; - return false; - } - } - - if (param_index != 14) { - 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; - - // Set the profile name to the primary apk profile. - package_parameters_.profile_name = "primary.prof"; - - return true; + return parameters_.ReadArguments(argc, const_cast<const char**>(argv)); } void PrepareEnvironment() { @@ -862,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; @@ -1090,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, '/'); @@ -1119,23 +564,23 @@ 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, - package_parameters_.profile_name); + 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); } int RunPreopt() { @@ -1166,12 +611,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(); } @@ -1296,13 +741,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..0cfaab8a26 --- /dev/null +++ b/cmds/installd/otapreopt_parameters.cpp @@ -0,0 +1,601 @@ +/* + ** 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 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); + case 5: + return ReadArgumentsV5(argc, argv); + + default: + LOG(ERROR) << "Unsupported version " << version; + return false; + } +} + +bool OTAPreoptParameters::ReadArgumentsV2(int argc ATTRIBUTE_UNUSED, const 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: + 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; + + 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. + 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"; + + if (param_index != 11) { + LOG(ERROR) << "Not enough parameters"; + return false; + } + + return true; +} + +bool OTAPreoptParameters::ReadArgumentsV3(int argc ATTRIBUTE_UNUSED, const 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: + 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; + + 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. + target_sdk_version = 0; + + // Set the profile name to the primary apk profile. + profile_name = "primary.prof"; + + if (param_index != 12) { + LOG(ERROR) << "Not enough parameters"; + return false; + } + + return true; +} + +bool OTAPreoptParameters::ReadArgumentsV4(int argc ATTRIBUTE_UNUSED, const 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: + 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; + + default: + LOG(ERROR) << "Too many arguments, got " << param; + return false; + } + } + + // Set the profile name to the primary apk profile. + profile_name = "primary.prof"; + + if (param_index != 13) { + LOG(ERROR) << "Not enough parameters"; + return false; + } + + return true; +} + +// TODO: this pattern does not scale and result in a lot of code duplication. +// Either find a better pattern or refactor the code to eliminate the duplication. +bool OTAPreoptParameters::ReadArgumentsV5(int argc ATTRIBUTE_UNUSED, const 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: + 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; + + default: + LOG(ERROR) << "Too many arguments, got " << param; + return false; + } + } + + if (param_index != 14) { + 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 OTAPreoptParameters::ReadArgumentsV1(int argc ATTRIBUTE_UNUSED, 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\""; + 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"; + + 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..20faeb5c4e --- /dev/null +++ b/cmds/installd/otapreopt_parameters.h @@ -0,0 +1,61 @@ +/* + ** 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(int argc, const char** argv); + bool ReadArgumentsV2(int argc, const char** argv, bool versioned); + bool ReadArgumentsV3(int argc, const char** argv); + bool ReadArgumentsV4(int argc, const char** argv); + bool ReadArgumentsV5(int argc, const char** argv); + + 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; + + 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 47346fb7e7..7438d3d9b4 100644 --- a/cmds/installd/tests/Android.bp +++ b/cmds/installd/tests/Android.bp @@ -78,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_otapreopt_test.cpp b/cmds/installd/tests/installd_otapreopt_test.cpp new file mode 100644 index 0000000000..3bcc362436 --- /dev/null +++ b/cmds/installd/tests/installd_otapreopt_test.cpp @@ -0,0 +1,197 @@ +/** + * 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", "*:v", 1); + android::base::InitLogging(nullptr); + } + + 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_STREQ(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"); + } + } + + 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"; + } + 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 + } + 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, 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 |