diff options
-rw-r--r-- | cmds/installd/commands.cpp | 51 | ||||
-rw-r--r-- | cmds/installd/commands.h | 4 | ||||
-rw-r--r-- | cmds/installd/globals.cpp | 20 | ||||
-rw-r--r-- | cmds/installd/globals.h | 5 | ||||
-rw-r--r-- | cmds/installd/installd.cpp | 2 | ||||
-rw-r--r-- | cmds/installd/installd_constants.h | 39 | ||||
-rw-r--r-- | cmds/installd/installd_deps.h | 3 | ||||
-rw-r--r-- | cmds/installd/otapreopt.cpp | 378 | ||||
-rw-r--r-- | cmds/installd/otapreopt_chroot.cpp | 74 | ||||
-rw-r--r-- | cmds/installd/otapreopt_script.sh | 18 |
10 files changed, 431 insertions, 163 deletions
diff --git a/cmds/installd/commands.cpp b/cmds/installd/commands.cpp index 2392ef453b..64afd96778 100644 --- a/cmds/installd/commands.cpp +++ b/cmds/installd/commands.cpp @@ -18,6 +18,7 @@ #include <errno.h> #include <inttypes.h> +#include <regex> #include <stdlib.h> #include <sys/capability.h> #include <sys/file.h> @@ -58,6 +59,26 @@ namespace installd { static constexpr const char* kCpPath = "/system/bin/cp"; static constexpr const char* kXattrDefault = "user.default"; +static constexpr const char* PKG_LIB_POSTFIX = "/lib"; +static constexpr const char* CACHE_DIR_POSTFIX = "/cache"; +static constexpr const char* CODE_CACHE_DIR_POSTFIX = "/code_cache"; + +static constexpr const char* IDMAP_PREFIX = "/data/resource-cache/"; +static constexpr const char* IDMAP_SUFFIX = "@idmap"; + +// NOTE: keep in sync with StorageManager +static constexpr int FLAG_STORAGE_DE = 1 << 0; +static constexpr int FLAG_STORAGE_CE = 1 << 1; + +// NOTE: keep in sync with Installer +static constexpr int FLAG_CLEAR_CACHE_ONLY = 1 << 8; +static constexpr int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 9; + +/* dexopt needed flags matching those in dalvik.system.DexFile */ +static constexpr int DEXOPT_DEX2OAT_NEEDED = 1; +static constexpr int DEXOPT_PATCHOAT_NEEDED = 2; +static constexpr int DEXOPT_SELF_PATCHOAT_NEEDED = 3; + #define MIN_RESTRICTED_HOME_SDK_VERSION 24 // > M typedef int fd_t; @@ -1427,7 +1448,7 @@ static const char* parse_null(const char* arg) { } } -int dexopt(const char* params[DEXOPT_PARAM_COUNT]) { +int dexopt(const char* const params[DEXOPT_PARAM_COUNT]) { return dexopt(params[0], // apk_path atoi(params[1]), // uid params[2], // pkgname @@ -2093,6 +2114,26 @@ int move_ab(const char* apk_path, const char* instruction_set, const char* oat_d LOG(ERROR) << "Cannot move_ab with null input"; return -1; } + + // Get the current slot suffix. No suffix, no A/B. + std::string slot_suffix; + { + char buf[kPropertyValueMax]; + if (get_property("ro.boot.slot_suffix", buf, nullptr) <= 0) { + return -1; + } + slot_suffix = buf; + + // Validate. + std::regex slot_suffix_regex("[a-zA-Z0-9_]+"); + std::smatch slot_suffix_match; + if (!std::regex_match(slot_suffix, slot_suffix_match, slot_suffix_regex)) { + LOG(ERROR) << "Target slot suffix not legal: " << slot_suffix; + return -1; + } + } + + // Validate other inputs. if (validate_apk_path(apk_path) != 0) { LOG(ERROR) << "invalid apk_path " << apk_path; return -1; @@ -2108,9 +2149,11 @@ int move_ab(const char* apk_path, const char* instruction_set, const char* oat_d } const std::string a_image_path = create_image_filename(a_path); - // B path = A path + ".b" - const std::string b_path = StringPrintf("%s.b", a_path); - const std::string b_image_path = StringPrintf("%s.b", a_image_path.c_str()); + // B path = A path + slot suffix. + const std::string b_path = StringPrintf("%s.%s", a_path, slot_suffix.c_str()); + const std::string b_image_path = StringPrintf("%s.%s", + a_image_path.c_str(), + slot_suffix.c_str()); bool oat_success = move_ab_path(b_path, a_path); bool success; diff --git a/cmds/installd/commands.h b/cmds/installd/commands.h index c0c39c5e67..e990f1b695 100644 --- a/cmds/installd/commands.h +++ b/cmds/installd/commands.h @@ -28,6 +28,8 @@ namespace android { namespace installd { +static constexpr size_t DEXOPT_PARAM_COUNT = 10U; + int create_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags, appid_t appid, const char* seinfo, int target_sdk_version); int restorecon_app_data(const char* uuid, const char* pkgName, userid_t userid, int flags, @@ -69,7 +71,7 @@ int dexopt(const char *apk_path, static_assert(DEXOPT_PARAM_COUNT == 10U, "Unexpected dexopt param size"); // Helper for the above, converting arguments. -int dexopt(const char* params[DEXOPT_PARAM_COUNT]); +int dexopt(const char* const params[DEXOPT_PARAM_COUNT]); int mark_boot_complete(const char *instruction_set); int linklib(const char* uuid, const char* pkgname, const char* asecLibDir, int userId); diff --git a/cmds/installd/globals.cpp b/cmds/installd/globals.cpp index 6a67e29396..93e1ce54af 100644 --- a/cmds/installd/globals.cpp +++ b/cmds/installd/globals.cpp @@ -30,6 +30,22 @@ namespace android { namespace installd { +static constexpr const char* APP_SUBDIR = "app/"; // sub-directory under ANDROID_DATA + +static constexpr const char* PRIV_APP_SUBDIR = "priv-app/"; // sub-directory under ANDROID_DATA + +static constexpr const char* EPHEMERAL_APP_SUBDIR = "app-ephemeral/"; // sub-directory under + // ANDROID_DATA + +static constexpr const char* APP_LIB_SUBDIR = "app-lib/"; // sub-directory under ANDROID_DATA + +static constexpr const char* MEDIA_SUBDIR = "media/"; // sub-directory under ANDROID_DATA + +static constexpr const char* PROFILES_SUBDIR = "misc/profiles"; // sub-directory under ANDROID_DATA + +static constexpr const char* PRIVATE_APP_SUBDIR = "app-private/"; // sub-directory under + // ANDROID_DATA + /* Directory records that are used in execution of commands. */ dir_rec_t android_app_dir; dir_rec_t android_app_ephemeral_dir; @@ -77,7 +93,7 @@ bool init_globals_from_data_and_root(const char* data, const char* root) { // Get the android ephemeral app directory. if (copy_and_append(&android_app_ephemeral_dir, &android_data_dir, EPHEMERAL_APP_SUBDIR) < 0) { - return -1; + return false; } // Get the android app native library directory. @@ -86,7 +102,7 @@ bool init_globals_from_data_and_root(const char* data, const char* root) { } // Get the sd-card ASEC mount point. - if (get_path_from_env(&android_asec_dir, "ASEC_MOUNTPOINT") < 0) { + if (get_path_from_env(&android_asec_dir, ASEC_MOUNTPOINT_ENV_NAME) < 0) { return false; } diff --git a/cmds/installd/globals.h b/cmds/installd/globals.h index 3e523460ad..c90beec49c 100644 --- a/cmds/installd/globals.h +++ b/cmds/installd/globals.h @@ -23,6 +23,11 @@ namespace android { namespace installd { +/* constants */ + +// Name of the environment variable that contains the asec mountpoint. +static constexpr const char* ASEC_MOUNTPOINT_ENV_NAME = "ASEC_MOUNTPOINT"; + /* data structures */ struct dir_rec_t { diff --git a/cmds/installd/installd.cpp b/cmds/installd/installd.cpp index 68439b24db..2bc3dfa183 100644 --- a/cmds/installd/installd.cpp +++ b/cmds/installd/installd.cpp @@ -557,7 +557,7 @@ done: return 0; } -bool initialize_globals() { +static bool initialize_globals() { const char* data_path = getenv("ANDROID_DATA"); if (data_path == nullptr) { ALOGE("Could not find ANDROID_DATA"); diff --git a/cmds/installd/installd_constants.h b/cmds/installd/installd_constants.h index 823b8ee671..41732cce4f 100644 --- a/cmds/installd/installd_constants.h +++ b/cmds/installd/installd_constants.h @@ -21,58 +21,19 @@ namespace android { namespace installd { -constexpr size_t DEXOPT_PARAM_COUNT = 10U; - /* elements combined with a valid package name to form paths */ constexpr const char* PRIMARY_USER_PREFIX = "data/"; constexpr const char* SECONDARY_USER_PREFIX = "user/"; -constexpr const char* PKG_DIR_POSTFIX = ""; - -constexpr const char* PKG_LIB_POSTFIX = "/lib"; - -constexpr const char* CACHE_DIR_POSTFIX = "/cache"; -constexpr const char* CODE_CACHE_DIR_POSTFIX = "/code_cache"; - -constexpr const char* APP_SUBDIR = "app/"; // sub-directory under ANDROID_DATA -constexpr const char* PRIV_APP_SUBDIR = "priv-app/"; // sub-directory under ANDROID_DATA -constexpr const char* EPHEMERAL_APP_SUBDIR = "app-ephemeral/"; // sub-directory under ANDROID_DATA - -constexpr const char* APP_LIB_SUBDIR = "app-lib/"; // sub-directory under ANDROID_DATA - -constexpr const char* MEDIA_SUBDIR = "media/"; // sub-directory under ANDROID_DATA - -constexpr const char* PROFILES_SUBDIR = "misc/profiles"; // sub-directory under ANDROID_DATA - -/* other handy constants */ - -constexpr const char* PRIVATE_APP_SUBDIR = "app-private/"; // sub-directory under ANDROID_DATA - // 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"; - constexpr size_t PKG_NAME_MAX = 128u; /* largest allowed package name */ constexpr size_t PKG_PATH_MAX = 256u; /* max size of any path we use */ -// NOTE: keep in sync with StorageManager -constexpr int FLAG_STORAGE_DE = 1 << 0; -constexpr int FLAG_STORAGE_CE = 1 << 1; - -// NOTE: keep in sync with Installer -constexpr int FLAG_CLEAR_CACHE_ONLY = 1 << 8; -constexpr int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 9; - -/* dexopt needed flags matching those in dalvik.system.DexFile */ -constexpr int DEXOPT_DEX2OAT_NEEDED = 1; -constexpr int DEXOPT_PATCHOAT_NEEDED = 2; -constexpr int DEXOPT_SELF_PATCHOAT_NEEDED = 3; - /**************************************************************************** * IMPORTANT: These values are passed from Java code. Keep them in sync with * frameworks/base/services/core/java/com/android/server/pm/Installer.java diff --git a/cmds/installd/installd_deps.h b/cmds/installd/installd_deps.h index 5ff46e694f..509317803e 100644 --- a/cmds/installd/installd_deps.h +++ b/cmds/installd/installd_deps.h @@ -57,9 +57,6 @@ extern bool create_cache_path(char path[PKG_PATH_MAX], const char *src, const char *instruction_set); -// Initialize globals. May be implemented with the helper in globals.h. -extern bool initialize_globals(); - } // namespace installd } // namespace android diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp index e1cfc9d999..6c87f7a4d6 100644 --- a/cmds/installd/otapreopt.cpp +++ b/cmds/installd/otapreopt.cpp @@ -60,11 +60,6 @@ using android::base::StringPrintf; namespace android { namespace installd { -static constexpr const char* kBootClassPathPropertyName = "BOOTCLASSPATH"; -static constexpr const char* kAndroidRootPathPropertyName = "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); @@ -77,8 +72,6 @@ static constexpr T RoundUp(T x, typename std::remove_reference<T>::type 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). @@ -91,26 +84,31 @@ class OTAPreoptService { // // 5) Run update. int Main(int argc, char** argv) { + if (!ReadArguments(argc, argv)) { + LOG(ERROR) << "Failed reading command line."; + return 1; + } + if (!ReadSystemProperties()) { LOG(ERROR)<< "Failed reading system properties."; - return 1; + return 2; } if (!ReadEnvironment()) { LOG(ERROR) << "Failed reading environment properties."; - return 2; + return 3; } - if (!ReadPackage(argc, argv)) { - LOG(ERROR) << "Failed reading command line file."; - return 3; + if (!CheckAndInitializeInstalldGlobals()) { + LOG(ERROR) << "Failed initializing globals."; + return 4; } PrepareEnvironment(); - if (!PrepareBootImage()) { + if (!PrepareBootImage(/* force */ false)) { LOG(ERROR) << "Failed preparing boot image."; - return 4; + return 5; } int dexopt_retcode = RunPreopt(); @@ -118,7 +116,7 @@ class OTAPreoptService { return dexopt_retcode; } - int GetProperty(const char* key, char* value, const char* default_value) { + int GetProperty(const char* key, char* value, const char* default_value) const { const std::string* prop_value = system_properties_.GetProperty(key); if (prop_value == nullptr) { if (default_value == nullptr) { @@ -135,7 +133,16 @@ class OTAPreoptService { return static_cast<int>(size); } + std::string GetOTADataDirectory() const { + return StringPrintf("%s/%s", GetOtaDirectoryPrefix().c_str(), target_slot_.c_str()); + } + + const std::string& GetTargetSlot() const { + return target_slot_; + } + private: + bool ReadSystemProperties() { static constexpr const char* kPropertyFiles[] = { "/default.prop", "/system/build.prop" @@ -177,29 +184,110 @@ private: return false; } - // Check that we found important properties. - constexpr const char* kRequiredProperties[] = { - kBootClassPathPropertyName, kAndroidRootPathPropertyName - }; - for (size_t i = 0; i < arraysize(kRequiredProperties); ++i) { - if (system_properties_.GetProperty(kRequiredProperties[i]) == nullptr) { - return false; - } + if (system_properties_.GetProperty(kAndroidDataPathPropertyName) == nullptr) { + return false; } + android_data_ = *system_properties_.GetProperty(kAndroidDataPathPropertyName); + + if (system_properties_.GetProperty(kAndroidRootPathPropertyName) == nullptr) { + return false; + } + android_root_ = *system_properties_.GetProperty(kAndroidRootPathPropertyName); + + if (system_properties_.GetProperty(kBootClassPathPropertyName) == nullptr) { + return false; + } + boot_classpath_ = *system_properties_.GetProperty(kBootClassPathPropertyName); + + if (system_properties_.GetProperty(ASEC_MOUNTPOINT_ENV_NAME) == nullptr) { + return false; + } + asec_mountpoint_ = *system_properties_.GetProperty(ASEC_MOUNTPOINT_ENV_NAME); return true; } - bool ReadPackage(int argc ATTRIBUTE_UNUSED, char** argv) { - size_t index = 0; + const std::string& GetAndroidData() const { + return android_data_; + } + + const std::string& GetAndroidRoot() const { + return android_root_; + } + + const std::string GetOtaDirectoryPrefix() const { + return GetAndroidData() + "/ota"; + } + + bool CheckAndInitializeInstalldGlobals() { + // init_globals_from_data_and_root requires "ASEC_MOUNTPOINT" in the environment. We + // do not use any datapath that includes this, but we'll still have to set it. + CHECK(system_properties_.GetProperty(ASEC_MOUNTPOINT_ENV_NAME) != nullptr); + int result = setenv(ASEC_MOUNTPOINT_ENV_NAME, asec_mountpoint_.c_str(), 0); + if (result != 0) { + LOG(ERROR) << "Could not set ASEC_MOUNTPOINT environment variable"; + return false; + } + + if (!init_globals_from_data_and_root(GetAndroidData().c_str(), GetAndroidRoot().c_str())) { + LOG(ERROR) << "Could not initialize globals; exiting."; + return false; + } + + // 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. + if (access(GetOtaDirectoryPrefix().c_str(), R_OK) < 0) { + LOG(ERROR) << "Could not access " << GetOtaDirectoryPrefix(); + return false; + } + + return true; + } + + bool ReadArguments(int argc ATTRIBUTE_UNUSED, char** argv) { + // Expected command line: + // target-slot dexopt {DEXOPT_PARAMETERS} + // The DEXOPT_PARAMETERS are passed on to dexopt(), so we expect DEXOPT_PARAM_COUNT + // of them. We store them in package_parameters_ (size checks are done when + // parsing the special parameters and when copying into package_parameters_. + static_assert(DEXOPT_PARAM_COUNT == ARRAY_SIZE(package_parameters_), "Unexpected dexopt param count"); + + 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; + { + std::regex slot_suffix_regex("[a-zA-Z0-9_]+"); + std::smatch slot_suffix_match; + if (!std::regex_match(target_slot_, slot_suffix_match, slot_suffix_regex)) { + LOG(ERROR) << "Target slot suffix not legal: " << target_slot_; + return false; + } + } + + // Check for "dexopt" next. + if (argv[2] == nullptr) { + LOG(ERROR) << "Missing parameters"; + return false; + } + if (std::string("dexopt").compare(argv[2]) != 0) { + LOG(ERROR) << "Second parameter not dexopt: " << argv[2]; + return false; + } + + // Copy the rest into package_parameters_, but be careful about over- and underflow. + size_t index = 0; while (index < DEXOPT_PARAM_COUNT && - argv[index + 1] != nullptr) { - package_parameters_[index] = argv[index + 1]; + argv[index + 3] != nullptr) { + package_parameters_[index] = argv[index + 3]; index++; } - if (index != ARRAY_SIZE(package_parameters_) || argv[index + 1] != nullptr) { + if (index != ARRAY_SIZE(package_parameters_) || argv[index + 3] != nullptr) { LOG(ERROR) << "Wrong number of parameters"; return false; } @@ -208,15 +296,9 @@ private: } 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())); + environ_.push_back(StringPrintf("BOOTCLASSPATH=%s", boot_classpath_.c_str())); + environ_.push_back(StringPrintf("ANDROID_DATA=%s", GetOTADataDirectory().c_str())); + environ_.push_back(StringPrintf("ANDROID_ROOT=%s", android_root_.c_str())); for (const std::string& e : environ_) { putenv(const_cast<char*>(e.c_str())); @@ -225,7 +307,7 @@ private: // Ensure that we have the right boot image. The first time any app is // compiled, we'll try to generate it. - bool PrepareBootImage() { + bool PrepareBootImage(bool force) const { if (package_parameters_[kISAIndex] == nullptr) { LOG(ERROR) << "Instruction set missing."; return false; @@ -233,44 +315,114 @@ private: const char* isa = package_parameters_[kISAIndex]; // Check whether the file exists where expected. - std::string dalvik_cache = std::string(kOTADataDirectory) + "/" + DALVIK_CACHE; + std::string dalvik_cache = GetOTADataDirectory() + "/" + 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; + bool cleared = false; + if (access(art_path.c_str(), F_OK) == 0 && access(oat_path.c_str(), F_OK) == 0) { + // Files exist, assume everything is alright if not forced. Otherwise clean up. + if (!force) { + return true; + } + ClearDirectory(isa_path); + cleared = true; } + // Reset umask in otapreopt, so that we control the the access for the files we create. + umask(0); + // 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"; + if (!CreatePath(dalvik_cache)) { + PLOG(ERROR) << "Could not create dalvik-cache dir " << dalvik_cache; return false; } } if (access(isa_path.c_str(), F_OK) != 0) { - if (mkdir(isa_path.c_str(), 0711) != 0) { + if (!CreatePath(isa_path)) { PLOG(ERROR) << "Could not create dalvik-cache isa dir"; return false; } } // Prepare to create. - // TODO: Delete files, just for a blank slate. - const std::string& boot_cp = *system_properties_.GetProperty(kBootClassPathPropertyName); + if (!cleared) { + ClearDirectory(isa_path); + } std::string preopted_boot_art_path = StringPrintf("/system/framework/%s/boot.art", isa); if (access(preopted_boot_art_path.c_str(), F_OK) == 0) { return PatchoatBootImage(art_path, isa); } else { // No preopted boot image. Try to compile. - return Dex2oatBootImage(boot_cp, art_path, oat_path, isa); + return Dex2oatBootImage(boot_classpath_, art_path, oat_path, isa); } } - bool PatchoatBootImage(const std::string& art_path, const char* isa) { + static bool CreatePath(const std::string& path) { + // Create the given path. Use string processing instead of dirname, as dirname's need for + // a writable char buffer is painful. + + // First, try to use the full path. + if (mkdir(path.c_str(), 0711) == 0) { + return true; + } + if (errno != ENOENT) { + PLOG(ERROR) << "Could not create path " << path; + return false; + } + + // Now find the parent and try that first. + size_t last_slash = path.find_last_of('/'); + if (last_slash == std::string::npos || last_slash == 0) { + PLOG(ERROR) << "Could not create " << path; + return false; + } + + if (!CreatePath(path.substr(0, last_slash))) { + return false; + } + + if (mkdir(path.c_str(), 0711) == 0) { + return true; + } + PLOG(ERROR) << "Could not create " << path; + return false; + } + + static void ClearDirectory(const std::string& dir) { + DIR* c_dir = opendir(dir.c_str()); + if (c_dir == nullptr) { + PLOG(WARNING) << "Unable to open " << dir << " to delete it's contents"; + return; + } + + for (struct dirent* de = readdir(c_dir); de != nullptr; de = readdir(c_dir)) { + const char* name = de->d_name; + if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) { + continue; + } + // We only want to delete regular files and symbolic links. + std::string file = StringPrintf("%s/%s", dir.c_str(), name); + if (de->d_type != DT_REG && de->d_type != DT_LNK) { + LOG(WARNING) << "Unexpected file " + << file + << " of type " + << std::hex + << de->d_type + << " encountered."; + } else { + // Try to unlink the file. + if (unlink(file.c_str()) != 0) { + PLOG(ERROR) << "Unable to unlink " << file; + } + } + } + CHECK_EQ(0, closedir(c_dir)) << "Unable to close directory."; + } + + bool PatchoatBootImage(const std::string& art_path, const char* isa) const { // This needs to be kept in sync with ART, see art/runtime/gc/space/image_space.cc. std::vector<std::string> cmd; @@ -296,7 +448,7 @@ private: bool Dex2oatBootImage(const std::string& boot_cp, const std::string& art_path, const std::string& oat_path, - const char* isa) { + const char* isa) const { // 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("/system/bin/dex2oat"); @@ -362,9 +514,7 @@ private: return (strcmp(arg, "!") == 0) ? nullptr : arg; } - int RunPreopt() { - // Run the preopt. - // + bool ShouldSkipPreopt() const { // There's one thing we have to be careful about: we may/will be asked to compile an app // living in the system image. This may be a valid request - if the app wasn't compiled, // e.g., if the system image wasn't large enough to include preopted files. However, the @@ -391,9 +541,7 @@ private: constexpr size_t kApkPathIndex = 0; CHECK_GT(DEXOPT_PARAM_COUNT, kApkPathIndex); CHECK(package_parameters_[kApkPathIndex] != nullptr); - CHECK(system_properties_.GetProperty(kAndroidRootPathPropertyName) != nullptr); - if (StartsWith(package_parameters_[kApkPathIndex], - system_properties_.GetProperty(kAndroidRootPathPropertyName)->c_str())) { + if (StartsWith(package_parameters_[kApkPathIndex], android_root_.c_str())) { const char* last_slash = strrchr(package_parameters_[kApkPathIndex], '/'); if (last_slash != nullptr) { std::string path(package_parameters_[kApkPathIndex], @@ -401,11 +549,45 @@ private: CHECK(EndsWith(path, "/")); path = path + "oat"; if (access(path.c_str(), F_OK) == 0) { - return 0; + return true; } } } + // Another issue is unavailability of files in the new system. If the partition + // layout changes, otapreopt_chroot may not know about this. Then files from that + // partition will not be available and fail to build. This is problematic, as + // this tool will wipe the OTA artifact cache and try again (for robustness after + // a failed OTA with remaining cache artifacts). + if (access(package_parameters_[kApkPathIndex], F_OK) != 0) { + LOG(WARNING) << "Skipping preopt of non-existing package " + << package_parameters_[kApkPathIndex]; + return true; + } + + return false; + } + + int RunPreopt() { + if (ShouldSkipPreopt()) { + return 0; + } + + int dexopt_result = dexopt(package_parameters_); + if (dexopt_result == 0) { + return 0; + } + + // If the dexopt failed, we may have a stale boot image from a previous OTA run. + // Try to delete and retry. + + if (!PrepareBootImage(/* force */ true)) { + LOG(ERROR) << "Forced boot image creating failed. Original error return was " + << dexopt_result; + return dexopt_result; + } + + LOG(WARNING) << "Original dexopt failed, re-trying after boot image was regenerated."; return dexopt(package_parameters_); } @@ -414,7 +596,7 @@ private: //////////////////////////////////// // Wrapper on fork/execv to run a command in a subprocess. - bool Exec(const std::vector<std::string>& arg_vector, std::string* error_msg) { + static 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; @@ -504,9 +686,8 @@ private: 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); + std::vector<std::string>& out) const { + const std::string* value = system_properties_.GetProperty(system_property); if (value != nullptr) { if (runtime) { out.push_back("--runtime-arg"); @@ -519,10 +700,24 @@ private: } } + static constexpr const char* kBootClassPathPropertyName = "BOOTCLASSPATH"; + static constexpr const char* kAndroidRootPathPropertyName = "ANDROID_ROOT"; + static constexpr const char* kAndroidDataPathPropertyName = "ANDROID_DATA"; + // The index of the instruction-set string inside the package parameters. Needed for + // some special-casing that requires knowledge of the instruction-set. + static constexpr size_t kISAIndex = 3; + // 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_; + // 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_; + const char* package_parameters_[DEXOPT_PARAM_COUNT]; // Store environment values we need to set. @@ -563,8 +758,13 @@ bool calculate_oat_file_path(char path[PKG_PATH_MAX], const char *oat_dir, 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()); + snprintf(path, + PKG_PATH_MAX, + "%s/%s/%s.odex.%s", + oat_dir, + instruction_set, + file_name.c_str(), + gOps.GetTargetSlot().c_str()); return true; } @@ -576,11 +776,6 @@ bool calculate_oat_file_path(char path[PKG_PATH_MAX], const char *oat_dir, */ 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); @@ -596,11 +791,15 @@ bool calculate_odex_file_path(char path[PKG_PATH_MAX], const char *apk_path, } std::string name_component(name_begin, extension_start - name_begin); - std::string new_path = StringPrintf("%s/oat/%s/%s.odex.b", + std::string new_path = StringPrintf("%s/oat/%s/%s.odex.%s", path_component.c_str(), instruction_set, - name_component.c_str()); - CHECK_LT(new_path.length(), PKG_PATH_MAX); + name_component.c_str(), + gOps.GetTargetSlot().c_str()); + if (new_path.length() >= PKG_PATH_MAX) { + LOG(ERROR) << "apk_path of " << apk_path << " is too long: " << new_path; + return false; + } strcpy(path, new_path.c_str()); return true; } @@ -623,7 +822,7 @@ bool create_cache_path(char path[PKG_PATH_MAX], std::replace(from_src.begin(), from_src.end(), '/', '@'); std::string assembled_path = StringPrintf("%s/%s/%s/%s%s", - OTAPreoptService::kOTADataDirectory, + gOps.GetOTADataDirectory().c_str(), DALVIK_CACHE, instruction_set, from_src.c_str(), @@ -637,27 +836,6 @@ bool create_cache_path(char path[PKG_PATH_MAX], 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; @@ -685,8 +863,6 @@ static int otapreopt_main(const int argc, char *argv[]) { setenv("ANDROID_LOG_TAGS", "*:v", 1); android::base::InitLogging(argv); - ALOGI("otapreopt firing up\n"); - if (argc < 2) { ALOGE("Expecting parameters"); exit(1); @@ -696,16 +872,6 @@ static int otapreopt_main(const int argc, char *argv[]) { 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); diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp index be0ff2e140..e6030bf171 100644 --- a/cmds/installd/otapreopt_chroot.cpp +++ b/cmds/installd/otapreopt_chroot.cpp @@ -14,15 +14,18 @@ ** limitations under the License. */ +#include <fcntl.h> #include <linux/unistd.h> #include <sys/mount.h> #include <sys/wait.h> +#include <sstream> + #include <android-base/logging.h> #include <android-base/macros.h> #include <android-base/stringprintf.h> -#include <installd_constants.h> +#include <commands.h> #ifndef LOG_TAG #define LOG_TAG "otapreopt" @@ -33,7 +36,37 @@ using android::base::StringPrintf; namespace android { namespace installd { +static void CloseDescriptor(int fd) { + if (fd >= 0) { + int result = close(fd); + UNUSED(result); // Ignore result. Printing to logcat will open a new descriptor + // that we do *not* want. + } +} + +static void CloseDescriptor(const char* descriptor_string) { + int fd = -1; + std::istringstream stream(descriptor_string); + stream >> fd; + if (!stream.fail()) { + CloseDescriptor(fd); + } +} + +// Entry for otapreopt_chroot. Expected parameters are: +// [cmd] [status-fd] [target-slot] "dexopt" [dexopt-params] +// The file descriptor denoted by status-fd will be closed. The rest of the parameters will +// be passed on to otapreopt in the chroot. static int otapreopt_chroot(const int argc, char **arg) { + // Close all file descriptors. They are coming from the caller, we do not want to pass them + // on across our fork/exec into a different domain. + // 1) Default descriptors. + CloseDescriptor(STDIN_FILENO); + CloseDescriptor(STDOUT_FILENO); + CloseDescriptor(STDERR_FILENO); + // 2) The status channel. + CloseDescriptor(arg[1]); + // We need to run the otapreopt tool from the postinstall partition. As such, set up a // mount namespace and change root. @@ -80,13 +113,42 @@ static int otapreopt_chroot(const int argc, char **arg) { // Now go on and run otapreopt. - const char* argv[1 + DEXOPT_PARAM_COUNT + 1]; - CHECK_EQ(static_cast<size_t>(argc), DEXOPT_PARAM_COUNT + 1); + // Incoming: cmd + status-fd + target-slot + "dexopt" + dexopt-params + null + // Outgoing: cmd + target-slot + "dexopt" + dexopt-params + null + constexpr size_t kInArguments = 1 // Binary name. + + 1 // status file descriptor. + + 1 // target-slot. + + 1 // "dexopt." + + DEXOPT_PARAM_COUNT // dexopt parameters. + + 1; // null termination. + constexpr size_t kOutArguments = 1 // Binary name. + + 1 // target-slot. + + 1 // "dexopt." + + DEXOPT_PARAM_COUNT // dexopt parameters. + + 1; // null termination. + const char* argv[kOutArguments]; + if (static_cast<size_t>(argc) != kInArguments - 1 /* null termination */) { + LOG(ERROR) << "Unexpected argument size " + << argc + << " vs " + << (kInArguments - 1); + for (size_t i = 0; i < static_cast<size_t>(argc); ++i) { + if (arg[i] == nullptr) { + LOG(ERROR) << "(null)"; + } else { + LOG(ERROR) << "\"" << arg[i] << "\""; + } + } + exit(206); + } argv[0] = "/system/bin/otapreopt"; - for (size_t i = 1; i <= DEXOPT_PARAM_COUNT; ++i) { - argv[i] = arg[i]; + + // The first parameter is the status file descriptor, skip. + + for (size_t i = 1; i <= kOutArguments - 2 /* cmd + null */; ++i) { + argv[i] = arg[i + 1]; } - argv[DEXOPT_PARAM_COUNT + 1] = nullptr; + argv[kOutArguments - 1] = nullptr; execv(argv[0], (char * const *)argv); PLOG(ERROR) << "execv(OTAPREOPT) failed."; diff --git a/cmds/installd/otapreopt_script.sh b/cmds/installd/otapreopt_script.sh index 394c244336..8af4a901f5 100644 --- a/cmds/installd/otapreopt_script.sh +++ b/cmds/installd/otapreopt_script.sh @@ -18,11 +18,25 @@ # This script will run as a postinstall step to drive otapreopt. +TARGET_SLOT="$1" STATUS_FD="$2" # Maximum number of packages/steps. MAXIMUM_PACKAGES=1000 +# Compute target slot suffix. +# TODO: Once bootctl is not restricted, we should query from there. Or get this from +# update_engine as a parameter. +if [ "$TARGET_SLOT" = "0" ] ; then + TARGET_SLOT_SUFFIX="_a" +elif [ "$TARGET_SLOT" = "1" ] ; then + TARGET_SLOT_SUFFIX="_b" +else + echo "Unknown target slot $TARGET_SLOT" + exit 1 +fi + + PREPARE=$(cmd otadexopt prepare) # Note: Ignore preparation failures. Step and done will fail and exit this. # This is necessary to support suspends - the OTA service will keep @@ -33,7 +47,9 @@ print -u${STATUS_FD} "global_progress $PROGRESS" i=0 while ((i<MAXIMUM_PACKAGES)) ; do - cmd otadexopt step + DEXOPT_PARAMS=$(cmd otadexopt next) + + /system/bin/otapreopt_chroot $STATUS_FD $TARGET_SLOT_SUFFIX $DEXOPT_PARAMS >&- 2>&- PROGRESS=$(cmd otadexopt progress) print -u${STATUS_FD} "global_progress $PROGRESS" |