diff options
author | 2015-11-19 14:12:14 -0800 | |
---|---|---|
committer | 2016-02-01 21:01:51 -0800 | |
commit | 73dae11162aa61396c06cbdb05b954764e944e02 (patch) | |
tree | 28a32e1e8f30c325bb8bee6e8ec7a32e0d85b736 | |
parent | 8267a0b4d044c345e5bb24e19ea266d52381886c (diff) |
Installd: Introduce otapreopt
Add a tool for dexopt during OTA. The tool will use /data/ota as
an ANDROID_ROOT wrt/ the dalvik-cache, compiling a new boot image
and system apps into this directory. Other apps will be compiled
to odex files suffixed with ".b".
Bug: 25612095
Change-Id: I6ac382973f13850f6b37402a1b2330c0014b47d9
-rw-r--r-- | cmds/installd/Android.mk | 37 | ||||
-rw-r--r-- | cmds/installd/installd.cpp | 33 | ||||
-rw-r--r-- | cmds/installd/installd_constants.h | 5 | ||||
-rw-r--r-- | cmds/installd/otapreopt.cpp | 641 | ||||
-rw-r--r-- | cmds/installd/string_helpers.h | 67 | ||||
-rw-r--r-- | cmds/installd/system_properties.h | 89 |
6 files changed, 869 insertions, 3 deletions
diff --git a/cmds/installd/Android.mk b/cmds/installd/Android.mk index 209632e53e..65bcf39c8c 100644 --- a/cmds/installd/Android.mk +++ b/cmds/installd/Android.mk @@ -43,6 +43,43 @@ LOCAL_INIT_RC := installd.rc LOCAL_CLANG := true include $(BUILD_EXECUTABLE) +# +# OTA Executable +# + +include $(CLEAR_VARS) +LOCAL_MODULE := otapreopt +LOCAL_MODULE_TAGS := optional +LOCAL_CFLAGS := $(common_cflags) + +# Base & ASLR boundaries for boot image creation. +ifndef LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA + LOCAL_LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA := -0x1000000 +else + LOCAL_LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA := $(LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA) +endif +ifndef LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA + LOCAL_LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA := 0x1000000 +else + LOCAL_LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA := $(LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA) +endif +LOCAL_CFLAGS += -DART_BASE_ADDRESS=$(LIBART_IMG_HOST_BASE_ADDRESS) +LOCAL_CFLAGS += -DART_BASE_ADDRESS_MIN_DELTA=$(LOCAL_LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA) +LOCAL_CFLAGS += -DART_BASE_ADDRESS_MAX_DELTA=$(LOCAL_LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA) + +LOCAL_SRC_FILES := otapreopt.cpp $(common_src_files) +LOCAL_SHARED_LIBRARIES := \ + libbase \ + libcutils \ + liblog \ + liblogwrap \ + libselinux \ + +LOCAL_STATIC_LIBRARIES := libdiskusage +LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk +LOCAL_CLANG := true +include $(BUILD_EXECUTABLE) + # Tests. include $(LOCAL_PATH)/tests/Android.mk
\ No newline at end of file diff --git a/cmds/installd/installd.cpp b/cmds/installd/installd.cpp index 02e96015cc..c0ae5b7905 100644 --- a/cmds/installd/installd.cpp +++ b/cmds/installd/installd.cpp @@ -211,12 +211,41 @@ static int do_destroy_app_data(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSE return destroy_app_data(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3])); } -static int do_dexopt(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) +static int do_ota_dexopt(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) { + // Time to fork and run otapreopt. + pid_t pid = fork(); + if (pid == 0) { + const char* argv[1 + 9 + 1]; + argv[0] = "/system/bin/otapreopt"; + for (size_t i = 1; i <= 9; ++i) { + argv[i] = arg[i - 1]; + } + argv[10] = nullptr; + + execv(argv[0], (char * const *)argv); + ALOGE("execv(OTAPREOPT) failed: %s\n", strerror(errno)); + exit(99); + } else { + int res = wait_child(pid); + if (res == 0) { + ALOGV("DexInv: --- END OTAPREOPT (success) ---\n"); + } else { + ALOGE("DexInv: --- END OTAPREOPT --- status=0x%04x, process failed\n", res); + } + return res; + } +} + +static int do_dexopt(char **arg, char reply[REPLY_MAX]) { + int dexopt_flags = atoi(arg[6]); + if ((dexopt_flags & DEXOPT_OTA) != 0) { + return do_ota_dexopt(arg, reply); + } /* apk_path, uid, pkgname, instruction_set, dexopt_needed, oat_dir, dexopt_flags, volume_uuid, use_profiles */ return dexopt(arg[0], atoi(arg[1]), arg[2], arg[3], atoi(arg[4]), - arg[5], atoi(arg[6]), parse_null(arg[7]), (atoi(arg[8]) == 0 ? false : true)); + arg[5], dexopt_flags, parse_null(arg[7]), (atoi(arg[8]) == 0 ? false : true)); } static int do_mark_boot_complete(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) diff --git a/cmds/installd/installd_constants.h b/cmds/installd/installd_constants.h index 18f998d928..0d21519afc 100644 --- a/cmds/installd/installd_constants.h +++ b/cmds/installd/installd_constants.h @@ -48,6 +48,7 @@ constexpr const char* PRIVATE_APP_SUBDIR = "app-private/"; // sub-directory unde // This is used as a string literal, can't be constants. TODO: std::string... #define DALVIK_CACHE "dalvik-cache" constexpr const char* DALVIK_CACHE_POSTFIX = "/classes.dex"; +constexpr const char* DALVIK_CACHE_POSTFIX2 = "@classes.dex"; constexpr const char* IDMAP_PREFIX = "/data/resource-cache/"; constexpr const char* IDMAP_SUFFIX = "@idmap"; @@ -74,6 +75,7 @@ constexpr int DEXOPT_SAFEMODE = 1 << 2; constexpr int DEXOPT_DEBUGGABLE = 1 << 3; constexpr int DEXOPT_BOOTCOMPLETE = 1 << 4; constexpr int DEXOPT_EXTRACTONLY = 1 << 5; +constexpr int DEXOPT_OTA = 1 << 6; /* all known values for dexopt flags */ constexpr int DEXOPT_MASK = @@ -81,7 +83,8 @@ constexpr int DEXOPT_MASK = | DEXOPT_SAFEMODE | DEXOPT_DEBUGGABLE | DEXOPT_BOOTCOMPLETE - | DEXOPT_EXTRACTONLY; + | DEXOPT_EXTRACTONLY + | DEXOPT_OTA; #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a))) diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp new file mode 100644 index 0000000000..27f7939b57 --- /dev/null +++ b/cmds/installd/otapreopt.cpp @@ -0,0 +1,641 @@ +/* + ** 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 <algorithm> +#include <inttypes.h> +#include <random> +#include <selinux/android.h> +#include <selinux/avc.h> +#include <stdlib.h> +#include <string.h> +#include <sys/capability.h> +#include <sys/prctl.h> +#include <sys/stat.h> +#include <sys/wait.h> + +#include <android-base/logging.h> +#include <android-base/macros.h> +#include <android-base/stringprintf.h> +#include <cutils/fs.h> +#include <cutils/log.h> +#include <cutils/properties.h> +#include <private/android_filesystem_config.h> + +#include <commands.h> +#include <globals.h> +#include <installd_deps.h> // Need to fill in requirements of commands. +#include <string_helpers.h> +#include <system_properties.h> +#include <utils.h> + +#ifndef LOG_TAG +#define LOG_TAG "otapreopt" +#endif + +#define BUFFER_MAX 1024 /* input buffer for commands */ +#define TOKEN_MAX 16 /* max number of arguments in buffer */ +#define REPLY_MAX 256 /* largest reply allowed */ + +using android::base::StringPrintf; + +namespace android { +namespace installd { + +static constexpr const char* kBootClassPathPropertyName = "env.BOOTCLASSPATH"; +static constexpr const char* kAndroidRootPathPropertyName = "env.ANDROID_ROOT"; +static constexpr const char* kOTARootDirectory = "/system-b"; +static constexpr size_t kISAIndex = 3; + +template<typename T> +static constexpr T RoundDown(T x, typename std::decay<T>::type n) { + return DCHECK_CONSTEXPR(IsPowerOfTwo(n), , T(0))(x & -n); +} + +template<typename T> +static constexpr T RoundUp(T x, typename std::remove_reference<T>::type n) { + return RoundDown(x + n - 1, n); +} + +class OTAPreoptService { + public: + static constexpr const char* kOTADataDirectory = "/data/ota"; + + // Main driver. Performs the following steps. + // + // 1) Parse options (read system properties etc from B partition). + // + // 2) Read in package data. + // + // 3) Prepare environment variables. + // + // 4) Prepare(compile) boot image, if necessary. + // + // 5) Run update. + int Main(int argc, char** argv) { + if (!ReadSystemProperties()) { + LOG(ERROR)<< "Failed reading system properties."; + return 1; + } + + if (!ReadEnvironment()) { + LOG(ERROR) << "Failed reading environment properties."; + return 2; + } + + if (!ReadPackage(argc, argv)) { + LOG(ERROR) << "Failed reading command line file."; + return 3; + } + + PrepareEnvironment(); + + if (!PrepareBootImage()) { + LOG(ERROR) << "Failed preparing boot image."; + return 4; + } + + int dexopt_retcode = RunPreopt(); + + return dexopt_retcode; + } + + int GetProperty(const char* key, char* value, const char* default_value) { + const std::string* prop_value = system_properties_.GetProperty(key); + if (prop_value == nullptr) { + if (default_value == nullptr) { + return 0; + } + // Copy in the default value. + strncpy(value, default_value, kPropertyValueMax - 1); + value[kPropertyValueMax - 1] = 0; + return strlen(default_value);// TODO: Need to truncate? + } + size_t size = std::min(kPropertyValueMax - 1, prop_value->length()); + strncpy(value, prop_value->data(), size); + value[size] = 0; + return static_cast<int>(size); + } + +private: + bool ReadSystemProperties() { + // TODO(agampe): What to do about the things in default.prop? It's only heap sizes, so it's easy + // to emulate for now, but has issues (e.g., vendors modifying the boot classpath + // may require larger values here - revisit). That's why this goes first, so that + // if those dummy values are overridden in build.prop, that's what we'll get. + // + // Note: It seems we'll get access to the B root partition, so we should read the default.prop + // file. + // if (!system_properties_.Load(b_mount_path_ + "/default.prop") { + // return false; + // } + system_properties_.SetProperty("dalvik.vm.image-dex2oat-Xms", "64m"); + system_properties_.SetProperty("dalvik.vm.image-dex2oat-Xmx", "64m"); + system_properties_.SetProperty("dalvik.vm.dex2oat-Xms", "64m"); + system_properties_.SetProperty("dalvik.vm.dex2oat-Xmx", "512m"); + + // TODO(agampe): Do this properly/test. + return system_properties_.Load(b_mount_path_ + "/system/build.prop"); + } + + bool ReadEnvironment() { + // Read important environment variables. For simplicity, store them as + // system properties. + // TODO(agampe): We'll have to parse init.environ.rc for BOOTCLASSPATH. + // For now, just the A version. + const char* boot_classpath = getenv("BOOTCLASSPATH"); + if (boot_classpath == nullptr) { + return false; + } + system_properties_.SetProperty(kBootClassPathPropertyName, boot_classpath); + + const char* root_path = getenv("ANDROID_ROOT"); + if (root_path == nullptr) { + return false; + } + system_properties_.SetProperty(kAndroidRootPathPropertyName, b_mount_path_ + root_path); + + return true; + } + + bool ReadPackage(int argc ATTRIBUTE_UNUSED, char** argv) { + size_t index = 0; + while (index < ARRAY_SIZE(package_parameters_) && + argv[index + 1] != nullptr) { + package_parameters_[index] = argv[index + 1]; + index++; + } + if (index != ARRAY_SIZE(package_parameters_)) { + LOG(ERROR) << "Wrong number of parameters"; + return false; + } + + return true; + } + + void PrepareEnvironment() { + CHECK(system_properties_.GetProperty(kBootClassPathPropertyName) != nullptr); + const std::string& boot_cp = + *system_properties_.GetProperty(kBootClassPathPropertyName); + environ_.push_back(StringPrintf("BOOTCLASSPATH=%s", boot_cp.c_str())); + environ_.push_back(StringPrintf("ANDROID_DATA=%s", kOTADataDirectory)); + CHECK(system_properties_.GetProperty(kAndroidRootPathPropertyName) != nullptr); + const std::string& android_root = + *system_properties_.GetProperty(kAndroidRootPathPropertyName); + environ_.push_back(StringPrintf("ANDROID_ROOT=%s", android_root.c_str())); + + for (const std::string& e : environ_) { + putenv(const_cast<char*>(e.c_str())); + } + } + + // Ensure that we have the right boot image. The first time any app is + // compiled, we'll try to generate it. + bool PrepareBootImage() { + if (package_parameters_[kISAIndex] == nullptr) { + LOG(ERROR) << "Instruction set missing."; + return false; + } + const char* isa = package_parameters_[kISAIndex]; + + // Check whether the file exists where expected. + std::string dalvik_cache = std::string(kOTADataDirectory) + "/" + DALVIK_CACHE; + std::string isa_path = dalvik_cache + "/" + isa; + std::string art_path = isa_path + "/system@framework@boot.art"; + std::string oat_path = isa_path + "/system@framework@boot.oat"; + if (access(art_path.c_str(), F_OK) == 0 && + access(oat_path.c_str(), F_OK) == 0) { + // Files exist, assume everything is alright. + return true; + } + + // Create the directories, if necessary. + if (access(dalvik_cache.c_str(), F_OK) != 0) { + if (mkdir(dalvik_cache.c_str(), 0711) != 0) { + PLOG(ERROR) << "Could not create dalvik-cache dir"; + return false; + } + } + if (access(isa_path.c_str(), F_OK) != 0) { + if (mkdir(isa_path.c_str(), 0711) != 0) { + PLOG(ERROR) << "Could not create dalvik-cache isa dir"; + return false; + } + } + + // Prepare and run dex2oat. + // TODO: Delete files, just for a blank slate. + const std::string& boot_cp = *system_properties_.GetProperty(kBootClassPathPropertyName); + + // This needs to be kept in sync with ART, see art/runtime/gc/space/image_space.cc. + std::vector<std::string> cmd; + cmd.push_back(b_mount_path_ + "/system/bin/dex2oat"); + cmd.push_back(StringPrintf("--image=%s", art_path.c_str())); + for (const std::string& boot_part : Split(boot_cp, ':')) { + cmd.push_back(StringPrintf("--dex-file=%s", boot_part.c_str())); + } + cmd.push_back(StringPrintf("--oat-file=%s", oat_path.c_str())); + + int32_t base_offset = ChooseRelocationOffsetDelta(ART_BASE_ADDRESS_MIN_DELTA, + ART_BASE_ADDRESS_MAX_DELTA); + cmd.push_back(StringPrintf("--base=0x%x", ART_BASE_ADDRESS + base_offset)); + + cmd.push_back(StringPrintf("--instruction-set=%s", isa)); + + // These things are pushed by AndroidRuntime, see frameworks/base/core/jni/AndroidRuntime.cpp. + AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-Xms", + "-Xms", + true, + cmd); + AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-Xmx", + "-Xmx", + true, + cmd); + AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-filter", + "--compiler-filter=", + false, + cmd); + cmd.push_back(StringPrintf("--image-classes=%s/system/etc/preloaded-classes", + b_mount_path_.c_str())); + // TODO: Compiled-classes. + const std::string* extra_opts = + system_properties_.GetProperty("dalvik.vm.image-dex2oat-flags"); + if (extra_opts != nullptr) { + std::vector<std::string> extra_vals = Split(*extra_opts, ' '); + cmd.insert(cmd.end(), extra_vals.begin(), extra_vals.end()); + } + // TODO: Should we lower this? It's usually set close to max, because + // normally there's not much else going on at boot. + AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-threads", + "-j", + false, + cmd); + AddCompilerOptionFromSystemProperty( + StringPrintf("dalvik.vm.isa.%s.variant", isa).c_str(), + "--instruction-set-variant=", + false, + cmd); + AddCompilerOptionFromSystemProperty( + StringPrintf("dalvik.vm.isa.%s.features", isa).c_str(), + "--instruction-set-features=", + false, + cmd); + + std::string error_msg; + bool result = Exec(cmd, &error_msg); + if (!result) { + LOG(ERROR) << "Could not generate boot image: " << error_msg; + } + return result; + } + + static const char* ParseNull(const char* arg) { + return (strcmp(arg, "!") == 0) ? nullptr : arg; + } + + int RunPreopt() { + /* apk_path, uid, pkgname, instruction_set, dexopt_needed, oat_dir, dexopt_flags, + volume_uuid, use_profiles */ + int ret = dexopt(package_parameters_[0], + atoi(package_parameters_[1]), + package_parameters_[2], + package_parameters_[3], + atoi(package_parameters_[4]), + package_parameters_[5], + atoi(package_parameters_[6]), + ParseNull(package_parameters_[7]), + (atoi(package_parameters_[8]) == 0 ? false : true)); + return ret; + } + + //////////////////////////////////// + // Helpers, mostly taken from ART // + //////////////////////////////////// + + // Wrapper on fork/execv to run a command in a subprocess. + bool Exec(const std::vector<std::string>& arg_vector, std::string* error_msg) { + const std::string command_line(Join(arg_vector, ' ')); + + CHECK_GE(arg_vector.size(), 1U) << command_line; + + // Convert the args to char pointers. + const char* program = arg_vector[0].c_str(); + std::vector<char*> args; + for (size_t i = 0; i < arg_vector.size(); ++i) { + const std::string& arg = arg_vector[i]; + char* arg_str = const_cast<char*>(arg.c_str()); + CHECK(arg_str != nullptr) << i; + args.push_back(arg_str); + } + args.push_back(nullptr); + + // Fork and exec. + pid_t pid = fork(); + if (pid == 0) { + // No allocation allowed between fork and exec. + + // Change process groups, so we don't get reaped by ProcessManager. + setpgid(0, 0); + + execv(program, &args[0]); + + PLOG(ERROR) << "Failed to execv(" << command_line << ")"; + // _exit to avoid atexit handlers in child. + _exit(1); + } else { + if (pid == -1) { + *error_msg = StringPrintf("Failed to execv(%s) because fork failed: %s", + command_line.c_str(), strerror(errno)); + return false; + } + + // wait for subprocess to finish + int status; + pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0)); + if (got_pid != pid) { + *error_msg = StringPrintf("Failed after fork for execv(%s) because waitpid failed: " + "wanted %d, got %d: %s", + command_line.c_str(), pid, got_pid, strerror(errno)); + return false; + } + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + *error_msg = StringPrintf("Failed execv(%s) because non-0 exit status", + command_line.c_str()); + return false; + } + } + return true; + } + + // Choose a random relocation offset. Taken from art/runtime/gc/image_space.cc. + static int32_t ChooseRelocationOffsetDelta(int32_t min_delta, int32_t max_delta) { + constexpr size_t kPageSize = PAGE_SIZE; + CHECK_EQ(min_delta % kPageSize, 0u); + CHECK_EQ(max_delta % kPageSize, 0u); + CHECK_LT(min_delta, max_delta); + + std::default_random_engine generator; + generator.seed(GetSeed()); + std::uniform_int_distribution<int32_t> distribution(min_delta, max_delta); + int32_t r = distribution(generator); + if (r % 2 == 0) { + r = RoundUp(r, kPageSize); + } else { + r = RoundDown(r, kPageSize); + } + CHECK_LE(min_delta, r); + CHECK_GE(max_delta, r); + CHECK_EQ(r % kPageSize, 0u); + return r; + } + + static uint64_t GetSeed() { +#ifdef __BIONIC__ + // Bionic exposes arc4random, use it. + uint64_t random_data; + arc4random_buf(&random_data, sizeof(random_data)); + return random_data; +#else +#error "This is only supposed to run with bionic. Otherwise, implement..." +#endif + } + + void AddCompilerOptionFromSystemProperty(const char* system_property, + const char* prefix, + bool runtime, + std::vector<std::string>& out) { + const std::string* value = + system_properties_.GetProperty(system_property); + if (value != nullptr) { + if (runtime) { + out.push_back("--runtime-arg"); + } + if (prefix != nullptr) { + out.push_back(StringPrintf("%s%s", prefix, value->c_str())); + } else { + out.push_back(*value); + } + } + } + + // The path where the B partitions are mounted. + // TODO(agampe): If we're running this *inside* the change-root, we wouldn't need this. + std::string b_mount_path_; + + // Stores the system properties read out of the B partition. We need to use these properties + // to compile, instead of the A properties we could get from init/get_property. + SystemProperties system_properties_; + + const char* package_parameters_[9]; + + // Store environment values we need to set. + std::vector<std::string> environ_; +}; + +OTAPreoptService gOps; + +//////////////////////// +// Plug-in functions. // +//////////////////////// + +int get_property(const char *key, char *value, const char *default_value) { + // TODO: Replace with system-properties map. + return gOps.GetProperty(key, value, default_value); +} + +// Compute the output path of +bool calculate_oat_file_path(char path[PKG_PATH_MAX], const char *oat_dir, + const char *apk_path, + const char *instruction_set) { + // TODO: Insert B directory. + char *file_name_start; + char *file_name_end; + + file_name_start = strrchr(apk_path, '/'); + if (file_name_start == nullptr) { + ALOGE("apk_path '%s' has no '/'s in it\n", apk_path); + return false; + } + file_name_end = strrchr(file_name_start, '.'); + if (file_name_end == nullptr) { + ALOGE("apk_path '%s' has no extension\n", apk_path); + return false; + } + + // Calculate file_name + file_name_start++; // Move past '/', is valid as file_name_end is valid. + size_t file_name_len = file_name_end - file_name_start; + std::string file_name(file_name_start, file_name_len); + + // <apk_parent_dir>/oat/<isa>/<file_name>.odex.b + snprintf(path, PKG_PATH_MAX, "%s/%s/%s.odex.b", oat_dir, instruction_set, + file_name.c_str()); + return true; +} + +/* + * Computes the odex file for the given apk_path and instruction_set. + * /system/framework/whatever.jar -> /system/framework/oat/<isa>/whatever.odex + * + * Returns false if it failed to determine the odex file path. + */ +bool calculate_odex_file_path(char path[PKG_PATH_MAX], const char *apk_path, + const char *instruction_set) { + if (StringPrintf("%soat/%s/odex.b", apk_path, instruction_set).length() + 1 > PKG_PATH_MAX) { + ALOGE("apk_path '%s' may be too long to form odex file path.\n", apk_path); + return false; + } + + const char *path_end = strrchr(apk_path, '/'); + if (path_end == nullptr) { + ALOGE("apk_path '%s' has no '/'s in it?!\n", apk_path); + return false; + } + std::string path_component(apk_path, path_end - apk_path); + + const char *name_begin = path_end + 1; + const char *extension_start = strrchr(name_begin, '.'); + if (extension_start == nullptr) { + ALOGE("apk_path '%s' has no extension.\n", apk_path); + return false; + } + std::string name_component(name_begin, extension_start - name_begin); + + std::string new_path = StringPrintf("%s/oat/%s/%s.odex.b", + path_component.c_str(), + instruction_set, + name_component.c_str()); + CHECK_LT(new_path.length(), PKG_PATH_MAX); + strcpy(path, new_path.c_str()); + return true; +} + +bool create_cache_path(char path[PKG_PATH_MAX], + const char *src, + const char *instruction_set) { + size_t srclen = strlen(src); + + /* demand that we are an absolute path */ + if ((src == 0) || (src[0] != '/') || strstr(src,"..")) { + return false; + } + + if (srclen > PKG_PATH_MAX) { // XXX: PKG_NAME_MAX? + return false; + } + + std::string from_src = std::string(src + 1); + std::replace(from_src.begin(), from_src.end(), '/', '@'); + + std::string assembled_path = StringPrintf("%s/%s/%s/%s%s", + OTAPreoptService::kOTADataDirectory, + DALVIK_CACHE, + instruction_set, + from_src.c_str(), + DALVIK_CACHE_POSTFIX2); + + if (assembled_path.length() + 1 > PKG_PATH_MAX) { + return false; + } + strcpy(path, assembled_path.c_str()); + + return true; +} + +bool initialize_globals() { + const char* data_path = getenv("ANDROID_DATA"); + if (data_path == nullptr) { + ALOGE("Could not find ANDROID_DATA"); + return false; + } + return init_globals_from_data_and_root(data_path, kOTARootDirectory); +} + +static bool initialize_directories() { + // This is different from the normal installd. We only do the base + // directory, the rest will be created on demand when each app is compiled. + mode_t old_umask = umask(0); + LOG(INFO) << "Old umask: " << old_umask; + if (access(OTAPreoptService::kOTADataDirectory, R_OK) < 0) { + ALOGE("Could not access %s\n", OTAPreoptService::kOTADataDirectory); + return false; + } + return true; +} + +static int log_callback(int type, const char *fmt, ...) { + va_list ap; + int priority; + + switch (type) { + case SELINUX_WARNING: + priority = ANDROID_LOG_WARN; + break; + case SELINUX_INFO: + priority = ANDROID_LOG_INFO; + break; + default: + priority = ANDROID_LOG_ERROR; + break; + } + va_start(ap, fmt); + LOG_PRI_VA(priority, "SELinux", fmt, ap); + va_end(ap); + return 0; +} + +static int otapreopt_main(const int argc, char *argv[]) { + int selinux_enabled = (is_selinux_enabled() > 0); + + setenv("ANDROID_LOG_TAGS", "*:v", 1); + android::base::InitLogging(argv); + + ALOGI("otapreopt firing up\n"); + + if (argc < 2) { + ALOGE("Expecting parameters"); + exit(1); + } + + union selinux_callback cb; + cb.func_log = log_callback; + selinux_set_callback(SELINUX_CB_LOG, cb); + + if (!initialize_globals()) { + ALOGE("Could not initialize globals; exiting.\n"); + exit(1); + } + + if (!initialize_directories()) { + ALOGE("Could not create directories; exiting.\n"); + exit(1); + } + + if (selinux_enabled && selinux_status_open(true) < 0) { + ALOGE("Could not open selinux status; exiting.\n"); + exit(1); + } + + int ret = android::installd::gOps.Main(argc, argv); + + return ret; +} + +} // namespace installd +} // namespace android + +int main(const int argc, char *argv[]) { + return android::installd::otapreopt_main(argc, argv); +} diff --git a/cmds/installd/string_helpers.h b/cmds/installd/string_helpers.h new file mode 100644 index 0000000000..e8fcdef9cf --- /dev/null +++ b/cmds/installd/string_helpers.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2015 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 ART_OTAPREOPT_STRING_HELPERS_H_ +#define ART_OTAPREOPT_STRING_HELPERS_H_ + +#include <sstream> +#include <string> + +#include <android-base/macros.h> + +namespace android { +namespace installd { + +static inline bool StringStartsWith(const std::string& target, + const char* prefix) { + return target.compare(0, strlen(prefix), prefix) == 0; +} + +// Split the input according to the separator character. Doesn't honor quotation. +static inline std::vector<std::string> Split(const std::string& in, const char separator) { + if (in.empty()) { + return std::vector<std::string>(); + } + + std::vector<std::string> ret; + std::stringstream strstr(in); + std::string token; + + while (std::getline(strstr, token, separator)) { + ret.push_back(token); + } + + return ret; +} + +template <typename StringT> +static inline std::string Join(const std::vector<StringT>& strings, char separator) { + if (strings.empty()) { + return ""; + } + + std::string result(strings[0]); + for (size_t i = 1; i < strings.size(); ++i) { + result += separator; + result += strings[i]; + } + return result; +} + +} // namespace installd +} // namespace android + +#endif // ART_OTAPREOPT_STRING_HELPERS_H_ diff --git a/cmds/installd/system_properties.h b/cmds/installd/system_properties.h new file mode 100644 index 0000000000..1b5fb3aac2 --- /dev/null +++ b/cmds/installd/system_properties.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2015 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_SYSTEM_PROPERTIES_H_ +#define OTAPREOPT_SYSTEM_PROPERTIES_H_ + +#include <fstream> +#include <string> +#include <unordered_map> + +namespace android { +namespace installd { + +// Helper class to read system properties into and manage as a string->string map. +class SystemProperties { + public: + bool Load(const std::string& strFile) { + std::ifstream input_stream(strFile); + + if (!input_stream.is_open()) { + return false; + } + + while (!input_stream.eof()) { + // Read the next line. + std::string line; + getline(input_stream, line); + + // Is the line empty? Simplifies the next check. + if (line.empty()) { + continue; + } + + // Is this a comment (starts with pound)? + if (line[0] == '#') { + continue; + } + + size_t equals_pos = line.find('='); + if (equals_pos == std::string::npos || equals_pos == 0) { + // Did not find equals sign, or it's the first character - isn't a valid line. + continue; + } + + std::string key = line.substr(0, equals_pos); + std::string value = line.substr(equals_pos + 1, + line.length() - equals_pos + 1); + + properties_.insert(std::make_pair(key, value)); + } + + return true; + } + + // Look up the key in the map. Returns null if the key isn't mapped. + const std::string* GetProperty(const std::string& key) const { + auto it = properties_.find(key); + if (it != properties_.end()) { + return &it->second; + } + return nullptr; + } + + void SetProperty(const std::string& key, const std::string& value) { + properties_.insert(std::make_pair(key, value)); + } + + private: + // The actual map. + std::unordered_map<std::string, std::string> properties_; +}; + +} // namespace installd +} // namespace android + +#endif // OTAPREOPT_SYSTEM_PROPERTIES_H_ |