/* ** 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 #include #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::min() || std::numeric_limits::max() < result) { return false; } *out = static_cast(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; } void OTAPreoptParameters::SetDefaultsForPostV1Arguments() { // 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; // The compilation reason is ab-ota (match the system property pm.dexopt.ab-ota) compilation_reason = "ab-ota"; // Flag is enabled by default for A/B otas. dexopt_flags = DEXOPT_GENERATE_COMPACT_DEX; } 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; } SetDefaultsForPostV1Arguments(); 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; static_assert(DEXOPT_GENERATE_COMPACT_DEX > OLD_DEXOPT_OTA, "must not overlap"); 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; } 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; case 7: // Version 8 adds a new dexopt flag: DEXOPT_GENERATE_COMPACT_DEX case 8: num_args_expected = 16; break; // Version 9 adds a new dexopt flag: DEXOPT_GENERATE_APP_IMAGE case 9: num_args_expected = 16; break; // Version 10 is a compatibility bump. case 10: num_args_expected = 16; 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. SetDefaultsForPostV1Arguments(); 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); // Add CompactDex generation flag for versions less than 8 since it wasn't passed // from the package manager. Only conditionally set the flag here so that it can // be fully controlled by the package manager. dexopt_flags |= (version < 8) ? DEXOPT_GENERATE_COMPACT_DEX : 0u; 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; case 15: compilation_reason = 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; } } if (version < 10) { // Do not accept '&' as shared libraries from versions prior to 10. These may lead // to runtime crashes. The server side of version 10+ should send the correct // context in almost all cases (e.g., only for actual shared packages). if (shared_libraries != nullptr && std::string("&") == shared_libraries) { return false; } } return true; } } // namespace installd } // namespace android