Merge \"Fixed file descriptor leak in IMemory\" into nyc-dev
am: a13a22649b
Change-Id: Id692c99cbe9274e51f6d65cb38b3e0a7293e7518
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index cd7c4aa..a67d204 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -54,7 +54,7 @@
static char cmdline_buf[16384] = "(unknown)";
static const char *dump_traces_path = NULL;
-// TODO: should be part of dumpstate object
+// TODO: variables below should be part of dumpstate object
static unsigned long id;
static char build_type[PROPERTY_VALUE_MAX];
static time_t now;
@@ -68,7 +68,7 @@
#define PSTORE_LAST_KMSG "/sys/fs/pstore/console-ramoops"
-#define RAFT_DIR "/data/misc/raft/"
+#define RAFT_DIR "/data/misc/raft"
#define RECOVERY_DIR "/cache/recovery"
#define RECOVERY_DATA_DIR "/data/misc/recovery"
#define LOGPERSIST_DATA_DIR "/data/misc/logd"
@@ -219,6 +219,40 @@
}
}
+static void dump_raft() {
+ if (is_user_build()) {
+ return;
+ }
+
+ std::string raft_log_path = bugreport_dir + "/raft_log.txt";
+ if (raft_log_path.empty()) {
+ MYLOGD("raft_log_path is empty\n");
+ return;
+ }
+
+ struct stat s;
+ if (stat(RAFT_DIR, &s) != 0 || !S_ISDIR(s.st_mode)) {
+ MYLOGD("%s does not exist or is not a directory\n", RAFT_DIR);
+ return;
+ }
+
+ if (!zip_writer) {
+ // Write compressed and encoded raft logs to stdout if not zip_writer.
+ run_command("RAFT LOGS", 600, "logcompressor", "-r", RAFT_DIR, NULL);
+ return;
+ }
+
+ run_command("RAFT LOGS", 600, "logcompressor", "-n", "-r", RAFT_DIR,
+ "-o", raft_log_path.c_str(), NULL);
+ if (!add_zip_entry("raft_log.txt", raft_log_path)) {
+ MYLOGE("Unable to add raft log %s to zip file\n", raft_log_path.c_str());
+ } else {
+ if (remove(raft_log_path.c_str())) {
+ MYLOGE("Error removing raft file %s: %s\n", raft_log_path.c_str(), strerror(errno));
+ }
+ }
+}
+
static bool skip_not_stat(const char *path) {
static const char stat[] = "/stat";
size_t len = strlen(path);
@@ -507,7 +541,7 @@
property_get("ro.build.display.id", build, "(unknown)");
property_get("ro.build.fingerprint", fingerprint, "(unknown)");
property_get("ro.build.type", build_type, "(unknown)");
- property_get("ro.baseband", radio, "(unknown)");
+ property_get("gsm.version.baseband", radio, "(unknown)");
property_get("ro.bootloader", bootloader, "(unknown)");
property_get("gsm.operator.alpha", network, "(unknown)");
strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", localtime(&now));
@@ -701,8 +735,6 @@
run_command("LOG STATISTICS", 10, "logcat", "-b", "all", "-S", NULL);
- run_command("RAFT LOGS", 600, SU_PATH, "root", "logcompressor", "-r", RAFT_DIR, NULL);
-
/* show the traces we collected in main(), if that was done */
if (dump_traces_path != NULL) {
dump_file("VM TRACES JUST NOW", dump_traces_path);
@@ -1275,6 +1307,9 @@
// Dumps systrace right away, otherwise it will be filled with unnecessary events.
dump_systrace();
+ // TODO: Drop root user and move into dumpstate() once b/28633932 is fixed.
+ dump_raft();
+
// 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);
diff --git a/cmds/installd/commands.cpp b/cmds/installd/commands.cpp
index 2a9950a..8999441 100644
--- a/cmds/installd/commands.cpp
+++ b/cmds/installd/commands.cpp
@@ -28,9 +28,9 @@
#include <sys/xattr.h>
#include <unistd.h>
+#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
-#include <android-base/logging.h>
#include <android-base/unique_fd.h>
#include <cutils/fs.h>
#include <cutils/log.h> // TODO: Move everything to base/logging.
@@ -49,6 +49,7 @@
#define LOG_TAG "installd"
#endif
+using android::base::EndsWith;
using android::base::StringPrintf;
namespace android {
@@ -1313,13 +1314,29 @@
return true;
}
-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
+// 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) {
@@ -1330,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) {
@@ -1388,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;
@@ -1410,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.
}
@@ -1423,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;
@@ -1445,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);
@@ -1486,111 +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 = get_location_from_path(input_file);
- 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);
+ 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)
@@ -1926,6 +2044,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";
@@ -1944,37 +2093,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 7a42c5c..c0c39c5 100644
--- a/cmds/installd/commands.h
+++ b/cmds/installd/commands.h
@@ -56,9 +56,21 @@
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);
+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 061359e..9d2f71b 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])
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/otapreopt_script.sh b/cmds/installd/otapreopt_script.sh
index a31734a..394c244 100644
--- a/cmds/installd/otapreopt_script.sh
+++ b/cmds/installd/otapreopt_script.sh
@@ -18,24 +18,33 @@
# This script will run as a postinstall step to drive otapreopt.
+STATUS_FD="$2"
+
# Maximum number of packages/steps.
MAXIMUM_PACKAGES=1000
PREPARE=$(cmd otadexopt prepare)
-if [ "$PREPARE" != "Success" ] ; then
- echo "Failed to prepare."
- exit 1
-fi
+# Note: Ignore preparation failures. Step and done will fail and exit this.
+# This is necessary to support suspends - the OTA service will keep
+# the state around for us.
+
+PROGRESS=$(cmd otadexopt progress)
+print -u${STATUS_FD} "global_progress $PROGRESS"
i=0
while ((i<MAXIMUM_PACKAGES)) ; do
cmd otadexopt step
+
+ PROGRESS=$(cmd otadexopt progress)
+ print -u${STATUS_FD} "global_progress $PROGRESS"
+
DONE=$(cmd otadexopt done)
- if [ "$DONE" = "OTA complete." ] ; then
- break
+ if [ "$DONE" = "OTA incomplete." ] ; then
+ sleep 1
+ i=$((i+1))
+ continue
fi
- sleep 1
- i=$((i+1))
+ break
done
DONE=$(cmd otadexopt done)
@@ -45,6 +54,7 @@
echo "Complete or error."
fi
+print -u${STATUS_FD} "global_progress 1.0"
cmd otadexopt cleanup
exit 0
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/data/etc/wearable_core_hardware.xml b/data/etc/wearable_core_hardware.xml
index 4b7a706..4ff00b5 100644
--- a/data/etc/wearable_core_hardware.xml
+++ b/data/etc/wearable_core_hardware.xml
@@ -34,7 +34,6 @@
<!-- device administration -->
<feature name="android.software.device_admin" />
- <feature name="android.software.managed_users" />
<!-- devices with GPS must include device/google/clockwork/gps.xml -->
<!-- devices with an autofocus camera and/or flash must include either
diff --git a/include/android/keycodes.h b/include/android/keycodes.h
index 67e28da..a17c57a 100644
--- a/include/android/keycodes.h
+++ b/include/android/keycodes.h
@@ -757,7 +757,15 @@
/** Copy key. */
AKEYCODE_COPY = 278,
/** Paste key. */
- AKEYCODE_PASTE = 279
+ AKEYCODE_PASTE = 279,
+ /** fingerprint navigation key, up. */
+ AKEYCODE_FP_NAV_UP = 280,
+ /** fingerprint navigation key, down. */
+ AKEYCODE_FP_NAV_DOWN = 281,
+ /** fingerprint navigation key, left. */
+ AKEYCODE_FP_NAV_LEFT = 282,
+ /** fingerprint navigation key, right. */
+ AKEYCODE_FP_NAV_RIGHT = 283
// NOTE: If you add a new keycode here you must also add it to several other files.
// Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
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/BufferQueueConsumer.h b/include/gui/BufferQueueConsumer.h
index b2daae4..a9fce1a 100644
--- a/include/gui/BufferQueueConsumer.h
+++ b/include/gui/BufferQueueConsumer.h
@@ -136,6 +136,10 @@
// Retrieve the sideband buffer stream, if any.
virtual sp<NativeHandle> getSidebandStream() const;
+ // See IGraphicBufferConsumer::getOccupancyHistory
+ virtual status_t getOccupancyHistory(bool forceFlush,
+ std::vector<OccupancyTracker::Segment>* outHistory) override;
+
// dump our state in a String
virtual void dump(String8& result, const char* prefix) const;
diff --git a/include/gui/BufferQueueCore.h b/include/gui/BufferQueueCore.h
index 4337da9..6c69d69 100644
--- a/include/gui/BufferQueueCore.h
+++ b/include/gui/BufferQueueCore.h
@@ -20,6 +20,7 @@
#include <gui/BufferItem.h>
#include <gui/BufferQueueDefs.h>
#include <gui/BufferSlot.h>
+#include <gui/OccupancyTracker.h>
#include <utils/Condition.h>
#include <utils/Mutex.h>
@@ -322,6 +323,8 @@
// The slot of the last queued buffer
int mLastQueuedSlot;
+ OccupancyTracker mOccupancyTracker;
+
}; // class BufferQueueCore
} // namespace android
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/ConsumerBase.h b/include/gui/ConsumerBase.h
index 9307a26..d1f4cdd 100644
--- a/include/gui/ConsumerBase.h
+++ b/include/gui/ConsumerBase.h
@@ -85,6 +85,10 @@
// See IGraphicBufferConsumer::setDefaultBufferDataSpace
status_t setDefaultBufferDataSpace(android_dataspace defaultDataSpace);
+ // See IGraphicBufferConsumer::getOccupancyHistory
+ status_t getOccupancyHistory(bool forceFlush,
+ std::vector<OccupancyTracker::Segment>* outHistory);
+
private:
ConsumerBase(const ConsumerBase&);
void operator=(const ConsumerBase&);
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/IGraphicBufferConsumer.h b/include/gui/IGraphicBufferConsumer.h
index e983c16..4915478 100644
--- a/include/gui/IGraphicBufferConsumer.h
+++ b/include/gui/IGraphicBufferConsumer.h
@@ -27,6 +27,7 @@
#include <binder/IInterface.h>
#include <ui/PixelFormat.h>
#include <ui/Rect.h>
+#include <gui/OccupancyTracker.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
@@ -265,6 +266,12 @@
// Retrieve the sideband buffer stream, if any.
virtual sp<NativeHandle> getSidebandStream() const = 0;
+ // Retrieves any stored segments of the occupancy history of this
+ // BufferQueue and clears them. Optionally closes out the pending segment if
+ // forceFlush is true.
+ virtual status_t getOccupancyHistory(bool forceFlush,
+ std::vector<OccupancyTracker::Segment>* outHistory) = 0;
+
// dump state into a string
virtual void dump(String8& result, const char* prefix) const = 0;
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/OccupancyTracker.h b/include/gui/OccupancyTracker.h
new file mode 100644
index 0000000..1d15e7f
--- /dev/null
+++ b/include/gui/OccupancyTracker.h
@@ -0,0 +1,104 @@
+/*
+ * 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_OCCUPANCYTRACKER_H
+#define ANDROID_GUI_OCCUPANCYTRACKER_H
+
+#include <binder/Parcelable.h>
+
+#include <utils/Timers.h>
+
+#include <deque>
+#include <unordered_map>
+
+namespace android {
+
+class String8;
+
+class OccupancyTracker
+{
+public:
+ OccupancyTracker()
+ : mPendingSegment(),
+ mSegmentHistory(),
+ mLastOccupancy(0),
+ mLastOccupancyChangeTime(0) {}
+
+ struct Segment : public Parcelable {
+ Segment()
+ : totalTime(0),
+ numFrames(0),
+ occupancyAverage(0.0f),
+ usedThirdBuffer(false) {}
+
+ Segment(nsecs_t totalTime, size_t numFrames, float occupancyAverage,
+ bool usedThirdBuffer)
+ : totalTime(totalTime),
+ numFrames(numFrames),
+ occupancyAverage(occupancyAverage),
+ usedThirdBuffer(usedThirdBuffer) {}
+
+ // Parcelable interface
+ virtual status_t writeToParcel(Parcel* parcel) const override;
+ virtual status_t readFromParcel(const Parcel* parcel) override;
+
+ nsecs_t totalTime;
+ size_t numFrames;
+
+ // Average occupancy of the queue over this segment. (0.0, 1.0) implies
+ // double-buffered, (1.0, 2.0) implies triple-buffered.
+ float occupancyAverage;
+
+ // Whether a third buffer was used at all during this segment (since a
+ // segment could read as double-buffered on average, but still require a
+ // third buffer to avoid jank for some smaller portion)
+ bool usedThirdBuffer;
+ };
+
+ void registerOccupancyChange(size_t occupancy);
+ std::vector<Segment> getSegmentHistory(bool forceFlush);
+
+private:
+ static constexpr size_t MAX_HISTORY_SIZE = 10;
+ static constexpr nsecs_t NEW_SEGMENT_DELAY = ms2ns(100);
+ static constexpr size_t LONG_SEGMENT_THRESHOLD = 3;
+
+ struct PendingSegment {
+ void clear() {
+ totalTime = 0;
+ numFrames = 0;
+ mOccupancyTimes.clear();
+ }
+
+ nsecs_t totalTime;
+ size_t numFrames;
+ std::unordered_map<size_t, nsecs_t> mOccupancyTimes;
+ };
+
+ void recordPendingSegment();
+
+ PendingSegment mPendingSegment;
+ std::deque<Segment> mSegmentHistory;
+
+ size_t mLastOccupancy;
+ nsecs_t mLastOccupancyChangeTime;
+
+}; // class OccupancyTracker
+
+} // namespace android
+
+#endif
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/input/InputEventLabels.h b/include/input/InputEventLabels.h
index b7012eb..542f647 100644
--- a/include/input/InputEventLabels.h
+++ b/include/input/InputEventLabels.h
@@ -319,6 +319,10 @@
DEFINE_KEYCODE(CUT),
DEFINE_KEYCODE(COPY),
DEFINE_KEYCODE(PASTE),
+ DEFINE_KEYCODE(FP_NAV_UP),
+ DEFINE_KEYCODE(FP_NAV_DOWN),
+ DEFINE_KEYCODE(FP_NAV_LEFT),
+ DEFINE_KEYCODE(FP_NAV_RIGHT),
{ NULL, 0 }
};
diff --git a/libs/gui/Android.mk b/libs/gui/Android.mk
index 6e92a47..3e30bb2 100644
--- a/libs/gui/Android.mk
+++ b/libs/gui/Android.mk
@@ -64,6 +64,7 @@
ISurfaceComposer.cpp \
ISurfaceComposerClient.cpp \
LayerState.cpp \
+ OccupancyTracker.cpp \
Sensor.cpp \
SensorEventQueue.cpp \
SensorManager.cpp \
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/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index cbc8893..e8860d1 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -260,6 +260,7 @@
mCore->mDequeueCondition.broadcast();
ATRACE_INT(mCore->mConsumerName.string(), mCore->mQueue.size());
+ mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size());
VALIDATE_CONSISTENCY();
}
@@ -717,6 +718,13 @@
return mCore->mSidebandStream;
}
+status_t BufferQueueConsumer::getOccupancyHistory(bool forceFlush,
+ std::vector<OccupancyTracker::Segment>* outHistory) {
+ Mutex::Autolock lock(mCore->mMutex);
+ *outHistory = mCore->mOccupancyTracker.getSegmentHistory(forceFlush);
+ return NO_ERROR;
+}
+
void BufferQueueConsumer::dump(String8& result, const char* prefix) const {
const IPCThreadState* ipc = IPCThreadState::self();
const pid_t pid = ipc->getCallingPid();
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 69a408c..3a0a283 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -890,6 +890,7 @@
static_cast<uint32_t>(mCore->mQueue.size()));
ATRACE_INT(mCore->mConsumerName.string(), mCore->mQueue.size());
+ mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size());
// Take a ticket for the callback functions
callbackTicket = mNextCallbackTicket++;
@@ -1404,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/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp
index a6a9712..84965ef 100644
--- a/libs/gui/ConsumerBase.cpp
+++ b/libs/gui/ConsumerBase.cpp
@@ -235,6 +235,16 @@
return mConsumer->setDefaultBufferDataSpace(defaultDataSpace);
}
+status_t ConsumerBase::getOccupancyHistory(bool forceFlush,
+ std::vector<OccupancyTracker::Segment>* outHistory) {
+ Mutex::Autolock _l(mMutex);
+ if (mAbandoned) {
+ CB_LOGE("getOccupancyHistory: ConsumerBase is abandoned!");
+ return NO_INIT;
+ }
+ return mConsumer->getOccupancyHistory(forceFlush, outHistory);
+}
+
void ConsumerBase::dump(String8& result) const {
dump(result, "");
}
diff --git a/libs/gui/IConsumerListener.cpp b/libs/gui/IConsumerListener.cpp
index cab7dc3..9a06011 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/IGraphicBufferConsumer.cpp b/libs/gui/IGraphicBufferConsumer.cpp
index cb1ad35..7c4379f 100644
--- a/libs/gui/IGraphicBufferConsumer.cpp
+++ b/libs/gui/IGraphicBufferConsumer.cpp
@@ -51,6 +51,7 @@
SET_CONSUMER_USAGE_BITS,
SET_TRANSFORM_HINT,
GET_SIDEBAND_STREAM,
+ GET_OCCUPANCY_HISTORY,
DUMP,
};
@@ -260,6 +261,31 @@
return stream;
}
+ virtual status_t getOccupancyHistory(bool forceFlush,
+ std::vector<OccupancyTracker::Segment>* outHistory) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
+ status_t error = data.writeBool(forceFlush);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = remote()->transact(GET_OCCUPANCY_HISTORY, data,
+ &reply);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = reply.readParcelableVector(outHistory);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ status_t result = NO_ERROR;
+ error = reply.readInt32(&result);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ return result;
+ }
+
virtual void dump(String8& result, const char* prefix) const {
Parcel data, reply;
data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
@@ -409,6 +435,25 @@
}
return NO_ERROR;
}
+ case GET_OCCUPANCY_HISTORY: {
+ CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
+ bool forceFlush = false;
+ status_t error = data.readBool(&forceFlush);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ std::vector<OccupancyTracker::Segment> history;
+ status_t result = getOccupancyHistory(forceFlush, &history);
+ error = reply->writeParcelableVector(history);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = reply->writeInt32(result);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ return NO_ERROR;
+ }
case DUMP: {
CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
String8 result = data.readString8();
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index 2c48d83..9317eff 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/OccupancyTracker.cpp b/libs/gui/OccupancyTracker.cpp
new file mode 100644
index 0000000..9687aaf
--- /dev/null
+++ b/libs/gui/OccupancyTracker.cpp
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "OccupancyTracker"
+
+#include <gui/OccupancyTracker.h>
+#include <binder/Parcel.h>
+#include <utils/String8.h>
+#include <utils/Trace.h>
+
+#include <inttypes.h>
+
+namespace android {
+
+status_t OccupancyTracker::Segment::writeToParcel(Parcel* parcel) const {
+ status_t result = parcel->writeInt64(totalTime);
+ if (result != OK) {
+ return result;
+ }
+ result = parcel->writeUint64(static_cast<uint64_t>(numFrames));
+ if (result != OK) {
+ return result;
+ }
+ result = parcel->writeFloat(occupancyAverage);
+ if (result != OK) {
+ return result;
+ }
+ return parcel->writeBool(usedThirdBuffer);
+}
+
+status_t OccupancyTracker::Segment::readFromParcel(const Parcel* parcel) {
+ status_t result = parcel->readInt64(&totalTime);
+ if (result != OK) {
+ return result;
+ }
+ uint64_t uintNumFrames = 0;
+ result = parcel->readUint64(&uintNumFrames);
+ if (result != OK) {
+ return result;
+ }
+ numFrames = static_cast<size_t>(uintNumFrames);
+ result = parcel->readFloat(&occupancyAverage);
+ if (result != OK) {
+ return result;
+ }
+ return parcel->readBool(&usedThirdBuffer);
+}
+
+void OccupancyTracker::registerOccupancyChange(size_t occupancy) {
+ ATRACE_CALL();
+ nsecs_t now = systemTime();
+ nsecs_t delta = now - mLastOccupancyChangeTime;
+ if (delta > NEW_SEGMENT_DELAY) {
+ recordPendingSegment();
+ } else {
+ mPendingSegment.totalTime += delta;
+ if (mPendingSegment.mOccupancyTimes.count(mLastOccupancy)) {
+ mPendingSegment.mOccupancyTimes[mLastOccupancy] += delta;
+ } else {
+ mPendingSegment.mOccupancyTimes[mLastOccupancy] = delta;
+ }
+ }
+ if (occupancy > mLastOccupancy) {
+ ++mPendingSegment.numFrames;
+ }
+ mLastOccupancyChangeTime = now;
+ mLastOccupancy = occupancy;
+}
+
+std::vector<OccupancyTracker::Segment> OccupancyTracker::getSegmentHistory(
+ bool forceFlush) {
+ if (forceFlush) {
+ recordPendingSegment();
+ }
+ std::vector<Segment> segments(mSegmentHistory.cbegin(),
+ mSegmentHistory.cend());
+ mSegmentHistory.clear();
+ return segments;
+}
+
+void OccupancyTracker::recordPendingSegment() {
+ // Only record longer segments to get a better measurement of actual double-
+ // vs. triple-buffered time
+ if (mPendingSegment.numFrames > LONG_SEGMENT_THRESHOLD) {
+ float occupancyAverage = 0.0f;
+ bool usedThirdBuffer = false;
+ for (const auto& timePair : mPendingSegment.mOccupancyTimes) {
+ size_t occupancy = timePair.first;
+ float timeRatio = static_cast<float>(timePair.second) /
+ mPendingSegment.totalTime;
+ occupancyAverage += timeRatio * occupancy;
+ usedThirdBuffer = usedThirdBuffer || (occupancy > 1);
+ }
+ mSegmentHistory.push_front({mPendingSegment.totalTime,
+ mPendingSegment.numFrames, occupancyAverage, usedThirdBuffer});
+ if (mSegmentHistory.size() > MAX_HISTORY_SIZE) {
+ mSegmentHistory.pop_back();
+ }
+ }
+ mPendingSegment.clear();
+}
+
+} // namespace android
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 9d130cd..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);
@@ -617,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;
@@ -737,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/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index 85d63b4..210ce8c 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -34,6 +34,10 @@
#include <gtest/gtest.h>
+#include <thread>
+
+using namespace std::chrono_literals;
+
namespace android {
class BufferQueueTest : public ::testing::Test {
@@ -850,4 +854,140 @@
returnedBuffer->getNativeBuffer()->handle);
}
+TEST_F(BufferQueueTest, TestOccupancyHistory) {
+ createBufferQueue();
+ sp<DummyConsumer> dc(new DummyConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false));
+ IGraphicBufferProducer::QueueBufferOutput output;
+ ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
+ NATIVE_WINDOW_API_CPU, false, &output));
+
+ int slot = BufferQueue::INVALID_BUFFER_SLOT;
+ sp<Fence> fence = Fence::NO_FENCE;
+ sp<GraphicBuffer> buffer = nullptr;
+ IGraphicBufferProducer::QueueBufferInput input(0ull, true,
+ HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT,
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE);
+ BufferItem item{};
+
+ // Preallocate, dequeue, request, and cancel 3 buffers so we don't get
+ // BUFFER_NEEDS_REALLOCATION below
+ int slots[3] = {};
+ mProducer->setMaxDequeuedBufferCount(3);
+ for (size_t i = 0; i < 3; ++i) {
+ status_t result = mProducer->dequeueBuffer(&slots[i], &fence,
+ 0, 0, 0, 0);
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result);
+ ASSERT_EQ(OK, mProducer->requestBuffer(slots[i], &buffer));
+ }
+ for (size_t i = 0; i < 3; ++i) {
+ ASSERT_EQ(OK, mProducer->cancelBuffer(slots[i], Fence::NO_FENCE));
+ }
+
+ // Create 3 segments
+
+ // The first segment is a two-buffer segment, so we only put one buffer into
+ // the queue at a time
+ for (size_t i = 0; i < 5; ++i) {
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+ ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+ std::this_thread::sleep_for(16ms);
+ }
+
+ // Sleep between segments
+ std::this_thread::sleep_for(500ms);
+
+ // The second segment is a double-buffer segment. It starts the same as the
+ // two-buffer segment, but then at the end, we put two buffers in the queue
+ // at the same time before draining it.
+ for (size_t i = 0; i < 5; ++i) {
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+ ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+ std::this_thread::sleep_for(16ms);
+ }
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+ ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+ std::this_thread::sleep_for(16ms);
+ ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+
+ // Sleep between segments
+ std::this_thread::sleep_for(500ms);
+
+ // The third segment is a triple-buffer segment, so the queue is switching
+ // between one buffer and two buffers deep.
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+ for (size_t i = 0; i < 5; ++i) {
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+ ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+ std::this_thread::sleep_for(16ms);
+ }
+ ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+
+ // Now we read the segments
+ std::vector<OccupancyTracker::Segment> history;
+ ASSERT_EQ(OK, mConsumer->getOccupancyHistory(false, &history));
+
+ // Since we didn't force a flush, we should only get the first two segments
+ // (since the third segment hasn't been closed out by the appearance of a
+ // new segment yet)
+ ASSERT_EQ(2u, history.size());
+
+ // The first segment (which will be history[1], since the newest segment
+ // should be at the front of the vector) should be a two-buffer segment,
+ // which implies that the occupancy average should be between 0 and 1, and
+ // usedThirdBuffer should be false
+ const auto& firstSegment = history[1];
+ ASSERT_EQ(5u, firstSegment.numFrames);
+ ASSERT_LT(0, firstSegment.occupancyAverage);
+ ASSERT_GT(1, firstSegment.occupancyAverage);
+ ASSERT_EQ(false, firstSegment.usedThirdBuffer);
+
+ // The second segment should be a double-buffered segment, which implies that
+ // the occupancy average should be between 0 and 1, but usedThirdBuffer
+ // should be true
+ const auto& secondSegment = history[0];
+ ASSERT_EQ(7u, secondSegment.numFrames);
+ ASSERT_LT(0, secondSegment.occupancyAverage);
+ ASSERT_GT(1, secondSegment.occupancyAverage);
+ ASSERT_EQ(true, secondSegment.usedThirdBuffer);
+
+ // If we read the segments again without flushing, we shouldn't get any new
+ // segments
+ ASSERT_EQ(OK, mConsumer->getOccupancyHistory(false, &history));
+ ASSERT_EQ(0u, history.size());
+
+ // Read the segments again, this time forcing a flush so we get the third
+ // segment
+ ASSERT_EQ(OK, mConsumer->getOccupancyHistory(true, &history));
+ ASSERT_EQ(1u, history.size());
+
+ // This segment should be a triple-buffered segment, which implies that the
+ // occupancy average should be between 1 and 2, and usedThirdBuffer should
+ // be true
+ const auto& thirdSegment = history[0];
+ ASSERT_EQ(6u, thirdSegment.numFrames);
+ ASSERT_LT(1, thirdSegment.occupancyAverage);
+ ASSERT_GT(2, thirdSegment.occupancyAverage);
+ ASSERT_EQ(true, thirdSegment.usedThirdBuffer);
+}
+
} // namespace android
diff --git a/opengl/libagl/context.h b/opengl/libagl/context.h
index d23f435..18ef7d5 100644
--- a/opengl/libagl/context.h
+++ b/opengl/libagl/context.h
@@ -26,7 +26,8 @@
#endif
#include <private/pixelflinger/ggl_context.h>
-#include <hardware/gralloc.h>
+
+#include <system/window.h>
#include <GLES/gl.h>
#include <GLES/glext.h>
@@ -615,7 +616,7 @@
culling_t cull;
lighting_t lighting;
user_clip_planes_t clipPlanes;
- compute_iterators_t lerp; __attribute__((aligned(32)));
+ compute_iterators_t lerp __attribute__((aligned(32)));
vertex_t current;
vec4_t currentColorClamped;
vec3_t currentNormal;
diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp
index 92139e9..c1efd1c 100644
--- a/opengl/libagl/egl.cpp
+++ b/opengl/libagl/egl.cpp
@@ -32,6 +32,7 @@
#include <utils/threads.h>
#include <ui/ANativeObjectBase.h>
#include <ui/Fence.h>
+#include <ui/GraphicBufferMapper.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
@@ -242,7 +243,6 @@
ANativeWindow* nativeWindow;
ANativeWindowBuffer* buffer;
ANativeWindowBuffer* previousBuffer;
- gralloc_module_t const* module;
int width;
int height;
void* bits;
@@ -341,16 +341,12 @@
EGLConfig config,
int32_t depthFormat,
ANativeWindow* window)
- : egl_surface_t(dpy, config, depthFormat),
- nativeWindow(window), buffer(0), previousBuffer(0), module(0),
- bits(NULL)
+ : egl_surface_t(dpy, config, depthFormat),
+ nativeWindow(window), buffer(0), previousBuffer(0), bits(NULL)
{
- hw_module_t const* pModule;
- hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &pModule);
- module = reinterpret_cast<gralloc_module_t const*>(pModule);
pixelFormatTable = gglGetPixelFormatTable();
-
+
// keep a reference on the window
nativeWindow->common.incRef(&nativeWindow->common);
nativeWindow->query(nativeWindow, NATIVE_WINDOW_WIDTH, &width);
@@ -440,22 +436,16 @@
status_t egl_window_surface_v2_t::lock(
ANativeWindowBuffer* buf, int usage, void** vaddr)
{
- int err;
-
- err = module->lock(module, buf->handle,
- usage, 0, 0, buf->width, buf->height, vaddr);
-
- return err;
+ auto& mapper = GraphicBufferMapper::get();
+ return mapper.lock(buf->handle, usage,
+ android::Rect(buf->width, buf->height), vaddr);
}
status_t egl_window_surface_v2_t::unlock(ANativeWindowBuffer* buf)
{
if (!buf) return BAD_VALUE;
- int err = NO_ERROR;
-
- err = module->unlock(module, buf->handle);
-
- return err;
+ auto& mapper = GraphicBufferMapper::get();
+ return mapper.unlock(buf->handle);
}
void egl_window_surface_v2_t::copyBlt(
diff --git a/opengl/libagl/light.cpp b/opengl/libagl/light.cpp
index 479bf7e..e7fe9d7 100644
--- a/opengl/libagl/light.cpp
+++ b/opengl/libagl/light.cpp
@@ -229,7 +229,7 @@
#endif
vnorm3(l.normalizedObjPosition.v, l.objPosition.v);
}
- const vec4_t eyeViewer = { 0, 0, 0x10000, 0 };
+ const vec4_t eyeViewer = {{{ 0, 0, 0x10000, 0 }}};
#if OBJECT_SPACE_LIGHTING
c->transforms.mvui.point3(&c->transforms.mvui,
&c->lighting.objViewer, &eyeViewer);
diff --git a/opengl/libagl/matrix.cpp b/opengl/libagl/matrix.cpp
index cdeccb3..034c857 100644
--- a/opengl/libagl/matrix.cpp
+++ b/opengl/libagl/matrix.cpp
@@ -253,13 +253,13 @@
{
GLfloat const* const m = lhs.m;
for (int i=0 ; i<4 ; i++) {
- register const float rhs_i0 = rhs.m[ I(i,0) ];
- register float ri0 = m[ I(0,0) ] * rhs_i0;
- register float ri1 = m[ I(0,1) ] * rhs_i0;
- register float ri2 = m[ I(0,2) ] * rhs_i0;
- register float ri3 = m[ I(0,3) ] * rhs_i0;
+ const float rhs_i0 = rhs.m[ I(i,0) ];
+ float ri0 = m[ I(0,0) ] * rhs_i0;
+ float ri1 = m[ I(0,1) ] * rhs_i0;
+ float ri2 = m[ I(0,2) ] * rhs_i0;
+ float ri3 = m[ I(0,3) ] * rhs_i0;
for (int j=1 ; j<4 ; j++) {
- register const float rhs_ij = rhs.m[ I(i,j) ];
+ const float rhs_ij = rhs.m[ I(i,j) ];
ri0 += m[ I(j,0) ] * rhs_ij;
ri1 += m[ I(j,1) ] * rhs_ij;
ri2 += m[ I(j,2) ] * rhs_ij;
diff --git a/opengl/libagl/texture.cpp b/opengl/libagl/texture.cpp
index 9aa1c4f..3fe5ed0 100644
--- a/opengl/libagl/texture.cpp
+++ b/opengl/libagl/texture.cpp
@@ -25,6 +25,9 @@
#include <ETC1/etc1.h>
+#include <ui/GraphicBufferMapper.h>
+#include <ui/Rect.h>
+
namespace android {
// ----------------------------------------------------------------------------
@@ -128,17 +131,11 @@
ANativeWindowBuffer* native_buffer = u.texture->buffer;
if (native_buffer) {
c->rasterizer.procs.activeTexture(c, i);
- hw_module_t const* pModule;
- if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &pModule))
- continue;
- gralloc_module_t const* module =
- reinterpret_cast<gralloc_module_t const*>(pModule);
-
+ auto& mapper = GraphicBufferMapper::get();
void* vaddr;
- int err = module->lock(module, native_buffer->handle,
- GRALLOC_USAGE_SW_READ_OFTEN,
- 0, 0, native_buffer->width, native_buffer->height,
+ mapper.lock(native_buffer->handle, GRALLOC_USAGE_SW_READ_OFTEN,
+ Rect(native_buffer->width, native_buffer->height),
&vaddr);
u.texture->setImageBits(vaddr);
@@ -156,14 +153,10 @@
ANativeWindowBuffer* native_buffer = u.texture->buffer;
if (native_buffer) {
c->rasterizer.procs.activeTexture(c, i);
- hw_module_t const* pModule;
- if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &pModule))
- continue;
- gralloc_module_t const* module =
- reinterpret_cast<gralloc_module_t const*>(pModule);
+ auto& mapper = GraphicBufferMapper::get();
+ mapper.unlock(native_buffer->handle);
- module->unlock(module, native_buffer->handle);
u.texture->setImageBits(NULL);
c->rasterizer.procs.bindTexture(c, &(u.texture->surface));
}
@@ -405,7 +398,7 @@
return 0;
}
-static size_t dataSizePalette4(int numLevels, int width, int height, int format)
+static GLsizei dataSizePalette4(int numLevels, int width, int height, int format)
{
int indexBits = 8;
int entrySize = 0;
diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp
index 374a5de..a2d689b 100644
--- a/services/inputflinger/InputReader.cpp
+++ b/services/inputflinger/InputReader.cpp
@@ -134,6 +134,10 @@
{ AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN },
{ AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT },
{ AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP },
+ { AKEYCODE_FP_NAV_DOWN, AKEYCODE_FP_NAV_RIGHT, AKEYCODE_FP_NAV_UP, AKEYCODE_FP_NAV_LEFT },
+ { AKEYCODE_FP_NAV_RIGHT, AKEYCODE_FP_NAV_UP, AKEYCODE_FP_NAV_LEFT, AKEYCODE_FP_NAV_DOWN },
+ { AKEYCODE_FP_NAV_UP, AKEYCODE_FP_NAV_LEFT, AKEYCODE_FP_NAV_DOWN, AKEYCODE_FP_NAV_RIGHT },
+ { AKEYCODE_FP_NAV_LEFT, AKEYCODE_FP_NAV_DOWN, AKEYCODE_FP_NAV_RIGHT, AKEYCODE_FP_NAV_UP },
};
static const size_t keyCodeRotationMapSize =
sizeof(keyCodeRotationMap) / sizeof(keyCodeRotationMap[0]);
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 0247723..d732c07 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -155,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);
@@ -1674,7 +1675,8 @@
return mQueuedFrames > 0 || mSidebandStreamChanged || mAutoRefresh;
}
-void Layer::onPostComposition() {
+bool Layer::onPostComposition() {
+ bool frameLatencyNeeded = mFrameLatencyNeeded;
if (mFrameLatencyNeeded) {
nsecs_t desiredPresentTime = mSurfaceFlingerConsumer->getTimestamp();
mFrameTracker.setDesiredPresentTime(desiredPresentTime);
@@ -1706,6 +1708,7 @@
mFrameTracker.advanceFrame();
mFrameLatencyNeeded = false;
}
+ return frameLatencyNeeded;
}
#ifdef USE_HWC2
@@ -2180,6 +2183,20 @@
*outAcquireFence = mSurfaceFlingerConsumer->getCurrentFence();
*outPrevReleaseFence = mSurfaceFlingerConsumer->getPrevReleaseFence();
}
+
+std::vector<OccupancyTracker::Segment> Layer::getOccupancyHistory(
+ bool forceFlush) {
+ std::vector<OccupancyTracker::Segment> history;
+ status_t result = mSurfaceFlingerConsumer->getOccupancyHistory(forceFlush,
+ &history);
+ if (result != NO_ERROR) {
+ ALOGW("[%s] Failed to obtain occupancy history (%d)", mName.string(),
+ result);
+ return {};
+ }
+ return history;
+}
+
// ---------------------------------------------------------------------------
Layer::LayerCleaner::LayerCleaner(const sp<SurfaceFlinger>& flinger,
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index ba7184f..4257c37 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -274,9 +274,10 @@
bool onPreComposition();
/*
- * called after composition.
+ * called after composition.
+ * returns true if the layer latched a new buffer this frame.
*/
- void onPostComposition();
+ bool onPostComposition();
#ifdef USE_HWC2
// If a buffer was replaced this frame, release the former buffer
@@ -406,6 +407,13 @@
bool* outIsGlesComposition, nsecs_t* outPostedTime,
sp<Fence>* outAcquireFence, sp<Fence>* outPrevReleaseFence) const;
+ 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;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index a10a813..9f357df 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");
@@ -1045,7 +1037,11 @@
const LayerVector& layers(mDrawingState.layersSortedByZ);
const size_t count = layers.size();
for (size_t i=0 ; i<count ; i++) {
- layers[i]->onPostComposition();
+ bool frameLatched = layers[i]->onPostComposition();
+ if (frameLatched) {
+ recordBufferingStats(layers[i]->getName().string(),
+ layers[i]->getOccupancyHistory(false));
+ }
}
sp<Fence> presentFence = mHwc->getRetireFence(HWC_DISPLAY_PRIMARY);
@@ -1065,10 +1061,8 @@
}
}
-#ifdef ENABLE_FENCE_TRACKING
mFenceTracker.addFrame(refreshStartTime, presentFence,
hw->getVisibleLayersSortedByZ(), hw->getClientTargetAcquireFence());
-#endif
if (mAnimCompositionPending) {
mAnimCompositionPending = false;
@@ -1646,6 +1640,8 @@
if (!mLayersPendingRemoval.isEmpty()) {
// Notify removed layers now that they can't be drawn from
for (size_t i = 0; i < mLayersPendingRemoval.size(); i++) {
+ recordBufferingStats(mLayersPendingRemoval[i]->getName().string(),
+ mLayersPendingRemoval[i]->getOccupancyHistory(true));
mLayersPendingRemoval[i]->onRemoved();
}
mLayersPendingRemoval.clear();
@@ -2607,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) {
@@ -2735,6 +2729,59 @@
NUM_BUCKETS - 1, bucketTimeSec, percent);
}
+void SurfaceFlinger::recordBufferingStats(const char* layerName,
+ std::vector<OccupancyTracker::Segment>&& history) {
+ Mutex::Autolock lock(mBufferingStatsMutex);
+ auto& stats = mBufferingStats[layerName];
+ for (const auto& segment : history) {
+ if (!segment.usedThirdBuffer) {
+ stats.twoBufferTime += segment.totalTime;
+ }
+ if (segment.occupancyAverage < 1.0f) {
+ stats.doubleBufferedTime += segment.totalTime;
+ } else if (segment.occupancyAverage < 2.0f) {
+ stats.tripleBufferedTime += segment.totalTime;
+ }
+ ++stats.numSegments;
+ stats.totalTime += segment.totalTime;
+ }
+}
+
+void SurfaceFlinger::dumpBufferingStats(String8& result) const {
+ result.append("Buffering stats:\n");
+ result.append(" [Layer name] <Active time> <Two buffer> "
+ "<Double buffered> <Triple buffered>\n");
+ Mutex::Autolock lock(mBufferingStatsMutex);
+ typedef std::tuple<std::string, float, float, float> BufferTuple;
+ std::map<float, BufferTuple, std::greater<float>> sorted;
+ for (const auto& statsPair : mBufferingStats) {
+ const char* name = statsPair.first.c_str();
+ const BufferingStats& stats = statsPair.second;
+ if (stats.numSegments == 0) {
+ continue;
+ }
+ float activeTime = ns2ms(stats.totalTime) / 1000.0f;
+ float twoBufferRatio = static_cast<float>(stats.twoBufferTime) /
+ stats.totalTime;
+ float doubleBufferRatio = static_cast<float>(
+ stats.doubleBufferedTime) / stats.totalTime;
+ float tripleBufferRatio = static_cast<float>(
+ stats.tripleBufferedTime) / stats.totalTime;
+ sorted.insert({activeTime, {name, twoBufferRatio,
+ doubleBufferRatio, tripleBufferRatio}});
+ }
+ for (const auto& sortedPair : sorted) {
+ float activeTime = sortedPair.first;
+ const BufferTuple& values = sortedPair.second;
+ result.appendFormat(" [%s] %.2f %.3f %.3f %.3f\n",
+ std::get<0>(values).c_str(), activeTime,
+ std::get<1>(values), std::get<2>(values),
+ std::get<3>(values));
+ }
+ result.append("\n");
+}
+
+
void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index,
String8& result) const
{
@@ -2788,6 +2835,8 @@
dumpStaticScreenStats(result);
result.append("\n");
+ dumpBufferingStats(result);
+
/*
* Dump the visible layer list
*/
@@ -3560,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 633e956..28666e2 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -42,6 +42,7 @@
#include <gui/ISurfaceComposer.h>
#include <gui/ISurfaceComposerClient.h>
+#include <gui/OccupancyTracker.h>
#include <hardware/hwcomposer_defs.h>
@@ -57,6 +58,9 @@
#include "DisplayHardware/HWComposer.h"
#include "Effects/Daltonizer.h"
+#include <map>
+#include <string>
+
namespace android {
// ---------------------------------------------------------------------------
@@ -432,6 +436,13 @@
void dumpStaticScreenStats(String8& result) const;
+ void recordBufferingStats(const char* layerName,
+ std::vector<OccupancyTracker::Segment>&& history);
+ void dumpBufferingStats(String8& result) const;
+
+ bool getFrameTimestamps(const Layer& layer, uint64_t frameNumber,
+ FrameTimestamps* outTimestamps);
+
/* ------------------------------------------------------------------------
* Attributes
*/
@@ -526,6 +537,29 @@
nsecs_t mFrameBuckets[NUM_BUCKETS];
nsecs_t mTotalTime;
std::atomic<nsecs_t> mLastSwapTime;
+
+ // Double- vs. triple-buffering stats
+ struct BufferingStats {
+ BufferingStats()
+ : numSegments(0),
+ totalTime(0),
+ twoBufferTime(0),
+ doubleBufferedTime(0),
+ tripleBufferedTime(0) {}
+
+ size_t numSegments;
+ nsecs_t totalTime;
+
+ // "Two buffer" means that a third buffer was never used, whereas
+ // "double-buffered" means that on average the segment only used two
+ // buffers (though it may have used a third for some part of the
+ // segment)
+ nsecs_t twoBufferTime;
+ nsecs_t doubleBufferedTime;
+ nsecs_t tripleBufferedTime;
+ };
+ mutable Mutex mBufferingStatsMutex;
+ std::unordered_map<std::string, BufferingStats> mBufferingStats;
};
}; // namespace android
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 7f3b269..a86c692 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,16 +1025,16 @@
}
}
-#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();
for (size_t i=0 ; i<count ; i++) {
- layers[i]->onPostComposition();
+ bool frameLatched = layers[i]->onPostComposition();
+ if (frameLatched) {
+ recordBufferingStats(layers[i]->getName().string(),
+ layers[i]->getOccupancyHistory(false));
+ }
}
const HWComposer& hwc = getHwComposer();
@@ -1059,10 +1055,8 @@
}
}
-#ifdef ENABLE_FENCE_TRACKING
mFenceTracker.addFrame(refreshStartTime, presentFence,
hw->getVisibleLayersSortedByZ(), hw->getClientTargetAcquireFence());
-#endif
if (mAnimCompositionPending) {
mAnimCompositionPending = false;
@@ -1666,6 +1660,8 @@
if (!mLayersPendingRemoval.isEmpty()) {
// Notify removed layers now that they can't be drawn from
for (size_t i = 0; i < mLayersPendingRemoval.size(); i++) {
+ recordBufferingStats(mLayersPendingRemoval[i]->getName().string(),
+ mLayersPendingRemoval[i]->getOccupancyHistory(true));
mLayersPendingRemoval[i]->onRemoved();
}
mLayersPendingRemoval.clear();
@@ -2623,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) {
@@ -2751,6 +2745,58 @@
NUM_BUCKETS - 1, bucketTimeSec, percent);
}
+void SurfaceFlinger::recordBufferingStats(const char* layerName,
+ std::vector<OccupancyTracker::Segment>&& history) {
+ Mutex::Autolock lock(mBufferingStatsMutex);
+ auto& stats = mBufferingStats[layerName];
+ for (const auto& segment : history) {
+ if (!segment.usedThirdBuffer) {
+ stats.twoBufferTime += segment.totalTime;
+ }
+ if (segment.occupancyAverage < 1.0f) {
+ stats.doubleBufferedTime += segment.totalTime;
+ } else if (segment.occupancyAverage < 2.0f) {
+ stats.tripleBufferedTime += segment.totalTime;
+ }
+ ++stats.numSegments;
+ stats.totalTime += segment.totalTime;
+ }
+}
+
+void SurfaceFlinger::dumpBufferingStats(String8& result) const {
+ result.append("Buffering stats:\n");
+ result.append(" [Layer name] <Active time> <Two buffer> "
+ "<Double buffered> <Triple buffered>\n");
+ Mutex::Autolock lock(mBufferingStatsMutex);
+ typedef std::tuple<std::string, float, float, float> BufferTuple;
+ std::map<float, BufferTuple, std::greater<float>> sorted;
+ for (const auto& statsPair : mBufferingStats) {
+ const char* name = statsPair.first.c_str();
+ const BufferingStats& stats = statsPair.second;
+ if (stats.numSegments == 0) {
+ continue;
+ }
+ float activeTime = ns2ms(stats.totalTime) / 1000.0f;
+ float twoBufferRatio = static_cast<float>(stats.twoBufferTime) /
+ stats.totalTime;
+ float doubleBufferRatio = static_cast<float>(
+ stats.doubleBufferedTime) / stats.totalTime;
+ float tripleBufferRatio = static_cast<float>(
+ stats.tripleBufferedTime) / stats.totalTime;
+ sorted.insert({activeTime, {name, twoBufferRatio,
+ doubleBufferRatio, tripleBufferRatio}});
+ }
+ for (const auto& sortedPair : sorted) {
+ float activeTime = sortedPair.first;
+ const BufferTuple& values = sortedPair.second;
+ result.appendFormat(" [%s] %.2f %.3f %.3f %.3f\n",
+ std::get<0>(values).c_str(), activeTime,
+ std::get<1>(values), std::get<2>(values),
+ std::get<3>(values));
+ }
+ result.append("\n");
+}
+
void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index,
String8& result) const
{
@@ -2802,6 +2848,8 @@
dumpStaticScreenStats(result);
result.append("\n");
+ dumpBufferingStats(result);
+
/*
* Dump the visible layer list
*/
@@ -3548,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/surfaceflinger.rc b/services/surfaceflinger/surfaceflinger.rc
index 2b4ea2a..435aa0c 100644
--- a/services/surfaceflinger/surfaceflinger.rc
+++ b/services/surfaceflinger/surfaceflinger.rc
@@ -3,4 +3,4 @@
user system
group graphics drmrpc readproc
onrestart restart zygote
- writepid /sys/fs/cgroup/stune/foreground/tasks
+ writepid /dev/stune/foreground/tasks