Extract the otapreopt parameters in their own class and add tests
This is in preparation to get rid of the annoying
ReadArgumentV1..5 pattern and to be able to write
unit tests.
Test: installd_dexopt_test
Bug: 72666394
Change-Id: I0ad438ee69aa82d9ff2ad2f94d465dcc78ea8c28
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