|  | /* | 
|  | ** 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; | 
|  | } | 
|  |  | 
|  | 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; | 
|  | 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; | 
|  | } | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | }  // namespace installd | 
|  | }  // namespace android |