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;
return 0;
}
-/**
- * Prepare an app cache directory, which offers to fix-up the GID and
- * directory mode flags during a platform upgrade.
- */
-static int prepare_app_cache_dir(const std::string& parent, const char* name, mode_t target_mode,
- uid_t uid, gid_t gid) {
- auto path = StringPrintf("%s/%s", parent.c_str(), name);
- struct stat st;
- if (stat(path.c_str(), &st) != 0) {
- if (errno == ENOENT) {
- // This is fine, just create it
- if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, gid) != 0) {
- PLOG(ERROR) << "Failed to prepare " << path;
- return -1;
- } else {
- return 0;
- }
- } else {
- PLOG(ERROR) << "Failed to stat " << path;
- return -1;
- }
- }
-
- mode_t actual_mode = st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISGID);
- if (st.st_uid != uid) {
- // Mismatched UID is real trouble; we can't recover
- LOG(ERROR) << "Mismatched UID at " << path << ": found " << st.st_uid
- << " but expected " << uid;
- return -1;
- } else if (st.st_gid == gid && actual_mode == target_mode) {
- // Everything looks good!
- return 0;
- }
-
- // Directory is owned correctly, but GID or mode mismatch means it's
- // probably a platform upgrade so we need to fix them
- FTS *fts;
- FTSENT *p;
- char *argv[] = { (char*) path.c_str(), nullptr };
- if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_XDEV, NULL))) {
- PLOG(ERROR) << "Failed to fts_open " << path;
- return -1;
- }
- while ((p = fts_read(fts)) != NULL) {
- switch (p->fts_info) {
- case FTS_DP:
- if (chmod(p->fts_accpath, target_mode) != 0) {
- PLOG(WARNING) << "Failed to chmod " << p->fts_path;
- }
- // Intentional fall through to also set GID
- case FTS_F:
- if (chown(p->fts_accpath, -1, gid) != 0) {
- PLOG(WARNING) << "Failed to chown " << p->fts_path;
- }
- break;
- case FTS_SL:
- case FTS_SLNONE:
- if (lchown(p->fts_accpath, -1, gid) != 0) {
- PLOG(WARNING) << "Failed to chown " << p->fts_path;
- }
- break;
- }
- }
- fts_close(fts);
- return 0;
-}
-
binder::Status InstalldNativeService::createAppData(const std::unique_ptr<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return) {
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
+#include <cutils/fs.h>
#include <cutils/properties.h>
#include <cutils/sched_policy.h>
#include <log/log.h> // TODO: Move everything to base/logging.
}
static bool create_oat_out_path(const char* apk_path, const char* instruction_set,
- const char* oat_dir, /*out*/ char* out_oat_path) {
+ const char* oat_dir, bool is_secondary_dex, /*out*/ char* out_oat_path) {
// Early best-effort check whether we can fit the the path into our buffers.
// Note: the cache path will require an additional 5 bytes for ".swap", but we'll try to run
// without a swap file, if necessary. Reference profiles file also add an extra ".prof"
}
if (!IsOutputDalvikCache(oat_dir)) {
- if (validate_apk_path(oat_dir)) {
+ // Oat dirs for secondary dex files are already validated.
+ if (!is_secondary_dex && validate_apk_path(oat_dir)) {
ALOGE("cannot validate apk path with oat_dir '%s'\n", oat_dir);
return false;
}
// Opens the reference profiles if needed.
// Note that the reference profile might not exist so it's OK if the fd will be -1.
Dex2oatFileWrapper maybe_open_reference_profile(const char* pkgname, bool profile_guided,
- bool is_public, int uid) {
+ bool is_public, int uid, bool is_secondary_dex) {
// Public apps should not be compiled with profile information ever. Same goes for the special
// package '*' used for the system server.
- if (profile_guided && !is_public && (pkgname[0] != '*')) {
+ // TODO(calin): add support for writing profiles for secondary dex files
+ if (profile_guided && !is_secondary_dex && !is_public && (pkgname[0] != '*')) {
// Open reference profile in read only mode as dex2oat does not get write permissions.
const std::string pkgname_str(pkgname);
return Dex2oatFileWrapper(
// Opens the output oat file for the given apk.
// If successful it stores the output path into out_oat_path and returns true.
Dex2oatFileWrapper open_oat_out_file(const char* apk_path, const char* oat_dir,
- bool is_public, int uid, const char* instruction_set, char* out_oat_path) {
- if (!create_oat_out_path(apk_path, instruction_set, oat_dir, out_oat_path)) {
+ bool is_public, int uid, const char* instruction_set, bool is_secondary_dex,
+ char* out_oat_path) {
+ if (!create_oat_out_path(apk_path, instruction_set, oat_dir, is_secondary_dex, out_oat_path)) {
return Dex2oatFileWrapper();
}
const std::string out_oat_path_str(out_oat_path);
open_output_file(out_oat_path, /*recreate*/true, /*permissions*/0644),
[out_oat_path_str]() { unlink(out_oat_path_str.c_str()); });
if (wrapper_fd.get() < 0) {
- ALOGE("installd cannot open '%s' for output during dexopt\n", out_oat_path);
+ PLOG(ERROR) << "installd cannot open output during dexopt" << out_oat_path;
} else if (!set_permissions_and_ownership(wrapper_fd.get(), is_public, uid, out_oat_path)) {
ALOGE("installd cannot set owner '%s' for output during dexopt\n", out_oat_path);
wrapper_fd.reset(-1);
}
}
+// Runs (execv) dexoptanalyzer on the given arguments.
+static void exec_dexoptanalyzer(const char* dex_file, const char* instruction_set,
+ const char* compiler_filter) {
+ static const char* DEXOPTANALYZER_BIN = "/system/bin/dexoptanalyzer";
+ static const unsigned int MAX_INSTRUCTION_SET_LEN = 7;
+
+ if (strlen(instruction_set) >= MAX_INSTRUCTION_SET_LEN) {
+ ALOGE("Instruction set %s longer than max length of %d",
+ instruction_set, MAX_INSTRUCTION_SET_LEN);
+ return;
+ }
+
+ char dex_file_arg[strlen("--dex-file=") + PKG_PATH_MAX];
+ char isa_arg[strlen("--isa=") + MAX_INSTRUCTION_SET_LEN];
+ char compiler_filter_arg[strlen("--compiler-filter=") + kPropertyValueMax];
+
+ sprintf(dex_file_arg, "--dex-file=%s", dex_file);
+ sprintf(isa_arg, "--isa=%s", instruction_set);
+ sprintf(compiler_filter_arg, "--compiler-filter=%s", compiler_filter);
+
+ // program name, dex file, isa, filter, the final NULL
+ const char* argv[5];
+ int i = 0;
+ argv[i++] = DEXOPTANALYZER_BIN;
+ argv[i++] = dex_file_arg;
+ argv[i++] = isa_arg;
+ argv[i++] = compiler_filter_arg;
+ argv[i] = NULL;
+
+ execv(DEXOPTANALYZER_BIN, (char * const *)argv);
+ ALOGE("execv(%s) failed: %s\n", DEXOPTANALYZER_BIN, strerror(errno));
+}
+
+// Prepares the oat dir for the secondary dex files.
+static bool prepare_secondary_dex_oat_dir(const char* apk_path, int uid,
+ const char* instruction_set, std::string* oat_dir_out) {
+ std::string apk_path_str(apk_path);
+ unsigned long dirIndex = apk_path_str.rfind('/');
+ if (dirIndex == std::string::npos) {
+ LOG(WARNING) << "Unexpected dir structure for secondary dex " << apk_path;
+ return false;
+ }
+ std::string apk_dir = apk_path_str.substr(0, dirIndex);
+
+ // Assign the gid to the cache gid so that the oat file storage
+ // is counted towards the app cache.
+ int32_t cache_gid = multiuser_get_cache_gid(
+ multiuser_get_user_id(uid), multiuser_get_app_id(uid));
+ // If UID doesn't have a specific cache GID, use UID value
+ if (cache_gid == -1) {
+ cache_gid = uid;
+ }
+
+ // Create oat file output directory.
+ if (prepare_app_cache_dir(apk_dir, "oat", 02711, uid, cache_gid) != 0) {
+ LOG(ERROR) << "Could not prepare oat dir for secondary dex: " << apk_path;
+ return false;
+ }
+
+ char oat_dir[PKG_PATH_MAX];
+ snprintf(oat_dir, PKG_PATH_MAX, "%s/oat", apk_dir.c_str());
+ oat_dir_out->assign(oat_dir);
+
+ // Create oat/isa output directory.
+ if (prepare_app_cache_dir(*oat_dir_out, instruction_set, 02711, uid, cache_gid) != 0) {
+ LOG(ERROR) << "Could not prepare oat/isa dir for secondary dex: " << apk_path;
+ return false;
+ }
+
+ return true;
+}
+
+static int constexpr DEXOPTANALYZER_BIN_EXEC_ERROR = 200;
+
+// Verifies the result of dexoptanalyzer executed for the apk_path.
+// If the result is valid returns true and sets dexopt_needed_out to a valid value.
+// Returns false for errors or unexpected result values.
+static bool process_dexoptanalyzer_result(const char* apk_path, int result,
+ int* dexopt_needed_out) {
+ // The result values are defined in dexoptanalyzer.
+ switch (result) {
+ case 0: // no_dexopt_needed
+ *dexopt_needed_out = NO_DEXOPT_NEEDED; return true;
+ case 1: // dex2oat_from_scratch
+ *dexopt_needed_out = DEX2OAT_FROM_SCRATCH; return true;
+ case 5: // dex2oat_for_bootimage_odex
+ *dexopt_needed_out = -DEX2OAT_FOR_BOOT_IMAGE; return true;
+ case 6: // dex2oat_for_filter_odex
+ *dexopt_needed_out = -DEX2OAT_FOR_FILTER; return true;
+ case 7: // dex2oat_for_relocation_odex
+ *dexopt_needed_out = -DEX2OAT_FOR_RELOCATION; return true;
+ case 2: // dex2oat_for_bootimage_oat
+ case 3: // dex2oat_for_filter_oat
+ case 4: // dex2oat_for_relocation_oat
+ LOG(ERROR) << "Expected odex file status for secondary dex " << apk_path
+ << " : dexoptanalyzer result=" << result;
+ return false;
+ default:
+ LOG(ERROR) << "Unexpected result for dexoptanalyzer " << apk_path
+ << " exec_dexoptanalyzer result=" << result;
+ return false;
+ }
+}
+
+// Processes the apk_path as a secondary dex files and return true if the path dex file should
+// be compiled. Returns false for errors (logged) or true if the secondary dex path was process
+// successfully.
+// When returning true, dexopt_needed_out is assigned a valid OatFileAsssitant::DexOptNeeded
+// code and aot_dir_out is assigned the oat dir path where the oat file should be stored.
+static bool process_secondary_dex_dexopt(const char* apk_path, const char* pkgname,
+ int dexopt_flags, const char* volume_uuid, int uid, const char* instruction_set,
+ const char* compiler_filter, int* dexopt_needed_out, std::string* aot_dir_out) {
+ int storage_flag;
+
+ if ((dexopt_flags & DEXOPT_STORAGE_CE) != 0) {
+ storage_flag = FLAG_STORAGE_CE;
+ if ((dexopt_flags & DEXOPT_STORAGE_DE) != 0) {
+ LOG(ERROR) << "Ambiguous secondary dex storage flag. Both, CE and DE, flags are set";
+ return false;
+ }
+ } else if ((dexopt_flags & DEXOPT_STORAGE_DE) != 0) {
+ storage_flag = FLAG_STORAGE_DE;
+ } else {
+ LOG(ERROR) << "Secondary dex storage flag must be set";
+ return false;
+ }
+
+ if (!validate_secondary_dex_path(pkgname, apk_path, volume_uuid, uid, storage_flag)) {
+ LOG(ERROR) << "Could not validate secondary dex path " << apk_path;
+ return false;
+ }
+
+ // Check if the path exist. If not, there's nothing to do.
+ if (access(apk_path, F_OK) != 0) {
+ if (errno == ENOENT) {
+ // Secondary dex files might be deleted any time by the app.
+ // Nothing to do if that's the case
+ ALOGV("Secondary dex does not exist %s", apk_path);
+ return NO_DEXOPT_NEEDED;
+ } else {
+ PLOG(ERROR) << "Could not access secondary dex " << apk_path;
+ }
+ }
+
+ // Prepare the oat directories.
+ if (!prepare_secondary_dex_oat_dir(apk_path, uid, instruction_set, aot_dir_out)) {
+ return false;
+ }
+
+ pid_t pid = fork();
+ if (pid == 0) {
+ // child -- drop privileges before continuing.
+ drop_capabilities(uid);
+ // Run dexoptanalyzer to get dexopt_needed code.
+ exec_dexoptanalyzer(apk_path, instruction_set, compiler_filter);
+ exit(DEXOPTANALYZER_BIN_EXEC_ERROR);
+ }
+
+ /* parent */
+
+ int result = wait_child(pid);
+ if (!WIFEXITED(result)) {
+ LOG(ERROR) << "dexoptanalyzer failed for path " << apk_path << ": " << result;
+ return false;
+ }
+ result = WEXITSTATUS(result);
+ bool success = process_dexoptanalyzer_result(apk_path, result, dexopt_needed_out);
+ // Run dexopt only if needed or forced.
+ // Note that dexoptanalyzer is executed even if force compilation is enabled.
+ // We ignore its valid dexopNeeded result, but still check (in process_dexoptanalyzer_result)
+ // that we only get results for odex files (apk_dir/oat/isa/code.odex) and not
+ // for oat files from dalvik-cache.
+ if (success && ((dexopt_flags & DEXOPT_FORCE) != 0)) {
+ *dexopt_needed_out = DEX2OAT_FROM_SCRATCH;
+ }
+
+ return success;
+}
+
int dexopt(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 ATTRIBUTE_UNUSED, const char* shared_libraries) {
+ int dexopt_needed, const char* oat_dir, int dexopt_flags, const char* compiler_filter,
+ const char* volume_uuid, const char* shared_libraries) {
CHECK(pkgname != nullptr);
CHECK(pkgname[0] != 0);
if ((dexopt_flags & ~DEXOPT_MASK) != 0) {
bool debuggable = (dexopt_flags & DEXOPT_DEBUGGABLE) != 0;
bool boot_complete = (dexopt_flags & DEXOPT_BOOTCOMPLETE) != 0;
bool profile_guided = (dexopt_flags & DEXOPT_PROFILE_GUIDED) != 0;
+ bool is_secondary_dex = (dexopt_flags & DEXOPT_SECONDARY_DEX) != 0;
+
+ // Check if we're dealing with a secondary dex file and if we need to compile it.
+ std::string oat_dir_str;
+ if (is_secondary_dex) {
+ if (process_secondary_dex_dexopt(apk_path, pkgname, dexopt_flags, volume_uuid, uid,
+ instruction_set, compiler_filter, &dexopt_needed, &oat_dir_str)) {
+ oat_dir = oat_dir_str.c_str();
+ if (dexopt_needed == NO_DEXOPT_NEEDED) {
+ return 0; // Nothing to do, report success.
+ }
+ } else {
+ return -1; // We had an error, logged in the process method.
+ }
+ } else {
+ // Verify that secondary dex files are not set.
+ CHECK((dexopt_flags & DEXOPT_FORCE) == 0);
+ CHECK((dexopt_flags & DEXOPT_STORAGE_CE) == 0);
+ CHECK((dexopt_flags & DEXOPT_STORAGE_DE) == 0);
+ }
// Open the input file.
base::unique_fd input_fd(open(apk_path, O_RDONLY, 0));
// Create the output OAT file.
char out_oat_path[PKG_PATH_MAX];
Dex2oatFileWrapper out_oat_fd = open_oat_out_file(apk_path, oat_dir, is_public, uid,
- instruction_set, out_oat_path);
+ instruction_set, is_secondary_dex, out_oat_path);
if (out_oat_fd.get() < 0) {
return -1;
}
// Open the reference profile if needed.
Dex2oatFileWrapper reference_profile_fd =
- maybe_open_reference_profile(pkgname, profile_guided, is_public, uid);
+ maybe_open_reference_profile(pkgname, profile_guided, is_public, uid, is_secondary_dex);
ALOGV("DexInv: --- BEGIN '%s' ---\n", apk_path);
bool delete_odex(const char* apk_path, const char* instruction_set, const char* oat_dir) {
// Delete the oat/odex file.
char out_path[PKG_PATH_MAX];
- if (!create_oat_out_path(apk_path, instruction_set, oat_dir, out_path)) {
+ if (!create_oat_out_path(apk_path, instruction_set, oat_dir,
+ /*is_secondary_dex*/ false, out_path)) {
return false;
}
return -1;
}
+bool validate_secondary_dex_path(const char* pkgname, const char* path,
+ const char* volume_uuid, int uid, int storage_flag) {
+ CHECK(storage_flag == FLAG_STORAGE_CE || storage_flag == FLAG_STORAGE_DE);
+
+ std::string app_private_dir = storage_flag == FLAG_STORAGE_CE
+ ? create_data_user_ce_package_path(volume_uuid, multiuser_get_user_id(uid), pkgname)
+ : create_data_user_de_package_path(volume_uuid, multiuser_get_user_id(uid), pkgname);
+ dir_rec_t dir;
+ if (get_path_from_string(&dir, app_private_dir.c_str()) != 0) {
+ LOG(WARNING) << "Could not get dir rec for " << app_private_dir;
+ return false;
+ }
+ // Usually secondary dex files have a nested directory structure.
+ // Pick at most 10 subdirectories when validating (arbitrary value).
+ // If the secondary dex file is >10 directory nested then validation will
+ // fail and the file will not be compiled.
+ return validate_path(&dir, path, /*max_subdirs*/ 10) == 0;
+}
+
/**
* Get the contents of a environment variable that contains a path. Caller
* owns the string that is inserted into the directory record. Returns
}
}
+/**
+ * Prepare an app cache directory, which offers to fix-up the GID and
+ * directory mode flags during a platform upgrade.
+ * The app cache directory path will be 'parent'/'name'.
+ */
+int prepare_app_cache_dir(const std::string& parent, const char* name, mode_t target_mode,
+ uid_t uid, gid_t gid) {
+ auto path = StringPrintf("%s/%s", parent.c_str(), name);
+ struct stat st;
+ if (stat(path.c_str(), &st) != 0) {
+ if (errno == ENOENT) {
+ // This is fine, just create it
+ if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, gid) != 0) {
+ PLOG(ERROR) << "Failed to prepare " << path;
+ return -1;
+ } else {
+ return 0;
+ }
+ } else {
+ PLOG(ERROR) << "Failed to stat " << path;
+ return -1;
+ }
+ }
+
+ mode_t actual_mode = st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISGID);
+ if (st.st_uid != uid) {
+ // Mismatched UID is real trouble; we can't recover
+ LOG(ERROR) << "Mismatched UID at " << path << ": found " << st.st_uid
+ << " but expected " << uid;
+ return -1;
+ } else if (st.st_gid == gid && actual_mode == target_mode) {
+ // Everything looks good!
+ return 0;
+ }
+
+ // Directory is owned correctly, but GID or mode mismatch means it's
+ // probably a platform upgrade so we need to fix them
+ FTS *fts;
+ FTSENT *p;
+ char *argv[] = { (char*) path.c_str(), nullptr };
+ if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_XDEV, NULL))) {
+ PLOG(ERROR) << "Failed to fts_open " << path;
+ return -1;
+ }
+ while ((p = fts_read(fts)) != NULL) {
+ switch (p->fts_info) {
+ case FTS_DP:
+ if (chmod(p->fts_accpath, target_mode) != 0) {
+ PLOG(WARNING) << "Failed to chmod " << p->fts_path;
+ }
+ // Intentional fall through to also set GID
+ case FTS_F:
+ if (chown(p->fts_accpath, -1, gid) != 0) {
+ PLOG(WARNING) << "Failed to chown " << p->fts_path;
+ }
+ break;
+ case FTS_SL:
+ case FTS_SLNONE:
+ if (lchown(p->fts_accpath, -1, gid) != 0) {
+ PLOG(WARNING) << "Failed to chown " << p->fts_path;
+ }
+ break;
+ }
+ }
+ fts_close(fts);
+ return 0;
+}
+
} // namespace installd
} // namespace android