Merge "dumpsys: report per-service dump times"
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 08eff9e..3ca7de9 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -363,7 +363,7 @@
// Check whether the category would be supported on the device if the user
// were root. This function assumes that root is able to write to any file
// that exists. It performs the same logic as isCategorySupported, but it
-// uses file existance rather than writability in the /sys/ file checks.
+// uses file existence rather than writability in the /sys/ file checks.
static bool isCategorySupportedForRoot(const TracingCategory& category)
{
bool ok = category.tags != 0;
@@ -970,9 +970,9 @@
" -k fname,... trace the listed kernel functions\n"
" -n ignore signals\n"
" -s N sleep for N seconds before tracing [default 0]\n"
- " -t N trace for N seconds [defualt 5]\n"
+ " -t N trace for N seconds [default 5]\n"
" -z compress the trace dump\n"
- " --async_start start circular trace and return immediatly\n"
+ " --async_start start circular trace and return immediately\n"
" --async_dump dump the current contents of circular trace buffer\n"
" --async_stop stop tracing and dump the current contents of circular\n"
" trace buffer\n"
diff --git a/cmds/bugreportz/bugreportz.cpp b/cmds/bugreportz/bugreportz.cpp
index 19d2d64..312dceb 100644
--- a/cmds/bugreportz/bugreportz.cpp
+++ b/cmds/bugreportz/bugreportz.cpp
@@ -80,8 +80,8 @@
}
if (s == -1) {
- printf("Failed to connect to dumpstatez service: %s\n", strerror(errno));
- return EXIT_FAILURE;
+ printf("FAIL:Failed to connect to dumpstatez service: %s\n", strerror(errno));
+ return EXIT_SUCCESS;
}
// Set a timeout so that if nothing is read in 10 minutes, we'll stop
@@ -91,7 +91,7 @@
tv.tv_sec = 10 * 60;
tv.tv_usec = 0;
if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) {
- printf("WARNING: Cannot set socket timeout: %s\n", strerror(errno));
+ fprintf(stderr, "WARNING: Cannot set socket timeout: %s\n", strerror(errno));
}
while (1) {
@@ -105,8 +105,7 @@
if (errno == EAGAIN) {
errno = ETIMEDOUT;
}
- printf("\nBugreport read terminated abnormally (%s).\n",
- strerror(errno));
+ printf("FAIL:Bugreport read terminated abnormally (%s)\n", strerror(errno));
break;
}
@@ -117,15 +116,17 @@
write(STDOUT_FILENO, buffer + bytes_read - bytes_to_send,
bytes_to_send));
if (bytes_written == -1) {
- printf(
+ fprintf(stderr,
"Failed to write data to stdout: read %zd, trying to send %zd (%s)\n",
bytes_read, bytes_to_send, strerror(errno));
- return EXIT_FAILURE;
+ break;
}
bytes_to_send -= bytes_written;
} while (bytes_written != 0 && bytes_to_send > 0);
}
- close(s);
+ if (close(s) == -1) {
+ fprintf(stderr, "WARNING: error closing socket: %s\n", strerror(errno));
+ }
return EXIT_SUCCESS;
}
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 009988a..5bfa919 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -72,6 +72,8 @@
#define RECOVERY_DIR "/cache/recovery"
#define RECOVERY_DATA_DIR "/data/misc/recovery"
#define LOGPERSIST_DATA_DIR "/data/misc/logd"
+#define PROFILE_DATA_DIR_CUR "/data/misc/profiles/cur"
+#define PROFILE_DATA_DIR_REF "/data/misc/profiles/ref"
#define TOMBSTONE_DIR "/data/tombstones"
#define TOMBSTONE_FILE_PREFIX TOMBSTONE_DIR "/tombstone_"
/* Can accomodate a tombstone number up to 9999. */
@@ -97,7 +99,7 @@
// TODO: change to "v1" before final N build
static std::string VERSION_DEFAULT = "v1-dev4";
-static bool is_user_build() {
+bool is_user_build() {
return 0 == strncmp(build_type, "user", PROPERTY_VALUE_MAX - 1);
}
@@ -620,7 +622,6 @@
return add_zip_entry_from_fd(ZIP_ROOT_DIR + path, fd) ? 0 : 1;
}
-/* adds all files from a directory to the zipped bugreport file */
void add_dir(const char *dir, bool recursive) {
if (!zip_writer) {
MYLOGD("Not adding dir %s because zip_writer is not set\n", dir);
@@ -857,7 +858,7 @@
#endif
dump_file("INTERRUPTS (1)", "/proc/interrupts");
- run_command("NETWORK DIAGNOSTICS", 10, "dumpsys", "connectivity", "--diag", NULL);
+ run_command("NETWORK DIAGNOSTICS", 10, "dumpsys", "-t", "10", "connectivity", "--diag", NULL);
#ifdef FWDUMP_bcmdhd
run_command("DUMP WIFI STATUS", 20,
@@ -933,30 +934,30 @@
printf("== Checkins\n");
printf("========================================================\n");
- run_command("CHECKIN BATTERYSTATS", 30, "dumpsys", "batterystats", "-c", NULL);
- run_command("CHECKIN MEMINFO", 30, "dumpsys", "meminfo", "--checkin", NULL);
- run_command("CHECKIN NETSTATS", 30, "dumpsys", "netstats", "--checkin", NULL);
- run_command("CHECKIN PROCSTATS", 30, "dumpsys", "procstats", "-c", NULL);
- run_command("CHECKIN USAGESTATS", 30, "dumpsys", "usagestats", "-c", NULL);
- run_command("CHECKIN PACKAGE", 30, "dumpsys", "package", "--checkin", NULL);
+ run_command("CHECKIN BATTERYSTATS", 30, "dumpsys", "-t", "30", "batterystats", "-c", NULL);
+ run_command("CHECKIN MEMINFO", 30, "dumpsys", "-t", "30", "meminfo", "--checkin", NULL);
+ run_command("CHECKIN NETSTATS", 30, "dumpsys", "-t", "30", "netstats", "--checkin", NULL);
+ run_command("CHECKIN PROCSTATS", 30, "dumpsys", "-t", "30", "procstats", "-c", NULL);
+ run_command("CHECKIN USAGESTATS", 30, "dumpsys", "-t", "30", "usagestats", "-c", NULL);
+ run_command("CHECKIN PACKAGE", 30, "dumpsys", "-t", "30", "package", "--checkin", NULL);
printf("========================================================\n");
printf("== Running Application Activities\n");
printf("========================================================\n");
- run_command("APP ACTIVITIES", 30, "dumpsys", "activity", "all", NULL);
+ run_command("APP ACTIVITIES", 30, "-t", "30", "dumpsys", "activity", "all", NULL);
printf("========================================================\n");
printf("== Running Application Services\n");
printf("========================================================\n");
- run_command("APP SERVICES", 30, "dumpsys", "activity", "service", "all", NULL);
+ run_command("APP SERVICES", 30, "-t", "30", "dumpsys", "activity", "service", "all", NULL);
printf("========================================================\n");
printf("== Running Application Providers\n");
printf("========================================================\n");
- run_command("APP SERVICES", 30, "dumpsys", "activity", "provider", "all", NULL);
+ run_command("APP SERVICES", 30, "-t", "30", "dumpsys", "activity", "provider", "all", NULL);
printf("========================================================\n");
@@ -1315,7 +1316,7 @@
// Invoking the following dumpsys calls before dump_traces() to try and
// keep the system stats as close to its initial state as possible.
run_command_as_shell("DUMPSYS MEMINFO", 30, "dumpsys", "-t", "30", "meminfo", "-a", NULL);
- run_command_as_shell("DUMPSYS CPUINFO", 10, "dumpsys", "cpuinfo", "-a", NULL);
+ run_command_as_shell("DUMPSYS CPUINFO", 10, "dumpsys", "-t", "10", "cpuinfo", "-a", NULL);
/* collect stack traces from Dalvik and native processes (needs root) */
dump_traces_path = dump_traces();
@@ -1325,6 +1326,10 @@
add_dir(RECOVERY_DIR, true);
add_dir(RECOVERY_DATA_DIR, true);
add_dir(LOGPERSIST_DATA_DIR, false);
+ if (!is_user_build()) {
+ add_dir(PROFILE_DATA_DIR_CUR, true);
+ add_dir(PROFILE_DATA_DIR_REF, true);
+ }
add_mountinfo();
if (!drop_root_user()) {
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 94bfc5a..4769974 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -93,6 +93,9 @@
/* adds a new entry to the existing zip file. */
bool add_zip_entry_from_fd(const std::string& entry_name, int fd);
+/* adds all files from a directory to the zipped bugreport file */
+void add_dir(const char *dir, bool recursive);
+
/* prints the contents of a file */
int dump_file(const char *title, const char *path);
@@ -196,12 +199,15 @@
/** Gets the last modification time of a file, or default time if file is not found. */
time_t get_mtime(int fd, time_t default_mtime);
-/* dump eMMC Extended CSD data */
+/* Dumps eMMC Extended CSD data. */
void dump_emmc_ecsd(const char *ext_csd_path);
-/** gets command-line arguments */
+/** Gets command-line arguments. */
void format_args(int argc, const char *argv[], std::string *args);
+/** Tells if the device is running a user build. */
+bool is_user_build();
+
/*
* Helper class used to report how long it takes for a section to finish.
*
diff --git a/cmds/installd/commands.cpp b/cmds/installd/commands.cpp
index 25a5a80..90ccbf8 100644
--- a/cmds/installd/commands.cpp
+++ b/cmds/installd/commands.cpp
@@ -28,8 +28,9 @@
#include <sys/xattr.h>
#include <unistd.h>
-#include <android-base/stringprintf.h>
#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <cutils/fs.h>
#include <cutils/log.h> // TODO: Move everything to base/logging.
@@ -48,6 +49,7 @@
#define LOG_TAG "installd"
#endif
+using android::base::EndsWith;
using android::base::StringPrintf;
namespace android {
@@ -56,7 +58,7 @@
static constexpr const char* kCpPath = "/system/bin/cp";
static constexpr const char* kXattrDefault = "user.default";
-#define MIN_RESTRICTED_HOME_SDK_VERSION 24 // > M
+static constexpr const int MIN_RESTRICTED_HOME_SDK_VERSION = 24; // > M
typedef int fd_t;
@@ -69,6 +71,12 @@
return strcmp(tmp_property_value, "true") == 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);
+}
+
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);
@@ -104,6 +112,12 @@
PLOG(ERROR) << "Failed to prepare " << profile_path;
return -1;
}
+ std::string profile_file = create_primary_profile(profile_path);
+ // read-write only for the app user.
+ if (fs_prepare_file_strict(profile_file.c_str(), 0600, 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.
@@ -156,12 +170,6 @@
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 bool clear_profile(const std::string& profile) {
base::unique_fd ufd(open(profile.c_str(), O_WRONLY | O_NOFOLLOW | O_CLOEXEC));
if (ufd.get() < 0) {
@@ -1085,7 +1093,7 @@
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 void run_profman_merge(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";
@@ -1133,13 +1141,13 @@
return false;
}
- ALOGV("PROFMAN: --- BEGIN '%s' ---\n", pkgname);
+ ALOGV("PROFMAN (MERGE): --- 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);
+ run_profman_merge(profiles_fd, reference_profile_fd);
exit(68); /* only get here on exec failure */
}
/* parent */
@@ -1199,13 +1207,136 @@
return need_to_compile;
}
-static void trim_extension(char* path) {
- // Trim the extension.
- int pos = strlen(path);
- for (; pos >= 0 && path[pos] != '.'; --pos) {}
- if (pos >= 0) {
- path[pos] = '\0'; // Trim extension
+static void run_profman_dump(const std::vector<fd_t>& profile_fds,
+ fd_t reference_profile_fd,
+ const std::vector<std::string>& dex_locations,
+ const std::vector<fd_t>& apk_fds,
+ fd_t output_fd) {
+ std::vector<std::string> profman_args;
+ static const char* PROFMAN_BIN = "/system/bin/profman";
+ profman_args.push_back(PROFMAN_BIN);
+ profman_args.push_back("--dump-only");
+ profman_args.push_back(StringPrintf("--dump-output-to-fd=%d", output_fd));
+ if (reference_profile_fd != -1) {
+ profman_args.push_back(StringPrintf("--reference-profile-file-fd=%d",
+ reference_profile_fd));
+ }
+ for (fd_t profile_fd : profile_fds) {
+ profman_args.push_back(StringPrintf("--profile-file-fd=%d", profile_fd));
+ }
+ for (const std::string& dex_location : dex_locations) {
+ profman_args.push_back(StringPrintf("--dex-location=%s", dex_location.c_str()));
+ }
+ for (fd_t apk_fd : apk_fds) {
+ profman_args.push_back(StringPrintf("--apk-fd=%d", apk_fd));
+ }
+ const char **argv = new const char*[profman_args.size() + 1];
+ size_t i = 0;
+ for (const std::string& profman_arg : profman_args) {
+ argv[i++] = profman_arg.c_str();
+ }
+ 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 */
+}
+
+static const char* get_location_from_path(const char* path) {
+ static constexpr char kLocationSeparator = '/';
+ const char *location = strrchr(path, kLocationSeparator);
+ if (location == NULL) {
+ return path;
+ } else {
+ // Skip the separator character.
+ return location + 1;
+ }
+}
+
+// Dumps the contents of a profile file, using pkgname's dex files for pretty
+// printing the result.
+bool dump_profile(uid_t uid, const char* pkgname, const char* code_path_string) {
+ 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);
+
+ open_profile_files(uid, pkgname, &profile_fds, &reference_profile_fd);
+
+ const bool has_reference_profile = (reference_profile_fd != -1);
+ const bool has_profiles = !profile_fds.empty();
+
+ if (!has_reference_profile && !has_profiles) {
+ ALOGE("profman dump: no profiles to dump for '%s'", pkgname);
+ return false;
+ }
+
+ fd_t output_fd = open(out_file_name.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW);
+ if (fchmod(output_fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
+ ALOGE("installd cannot chmod '%s' dump_profile\n", out_file_name.c_str());
+ return false;
+ }
+ std::vector<std::string> code_full_paths = base::Split(code_path_string, ";");
+ std::vector<std::string> dex_locations;
+ std::vector<fd_t> apk_fds;
+ for (const std::string& code_full_path : code_full_paths) {
+ const char* full_path = code_full_path.c_str();
+ fd_t apk_fd = open(full_path, O_RDONLY | O_NOFOLLOW);
+ if (apk_fd == -1) {
+ ALOGE("installd cannot open '%s'\n", full_path);
+ return false;
+ }
+ dex_locations.push_back(get_location_from_path(full_path));
+ apk_fds.push_back(apk_fd);
+ }
+
+ pid_t pid = fork();
+ if (pid == 0) {
+ /* child -- drop privileges before continuing */
+ drop_capabilities(uid);
+ run_profman_dump(profile_fds, reference_profile_fd, dex_locations,
+ apk_fds, output_fd);
+ exit(68); /* only get here on exec failure */
+ }
+ /* parent */
+ close_all_fds(apk_fds, "apk_fds");
+ close_all_fds(profile_fds, "profile_fds");
+ if (close(reference_profile_fd) != 0) {
+ PLOG(WARNING) << "Failed to close fd for reference profile";
+ }
+ int return_code = wait_child(pid);
+ if (!WIFEXITED(return_code)) {
+ LOG(WARNING) << "profman failed for package " << pkgname << ": "
+ << return_code;
+ return false;
+ }
+ return true;
+}
+
+// Translate the given oat path to an art (app image) path. An empty string
+// denotes an error.
+static std::string create_image_filename(const std::string& oat_path) {
+ // A standard dalvik-cache entry. Replace ".dex" with ".art."
+ if (EndsWith(oat_path, ".dex")) {
+ std::string art_path = oat_path;
+ art_path.replace(art_path.length() - strlen("dex"), strlen("dex"), "art");
+ CHECK(EndsWith(art_path, ".art"));
+ return art_path;
}
+
+ // An odex entry. Not that this may not be an extension, e.g., in the OTA
+ // case (where the base name will have an extension for the B artifact).
+ size_t odex_pos = oat_path.rfind(".odex");
+ if (odex_pos != std::string::npos) {
+ std::string art_path = oat_path;
+ art_path.replace(odex_pos, strlen(".odex"), ".art");
+ CHECK_NE(art_path.find(".art"), std::string::npos);
+ return art_path;
+ }
+
+ // Don't know how to handle this.
+ return "";
}
static bool add_extension_to_file_name(char* file_name, const char* extension) {
@@ -1216,7 +1347,7 @@
return true;
}
-static int open_output_file(char* file_name, bool recreate, int permissions) {
+static int open_output_file(const char* file_name, bool recreate, int permissions) {
int flags = O_RDWR | O_CREAT;
if (recreate) {
if (unlink(file_name) < 0) {
@@ -1274,19 +1405,110 @@
return analyse_profiles(uid, pkgname);
}
+static const char* parse_null(const char* arg) {
+ if (strcmp(arg, "!") == 0) {
+ return nullptr;
+ } else {
+ return arg;
+ }
+}
+
+int dexopt(const char* params[DEXOPT_PARAM_COUNT]) {
+ return dexopt(params[0], // apk_path
+ atoi(params[1]), // uid
+ params[2], // pkgname
+ params[3], // instruction_set
+ atoi(params[4]), // dexopt_needed
+ params[5], // oat_dir
+ atoi(params[6]), // dexopt_flags
+ params[7], // compiler_filter
+ parse_null(params[8]), // volume_uuid
+ parse_null(params[9])); // shared_libraries
+ static_assert(DEXOPT_PARAM_COUNT == 10U, "Unexpected dexopt param count");
+}
+
+// Helper for fd management. This is similar to a unique_fd in that it closes the file descriptor
+// on destruction. It will also run the given cleanup (unless told not to) after closing.
+//
+// Usage example:
+//
+// Dex2oatFileWrapper<std::function<void ()>> file(open(...),
+// [name]() {
+// unlink(name.c_str());
+// });
+// // Note: care needs to be taken about name, as it needs to have a lifetime longer than the
+// wrapper if captured as a reference.
+//
+// if (file.get() == -1) {
+// // Error opening...
+// }
+//
+// ...
+// if (error) {
+// // At this point, when the Dex2oatFileWrapper is destructed, the cleanup function will run
+// // and delete the file (after the fd is closed).
+// return -1;
+// }
+//
+// (Success case)
+// file.SetCleanup(false);
+// // At this point, when the Dex2oatFileWrapper is destructed, the cleanup function will not run
+// // (leaving the file around; after the fd is closed).
+//
+template <typename Cleanup>
+class Dex2oatFileWrapper {
+ public:
+ Dex2oatFileWrapper() : value_(-1), cleanup_(), do_cleanup_(true) {
+ }
+
+ Dex2oatFileWrapper(int value, Cleanup cleanup)
+ : value_(value), cleanup_(cleanup), do_cleanup_(true) {}
+
+ ~Dex2oatFileWrapper() {
+ reset(-1);
+ }
+
+ int get() {
+ return value_;
+ }
+
+ void SetCleanup(bool cleanup) {
+ do_cleanup_ = cleanup;
+ }
+
+ void reset(int new_value) {
+ if (value_ >= 0) {
+ close(value_);
+ }
+ if (do_cleanup_ && cleanup_ != nullptr) {
+ cleanup_();
+ }
+
+ value_ = new_value;
+ }
+
+ void reset(int new_value, Cleanup new_cleanup) {
+ if (value_ >= 0) {
+ close(value_);
+ }
+ if (do_cleanup_ && cleanup_ != nullptr) {
+ cleanup_();
+ }
+
+ value_ = new_value;
+ cleanup_ = new_cleanup;
+ }
+
+ private:
+ int value_;
+ Cleanup cleanup_;
+ bool do_cleanup_;
+};
+
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)
{
- struct utimbuf ut;
- struct stat input_stat;
- char out_path[PKG_PATH_MAX];
- char swap_file_name[PKG_PATH_MAX];
- char image_path[PKG_PATH_MAX];
- const char *input_file;
- char in_odex_path[PKG_PATH_MAX];
- 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;
@@ -1296,12 +1518,16 @@
CHECK(pkgname != nullptr);
CHECK(pkgname[0] != 0);
- fd_t reference_profile_fd = -1;
// Public apps should not be compiled with profile information ever. Same goes for the special
// package '*' used for the system server.
+ Dex2oatFileWrapper<std::function<void ()>> reference_profile_fd;
if (!is_public && pkgname[0] != '*') {
// Open reference profile in read only mode as dex2oat does not get write permissions.
- reference_profile_fd = open_reference_profile(uid, pkgname, /*read_write*/ false);
+ const std::string pkgname_str(pkgname);
+ reference_profile_fd.reset(open_reference_profile(uid, pkgname, /*read_write*/ false),
+ [pkgname_str]() {
+ clear_reference_profile(pkgname_str.c_str());
+ });
// Note: it's OK to not find a profile here.
}
@@ -1309,10 +1535,13 @@
LOG_FATAL("dexopt flags contains unknown fields\n");
}
+ char out_path[PKG_PATH_MAX];
if (!create_oat_out_path(apk_path, instruction_set, oat_dir, out_path)) {
return false;
}
+ const char *input_file;
+ char in_odex_path[PKG_PATH_MAX];
switch (dexopt_needed) {
case DEXOPT_DEX2OAT_NEEDED:
input_file = apk_path;
@@ -1331,35 +1560,41 @@
default:
ALOGE("Invalid dexopt needed: %d\n", dexopt_needed);
- exit(72);
+ return 72;
}
+ struct stat input_stat;
memset(&input_stat, 0, sizeof(input_stat));
stat(input_file, &input_stat);
- input_fd = open(input_file, O_RDONLY, 0);
- if (input_fd < 0) {
+ base::unique_fd input_fd(open(input_file, O_RDONLY, 0));
+ if (input_fd.get() < 0) {
ALOGE("installd cannot open '%s' for input during dexopt\n", input_file);
return -1;
}
- out_fd = open_output_file(out_path, /*recreate*/true, /*permissions*/0644);
- if (out_fd < 0) {
+ const std::string out_path_str(out_path);
+ Dex2oatFileWrapper<std::function<void ()>> out_fd(
+ open_output_file(out_path, /*recreate*/true, /*permissions*/0644),
+ [out_path_str]() { unlink(out_path_str.c_str()); });
+ if (out_fd.get() < 0) {
ALOGE("installd cannot open '%s' for output during dexopt\n", out_path);
- goto fail;
+ return -1;
}
- if (!set_permissions_and_ownership(out_fd, is_public, uid, out_path)) {
- goto fail;
+ if (!set_permissions_and_ownership(out_fd.get(), is_public, uid, out_path)) {
+ return -1;
}
// Create a swap file if necessary.
+ base::unique_fd swap_fd;
if (ShouldUseSwapFileForDexopt()) {
// Make sure there really is enough space.
+ char swap_file_name[PKG_PATH_MAX];
strcpy(swap_file_name, out_path);
if (add_extension_to_file_name(swap_file_name, ".swap")) {
- swap_fd = open_output_file(swap_file_name, /*recreate*/true, /*permissions*/0600);
+ swap_fd.reset(open_output_file(swap_file_name, /*recreate*/true, /*permissions*/0600));
}
- if (swap_fd < 0) {
+ if (swap_fd.get() < 0) {
// Could not create swap file. Optimistically go on and hope that we can compile
// without it.
ALOGE("installd could not create '%s' for swap during dexopt\n", swap_file_name);
@@ -1372,116 +1607,108 @@
}
// Avoid generating an app image for extract only since it will not contain any classes.
- strcpy(image_path, out_path);
- trim_extension(image_path);
- if (add_extension_to_file_name(image_path, ".art")) {
- char app_image_format[kPropertyValueMax];
- bool have_app_image_format =
- get_property("dalvik.vm.appimageformat", app_image_format, NULL) > 0;
- // 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 && have_app_image_format) {
- // Recreate is true since we do not want to modify a mapped image. If the app is already
- // running and we modify the image file, it can cause crashes (b/27493510).
- image_fd = open_output_file(image_path, /*recreate*/true, /*permissions*/0600);
- if (image_fd < 0) {
- // Could not create application image file. Go on since we can compile without it.
- ALOGE("installd could not create '%s' for image file during dexopt\n", image_path);
- } else if (!set_permissions_and_ownership(image_fd, is_public, uid, image_path)) {
- image_fd = -1;
- }
- }
- // If we have a valid image file path but no image fd, erase the image file.
- if (image_fd < 0) {
- if (unlink(image_path) < 0) {
- if (errno != ENOENT) {
- PLOG(ERROR) << "Couldn't unlink image file " << image_path;
- }
- }
- }
+ Dex2oatFileWrapper<std::function<void ()>> image_fd;
+ const std::string image_path = create_image_filename(out_path);
+ if (!image_path.empty()) {
+ char app_image_format[kPropertyValueMax];
+ bool have_app_image_format =
+ get_property("dalvik.vm.appimageformat", app_image_format, NULL) > 0;
+ // 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 && have_app_image_format) {
+ // Recreate is true since we do not want to modify a mapped image. If the app is
+ // already running and we modify the image file, it can cause crashes (b/27493510).
+ image_fd.reset(open_output_file(image_path.c_str(),
+ true /*recreate*/,
+ 0600 /*permissions*/),
+ [image_path]() { unlink(image_path.c_str()); }
+ );
+ if (image_fd.get() < 0) {
+ // Could not create application image file. Go on since we can compile without
+ // it.
+ LOG(ERROR) << "installd could not create '"
+ << image_path
+ << "' for image file during dexopt";
+ } else if (!set_permissions_and_ownership(image_fd.get(),
+ is_public,
+ uid,
+ image_path.c_str())) {
+ image_fd.reset(-1);
+ }
+ }
+ // If we have a valid image file path but no image fd, explicitly erase the image file.
+ if (image_fd.get() < 0) {
+ if (unlink(image_path.c_str()) < 0) {
+ if (errno != ENOENT) {
+ PLOG(ERROR) << "Couldn't unlink image file " << image_path;
+ }
+ }
+ }
}
ALOGV("DexInv: --- BEGIN '%s' ---\n", input_file);
- pid_t pid;
- pid = fork();
+ pid_t pid = fork();
if (pid == 0) {
/* child -- drop privileges before continuing */
drop_capabilities(uid);
SetDex2OatAndPatchOatScheduling(boot_complete);
- if (flock(out_fd, LOCK_EX | LOCK_NB) != 0) {
+ if (flock(out_fd.get(), LOCK_EX | LOCK_NB) != 0) {
ALOGE("flock(%s) failed: %s\n", out_path, strerror(errno));
- exit(67);
+ _exit(67);
}
if (dexopt_needed == DEXOPT_PATCHOAT_NEEDED
|| dexopt_needed == DEXOPT_SELF_PATCHOAT_NEEDED) {
- run_patchoat(input_fd, out_fd, input_file, out_path, pkgname, instruction_set);
+ run_patchoat(input_fd.get(),
+ out_fd.get(),
+ input_file,
+ out_path,
+ pkgname,
+ instruction_set);
} else if (dexopt_needed == DEXOPT_DEX2OAT_NEEDED) {
// Pass dex2oat the relative path to the input file.
- const char *input_file_name = strrchr(input_file, '/');
- if (input_file_name == NULL) {
- input_file_name = input_file;
- } else {
- input_file_name++;
- }
- run_dex2oat(input_fd, out_fd, image_fd, input_file_name, out_path, swap_fd,
- instruction_set, compiler_filter, vm_safe_mode, debuggable, boot_complete,
- reference_profile_fd, shared_libraries);
+ const char *input_file_name = get_location_from_path(input_file);
+ run_dex2oat(input_fd.get(),
+ out_fd.get(),
+ image_fd.get(),
+ input_file_name,
+ out_path,
+ swap_fd.get(),
+ instruction_set,
+ compiler_filter,
+ vm_safe_mode,
+ debuggable,
+ boot_complete,
+ reference_profile_fd.get(),
+ shared_libraries);
} else {
ALOGE("Invalid dexopt needed: %d\n", dexopt_needed);
- exit(73);
+ _exit(73);
}
- exit(68); /* only get here on exec failure */
+ _exit(68); /* only get here on exec failure */
} else {
- res = wait_child(pid);
+ int res = wait_child(pid);
if (res == 0) {
ALOGV("DexInv: --- END '%s' (success) ---\n", input_file);
} else {
ALOGE("DexInv: --- END '%s' --- status=0x%04x, process failed\n", input_file, res);
- goto fail;
+ return -1;
}
}
+ struct utimbuf ut;
ut.actime = input_stat.st_atime;
ut.modtime = input_stat.st_mtime;
utime(out_path, &ut);
- close(out_fd);
- close(input_fd);
- if (swap_fd >= 0) {
- close(swap_fd);
- }
- if (reference_profile_fd >= 0) {
- close(reference_profile_fd);
- }
- if (image_fd >= 0) {
- close(image_fd);
- }
- return 0;
+ // We've been successful, don't delete output.
+ out_fd.SetCleanup(false);
+ image_fd.SetCleanup(false);
+ reference_profile_fd.SetCleanup(false);
-fail:
- if (out_fd >= 0) {
- close(out_fd);
- unlink(out_path);
- }
- if (input_fd >= 0) {
- close(input_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.
- clear_reference_profile(pkgname);
- }
- if (swap_fd >= 0) {
- close(swap_fd);
- }
- if (image_fd >= 0) {
- close(image_fd);
- }
- return -1;
+ return 0;
}
int mark_boot_complete(const char* instruction_set)
@@ -1816,6 +2043,37 @@
return true;
}
+// Move/rename a B artifact (from) to an A artifact (to).
+static bool move_ab_path(const std::string& b_path, const std::string& a_path) {
+ // Check whether B exists.
+ {
+ struct stat s;
+ if (stat(b_path.c_str(), &s) != 0) {
+ // Silently ignore for now. The service calling this isn't smart enough to understand
+ // lack of artifacts at the moment.
+ return false;
+ }
+ if (!S_ISREG(s.st_mode)) {
+ LOG(ERROR) << "A/B artifact " << b_path << " is not a regular file.";
+ // Try to unlink, but swallow errors.
+ unlink(b_path.c_str());
+ return false;
+ }
+ }
+
+ // Rename B to A.
+ if (!unlink_and_rename(b_path.c_str(), a_path.c_str())) {
+ // Delete the b_path so we don't try again (or fail earlier).
+ if (unlink(b_path.c_str()) != 0) {
+ PLOG(ERROR) << "Could not unlink " << b_path;
+ }
+
+ return false;
+ }
+
+ return true;
+}
+
int move_ab(const char* apk_path, const char* instruction_set, const char* oat_dir) {
if (apk_path == nullptr || instruction_set == nullptr || oat_dir == nullptr) {
LOG(ERROR) << "Cannot move_ab with null input";
@@ -1834,37 +2092,35 @@
if (!calculate_oat_file_path(a_path, oat_dir, apk_path, instruction_set)) {
return -1;
}
+ const std::string a_image_path = create_image_filename(a_path);
// B path = A path + ".b"
- std::string b_path = StringPrintf("%s.b", a_path);
+ const std::string b_path = StringPrintf("%s.b", a_path);
+ const std::string b_image_path = StringPrintf("%s.b", a_image_path.c_str());
- // Check whether B exists.
- {
- struct stat s;
- if (stat(b_path.c_str(), &s) != 0) {
- // Silently ignore for now. The service calling this isn't smart enough to understand
- // lack of artifacts at the moment.
- return -1;
+ bool oat_success = move_ab_path(b_path, a_path);
+ bool success;
+
+ if (oat_success) {
+ // Note: we can live without an app image. As such, ignore failure to move the image file.
+ // If we decide to require the app image, or the app image being moved correctly,
+ // then change accordingly.
+ constexpr bool kIgnoreAppImageFailure = true;
+
+ bool art_success = true;
+ if (!a_image_path.empty()) {
+ art_success = move_ab_path(b_image_path, a_image_path);
}
- if (!S_ISREG(s.st_mode)) {
- LOG(ERROR) << "A/B artifact " << b_path << " is not a regular file.";
- // Try to unlink, but swallow errors.
- unlink(b_path.c_str());
- return -1;
- }
+
+ success = art_success || kIgnoreAppImageFailure;
+ } else {
+ // Cleanup: delete B image, ignore errors.
+ unlink(b_image_path.c_str());
+
+ success = false;
}
- // Rename B to A.
- if (!unlink_and_rename(b_path.c_str(), a_path)) {
- // Delete the b_path so we don't try again (or fail earlier).
- if (unlink(b_path.c_str()) != 0) {
- PLOG(ERROR) << "Could not unlink " << b_path;
- }
-
- return -1;
- }
-
- return 0;
+ return success ? 0 : -1;
}
} // namespace installd
diff --git a/cmds/installd/commands.h b/cmds/installd/commands.h
index 41cc209..c0c39c5 100644
--- a/cmds/installd/commands.h
+++ b/cmds/installd/commands.h
@@ -54,9 +54,23 @@
bool merge_profiles(uid_t uid, const char *pkgname);
-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);
+bool dump_profile(uid_t uid, const char *pkgname, const char *dex_files);
+
+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);
+static_assert(DEXOPT_PARAM_COUNT == 10U, "Unexpected dexopt param size");
+
+// Helper for the above, converting arguments.
+int dexopt(const char* params[DEXOPT_PARAM_COUNT]);
+
int mark_boot_complete(const char *instruction_set);
int linklib(const char* uuid, const char* pkgname, const char* asecLibDir, int userId);
int idmap(const char *target_path, const char *overlay_path, uid_t uid);
diff --git a/cmds/installd/installd.cpp b/cmds/installd/installd.cpp
index d99d9dd..68439b2 100644
--- a/cmds/installd/installd.cpp
+++ b/cmds/installd/installd.cpp
@@ -219,7 +219,8 @@
// We use otapreopt_chroot to get into the chroot.
static constexpr const char* kOtaPreopt = "/system/bin/otapreopt_chroot";
-static int do_ota_dexopt(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
+static int do_ota_dexopt(const char* args[DEXOPT_PARAM_COUNT],
+ char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
// Time to fork and run otapreopt.
// Check that the tool exists.
@@ -231,12 +232,14 @@
pid_t pid = fork();
if (pid == 0) {
- const char* argv[1 + 9 + 1];
+ const char* argv[1 + DEXOPT_PARAM_COUNT + 1];
argv[0] = kOtaPreopt;
- for (size_t i = 1; i <= 9; ++i) {
- argv[i] = arg[i - 1];
+
+ for (size_t i = 0; i < DEXOPT_PARAM_COUNT; ++i) {
+ argv[i + 1] = args[i];
}
- argv[10] = nullptr;
+
+ argv[DEXOPT_PARAM_COUNT + 1] = nullptr;
execv(argv[0], (char * const *)argv);
PLOG(ERROR) << "execv(OTAPREOPT_CHROOT) failed";
@@ -252,22 +255,30 @@
}
}
+static int do_regular_dexopt(const char* args[DEXOPT_PARAM_COUNT],
+ char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
+ return dexopt(args);
+}
+
+using DexoptFn = int (*)(const char* args[DEXOPT_PARAM_COUNT],
+ char reply[REPLY_MAX]);
+
static int do_dexopt(char **arg, char reply[REPLY_MAX])
{
- int dexopt_flags = atoi(arg[6]);
- if ((dexopt_flags & DEXOPT_OTA) != 0) {
- return do_ota_dexopt(arg, reply);
+ const char* args[DEXOPT_PARAM_COUNT];
+ for (size_t i = 0; i < DEXOPT_PARAM_COUNT; ++i) {
+ CHECK(arg[i] != nullptr);
+ args[i] = arg[i];
}
- return dexopt(arg[0], // apk_path
- atoi(arg[1]), // uid
- arg[2], // pkgname
- arg[3], // instruction_set
- atoi(arg[4]), // dexopt_needed
- arg[5], // oat_dir
- dexopt_flags,
- arg[7], // compiler_filter
- parse_null(arg[8]), // volume_uuid
- parse_null(arg[9])); // shared_libraries
+
+ int dexopt_flags = atoi(arg[6]);
+ DexoptFn dexopt_fn;
+ if ((dexopt_flags & DEXOPT_OTA) != 0) {
+ dexopt_fn = do_ota_dexopt;
+ } else {
+ dexopt_fn = do_regular_dexopt;
+ }
+ return dexopt_fn(args, reply);
}
static int do_merge_profiles(char **arg, char reply[REPLY_MAX])
@@ -282,6 +293,19 @@
return 0;
}
+static int do_dump_profiles(char **arg, char reply[REPLY_MAX])
+{
+ uid_t uid = static_cast<uid_t>(atoi(arg[0]));
+ const char* pkgname = arg[1];
+ const char* dex_files = arg[2];
+ if (dump_profile(uid, pkgname, dex_files)) {
+ strncpy(reply, "true", REPLY_MAX);
+ } else {
+ strncpy(reply, "false", REPLY_MAX);
+ }
+ return 0;
+}
+
static int do_mark_boot_complete(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
{
return mark_boot_complete(arg[0] /* instruction set */);
@@ -428,6 +452,7 @@
{ "linkfile", 3, do_link_file },
{ "move_ab", 3, do_move_ab },
{ "merge_profiles", 2, do_merge_profiles },
+ { "dump_profiles", 3, do_dump_profiles },
};
static int readx(int s, void *_buf, int count)
diff --git a/cmds/installd/installd_constants.h b/cmds/installd/installd_constants.h
index 8513695..823b8ee 100644
--- a/cmds/installd/installd_constants.h
+++ b/cmds/installd/installd_constants.h
@@ -21,6 +21,8 @@
namespace android {
namespace installd {
+constexpr size_t DEXOPT_PARAM_COUNT = 10U;
+
/* elements combined with a valid package name to form paths */
constexpr const char* PRIMARY_USER_PREFIX = "data/";
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index ac511ec..e1cfc9d 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -30,6 +30,7 @@
#include <android-base/logging.h>
#include <android-base/macros.h>
#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <cutils/fs.h>
#include <cutils/log.h>
#include <cutils/properties.h>
@@ -39,7 +40,6 @@
#include <file_parsing.h>
#include <globals.h>
#include <installd_deps.h> // Need to fill in requirements of commands.
-#include <string_helpers.h>
#include <system_properties.h>
#include <utils.h>
@@ -51,6 +51,10 @@
#define TOKEN_MAX 16 /* max number of arguments in buffer */
#define REPLY_MAX 256 /* largest reply allowed */
+using android::base::EndsWith;
+using android::base::Join;
+using android::base::Split;
+using android::base::StartsWith;
using android::base::StringPrintf;
namespace android {
@@ -188,12 +192,14 @@
bool ReadPackage(int argc ATTRIBUTE_UNUSED, char** argv) {
size_t index = 0;
- while (index < ARRAY_SIZE(package_parameters_) &&
+ static_assert(DEXOPT_PARAM_COUNT == ARRAY_SIZE(package_parameters_),
+ "Unexpected dexopt param count");
+ while (index < DEXOPT_PARAM_COUNT &&
argv[index + 1] != nullptr) {
package_parameters_[index] = argv[index + 1];
index++;
}
- if (index != ARRAY_SIZE(package_parameters_)) {
+ if (index != ARRAY_SIZE(package_parameters_) || argv[index + 1] != nullptr) {
LOG(ERROR) << "Wrong number of parameters";
return false;
}
@@ -295,7 +301,7 @@
std::vector<std::string> cmd;
cmd.push_back("/system/bin/dex2oat");
cmd.push_back(StringPrintf("--image=%s", art_path.c_str()));
- for (const std::string& boot_part : Split(boot_cp, ':')) {
+ for (const std::string& boot_part : Split(boot_cp, ":")) {
cmd.push_back(StringPrintf("--dex-file=%s", boot_part.c_str()));
}
cmd.push_back(StringPrintf("--oat-file=%s", oat_path.c_str()));
@@ -324,7 +330,7 @@
const std::string* extra_opts =
system_properties_.GetProperty("dalvik.vm.image-dex2oat-flags");
if (extra_opts != nullptr) {
- std::vector<std::string> extra_vals = Split(*extra_opts, ' ');
+ std::vector<std::string> extra_vals = Split(*extra_opts, " ");
cmd.insert(cmd.end(), extra_vals.begin(), extra_vals.end());
}
// TODO: Should we lower this? It's usually set close to max, because
@@ -357,17 +363,50 @@
}
int RunPreopt() {
- int ret = dexopt(package_parameters_[0], // apk_path
- atoi(package_parameters_[1]), // uid
- package_parameters_[2], // pkgname
- package_parameters_[3], // instruction_set
- atoi(package_parameters_[4]), // dexopt_needed
- package_parameters_[5], // oat_dir
- atoi(package_parameters_[6]), // dexopt_flags
- package_parameters_[7], // compiler_filter
- ParseNull(package_parameters_[8]), // volume_uuid
- ParseNull(package_parameters_[9])); // shared_libraries
- return ret;
+ // Run the preopt.
+ //
+ // There's one thing we have to be careful about: we may/will be asked to compile an app
+ // living in the system image. This may be a valid request - if the app wasn't compiled,
+ // e.g., if the system image wasn't large enough to include preopted files. However, the
+ // data we have is from the old system, so the driver (the OTA service) can't actually
+ // know. Thus, we will get requests for apps that have preopted components. To avoid
+ // duplication (we'd generate files that are not used and are *not* cleaned up), do two
+ // simple checks:
+ //
+ // 1) Does the apk_path start with the value of ANDROID_ROOT? (~in the system image)
+ // (For simplicity, assume the value of ANDROID_ROOT does not contain a symlink.)
+ //
+ // 2) If you replace the name in the apk_path with "oat," does the path exist?
+ // (=have a subdirectory for preopted files)
+ //
+ // If the answer to both is yes, skip the dexopt.
+ //
+ // Note: while one may think it's OK to call dexopt and it will fail (because APKs should
+ // be stripped), that's not true for APKs signed outside the build system (so the
+ // jar content must be exactly the same).
+
+ // (This is ugly as it's the only thing where we need to understand the contents
+ // of package_parameters_, but it beats postponing the decision or using the call-
+ // backs to do weird things.)
+ constexpr size_t kApkPathIndex = 0;
+ CHECK_GT(DEXOPT_PARAM_COUNT, kApkPathIndex);
+ CHECK(package_parameters_[kApkPathIndex] != nullptr);
+ CHECK(system_properties_.GetProperty(kAndroidRootPathPropertyName) != nullptr);
+ if (StartsWith(package_parameters_[kApkPathIndex],
+ system_properties_.GetProperty(kAndroidRootPathPropertyName)->c_str())) {
+ const char* last_slash = strrchr(package_parameters_[kApkPathIndex], '/');
+ if (last_slash != nullptr) {
+ std::string path(package_parameters_[kApkPathIndex],
+ last_slash - package_parameters_[kApkPathIndex] + 1);
+ CHECK(EndsWith(path, "/"));
+ path = path + "oat";
+ if (access(path.c_str(), F_OK) == 0) {
+ return 0;
+ }
+ }
+ }
+
+ return dexopt(package_parameters_);
}
////////////////////////////////////
@@ -376,7 +415,7 @@
// Wrapper on fork/execv to run a command in a subprocess.
bool Exec(const std::vector<std::string>& arg_vector, std::string* error_msg) {
- const std::string command_line(Join(arg_vector, ' '));
+ const std::string command_line = Join(arg_vector, ' ');
CHECK_GE(arg_vector.size(), 1U) << command_line;
@@ -484,7 +523,7 @@
// to compile, instead of the A properties we could get from init/get_property.
SystemProperties system_properties_;
- const char* package_parameters_[10];
+ const char* package_parameters_[DEXOPT_PARAM_COUNT];
// Store environment values we need to set.
std::vector<std::string> environ_;
@@ -497,7 +536,6 @@
////////////////////////
int get_property(const char *key, char *value, const char *default_value) {
- // TODO: Replace with system-properties map.
return gOps.GetProperty(key, value, default_value);
}
@@ -505,9 +543,8 @@
bool calculate_oat_file_path(char path[PKG_PATH_MAX], const char *oat_dir,
const char *apk_path,
const char *instruction_set) {
- // TODO: Insert B directory.
- char *file_name_start;
- char *file_name_end;
+ const char *file_name_start;
+ const char *file_name_end;
file_name_start = strrchr(apk_path, '/');
if (file_name_start == nullptr) {
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
index f7f69a9..be0ff2e 100644
--- a/cmds/installd/otapreopt_chroot.cpp
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -22,6 +22,8 @@
#include <android-base/macros.h>
#include <android-base/stringprintf.h>
+#include <installd_constants.h>
+
#ifndef LOG_TAG
#define LOG_TAG "otapreopt"
#endif
@@ -78,13 +80,13 @@
// Now go on and run otapreopt.
- const char* argv[1 + 9 + 1];
- CHECK_EQ(argc, 10);
+ const char* argv[1 + DEXOPT_PARAM_COUNT + 1];
+ CHECK_EQ(static_cast<size_t>(argc), DEXOPT_PARAM_COUNT + 1);
argv[0] = "/system/bin/otapreopt";
- for (size_t i = 1; i <= 9; ++i) {
+ for (size_t i = 1; i <= DEXOPT_PARAM_COUNT; ++i) {
argv[i] = arg[i];
}
- argv[10] = nullptr;
+ argv[DEXOPT_PARAM_COUNT + 1] = nullptr;
execv(argv[0], (char * const *)argv);
PLOG(ERROR) << "execv(OTAPREOPT) failed.";
diff --git a/cmds/installd/string_helpers.h b/cmds/installd/string_helpers.h
deleted file mode 100644
index e8fcdef..0000000
--- a/cmds/installd/string_helpers.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_OTAPREOPT_STRING_HELPERS_H_
-#define ART_OTAPREOPT_STRING_HELPERS_H_
-
-#include <sstream>
-#include <string>
-
-#include <android-base/macros.h>
-
-namespace android {
-namespace installd {
-
-static inline bool StringStartsWith(const std::string& target,
- const char* prefix) {
- return target.compare(0, strlen(prefix), prefix) == 0;
-}
-
-// Split the input according to the separator character. Doesn't honor quotation.
-static inline std::vector<std::string> Split(const std::string& in, const char separator) {
- if (in.empty()) {
- return std::vector<std::string>();
- }
-
- std::vector<std::string> ret;
- std::stringstream strstr(in);
- std::string token;
-
- while (std::getline(strstr, token, separator)) {
- ret.push_back(token);
- }
-
- return ret;
-}
-
-template <typename StringT>
-static inline std::string Join(const std::vector<StringT>& strings, char separator) {
- if (strings.empty()) {
- return "";
- }
-
- std::string result(strings[0]);
- for (size_t i = 1; i < strings.size(); ++i) {
- result += separator;
- result += strings[i];
- }
- return result;
-}
-
-} // namespace installd
-} // namespace android
-
-#endif // ART_OTAPREOPT_STRING_HELPERS_H_
diff --git a/docs/Doxyfile b/docs/Doxyfile
index 46d6d84..3ea453f 100644
--- a/docs/Doxyfile
+++ b/docs/Doxyfile
@@ -677,7 +677,7 @@
# directories like "/usr/src/myproject". Separate the files or directories
# with spaces.
-INPUT = ../include/android
+INPUT = ../include/android ../../av/include/ndk ../../av/include/camera/ndk
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
diff --git a/docs/images/camera2/metadata/android.colorCorrection.mode/processing_pipeline.png b/docs/images/camera2/metadata/android.colorCorrection.mode/processing_pipeline.png
new file mode 100644
index 0000000..7578b48
--- /dev/null
+++ b/docs/images/camera2/metadata/android.colorCorrection.mode/processing_pipeline.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.statistics.lensShadingMap/blue_shading.png b/docs/images/camera2/metadata/android.statistics.lensShadingMap/blue_shading.png
new file mode 100644
index 0000000..7b10f6b
--- /dev/null
+++ b/docs/images/camera2/metadata/android.statistics.lensShadingMap/blue_shading.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.statistics.lensShadingMap/green_e_shading.png b/docs/images/camera2/metadata/android.statistics.lensShadingMap/green_e_shading.png
new file mode 100644
index 0000000..41972cf
--- /dev/null
+++ b/docs/images/camera2/metadata/android.statistics.lensShadingMap/green_e_shading.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.statistics.lensShadingMap/green_o_shading.png b/docs/images/camera2/metadata/android.statistics.lensShadingMap/green_o_shading.png
new file mode 100644
index 0000000..d26600b
--- /dev/null
+++ b/docs/images/camera2/metadata/android.statistics.lensShadingMap/green_o_shading.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.statistics.lensShadingMap/inv_shading.png b/docs/images/camera2/metadata/android.statistics.lensShadingMap/inv_shading.png
new file mode 100644
index 0000000..1e7208e
--- /dev/null
+++ b/docs/images/camera2/metadata/android.statistics.lensShadingMap/inv_shading.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.statistics.lensShadingMap/red_shading.png b/docs/images/camera2/metadata/android.statistics.lensShadingMap/red_shading.png
new file mode 100644
index 0000000..ecef3ae
--- /dev/null
+++ b/docs/images/camera2/metadata/android.statistics.lensShadingMap/red_shading.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.tonemap.curveRed/gamma_tonemap.png b/docs/images/camera2/metadata/android.tonemap.curveRed/gamma_tonemap.png
new file mode 100644
index 0000000..a02fd89
--- /dev/null
+++ b/docs/images/camera2/metadata/android.tonemap.curveRed/gamma_tonemap.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.tonemap.curveRed/inverse_tonemap.png b/docs/images/camera2/metadata/android.tonemap.curveRed/inverse_tonemap.png
new file mode 100644
index 0000000..c309ac5
--- /dev/null
+++ b/docs/images/camera2/metadata/android.tonemap.curveRed/inverse_tonemap.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.tonemap.curveRed/linear_tonemap.png b/docs/images/camera2/metadata/android.tonemap.curveRed/linear_tonemap.png
new file mode 100644
index 0000000..414fad4
--- /dev/null
+++ b/docs/images/camera2/metadata/android.tonemap.curveRed/linear_tonemap.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.tonemap.curveRed/rec709_tonemap.png b/docs/images/camera2/metadata/android.tonemap.curveRed/rec709_tonemap.png
new file mode 100644
index 0000000..c147a87
--- /dev/null
+++ b/docs/images/camera2/metadata/android.tonemap.curveRed/rec709_tonemap.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png b/docs/images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png
new file mode 100644
index 0000000..4ce2125
--- /dev/null
+++ b/docs/images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png
Binary files differ
diff --git a/include/binder/Parcel.h b/include/binder/Parcel.h
index 2f441ed..42ed148 100644
--- a/include/binder/Parcel.h
+++ b/include/binder/Parcel.h
@@ -723,9 +723,9 @@
}
setDataPosition(start);
- val->reset(new std::vector<T>());
+ val->reset(new std::vector<std::unique_ptr<T>>());
- status = unsafeReadTypedVector(val->get(), &Parcel::readParcelable);
+ status = unsafeReadTypedVector(val->get(), &Parcel::readParcelable<T>);
if (status != OK) {
val->reset();
diff --git a/include/gui/BufferQueue.h b/include/gui/BufferQueue.h
index 09300a2..fe4b1fa 100644
--- a/include/gui/BufferQueue.h
+++ b/include/gui/BufferQueue.h
@@ -66,6 +66,8 @@
virtual void onFrameReplaced(const BufferItem& item) override;
virtual void onBuffersReleased() override;
virtual void onSidebandStreamChanged() override;
+ virtual bool getFrameTimestamps(uint64_t frameNumber,
+ FrameTimestamps* outTimestamps) const override;
private:
// mConsumerListener is a weak reference to the IConsumerListener. This is
// the raison d'etre of ProxyConsumerListener.
diff --git a/include/gui/BufferQueueProducer.h b/include/gui/BufferQueueProducer.h
index a75ed98..a85bbb7 100644
--- a/include/gui/BufferQueueProducer.h
+++ b/include/gui/BufferQueueProducer.h
@@ -186,6 +186,10 @@
virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
sp<Fence>* outFence, float outTransformMatrix[16]) override;
+ // See IGraphicBufferProducer::getFrameTimestamps
+ virtual bool getFrameTimestamps(uint64_t frameNumber,
+ FrameTimestamps* outTimestamps) const override;
+
private:
// This is required by the IBinder::DeathRecipient interface
virtual void binderDied(const wp<IBinder>& who);
diff --git a/include/gui/FrameTimestamps.h b/include/gui/FrameTimestamps.h
new file mode 100644
index 0000000..4dc7467
--- /dev/null
+++ b/include/gui/FrameTimestamps.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_GUI_FRAMETIMESTAMPS_H
+#define ANDROID_GUI_FRAMETIMESTAMPS_H
+
+#include <utils/Timers.h>
+#include <utils/Flattenable.h>
+
+namespace android {
+
+struct FrameTimestamps : public LightFlattenablePod<FrameTimestamps> {
+ FrameTimestamps() :
+ frameNumber(0),
+ postedTime(0),
+ acquireTime(0),
+ refreshStartTime(0),
+ glCompositionDoneTime(0),
+ displayRetireTime(0),
+ releaseTime(0) {}
+
+ uint64_t frameNumber;
+ nsecs_t postedTime;
+ nsecs_t acquireTime;
+ nsecs_t refreshStartTime;
+ nsecs_t glCompositionDoneTime;
+ nsecs_t displayRetireTime;
+ nsecs_t releaseTime;
+};
+
+} // namespace android
+#endif
diff --git a/include/gui/IConsumerListener.h b/include/gui/IConsumerListener.h
index 3f39799..1efcf3c 100644
--- a/include/gui/IConsumerListener.h
+++ b/include/gui/IConsumerListener.h
@@ -25,6 +25,8 @@
#include <binder/IInterface.h>
+#include <gui/FrameTimestamps.h>
+
namespace android {
// ----------------------------------------------------------------------------
@@ -78,6 +80,11 @@
// stream is first attached and when it is either detached or replaced by a
// different stream.
virtual void onSidebandStreamChanged() = 0; /* Asynchronous */
+
+ // See IGraphicBufferProducer::getFrameTimestamps
+ // This queries the consumer for the timestamps
+ virtual bool getFrameTimestamps(uint64_t /*frameNumber*/,
+ FrameTimestamps* /*outTimestamps*/) const { return false; }
};
diff --git a/include/gui/IGraphicBufferProducer.h b/include/gui/IGraphicBufferProducer.h
index 37ae6df..0c24606 100644
--- a/include/gui/IGraphicBufferProducer.h
+++ b/include/gui/IGraphicBufferProducer.h
@@ -30,6 +30,8 @@
#include <ui/Rect.h>
#include <ui/Region.h>
+#include <gui/FrameTimestamps.h>
+
namespace android {
// ----------------------------------------------------------------------------
@@ -568,6 +570,14 @@
// Returns NO_ERROR or the status of the Binder transaction
virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
sp<Fence>* outFence, float outTransformMatrix[16]) = 0;
+
+ // Attempts to retrieve timestamp information for the given frame number.
+ // If information for the given frame number is not found, returns false.
+ // Returns true otherwise.
+ //
+ // If a fence has not yet signaled the timestamp returned will be 0;
+ virtual bool getFrameTimestamps(uint64_t /*frameNumber*/,
+ FrameTimestamps* /*outTimestamps*/) const { return false; }
};
// ----------------------------------------------------------------------------
diff --git a/include/gui/Surface.h b/include/gui/Surface.h
index 646203b..7d9d901 100644
--- a/include/gui/Surface.h
+++ b/include/gui/Surface.h
@@ -134,6 +134,12 @@
status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
sp<Fence>* outFence, float outTransformMatrix[16]);
+ // See IGraphicBufferProducer::getFrameTimestamps
+ bool getFrameTimestamps(uint64_t frameNumber, nsecs_t* outPostedTime,
+ nsecs_t* outAcquireTime, nsecs_t* outRefreshStartTime,
+ nsecs_t* outGlCompositionDoneTime, nsecs_t* outDisplayRetireTime,
+ nsecs_t* outReleaseTime);
+
protected:
virtual ~Surface();
@@ -183,6 +189,7 @@
int dispatchSetSurfaceDamage(va_list args);
int dispatchSetSharedBufferMode(va_list args);
int dispatchSetAutoRefresh(va_list args);
+ int dispatchGetFrameTimestamps(va_list args);
protected:
virtual int dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd);
diff --git a/include/gui/SurfaceComposerClient.h b/include/gui/SurfaceComposerClient.h
index 73f923c..312e02f 100644
--- a/include/gui/SurfaceComposerClient.h
+++ b/include/gui/SurfaceComposerClient.h
@@ -140,6 +140,8 @@
const sp<IBinder>& handle, uint64_t frameNumber);
status_t setOverrideScalingMode(const sp<IBinder>& id,
int32_t overrideScalingMode);
+ status_t setPositionAppliesWithResize(const sp<IBinder>& id);
+
status_t destroySurface(const sp<IBinder>& id);
status_t clearLayerFrameStats(const sp<IBinder>& token) const;
diff --git a/include/gui/SurfaceControl.h b/include/gui/SurfaceControl.h
index bedebb6..fafd194 100644
--- a/include/gui/SurfaceControl.h
+++ b/include/gui/SurfaceControl.h
@@ -73,6 +73,11 @@
status_t setCrop(const Rect& crop);
status_t setFinalCrop(const Rect& crop);
+ // If the size changes in this transaction, position updates specified
+ // in this transaction will not complete until a buffer of the new size
+ // arrives.
+ status_t setPositionAppliesWithResize();
+
// Defers applying any changes made in this transaction until the Layer
// identified by handle reaches the given frameNumber
status_t deferTransactionUntil(sp<IBinder> handle, uint64_t frameNumber);
diff --git a/include/private/gui/LayerState.h b/include/private/gui/LayerState.h
index 92d31d1..4885e05 100644
--- a/include/private/gui/LayerState.h
+++ b/include/private/gui/LayerState.h
@@ -54,7 +54,8 @@
eCropChanged = 0x00000100,
eDeferTransaction = 0x00000200,
eFinalCropChanged = 0x00000400,
- eOverrideScalingModeChanged = 0x00000800
+ eOverrideScalingModeChanged = 0x00000800,
+ ePositionAppliesWithResize = 0x00001000,
};
layer_state_t()
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index ccbb5a2..6de98f5 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -61,6 +61,15 @@
}
}
+bool BufferQueue::ProxyConsumerListener::getFrameTimestamps(
+ uint64_t frameNumber, FrameTimestamps* outTimestamps) const {
+ sp<ConsumerListener> listener(mConsumerListener.promote());
+ if (listener != NULL) {
+ return listener->getFrameTimestamps(frameNumber, outTimestamps);
+ }
+ return false;
+}
+
void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
sp<IGraphicBufferConsumer>* outConsumer,
const sp<IGraphicBufferAlloc>& allocator) {
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 65f8255..3a0a283 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -444,14 +444,6 @@
mSlots[found].mBufferState.dequeue();
- // If shared buffer mode has just been enabled, cache the slot of the
- // first buffer that is dequeued and mark it as the shared buffer.
- if (mCore->mSharedBufferMode && mCore->mSharedBufferSlot ==
- BufferQueueCore::INVALID_BUFFER_SLOT) {
- mCore->mSharedBufferSlot = found;
- mSlots[found].mBufferState.mShared = true;
- }
-
if ((buffer == NULL) ||
buffer->needsReallocation(width, height, format, usage))
{
@@ -483,9 +475,21 @@
eglDisplay = mSlots[found].mEglDisplay;
eglFence = mSlots[found].mEglFence;
- *outFence = mSlots[found].mFence;
+ // Don't return a fence in shared buffer mode, except for the first
+ // frame.
+ *outFence = (mCore->mSharedBufferMode &&
+ mCore->mSharedBufferSlot == found) ?
+ Fence::NO_FENCE : mSlots[found].mFence;
mSlots[found].mEglFence = EGL_NO_SYNC_KHR;
mSlots[found].mFence = Fence::NO_FENCE;
+
+ // If shared buffer mode has just been enabled, cache the slot of the
+ // first buffer that is dequeued and mark it as the shared buffer.
+ if (mCore->mSharedBufferMode && mCore->mSharedBufferSlot ==
+ BufferQueueCore::INVALID_BUFFER_SLOT) {
+ mCore->mSharedBufferSlot = found;
+ mSlots[found].mBufferState.mShared = true;
+ }
} // Autolock scope
if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
@@ -1401,6 +1405,22 @@
return NO_ERROR;
}
+bool BufferQueueProducer::getFrameTimestamps(uint64_t frameNumber,
+ FrameTimestamps* outTimestamps) const {
+ ATRACE_CALL();
+ BQ_LOGV("getFrameTimestamps, %" PRIu64, frameNumber);
+ sp<IConsumerListener> listener;
+
+ {
+ Mutex::Autolock lock(mCore->mMutex);
+ listener = mCore->mConsumerListener;
+ }
+ if (listener != NULL) {
+ return listener->getFrameTimestamps(frameNumber, outTimestamps);
+ }
+ return false;
+}
+
void BufferQueueProducer::binderDied(const wp<android::IBinder>& /* who */) {
// If we're here, it means that a producer we were connected to died.
// We're guaranteed that we are still connected to it because we remove
diff --git a/libs/gui/IConsumerListener.cpp b/libs/gui/IConsumerListener.cpp
index 9a0b7a4..3175b91 100644
--- a/libs/gui/IConsumerListener.cpp
+++ b/libs/gui/IConsumerListener.cpp
@@ -31,6 +31,7 @@
ON_FRAME_AVAILABLE = IBinder::FIRST_CALL_TRANSACTION,
ON_BUFFER_RELEASED,
ON_SIDEBAND_STREAM_CHANGED,
+ GET_FRAME_TIMESTAMPS
};
class BpConsumerListener : public BpInterface<IConsumerListener>
@@ -60,6 +61,42 @@
data.writeInterfaceToken(IConsumerListener::getInterfaceDescriptor());
remote()->transact(ON_SIDEBAND_STREAM_CHANGED, data, &reply, IBinder::FLAG_ONEWAY);
}
+
+ virtual bool getFrameTimestamps(uint64_t frameNumber,
+ FrameTimestamps* outTimestamps) const {
+ Parcel data, reply;
+ status_t result = data.writeInterfaceToken(
+ IConsumerListener::getInterfaceDescriptor());
+ if (result != NO_ERROR) {
+ ALOGE("getFrameTimestamps failed to write token: %d", result);
+ return false;
+ }
+ result = data.writeUint64(frameNumber);
+ if (result != NO_ERROR) {
+ ALOGE("getFrameTimestamps failed to write: %d", result);
+ return false;
+ }
+ result = remote()->transact(GET_FRAME_TIMESTAMPS, data, &reply);
+ if (result != NO_ERROR) {
+ ALOGE("getFrameTimestamps failed to transact: %d", result);
+ return false;
+ }
+ bool found = false;
+ result = reply.readBool(&found);
+ if (result != NO_ERROR) {
+ ALOGE("getFrameTimestamps failed to read: %d", result);
+ return false;
+ }
+ if (found) {
+ result = reply.read(*outTimestamps);
+ if (result != NO_ERROR) {
+ ALOGE("getFrameTimestamps failed to read timestamps: %d",
+ result);
+ return false;
+ }
+ }
+ return found;
+ }
};
// Out-of-line virtual method definition to trigger vtable emission in this
@@ -88,6 +125,30 @@
CHECK_INTERFACE(IConsumerListener, data, reply);
onSidebandStreamChanged();
return NO_ERROR; }
+ case GET_FRAME_TIMESTAMPS: {
+ CHECK_INTERFACE(IConsumerListener, data, reply);
+ uint64_t frameNumber = 0;
+ status_t result = data.readUint64(&frameNumber);
+ if (result != NO_ERROR) {
+ ALOGE("onTransact failed to read: %d", result);
+ return result;
+ }
+ FrameTimestamps timestamps;
+ bool found = getFrameTimestamps(frameNumber, ×tamps);
+ result = reply->writeBool(found);
+ if (result != NO_ERROR) {
+ ALOGE("onTransact failed to write: %d", result);
+ return result;
+ }
+ if (found) {
+ result = reply->write(timestamps);
+ if (result != NO_ERROR) {
+ ALOGE("onTransact failed to write timestamps: %d", result);
+ return result;
+ }
+ }
+ return NO_ERROR;
+ }
}
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index 81e9407..ab83317 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -55,6 +55,7 @@
SET_AUTO_REFRESH,
SET_DEQUEUE_TIMEOUT,
GET_LAST_QUEUED_BUFFER,
+ GET_FRAME_TIMESTAMPS
};
class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer>
@@ -418,6 +419,42 @@
*outFence = fence;
return result;
}
+
+ virtual bool getFrameTimestamps(uint64_t frameNumber,
+ FrameTimestamps* outTimestamps) const {
+ Parcel data, reply;
+ status_t result = data.writeInterfaceToken(
+ IGraphicBufferProducer::getInterfaceDescriptor());
+ if (result != NO_ERROR) {
+ ALOGE("getFrameTimestamps failed to write token: %d", result);
+ return false;
+ }
+ result = data.writeUint64(frameNumber);
+ if (result != NO_ERROR) {
+ ALOGE("getFrameTimestamps failed to write: %d", result);
+ return false;
+ }
+ result = remote()->transact(GET_FRAME_TIMESTAMPS, data, &reply);
+ if (result != NO_ERROR) {
+ ALOGE("getFrameTimestamps failed to transact: %d", result);
+ return false;
+ }
+ bool found = false;
+ result = reply.readBool(&found);
+ if (result != NO_ERROR) {
+ ALOGE("getFrameTimestamps failed to read: %d", result);
+ return false;
+ }
+ if (found) {
+ result = reply.read(*outTimestamps);
+ if (result != NO_ERROR) {
+ ALOGE("getFrameTimestamps failed to read timestamps: %d",
+ result);
+ return false;
+ }
+ }
+ return found;
+ }
};
// Out-of-line virtual method definition to trigger vtable emission in this
@@ -659,6 +696,30 @@
}
return NO_ERROR;
}
+ case GET_FRAME_TIMESTAMPS: {
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+ uint64_t frameNumber = 0;
+ status_t result = data.readUint64(&frameNumber);
+ if (result != NO_ERROR) {
+ ALOGE("onTransact failed to read: %d", result);
+ return result;
+ }
+ FrameTimestamps timestamps;
+ bool found = getFrameTimestamps(frameNumber, ×tamps);
+ result = reply->writeBool(found);
+ if (result != NO_ERROR) {
+ ALOGE("onTransact failed to write: %d", result);
+ return result;
+ }
+ if (found) {
+ result = reply->write(timestamps);
+ if (result != NO_ERROR) {
+ ALOGE("onTransact failed to write timestamps: %d", result);
+ return result;
+ }
+ }
+ return NO_ERROR;
+ }
}
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 6811269..4739ca4 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -133,6 +133,39 @@
outTransformMatrix);
}
+bool Surface::getFrameTimestamps(uint64_t frameNumber, nsecs_t* outPostedTime,
+ nsecs_t* outAcquireTime, nsecs_t* outRefreshStartTime,
+ nsecs_t* outGlCompositionDoneTime, nsecs_t* outDisplayRetireTime,
+ nsecs_t* outReleaseTime) {
+ ATRACE_CALL();
+
+ FrameTimestamps timestamps;
+ bool found = mGraphicBufferProducer->getFrameTimestamps(frameNumber,
+ ×tamps);
+ if (found) {
+ if (outPostedTime) {
+ *outPostedTime = timestamps.postedTime;
+ }
+ if (outAcquireTime) {
+ *outAcquireTime = timestamps.acquireTime;
+ }
+ if (outRefreshStartTime) {
+ *outRefreshStartTime = timestamps.refreshStartTime;
+ }
+ if (outGlCompositionDoneTime) {
+ *outGlCompositionDoneTime = timestamps.glCompositionDoneTime;
+ }
+ if (outDisplayRetireTime) {
+ *outDisplayRetireTime = timestamps.displayRetireTime;
+ }
+ if (outReleaseTime) {
+ *outReleaseTime = timestamps.releaseTime;
+ }
+ return true;
+ }
+ return false;
+}
+
int Surface::hook_setSwapInterval(ANativeWindow* window, int interval) {
Surface* c = getSelf(window);
return c->setSwapInterval(interval);
@@ -162,6 +195,9 @@
ANativeWindowBuffer* buf;
int fenceFd = -1;
int result = c->dequeueBuffer(&buf, &fenceFd);
+ if (result != OK) {
+ return result;
+ }
sp<Fence> fence(new Fence(fenceFd));
int waitResult = fence->waitForever("dequeueBuffer_DEPRECATED");
if (waitResult != OK) {
@@ -614,6 +650,9 @@
case NATIVE_WINDOW_SET_AUTO_REFRESH:
res = dispatchSetAutoRefresh(args);
break;
+ case NATIVE_WINDOW_GET_FRAME_TIMESTAMPS:
+ res = dispatchGetFrameTimestamps(args);
+ break;
default:
res = NAME_NOT_FOUND;
break;
@@ -734,6 +773,20 @@
return setAutoRefresh(autoRefresh);
}
+int Surface::dispatchGetFrameTimestamps(va_list args) {
+ uint32_t framesAgo = va_arg(args, uint32_t);
+ nsecs_t* outPostedTime = va_arg(args, int64_t*);
+ nsecs_t* outAcquireTime = va_arg(args, int64_t*);
+ nsecs_t* outRefreshStartTime = va_arg(args, int64_t*);
+ nsecs_t* outGlCompositionDoneTime = va_arg(args, int64_t*);
+ nsecs_t* outDisplayRetireTime = va_arg(args, int64_t*);
+ nsecs_t* outReleaseTime = va_arg(args, int64_t*);
+ bool ret = getFrameTimestamps(getNextFrameNumber() - 1 - framesAgo,
+ outPostedTime, outAcquireTime, outRefreshStartTime,
+ outGlCompositionDoneTime, outDisplayRetireTime, outReleaseTime);
+ return ret ? NO_ERROR : BAD_VALUE;
+}
+
int Surface::connect(int api) {
static sp<IProducerListener> listener = new DummyProducerListener();
return connect(api, listener);
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index cc04882..2189047 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -165,6 +165,8 @@
uint64_t frameNumber);
status_t setOverrideScalingMode(const sp<SurfaceComposerClient>& client,
const sp<IBinder>& id, int32_t overrideScalingMode);
+ status_t setPositionAppliesWithResize(const sp<SurfaceComposerClient>& client,
+ const sp<IBinder>& id);
void setDisplaySurface(const sp<IBinder>& token,
const sp<IGraphicBufferProducer>& bufferProducer);
@@ -443,6 +445,18 @@
return NO_ERROR;
}
+status_t Composer::setPositionAppliesWithResize(
+ const sp<SurfaceComposerClient>& client,
+ const sp<IBinder>& id) {
+ Mutex::Autolock lock(mLock);
+ layer_state_t* s = getLayerStateLocked(client, id);
+ if (!s) {
+ return BAD_INDEX;
+ }
+ s->what |= layer_state_t::ePositionAppliesWithResize;
+ return NO_ERROR;
+}
+
// ---------------------------------------------------------------------------
DisplayState& Composer::getDisplayStateLocked(const sp<IBinder>& token) {
@@ -685,6 +699,11 @@
this, id, overrideScalingMode);
}
+status_t SurfaceComposerClient::setPositionAppliesWithResize(
+ const sp<IBinder>& id) {
+ return getComposer().setPositionAppliesWithResize(this, id);
+}
+
// ----------------------------------------------------------------------------
void SurfaceComposerClient::setDisplaySurface(const sp<IBinder>& token,
diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp
index 314d83a..4671e50 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -112,6 +112,11 @@
if (err < 0) return err;
return mClient->setPosition(mHandle, x, y);
}
+status_t SurfaceControl::setPositionAppliesWithResize() {
+ status_t err = validate();
+ if (err < 0) return err;
+ return mClient->setPositionAppliesWithResize(mHandle);
+}
status_t SurfaceControl::setSize(uint32_t w, uint32_t h) {
status_t err = validate();
if (err < 0) return err;
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index 00bfc24..24394a9 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -1794,9 +1794,9 @@
uint32_t blue_size = 0;
uint32_t alpha_size = 0;
-#define GET_POSITIVE_VALUE(case_name, target) \
+#define GET_NONNEGATIVE_VALUE(case_name, target) \
case case_name: \
- if (value > 0) { \
+ if (value >= 0) { \
target = value; \
} else { \
return setError(EGL_BAD_PARAMETER, (EGLClientBuffer)0); \
@@ -1808,12 +1808,12 @@
GLint attr = *attrib_list++;
GLint value = *attrib_list++;
switch (attr) {
- GET_POSITIVE_VALUE(EGL_WIDTH, width);
- GET_POSITIVE_VALUE(EGL_HEIGHT, height);
- GET_POSITIVE_VALUE(EGL_RED_SIZE, red_size);
- GET_POSITIVE_VALUE(EGL_GREEN_SIZE, green_size);
- GET_POSITIVE_VALUE(EGL_BLUE_SIZE, blue_size);
- GET_POSITIVE_VALUE(EGL_ALPHA_SIZE, alpha_size);
+ GET_NONNEGATIVE_VALUE(EGL_WIDTH, width);
+ GET_NONNEGATIVE_VALUE(EGL_HEIGHT, height);
+ GET_NONNEGATIVE_VALUE(EGL_RED_SIZE, red_size);
+ GET_NONNEGATIVE_VALUE(EGL_GREEN_SIZE, green_size);
+ GET_NONNEGATIVE_VALUE(EGL_BLUE_SIZE, blue_size);
+ GET_NONNEGATIVE_VALUE(EGL_ALPHA_SIZE, alpha_size);
case EGL_NATIVE_BUFFER_USAGE_ANDROID:
if (value & EGL_NATIVE_BUFFER_USAGE_PROTECTED_BIT_ANDROID) {
usage |= GRALLOC_USAGE_PROTECTED;
@@ -1836,7 +1836,7 @@
}
}
}
-#undef GET_POSITIVE_VALUE
+#undef GET_NONNEGATIVE_VALUE
// Validate format.
if (red_size == 8 && green_size == 8 && blue_size == 8) {
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index fb6307e..d654b17 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -44,7 +44,6 @@
LOCAL_CFLAGS := -DLOG_TAG=\"SurfaceFlinger\"
LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
-#LOCAL_CFLAGS += -DENABLE_FENCE_TRACKING
USE_HWC2 := false
ifeq ($(USE_HWC2),true)
diff --git a/services/surfaceflinger/FenceTracker.cpp b/services/surfaceflinger/FenceTracker.cpp
index 885d712..3ff9bbf 100644
--- a/services/surfaceflinger/FenceTracker.cpp
+++ b/services/surfaceflinger/FenceTracker.cpp
@@ -26,7 +26,9 @@
FenceTracker::FenceTracker() :
mFrameCounter(0),
mOffset(0),
- mFrames() {}
+ mFrames(),
+ mMutex() {
+}
void FenceTracker::dump(String8* outString) {
Mutex::Autolock lock(mMutex);
@@ -135,7 +137,7 @@
nsecs_t postedTime;
sp<Fence> acquireFence;
sp<Fence> prevReleaseFence;
- int32_t key = layers[i]->getSequence();
+ int32_t layerId = layers[i]->getSequence();
layers[i]->getFenceData(&name, &frameNumber, &glesComposition,
&postedTime, &acquireFence, &prevReleaseFence);
@@ -144,13 +146,17 @@
frame.layers.emplace(std::piecewise_construct,
std::forward_as_tuple(key),
std::forward_as_tuple(name, frameNumber, glesComposition,
- postedTime, 0, 0, acquireFence, prevReleaseFence));
+ postedTime, FrameTimestamps::INVALID_TIME,
+ FrameTimestamps::INVALID_TIME, acquireFence,
+ prevReleaseFence));
wasGlesCompositionDone = true;
} else {
frame.layers.emplace(std::piecewise_construct,
std::forward_as_tuple(key),
std::forward_as_tuple(name, frameNumber, glesComposition,
- postedTime, 0, 0, acquireFence, Fence::NO_FENCE));
+ postedTime, FrameTimestamps::INVALID_TIME,
+ FrameTimestamps::INVALID_TIME, acquireFence,
+ Fence::NO_FENCE));
auto prevLayer = prevFrame.layers.find(key);
if (prevLayer != prevFrame.layers.end()) {
@@ -159,7 +165,7 @@
}
#else
frame.layers.emplace(std::piecewise_construct,
- std::forward_as_tuple(key),
+ std::forward_as_tuple(layerId),
std::forward_as_tuple(name, frameNumber, glesComposition,
postedTime, 0, 0, acquireFence,
glesComposition ? Fence::NO_FENCE : prevReleaseFence));
@@ -168,7 +174,7 @@
}
#endif
frame.layers.emplace(std::piecewise_construct,
- std::forward_as_tuple(key),
+ std::forward_as_tuple(layerId),
std::forward_as_tuple(name, frameNumber, glesComposition,
postedTime, 0, 0, acquireFence, prevReleaseFence));
}
@@ -184,8 +190,35 @@
mOffset = (mOffset + 1) % MAX_FRAME_HISTORY;
mFrameCounter++;
+}
+bool FenceTracker::getFrameTimestamps(const Layer& layer,
+ uint64_t frameNumber, FrameTimestamps* outTimestamps) {
+ Mutex::Autolock lock(mMutex);
checkFencesForCompletion();
+ int32_t layerId = layer.getSequence();
+
+ size_t i = 0;
+ for (; i < MAX_FRAME_HISTORY; i++) {
+ if (mFrames[i].layers.count(layerId) &&
+ mFrames[i].layers[layerId].frameNumber == frameNumber) {
+ break;
+ }
+ }
+ if (i == MAX_FRAME_HISTORY) {
+ return false;
+ }
+
+ const FrameRecord& frameRecord = mFrames[i];
+ const LayerRecord& layerRecord = mFrames[i].layers[layerId];
+ outTimestamps->frameNumber = frameNumber;
+ outTimestamps->postedTime = layerRecord.postedTime;
+ outTimestamps->acquireTime = layerRecord.acquireTime;
+ outTimestamps->refreshStartTime = frameRecord.refreshStartTime;
+ outTimestamps->glCompositionDoneTime = frameRecord.glesCompositionDoneTime;
+ outTimestamps->displayRetireTime = frameRecord.retireTime;
+ outTimestamps->releaseTime = layerRecord.releaseTime;
+ return true;
}
} // namespace android
diff --git a/services/surfaceflinger/FenceTracker.h b/services/surfaceflinger/FenceTracker.h
index de99820..4cb14a5 100644
--- a/services/surfaceflinger/FenceTracker.h
+++ b/services/surfaceflinger/FenceTracker.h
@@ -29,7 +29,7 @@
namespace android {
class Layer;
-
+struct FrameTimestamps;
/*
* Keeps a circular buffer of fence/timestamp data for the last N frames in
* SurfaceFlinger. Gets timestamps for fences after they have signaled.
@@ -40,9 +40,11 @@
void dump(String8* outString);
void addFrame(nsecs_t refreshStartTime, sp<Fence> retireFence,
const Vector<sp<Layer>>& layers, sp<Fence> glDoneFence);
+ bool getFrameTimestamps(const Layer& layer, uint64_t frameNumber,
+ FrameTimestamps* outTimestamps);
protected:
- static constexpr size_t MAX_FRAME_HISTORY = 128;
+ static constexpr size_t MAX_FRAME_HISTORY = 8;
struct LayerRecord {
String8 name; // layer name
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 6f41372..1554d70 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -95,7 +95,8 @@
mQueueItems(),
mLastFrameNumberReceived(0),
mUpdateTexImageFailed(false),
- mAutoRefresh(false)
+ mAutoRefresh(false),
+ mFreezePositionUpdates(false)
{
#ifdef USE_HWC2
ALOGV("Creating Layer %s", name.string());
@@ -154,7 +155,8 @@
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
mProducer = new MonitoredProducer(producer, mFlinger);
- mSurfaceFlingerConsumer = new SurfaceFlingerConsumer(consumer, mTextureName);
+ mSurfaceFlingerConsumer = new SurfaceFlingerConsumer(consumer, mTextureName,
+ this);
mSurfaceFlingerConsumer->setConsumerUsageBits(getEffectiveUsage(0));
mSurfaceFlingerConsumer->setContentsChangedListener(this);
mSurfaceFlingerConsumer->setName(mName);
@@ -1413,11 +1415,9 @@
c.requested.w, c.requested.h);
}
+ const bool resizePending = (c.requested.w != c.active.w) ||
+ (c.requested.h != c.active.h);
if (!isFixedSize()) {
-
- const bool resizePending = (c.requested.w != c.active.w) ||
- (c.requested.h != c.active.h);
-
if (resizePending && mSidebandStream == NULL) {
// don't let Layer::doTransaction update the drawing state
// if we have a pending resize, unless we are in fixed-size mode.
@@ -1440,7 +1440,17 @@
// this is used by Layer, which special cases resizes.
if (flags & eDontUpdateGeometryState) {
} else {
- c.active = c.requested;
+ Layer::State& editCurrentState(getCurrentState());
+ if (mFreezePositionUpdates) {
+ float tx = c.active.transform.tx();
+ float ty = c.active.transform.ty();
+ c.active = c.requested;
+ c.active.transform.set(tx, ty);
+ editCurrentState.active = c.active;
+ } else {
+ editCurrentState.active = editCurrentState.requested;
+ c.active = c.requested;
+ }
}
if (s.active != c.active) {
@@ -1487,7 +1497,7 @@
return android_atomic_or(flags, &mTransactionFlags);
}
-bool Layer::setPosition(float x, float y) {
+bool Layer::setPosition(float x, float y, bool immediate) {
if (mCurrentState.requested.transform.tx() == x && mCurrentState.requested.transform.ty() == y)
return false;
mCurrentState.sequence++;
@@ -1496,12 +1506,16 @@
// we want to apply the position portion of the transform matrix immediately,
// but still delay scaling when resizing a SCALING_MODE_FREEZE layer.
mCurrentState.requested.transform.set(x, y);
- mCurrentState.active.transform.set(x, y);
+ if (immediate && !mFreezePositionUpdates) {
+ mCurrentState.active.transform.set(x, y);
+ }
+ mFreezePositionUpdates = mFreezePositionUpdates || !immediate;
mCurrentState.modified = true;
setTransactionFlags(eTransactionNeeded);
return true;
}
+
bool Layer::setLayer(uint32_t z) {
if (mCurrentState.z == z)
return false;
@@ -1581,6 +1595,7 @@
if (scalingMode == mOverrideScalingMode)
return false;
mOverrideScalingMode = scalingMode;
+ setTransactionFlags(eTransactionNeeded);
return true;
}
@@ -2002,6 +2017,7 @@
if (bufWidth != uint32_t(oldActiveBuffer->width) ||
bufHeight != uint32_t(oldActiveBuffer->height)) {
recomputeVisibleRegions = true;
+ mFreezePositionUpdates = false;
}
}
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 79750a6..4257c37 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -145,7 +145,7 @@
status_t setBuffers(uint32_t w, uint32_t h, PixelFormat format, uint32_t flags);
// modify current state
- bool setPosition(float x, float y);
+ bool setPosition(float x, float y, bool immediate);
bool setLayer(uint32_t z);
bool setSize(uint32_t w, uint32_t h);
#ifdef USE_HWC2
@@ -409,6 +409,11 @@
std::vector<OccupancyTracker::Segment> getOccupancyHistory(bool forceFlush);
+ bool getFrameTimestamps(uint64_t frameNumber,
+ FrameTimestamps* outTimestamps) const {
+ return mFlinger->getFrameTimestamps(*this, frameNumber, outTimestamps);
+ }
+
protected:
// constant
sp<SurfaceFlinger> mFlinger;
@@ -599,6 +604,7 @@
bool mUpdateTexImageFailed; // This is only modified from the main thread
bool mAutoRefresh;
+ bool mFreezePositionUpdates;
};
// ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 29e3e0e..7ce7777 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -939,11 +939,7 @@
void SurfaceFlinger::handleMessageRefresh() {
ATRACE_CALL();
-#ifdef ENABLE_FENCE_TRACKING
nsecs_t refreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
-#else
- nsecs_t refreshStartTime = 0;
-#endif
static nsecs_t previousExpectedPresent = 0;
nsecs_t expectedPresent = mPrimaryDispSync.computeNextRefresh(0);
static bool previousFrameMissed = false;
@@ -1033,11 +1029,7 @@
}
}
-#ifdef ENABLE_FENCE_TRACKING
void SurfaceFlinger::postComposition(nsecs_t refreshStartTime)
-#else
-void SurfaceFlinger::postComposition(nsecs_t /*refreshStartTime*/)
-#endif
{
ATRACE_CALL();
ALOGV("postComposition");
@@ -1069,10 +1061,8 @@
}
}
-#ifdef ENABLE_FENCE_TRACKING
mFenceTracker.addFrame(refreshStartTime, presentFence,
hw->getVisibleLayersSortedByZ(), hw->getClientTargetAcquireFence());
-#endif
if (mAnimCompositionPending) {
mAnimCompositionPending = false;
@@ -2257,9 +2247,12 @@
sp<Layer> layer(client->getLayerUser(s.surface));
if (layer != 0) {
const uint32_t what = s.what;
+ bool positionAppliesWithResize =
+ what & layer_state_t::ePositionAppliesWithResize;
if (what & layer_state_t::ePositionChanged) {
- if (layer->setPosition(s.x, s.y))
+ if (layer->setPosition(s.x, s.y, !positionAppliesWithResize)) {
flags |= eTraversalNeeded;
+ }
}
if (what & layer_state_t::eLayerChanged) {
// NOTE: index needs to be calculated before we update the state
@@ -2610,14 +2603,12 @@
dumpAll = false;
}
-#ifdef ENABLE_FENCE_TRACKING
if ((index < numArgs) &&
(args[index] == String16("--fences"))) {
index++;
mFenceTracker.dump(&result);
dumpAll = false;
}
-#endif
}
if (dumpAll) {
@@ -3618,6 +3609,11 @@
}
}
+bool SurfaceFlinger::getFrameTimestamps(const Layer& layer,
+ uint64_t frameNumber, FrameTimestamps* outTimestamps) {
+ return mFenceTracker.getFrameTimestamps(layer, frameNumber, outTimestamps);
+}
+
// ---------------------------------------------------------------------------
SurfaceFlinger::LayerVector::LayerVector() {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 8263994..28666e2 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -440,6 +440,9 @@
std::vector<OccupancyTracker::Segment>&& history);
void dumpBufferingStats(String8& result) const;
+ bool getFrameTimestamps(const Layer& layer, uint64_t frameNumber,
+ FrameTimestamps* outTimestamps);
+
/* ------------------------------------------------------------------------
* Attributes
*/
diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.cpp b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
index c71b3bc..ba0a527 100644
--- a/services/surfaceflinger/SurfaceFlingerConsumer.cpp
+++ b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
@@ -18,6 +18,7 @@
//#define LOG_NDEBUG 0
#include "SurfaceFlingerConsumer.h"
+#include "Layer.h"
#include <private/gui/SyncFeatures.h>
@@ -251,6 +252,12 @@
}
}
+bool SurfaceFlingerConsumer::getFrameTimestamps(uint64_t frameNumber,
+ FrameTimestamps* outTimestamps) const {
+ sp<const Layer> l = mLayer.promote();
+ return l.get() ? l->getFrameTimestamps(frameNumber, outTimestamps) : false;
+}
+
// ---------------------------------------------------------------------------
}; // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.h b/services/surfaceflinger/SurfaceFlingerConsumer.h
index 51b002f..3762659 100644
--- a/services/surfaceflinger/SurfaceFlingerConsumer.h
+++ b/services/surfaceflinger/SurfaceFlingerConsumer.h
@@ -23,6 +23,8 @@
namespace android {
// ----------------------------------------------------------------------------
+class Layer;
+
/*
* This is a thin wrapper around GLConsumer.
*/
@@ -35,10 +37,10 @@
};
SurfaceFlingerConsumer(const sp<IGraphicBufferConsumer>& consumer,
- uint32_t tex)
+ uint32_t tex, const Layer* layer)
: GLConsumer(consumer, tex, GLConsumer::TEXTURE_EXTERNAL, false, false),
mTransformToDisplayInverse(false), mSurfaceDamage(),
- mPrevReleaseFence(Fence::NO_FENCE)
+ mPrevReleaseFence(Fence::NO_FENCE), mLayer(layer)
{}
class BufferRejecter {
@@ -82,6 +84,9 @@
void releasePendingBuffer();
#endif
+ virtual bool getFrameTimestamps(uint64_t frameNumber,
+ FrameTimestamps* outTimestamps) const override;
+
private:
virtual void onSidebandStreamChanged();
@@ -103,6 +108,9 @@
// The release fence of the already displayed buffer (previous frame).
sp<Fence> mPrevReleaseFence;
+
+ // The layer for this SurfaceFlingerConsumer
+ wp<const Layer> mLayer;
};
// ----------------------------------------------------------------------------
diff --git a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
index c87d010..b49f8af 100644
--- a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
+++ b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
@@ -943,11 +943,7 @@
void SurfaceFlinger::handleMessageRefresh() {
ATRACE_CALL();
-#ifdef ENABLE_FENCE_TRACKING
nsecs_t refreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
-#else
- nsecs_t refreshStartTime = 0;
-#endif
static nsecs_t previousExpectedPresent = 0;
nsecs_t expectedPresent = mPrimaryDispSync.computeNextRefresh(0);
static bool previousFrameMissed = false;
@@ -1029,11 +1025,7 @@
}
}
-#ifdef ENABLE_FENCE_TRACKING
void SurfaceFlinger::postComposition(nsecs_t refreshStartTime)
-#else
-void SurfaceFlinger::postComposition(nsecs_t /*refreshStartTime*/)
-#endif
{
const LayerVector& layers(mDrawingState.layersSortedByZ);
const size_t count = layers.size();
@@ -1063,10 +1055,8 @@
}
}
-#ifdef ENABLE_FENCE_TRACKING
mFenceTracker.addFrame(refreshStartTime, presentFence,
hw->getVisibleLayersSortedByZ(), hw->getClientTargetAcquireFence());
-#endif
if (mAnimCompositionPending) {
mAnimCompositionPending = false;
@@ -2273,9 +2263,12 @@
sp<Layer> layer(client->getLayerUser(s.surface));
if (layer != 0) {
const uint32_t what = s.what;
+ bool positionAppliesWithResize =
+ what & layer_state_t::ePositionAppliesWithResize;
if (what & layer_state_t::ePositionChanged) {
- if (layer->setPosition(s.x, s.y))
+ if (layer->setPosition(s.x, s.y, !positionAppliesWithResize)) {
flags |= eTraversalNeeded;
+ }
}
if (what & layer_state_t::eLayerChanged) {
// NOTE: index needs to be calculated before we update the state
@@ -2626,14 +2619,12 @@
dumpAll = false;
}
-#ifdef ENABLE_FENCE_TRACKING
if ((index < numArgs) &&
(args[index] == String16("--fences"))) {
index++;
mFenceTracker.dump(&result);
dumpAll = false;
}
-#endif
}
if (dumpAll) {
@@ -3605,6 +3596,11 @@
return result;
}
+bool SurfaceFlinger::getFrameTimestamps(const Layer& layer,
+ uint64_t frameNumber, FrameTimestamps* outTimestamps) {
+ return mFenceTracker.getFrameTimestamps(layer, frameNumber, outTimestamps);
+}
+
void SurfaceFlinger::checkScreenshot(size_t w, size_t s, size_t h, void const* vaddr,
const sp<const DisplayDevice>& hw, uint32_t minLayerZ, uint32_t maxLayerZ) {
if (DEBUG_SCREENSHOTS) {
diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp
index 320fddb..f8d4d13 100644
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ b/services/surfaceflinger/tests/Transaction_test.cpp
@@ -66,6 +66,8 @@
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
sp<IBinder> display(sf->getBuiltInDisplay(
ISurfaceComposer::eDisplayIdMain));
+ SurfaceComposerClient::openGlobalTransaction();
+ SurfaceComposerClient::closeGlobalTransaction(true);
ASSERT_EQ(NO_ERROR, sf->captureScreen(display, producer, Rect(), 0, 0,
0, INT_MAX, false));
*sc = new ScreenCapture(cpuConsumer);
diff --git a/vulkan/libvulkan/api_gen.cpp b/vulkan/libvulkan/api_gen.cpp
index ff00f32..0a1dda2 100644
--- a/vulkan/libvulkan/api_gen.cpp
+++ b/vulkan/libvulkan/api_gen.cpp
@@ -128,7 +128,6 @@
INIT_PROC(instance, GetPhysicalDeviceFormatProperties);
INIT_PROC(instance, GetPhysicalDeviceImageFormatProperties);
INIT_PROC(instance, CreateDevice);
- INIT_PROC(instance, EnumerateDeviceLayerProperties);
INIT_PROC(instance, EnumerateDeviceExtensionProperties);
INIT_PROC(instance, GetPhysicalDeviceSparseImageFormatProperties);
INIT_PROC_EXT(KHR_surface, instance, DestroySurfaceKHR);
diff --git a/vulkan/libvulkan/api_gen.h b/vulkan/libvulkan/api_gen.h
index 779b654..7f8d274 100644
--- a/vulkan/libvulkan/api_gen.h
+++ b/vulkan/libvulkan/api_gen.h
@@ -38,7 +38,6 @@
PFN_vkGetPhysicalDeviceFormatProperties GetPhysicalDeviceFormatProperties;
PFN_vkGetPhysicalDeviceImageFormatProperties GetPhysicalDeviceImageFormatProperties;
PFN_vkCreateDevice CreateDevice;
- PFN_vkEnumerateDeviceLayerProperties EnumerateDeviceLayerProperties;
PFN_vkEnumerateDeviceExtensionProperties EnumerateDeviceExtensionProperties;
PFN_vkGetPhysicalDeviceSparseImageFormatProperties GetPhysicalDeviceSparseImageFormatProperties;
PFN_vkDestroySurfaceKHR DestroySurfaceKHR;
diff --git a/vulkan/libvulkan/code-generator.tmpl b/vulkan/libvulkan/code-generator.tmpl
index 307f0e4..f9a4670 100644
--- a/vulkan/libvulkan/code-generator.tmpl
+++ b/vulkan/libvulkan/code-generator.tmpl
@@ -434,7 +434,10 @@
{{AssertType $ "Function"}}
{{if and (Macro "IsFunctionExported" $) (Macro "IsInstanceDispatched" $)}}
- true
+ {{/* deprecated and unused internally */}}
+ {{if not (eq $.Name "vkEnumerateDeviceLayerProperties")}}
+ true
+ {{end}}
{{end}}
{{end}}
@@ -938,8 +941,6 @@
{{else if eq $.Name "vkDestroyInstance"}}true
{{else if eq $.Name "vkDestroyDevice"}}true
- {{else if eq $.Name "vkEnumerateDeviceLayerProperties"}}true
-
{{/* Enumeration of extensions */}}
{{else if eq $.Name "vkEnumerateDeviceExtensionProperties"}}true
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index eabbf1f..2555272 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -124,7 +124,7 @@
Hal Hal::hal_;
bool Hal::Open() {
- ALOG_ASSERT(!dev_, "OpenHAL called more than once");
+ ALOG_ASSERT(!hal_.dev_, "OpenHAL called more than once");
// Use a stub device unless we successfully open a real HAL device.
hal_.dev_ = &stubhal::kDevice;
@@ -797,6 +797,7 @@
return VK_ERROR_INCOMPATIBLE_DRIVER;
}
+ data->driver_device = dev;
*pDevice = dev;
diff --git a/vulkan/libvulkan/driver.h b/vulkan/libvulkan/driver.h
index 210c3c7..a02ebd7 100644
--- a/vulkan/libvulkan/driver.h
+++ b/vulkan/libvulkan/driver.h
@@ -98,6 +98,7 @@
std::bitset<ProcHook::EXTENSION_COUNT> hook_extensions;
+ VkDevice driver_device;
DeviceDriverTable driver;
};
diff --git a/vulkan/libvulkan/driver_gen.cpp b/vulkan/libvulkan/driver_gen.cpp
index 29351a1..d979a34 100644
--- a/vulkan/libvulkan/driver_gen.cpp
+++ b/vulkan/libvulkan/driver_gen.cpp
@@ -328,7 +328,6 @@
INIT_PROC(instance, EnumeratePhysicalDevices);
INIT_PROC(instance, GetInstanceProcAddr);
INIT_PROC(instance, CreateDevice);
- INIT_PROC(instance, EnumerateDeviceLayerProperties);
INIT_PROC(instance, EnumerateDeviceExtensionProperties);
INIT_PROC_EXT(EXT_debug_report, instance, CreateDebugReportCallbackEXT);
INIT_PROC_EXT(EXT_debug_report, instance, DestroyDebugReportCallbackEXT);
diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h
index ca17d57..a60b2fe 100644
--- a/vulkan/libvulkan/driver_gen.h
+++ b/vulkan/libvulkan/driver_gen.h
@@ -58,7 +58,6 @@
PFN_vkEnumeratePhysicalDevices EnumeratePhysicalDevices;
PFN_vkGetInstanceProcAddr GetInstanceProcAddr;
PFN_vkCreateDevice CreateDevice;
- PFN_vkEnumerateDeviceLayerProperties EnumerateDeviceLayerProperties;
PFN_vkEnumerateDeviceExtensionProperties EnumerateDeviceExtensionProperties;
PFN_vkCreateDebugReportCallbackEXT CreateDebugReportCallbackEXT;
PFN_vkDestroyDebugReportCallbackEXT DestroyDebugReportCallbackEXT;
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index c3d71d5..adc7d5c 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -109,6 +109,7 @@
struct Surface {
android::sp<ANativeWindow> window;
+ VkSwapchainKHR swapchain_handle;
};
VkSurfaceKHR HandleFromSurface(Surface* surface) {
@@ -147,6 +148,65 @@
return reinterpret_cast<Swapchain*>(handle);
}
+void ReleaseSwapchainImage(VkDevice device,
+ ANativeWindow* window,
+ int release_fence,
+ Swapchain::Image& image) {
+ ALOG_ASSERT(release_fence == -1 || image.dequeued,
+ "ReleaseSwapchainImage: can't provide a release fence for "
+ "non-dequeued images");
+
+ if (image.dequeued) {
+ if (release_fence >= 0) {
+ // We get here from vkQueuePresentKHR. The application is
+ // responsible for creating an execution dependency chain from
+ // vkAcquireNextImage (dequeue_fence) to vkQueuePresentKHR
+ // (release_fence), so we can drop the dequeue_fence here.
+ if (image.dequeue_fence >= 0)
+ close(image.dequeue_fence);
+ } else {
+ // We get here during swapchain destruction, or various serious
+ // error cases e.g. when we can't create the release_fence during
+ // vkQueuePresentKHR. In non-error cases, the dequeue_fence should
+ // have already signalled, since the swapchain images are supposed
+ // to be idle before the swapchain is destroyed. In error cases,
+ // there may be rendering in flight to the image, but since we
+ // weren't able to create a release_fence, waiting for the
+ // dequeue_fence is about the best we can do.
+ release_fence = image.dequeue_fence;
+ }
+ image.dequeue_fence = -1;
+
+ if (window) {
+ window->cancelBuffer(window, image.buffer.get(), release_fence);
+ } else {
+ if (release_fence >= 0) {
+ sync_wait(release_fence, -1 /* forever */);
+ close(release_fence);
+ }
+ }
+
+ image.dequeued = false;
+ }
+
+ if (image.image) {
+ GetData(device).driver.DestroyImage(device, image.image, nullptr);
+ image.image = VK_NULL_HANDLE;
+ }
+
+ image.buffer.clear();
+}
+
+void OrphanSwapchain(VkDevice device, Swapchain* swapchain) {
+ if (swapchain->surface.swapchain_handle != HandleFromSwapchain(swapchain))
+ return;
+ for (uint32_t i = 0; i < swapchain->num_images; i++) {
+ if (!swapchain->images[i].dequeued)
+ ReleaseSwapchainImage(device, nullptr, -1, swapchain->images[i]);
+ }
+ swapchain->surface.swapchain_handle = VK_NULL_HANDLE;
+}
+
} // anonymous namespace
VKAPI_ATTR
@@ -165,6 +225,7 @@
Surface* surface = new (mem) Surface;
surface->window = pCreateInfo->window;
+ surface->swapchain_handle = VK_NULL_HANDLE;
// TODO(jessehall): Create and use NATIVE_WINDOW_API_VULKAN.
int err =
@@ -191,6 +252,11 @@
if (!surface)
return;
native_window_api_disconnect(surface->window.get(), NATIVE_WINDOW_API_EGL);
+ ALOGV_IF(surface->swapchain_handle != VK_NULL_HANDLE,
+ "destroyed VkSurfaceKHR 0x%" PRIx64
+ " has active VkSwapchainKHR 0x%" PRIx64,
+ reinterpret_cast<uint64_t>(surface_handle),
+ reinterpret_cast<uint64_t>(surface->swapchain_handle));
surface->~Surface();
if (!allocator)
allocator = &GetData(instance).allocator;
@@ -344,29 +410,53 @@
allocator = &GetData(device).allocator;
ALOGV_IF(create_info->imageArrayLayers != 1,
- "Swapchain imageArrayLayers (%u) != 1 not supported",
+ "swapchain imageArrayLayers=%u not supported",
create_info->imageArrayLayers);
-
- ALOGE_IF(create_info->imageColorSpace != VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
- "color spaces other than SRGB_NONLINEAR not yet implemented");
- ALOGE_IF(create_info->oldSwapchain,
- "swapchain re-creation not yet implemented");
- ALOGE_IF((create_info->preTransform & ~kSupportedTransforms) != 0,
- "swapchain preTransform %d not supported",
+ ALOGV_IF(create_info->imageColorSpace != VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
+ "swapchain imageColorSpace=%u not supported",
+ create_info->imageColorSpace);
+ ALOGV_IF((create_info->preTransform & ~kSupportedTransforms) != 0,
+ "swapchain preTransform=%#x not supported",
create_info->preTransform);
- ALOGW_IF(!(create_info->presentMode == VK_PRESENT_MODE_FIFO_KHR ||
+ ALOGV_IF(!(create_info->presentMode == VK_PRESENT_MODE_FIFO_KHR ||
create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR),
- "swapchain present mode %d not supported",
+ "swapchain presentMode=%u not supported",
create_info->presentMode);
Surface& surface = *SurfaceFromHandle(create_info->surface);
+ if (surface.swapchain_handle != create_info->oldSwapchain) {
+ ALOGV("Can't create a swapchain for VkSurfaceKHR 0x%" PRIx64
+ " because it already has active swapchain 0x%" PRIx64
+ " but VkSwapchainCreateInfo::oldSwapchain=0x%" PRIx64,
+ reinterpret_cast<uint64_t>(create_info->surface),
+ reinterpret_cast<uint64_t>(surface.swapchain_handle),
+ reinterpret_cast<uint64_t>(create_info->oldSwapchain));
+ return VK_ERROR_NATIVE_WINDOW_IN_USE_KHR;
+ }
+ if (create_info->oldSwapchain != VK_NULL_HANDLE)
+ OrphanSwapchain(device, SwapchainFromHandle(create_info->oldSwapchain));
+
// -- Reset the native window --
// The native window might have been used previously, and had its properties
// changed from defaults. That will affect the answer we get for queries
// like MIN_UNDEQUED_BUFFERS. Reset to a known/default state before we
// attempt such queries.
+ // The native window only allows dequeueing all buffers before any have
+ // been queued, since after that point at least one is assumed to be in
+ // non-FREE state at any given time. Disconnecting and re-connecting
+ // orphans the previous buffers, getting us back to the state where we can
+ // dequeue all buffers.
+ err = native_window_api_disconnect(surface.window.get(),
+ NATIVE_WINDOW_API_EGL);
+ ALOGW_IF(err != 0, "native_window_api_disconnect failed: %s (%d)",
+ strerror(-err), err);
+ err =
+ native_window_api_connect(surface.window.get(), NATIVE_WINDOW_API_EGL);
+ ALOGW_IF(err != 0, "native_window_api_connect failed: %s (%d)",
+ strerror(-err), err);
+
err = native_window_set_buffer_count(surface.window.get(), 0);
if (err != 0) {
ALOGE("native_window_set_buffer_count(0) failed: %s (%d)",
@@ -397,7 +487,7 @@
native_format = HAL_PIXEL_FORMAT_RGB_565;
break;
default:
- ALOGE("unsupported swapchain format %d", create_info->imageFormat);
+ ALOGV("unsupported swapchain format %d", create_info->imageFormat);
break;
}
err = native_window_set_buffers_format(surface.window.get(), native_format);
@@ -618,7 +708,8 @@
return result;
}
- *swapchain_handle = HandleFromSwapchain(swapchain);
+ surface.swapchain_handle = HandleFromSwapchain(swapchain);
+ *swapchain_handle = surface.swapchain_handle;
return VK_SUCCESS;
}
@@ -628,21 +719,13 @@
const VkAllocationCallbacks* allocator) {
const auto& dispatch = GetData(device).driver;
Swapchain* swapchain = SwapchainFromHandle(swapchain_handle);
- const android::sp<ANativeWindow>& window = swapchain->surface.window;
+ bool active = swapchain->surface.swapchain_handle == swapchain_handle;
+ ANativeWindow* window = active ? swapchain->surface.window.get() : nullptr;
- for (uint32_t i = 0; i < swapchain->num_images; i++) {
- Swapchain::Image& img = swapchain->images[i];
- if (img.dequeued) {
- window->cancelBuffer(window.get(), img.buffer.get(),
- img.dequeue_fence);
- img.dequeue_fence = -1;
- img.dequeued = false;
- }
- if (img.image) {
- dispatch.DestroyImage(device, img.image, nullptr);
- }
- }
-
+ for (uint32_t i = 0; i < swapchain->num_images; i++)
+ ReleaseSwapchainImage(device, window, -1, swapchain->images[i]);
+ if (active)
+ swapchain->surface.swapchain_handle = VK_NULL_HANDLE;
if (!allocator)
allocator = &GetData(device).allocator;
swapchain->~Swapchain();
@@ -655,6 +738,10 @@
uint32_t* count,
VkImage* images) {
Swapchain& swapchain = *SwapchainFromHandle(swapchain_handle);
+ ALOGW_IF(swapchain.surface.swapchain_handle != swapchain_handle,
+ "getting images for non-active swapchain 0x%" PRIx64
+ "; only dequeued image handles are valid",
+ reinterpret_cast<uint64_t>(swapchain_handle));
VkResult result = VK_SUCCESS;
if (images) {
uint32_t n = swapchain.num_images;
@@ -681,6 +768,9 @@
VkResult result;
int err;
+ if (swapchain.surface.swapchain_handle != swapchain_handle)
+ return VK_ERROR_OUT_OF_DATE_KHR;
+
ALOGW_IF(
timeout != UINT64_MAX,
"vkAcquireNextImageKHR: non-infinite timeouts not yet implemented");
@@ -739,6 +829,26 @@
return VK_SUCCESS;
}
+static VkResult WorstPresentResult(VkResult a, VkResult b) {
+ // See the error ranking for vkQueuePresentKHR at the end of section 29.6
+ // (in spec version 1.0.14).
+ static const VkResult kWorstToBest[] = {
+ VK_ERROR_DEVICE_LOST,
+ VK_ERROR_SURFACE_LOST_KHR,
+ VK_ERROR_OUT_OF_DATE_KHR,
+ VK_ERROR_OUT_OF_DEVICE_MEMORY,
+ VK_ERROR_OUT_OF_HOST_MEMORY,
+ VK_SUBOPTIMAL_KHR,
+ };
+ for (auto result : kWorstToBest) {
+ if (a == result || b == result)
+ return result;
+ }
+ ALOG_ASSERT(a == VK_SUCCESS, "invalid vkQueuePresentKHR result %d", a);
+ ALOG_ASSERT(b == VK_SUCCESS, "invalid vkQueuePresentKHR result %d", b);
+ return a != VK_SUCCESS ? a : b;
+}
+
VKAPI_ATTR
VkResult QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* present_info) {
ALOGV_IF(present_info->sType != VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
@@ -746,14 +856,16 @@
present_info->sType);
ALOGV_IF(present_info->pNext, "VkPresentInfo::pNext != NULL");
+ VkDevice device = GetData(queue).driver_device;
const auto& dispatch = GetData(queue).driver;
VkResult final_result = VK_SUCCESS;
+
for (uint32_t sc = 0; sc < present_info->swapchainCount; sc++) {
Swapchain& swapchain =
*SwapchainFromHandle(present_info->pSwapchains[sc]);
- ANativeWindow* window = swapchain.surface.window.get();
uint32_t image_idx = present_info->pImageIndices[sc];
Swapchain::Image& img = swapchain.images[image_idx];
+ VkResult swapchain_result = VK_SUCCESS;
VkResult result;
int err;
@@ -763,37 +875,42 @@
present_info->pWaitSemaphores, img.image, &fence);
if (result != VK_SUCCESS) {
ALOGE("QueueSignalReleaseImageANDROID failed: %d", result);
- if (present_info->pResults)
- present_info->pResults[sc] = result;
- if (final_result == VK_SUCCESS)
- final_result = result;
- // TODO(jessehall): What happens to the buffer here? Does the app
- // still own it or not, i.e. should we cancel the buffer? Hard to
- // do correctly without synchronizing, though I guess we could wait
- // for the queue to idle.
- continue;
+ swapchain_result = result;
}
- err = window->queueBuffer(window, img.buffer.get(), fence);
- if (err != 0) {
- // TODO(jessehall): What now? We should probably cancel the buffer,
- // I guess?
- ALOGE("queueBuffer failed: %s (%d)", strerror(-err), err);
- if (present_info->pResults)
- present_info->pResults[sc] = result;
- if (final_result == VK_SUCCESS)
- final_result = VK_ERROR_INITIALIZATION_FAILED;
- continue;
+ if (swapchain.surface.swapchain_handle ==
+ present_info->pSwapchains[sc]) {
+ ANativeWindow* window = swapchain.surface.window.get();
+ if (swapchain_result == VK_SUCCESS) {
+ err = window->queueBuffer(window, img.buffer.get(), fence);
+ // queueBuffer always closes fence, even on error
+ if (err != 0) {
+ // TODO(jessehall): What now? We should probably cancel the
+ // buffer, I guess?
+ ALOGE("queueBuffer failed: %s (%d)", strerror(-err), err);
+ swapchain_result = WorstPresentResult(
+ swapchain_result, VK_ERROR_OUT_OF_DATE_KHR);
+ }
+ if (img.dequeue_fence >= 0) {
+ close(img.dequeue_fence);
+ img.dequeue_fence = -1;
+ }
+ img.dequeued = false;
+ }
+ if (swapchain_result != VK_SUCCESS) {
+ ReleaseSwapchainImage(device, window, fence, img);
+ OrphanSwapchain(device, &swapchain);
+ }
+ } else {
+ ReleaseSwapchainImage(device, nullptr, fence, img);
+ swapchain_result = VK_ERROR_OUT_OF_DATE_KHR;
}
- if (img.dequeue_fence != -1) {
- close(img.dequeue_fence);
- img.dequeue_fence = -1;
- }
- img.dequeued = false;
-
if (present_info->pResults)
- present_info->pResults[sc] = VK_SUCCESS;
+ present_info->pResults[sc] = swapchain_result;
+
+ if (swapchain_result != final_result)
+ final_result = WorstPresentResult(final_result, swapchain_result);
}
return final_result;