Merge changes Ib6b72ca5,I8ea5dfb9,Iaf954877,I803bf9f4
* changes:
vulkan: update Vulkan headers to 1.0.43
vulkan: update Vulkan headers to 1.0.42
vulkan: update Vulkan headers to 1.0.41
vulkan: update Vulkan headers to 1.0.39
diff --git a/cmds/bugreportz/readme.md b/cmds/bugreportz/readme.md
index 2697f09..eb0d898 100644
--- a/cmds/bugreportz/readme.md
+++ b/cmds/bugreportz/readme.md
@@ -17,3 +17,4 @@
- `OK:<path_to_bugreport_file>` in case of success.
- `FAIL:<error message>` in case of failure.
+
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 5b01be1..93f17fe 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -948,7 +948,7 @@
#endif
dump_file("INTERRUPTS (2)", "/proc/interrupts");
- print_properties();
+ run_command("SYSTEM PROPERTIES", 5, "getprop", NULL);
run_command("VOLD DUMP", 10, "vdc", "dump", NULL);
run_command("SECURE CONTAINERS", 10, "vdc", "asec", "list", NULL);
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 905fc22..5ed9023 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -142,9 +142,6 @@
/* updates the overall progress of dumpstate by the given weight increment */
void update_progress(int weight);
-/* prints all the system properties */
-void print_properties();
-
/** opens a socket and returns its file descriptor */
int open_socket(const char *service);
diff --git a/cmds/dumpstate/utils.cpp b/cmds/dumpstate/utils.cpp
index 4ac5b67..bbe48be 100644
--- a/cmds/dumpstate/utils.cpp
+++ b/cmds/dumpstate/utils.cpp
@@ -885,40 +885,6 @@
run_command_always(NULL, DROP_ROOT, REDIRECT_TO_STDERR, 20, am_args);
}
-size_t num_props = 0;
-static char* props[2000];
-
-static void print_prop(const char *key, const char *name, void *user) {
- (void) user;
- if (num_props < sizeof(props) / sizeof(props[0])) {
- char buf[PROPERTY_KEY_MAX + PROPERTY_VALUE_MAX + 10];
- snprintf(buf, sizeof(buf), "[%s]: [%s]\n", key, name);
- props[num_props++] = strdup(buf);
- }
-}
-
-static int compare_prop(const void *a, const void *b) {
- return strcmp(*(char * const *) a, *(char * const *) b);
-}
-
-/* prints all the system properties */
-void print_properties() {
- const char* title = "SYSTEM PROPERTIES";
- DurationReporter duration_reporter(title);
- printf("------ %s ------\n", title);
- ON_DRY_RUN_RETURN();
- size_t i;
- num_props = 0;
- property_list(print_prop, NULL);
- qsort(&props, num_props, sizeof(props[0]), compare_prop);
-
- for (i = 0; i < num_props; ++i) {
- fputs(props[i], stdout);
- free(props[i]);
- }
- printf("\n");
-}
-
int open_socket(const char *service) {
int s = android_get_control_socket(service);
if (s < 0) {
diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp
index a61cb00..66beb6d 100644
--- a/cmds/dumpsys/tests/dumpsys_test.cpp
+++ b/cmds/dumpsys/tests/dumpsys_test.cpp
@@ -23,6 +23,7 @@
#include <android-base/file.h>
#include <utils/String16.h>
+#include <utils/String8.h>
#include <utils/Vector.h>
using namespace android;
@@ -95,7 +96,7 @@
int i = 0;
std::ostringstream actual_stream, expected_stream;
for (String16 actual : arg) {
- std::string actual_str = String16::std_string(actual);
+ std::string actual_str = String8(actual).c_str();
std::string expected_str = expected[i];
actual_stream << "'" << actual_str << "' ";
expected_stream << "'" << expected_str << "' ";
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index cdef7e1..f9235e5 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -79,10 +79,6 @@
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;
@@ -285,73 +281,6 @@
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) {
@@ -495,12 +424,11 @@
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
std::lock_guard<std::recursive_mutex> lock(mLock);
- const char* pkgname = packageName.c_str();
binder::Status res = ok();
- if (!clear_reference_profile(pkgname)) {
+ if (!clear_reference_profile(packageName)) {
res = error("Failed to clear reference profile for " + packageName);
}
- if (!clear_current_profiles(pkgname)) {
+ if (!clear_current_profiles(packageName)) {
res = error("Failed to clear current profiles for " + packageName);
}
return res;
@@ -548,7 +476,7 @@
}
}
if (!only_cache) {
- if (!clear_current_profile(pkgname, userId)) {
+ if (!clear_current_profile(packageName, userId)) {
res = error("Failed to clear current profile for " + packageName);
}
}
@@ -556,13 +484,13 @@
return res;
}
-static int destroy_app_reference_profile(const char *pkgname) {
+static int destroy_app_reference_profile(const std::string& pkgname) {
return delete_dir_contents_and_dir(
create_data_ref_profile_package_path(pkgname),
/*ignore_if_missing*/ true);
}
-static int destroy_app_current_profiles(const char *pkgname, userid_t userid) {
+static int destroy_app_current_profiles(const std::string& pkgname, userid_t userid) {
return delete_dir_contents_and_dir(
create_data_user_profile_package_path(userid, pkgname),
/*ignore_if_missing*/ true);
@@ -573,15 +501,14 @@
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
std::lock_guard<std::recursive_mutex> lock(mLock);
- const char* pkgname = packageName.c_str();
binder::Status res = ok();
std::vector<userid_t> users = get_known_users(/*volume_uuid*/ nullptr);
for (auto user : users) {
- if (destroy_app_current_profiles(pkgname, user) != 0) {
+ if (destroy_app_current_profiles(packageName, user) != 0) {
res = error("Failed to destroy current profiles for " + packageName);
}
}
- if (destroy_app_reference_profile(pkgname) != 0) {
+ if (destroy_app_reference_profile(packageName) != 0) {
res = error("Failed to destroy reference profile for " + packageName);
}
return res;
@@ -609,11 +536,11 @@
if (delete_dir_contents_and_dir(path) != 0) {
res = error("Failed to delete " + path);
}
- destroy_app_current_profiles(pkgname, userId);
+ destroy_app_current_profiles(packageName, userId);
// TODO(calin): If the package is still installed by other users it's probably
// beneficial to keep the reference profile around.
// Verify if it's ok to do that.
- destroy_app_reference_profile(pkgname);
+ destroy_app_reference_profile(packageName);
}
return res;
}
@@ -1309,7 +1236,7 @@
calculate_tree_size(create_data_dalvik_cache_path(), &stats.codeSize,
sharedGid, -1);
}
- calculate_tree_size(create_data_misc_foreign_dex_path(userId), &stats.dataSize,
+ calculate_tree_size(create_data_user_profile_path(userId), &stats.dataSize,
multiuser_get_uid(userId, appId), -1);
ATRACE_END();
}
@@ -1407,7 +1334,7 @@
ATRACE_BEGIN("dalvik");
calculate_tree_size(create_data_dalvik_cache_path(), &stats.codeSize,
-1, -1, true);
- calculate_tree_size(create_data_misc_foreign_dex_path(userId), &stats.dataSize,
+ calculate_tree_size(create_data_user_profile_path(userId), &stats.dataSize,
-1, -1, true);
ATRACE_END();
@@ -1457,7 +1384,7 @@
ATRACE_BEGIN("dalvik");
calculate_tree_size(create_data_dalvik_cache_path(), &stats.codeSize);
- calculate_tree_size(create_data_misc_foreign_dex_path(userId), &stats.dataSize);
+ calculate_tree_size(create_data_user_profile_path(userId), &stats.dataSize);
ATRACE_END();
}
@@ -2013,6 +1940,20 @@
return res ? ok() : error();
}
+binder::Status InstalldNativeService::reconcileSecondaryDexFile(
+ const std::string& dexPath, const std::string& packageName, int32_t uid,
+ const std::vector<std::string>& isas, const std::unique_ptr<std::string>& volumeUuid,
+ int32_t storage_flag, bool* _aidl_return) {
+ ENFORCE_UID(AID_SYSTEM);
+ CHECK_ARGUMENT_UUID(volumeUuid);
+ CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+
+ std::lock_guard<std::recursive_mutex> lock(mLock);
+ bool result = android::installd::reconcile_secondary_dex_file(
+ dexPath, packageName, uid, isas, volumeUuid, storage_flag, _aidl_return);
+ return result ? ok() : error();
+}
+
binder::Status InstalldNativeService::invalidateMounts() {
ENFORCE_UID(AID_SYSTEM);
std::lock_guard<std::recursive_mutex> lock(mLock);
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index 0a9f12f..37e0090 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -105,6 +105,9 @@
const std::string& outputPath);
binder::Status deleteOdex(const std::string& apkPath, const std::string& instructionSet,
const std::string& outputPath);
+ binder::Status reconcileSecondaryDexFile(const std::string& dexPath,
+ const std::string& packageName, int32_t uid, const std::vector<std::string>& isa,
+ const std::unique_ptr<std::string>& volumeUuid, int32_t storage_flag, bool* _aidl_return);
binder::Status invalidateMounts();
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index aa5e4f2..4dbfa91 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -71,5 +71,9 @@
void deleteOdex(@utf8InCpp String apkPath, @utf8InCpp String instructionSet,
@utf8InCpp String outputPath);
+ boolean reconcileSecondaryDexFile(@utf8InCpp String dexPath, @utf8InCpp String pkgName,
+ int uid, in @utf8InCpp String[] isas, @nullable @utf8InCpp String volume_uuid,
+ int storage_flag);
+
void invalidateMounts();
}
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 5d84157..58b9d2c 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -31,6 +31,7 @@
#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.
@@ -100,19 +101,19 @@
return truncated;
}
-bool clear_reference_profile(const char* pkgname) {
+bool clear_reference_profile(const std::string& pkgname) {
std::string reference_profile_dir = create_data_ref_profile_package_path(pkgname);
std::string reference_profile = create_primary_profile(reference_profile_dir);
return clear_profile(reference_profile);
}
-bool clear_current_profile(const char* pkgname, userid_t user) {
+bool clear_current_profile(const std::string& pkgname, userid_t user) {
std::string profile_dir = create_data_user_profile_package_path(user, pkgname);
std::string profile = create_primary_profile(profile_dir);
return clear_profile(profile);
}
-bool clear_current_profiles(const char* pkgname) {
+bool clear_current_profiles(const std::string& pkgname) {
bool success = true;
std::vector<userid_t> users = get_known_users(/*volume_uuid*/ nullptr);
for (auto user : users) {
@@ -511,12 +512,12 @@
return profile_fd;
}
-static fd_t open_primary_profile_file(userid_t user, const char* pkgname) {
+static fd_t open_primary_profile_file(userid_t user, const std::string& pkgname) {
std::string profile_dir = create_data_user_profile_package_path(user, pkgname);
return open_primary_profile_file_from_dir(profile_dir, O_RDONLY);
}
-static fd_t open_reference_profile(uid_t uid, const char* pkgname, bool read_write) {
+static fd_t open_reference_profile(uid_t uid, const std::string& pkgname, bool read_write) {
std::string reference_profile_dir = create_data_ref_profile_package_path(pkgname);
int flags = read_write ? O_RDWR | O_CREAT : O_RDONLY;
fd_t fd = open_primary_profile_file_from_dir(reference_profile_dir, flags);
@@ -533,7 +534,7 @@
return fd;
}
-static void open_profile_files(uid_t uid, const char* pkgname,
+static void open_profile_files(uid_t uid, const std::string& pkgname,
/*out*/ std::vector<fd_t>* profiles_fd, /*out*/ fd_t* reference_profile_fd) {
// Open the reference profile in read-write mode as profman might need to save the merge.
*reference_profile_fd = open_reference_profile(uid, pkgname, /*read_write*/ true);
@@ -613,7 +614,7 @@
// a re-compilation of the package.
// If the return value is true all the current profiles would have been merged into
// the reference profiles accessible with open_reference_profile().
-bool analyse_profiles(uid_t uid, const char* pkgname) {
+bool analyse_profiles(uid_t uid, const std::string& pkgname) {
std::vector<fd_t> profiles_fd;
fd_t reference_profile_fd = -1;
open_profile_files(uid, pkgname, &profiles_fd, &reference_profile_fd);
@@ -627,8 +628,6 @@
return false;
}
- ALOGV("PROFMAN (MERGE): --- BEGIN '%s' ---\n", pkgname);
-
pid_t pid = fork();
if (pid == 0) {
/* child -- drop privileges before continuing */
@@ -739,12 +738,10 @@
}
}
-bool dump_profiles(int32_t uid, const char* pkgname, const char* code_paths) {
+bool dump_profiles(int32_t uid, const std::string& pkgname, const char* code_paths) {
std::vector<fd_t> profile_fds;
fd_t reference_profile_fd = -1;
- std::string out_file_name = StringPrintf("/data/misc/profman/%s.txt", pkgname);
-
- ALOGV("PROFMAN (DUMP): --- BEGIN '%s' ---\n", pkgname);
+ std::string out_file_name = StringPrintf("/data/misc/profman/%s.txt", pkgname.c_str());
open_profile_files(uid, pkgname, &profile_fds, &reference_profile_fd);
@@ -752,7 +749,7 @@
const bool has_profiles = !profile_fds.empty();
if (!has_reference_profile && !has_profiles) {
- ALOGE("profman dump: no profiles to dump for '%s'", pkgname);
+ LOG(ERROR) << "profman dump: no profiles to dump for " << pkgname;
return false;
}
@@ -853,13 +850,16 @@
return open(file_name, flags, permissions);
}
-static bool set_permissions_and_ownership(int fd, bool is_public, int uid, const char* path) {
+static bool set_permissions_and_ownership(
+ int fd, bool is_public, int uid, const char* path, bool is_secondary_dex) {
+ // Primary apks are owned by the system. Secondary dex files are owned by the app.
+ int owning_uid = is_secondary_dex ? uid : AID_SYSTEM;
if (fchmod(fd,
S_IRUSR|S_IWUSR|S_IRGRP |
(is_public ? S_IROTH : 0)) < 0) {
ALOGE("installd cannot chmod '%s' during dexopt\n", path);
return false;
- } else if (fchown(fd, AID_SYSTEM, uid) < 0) {
+ } else if (fchown(fd, owning_uid, uid) < 0) {
ALOGE("installd cannot chown '%s' during dexopt\n", path);
return false;
}
@@ -875,7 +875,7 @@
}
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"
@@ -886,7 +886,8 @@
}
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;
}
@@ -1007,10 +1008,11 @@
// (re)Creates the app image if needed.
Dex2oatFileWrapper maybe_open_app_image(const char* out_oat_path, bool profile_guided,
- bool is_public, int uid) {
+ bool is_public, int uid, bool is_secondary_dex) {
// Use app images only if it is enabled (by a set image format) and we are compiling
// profile-guided (so the app image doesn't conservatively contain all classes).
- if (!profile_guided) {
+ // Note that we don't create an image for secondary dex files.
+ if (is_secondary_dex || !profile_guided) {
return Dex2oatFileWrapper();
}
@@ -1041,7 +1043,7 @@
}
}
} else if (!set_permissions_and_ownership(
- wrapper_fd.get(), is_public, uid, image_path.c_str())) {
+ wrapper_fd.get(), is_public, uid, image_path.c_str(), is_secondary_dex)) {
ALOGE("installd cannot set owner '%s' for image during dexopt\n", image_path.c_str());
wrapper_fd.reset(-1);
}
@@ -1079,10 +1081,11 @@
// 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(
@@ -1098,8 +1101,8 @@
// Opens the vdex files and assigns the input fd to in_vdex_wrapper_fd and the output fd to
// out_vdex_wrapper_fd. Returns true for success or false in case of errors.
bool open_vdex_files(const char* apk_path, const char* out_oat_path, int dexopt_needed,
- const char* instruction_set, bool is_public, int uid,
- Dex2oatFileWrapper* in_vdex_wrapper_fd,
+ const char* instruction_set, bool is_public, bool profile_guided,
+ int uid, bool is_secondary_dex, Dex2oatFileWrapper* in_vdex_wrapper_fd,
Dex2oatFileWrapper* out_vdex_wrapper_fd) {
CHECK(in_vdex_wrapper_fd != nullptr);
CHECK(out_vdex_wrapper_fd != nullptr);
@@ -1109,7 +1112,9 @@
int dexopt_action = abs(dexopt_needed);
bool is_odex_location = dexopt_needed < 0;
std::string in_vdex_path_str;
- if (dexopt_action != DEX2OAT_FROM_SCRATCH) {
+ // Disable passing an input vdex when the compilation is profile-guided. The dexlayout
+ // optimization in dex2oat is incompatible with it. b/35872504.
+ if (dexopt_action != DEX2OAT_FROM_SCRATCH && !profile_guided) {
// Open the possibly existing vdex. If none exist, we pass -1 to dex2oat for input-vdex-fd.
const char* path = nullptr;
if (is_odex_location) {
@@ -1128,7 +1133,7 @@
return false;
}
if (dexopt_action == DEX2OAT_FOR_BOOT_IMAGE) {
- // When we dex2oat because iof boot image change, we are going to update
+ // When we dex2oat because of boot image change, we are going to update
// in-place the vdex file.
in_vdex_wrapper_fd->reset(open(in_vdex_path_str.c_str(), O_RDWR, 0));
} else {
@@ -1161,7 +1166,7 @@
}
}
if (!set_permissions_and_ownership(out_vdex_wrapper_fd->get(), is_public, uid,
- out_vdex_path_str.c_str())) {
+ out_vdex_path_str.c_str(), is_secondary_dex)) {
ALOGE("installd cannot set owner '%s' for vdex during dexopt\n", out_vdex_path_str.c_str());
return false;
}
@@ -1173,8 +1178,9 @@
// 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);
@@ -1182,8 +1188,9 @@
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);
- } else if (!set_permissions_and_ownership(wrapper_fd.get(), is_public, uid, 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, is_secondary_dex)) {
ALOGE("installd cannot set owner '%s' for output during dexopt\n", out_oat_path);
wrapper_fd.reset(-1);
}
@@ -1207,9 +1214,189 @@
}
}
-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) {
+// 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* dex_path, int uid,
+ const char* instruction_set, std::string* oat_dir_out) {
+ std::string apk_path_str(dex_path);
+ unsigned long dirIndex = apk_path_str.rfind('/');
+ if (dirIndex == std::string::npos) {
+ LOG(ERROR ) << "Unexpected dir structure for secondary dex " << dex_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: " << dex_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: " << dex_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* dex_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) << "Dexoptnalyzer return the status of an oat file."
+ << " Expected odex file status for secondary dex " << dex_path
+ << " : dexoptanalyzer result=" << result;
+ return false;
+ default:
+ LOG(ERROR) << "Unexpected result for dexoptanalyzer " << dex_path
+ << " exec_dexoptanalyzer result=" << result;
+ return false;
+ }
+}
+
+// Processes the dex_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* dex_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, dex_path, volume_uuid, uid, storage_flag)) {
+ LOG(ERROR) << "Could not validate secondary dex path " << dex_path;
+ return false;
+ }
+
+ // Check if the path exist. If not, there's nothing to do.
+ if (access(dex_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", dex_path);
+ return NO_DEXOPT_NEEDED;
+ } else {
+ PLOG(ERROR) << "Could not access secondary dex " << dex_path;
+ }
+ }
+
+ // Prepare the oat directories.
+ if (!prepare_secondary_dex_oat_dir(dex_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(dex_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 " << dex_path << ": " << result;
+ return false;
+ }
+ result = WEXITSTATUS(result);
+ bool success = process_dexoptanalyzer_result(dex_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* dex_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) {
CHECK(pkgname != nullptr);
CHECK(pkgname[0] != 0);
if ((dexopt_flags & ~DEXOPT_MASK) != 0) {
@@ -1221,18 +1408,38 @@
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(dex_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 {
+ // Currently these flags are only use for secondary dex files.
+ // Verify that they are not set for primary apks.
+ 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));
+ base::unique_fd input_fd(open(dex_path, O_RDONLY, 0));
if (input_fd.get() < 0) {
- ALOGE("installd cannot open '%s' for input during dexopt\n", apk_path);
+ ALOGE("installd cannot open '%s' for input during dexopt\n", dex_path);
return -1;
}
// 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);
+ Dex2oatFileWrapper out_oat_fd = open_oat_out_file(dex_path, oat_dir, is_public, uid,
+ instruction_set, is_secondary_dex, out_oat_path);
if (out_oat_fd.get() < 0) {
return -1;
}
@@ -1240,8 +1447,8 @@
// Open vdex files.
Dex2oatFileWrapper in_vdex_fd;
Dex2oatFileWrapper out_vdex_fd;
- if (!open_vdex_files(apk_path, out_oat_path, dexopt_needed, instruction_set, is_public, uid,
- &in_vdex_fd, &out_vdex_fd)) {
+ if (!open_vdex_files(dex_path, out_oat_path, dexopt_needed, instruction_set, is_public,
+ profile_guided, uid, is_secondary_dex, &in_vdex_fd, &out_vdex_fd)) {
return -1;
}
@@ -1250,13 +1457,13 @@
// Create the app image file if needed.
Dex2oatFileWrapper image_fd =
- maybe_open_app_image(out_oat_path, profile_guided, is_public, uid);
+ maybe_open_app_image(out_oat_path, profile_guided, is_public, uid, is_secondary_dex);
// 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);
+ ALOGV("DexInv: --- BEGIN '%s' ---\n", dex_path);
pid_t pid = fork();
if (pid == 0) {
@@ -1270,7 +1477,7 @@
}
// Pass dex2oat the relative path to the input file.
- const char *input_file_name = get_location_from_path(apk_path);
+ const char *input_file_name = get_location_from_path(dex_path);
run_dex2oat(input_fd.get(),
out_oat_fd.get(),
in_vdex_fd.get(),
@@ -1290,14 +1497,14 @@
} else {
int res = wait_child(pid);
if (res == 0) {
- ALOGV("DexInv: --- END '%s' (success) ---\n", apk_path);
+ ALOGV("DexInv: --- END '%s' (success) ---\n", dex_path);
} else {
- ALOGE("DexInv: --- END '%s' --- status=0x%04x, process failed\n", apk_path, res);
+ ALOGE("DexInv: --- END '%s' --- status=0x%04x, process failed\n", dex_path, res);
return -1;
}
}
- update_out_oat_access_times(apk_path, out_oat_path);
+ update_out_oat_access_times(dex_path, out_oat_path);
// We've been successful, don't delete output.
out_oat_fd.SetCleanup(false);
@@ -1308,6 +1515,115 @@
return 0;
}
+// Try to remove the given directory. Log an error if the directory exists
+// and is empty but could not be removed.
+static bool rmdir_if_empty(const char* dir) {
+ if (rmdir(dir) == 0) {
+ return true;
+ }
+ if (errno == ENOENT || errno == ENOTEMPTY) {
+ return true;
+ }
+ PLOG(ERROR) << "Failed to remove dir: " << dir;
+ return false;
+}
+
+// Try to unlink the given file. Log an error if the file exists and could not
+// be unlinked.
+static bool unlink_if_exists(const std::string& file) {
+ if (unlink(file.c_str()) == 0) {
+ return true;
+ }
+ if (errno == ENOENT) {
+ return true;
+
+ }
+ PLOG(ERROR) << "Could not unlink: " << file;
+ return false;
+}
+
+// Create the oat file structure for the secondary dex 'dex_path' and assign
+// the individual path component to the 'out_' parameters.
+static bool create_secondary_dex_oat_layout(const std::string& dex_path, const std::string& isa,
+ /*out*/char* out_oat_dir, /*out*/char* out_oat_isa_dir, /*out*/char* out_oat_path) {
+ size_t dirIndex = dex_path.rfind('/');
+ if (dirIndex == std::string::npos) {
+ LOG(ERROR) << "Unexpected dir structure for dex file " << dex_path;
+ return false;
+ }
+ // TODO(calin): we have similar computations in at lest 3 other places
+ // (InstalldNativeService, otapropt and dexopt). Unify them and get rid of snprintf by
+ // use string append.
+ std::string apk_dir = dex_path.substr(0, dirIndex);
+ snprintf(out_oat_dir, PKG_PATH_MAX, "%s/oat", apk_dir.c_str());
+ snprintf(out_oat_isa_dir, PKG_PATH_MAX, "%s/%s", out_oat_dir, isa.c_str());
+
+ if (!create_oat_out_path(dex_path.c_str(), isa.c_str(), out_oat_dir,
+ /*is_secondary_dex*/ true, out_oat_path)) {
+ LOG(ERROR) << "Could not create oat path for secondary dex " << dex_path;
+ return false;
+ }
+ return true;
+}
+
+// Reconcile the secondary dex 'dex_path' and its generated oat files.
+// Return true if all the parameters are valid and the secondary dex file was
+// processed successfully (i.e. the dex_path either exists, or if not, its corresponding
+// oat/vdex/art files where deleted successfully). In this case, out_secondary_dex_exists
+// will be true if the secondary dex file still exists. If the secondary dex file does not exist,
+// the method cleans up any previously generated compiler artifacts (oat, vdex, art).
+// Return false if there were errors during processing. In this case
+// out_secondary_dex_exists will be set to false.
+bool reconcile_secondary_dex_file(const std::string& dex_path,
+ const std::string& pkgname, int uid, const std::vector<std::string>& isas,
+ const std::unique_ptr<std::string>& volume_uuid, int storage_flag,
+ /*out*/bool* out_secondary_dex_exists) {
+ // Set out to false to start with, just in case we have validation errors.
+ *out_secondary_dex_exists = false;
+ if (isas.size() == 0) {
+ LOG(ERROR) << "reconcile_secondary_dex_file called with empty isas vector";
+ return false;
+ }
+
+ const char* volume_uuid_cstr = volume_uuid == nullptr ? nullptr : volume_uuid->c_str();
+ if (!validate_secondary_dex_path(pkgname.c_str(), dex_path.c_str(), volume_uuid_cstr,
+ uid, storage_flag)) {
+ LOG(ERROR) << "Could not validate secondary dex path " << dex_path;
+ return false;
+ }
+
+ if (access(dex_path.c_str(), F_OK) == 0) {
+ // The path exists, nothing to do. The odex files (if any) will be left untouched.
+ *out_secondary_dex_exists = true;
+ return true;
+ } else if (errno != ENOENT) {
+ PLOG(ERROR) << "Failed to check access to secondary dex " << dex_path;
+ return false;
+ }
+
+ // The secondary dex does not exist anymore. Clear any generated files.
+ char oat_path[PKG_PATH_MAX];
+ char oat_dir[PKG_PATH_MAX];
+ char oat_isa_dir[PKG_PATH_MAX];
+ bool result = true;
+ for (size_t i = 0; i < isas.size(); i++) {
+ if (!create_secondary_dex_oat_layout(dex_path, isas[i], oat_dir, oat_isa_dir, oat_path)) {
+ LOG(ERROR) << "Could not create secondary odex layout: " << dex_path;
+ result = false;
+ continue;
+ }
+ result = unlink_if_exists(oat_path) && result;
+ result = unlink_if_exists(create_vdex_filename(oat_path)) && result;
+ result = unlink_if_exists(create_image_filename(oat_path)) && result;
+
+ // Try removing the directories as well, they might be empty.
+ result = rmdir_if_empty(oat_isa_dir) && result;
+ result = rmdir_if_empty(oat_dir) && result;
+ }
+
+ return result;
+}
+
// Helper for move_ab, so that we can have common failure-case cleanup.
static bool unlink_and_rename(const char* from, const char* to) {
// Check whether "from" exists, and if so whether it's regular. If it is, unlink. Otherwise,
@@ -1439,7 +1755,8 @@
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;
}
diff --git a/cmds/installd/dexopt.h b/cmds/installd/dexopt.h
index 1115c78..fab4d94 100644
--- a/cmds/installd/dexopt.h
+++ b/cmds/installd/dexopt.h
@@ -25,6 +25,7 @@
namespace installd {
/* dexopt needed flags matching those in dalvik.system.DexFile */
+static constexpr int NO_DEXOPT_NEEDED = 0;
static constexpr int DEX2OAT_FROM_SCRATCH = 1;
static constexpr int DEX2OAT_FOR_BOOT_IMAGE = 2;
static constexpr int DEX2OAT_FOR_FILTER = 3;
@@ -33,17 +34,22 @@
typedef int fd_t;
-bool clear_reference_profile(const char* pkgname);
-bool clear_current_profile(const char* pkgname, userid_t user);
-bool clear_current_profiles(const char* pkgname);
+bool clear_reference_profile(const std::string& pkgname);
+bool clear_current_profile(const std::string& pkgname, userid_t user);
+bool clear_current_profiles(const std::string& pkgname);
bool move_ab(const char* apk_path, const char* instruction_set, const char* output_path);
-bool analyse_profiles(uid_t uid, const char* pkgname);
-bool dump_profiles(int32_t uid, const char* pkgname, const char* code_paths);
+bool analyse_profiles(uid_t uid, const std::string& pkgname);
+bool dump_profiles(int32_t uid, const std::string& pkgname, const char* code_paths);
bool delete_odex(const char* apk_path, const char* instruction_set, const char* output_path);
+bool reconcile_secondary_dex_file(const std::string& dex_path,
+ const std::string& pkgname, int uid, const std::vector<std::string>& isas,
+ const std::unique_ptr<std::string>& volumeUuid, int storage_flag,
+ /*out*/bool* out_secondary_dex_exists);
+
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, const char* shared_libraries);
diff --git a/cmds/installd/installd_constants.h b/cmds/installd/installd_constants.h
index 401e581..d8a754c 100644
--- a/cmds/installd/installd_constants.h
+++ b/cmds/installd/installd_constants.h
@@ -42,6 +42,14 @@
constexpr int DEXOPT_DEBUGGABLE = 1 << 3;
constexpr int DEXOPT_BOOTCOMPLETE = 1 << 4;
constexpr int DEXOPT_PROFILE_GUIDED = 1 << 5;
+constexpr int DEXOPT_SECONDARY_DEX = 1 << 6;
+// DEXOPT_FORCE, DEXOPT_STORAGE_CE, DEXOPT_STORAGE_DE are exposed for secondary
+// dex files only. Primary apks are analyzed in PackageManager and installd
+// does not need to know if the compilation is forced or on what kind of storage
+// the dex files are.
+constexpr int DEXOPT_FORCE = 1 << 7;
+constexpr int DEXOPT_STORAGE_CE = 1 << 8;
+constexpr int DEXOPT_STORAGE_DE = 1 << 9;
/* all known values for dexopt flags */
constexpr int DEXOPT_MASK =
@@ -49,7 +57,15 @@
| DEXOPT_SAFEMODE
| DEXOPT_DEBUGGABLE
| DEXOPT_BOOTCOMPLETE
- | DEXOPT_PROFILE_GUIDED;
+ | DEXOPT_PROFILE_GUIDED
+ | DEXOPT_SECONDARY_DEX
+ | DEXOPT_FORCE
+ | DEXOPT_STORAGE_CE
+ | DEXOPT_STORAGE_DE;
+
+// NOTE: keep in sync with StorageManager
+constexpr int FLAG_STORAGE_DE = 1 << 0;
+constexpr int FLAG_STORAGE_CE = 1 << 1;
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp
index 947cc0d..940046f 100644
--- a/cmds/installd/tests/installd_utils_test.cpp
+++ b/cmds/installd/tests/installd_utils_test.cpp
@@ -35,6 +35,8 @@
#define TEST_SYSTEM_DIR1 "/system/app/"
#define TEST_SYSTEM_DIR2 "/vendor/app/"
+#define TEST_PROFILE_DIR "/data/misc/profiles"
+
#define REALLY_LONG_APP_NAME "com.example." \
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." \
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." \
@@ -74,6 +76,9 @@
android_system_dirs.dirs[1].path = (char*) TEST_SYSTEM_DIR2;
android_system_dirs.dirs[1].len = strlen(TEST_SYSTEM_DIR2);
+
+ android_profiles_dir.path = (char*) TEST_PROFILE_DIR;
+ android_profiles_dir.len = strlen(TEST_PROFILE_DIR);
}
virtual void TearDown() {
@@ -317,6 +322,7 @@
size_t pkgnameSize = PKG_NAME_MAX;
char pkgname[pkgnameSize + 1];
memset(pkgname, 'a', pkgnameSize);
+ pkgname[1] = '.';
pkgname[pkgnameSize] = '\0';
EXPECT_EQ(0, create_pkg_path(path, pkgname, "", 0))
@@ -329,19 +335,6 @@
<< "Package path should be a really long string of a's";
}
-TEST_F(UtilsTest, CreatePkgPath_LongPkgNameFail) {
- char path[PKG_PATH_MAX];
-
- // Create long packagename of "aaaaa..."
- size_t pkgnameSize = PKG_NAME_MAX + 1;
- char pkgname[pkgnameSize + 1];
- memset(pkgname, 'a', pkgnameSize);
- pkgname[pkgnameSize] = '\0';
-
- EXPECT_EQ(-1, create_pkg_path(path, pkgname, "", 0))
- << "Should return error because package name is too long.";
-}
-
TEST_F(UtilsTest, CreatePkgPath_LongPostfixFail) {
char path[PKG_PATH_MAX];
@@ -508,5 +501,50 @@
create_data_user_ce_package_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", 10, "com.example"));
}
+TEST_F(UtilsTest, IsValidPackageName) {
+ EXPECT_EQ(true, is_valid_package_name("android"));
+ EXPECT_EQ(true, is_valid_package_name("com.example"));
+ EXPECT_EQ(true, is_valid_package_name("com.example-1"));
+ EXPECT_EQ(true, is_valid_package_name("com.example-1024"));
+ EXPECT_EQ(true, is_valid_package_name("com.example.foo---KiJFj4a_tePVw95pSrjg=="));
+ EXPECT_EQ(true, is_valid_package_name("really_LONG.a1234.package_name"));
+
+ EXPECT_EQ(false, is_valid_package_name("1234.package"));
+ EXPECT_EQ(false, is_valid_package_name("com.1234.package"));
+ EXPECT_EQ(false, is_valid_package_name(""));
+ EXPECT_EQ(false, is_valid_package_name("."));
+ EXPECT_EQ(false, is_valid_package_name(".."));
+ EXPECT_EQ(false, is_valid_package_name("../"));
+ EXPECT_EQ(false, is_valid_package_name("com.example/../com.evil/"));
+ EXPECT_EQ(false, is_valid_package_name("com.example-1/../com.evil/"));
+ EXPECT_EQ(false, is_valid_package_name("/com.evil"));
+}
+
+TEST_F(UtilsTest, CreateDataUserProfilePath) {
+ EXPECT_EQ("/data/misc/profiles/cur/0", create_data_user_profile_path(0));
+ EXPECT_EQ("/data/misc/profiles/cur/1", create_data_user_profile_path(1));
+}
+
+TEST_F(UtilsTest, CreateDataUserProfilePackagePath) {
+ EXPECT_EQ("/data/misc/profiles/cur/0/com.example",
+ create_data_user_profile_package_path(0, "com.example"));
+ EXPECT_EQ("/data/misc/profiles/cur/1/com.example",
+ create_data_user_profile_package_path(1, "com.example"));
+}
+
+TEST_F(UtilsTest, CreateDataRefProfilePath) {
+ EXPECT_EQ("/data/misc/profiles/ref", create_data_ref_profile_path());
+}
+
+TEST_F(UtilsTest, CreateDataRefProfilePackagePath) {
+ EXPECT_EQ("/data/misc/profiles/ref/com.example",
+ create_data_ref_profile_package_path("com.example"));
+}
+
+TEST_F(UtilsTest, CreatePrimaryProfile) {
+ EXPECT_EQ("/data/misc/profiles/ref/com.example/primary.prof",
+ create_primary_profile("/data/misc/profiles/ref/com.example"));
+}
+
} // namespace installd
} // namespace android
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index 0b1cd7e..a6fa656 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -217,28 +217,24 @@
return StringPrintf("%s/cur/%u", android_profiles_dir.path, userid);
}
-std::string create_data_user_profile_package_path(userid_t user, const char* package_name) {
- check_package_name(package_name);
- return StringPrintf("%s/%s",create_data_user_profile_path(user).c_str(), package_name);
+std::string create_data_user_profile_package_path(userid_t user, const std::string& package_name) {
+ check_package_name(package_name.c_str());
+ return StringPrintf("%s/%s",create_data_user_profile_path(user).c_str(), package_name.c_str());
}
std::string create_data_ref_profile_path() {
return StringPrintf("%s/ref", android_profiles_dir.path);
}
-std::string create_data_ref_profile_package_path(const char* package_name) {
- check_package_name(package_name);
- return StringPrintf("%s/ref/%s", android_profiles_dir.path, package_name);
+std::string create_data_ref_profile_package_path(const std::string& package_name) {
+ check_package_name(package_name.c_str());
+ return StringPrintf("%s/ref/%s", android_profiles_dir.path, package_name.c_str());
}
std::string create_data_dalvik_cache_path() {
return "/data/dalvik-cache";
}
-std::string create_data_misc_foreign_dex_path(userid_t userid) {
- return StringPrintf("/data/misc/profiles/cur/%d/foreign-dex", userid);
-}
-
// Keep profile paths in sync with ActivityThread.
constexpr const char* PRIMARY_PROFILE_NAME = "primary.prof";
@@ -350,46 +346,42 @@
* 0 on success.
*/
bool is_valid_package_name(const std::string& packageName) {
- const char* pkgname = packageName.c_str();
- const char *x = pkgname;
- int alpha = -1;
+ // This logic is borrowed from PackageParser.java
+ bool hasSep = false;
+ bool front = true;
- if (strlen(pkgname) > PKG_NAME_MAX) {
+ auto it = packageName.begin();
+ for (; it != packageName.end() && *it != '-'; it++) {
+ char c = *it;
+ if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
+ front = false;
+ continue;
+ }
+ if (!front) {
+ if ((c >= '0' && c <= '9') || c == '_') {
+ continue;
+ }
+ }
+ if (c == '.') {
+ hasSep = true;
+ front = true;
+ continue;
+ }
+ LOG(WARNING) << "Bad package character " << c << " in " << packageName;
return false;
}
- while (*x) {
- if (isalnum(*x) || (*x == '_')) {
- /* alphanumeric or underscore are fine */
- } else if (*x == '.') {
- if ((x == pkgname) || (x[1] == '.') || (x[1] == 0)) {
- /* periods must not be first, last, or doubled */
- ALOGE("invalid package name '%s'\n", pkgname);
- return false;
- }
- } else if (*x == '-') {
- /* Suffix -X is fine to let versioning of packages.
- But whatever follows should be alphanumeric.*/
- alpha = 1;
- } else {
- /* anything not A-Z, a-z, 0-9, _, or . is invalid */
- ALOGE("invalid package name '%s'\n", pkgname);
- return false;
- }
-
- x++;
+ if (front) {
+ LOG(WARNING) << "Missing separator in " << packageName;
+ return false;
}
- if (alpha == 1) {
- // Skip current character
- x++;
- while (*x) {
- if (!isalnum(*x)) {
- ALOGE("invalid package name '%s' should include only numbers after -\n", pkgname);
- return false;
- }
- x++;
- }
+ for (; it != packageName.end(); it++) {
+ char c = *it;
+ if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) continue;
+ if ((c >= '0' && c <= '9') || c == '_' || c == '-' || c == '=') continue;
+ LOG(WARNING) << "Bad suffix character " << c << " in " << packageName;
+ return false;
}
return true;
@@ -1171,6 +1163,25 @@
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
@@ -1370,5 +1381,73 @@
}
}
+/**
+ * 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
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index 5e396c7..8090b18 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -95,13 +95,12 @@
std::string create_data_misc_legacy_path(userid_t userid);
std::string create_data_user_profile_path(userid_t userid);
-std::string create_data_user_profile_package_path(userid_t user, const char* package_name);
+std::string create_data_user_profile_package_path(userid_t user, const std::string& package_name);
std::string create_data_ref_profile_path();
-std::string create_data_ref_profile_package_path(const char* package_name);
+std::string create_data_ref_profile_package_path(const std::string& package_name);
std::string create_data_dalvik_cache_path();
-std::string create_data_misc_foreign_dex_path(userid_t userid);
std::string create_primary_profile(const std::string& profile_dir);
@@ -148,6 +147,8 @@
void finish_cache_collection(cache_t* cache);
int validate_system_app_path(const char* path);
+bool validate_secondary_dex_path(const char* pkgname, const char* path,
+ const char* volume_uuid, int uid, int storage_flag);
int get_path_from_env(dir_rec_t* rec, const char* var);
@@ -167,6 +168,9 @@
int wait_child(pid_t pid);
+int prepare_app_cache_dir(const std::string& parent, const char* name, mode_t target_mode,
+ uid_t uid, gid_t gid);
+
} // namespace installd
} // namespace android
diff --git a/cmds/lshal/Android.bp b/cmds/lshal/Android.bp
index 5aab35a..39e0ba3 100644
--- a/cmds/lshal/Android.bp
+++ b/cmds/lshal/Android.bp
@@ -18,8 +18,10 @@
"libbase",
"libutils",
"libhidlbase",
- "android.hidl.manager@1.0",
"libhidltransport",
+ "libhidl-gen-utils",
+ "libvintf",
+ "android.hidl.manager@1.0",
],
srcs: [
"Lshal.cpp"
diff --git a/cmds/lshal/Lshal.cpp b/cmds/lshal/Lshal.cpp
index 6fd9b21..8750147 100644
--- a/cmds/lshal/Lshal.cpp
+++ b/cmds/lshal/Lshal.cpp
@@ -28,6 +28,9 @@
#include <android-base/parseint.h>
#include <android/hidl/manager/1.0/IServiceManager.h>
#include <hidl/ServiceManagement.h>
+#include <hidl-util/FQName.h>
+#include <vintf/HalManifest.h>
+#include <vintf/parse_xml.h>
#include "Timeout.h"
@@ -58,12 +61,13 @@
return os.str();
}
-static std::pair<hidl_string, hidl_string> split(const hidl_string &s, char c) {
+template<typename String>
+static std::pair<String, String> splitFirst(const String &s, char c) {
const char *pos = strchr(s.c_str(), c);
if (pos == nullptr) {
return {s, {}};
}
- return {hidl_string(s.c_str(), pos - s.c_str()), hidl_string(pos + 1)};
+ return {String(s.c_str(), pos - s.c_str()), String(pos + 1)};
}
static std::vector<std::string> split(const std::string &s, char c) {
@@ -81,6 +85,14 @@
return components;
}
+static void replaceAll(std::string *s, char from, char to) {
+ for (size_t i = 0; i < s->size(); ++i) {
+ if (s->at(i) == from) {
+ s->at(i) = to;
+ }
+ }
+}
+
std::string getCmdline(pid_t pid) {
std::ifstream ifs("/proc/" + std::to_string(pid) + "/cmdline");
std::string cmdline;
@@ -147,22 +159,57 @@
return true;
}
+void Lshal::forEachTable(const std::function<void(Table &)> &f) {
+ f(mServicesTable);
+ f(mPassthroughRefTable);
+ f(mImplementationsTable);
+}
+void Lshal::forEachTable(const std::function<void(const Table &)> &f) const {
+ f(mServicesTable);
+ f(mPassthroughRefTable);
+ f(mImplementationsTable);
+}
+
void Lshal::postprocess() {
- if (mSortColumn) {
- std::sort(mTable.begin(), mTable.end(), mSortColumn);
- }
- for (TableEntry &entry : mTable) {
- entry.serverCmdline = getCmdline(entry.serverPid);
- removeDeadProcesses(&entry.clientPids);
- for (auto pid : entry.clientPids) {
- entry.clientCmdlines.push_back(this->getCmdline(pid));
+ forEachTable([this](Table &table) {
+ if (mSortColumn) {
+ std::sort(table.begin(), table.end(), mSortColumn);
+ }
+ for (TableEntry &entry : table) {
+ entry.serverCmdline = getCmdline(entry.serverPid);
+ removeDeadProcesses(&entry.clientPids);
+ for (auto pid : entry.clientPids) {
+ entry.clientCmdlines.push_back(this->getCmdline(pid));
+ }
+ }
+ });
+ // use a double for loop here because lshal doesn't care about efficiency.
+ for (TableEntry &packageEntry : mImplementationsTable) {
+ std::string packageName = packageEntry.interfaceName;
+ FQName fqPackageName{packageName.substr(0, packageName.find("::"))};
+ if (!fqPackageName.isValid()) {
+ continue;
+ }
+ for (TableEntry &interfaceEntry : mPassthroughRefTable) {
+ if (interfaceEntry.arch != ARCH_UNKNOWN) {
+ continue;
+ }
+ FQName interfaceName{splitFirst(interfaceEntry.interfaceName, '/').first};
+ if (!interfaceName.isValid()) {
+ continue;
+ }
+ if (interfaceName.getPackageAndVersion() == fqPackageName) {
+ interfaceEntry.arch = packageEntry.arch;
+ }
}
}
}
void Lshal::printLine(
const std::string &interfaceName,
- const std::string &transport, const std::string &server,
+ const std::string &transport,
+ const std::string &arch,
+ const std::string &server,
const std::string &serverCmdline,
const std::string &address, const std::string &clients,
const std::string &clientCmdlines) const {
@@ -170,6 +217,8 @@
mOut << std::setw(80) << interfaceName << "\t";
if (mSelectedColumns & ENABLE_TRANSPORT)
mOut << std::setw(10) << transport << "\t";
+ if (mSelectedColumns & ENABLE_ARCH)
+ mOut << std::setw(5) << arch << "\t";
if (mSelectedColumns & ENABLE_SERVER_PID) {
if (mEnableCmdlines) {
mOut << std::setw(15) << serverCmdline << "\t";
@@ -189,38 +238,207 @@
mOut << std::endl;
}
-void Lshal::dump() const {
- mOut << "All services:" << std::endl;
- mOut << std::left;
- printLine("Interface", "Transport", "Server", "Server CMD", "PTR", "Clients", "Clients CMD");
- for (const auto &entry : mTable) {
- printLine(entry.interfaceName,
- entry.transport,
- entry.serverPid == NO_PID ? "N/A" : std::to_string(entry.serverPid),
- entry.serverCmdline,
- entry.serverObjectAddress == NO_PTR ? "N/A" : toHexString(entry.serverObjectAddress),
- join(entry.clientPids, " "),
- join(entry.clientCmdlines, ";"));
+void Lshal::dumpVintf() const {
+ vintf::HalManifest manifest;
+ forEachTable([this, &manifest] (const Table &table) {
+ for (const TableEntry &entry : table) {
+
+ std::string fqInstanceName = entry.interfaceName;
+
+ if (&table == &mImplementationsTable) {
+ // Quick hack to work around *'s
+ replaceAll(&fqInstanceName, '*', 'D');
+ }
+ auto splittedFqInstanceName = splitFirst(fqInstanceName, '/');
+ FQName fqName(splittedFqInstanceName.first);
+ if (!fqName.isValid()) {
+ mErr << "Warning: '" << splittedFqInstanceName.first
+ << "' is not a valid FQName." << std::endl;
+ continue;
+ }
+ // Strip out system libs.
+ // TODO(b/34772739): might want to add other framework HAL packages
+ if (fqName.inPackage("android.hidl")) {
+ continue;
+ }
+ std::string interfaceName =
+ &table == &mImplementationsTable ? "" : fqName.name();
+ std::string instanceName =
+ &table == &mImplementationsTable ? "" : splittedFqInstanceName.second;
+
+ vintf::Transport transport;
+ vintf::Arch arch;
+ if (entry.transport == "hwbinder") {
+ transport = vintf::Transport::HWBINDER;
+ arch = vintf::Arch::ARCH_EMPTY;
+ } else if (entry.transport == "passthrough") {
+ transport = vintf::Transport::PASSTHROUGH;
+ switch (entry.arch) {
+ case lshal::ARCH32:
+ arch = vintf::Arch::ARCH_32; break;
+ case lshal::ARCH64:
+ arch = vintf::Arch::ARCH_64; break;
+ case lshal::ARCH_BOTH:
+ arch = vintf::Arch::ARCH_32_64; break;
+ case lshal::ARCH_UNKNOWN: // fallthrough
+ default:
+ mErr << "Warning: '" << fqName.package()
+ << "' doesn't have bitness info, assuming 32+64." << std::endl;
+ arch = vintf::Arch::ARCH_32_64;
+ }
+ } else {
+ mErr << "Warning: '" << entry.transport << "' is not a valid transport." << std::endl;
+ continue;
+ }
+
+ vintf::ManifestHal *hal = manifest.getHal(fqName.package());
+ if (hal == nullptr) {
+ if (!manifest.add(vintf::ManifestHal{
+ .format = vintf::HalFormat::HIDL,
+ .name = fqName.package(),
+ .impl = {.implLevel = vintf::ImplLevel::GENERIC, .impl = ""},
+ .transportArch = {transport, arch}
+ })) {
+ mErr << "Warning: cannot add hal '" << fqInstanceName << "'" << std::endl;
+ continue;
+ }
+ hal = manifest.getHal(fqName.package());
+ }
+ if (hal == nullptr) {
+ mErr << "Warning: cannot get hal '" << fqInstanceName
+ << "' after adding it" << std::endl;
+ continue;
+ }
+ vintf::Version version{fqName.getPackageMajorVersion(), fqName.getPackageMinorVersion()};
+ if (std::find(hal->versions.begin(), hal->versions.end(), version) == hal->versions.end()) {
+ hal->versions.push_back(version);
+ }
+ if (&table != &mImplementationsTable) {
+ auto it = hal->interfaces.find(interfaceName);
+ if (it == hal->interfaces.end()) {
+ hal->interfaces.insert({interfaceName, {interfaceName, {{instanceName}}}});
+ } else {
+ it->second.instances.insert(instanceName);
+ }
+ }
+ }
+ });
+ mOut << vintf::gHalManifestConverter(manifest);
+}
+
+static const std::string &getArchString(Architecture arch) {
+ static const std::string sStr64 = "64";
+ static const std::string sStr32 = "32";
+ static const std::string sStrBoth = "32+64";
+ static const std::string sStrUnknown = "";
+ switch (arch) {
+ case ARCH64:
+ return sStr64;
+ case ARCH32:
+ return sStr32;
+ case ARCH_BOTH:
+ return sStrBoth;
+ case ARCH_UNKNOWN: // fall through
+ default:
+ return sStrUnknown;
}
}
-void Lshal::putEntry(TableEntry &&entry) {
- mTable.push_back(std::forward<TableEntry>(entry));
+static Architecture fromBaseArchitecture(::android::hidl::base::V1_0::DebugInfo::Architecture a) {
+ switch (a) {
+ case ::android::hidl::base::V1_0::DebugInfo::Architecture::IS_64BIT:
+ return ARCH64;
+ case ::android::hidl::base::V1_0::DebugInfo::Architecture::IS_32BIT:
+ return ARCH32;
+ case ::android::hidl::base::V1_0::DebugInfo::Architecture::UNKNOWN: // fallthrough
+ default:
+ return ARCH_UNKNOWN;
+ }
+}
+
+void Lshal::dumpTable() {
+ mServicesTable.description =
+ "All binderized services (registered services through hwservicemanager)";
+ mPassthroughRefTable.description =
+ "All interfaces that getService() has ever return as a passthrough interface;\n"
+ "PIDs / processes shown below might be inaccurate because the process\n"
+ "might have relinquished the interface or might have died.\n"
+ "The Server / Server CMD column can be ignored.\n"
+ "The Clients / Clients CMD column shows all process that have ever dlopen'ed \n"
+ "the library and successfully fetched the passthrough implementation.";
+ mImplementationsTable.description =
+ "All available passthrough implementations (all -impl.so files)";
+ forEachTable([this] (const Table &table) {
+ mOut << table.description << std::endl;
+ mOut << std::left;
+ printLine("Interface", "Transport", "Arch", "Server", "Server CMD",
+ "PTR", "Clients", "Clients CMD");
+ for (const auto &entry : table) {
+ printLine(entry.interfaceName,
+ entry.transport,
+ getArchString(entry.arch),
+ entry.serverPid == NO_PID ? "N/A" : std::to_string(entry.serverPid),
+ entry.serverCmdline,
+ entry.serverObjectAddress == NO_PTR ? "N/A" : toHexString(entry.serverObjectAddress),
+ join(entry.clientPids, " "),
+ join(entry.clientCmdlines, ";"));
+ }
+ mOut << std::endl;
+ });
+
+}
+
+void Lshal::dump() {
+ if (mVintf) {
+ dumpVintf();
+ if (!!mFileOutput) {
+ mFileOutput.buf().close();
+ delete &mFileOutput.buf();
+ mFileOutput = nullptr;
+ }
+ mOut = std::cout;
+ } else {
+ dumpTable();
+ }
+}
+
+void Lshal::putEntry(TableEntrySource source, TableEntry &&entry) {
+ Table *table = nullptr;
+ switch (source) {
+ case HWSERVICEMANAGER_LIST :
+ table = &mServicesTable; break;
+ case PTSERVICEMANAGER_REG_CLIENT :
+ table = &mPassthroughRefTable; break;
+ case LIST_DLLIB :
+ table = &mImplementationsTable; break;
+ default:
+ mErr << "Error: Unknown source of entry " << source << std::endl;
+ }
+ if (table) {
+ table->entries.push_back(std::forward<TableEntry>(entry));
+ }
}
Status Lshal::fetchAllLibraries(const sp<IServiceManager> &manager) {
using namespace ::android::hardware;
using namespace ::android::hidl::manager::V1_0;
using namespace ::android::hidl::base::V1_0;
- auto ret = timeoutIPC(manager, &IServiceManager::list, [&] (const auto &fqInstanceNames) {
- for (const auto &fqInstanceName : fqInstanceNames) {
- putEntry({
- .interfaceName = fqInstanceName,
+ auto ret = timeoutIPC(manager, &IServiceManager::debugDump, [&] (const auto &infos) {
+ std::map<std::string, TableEntry> entries;
+ for (const auto &info : infos) {
+ std::string interfaceName = std::string{info.interfaceName.c_str()} + "/" +
+ std::string{info.instanceName.c_str()};
+ entries.emplace(interfaceName, TableEntry{
+ .interfaceName = interfaceName,
.transport = "passthrough",
.serverPid = NO_PID,
.serverObjectAddress = NO_PTR,
- .clientPids = {}
- });
+ .clientPids = {},
+ .arch = ARCH_UNKNOWN
+ }).first->second.arch |= fromBaseArchitecture(info.arch);
+ }
+ for (auto &&pair : entries) {
+ putEntry(LIST_DLLIB, std::move(pair.second));
}
});
if (!ret.isOk()) {
@@ -233,18 +451,20 @@
Status Lshal::fetchPassthrough(const sp<IServiceManager> &manager) {
using namespace ::android::hardware;
+ using namespace ::android::hardware::details;
using namespace ::android::hidl::manager::V1_0;
using namespace ::android::hidl::base::V1_0;
auto ret = timeoutIPC(manager, &IServiceManager::debugDump, [&] (const auto &infos) {
for (const auto &info : infos) {
- putEntry({
+ putEntry(PTSERVICEMANAGER_REG_CLIENT, {
.interfaceName =
std::string{info.interfaceName.c_str()} + "/" +
std::string{info.instanceName.c_str()},
.transport = "passthrough",
.serverPid = info.clientPids.size() == 1 ? info.clientPids[0] : NO_PID,
.serverObjectAddress = NO_PTR,
- .clientPids = info.clientPids
+ .clientPids = info.clientPids,
+ .arch = fromBaseArchitecture(info.arch)
});
}
});
@@ -262,79 +482,85 @@
using namespace ::android::hidl::manager::V1_0;
using namespace ::android::hidl::base::V1_0;
const std::string mode = "hwbinder";
- Status status = OK;
- auto listRet = timeoutIPC(manager, &IServiceManager::list, [&] (const auto &fqInstanceNames) {
- // server pid, .ptr value of binder object, child pids
- std::map<std::string, DebugInfo> allDebugInfos;
- std::map<pid_t, std::map<uint64_t, Pids>> allPids;
- for (const auto &fqInstanceName : fqInstanceNames) {
- const auto pair = split(fqInstanceName, '/');
- const auto &serviceName = pair.first;
- const auto &instanceName = pair.second;
- auto getRet = timeoutIPC(manager, &IServiceManager::get, serviceName, instanceName);
- if (!getRet.isOk()) {
- mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
- << "cannot be fetched from service manager:"
- << getRet.description() << std::endl;
- status |= DUMP_BINDERIZED_ERROR;
- continue;
- }
- sp<IBase> service = getRet;
- if (service == nullptr) {
- mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
- << "cannot be fetched from service manager (null)";
- status |= DUMP_BINDERIZED_ERROR;
- continue;
- }
- auto debugRet = timeoutIPC(service, &IBase::getDebugInfo, [&] (const auto &debugInfo) {
- allDebugInfos[fqInstanceName] = debugInfo;
- if (debugInfo.pid >= 0) {
- allPids[static_cast<pid_t>(debugInfo.pid)].clear();
- }
- });
- if (!debugRet.isOk()) {
- mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
- << "debugging information cannot be retrieved:"
- << debugRet.description() << std::endl;
- status |= DUMP_BINDERIZED_ERROR;
- }
- }
- for (auto &pair : allPids) {
- pid_t serverPid = pair.first;
- if (!getReferencedPids(serverPid, &allPids[serverPid])) {
- mErr << "Warning: no information for PID " << serverPid
- << ", are you root?" << std::endl;
- status |= DUMP_BINDERIZED_ERROR;
- }
- }
- for (const auto &fqInstanceName : fqInstanceNames) {
- auto it = allDebugInfos.find(fqInstanceName);
- if (it == allDebugInfos.end()) {
- putEntry({
- .interfaceName = fqInstanceName,
- .transport = mode,
- .serverPid = NO_PID,
- .serverObjectAddress = NO_PTR,
- .clientPids = {}
- });
- continue;
- }
- const DebugInfo &info = it->second;
- putEntry({
- .interfaceName = fqInstanceName,
- .transport = mode,
- .serverPid = info.pid,
- .serverObjectAddress = info.ptr,
- .clientPids = info.pid == NO_PID || info.ptr == NO_PTR
- ? Pids{} : allPids[info.pid][info.ptr]
- });
- }
+ hidl_vec<hidl_string> fqInstanceNames;
+ // copying out for timeoutIPC
+ auto listRet = timeoutIPC(manager, &IServiceManager::list, [&] (const auto &names) {
+ fqInstanceNames = names;
});
if (!listRet.isOk()) {
mErr << "Error: Failed to list services for " << mode << ": "
<< listRet.description() << std::endl;
- status |= DUMP_BINDERIZED_ERROR;
+ return DUMP_BINDERIZED_ERROR;
+ }
+
+ Status status = OK;
+ // server pid, .ptr value of binder object, child pids
+ std::map<std::string, DebugInfo> allDebugInfos;
+ std::map<pid_t, std::map<uint64_t, Pids>> allPids;
+ for (const auto &fqInstanceName : fqInstanceNames) {
+ const auto pair = splitFirst(fqInstanceName, '/');
+ const auto &serviceName = pair.first;
+ const auto &instanceName = pair.second;
+ auto getRet = timeoutIPC(manager, &IServiceManager::get, serviceName, instanceName);
+ if (!getRet.isOk()) {
+ mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
+ << "cannot be fetched from service manager:"
+ << getRet.description() << std::endl;
+ status |= DUMP_BINDERIZED_ERROR;
+ continue;
+ }
+ sp<IBase> service = getRet;
+ if (service == nullptr) {
+ mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
+ << "cannot be fetched from service manager (null)";
+ status |= DUMP_BINDERIZED_ERROR;
+ continue;
+ }
+ auto debugRet = timeoutIPC(service, &IBase::getDebugInfo, [&] (const auto &debugInfo) {
+ allDebugInfos[fqInstanceName] = debugInfo;
+ if (debugInfo.pid >= 0) {
+ allPids[static_cast<pid_t>(debugInfo.pid)].clear();
+ }
+ });
+ if (!debugRet.isOk()) {
+ mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
+ << "debugging information cannot be retrieved:"
+ << debugRet.description() << std::endl;
+ status |= DUMP_BINDERIZED_ERROR;
+ }
+ }
+ for (auto &pair : allPids) {
+ pid_t serverPid = pair.first;
+ if (!getReferencedPids(serverPid, &allPids[serverPid])) {
+ mErr << "Warning: no information for PID " << serverPid
+ << ", are you root?" << std::endl;
+ status |= DUMP_BINDERIZED_ERROR;
+ }
+ }
+ for (const auto &fqInstanceName : fqInstanceNames) {
+ auto it = allDebugInfos.find(fqInstanceName);
+ if (it == allDebugInfos.end()) {
+ putEntry(HWSERVICEMANAGER_LIST, {
+ .interfaceName = fqInstanceName,
+ .transport = mode,
+ .serverPid = NO_PID,
+ .serverObjectAddress = NO_PTR,
+ .clientPids = {},
+ .arch = ARCH_UNKNOWN
+ });
+ continue;
+ }
+ const DebugInfo &info = it->second;
+ putEntry(HWSERVICEMANAGER_LIST, {
+ .interfaceName = fqInstanceName,
+ .transport = mode,
+ .serverPid = info.pid,
+ .serverObjectAddress = info.ptr,
+ .clientPids = info.pid == NO_PID || info.ptr == NO_PTR
+ ? Pids{} : allPids[info.pid][info.ptr],
+ .arch = fromBaseArchitecture(info.arch),
+ });
}
return status;
}
@@ -364,13 +590,14 @@
void Lshal::usage() const {
mErr
<< "usage: lshal" << std::endl
- << " Dump all hals with default ordering and columns [-itpc]." << std::endl
- << " lshal [--interface|-i] [--transport|-t]" << std::endl
+ << " Dump all hals with default ordering and columns [-ipc]." << std::endl
+ << " lshal [--interface|-i] [--transport|-t] [-r|--arch]" << std::endl
<< " [--pid|-p] [--address|-a] [--clients|-c] [--cmdline|-m]" << std::endl
- << " [--sort={interface|i|pid|p}]" << std::endl
+ << " [--sort={interface|i|pid|p}] [--init-vintf[=path]]" << std::endl
<< " -i, --interface: print the interface name column" << std::endl
<< " -n, --instance: print the instance name column" << std::endl
<< " -t, --transport: print the transport mode column" << std::endl
+ << " -r, --arch: print if the HAL is in 64-bit or 32-bit" << std::endl
<< " -p, --pid: print the server PID, or server cmdline if -m is set" << std::endl
<< " -a, --address: print the server object address column" << std::endl
<< " -c, --clients: print the client PIDs, or client cmdlines if -m is set"
@@ -378,6 +605,8 @@
<< " -m, --cmdline: print cmdline instead of PIDs" << std::endl
<< " --sort=i, --sort=interface: sort by interface name" << std::endl
<< " --sort=p, --sort=pid: sort by server pid" << std::endl
+ << " --init-vintf=path: form a skeleton HAL manifest to specified file " << std::endl
+ << " (stdout if no file specified)" << std::endl
<< " lshal [-h|--help]" << std::endl
<< " -h, --help: show this help information." << std::endl;
}
@@ -388,6 +617,7 @@
{"help", no_argument, 0, 'h' },
{"interface", no_argument, 0, 'i' },
{"transport", no_argument, 0, 't' },
+ {"arch", no_argument, 0, 'r' },
{"pid", no_argument, 0, 'p' },
{"address", no_argument, 0, 'a' },
{"clients", no_argument, 0, 'c' },
@@ -395,6 +625,7 @@
// long options without short alternatives
{"sort", required_argument, 0, 's' },
+ {"init-vintf",optional_argument, 0, 'v' },
{ 0, 0, 0, 0 }
};
@@ -403,7 +634,7 @@
optind = 1;
for (;;) {
// using getopt_long in case we want to add other options in the future
- c = getopt_long(argc, argv, "hitpacm", longOptions, &optionIndex);
+ c = getopt_long(argc, argv, "hitrpacm", longOptions, &optionIndex);
if (c == -1) {
break;
}
@@ -420,6 +651,17 @@
}
break;
}
+ case 'v': {
+ if (optarg) {
+ mFileOutput = new std::ofstream{optarg};
+ mOut = mFileOutput;
+ if (!mFileOutput.buf().is_open()) {
+ mErr << "Could not open file '" << optarg << "'." << std::endl;
+ return IO_ERROR;
+ }
+ }
+ mVintf = true;
+ }
case 'i': {
mSelectedColumns |= ENABLE_INTERFACE_NAME;
break;
@@ -428,6 +670,10 @@
mSelectedColumns |= ENABLE_TRANSPORT;
break;
}
+ case 'r': {
+ mSelectedColumns |= ENABLE_ARCH;
+ break;
+ }
case 'p': {
mSelectedColumns |= ENABLE_SERVER_PID;
break;
@@ -452,8 +698,7 @@
}
if (mSelectedColumns == 0) {
- mSelectedColumns = ENABLE_INTERFACE_NAME
- | ENABLE_TRANSPORT | ENABLE_SERVER_PID | ENABLE_CLIENT_PIDS;
+ mSelectedColumns = ENABLE_INTERFACE_NAME | ENABLE_SERVER_PID | ENABLE_CLIENT_PIDS;
}
return OK;
}
diff --git a/cmds/lshal/Lshal.h b/cmds/lshal/Lshal.h
index ead99dc..c9c6660 100644
--- a/cmds/lshal/Lshal.h
+++ b/cmds/lshal/Lshal.h
@@ -19,12 +19,13 @@
#include <stdint.h>
-#include <iostream>
+#include <fstream>
#include <string>
#include <vector>
#include <android/hidl/manager/1.0/IServiceManager.h>
+#include "NullableOStream.h"
#include "TableEntry.h"
namespace android {
@@ -38,6 +39,7 @@
DUMP_BINDERIZED_ERROR = 1 << 3,
DUMP_PASSTHROUGH_ERROR = 1 << 4,
DUMP_ALL_LIBS_ERROR = 1 << 5,
+ IO_ERROR = 1 << 6,
};
using Status = unsigned int;
@@ -49,17 +51,21 @@
Status parseArgs(int argc, char **argv);
Status fetch();
void postprocess();
- void dump() const;
+ void dump();
void usage() const;
- void putEntry(TableEntry &&entry);
+ void putEntry(TableEntrySource source, TableEntry &&entry);
Status fetchPassthrough(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager);
Status fetchBinderized(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager);
Status fetchAllLibraries(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager);
bool getReferencedPids(
pid_t serverPid, std::map<uint64_t, Pids> *objects) const;
+ void dumpTable();
+ void dumpVintf() const;
void printLine(
const std::string &interfaceName,
- const std::string &transport, const std::string &server,
+ const std::string &transport,
+ const std::string &arch,
+ const std::string &server,
const std::string &serverCmdline,
const std::string &address, const std::string &clients,
const std::string &clientCmdlines) const ;
@@ -68,14 +74,21 @@
// Call getCmdline on all pid in pids. If it returns empty string, the process might
// have died, and the pid is removed from pids.
void removeDeadProcesses(Pids *pids);
+ void forEachTable(const std::function<void(Table &)> &f);
+ void forEachTable(const std::function<void(const Table &)> &f) const;
- Table mTable{};
- std::ostream &mErr = std::cerr;
- std::ostream &mOut = std::cout;
+ Table mServicesTable{};
+ Table mPassthroughRefTable{};
+ Table mImplementationsTable{};
+
+ NullableOStream<std::ostream> mErr = std::cerr;
+ NullableOStream<std::ostream> mOut = std::cout;
+ NullableOStream<std::ofstream> mFileOutput = nullptr;
TableEntryCompare mSortColumn = nullptr;
TableEntrySelect mSelectedColumns = 0;
// If true, cmdlines will be printed instead of pid.
- bool mEnableCmdlines;
+ bool mEnableCmdlines = false;
+ bool mVintf = false;
// If an entry does not exist, need to ask /proc/{pid}/cmdline to get it.
// If an entry exist but is an empty string, process might have died.
// If an entry exist and not empty, it contains the cached content of /proc/{pid}/cmdline.
diff --git a/cmds/lshal/NullableOStream.h b/cmds/lshal/NullableOStream.h
new file mode 100644
index 0000000..ab37a04
--- /dev/null
+++ b/cmds/lshal/NullableOStream.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef FRAMEWORK_NATIVE_CMDS_LSHAL_NULLABLE_O_STREAM_H_
+#define FRAMEWORK_NATIVE_CMDS_LSHAL_NULLABLE_O_STREAM_H_
+
+#include <iostream>
+
+namespace android {
+namespace lshal {
+
+template<typename S>
+class NullableOStream {
+public:
+ NullableOStream(S &os) : mOs(&os) {}
+ NullableOStream(S *os) : mOs(os) {}
+ NullableOStream &operator=(S &os) {
+ mOs = &os;
+ return *this;
+ }
+ NullableOStream &operator=(S *os) {
+ mOs = os;
+ return *this;
+ }
+ template<typename Other>
+ NullableOStream &operator=(const NullableOStream<Other> &other) {
+ mOs = other.mOs;
+ return *this;
+ }
+
+ const NullableOStream &operator<<(std::ostream& (*pf)(std::ostream&)) const {
+ if (mOs) {
+ (*mOs) << pf;
+ }
+ return *this;
+ }
+ template<typename T>
+ const NullableOStream &operator<<(const T &rhs) const {
+ if (mOs) {
+ (*mOs) << rhs;
+ }
+ return *this;
+ }
+ S& buf() const {
+ return *mOs;
+ }
+ operator bool() const {
+ return mOs != nullptr;
+ }
+private:
+ template<typename>
+ friend class NullableOStream;
+
+ S *mOs = nullptr;
+};
+
+} // namespace lshal
+} // namespace android
+
+#endif // FRAMEWORK_NATIVE_CMDS_LSHAL_NULLABLE_O_STREAM_H_
diff --git a/cmds/lshal/TableEntry.h b/cmds/lshal/TableEntry.h
index 4ec3a0c..9ae8f78 100644
--- a/cmds/lshal/TableEntry.h
+++ b/cmds/lshal/TableEntry.h
@@ -28,6 +28,21 @@
using Pids = std::vector<int32_t>;
+enum : unsigned int {
+ HWSERVICEMANAGER_LIST, // through defaultServiceManager()->list()
+ PTSERVICEMANAGER_REG_CLIENT, // through registerPassthroughClient
+ LIST_DLLIB, // through listing dynamic libraries
+};
+using TableEntrySource = unsigned int;
+
+enum : unsigned int {
+ ARCH_UNKNOWN = 0,
+ ARCH32 = 1 << 0,
+ ARCH64 = 1 << 1,
+ ARCH_BOTH = ARCH32 | ARCH64
+};
+using Architecture = unsigned int;
+
struct TableEntry {
std::string interfaceName;
std::string transport;
@@ -36,6 +51,7 @@
uint64_t serverObjectAddress;
Pids clientPids;
std::vector<std::string> clientCmdlines;
+ Architecture arch;
static bool sortByInterfaceName(const TableEntry &a, const TableEntry &b) {
return a.interfaceName < b.interfaceName;
@@ -45,7 +61,17 @@
};
};
-using Table = std::vector<TableEntry>;
+struct Table {
+ using Entries = std::vector<TableEntry>;
+ std::string description;
+ Entries entries;
+
+ Entries::iterator begin() { return entries.begin(); }
+ Entries::const_iterator begin() const { return entries.begin(); }
+ Entries::iterator end() { return entries.end(); }
+ Entries::const_iterator end() const { return entries.end(); }
+};
+
using TableEntryCompare = std::function<bool(const TableEntry &, const TableEntry &)>;
enum : unsigned int {
@@ -53,7 +79,8 @@
ENABLE_TRANSPORT = 1 << 1,
ENABLE_SERVER_PID = 1 << 2,
ENABLE_SERVER_ADDR = 1 << 3,
- ENABLE_CLIENT_PIDS = 1 << 4
+ ENABLE_CLIENT_PIDS = 1 << 4,
+ ENABLE_ARCH = 1 << 5
};
using TableEntrySelect = unsigned int;
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index d42bb82..5b70501 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -40,7 +40,7 @@
#include <sys/stat.h>
#include <sys/types.h>
-#define BINDER_VM_SIZE ((1*1024*1024) - (4096 *2))
+#define BINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2)
#define DEFAULT_MAX_BINDER_THREADS 15
// -------------------------------------------------------------------------