Merge "Add a callback thread to ConsumerBase" into nyc-dev
diff --git a/cmds/installd/commands.cpp b/cmds/installd/commands.cpp
index 2c5bb1c..e31c6e2 100644
--- a/cmds/installd/commands.cpp
+++ b/cmds/installd/commands.cpp
@@ -24,6 +24,7 @@
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <sys/wait.h>
#include <sys/xattr.h>
#include <unistd.h>
@@ -56,6 +57,17 @@
#define MIN_RESTRICTED_HOME_SDK_VERSION 24 // > M
+typedef int fd_t;
+
+static bool property_get_bool(const char* property_name, bool default_value = false) {
+ char tmp_property_value[kPropertyValueMax];
+ bool have_property = get_property(property_name, tmp_property_value, nullptr) > 0;
+ if (!have_property) {
+ return default_value;
+ }
+ return strcmp(tmp_property_value, "true") == 0;
+}
+
int create_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags,
appid_t appid, const char* seinfo, int target_sdk_version) {
uid_t uid = multiuser_get_uid(userid, appid);
@@ -83,6 +95,24 @@
// TODO: include result once 25796509 is fixed
return 0;
}
+
+ if (property_get_bool("dalvik.vm.usejitprofiles")) {
+ const std::string profile_path = create_data_user_profile_package_path(userid, pkgname);
+ // read-write-execute only for the app user.
+ if (fs_prepare_dir_strict(profile_path.c_str(), 0700, uid, uid) != 0) {
+ PLOG(ERROR) << "Failed to prepare " << profile_path;
+ return -1;
+ }
+ const std::string ref_profile_path = create_data_ref_profile_package_path(pkgname);
+ // dex2oat/profman runs under the shared app gid and it needs to read/write reference
+ // profiles.
+ appid_t shared_app_gid = multiuser_get_shared_app_gid(uid);
+ if (fs_prepare_dir_strict(
+ ref_profile_path.c_str(), 0700, shared_app_gid, shared_app_gid) != 0) {
+ PLOG(ERROR) << "Failed to prepare " << ref_profile_path;
+ return -1;
+ }
+ }
}
return 0;
}
@@ -125,6 +155,36 @@
return 0;
}
+// Keep profile paths in sync with ActivityThread.
+constexpr const char* PRIMARY_PROFILE_NAME = "primary.prof";
+static std::string create_primary_profile(const std::string& profile_dir) {
+ return StringPrintf("%s/%s", profile_dir.c_str(), PRIMARY_PROFILE_NAME);
+}
+
+static void unlink_reference_profile(const char* pkgname) {
+ std::string reference_profile_dir = create_data_ref_profile_package_path(pkgname);
+ std::string reference_profile = create_primary_profile(reference_profile_dir);
+ if (unlink(reference_profile.c_str()) != 0) {
+ PLOG(WARNING) << "Could not unlink " << reference_profile;
+ }
+}
+
+static void unlink_current_profiles(const char* pkgname) {
+ std::vector<userid_t> users = get_known_users(/*volume_uuid*/ nullptr);
+ for (auto user : users) {
+ std::string profile_dir = create_data_user_profile_package_path(user, pkgname);
+ std::string profile = create_primary_profile(profile_dir);
+ if (unlink(profile.c_str()) != 0) {
+ PLOG(WARNING) << "Could not unlink " << profile;
+ }
+ }
+}
+
+static void unlink_all_profiles(const char* pkgname) {
+ unlink_reference_profile(pkgname);
+ unlink_current_profiles(pkgname);
+}
+
int clear_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags) {
std::string suffix = "";
if (flags & FLAG_CLEAR_CACHE_ONLY) {
@@ -146,6 +206,7 @@
// TODO: include result once 25796509 is fixed
delete_dir_contents(path);
}
+ unlink_all_profiles(pkgname);
}
return res;
}
@@ -160,6 +221,7 @@
// TODO: include result once 25796509 is fixed
delete_dir_contents_and_dir(
create_data_user_de_package_path(uuid, userid, pkgname));
+ unlink_all_profiles(pkgname);
}
return res;
}
@@ -289,11 +351,13 @@
std::string data_path(create_data_user_path(uuid, userid));
std::string data_de_path(create_data_user_de_path(uuid, userid));
std::string media_path(create_data_media_path(uuid, userid));
+ std::string profiles_path(create_data_user_profiles_path(userid));
res |= delete_dir_contents_and_dir(data_path);
// TODO: include result once 25796509 is fixed
delete_dir_contents_and_dir(data_de_path);
res |= delete_dir_contents_and_dir(media_path);
+ res |= delete_dir_contents_and_dir(profiles_path);
// Config paths only exist on internal storage
if (uuid == nullptr) {
@@ -630,19 +694,10 @@
ALOGE("execv(%s) failed: %s\n", PATCHOAT_BIN, strerror(errno));
}
-static bool check_boolean_property(const char* property_name, bool default_value = false) {
- char tmp_property_value[kPropertyValueMax];
- bool have_property = get_property(property_name, tmp_property_value, nullptr) > 0;
- if (!have_property) {
- return default_value;
- }
- return strcmp(tmp_property_value, "true") == 0;
-}
-
static void run_dex2oat(int zip_fd, int oat_fd, int image_fd, const char* input_file_name,
- const char* output_file_name, int swap_fd, const char *instruction_set,
- bool vm_safe_mode, bool debuggable, bool post_bootcomplete, bool extract_only,
- const std::vector<int>& profile_files_fd, const std::vector<int>& reference_profile_files_fd) {
+ const char* output_file_name, int swap_fd, const char *instruction_set,
+ bool vm_safe_mode, bool debuggable, bool post_bootcomplete, bool extract_only,
+ int profile_fd) {
static const unsigned int MAX_INSTRUCTION_SET_LEN = 7;
if (strlen(instruction_set) >= MAX_INSTRUCTION_SET_LEN) {
@@ -651,12 +706,6 @@
return;
}
- if (profile_files_fd.size() != reference_profile_files_fd.size()) {
- ALOGE("Invalid configuration of profile files: pf_size (%zu) != rpf_size (%zu)",
- profile_files_fd.size(), reference_profile_files_fd.size());
- return;
- }
-
char dex2oat_Xms_flag[kPropertyValueMax];
bool have_dex2oat_Xms_flag = get_property("dalvik.vm.dex2oat-Xms", dex2oat_Xms_flag, NULL) > 0;
@@ -705,7 +754,7 @@
(strcmp(vold_decrypt, "trigger_restart_min_framework") == 0 ||
(strcmp(vold_decrypt, "1") == 0)));
- bool generate_debug_info = check_boolean_property("debug.generate-debug-info");
+ bool generate_debug_info = property_get_bool("debug.generate-debug-info");
char app_image_format[kPropertyValueMax];
char image_format_arg[strlen("--image-format=") + kPropertyValueMax];
@@ -779,18 +828,12 @@
(get_property("dalvik.vm.always_debuggable", prop_buf, "0") > 0) &&
(prop_buf[0] == '1');
}
- std::vector<std::string> profile_file_args(profile_files_fd.size());
- std::vector<std::string> reference_profile_file_args(profile_files_fd.size());
- // "reference-profile-file-fd" is longer than "profile-file-fd" so we can
- // use it to set the max length.
- char profile_buf[strlen("--reference-profile-file-fd=") + MAX_INT_LEN];
- for (size_t k = 0; k < profile_files_fd.size(); k++) {
- sprintf(profile_buf, "--profile-file-fd=%d", profile_files_fd[k]);
- profile_file_args[k].assign(profile_buf);
- sprintf(profile_buf, "--reference-profile-file-fd=%d", reference_profile_files_fd[k]);
- reference_profile_file_args[k].assign(profile_buf);
+ char profile_arg[strlen("--profile-file-fd=") + MAX_INT_LEN];
+ if (profile_fd != -1) {
+ sprintf(profile_arg, "--profile-file-fd=%d", profile_fd);
}
+
ALOGV("Running %s in=%s out=%s\n", DEX2OAT_BIN, input_file_name, output_file_name);
const char* argv[7 // program name, mandatory arguments and the final NULL
@@ -807,8 +850,7 @@
+ (debuggable ? 1 : 0)
+ (have_app_image_format ? 1 : 0)
+ dex2oat_flags_count
- + profile_files_fd.size()
- + reference_profile_files_fd.size()];
+ + (profile_fd == -1 ? 0 : 1)];
int i = 0;
argv[i++] = DEX2OAT_BIN;
argv[i++] = zip_fd_arg;
@@ -858,9 +900,8 @@
argv[i++] = RUNTIME_ARG;
argv[i++] = dex2oat_norelocation;
}
- for (size_t k = 0; k < profile_file_args.size(); k++) {
- argv[i++] = profile_file_args[k].c_str();
- argv[i++] = reference_profile_file_args[k].c_str();
+ if (profile_fd != -1) {
+ argv[i++] = profile_arg;
}
// Do not add after dex2oat_flags, they should override others for debugging.
argv[i] = NULL;
@@ -906,7 +947,7 @@
return true;
}
- bool is_low_mem = check_boolean_property("ro.config.low_ram");
+ bool is_low_mem = property_get_bool("ro.config.low_ram");
if (is_low_mem) {
return true;
}
@@ -928,10 +969,7 @@
}
}
-constexpr const char* PROFILE_FILE_EXTENSION = ".prof";
-constexpr const char* REFERENCE_PROFILE_FILE_EXTENSION = ".prof.ref";
-
-static void close_all_fds(const std::vector<int>& fds, const char* description) {
+static void close_all_fds(const std::vector<fd_t>& fds, const char* description) {
for (size_t i = 0; i < fds.size(); i++) {
if (close(fds[i]) != 0) {
PLOG(WARNING) << "Failed to close fd for " << description << " at index " << i;
@@ -939,92 +977,224 @@
}
}
-static int open_code_cache_for_user(userid_t user, const char* volume_uuid, const char* pkgname) {
- std::string code_cache_path =
- create_data_user_de_package_path(volume_uuid, user, pkgname) + CODE_CACHE_DIR_POSTFIX;
-
+static fd_t open_profile_dir(const std::string& profile_dir) {
struct stat buffer;
- // Check that the code cache exists. If not, return and don't log an error.
- if (TEMP_FAILURE_RETRY(lstat(code_cache_path.c_str(), &buffer)) == -1) {
+ if (TEMP_FAILURE_RETRY(lstat(profile_dir.c_str(), &buffer)) == -1) {
+ PLOG(ERROR) << "Failed to lstat profile_dir: " << profile_dir;
+ return -1;
+ }
+
+ fd_t profile_dir_fd = TEMP_FAILURE_RETRY(open(profile_dir.c_str(),
+ O_PATH | O_CLOEXEC | O_DIRECTORY | O_NOFOLLOW));
+ if (profile_dir_fd < 0) {
+ PLOG(ERROR) << "Failed to open profile_dir: " << profile_dir;
+ }
+ return profile_dir_fd;
+}
+
+static fd_t open_primary_profile_file_from_dir(const std::string& profile_dir, mode_t open_mode) {
+ fd_t profile_dir_fd = open_profile_dir(profile_dir);
+ if (profile_dir_fd < 0) {
+ return -1;
+ }
+
+ fd_t profile_fd = -1;
+ std::string profile_file = create_primary_profile(profile_dir);
+
+ profile_fd = TEMP_FAILURE_RETRY(open(profile_file.c_str(), open_mode | O_NOFOLLOW));
+ if (profile_fd == -1) {
+ // It's not an error if the profile file does not exist.
if (errno != ENOENT) {
- PLOG(ERROR) << "Failed to lstat code_cache: " << code_cache_path;
+ PLOG(ERROR) << "Failed to lstat profile_dir: " << profile_dir;
+ }
+ }
+ // TODO(calin): use AutoCloseFD instead of closing the fd manually.
+ if (close(profile_dir_fd) != 0) {
+ PLOG(WARNING) << "Could not close profile dir " << profile_dir;
+ }
+ return profile_fd;
+}
+
+static fd_t open_primary_profile_file(userid_t user, const char* 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) {
+ 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);
+ if (fd < 0) {
+ return -1;
+ }
+ if (read_write) {
+ // Fix the owner.
+ if (fchown(fd, uid, uid) < 0) {
+ close(fd);
return -1;
}
}
-
- int code_cache_fd = open(code_cache_path.c_str(),
- O_PATH | O_CLOEXEC | O_DIRECTORY | O_NOFOLLOW);
- if (code_cache_fd < 0) {
- PLOG(ERROR) << "Failed to open code_cache: " << code_cache_path;
- }
- return code_cache_fd;
+ return fd;
}
-// Keep profile paths in sync with ActivityThread.
-static void open_profile_files_for_user(uid_t uid, const char* pkgname, int code_cache_fd,
- /*out*/ int* profile_fd, /*out*/ int* reference_profile_fd) {
- *profile_fd = -1;
- *reference_profile_fd = -1;
- std::string profile_file(pkgname);
- profile_file += PROFILE_FILE_EXTENSION;
-
- // Check if the profile exists. If not, early return and don't log an error.
- struct stat buffer;
- if (TEMP_FAILURE_RETRY(fstatat(
- code_cache_fd, profile_file.c_str(), &buffer, AT_SYMLINK_NOFOLLOW)) == -1) {
- if (errno != ENOENT) {
- PLOG(ERROR) << "Failed to fstatat profile file: " << profile_file;
- return;
- }
- }
-
- // Open in read-write to allow transfer of information from the current profile
- // to the reference profile.
- *profile_fd = openat(code_cache_fd, profile_file.c_str(), O_RDWR | O_NOFOLLOW);
- if (*profile_fd < 0) {
- PLOG(ERROR) << "Failed to open profile file: " << profile_file;
- return;
- }
-
- std::string reference_profile(pkgname);
- reference_profile += REFERENCE_PROFILE_FILE_EXTENSION;
- // Give read-write permissions just for the user (changed with fchown after opening).
- // We need write permission because dex2oat will update the reference profile files
- // with the content of the corresponding current profile files.
- *reference_profile_fd = openat(code_cache_fd, reference_profile.c_str(),
- O_CREAT | O_RDWR | O_NOFOLLOW, S_IWUSR | S_IRUSR);
+static void open_profile_files(uid_t uid, const char* 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);
if (*reference_profile_fd < 0) {
- close(*profile_fd);
+ // We can't access the reference profile file.
return;
}
- if (fchown(*reference_profile_fd, uid, uid) < 0) {
- PLOG(ERROR) << "Cannot change reference profile file owner: " << reference_profile;
- close(*profile_fd);
- *profile_fd = -1;
- *reference_profile_fd = -1;
+
+ std::vector<userid_t> users = get_known_users(/*volume_uuid*/ nullptr);
+ for (auto user : users) {
+ fd_t profile_fd = open_primary_profile_file(user, pkgname);
+ // Add to the lists only if both fds are valid.
+ if (profile_fd >= 0) {
+ profiles_fd->push_back(profile_fd);
+ }
}
}
-static void open_profile_files(const char* volume_uuid, uid_t uid, const char* pkgname,
- std::vector<int>* profile_fds, std::vector<int>* reference_profile_fds) {
- std::vector<userid_t> users = get_known_users(volume_uuid);
- for (auto user : users) {
- int code_cache_fd = open_code_cache_for_user(user, volume_uuid, pkgname);
- if (code_cache_fd < 0) {
- continue;
- }
- int profile_fd = -1;
- int reference_profile_fd = -1;
- open_profile_files_for_user(
- uid, pkgname, code_cache_fd, &profile_fd, &reference_profile_fd);
- close(code_cache_fd);
+static void drop_capabilities(uid_t uid) {
+ if (setgid(uid) != 0) {
+ ALOGE("setgid(%d) failed in installd during dexopt\n", uid);
+ exit(64);
+ }
+ if (setuid(uid) != 0) {
+ ALOGE("setuid(%d) failed in installd during dexopt\n", uid);
+ exit(65);
+ }
+ // drop capabilities
+ struct __user_cap_header_struct capheader;
+ struct __user_cap_data_struct capdata[2];
+ memset(&capheader, 0, sizeof(capheader));
+ memset(&capdata, 0, sizeof(capdata));
+ capheader.version = _LINUX_CAPABILITY_VERSION_3;
+ if (capset(&capheader, &capdata[0]) < 0) {
+ ALOGE("capset failed: %s\n", strerror(errno));
+ exit(66);
+ }
+}
- // Add to the lists only if both fds are valid.
- if ((profile_fd >= 0) && (reference_profile_fd >= 0)) {
- profile_fds->push_back(profile_fd);
- reference_profile_fds->push_back(reference_profile_fd);
+static constexpr int PROFMAN_BIN_RETURN_CODE_COMPILE = 0;
+static constexpr int PROFMAN_BIN_RETURN_CODE_SKIP_COMPILATION = 1;
+static constexpr int PROFMAN_BIN_RETURN_CODE_BAD_PROFILES = 2;
+static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_IO = 3;
+static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_LOCKING = 4;
+
+static void run_profman(const std::vector<fd_t>& profiles_fd, fd_t reference_profile_fd) {
+ static const size_t MAX_INT_LEN = 32;
+ static const char* PROFMAN_BIN = "/system/bin/profman";
+
+ std::vector<std::string> profile_args(profiles_fd.size());
+ char profile_buf[strlen("--profile-file-fd=") + MAX_INT_LEN];
+ for (size_t k = 0; k < profiles_fd.size(); k++) {
+ sprintf(profile_buf, "--profile-file-fd=%d", profiles_fd[k]);
+ profile_args[k].assign(profile_buf);
+ }
+ char reference_profile_arg[strlen("--reference-profile-file-fd=") + MAX_INT_LEN];
+ sprintf(reference_profile_arg, "--reference-profile-file-fd=%d", reference_profile_fd);
+
+ // program name, reference profile fd, the final NULL and the profile fds
+ const char* argv[3 + profiles_fd.size()];
+ int i = 0;
+ argv[i++] = PROFMAN_BIN;
+ argv[i++] = reference_profile_arg;
+ for (size_t k = 0; k < profile_args.size(); k++) {
+ argv[i++] = profile_args[k].c_str();
+ }
+ // Do not add after dex2oat_flags, they should override others for debugging.
+ argv[i] = NULL;
+
+ execv(PROFMAN_BIN, (char * const *)argv);
+ ALOGE("execv(%s) failed: %s\n", PROFMAN_BIN, strerror(errno));
+ exit(68); /* only get here on exec failure */
+}
+
+// Decides if profile guided compilation is needed or not based on existing profiles.
+// Returns true if there is enough information in the current profiles that worth
+// 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().
+static bool analyse_profiles(uid_t uid, const char* pkgname) {
+ std::vector<fd_t> profiles_fd;
+ fd_t reference_profile_fd = -1;
+ open_profile_files(uid, pkgname, &profiles_fd, &reference_profile_fd);
+ if (profiles_fd.empty() || (reference_profile_fd == -1)) {
+ // Skip profile guided compilation because no profiles were found.
+ // Or if the reference profile info couldn't be opened.
+ close_all_fds(profiles_fd, "profiles_fd");
+ if ((reference_profile_fd != - 1) && (close(reference_profile_fd) != 0)) {
+ PLOG(WARNING) << "Failed to close fd for reference profile";
+ }
+ return false;
+ }
+
+ ALOGV("PROFMAN: --- BEGIN '%s' ---\n", pkgname);
+
+ pid_t pid = fork();
+ if (pid == 0) {
+ /* child -- drop privileges before continuing */
+ drop_capabilities(uid);
+ run_profman(profiles_fd, reference_profile_fd);
+ exit(68); /* only get here on exec failure */
+ }
+ /* parent */
+ int return_code = wait_child(pid);
+ bool need_to_compile = false;
+ bool delete_current_profiles = false;
+ bool delete_reference_profile = false;
+ if (!WIFEXITED(return_code)) {
+ LOG(WARNING) << "profman failed for package " << pkgname << ": " << return_code;
+ } else {
+ return_code = WEXITSTATUS(return_code);
+ switch (return_code) {
+ case PROFMAN_BIN_RETURN_CODE_COMPILE:
+ need_to_compile = true;
+ delete_current_profiles = true;
+ delete_reference_profile = false;
+ break;
+ case PROFMAN_BIN_RETURN_CODE_SKIP_COMPILATION:
+ need_to_compile = false;
+ delete_current_profiles = false;
+ delete_reference_profile = false;
+ break;
+ case PROFMAN_BIN_RETURN_CODE_BAD_PROFILES:
+ LOG(WARNING) << "Bad profiles for package " << pkgname;
+ need_to_compile = false;
+ delete_current_profiles = true;
+ delete_reference_profile = true;
+ break;
+ case PROFMAN_BIN_RETURN_CODE_ERROR_IO: // fall-through
+ case PROFMAN_BIN_RETURN_CODE_ERROR_LOCKING:
+ // Temporary IO problem (e.g. locking). Ignore but log a warning.
+ LOG(WARNING) << "IO error while reading profiles for package " << pkgname;
+ need_to_compile = false;
+ delete_current_profiles = false;
+ delete_reference_profile = false;
+ break;
+ default:
+ // Unknown return code or error. Unlink profiles.
+ LOG(WARNING) << "Unknown error code while processing profiles for package " << pkgname
+ << ": " << return_code;
+ need_to_compile = false;
+ delete_current_profiles = true;
+ delete_reference_profile = true;
+ break;
}
}
+ close_all_fds(profiles_fd, "profiles_fd");
+ if (close(reference_profile_fd) != 0) {
+ PLOG(WARNING) << "Failed to close fd for reference profile";
+ }
+ if (delete_current_profiles) {
+ unlink_current_profiles(pkgname);
+ }
+ if (delete_reference_profile) {
+ unlink_reference_profile(pkgname);
+ }
+ return need_to_compile;
}
static void trim_extension(char* path) {
@@ -1066,9 +1236,36 @@
return true;
}
+static bool create_oat_out_path(const char* apk_path, const char* instruction_set,
+ const char* oat_dir, /*out*/ char* out_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"
+ // extension to the cache path (5 bytes).
+ if (strlen(apk_path) >= (PKG_PATH_MAX - 8)) {
+ ALOGE("apk_path too long '%s'\n", apk_path);
+ return false;
+ }
+
+ if (oat_dir != NULL && oat_dir[0] != '!') {
+ if (validate_apk_path(oat_dir)) {
+ ALOGE("invalid oat_dir '%s'\n", oat_dir);
+ return false;
+ }
+ if (!calculate_oat_file_path(out_path, oat_dir, apk_path, instruction_set)) {
+ return false;
+ }
+ } else {
+ if (!create_cache_path(out_path, apk_path, instruction_set)) {
+ return false;
+ }
+ }
+ return true;
+}
+
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* volume_uuid,
- bool use_profiles)
+ int dexopt_needed, const char* oat_dir, int dexopt_flags,
+ const char* volume_uuid ATTRIBUTE_UNUSED, bool use_profiles)
{
struct utimbuf ut;
struct stat input_stat;
@@ -1077,19 +1274,25 @@
char image_path[PKG_PATH_MAX];
const char *input_file;
char in_odex_path[PKG_PATH_MAX];
- int res, input_fd=-1, out_fd=-1, image_fd=-1, swap_fd=-1;
+ int res;
+ fd_t input_fd=-1, out_fd=-1, image_fd=-1, swap_fd=-1;
bool is_public = (dexopt_flags & DEXOPT_PUBLIC) != 0;
bool vm_safe_mode = (dexopt_flags & DEXOPT_SAFEMODE) != 0;
bool debuggable = (dexopt_flags & DEXOPT_DEBUGGABLE) != 0;
bool boot_complete = (dexopt_flags & DEXOPT_BOOTCOMPLETE) != 0;
bool extract_only = (dexopt_flags & DEXOPT_EXTRACTONLY) != 0;
- std::vector<int> profile_files_fd;
- std::vector<int> reference_profile_files_fd;
+ fd_t reference_profile_fd = -1;
if (use_profiles) {
- open_profile_files(volume_uuid, uid, pkgname,
- &profile_files_fd, &reference_profile_files_fd);
- if (profile_files_fd.empty()) {
- // Skip profile guided compilation because no profiles were found.
+ if (analyse_profiles(uid, pkgname)) {
+ // Open again reference profile in read only mode as dex2oat does not get write
+ // permissions.
+ reference_profile_fd = open_reference_profile(uid, pkgname, /*read_write*/ false);
+ if (reference_profile_fd == -1) {
+ PLOG(WARNING) << "Couldn't open reference profile in read only mode " << pkgname;
+ exit(72);
+ }
+ } else {
+ // No need to (re)compile. Return early.
return 0;
}
}
@@ -1098,26 +1301,8 @@
LOG_FATAL("dexopt flags contains unknown fields\n");
}
- // 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.
- if (strlen(apk_path) >= (PKG_PATH_MAX - 8)) {
- ALOGE("apk_path too long '%s'\n", apk_path);
- return -1;
- }
-
- if (oat_dir != NULL && oat_dir[0] != '!') {
- if (validate_apk_path(oat_dir)) {
- ALOGE("invalid oat_dir '%s'\n", oat_dir);
- return -1;
- }
- if (!calculate_oat_file_path(out_path, oat_dir, apk_path, instruction_set)) {
- return -1;
- }
- } else {
- if (!create_cache_path(out_path, apk_path, instruction_set)) {
- return -1;
- }
+ if (!create_oat_out_path(apk_path, instruction_set, oat_dir, out_path)) {
+ return false;
}
switch (dexopt_needed) {
@@ -1207,24 +1392,8 @@
pid = fork();
if (pid == 0) {
/* child -- drop privileges before continuing */
- if (setgid(uid) != 0) {
- ALOGE("setgid(%d) failed in installd during dexopt\n", uid);
- exit(64);
- }
- if (setuid(uid) != 0) {
- ALOGE("setuid(%d) failed in installd during dexopt\n", uid);
- exit(65);
- }
- // drop capabilities
- struct __user_cap_header_struct capheader;
- struct __user_cap_data_struct capdata[2];
- memset(&capheader, 0, sizeof(capheader));
- memset(&capdata, 0, sizeof(capdata));
- capheader.version = _LINUX_CAPABILITY_VERSION_3;
- if (capset(&capheader, &capdata[0]) < 0) {
- ALOGE("capset failed: %s\n", strerror(errno));
- exit(66);
- }
+ drop_capabilities(uid);
+
SetDex2OatAndPatchOatScheduling(boot_complete);
if (flock(out_fd, LOCK_EX | LOCK_NB) != 0) {
ALOGE("flock(%s) failed: %s\n", out_path, strerror(errno));
@@ -1244,7 +1413,7 @@
}
run_dex2oat(input_fd, out_fd, image_fd, input_file_name, out_path, swap_fd,
instruction_set, vm_safe_mode, debuggable, boot_complete, extract_only,
- profile_files_fd, reference_profile_files_fd);
+ reference_profile_fd);
} else {
ALOGE("Invalid dexopt needed: %d\n", dexopt_needed);
exit(73);
@@ -1269,9 +1438,8 @@
if (swap_fd >= 0) {
close(swap_fd);
}
- if (use_profiles != 0) {
- close_all_fds(profile_files_fd, "profile_files_fd");
- close_all_fds(reference_profile_files_fd, "reference_profile_files_fd");
+ if (reference_profile_fd >= 0) {
+ close(reference_profile_fd);
}
if (image_fd >= 0) {
close(image_fd);
@@ -1286,9 +1454,11 @@
if (input_fd >= 0) {
close(input_fd);
}
- if (use_profiles != 0) {
- close_all_fds(profile_files_fd, "profile_files_fd");
- close_all_fds(reference_profile_files_fd, "reference_profile_files_fd");
+ if (reference_profile_fd >= 0) {
+ close(reference_profile_fd);
+ // We failed to compile. Unlink the reference profile. Current profiles are already unlinked
+ // when profmoan advises compilation.
+ unlink_reference_profile(pkgname);
}
if (swap_fd >= 0) {
close(swap_fd);
diff --git a/cmds/installd/globals.cpp b/cmds/installd/globals.cpp
index bee2790..6a67e29 100644
--- a/cmds/installd/globals.cpp
+++ b/cmds/installd/globals.cpp
@@ -39,6 +39,7 @@
dir_rec_t android_data_dir;
dir_rec_t android_media_dir;
dir_rec_t android_mnt_expand_dir;
+dir_rec_t android_profiles_dir;
dir_rec_array_t android_system_dirs;
@@ -99,6 +100,11 @@
return false;
}
+ // Get the android profiles directory.
+ if (copy_and_append(&android_profiles_dir, &android_data_dir, PROFILES_SUBDIR) < 0) {
+ return false;
+ }
+
// Take note of the system and vendor directories.
android_system_dirs.count = 4;
diff --git a/cmds/installd/globals.h b/cmds/installd/globals.h
index 2e61f85..3e52346 100644
--- a/cmds/installd/globals.h
+++ b/cmds/installd/globals.h
@@ -43,6 +43,7 @@
extern dir_rec_t android_data_dir;
extern dir_rec_t android_media_dir;
extern dir_rec_t android_mnt_expand_dir;
+extern dir_rec_t android_profiles_dir;
extern dir_rec_array_t android_system_dirs;
diff --git a/cmds/installd/installd_constants.h b/cmds/installd/installd_constants.h
index 4e1d38f..8f6e928 100644
--- a/cmds/installd/installd_constants.h
+++ b/cmds/installd/installd_constants.h
@@ -4,7 +4,7 @@
**
** 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
+** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
@@ -41,6 +41,8 @@
constexpr const char* MEDIA_SUBDIR = "media/"; // sub-directory under ANDROID_DATA
+constexpr const char* PROFILES_SUBDIR = "misc/profiles"; // sub-directory under ANDROID_DATA
+
/* other handy constants */
constexpr const char* PRIVATE_APP_SUBDIR = "app-private/"; // sub-directory under ANDROID_DATA
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index d25bf71..74f4264 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -59,6 +59,11 @@
}
}
+static void check_package_name(const char* package_name) {
+ CHECK(is_valid_filename(package_name));
+ CHECK(is_valid_package_name(package_name) == 0);
+}
+
/**
* Create the path name where package app contents should be stored for
* the given volume UUID and package name. An empty UUID is assumed to
@@ -66,9 +71,7 @@
*/
std::string create_data_app_package_path(const char* volume_uuid,
const char* package_name) {
- CHECK(is_valid_filename(package_name));
- CHECK(is_valid_package_name(package_name) == 0);
-
+ check_package_name(package_name);
return StringPrintf("%s/%s",
create_data_app_path(volume_uuid).c_str(), package_name);
}
@@ -80,18 +83,14 @@
*/
std::string create_data_user_package_path(const char* volume_uuid,
userid_t user, const char* package_name) {
- CHECK(is_valid_filename(package_name));
- CHECK(is_valid_package_name(package_name) == 0);
-
+ check_package_name(package_name);
return StringPrintf("%s/%s",
create_data_user_path(volume_uuid, user).c_str(), package_name);
}
std::string create_data_user_de_package_path(const char* volume_uuid,
userid_t user, const char* package_name) {
- CHECK(is_valid_filename(package_name));
- CHECK(is_valid_package_name(package_name) == 0);
-
+ check_package_name(package_name);
return StringPrintf("%s/%s",
create_data_user_de_path(volume_uuid, user).c_str(), package_name);
}
@@ -161,6 +160,20 @@
return StringPrintf("%s/media/%u", create_data_path(volume_uuid).c_str(), userid);
}
+std::string create_data_user_profiles_path(userid_t userid) {
+ 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_profiles_path(user).c_str(), package_name);
+}
+
+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::vector<userid_t> get_known_users(const char* volume_uuid) {
std::vector<userid_t> users;
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index 2d9573e..9bbddca 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -84,6 +84,10 @@
std::string create_data_media_path(const char* volume_uuid, userid_t userid);
+std::string create_data_user_profiles_path(userid_t userid);
+std::string create_data_user_profile_package_path(userid_t user, const char* package_name);
+std::string create_data_ref_profile_package_path(const char* package_name);
+
std::vector<userid_t> get_known_users(const char* volume_uuid);
int create_user_config_path(char path[PKG_PATH_MAX], userid_t userid);
@@ -135,6 +139,7 @@
char *build_string3(const char *s1, const char *s2, const char *s3);
int ensure_dir(const char* path, mode_t mode, uid_t uid, gid_t gid);
+int ensure_media_user_dirs(const char* uuid, userid_t userid);
int ensure_config_user_dirs(userid_t userid);
int wait_child(pid_t pid);
diff --git a/cmds/servicemanager/Android.mk b/cmds/servicemanager/Android.mk
index 7ee0dd1..73c0367 100644
--- a/cmds/servicemanager/Android.mk
+++ b/cmds/servicemanager/Android.mk
@@ -1,7 +1,7 @@
LOCAL_PATH:= $(call my-dir)
svc_c_flags = \
- -Wall -Wextra \
+ -Wall -Wextra -Werror \
ifneq ($(TARGET_USES_64_BIT_BINDER),true)
ifneq ($(TARGET_IS_64_BIT),true)
diff --git a/cmds/servicemanager/bctest.c b/cmds/servicemanager/bctest.c
index fd91633..6466654 100644
--- a/cmds/servicemanager/bctest.c
+++ b/cmds/servicemanager/bctest.c
@@ -58,7 +58,6 @@
int main(int argc, char **argv)
{
- int fd;
struct binder_state *bs;
uint32_t svcmgr = BINDER_SERVICE_MANAGER;
uint32_t handle;
diff --git a/cmds/servicemanager/binder.c b/cmds/servicemanager/binder.c
index 3d14451..9e99085 100644
--- a/cmds/servicemanager/binder.c
+++ b/cmds/servicemanager/binder.c
@@ -449,7 +449,7 @@
}
void binder_done(struct binder_state *bs,
- struct binder_io *msg,
+ __unused struct binder_io *msg,
struct binder_io *reply)
{
struct {
diff --git a/cmds/servicemanager/service_manager.c b/cmds/servicemanager/service_manager.c
index 8596f96..21fdff0 100644
--- a/cmds/servicemanager/service_manager.c
+++ b/cmds/servicemanager/service_manager.c
@@ -178,7 +178,7 @@
};
-uint32_t do_find_service(struct binder_state *bs, const uint16_t *s, size_t len, uid_t uid, pid_t spid)
+uint32_t do_find_service(const uint16_t *s, size_t len, uid_t uid, pid_t spid)
{
struct svcinfo *si = find_svc(s, len);
@@ -304,7 +304,7 @@
if (s == NULL) {
return -1;
}
- handle = do_find_service(bs, s, len, txn->sender_euid, txn->sender_pid);
+ handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid);
if (!handle)
break;
bio_put_ref(reply, handle);
@@ -349,7 +349,7 @@
}
-static int audit_callback(void *data, security_class_t cls, char *buf, size_t len)
+static int audit_callback(void *data, __unused security_class_t cls, char *buf, size_t len)
{
struct audit_data *ad = (struct audit_data *)data;
@@ -362,7 +362,7 @@
return 0;
}
-int main(int argc, char **argv)
+int main()
{
struct binder_state *bs;
diff --git a/include/gui/BufferItem.h b/include/gui/BufferItem.h
index a515f39..6f45181 100644
--- a/include/gui/BufferItem.h
+++ b/include/gui/BufferItem.h
@@ -119,8 +119,10 @@
// previous frame
Region mSurfaceDamage;
- // Indicates that the BufferQueue is in single buffer mode
- bool mSingleBufferMode;
+ // Indicates that the consumer should acquire the next frame as soon as it
+ // can and not wait for a frame to become available. This is only relevant
+ // in single buffer mode.
+ bool mAutoRefresh;
// Indicates that this buffer was queued by the producer. When in single
// buffer mode acquire() can return a BufferItem that wasn't in the queue.
diff --git a/include/gui/BufferQueueCore.h b/include/gui/BufferQueueCore.h
index e2e73a0..d10ba2f 100644
--- a/include/gui/BufferQueueCore.h
+++ b/include/gui/BufferQueueCore.h
@@ -292,6 +292,11 @@
// consumer and producer to access the same buffer simultaneously.
bool mSingleBufferMode;
+ // When single buffer mode is enabled, this indicates whether the consumer
+ // should acquire buffers even if BufferQueue doesn't indicate that they are
+ // available.
+ bool mAutoRefresh;
+
// When single buffer mode is enabled, this tracks which slot contains the
// shared buffer.
int mSingleBufferSlot;
diff --git a/include/gui/BufferQueueProducer.h b/include/gui/BufferQueueProducer.h
index dc05e98..312f323 100644
--- a/include/gui/BufferQueueProducer.h
+++ b/include/gui/BufferQueueProducer.h
@@ -176,6 +176,9 @@
// See IGraphicBufferProducer::setSingleBufferMode
virtual status_t setSingleBufferMode(bool singleBufferMode) override;
+ // See IGraphicBufferProducer::setAutoRefresh
+ virtual status_t setAutoRefresh(bool autoRefresh) override;
+
// See IGraphicBufferProducer::setDequeueTimeout
virtual status_t setDequeueTimeout(nsecs_t timeout) override;
diff --git a/include/gui/IGraphicBufferProducer.h b/include/gui/IGraphicBufferProducer.h
index 265728f..f6b4230 100644
--- a/include/gui/IGraphicBufferProducer.h
+++ b/include/gui/IGraphicBufferProducer.h
@@ -531,6 +531,14 @@
// the producer and consumer to simultaneously access the same buffer.
virtual status_t setSingleBufferMode(bool singleBufferMode) = 0;
+ // Used to enable/disable auto-refresh.
+ //
+ // Auto refresh has no effect outside of single buffer mode. In single
+ // buffer mode, when enabled, it indicates to the consumer that it should
+ // attempt to acquire buffers even if it is not aware of any being
+ // available.
+ virtual status_t setAutoRefresh(bool autoRefresh) = 0;
+
// Sets how long dequeueBuffer will wait for a buffer to become available
// before returning an error (TIMED_OUT).
//
diff --git a/include/gui/Surface.h b/include/gui/Surface.h
index f79210b..3afdaae 100644
--- a/include/gui/Surface.h
+++ b/include/gui/Surface.h
@@ -168,6 +168,7 @@
int dispatchSetBuffersDataSpace(va_list args);
int dispatchSetSurfaceDamage(va_list args);
int dispatchSetSingleBufferMode(va_list args);
+ int dispatchSetAutoRefresh(va_list args);
protected:
virtual int dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd);
@@ -197,6 +198,7 @@
virtual int setMaxDequeuedBufferCount(int maxDequeuedBuffers);
virtual int setAsyncMode(bool async);
virtual int setSingleBufferMode(bool singleBufferMode);
+ virtual int setAutoRefresh(bool autoRefresh);
virtual int lock(ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds);
virtual int unlockAndPost();
@@ -331,6 +333,19 @@
// Stores the current generation number. See setGenerationNumber and
// IGraphicBufferProducer::setGenerationNumber for more information.
uint32_t mGenerationNumber;
+
+ // Caches the values that have been passed to the producer.
+ bool mSingleBufferMode;
+ bool mAutoRefresh;
+
+ // If in single buffer mode and auto refresh is enabled, store the shared
+ // buffer slot and return it for all calls to queue/dequeue without going
+ // over Binder.
+ int mSharedBufferSlot;
+
+ // This is true if the shared buffer has already been queued/canceled. It's
+ // used to prevent a mismatch between the number of queue/dequeue calls.
+ bool mSharedBufferHasBeenQueued;
};
}; // namespace android
diff --git a/libs/gui/BufferItem.cpp b/libs/gui/BufferItem.cpp
index 036ef1e..5e3924a 100644
--- a/libs/gui/BufferItem.cpp
+++ b/libs/gui/BufferItem.cpp
@@ -38,7 +38,7 @@
mAcquireCalled(false),
mTransformToDisplayInverse(false),
mSurfaceDamage(),
- mSingleBufferMode(false),
+ mAutoRefresh(false),
mQueuedBuffer(true),
mIsStale(false) {
}
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 92285e5..8466aaa 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -32,6 +32,10 @@
#include <gui/IConsumerListener.h>
#include <gui/IProducerListener.h>
+#include <binder/IPCThreadState.h>
+#include <binder/PermissionCache.h>
+#include <private/android_filesystem_config.h>
+
namespace android {
BufferQueueConsumer::BufferQueueConsumer(const sp<BufferQueueCore>& core) :
@@ -67,7 +71,7 @@
}
bool sharedBufferAvailable = mCore->mSingleBufferMode &&
- mCore->mSingleBufferSlot !=
+ mCore->mAutoRefresh && mCore->mSingleBufferSlot !=
BufferQueueCore::INVALID_BUFFER_SLOT;
// In asynchronous mode the list is guaranteed to be one buffer deep,
@@ -214,16 +218,15 @@
(mCore->mSingleBufferCache.transform &
NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) != 0;
outBuffer->mSurfaceDamage = Region::INVALID_REGION;
- outBuffer->mSingleBufferMode = true;
outBuffer->mQueuedBuffer = false;
outBuffer->mIsStale = false;
+ outBuffer->mAutoRefresh = mCore->mSingleBufferMode &&
+ mCore->mAutoRefresh;
} else {
slot = front->mSlot;
*outBuffer = *front;
}
- outBuffer->mSingleBufferMode = mCore->mSingleBufferMode;
-
ATRACE_BUFFER_INDEX(slot);
BQ_LOGV("acquireBuffer: acquiring { slot=%d/%" PRIu64 " buffer=%p }",
@@ -713,7 +716,17 @@
}
void BufferQueueConsumer::dump(String8& result, const char* prefix) const {
- mCore->dump(result, prefix);
+ const IPCThreadState* ipc = IPCThreadState::self();
+ const pid_t pid = ipc->getCallingPid();
+ const uid_t uid = ipc->getCallingUid();
+ if ((uid != AID_SHELL)
+ && !PermissionCache::checkPermission(String16(
+ "android.permission.DUMP"), pid, uid)) {
+ result.appendFormat("Permission Denial: can't dump BufferQueueConsumer "
+ "from pid=%d, uid=%d\n", pid, uid);
+ } else {
+ mCore->dump(result, prefix);
+ }
}
} // namespace android
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index f785db0..ba07362 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -79,6 +79,7 @@
mGenerationNumber(0),
mAsyncMode(false),
mSingleBufferMode(false),
+ mAutoRefresh(false),
mSingleBufferSlot(INVALID_BUFFER_SLOT),
mSingleBufferCache(Rect::INVALID_RECT, 0, NATIVE_WINDOW_SCALING_MODE_FREEZE,
HAL_DATASPACE_UNKNOWN)
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 136a14a..17d4a2c 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -806,8 +806,8 @@
mCore->mDequeueBufferCannotBlock ||
(mCore->mSingleBufferMode && mCore->mSingleBufferSlot == slot);
item.mSurfaceDamage = surfaceDamage;
- item.mSingleBufferMode = mCore->mSingleBufferMode;
item.mQueuedBuffer = true;
+ item.mAutoRefresh = mCore->mSingleBufferMode && mCore->mAutoRefresh;
mStickyTransform = stickyTransform;
@@ -1309,6 +1309,16 @@
return NO_ERROR;
}
+status_t BufferQueueProducer::setAutoRefresh(bool autoRefresh) {
+ ATRACE_CALL();
+ BQ_LOGV("setAutoRefresh: %d", autoRefresh);
+
+ Mutex::Autolock lock(mCore->mMutex);
+
+ mCore->mAutoRefresh = autoRefresh;
+ return NO_ERROR;
+}
+
status_t BufferQueueProducer::setDequeueTimeout(nsecs_t timeout) {
ATRACE_CALL();
BQ_LOGV("setDequeueTimeout: %" PRId64, timeout);
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index e1abd45..55059dd 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -407,7 +407,7 @@
}
// Do whatever sync ops we need to do before releasing the old slot.
- if (!item.mSingleBufferMode || slot != mCurrentTexture) {
+ if (slot != mCurrentTexture) {
err = syncForReleaseLocked(mEglDisplay);
if (err != NO_ERROR) {
// Release the buffer we just acquired. It's not safe to
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index 2478601..c66694d 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -52,6 +52,7 @@
SET_ASYNC_MODE,
GET_NEXT_FRAME_NUMBER,
SET_SINGLE_BUFFER_MODE,
+ SET_AUTO_REFRESH,
SET_DEQUEUE_TIMEOUT,
};
@@ -355,6 +356,18 @@
return result;
}
+ virtual status_t setAutoRefresh(bool autoRefresh) {
+ Parcel data, reply;
+ data.writeInterfaceToken(
+ IGraphicBufferProducer::getInterfaceDescriptor());
+ data.writeInt32(autoRefresh);
+ status_t result = remote()->transact(SET_AUTO_REFRESH, data, &reply);
+ if (result == NO_ERROR) {
+ result = reply.readInt32();
+ }
+ return result;
+ }
+
virtual status_t setDequeueTimeout(nsecs_t timeout) {
Parcel data, reply;
data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
@@ -562,6 +575,13 @@
reply->writeInt32(result);
return NO_ERROR;
}
+ case SET_AUTO_REFRESH: {
+ CHECK_INTERFACE(IGraphicBuffer, data, reply);
+ bool autoRefresh = data.readInt32();
+ status_t result = setAutoRefresh(autoRefresh);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ }
case SET_DEQUEUE_TIMEOUT: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
nsecs_t timeout = data.readInt64();
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index ebbeae6..c295684 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -44,7 +44,11 @@
bool controlledByApp)
: mGraphicBufferProducer(bufferProducer),
mCrop(Rect::EMPTY_RECT),
- mGenerationNumber(0)
+ mGenerationNumber(0),
+ mSingleBufferMode(false),
+ mAutoRefresh(false),
+ mSharedBufferSlot(BufferItem::INVALID_BUFFER_SLOT),
+ mSharedBufferHasBeenQueued(false)
{
// Initialize the ANativeWindow function pointers.
ANativeWindow::setSwapInterval = hook_setSwapInterval;
@@ -232,6 +236,16 @@
reqFormat = mReqFormat;
reqUsage = mReqUsage;
+
+ if (mSingleBufferMode && mAutoRefresh && mSharedBufferSlot !=
+ BufferItem::INVALID_BUFFER_SLOT) {
+ sp<GraphicBuffer>& gbuf(mSlots[mSharedBufferSlot].buffer);
+ if (gbuf != NULL) {
+ *buffer = gbuf.get();
+ *fenceFd = -1;
+ return OK;
+ }
+ }
} // Drop the lock so that we can still touch the Surface while blocking in IGBP::dequeueBuffer
int buf = -1;
@@ -279,6 +293,15 @@
}
*buffer = gbuf.get();
+
+ if (mSingleBufferMode && mAutoRefresh) {
+ mSharedBufferSlot = buf;
+ mSharedBufferHasBeenQueued = false;
+ } else if (mSharedBufferSlot == buf) {
+ mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT;
+ mSharedBufferHasBeenQueued = false;
+ }
+
return OK;
}
@@ -294,8 +317,19 @@
}
return i;
}
+ if (mSharedBufferSlot == i && mSharedBufferHasBeenQueued) {
+ if (fenceFd >= 0) {
+ close(fenceFd);
+ }
+ return OK;
+ }
sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE);
mGraphicBufferProducer->cancelBuffer(i, fence);
+
+ if (mSingleBufferMode && mAutoRefresh && mSharedBufferSlot == i) {
+ mSharedBufferHasBeenQueued = true;
+ }
+
return OK;
}
@@ -323,6 +357,7 @@
Mutex::Autolock lock(mMutex);
int64_t timestamp;
bool isAutoTimestamp = false;
+
if (mTimestamp == NATIVE_WINDOW_TIMESTAMP_AUTO) {
timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
isAutoTimestamp = true;
@@ -338,6 +373,12 @@
}
return i;
}
+ if (mSharedBufferSlot == i && mSharedBufferHasBeenQueued) {
+ if (fenceFd >= 0) {
+ close(fenceFd);
+ }
+ return OK;
+ }
// Make sure the crop rectangle is entirely inside the buffer.
@@ -417,6 +458,7 @@
if (err != OK) {
ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err);
}
+
uint32_t numPendingBuffers = 0;
uint32_t hint = 0;
output.deflate(&mDefaultWidth, &mDefaultHeight, &hint,
@@ -434,6 +476,10 @@
mDirtyRegion = Region::INVALID_REGION;
}
+ if (mSingleBufferMode && mAutoRefresh && mSharedBufferSlot == i) {
+ mSharedBufferHasBeenQueued = true;
+ }
+
return err;
}
@@ -557,6 +603,9 @@
case NATIVE_WINDOW_SET_SINGLE_BUFFER_MODE:
res = dispatchSetSingleBufferMode(args);
break;
+ case NATIVE_WINDOW_SET_AUTO_REFRESH:
+ res = dispatchSetAutoRefresh(args);
+ break;
default:
res = NAME_NOT_FOUND;
break;
@@ -669,8 +718,12 @@
int Surface::dispatchSetSingleBufferMode(va_list args) {
bool singleBufferMode = va_arg(args, int);
- setSingleBufferMode(singleBufferMode);
- return NO_ERROR;
+ return setSingleBufferMode(singleBufferMode);
+}
+
+int Surface::dispatchSetAutoRefresh(va_list args) {
+ bool autoRefresh = va_arg(args, int);
+ return setAutoRefresh(autoRefresh);
}
int Surface::connect(int api) {
@@ -714,6 +767,8 @@
ATRACE_CALL();
ALOGV("Surface::disconnect");
Mutex::Autolock lock(mMutex);
+ mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT;
+ mSharedBufferHasBeenQueued = false;
freeAllBuffers();
int err = mGraphicBufferProducer->disconnect(api);
if (!err) {
@@ -796,6 +851,9 @@
{
ALOGV("Surface::setUsage");
Mutex::Autolock lock(mMutex);
+ if (reqUsage != mReqUsage) {
+ mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT;
+ }
mReqUsage = reqUsage;
return OK;
}
@@ -876,12 +934,29 @@
status_t err = mGraphicBufferProducer->setSingleBufferMode(
singleBufferMode);
- ALOGE_IF(err, "IGraphicsBufferProducer::setSingleBufferMode(%d) returned"
+ if (err == NO_ERROR) {
+ mSingleBufferMode = singleBufferMode;
+ }
+ ALOGE_IF(err, "IGraphicBufferProducer::setSingleBufferMode(%d) returned"
"%s", singleBufferMode, strerror(-err));
return err;
}
+int Surface::setAutoRefresh(bool autoRefresh) {
+ ATRACE_CALL();
+ ALOGV("Surface::setAutoRefresh (%d)", autoRefresh);
+ Mutex::Autolock lock(mMutex);
+
+ status_t err = mGraphicBufferProducer->setAutoRefresh(autoRefresh);
+ if (err == NO_ERROR) {
+ mAutoRefresh = autoRefresh;
+ }
+ ALOGE_IF(err, "IGraphicBufferProducer::setAutoRefresh(%d) returned %s",
+ autoRefresh, strerror(-err));
+ return err;
+}
+
int Surface::setBuffersDimensions(uint32_t width, uint32_t height)
{
ATRACE_CALL();
@@ -891,6 +966,9 @@
return BAD_VALUE;
Mutex::Autolock lock(mMutex);
+ if (width != mReqWidth || height != mReqHeight) {
+ mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT;
+ }
mReqWidth = width;
mReqHeight = height;
return NO_ERROR;
@@ -905,6 +983,9 @@
return BAD_VALUE;
Mutex::Autolock lock(mMutex);
+ if (width != mUserWidth || height != mUserHeight) {
+ mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT;
+ }
mUserWidth = width;
mUserHeight = height;
return NO_ERROR;
@@ -915,6 +996,9 @@
ALOGV("Surface::setBuffersFormat");
Mutex::Autolock lock(mMutex);
+ if (format != mReqFormat) {
+ mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT;
+ }
mReqFormat = format;
return NO_ERROR;
}
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index f4c47ed..b6af166 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -65,6 +65,27 @@
BufferQueue::createBufferQueue(&mProducer, &mConsumer);
}
+ void testBufferItem(const IGraphicBufferProducer::QueueBufferInput& input,
+ const BufferItem& item) {
+ int64_t timestamp;
+ bool isAutoTimestamp;
+ android_dataspace dataSpace;
+ Rect crop;
+ int scalingMode;
+ uint32_t transform;
+ sp<Fence> fence;
+
+ input.deflate(×tamp, &isAutoTimestamp, &dataSpace, &crop,
+ &scalingMode, &transform, &fence, NULL);
+ ASSERT_EQ(timestamp, item.mTimestamp);
+ ASSERT_EQ(isAutoTimestamp, item.mIsAutoTimestamp);
+ ASSERT_EQ(dataSpace, item.mDataSpace);
+ ASSERT_EQ(crop, item.mCrop);
+ ASSERT_EQ(static_cast<uint32_t>(scalingMode), item.mScalingMode);
+ ASSERT_EQ(transform, item.mTransform);
+ ASSERT_EQ(fence, item.mFence);
+ }
+
sp<IGraphicBufferProducer> mProducer;
sp<IGraphicBufferConsumer> mConsumer;
};
@@ -521,7 +542,7 @@
ASSERT_EQ(OK, mConsumer->attachBuffer(&outSlot, buffer));
}
-TEST_F(BufferQueueTest, TestSingleBufferMode) {
+TEST_F(BufferQueueTest, TestSingleBufferModeWithoutAutoRefresh) {
createBufferQueue();
sp<DummyConsumer> dc(new DummyConsumer);
ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
@@ -545,19 +566,66 @@
NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE);
ASSERT_EQ(OK, mProducer->queueBuffer(singleSlot, input, &output));
+ // Repeatedly queue and dequeue a buffer from the producer side, it should
+ // always return the same one. And we won't run out of buffers because it's
+ // always the same one and because async mode gets enabled.
+ int slot;
+ for (int i = 0; i < 5; i++) {
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(singleSlot, slot);
+ ASSERT_EQ(OK, mProducer->queueBuffer(singleSlot, input, &output));
+ }
+
+ // acquire the buffer
+ BufferItem item;
+ ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+ ASSERT_EQ(singleSlot, item.mSlot);
+ testBufferItem(input, item);
+ ASSERT_EQ(true, item.mQueuedBuffer);
+ ASSERT_EQ(false, item.mAutoRefresh);
+
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+
+ // attempt to acquire a second time should return no buffer available
+ ASSERT_EQ(IGraphicBufferConsumer::NO_BUFFER_AVAILABLE,
+ mConsumer->acquireBuffer(&item, 0));
+}
+
+TEST_F(BufferQueueTest, TestSingleBufferModeWithAutoRefresh) {
+ createBufferQueue();
+ sp<DummyConsumer> dc(new DummyConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+ IGraphicBufferProducer::QueueBufferOutput output;
+ ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
+ NATIVE_WINDOW_API_CPU, true, &output));
+
+ ASSERT_EQ(OK, mProducer->setSingleBufferMode(true));
+ ASSERT_EQ(OK, mProducer->setAutoRefresh(true));
+
+ // Get a buffer
+ int singleSlot;
+ sp<Fence> fence;
+ sp<GraphicBuffer> buffer;
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
+ mProducer->dequeueBuffer(&singleSlot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->requestBuffer(singleSlot, &buffer));
+
+ // Queue the buffer
+ IGraphicBufferProducer::QueueBufferInput input(0, false,
+ HAL_DATASPACE_UNKNOWN, Rect(0, 0, 1, 1),
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE);
+ ASSERT_EQ(OK, mProducer->queueBuffer(singleSlot, input, &output));
+
// Repeatedly acquire and release a buffer from the consumer side, it should
// always return the same one.
BufferItem item;
for (int i = 0; i < 5; i++) {
ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
ASSERT_EQ(singleSlot, item.mSlot);
- ASSERT_EQ(0, item.mTimestamp);
- ASSERT_EQ(false, item.mIsAutoTimestamp);
- ASSERT_EQ(HAL_DATASPACE_UNKNOWN, item.mDataSpace);
- ASSERT_EQ(Rect(0, 0, 1, 1), item.mCrop);
- ASSERT_EQ(NATIVE_WINDOW_SCALING_MODE_FREEZE, item.mScalingMode);
- ASSERT_EQ(0u, item.mTransform);
- ASSERT_EQ(Fence::NO_FENCE, item.mFence);
+ testBufferItem(input, item);
+ ASSERT_EQ(i == 0, item.mQueuedBuffer);
+ ASSERT_EQ(true, item.mAutoRefresh);
ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
@@ -585,6 +653,8 @@
ASSERT_EQ(NATIVE_WINDOW_SCALING_MODE_FREEZE, item.mScalingMode);
ASSERT_EQ(0u, item.mTransform);
ASSERT_EQ(Fence::NO_FENCE, item.mFence);
+ ASSERT_EQ(i == 0, item.mQueuedBuffer);
+ ASSERT_EQ(true, item.mAutoRefresh);
ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp
index fe649fb..0627ca6 100644
--- a/libs/input/KeyCharacterMap.cpp
+++ b/libs/input/KeyCharacterMap.cpp
@@ -608,7 +608,7 @@
return NULL;
}
if (numKeys > MAX_KEYS) {
- ALOGE("Too many keys in KeyCharacterMap (%d > %d)", numKeys, MAX_KEYS);
+ ALOGE("Too many keys in KeyCharacterMap (%zu > %d)", numKeys, MAX_KEYS);
return NULL;
}
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index c7e2afb..05700f8 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -1197,6 +1197,7 @@
//XXX: temporary hack for the EGL hook-up for single buffer mode
if (attribute == EGL_RENDER_BUFFER && (value == EGL_BACK_BUFFER ||
value == EGL_SINGLE_BUFFER)) {
+ native_window_set_auto_refresh(s->win.get(), true);
return (native_window_set_single_buffer_mode(s->win.get(),
value == EGL_SINGLE_BUFFER)) ? EGL_TRUE : EGL_FALSE;
}
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index 64c1dd9..1a0d689 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -547,8 +547,14 @@
return 0;
}
-status_t VirtualDisplaySurface::setSingleBufferMode(bool singleBufferMode) {
- return mSource[SOURCE_SINK]->setSingleBufferMode(singleBufferMode);
+status_t VirtualDisplaySurface::setSingleBufferMode(bool /*singleBufferMode*/) {
+ ALOGE("setSingleBufferMode not supported on VirtualDisplaySurface");
+ return INVALID_OPERATION;
+}
+
+status_t VirtualDisplaySurface::setAutoRefresh(bool /*autoRefresh*/) {
+ ALOGE("setAutoRefresh not supported on VirtualDisplaySurface");
+ return INVALID_OPERATION;
}
status_t VirtualDisplaySurface::setDequeueTimeout(nsecs_t /* timeout */) {
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index 7f451a9..ede204c 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -121,6 +121,7 @@
virtual String8 getConsumerName() const override;
virtual uint64_t getNextFrameNumber() const override;
virtual status_t setSingleBufferMode(bool singleBufferMode) override;
+ virtual status_t setAutoRefresh(bool autoRefresh) override;
virtual status_t setDequeueTimeout(nsecs_t timeout) override;
//
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 28e5e43..9dca798 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -88,7 +88,7 @@
mQueueItems(),
mLastFrameNumberReceived(0),
mUpdateTexImageFailed(false),
- mSingleBufferMode(false)
+ mAutoRefresh(false)
{
mCurrentCrop.makeInvalid();
mFlinger->getRenderEngine().genTextures(1, &mTextureName);
@@ -1263,7 +1263,7 @@
// ----------------------------------------------------------------------------
bool Layer::shouldPresentNow(const DispSync& dispSync) const {
- if (mSidebandStreamChanged || mSingleBufferMode) {
+ if (mSidebandStreamChanged || mAutoRefresh) {
return true;
}
@@ -1287,7 +1287,7 @@
bool Layer::onPreComposition() {
mRefreshPending = false;
- return mQueuedFrames > 0 || mSidebandStreamChanged || mSingleBufferMode;
+ return mQueuedFrames > 0 || mSidebandStreamChanged || mAutoRefresh;
}
void Layer::onPostComposition() {
@@ -1344,7 +1344,7 @@
}
Region outDirtyRegion;
- if (mQueuedFrames > 0 || mSingleBufferMode) {
+ if (mQueuedFrames > 0 || mAutoRefresh) {
// if we've already called updateTexImage() without going through
// a composition step, we have to skip this layer at this point
@@ -1510,7 +1510,7 @@
// buffer mode.
bool queuedBuffer = false;
status_t updateResult = mSurfaceFlingerConsumer->updateTexImage(&r,
- mFlinger->mPrimaryDispSync, &mSingleBufferMode, &queuedBuffer,
+ mFlinger->mPrimaryDispSync, &mAutoRefresh, &queuedBuffer,
mLastFrameNumberReceived);
if (updateResult == BufferQueue::PRESENT_LATER) {
// Producer doesn't want buffer to be displayed yet. Signal a
@@ -1566,7 +1566,7 @@
// Decrement the queued-frames count. Signal another event if we
// have more frames pending.
if ((queuedBuffer && android_atomic_dec(&mQueuedFrames) > 1)
- || mSingleBufferMode) {
+ || mAutoRefresh) {
mFlinger->signalLayerUpdate();
}
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index d91e94e..1773daf 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -307,7 +307,7 @@
* Returns if a frame is queued.
*/
bool hasQueuedFrame() const { return mQueuedFrames > 0 ||
- mSidebandStreamChanged || mSingleBufferMode; }
+ mSidebandStreamChanged || mAutoRefresh; }
// -----------------------------------------------------------------------
@@ -493,7 +493,7 @@
std::atomic<uint64_t> mLastFrameNumberReceived;
bool mUpdateTexImageFailed; // This is only modified from the main thread
- bool mSingleBufferMode;
+ bool mAutoRefresh;
};
// ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/MonitoredProducer.cpp b/services/surfaceflinger/MonitoredProducer.cpp
index efc44ab..e161d9f 100644
--- a/services/surfaceflinger/MonitoredProducer.cpp
+++ b/services/surfaceflinger/MonitoredProducer.cpp
@@ -135,6 +135,10 @@
return mProducer->setSingleBufferMode(singleBufferMode);
}
+status_t MonitoredProducer::setAutoRefresh(bool autoRefresh) {
+ return mProducer->setAutoRefresh(autoRefresh);
+}
+
status_t MonitoredProducer::setDequeueTimeout(nsecs_t timeout) {
return mProducer->setDequeueTimeout(timeout);
}
diff --git a/services/surfaceflinger/MonitoredProducer.h b/services/surfaceflinger/MonitoredProducer.h
index aea2e39..35ce558 100644
--- a/services/surfaceflinger/MonitoredProducer.h
+++ b/services/surfaceflinger/MonitoredProducer.h
@@ -60,7 +60,8 @@
virtual uint64_t getNextFrameNumber() const override;
virtual status_t setDequeueTimeout(nsecs_t timeout) override;
virtual IBinder* onAsBinder();
- virtual status_t setSingleBufferMode(bool singleBufferMode);
+ virtual status_t setSingleBufferMode(bool singleBufferMode) override;
+ virtual status_t setAutoRefresh(bool autoRefresh) override;
private:
sp<IGraphicBufferProducer> mProducer;
diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.cpp b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
index 5722fb4..d3b66e6 100644
--- a/services/surfaceflinger/SurfaceFlingerConsumer.cpp
+++ b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
@@ -32,7 +32,7 @@
// ---------------------------------------------------------------------------
status_t SurfaceFlingerConsumer::updateTexImage(BufferRejecter* rejecter,
- const DispSync& dispSync, bool* singleBufferMode, bool* queuedBuffer,
+ const DispSync& dispSync, bool* autoRefresh, bool* queuedBuffer,
uint64_t maxFrameNumber)
{
ATRACE_CALL();
@@ -78,8 +78,8 @@
return BUFFER_REJECTED;
}
- if (singleBufferMode) {
- *singleBufferMode = item.mSingleBufferMode;
+ if (autoRefresh) {
+ *autoRefresh = item.mAutoRefresh;
}
if (queuedBuffer) {
diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.h b/services/surfaceflinger/SurfaceFlingerConsumer.h
index 207c243..f3942ab 100644
--- a/services/surfaceflinger/SurfaceFlingerConsumer.h
+++ b/services/surfaceflinger/SurfaceFlingerConsumer.h
@@ -57,7 +57,7 @@
// this does not guarantee that the buffer has been bound to the GL
// texture.
status_t updateTexImage(BufferRejecter* rejecter, const DispSync& dispSync,
- bool* singleBufferMode, bool* queuedBuffer,
+ bool* autoRefresh, bool* queuedBuffer,
uint64_t maxFrameNumber = 0);
// See GLConsumer::bindTextureImageLocked().
diff --git a/vulkan/tools/vkinfo.cpp b/vulkan/tools/vkinfo.cpp
index b88c35d..3dc6653 100644
--- a/vulkan/tools/vkinfo.cpp
+++ b/vulkan/tools/vkinfo.cpp
@@ -29,6 +29,12 @@
namespace {
+struct Options {
+ bool layer_description;
+ bool layer_extensions;
+ bool validate;
+};
+
struct GpuInfo {
VkPhysicalDeviceProperties properties;
VkPhysicalDeviceMemoryProperties memory;
@@ -116,7 +122,9 @@
die("vkEnumerateDeviceExtensionProperties (data)", result);
}
-void GatherGpuInfo(VkPhysicalDevice gpu, GpuInfo& info) {
+void GatherGpuInfo(VkPhysicalDevice gpu,
+ const Options &options,
+ GpuInfo& info) {
VkResult result;
uint32_t count;
@@ -188,7 +196,7 @@
.pQueueCreateInfos = &queue_create_info,
.enabledExtensionCount = num_extensions,
.ppEnabledExtensionNames = extensions,
- .enabledLayerCount = num_layers,
+ .enabledLayerCount = (options.validate) ? num_layers : 0,
.ppEnabledLayerNames = kValidationLayers,
.pEnabledFeatures = &info.features,
};
@@ -198,7 +206,7 @@
vkDestroyDevice(device, nullptr);
}
-void GatherInfo(VulkanInfo* info) {
+void GatherInfo(VulkanInfo* info, const Options& options) {
VkResult result;
uint32_t count;
@@ -253,7 +261,7 @@
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
.enabledExtensionCount = num_extensions,
.ppEnabledExtensionNames = extensions,
- .enabledLayerCount = num_layers,
+ .enabledLayerCount = (options.validate) ? num_layers : 0,
.ppEnabledLayerNames = kValidationLayers,
};
VkInstance instance;
@@ -275,18 +283,13 @@
info->gpus.resize(num_gpus);
for (size_t i = 0; i < gpus.size(); i++)
- GatherGpuInfo(gpus[i], info->gpus.at(i));
+ GatherGpuInfo(gpus[i], options, info->gpus.at(i));
vkDestroyInstance(instance, nullptr);
}
// ----------------------------------------------------------------------------
-struct Options {
- bool layer_description;
- bool layer_extensions;
-};
-
const size_t kMaxIndent = 8;
const size_t kIndentSize = 3;
std::array<char, kMaxIndent * kIndentSize + 1> kIndent;
@@ -513,6 +516,7 @@
static volatile bool startup_pause = false;
Options options = {
.layer_description = false, .layer_extensions = false,
+ .validate = false,
};
for (int argi = 1; argi < argc; argi++) {
if (strcmp(argv[argi], "-v") == 0) {
@@ -522,6 +526,8 @@
options.layer_description = true;
} else if (strcmp(argv[argi], "-layer_extensions") == 0) {
options.layer_extensions = true;
+ } else if (strcmp(argv[argi], "-validate") == 0) {
+ options.validate = true;
} else if (strcmp(argv[argi], "-debug_pause") == 0) {
startup_pause = true;
}
@@ -532,7 +538,7 @@
}
VulkanInfo info;
- GatherInfo(&info);
+ GatherInfo(&info, options);
PrintInfo(info, options);
return 0;
}