Merge "Extract the otapreopt parameters in their own class and add tests"
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp
index 90c7da4..352cf0a 100644
--- a/cmds/installd/Android.bp
+++ b/cmds/installd/Android.bp
@@ -92,4 +92,28 @@
],
}
+//
+// 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 b3dc5ec..a4f95da 100644
--- a/cmds/installd/Android.mk
+++ b/cmds/installd/Android.mk
@@ -23,7 +23,7 @@
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 74dca72..52674b1 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 @@
}
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 @@
}
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 @@
// 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 @@
// 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, '/');
@@ -1120,23 +565,23 @@
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() {
@@ -1167,12 +612,12 @@
// 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();
}
@@ -1297,13 +742,12 @@
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 0000000..0cfaab8
--- /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 0000000..20faeb5
--- /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 47346fb..7438d3d 100644
--- a/cmds/installd/tests/Android.bp
+++ b/cmds/installd/tests/Android.bp
@@ -78,3 +78,20 @@
"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 0000000..3bcc362
--- /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