diff options
author | 2016-12-06 15:07:48 -0800 | |
---|---|---|
committer | 2016-12-06 15:07:48 -0800 | |
commit | ecea1bfeae1211f9a363925c172c0f23062107fb (patch) | |
tree | 99d7fc7c78f261a3ffde997e8354f8134c60bfbe | |
parent | f5c3b20f062ca646572ee6c07713eba691971c95 (diff) | |
parent | 6079aa6a8a845d8312435fe3e991bbe14588d018 (diff) |
Merge remote-tracking branch 'goog/stage-aosp-master' into HEAD
122 files changed, 3746 insertions, 669 deletions
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp index 25c2a300be..f4c6be2c2d 100644 --- a/cmds/atrace/atrace.cpp +++ b/cmds/atrace/atrace.cpp @@ -108,6 +108,7 @@ static const TracingCategory k_categories[] = { { "pm", "Package Manager", ATRACE_TAG_PACKAGE_MANAGER, { } }, { "ss", "System Server", ATRACE_TAG_SYSTEM_SERVER, { } }, { "database", "Database", ATRACE_TAG_DATABASE, { } }, + { "network", "Network", ATRACE_TAG_NETWORK, { } }, { k_coreServiceCategory, "Core services", 0, { } }, { "sched", "CPU Scheduling", 0, { { REQ, "/sys/kernel/debug/tracing/events/sched/sched_switch/enable" }, diff --git a/cmds/bugreportz/.clang-format b/cmds/bugreportz/.clang-format new file mode 100644 index 0000000000..fc4eb1bc03 --- /dev/null +++ b/cmds/bugreportz/.clang-format @@ -0,0 +1,13 @@ +BasedOnStyle: Google +AllowShortBlocksOnASingleLine: false +AllowShortFunctionsOnASingleLine: false + +AccessModifierOffset: -2 +ColumnLimit: 100 +CommentPragmas: NOLINT:.* +DerivePointerAlignment: false +IndentWidth: 4 +PointerAlignment: Left +TabWidth: 4 +UseTab: Never +PenaltyExcessCharacter: 32 diff --git a/cmds/bugreportz/Android.mk b/cmds/bugreportz/Android.mk index 14ba2259ec..880bc75bc3 100644 --- a/cmds/bugreportz/Android.mk +++ b/cmds/bugreportz/Android.mk @@ -1,12 +1,43 @@ LOCAL_PATH:= $(call my-dir) + +# bugreportz +# ========== + include $(CLEAR_VARS) -LOCAL_SRC_FILES:= bugreportz.cpp +LOCAL_SRC_FILES:= \ + bugreportz.cpp \ + main.cpp \ LOCAL_MODULE:= bugreportz -LOCAL_CFLAGS := -Wall +LOCAL_CFLAGS := -Werror -Wall -LOCAL_SHARED_LIBRARIES := libcutils +LOCAL_SHARED_LIBRARIES := \ + libbase \ + libcutils \ include $(BUILD_EXECUTABLE) + +# bugreportz_test +# =============== + +include $(CLEAR_VARS) + +LOCAL_MODULE := bugreportz_test +LOCAL_MODULE_TAGS := tests + +LOCAL_CFLAGS := -Werror -Wall + +LOCAL_SRC_FILES := \ + bugreportz.cpp \ + bugreportz_test.cpp \ + +LOCAL_STATIC_LIBRARIES := \ + libgmock \ + +LOCAL_SHARED_LIBRARIES := \ + libbase \ + libutils \ + +include $(BUILD_NATIVE_TEST) diff --git a/cmds/bugreportz/bugreportz.cpp b/cmds/bugreportz/bugreportz.cpp index 312dcebe0d..75855cfee1 100644 --- a/cmds/bugreportz/bugreportz.cpp +++ b/cmds/bugreportz/bugreportz.cpp @@ -15,89 +15,38 @@ */ #include <errno.h> -#include <getopt.h> #include <stdio.h> -#include <sys/socket.h> -#include <sys/types.h> +#include <stdlib.h> +#include <string.h> #include <unistd.h> -#include <cutils/properties.h> -#include <cutils/sockets.h> +#include <string> -static constexpr char VERSION[] = "1.0"; +#include <android-base/file.h> +#include <android-base/strings.h> -static void show_usage() { - fprintf(stderr, - "usage: bugreportz [-h | -v]\n" - " -h: to display this help message\n" - " -v: to display the version\n" - " or no arguments to generate a zipped bugreport\n"); -} - -static void show_version() { - fprintf(stderr, "%s\n", VERSION); -} - -int main(int argc, char *argv[]) { - - if (argc > 1) { - /* parse arguments */ - int c; - while ((c = getopt(argc, argv, "vh")) != -1) { - switch (c) { - case 'h': - show_usage(); - return EXIT_SUCCESS; - case 'v': - show_version(); - return EXIT_SUCCESS; - default: - show_usage(); - return EXIT_FAILURE; - } - } - // passed an argument not starting with - - if (optind > 1 || argv[optind] != nullptr) { - show_usage(); - return EXIT_FAILURE; - } - } - - // TODO: code below was copy-and-pasted from bugreport.cpp (except by the timeout value); - // should be reused instead. +#include "bugreportz.h" - // Start the dumpstatez service. - property_set("ctl.start", "dumpstatez"); +static constexpr char BEGIN_PREFIX[] = "BEGIN:"; +static constexpr char PROGRESS_PREFIX[] = "PROGRESS:"; - // Socket will not be available until service starts. - int s; - for (int i = 0; i < 20; i++) { - s = socket_local_client("dumpstate", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM); - if (s >= 0) - break; - // Try again in 1 second. - sleep(1); - } +static void write_line(const std::string& line, bool show_progress) { + if (line.empty()) return; - if (s == -1) { - printf("FAIL:Failed to connect to dumpstatez service: %s\n", strerror(errno)); - return EXIT_SUCCESS; - } + // When not invoked with the -p option, it must skip BEGIN and PROGRESS lines otherwise it + // will break adb (which is expecting either OK or FAIL). + if (!show_progress && (android::base::StartsWith(line, PROGRESS_PREFIX) || + android::base::StartsWith(line, BEGIN_PREFIX))) + return; - // Set a timeout so that if nothing is read in 10 minutes, we'll stop - // reading and quit. No timeout in dumpstate is longer than 60 seconds, - // so this gives lots of leeway in case of unforeseen time outs. - struct timeval tv; - tv.tv_sec = 10 * 60; - tv.tv_usec = 0; - if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) { - fprintf(stderr, "WARNING: Cannot set socket timeout: %s\n", strerror(errno)); - } + android::base::WriteStringToFd(line, STDOUT_FILENO); +} +int bugreportz(int s, bool show_progress) { + std::string line; while (1) { char buffer[65536]; - ssize_t bytes_read = TEMP_FAILURE_RETRY( - read(s, buffer, sizeof(buffer))); + ssize_t bytes_read = TEMP_FAILURE_RETRY(read(s, buffer, sizeof(buffer))); if (bytes_read == 0) { break; } else if (bytes_read == -1) { @@ -109,21 +58,18 @@ int main(int argc, char *argv[]) { break; } - ssize_t bytes_to_send = bytes_read; - ssize_t bytes_written; - do { - bytes_written = TEMP_FAILURE_RETRY( - write(STDOUT_FILENO, buffer + bytes_read - bytes_to_send, - bytes_to_send)); - if (bytes_written == -1) { - fprintf(stderr, - "Failed to write data to stdout: read %zd, trying to send %zd (%s)\n", - bytes_read, bytes_to_send, strerror(errno)); - break; + // Writes line by line. + for (int i = 0; i < bytes_read; i++) { + char c = buffer[i]; + line.append(1, c); + if (c == '\n') { + write_line(line, show_progress); + line.clear(); } - bytes_to_send -= bytes_written; - } while (bytes_written != 0 && bytes_to_send > 0); + } } + // Process final line, in case it didn't finish with newline + write_line(line, show_progress); if (close(s) == -1) { fprintf(stderr, "WARNING: error closing socket: %s\n", strerror(errno)); diff --git a/cmds/bugreportz/bugreportz.h b/cmds/bugreportz/bugreportz.h new file mode 100644 index 0000000000..304e4b3dc3 --- /dev/null +++ b/cmds/bugreportz/bugreportz.h @@ -0,0 +1,21 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// 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 BUGREPORTZ_H +#define BUGREPORTZ_H + +// Calls dumpstate using the given socket and output its result to stdout. +int bugreportz(int s, bool show_progress); + +#endif // BUGREPORTZ_H diff --git a/cmds/bugreportz/bugreportz_test.cpp b/cmds/bugreportz/bugreportz_test.cpp new file mode 100644 index 0000000000..58b23d1992 --- /dev/null +++ b/cmds/bugreportz/bugreportz_test.cpp @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <stdio.h> +#include <sys/types.h> +#include <unistd.h> + +#include <string> + +#include "bugreportz.h" + +using ::testing::StrEq; +using ::testing::internal::CaptureStdout; +using ::testing::internal::GetCapturedStdout; + +class BugreportzTest : public ::testing::Test { + public: + // Creates the pipe used to communicate with bugreportz() + void SetUp() { + int fds[2]; + ASSERT_EQ(0, pipe(fds)); + read_fd_ = fds[0]; + write_fd_ = fds[1]; + } + + // Closes the pipe FDs. + // If a FD is closed manually during a test, set it to -1 to prevent TearDown() trying to close + // it again. + void TearDown() { + for (int fd : {read_fd_, write_fd_}) { + if (fd >= 0) { + close(fd); + } + } + } + + // Emulates dumpstate output by writing to the socket passed to bugreportz() + void WriteToSocket(const std::string& data) { + if (write_fd_ < 0) { + ADD_FAILURE() << "cannot write '" << data << "' because socket is already closed"; + return; + } + int expected = data.length(); + int actual = write(write_fd_, data.data(), data.length()); + ASSERT_EQ(expected, actual) << "wrong number of bytes written to socket"; + } + + void AssertStdoutEquals(const std::string& expected) { + ASSERT_THAT(stdout_, StrEq(expected)) << "wrong stdout output"; + } + + // Calls bugreportz() using the internal pipe. + // + // Tests must call WriteToSocket() to set what's written prior to calling it, since the writing + // end of the pipe will be closed before calling bugreportz() (otherwise that function would + // hang). + void Bugreportz(bool show_progress) { + close(write_fd_); + write_fd_ = -1; + + CaptureStdout(); + int status = bugreportz(read_fd_, show_progress); + + close(read_fd_); + read_fd_ = -1; + stdout_ = GetCapturedStdout(); + + ASSERT_EQ(0, status) << "bugrepotz() call failed (stdout: " << stdout_ << ")"; + } + + private: + int read_fd_; + int write_fd_; + std::string stdout_; +}; + +// Tests 'bugreportz', without any argument - it will ignore progress lines. +TEST_F(BugreportzTest, NoArgument) { + WriteToSocket("BEGIN:THE IGNORED PATH WARS HAS!\n"); // Should be ommited. + WriteToSocket("What happens on 'dumpstate',"); + WriteToSocket("stays on 'bugreportz'.\n"); + WriteToSocket("PROGRESS:Y U NO OMITTED?\n"); // Should be ommited. + WriteToSocket("But "); + WriteToSocket("PROGRESS IN THE MIDDLE"); // Ok - not starting a line. + WriteToSocket(" is accepted\n"); + + Bugreportz(false); + + AssertStdoutEquals( + "What happens on 'dumpstate',stays on 'bugreportz'.\n" + "But PROGRESS IN THE MIDDLE is accepted\n"); +} + +// Tests 'bugreportz -p' - it will just echo dumpstate's output to stdout +TEST_F(BugreportzTest, WithProgress) { + WriteToSocket("BEGIN:I AM YOUR PATH\n"); + WriteToSocket("What happens on 'dumpstate',"); + WriteToSocket("stays on 'bugreportz'.\n"); + WriteToSocket("PROGRESS:IS INEVITABLE\n"); + WriteToSocket("PROG"); + WriteToSocket("RESS:IS NOT AUTOMATIC\n"); + WriteToSocket("Newline is optional"); + + Bugreportz(true); + + AssertStdoutEquals( + "BEGIN:I AM YOUR PATH\n" + "What happens on 'dumpstate',stays on 'bugreportz'.\n" + "PROGRESS:IS INEVITABLE\n" + "PROGRESS:IS NOT AUTOMATIC\n" + "Newline is optional"); +} diff --git a/cmds/bugreportz/main.cpp b/cmds/bugreportz/main.cpp new file mode 100644 index 0000000000..a3ae1ffa4d --- /dev/null +++ b/cmds/bugreportz/main.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <errno.h> +#include <getopt.h> +#include <stdio.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <unistd.h> + +#include <cutils/properties.h> +#include <cutils/sockets.h> + +#include "bugreportz.h" + +static constexpr char VERSION[] = "1.1"; + +static void show_usage() { + fprintf(stderr, + "usage: bugreportz [-h | -v]\n" + " -h: to display this help message\n" + " -p: display progress\n" + " -v: to display the version\n" + " or no arguments to generate a zipped bugreport\n"); +} + +static void show_version() { + fprintf(stderr, "%s\n", VERSION); +} + +int main(int argc, char* argv[]) { + bool show_progress = false; + if (argc > 1) { + /* parse arguments */ + int c; + while ((c = getopt(argc, argv, "hpv")) != -1) { + switch (c) { + case 'h': + show_usage(); + return EXIT_SUCCESS; + case 'p': + show_progress = true; + break; + case 'v': + show_version(); + return EXIT_SUCCESS; + default: + show_usage(); + return EXIT_FAILURE; + } + } + } + + // TODO: code below was copy-and-pasted from bugreport.cpp (except by the + // timeout value); + // should be reused instead. + + // Start the dumpstatez service. + property_set("ctl.start", "dumpstatez"); + + // Socket will not be available until service starts. + int s; + for (int i = 0; i < 20; i++) { + s = socket_local_client("dumpstate", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM); + if (s >= 0) break; + // Try again in 1 second. + sleep(1); + } + + if (s == -1) { + printf("FAIL:Failed to connect to dumpstatez service: %s\n", strerror(errno)); + return EXIT_SUCCESS; + } + + // Set a timeout so that if nothing is read in 10 minutes, we'll stop + // reading and quit. No timeout in dumpstate is longer than 60 seconds, + // so this gives lots of leeway in case of unforeseen time outs. + struct timeval tv; + tv.tv_sec = 10 * 60; + tv.tv_usec = 0; + if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) { + fprintf(stderr, "WARNING: Cannot set socket timeout: %s\n", strerror(errno)); + } + + bugreportz(s, show_progress); +} diff --git a/cmds/bugreportz/readme.md b/cmds/bugreportz/readme.md index 85aafcefe4..2697f09847 100644 --- a/cmds/bugreportz/readme.md +++ b/cmds/bugreportz/readme.md @@ -3,6 +3,13 @@ `bugreportz` is used to generate a zippped bugreport whose path is passed back to `adb`, using the simple protocol defined below. +# Version 1.1 +On version 1.1, in addition to the `OK` and `FAILURE` lines, when `bugreportz` is invoked with +`-p`, it outputs the following lines: + +- `BEGIN:<path_to_bugreport_file>` right away. +- `PROGRESS:<progress>/<total>` as `dumpstate` progresses (where `<progress>` is the current +progress units out of a max of `<total>`). ## Version 1.0 On version 1.0, `bugreportz` does not generate any output on `stdout` until the bugreport is diff --git a/cmds/dumpstate/Android.mk b/cmds/dumpstate/Android.mk index 0433f31eb2..e478651de0 100644 --- a/cmds/dumpstate/Android.mk +++ b/cmds/dumpstate/Android.mk @@ -10,7 +10,7 @@ LOCAL_SRC_FILES := dumpstate.cpp utils.cpp LOCAL_MODULE := dumpstate -LOCAL_SHARED_LIBRARIES := libcutils liblog libselinux libbase +LOCAL_SHARED_LIBRARIES := libcutils liblog libselinux libbase libhardware_legacy # ZipArchive support, the order matters here to get all symbols. LOCAL_STATIC_LIBRARIES := libziparchive libz libcrypto_static LOCAL_HAL_STATIC_LIBRARIES := libdumpstate diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index ad06e30673..5b01be1274 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -23,6 +23,7 @@ #include <memory> #include <regex> #include <set> +#include <signal.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> @@ -37,7 +38,9 @@ #include <android-base/stringprintf.h> #include <android-base/unique_fd.h> +#include <android-base/file.h> #include <cutils/properties.h> +#include <hardware_legacy/power.h> #include <private/android_filesystem_config.h> #include <private/android_logger.h> @@ -53,14 +56,14 @@ using android::base::StringPrintf; 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; static std::unique_ptr<ZipWriter> zip_writer; static std::set<std::string> mount_points; void add_mountinfo(); -static int control_socket_fd; +int control_socket_fd = -1; /* suffix of the bugreport files - it's typically the date (when invoked with -d), * although it could be changed by the user using a system property */ static std::string suffix; @@ -68,16 +71,19 @@ static std::string suffix; #define PSTORE_LAST_KMSG "/sys/fs/pstore/console-ramoops" #define ALT_PSTORE_LAST_KMSG "/sys/fs/pstore/console-ramoops-0" -#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" +#define PROFILE_DATA_DIR_CUR "/data/misc/profiles/cur" +#define PROFILE_DATA_DIR_REF "/data/misc/profiles/ref" #define TOMBSTONE_DIR "/data/tombstones" #define TOMBSTONE_FILE_PREFIX TOMBSTONE_DIR "/tombstone_" /* Can accomodate a tombstone number up to 9999. */ #define TOMBSTONE_MAX_LEN (sizeof(TOMBSTONE_FILE_PREFIX) + 4) #define NUM_TOMBSTONES 10 #define WLUTIL "/vendor/xbin/wlutil" +#define WAKE_LOCK_NAME "dumpstate_wakelock" typedef struct { char name[TOMBSTONE_MAX_LEN]; @@ -176,6 +182,136 @@ static void dump_dev_files(const char *title, const char *driverpath, const char closedir(d); } +// return pid of a userspace process. If not found or error, return 0. +static unsigned int pid_of_process(const char* ps_name) { + DIR *proc_dir; + struct dirent *ps; + unsigned int pid; + std::string cmdline; + + if (!(proc_dir = opendir("/proc"))) { + MYLOGE("Can't open /proc\n"); + return 0; + } + + while ((ps = readdir(proc_dir))) { + if (!(pid = atoi(ps->d_name))) { + continue; + } + android::base::ReadFileToString("/proc/" + + std::string(ps->d_name) + "/cmdline", &cmdline); + if (cmdline.find(ps_name) == std::string::npos) { + continue; + } else { + closedir(proc_dir); + return pid; + } + } + closedir(proc_dir); + return 0; +} + +// dump anrd's trace and add to the zip file. +// 1. check if anrd is running on this device. +// 2. send a SIGUSR1 to its pid which will dump anrd's trace. +// 3. wait until the trace generation completes and add to the zip file. +static bool dump_anrd_trace() { + unsigned int pid; + char buf[50], path[PATH_MAX]; + struct dirent *trace; + struct stat st; + DIR *trace_dir; + int retry = 5; + long max_ctime = 0, old_mtime; + long long cur_size = 0; + const char *trace_path = "/data/misc/anrd/"; + + if (!zip_writer) { + MYLOGE("Not dumping anrd trace because zip_writer is not set\n"); + return false; + } + + // find anrd's pid if it is running. + pid = pid_of_process("/system/xbin/anrd"); + + if (pid > 0) { + if (stat(trace_path, &st) == 0) { + old_mtime = st.st_mtime; + } else { + MYLOGE("Failed to find: %s\n", trace_path); + return false; + } + + // send SIGUSR1 to the anrd to generate a trace. + sprintf(buf, "%u", pid); + if (run_command("ANRD_DUMP", 1, "kill", "-SIGUSR1", buf, NULL)) { + MYLOGE("anrd signal timed out. Please manually collect trace\n"); + return false; + } + + while (retry-- > 0 && old_mtime == st.st_mtime) { + sleep(1); + stat(trace_path, &st); + } + + if (retry < 0 && old_mtime == st.st_mtime) { + MYLOGE("Failed to stat %s or trace creation timeout\n", trace_path); + return false; + } + + // identify the trace file by its creation time. + if (!(trace_dir = opendir(trace_path))) { + MYLOGE("Can't open trace file under %s\n", trace_path); + } + while ((trace = readdir(trace_dir))) { + if (strcmp(trace->d_name, ".") == 0 + || strcmp(trace->d_name, "..") == 0) { + continue; + } + sprintf(path, "%s%s", trace_path, trace->d_name); + if (stat(path, &st) == 0) { + if (st.st_ctime > max_ctime) { + max_ctime = st.st_ctime; + sprintf(buf, "%s", trace->d_name); + } + } + } + closedir(trace_dir); + + // Wait until the dump completes by checking the size of the trace. + if (max_ctime > 0) { + sprintf(path, "%s%s", trace_path, buf); + while(true) { + sleep(1); + if (stat(path, &st) == 0) { + if (st.st_size == cur_size) { + break; + } else if (st.st_size > cur_size) { + cur_size = st.st_size; + } else { + return false; + } + } else { + MYLOGE("Cant stat() %s anymore\n", path); + return false; + } + } + // Add to the zip file. + if (!add_zip_entry("anrd_trace.txt", path)) { + MYLOGE("Unable to add anrd_trace file %s to zip file\n", path); + } else { + if (remove(path)) { + MYLOGE("Error removing anrd_trace file %s: %s", path, strerror(errno)); + } + return true; + } + } else { + MYLOGE("Can't stats any trace file under %s\n", trace_path); + } + } + return false; +} + static void dump_systrace() { if (!zip_writer) { MYLOGD("Not dumping systrace because zip_writer is not set\n"); @@ -217,6 +353,40 @@ static void dump_systrace() { } } +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); @@ -417,7 +587,7 @@ static void print_header(std::string version) { 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)); @@ -441,18 +611,39 @@ static void print_header(std::string version) { printf("\n"); } +// List of file extensions that can cause a zip file attachment to be rejected by some email +// service providers. +static const std::set<std::string> PROBLEMATIC_FILE_EXTENSIONS = { + ".ade", ".adp", ".bat", ".chm", ".cmd", ".com", ".cpl", ".exe", ".hta", ".ins", ".isp", + ".jar", ".jse", ".lib", ".lnk", ".mde", ".msc", ".msp", ".mst", ".pif", ".scr", ".sct", + ".shb", ".sys", ".vb", ".vbe", ".vbs", ".vxd", ".wsc", ".wsf", ".wsh" +}; + bool add_zip_entry_from_fd(const std::string& entry_name, int fd) { if (!zip_writer) { MYLOGD("Not adding zip entry %s from fd because zip_writer is not set\n", entry_name.c_str()); return false; } + std::string valid_name = entry_name; + + // Rename extension if necessary. + size_t idx = entry_name.rfind("."); + if (idx != std::string::npos) { + std::string extension = entry_name.substr(idx); + std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower); + if (PROBLEMATIC_FILE_EXTENSIONS.count(extension) != 0) { + valid_name = entry_name + ".renamed"; + MYLOGI("Renaming entry %s to %s\n", entry_name.c_str(), valid_name.c_str()); + } + } + // Logging statement below is useful to time how long each entry takes, but it's too verbose. // MYLOGD("Adding zip entry %s\n", entry_name.c_str()); - int32_t err = zip_writer->StartEntryWithTime(entry_name.c_str(), + int32_t err = zip_writer->StartEntryWithTime(valid_name.c_str(), ZipWriter::kCompress, get_mtime(fd, now)); if (err) { - MYLOGE("zip_writer->StartEntryWithTime(%s): %s\n", entry_name.c_str(), + MYLOGE("zip_writer->StartEntryWithTime(%s): %s\n", valid_name.c_str(), ZipWriter::ErrorCodeString(err)); return false; } @@ -498,6 +689,7 @@ static int _add_file_from_fd(const char *title, const char *path, int fd) { return add_zip_entry_from_fd(ZIP_ROOT_DIR + path, fd) ? 0 : 1; } +// TODO: move to util.cpp void add_dir(const char *dir, bool recursive) { if (!zip_writer) { MYLOGD("Not adding dir %s because zip_writer is not set\n", dir); @@ -541,10 +733,12 @@ static bool add_text_zip_entry(const std::string& entry_name, const std::string& static void dump_iptables() { run_command("IPTABLES", 10, "iptables", "-L", "-nvx", NULL); run_command("IP6TABLES", 10, "ip6tables", "-L", "-nvx", NULL); - run_command("IPTABLE NAT", 10, "iptables", "-t", "nat", "-L", "-nvx", NULL); + run_command("IPTABLES NAT", 10, "iptables", "-t", "nat", "-L", "-nvx", NULL); /* no ip6 nat */ - run_command("IPTABLE RAW", 10, "iptables", "-t", "raw", "-L", "-nvx", NULL); - run_command("IP6TABLE RAW", 10, "ip6tables", "-t", "raw", "-L", "-nvx", NULL); + run_command("IPTABLES MANGLE", 10, "iptables", "-t", "mangle", "-L", "-nvx", NULL); + run_command("IP6TABLES MANGLE", 10, "ip6tables", "-t", "mangle", "-L", "-nvx", NULL); + run_command("IPTABLES RAW", 10, "iptables", "-t", "raw", "-L", "-nvx", NULL); + run_command("IP6TABLES RAW", 10, "ip6tables", "-t", "raw", "-L", "-nvx", NULL); } static void dumpstate(const std::string& screenshot_path, const std::string& version) { @@ -626,8 +820,6 @@ static void dumpstate(const std::string& screenshot_path, const std::string& ver 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); @@ -841,7 +1033,7 @@ static void dumpstate(const std::string& screenshot_path, const std::string& ver printf("== Running Application Providers\n"); printf("========================================================\n"); - run_command("APP SERVICES", 30, "dumpsys", "-t", "30", "activity", "provider", "all", NULL); + run_command("APP PROVIDERS", 30, "dumpsys", "-t", "30", "activity", "provider", "all", NULL); printf("========================================================\n"); @@ -875,11 +1067,31 @@ static void usage() { VERSION_DEFAULT.c_str()); } -static void sigpipe_handler(int n) { - // don't complain to stderr or stdout +static void wake_lock_releaser() { + if (release_wake_lock(WAKE_LOCK_NAME) < 0) { + MYLOGE("Failed to release wake lock: %s \n", strerror(errno)); + } else { + MYLOGD("Wake lock released.\n"); + } +} + +static void sig_handler(int signo) { + wake_lock_releaser(); _exit(EXIT_FAILURE); } +static void register_sig_handler() { + struct sigaction sa; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = sig_handler; + sigaction(SIGPIPE, &sa, NULL); // broken pipe + sigaction(SIGSEGV, &sa, NULL); // segment fault + sigaction(SIGINT, &sa, NULL); // ctrl-c + sigaction(SIGTERM, &sa, NULL); // killed + sigaction(SIGQUIT, &sa, NULL); // quit +} + /* adds the temporary report to the existing .zip file, closes the .zip file, and removes the temporary file. */ @@ -948,7 +1160,6 @@ static std::string SHA256_file_hash(std::string filepath) { } int main(int argc, char *argv[]) { - struct sigaction sigact; int do_add_date = 0; int do_zip_file = 0; int do_vibrate = 1; @@ -965,6 +1176,14 @@ int main(int argc, char *argv[]) { MYLOGI("begin\n"); + if (acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME) < 0) { + MYLOGE("Failed to acquire wake lock: %s \n", strerror(errno)); + } else { + MYLOGD("Wake lock acquired.\n"); + atexit(wake_lock_releaser); + register_sig_handler(); + } + /* gets the sequential id */ char last_id[PROPERTY_VALUE_MAX]; property_get("dumpstate.last_id", last_id, "0"); @@ -973,17 +1192,20 @@ int main(int argc, char *argv[]) { property_set("dumpstate.last_id", last_id); MYLOGI("dumpstate id: %lu\n", id); - /* clear SIGPIPE handler */ - memset(&sigact, 0, sizeof(sigact)); - sigact.sa_handler = sigpipe_handler; - sigaction(SIGPIPE, &sigact, NULL); - /* set as high priority, and protect from OOM killer */ setpriority(PRIO_PROCESS, 0, -20); - FILE *oom_adj = fopen("/proc/self/oom_adj", "we"); + + FILE *oom_adj = fopen("/proc/self/oom_score_adj", "we"); if (oom_adj) { - fputs("-17", oom_adj); + fputs("-1000", oom_adj); fclose(oom_adj); + } else { + /* fallback to kernels <= 2.6.35 */ + oom_adj = fopen("/proc/self/oom_adj", "we"); + if (oom_adj) { + fputs("-17", oom_adj); + fclose(oom_adj); + } } /* parse arguments */ @@ -1050,6 +1272,7 @@ int main(int argc, char *argv[]) { if (use_control_socket) { MYLOGD("Opening control socket\n"); control_socket_fd = open_socket("dumpstate"); + do_update_progress = 1; } /* full path of the temporary file containing the bugreport */ @@ -1123,14 +1346,21 @@ int main(int argc, char *argv[]) { } if (do_update_progress) { - std::vector<std::string> am_args = { - "--receiver-permission", "android.permission.DUMP", "--receiver-foreground", - "--es", "android.intent.extra.NAME", suffix, - "--ei", "android.intent.extra.ID", std::to_string(id), - "--ei", "android.intent.extra.PID", std::to_string(getpid()), - "--ei", "android.intent.extra.MAX", std::to_string(WEIGHT_TOTAL), - }; - send_broadcast("android.intent.action.BUGREPORT_STARTED", am_args); + if (do_broadcast) { + // clang-format off + std::vector<std::string> am_args = { + "--receiver-permission", "android.permission.DUMP", "--receiver-foreground", + "--es", "android.intent.extra.NAME", suffix, + "--ei", "android.intent.extra.ID", std::to_string(id), + "--ei", "android.intent.extra.PID", std::to_string(getpid()), + "--ei", "android.intent.extra.MAX", std::to_string(WEIGHT_TOTAL), + }; + // clang-format on + send_broadcast("android.intent.action.BUGREPORT_STARTED", am_args); + } + if (use_control_socket) { + dprintf(control_socket_fd, "BEGIN:%s\n", path.c_str()); + } } } @@ -1192,7 +1422,14 @@ int main(int argc, char *argv[]) { print_header(version); // Dumps systrace right away, otherwise it will be filled with unnecessary events. - dump_systrace(); + // First try to dump anrd trace if the daemon is running. Otherwise, dump + // the raw trace. + if (!dump_anrd_trace()) { + 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. @@ -1207,9 +1444,19 @@ int main(int argc, char *argv[]) { add_dir(RECOVERY_DIR, true); add_dir(RECOVERY_DATA_DIR, true); add_dir(LOGPERSIST_DATA_DIR, false); + if (!is_user_build()) { + add_dir(PROFILE_DATA_DIR_CUR, true); + add_dir(PROFILE_DATA_DIR_REF, true); + } add_mountinfo(); dump_iptables(); + // Capture any IPSec policies in play. No keys are exposed here. + run_command("IP XFRM POLICY", 10, "ip", "xfrm", "policy", nullptr); + + // Run ss as root so we can see socket marks. + run_command("DETAILED SOCKET STATE", 10, "ss", "-eionptu", NULL); + if (!drop_root_user()) { return -1; } @@ -1306,6 +1553,7 @@ int main(int argc, char *argv[]) { if (do_broadcast) { if (!path.empty()) { MYLOGI("Final bugreport path: %s\n", path.c_str()); + // clang-format off std::vector<std::string> am_args = { "--receiver-permission", "android.permission.DUMP", "--receiver-foreground", "--ei", "android.intent.extra.ID", std::to_string(id), @@ -1314,6 +1562,7 @@ int main(int argc, char *argv[]) { "--es", "android.intent.extra.BUGREPORT", path, "--es", "android.intent.extra.DUMPSTATE_LOG", log_path }; + // clang-format on if (do_fb) { am_args.push_back("--es"); am_args.push_back("android.intent.extra.SCREENSHOT"); @@ -1339,9 +1588,9 @@ int main(int argc, char *argv[]) { fclose(stderr); } - if (use_control_socket && control_socket_fd >= 0) { - MYLOGD("Closing control socket\n"); - close(control_socket_fd); + if (use_control_socket && control_socket_fd != -1) { + MYLOGD("Closing control socket\n"); + close(control_socket_fd); } return 0; diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h index 4dde125c5a..905fc2276a 100644 --- a/cmds/dumpstate/dumpstate.h +++ b/cmds/dumpstate/dumpstate.h @@ -79,7 +79,7 @@ static const int WEIGHT_FILE = 5; * It would be better to take advantage of the C++ migration and encapsulate the state in an object, * but that will be better handled in a major C++ refactoring, which would also get rid of other C * idioms (like using std::string instead of char*, removing varargs, etc...) */ -extern int do_update_progress, progress, weight_total; +extern int do_update_progress, progress, weight_total, control_socket_fd; /* full path of the directory where the bugreport files will be written */ extern std::string bugreport_dir; diff --git a/cmds/dumpstate/dumpstate.rc b/cmds/dumpstate/dumpstate.rc index 1f56d210cc..3448e91088 100644 --- a/cmds/dumpstate/dumpstate.rc +++ b/cmds/dumpstate/dumpstate.rc @@ -38,3 +38,11 @@ service bugreportremote /system/bin/dumpstate -d -q -B -R -z \ class main disabled oneshot + +# bugreportwear is a wearable version of bugreport that displays progress and takes early +# screenshot. +service bugreportwear /system/bin/dumpstate -d -B -P -p -z \ + -o /data/user_de/0/com.android.shell/files/bugreports/bugreport + class main + disabled + oneshot diff --git a/cmds/dumpstate/utils.cpp b/cmds/dumpstate/utils.cpp index 5527b24161..0d9cce28bb 100644 --- a/cmds/dumpstate/utils.cpp +++ b/cmds/dumpstate/utils.cpp @@ -828,7 +828,7 @@ bool drop_root_user() { } gid_t groups[] = { AID_LOG, AID_SDCARD_R, AID_SDCARD_RW, - AID_MOUNT, AID_INET, AID_NET_BW_STATS, AID_READPROC, + AID_MOUNT, AID_INET, AID_NET_BW_STATS, AID_READPROC, AID_WAKELOCK, AID_BLUETOOTH }; if (setgroups(sizeof(groups)/sizeof(groups[0]), groups) != 0) { MYLOGE("Unable to setgroups, aborting: %s\n", strerror(errno)); @@ -850,8 +850,10 @@ bool drop_root_user() { capheader.version = _LINUX_CAPABILITY_VERSION_3; capheader.pid = 0; - capdata[CAP_TO_INDEX(CAP_SYSLOG)].permitted = CAP_TO_MASK(CAP_SYSLOG); - capdata[CAP_TO_INDEX(CAP_SYSLOG)].effective = CAP_TO_MASK(CAP_SYSLOG); + capdata[CAP_TO_INDEX(CAP_SYSLOG)].permitted = + (CAP_TO_MASK(CAP_SYSLOG) | CAP_TO_MASK(CAP_BLOCK_SUSPEND)); + capdata[CAP_TO_INDEX(CAP_SYSLOG)].effective = + (CAP_TO_MASK(CAP_SYSLOG) | CAP_TO_MASK(CAP_BLOCK_SUSPEND)); capdata[0].inheritable = 0; capdata[1].inheritable = 0; @@ -1215,6 +1217,11 @@ void update_progress(int delta) { fprintf(stderr, "Setting progress (%s): %s/%d\n", key, value, weight_total); } + if (control_socket_fd >= 0) { + dprintf(control_socket_fd, "PROGRESS:%d/%d\n", progress, weight_total); + fsync(control_socket_fd); + } + int status = property_set(key, value); if (status) { MYLOGE("Could not update progress by setting system property %s to %s: %d\n", diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp index 0601c87158..772d17f6ae 100644 --- a/cmds/dumpsys/dumpsys.cpp +++ b/cmds/dumpsys/dumpsys.cpp @@ -10,6 +10,7 @@ #include <thread> #include <android-base/file.h> +#include <android-base/stringprintf.h> #include <android-base/unique_fd.h> #include <binder/IServiceManager.h> #include <binder/Parcel.h> @@ -30,6 +31,7 @@ #include <unistd.h> using namespace android; +using android::base::StringPrintf; using android::base::unique_fd; using android::base::WriteFully; @@ -210,7 +212,8 @@ int main(int argc, char* const argv[]) }); auto timeout = std::chrono::seconds(timeoutArg); - auto end = std::chrono::steady_clock::now() + timeout; + auto start = std::chrono::steady_clock::now(); + auto end = start + timeout; struct pollfd pfd = { .fd = local_end.get(), @@ -267,6 +270,13 @@ int main(int argc, char* const argv[]) } else { dump_thread.join(); } + + if (N > 1) { + std::chrono::duration<double> elapsed_seconds = + std::chrono::steady_clock::now() - start; + aout << StringPrintf("--------- %.3fs ", elapsed_seconds.count()).c_str() + << "was the duration of dumpsys " << service_name << endl; + } } else { aerr << "Can't find service: " << service_name << endl; } diff --git a/cmds/installd/Android.mk b/cmds/installd/Android.mk index c2d0df45f2..4ddbc8b9d7 100644 --- a/cmds/installd/Android.mk +++ b/cmds/installd/Android.mk @@ -39,6 +39,17 @@ LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk LOCAL_CLANG := true include $(BUILD_EXECUTABLE) +# OTA slot script + +include $(CLEAR_VARS) +LOCAL_MODULE:= otapreopt_slot +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := EXECUTABLES +LOCAL_SRC_FILES := otapreopt_slot.sh +LOCAL_INIT_RC := otapreopt.rc + +include $(BUILD_PREBUILT) + # OTA postinstall script include $(CLEAR_VARS) diff --git a/cmds/installd/commands.cpp b/cmds/installd/commands.cpp index ae52728b1a..bbc5160bc5 100644 --- a/cmds/installd/commands.cpp +++ b/cmds/installd/commands.cpp @@ -2418,6 +2418,7 @@ int move_ab(const char* apk_path, const char* instruction_set, const char* oat_d if (!a_image_path.empty()) { if (!move_ab_path(b_image_path, a_image_path)) { + unlink(a_image_path.c_str()); if (!kIgnoreAppImageFailure) { success = false; } diff --git a/data/etc/wearable_core_hardware.xml b/data/etc/wearable_core_hardware.xml index 4b7a706e47..84230da12e 100644 --- a/data/etc/wearable_core_hardware.xml +++ b/data/etc/wearable_core_hardware.xml @@ -24,6 +24,7 @@ <feature name="android.hardware.location" /> <!-- devices supporting compass/magnitometer sensor must include android.hardware.sensor.compass.xml --> + <feature name="android.hardware.sensor.gyroscope" /> <feature name="android.hardware.sensor.accelerometer" /> <feature name="android.hardware.bluetooth" /> <feature name="android.hardware.touchscreen" /> @@ -34,7 +35,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/docs/Makefile b/docs/Makefile index 5104d81c20..c655e0c09b 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -1,13 +1,12 @@ HEADERS := $(wildcard ../include/android/*.h) -all: html jd +all: html website html: $(HEADERS) Doxyfile mkdir -p html doxygen -jd: $(HEADERS) Doxyfile header.jd - mkdir -p jd - HTML_HEADER=header.jd HTML_FOOTER=footer.jd HTML_OUTPUT=jd doxygen - for file in jd/*.html; do mv "$${file}" "$${file/.html/.jd}"; done - rm -f jd/index.jd +website: $(HEADERS) Doxyfile header.html + mkdir -p website + HTML_HEADER=header.html HTML_FOOTER=footer.html HTML_OUTPUT=website doxygen + rm -f website/index.html diff --git a/docs/footer.html b/docs/footer.html new file mode 100644 index 0000000000..308b1d01b6 --- /dev/null +++ b/docs/footer.html @@ -0,0 +1,2 @@ +</body> +</html> diff --git a/docs/footer.jd b/docs/footer.jd deleted file mode 100644 index e69de29bb2..0000000000 --- a/docs/footer.jd +++ /dev/null diff --git a/docs/header.html b/docs/header.html new file mode 100644 index 0000000000..04727b3dab --- /dev/null +++ b/docs/header.html @@ -0,0 +1,10 @@ +<html devsite> +<head> + <meta name="top_category" value="ndk" /> + <meta name="subcategory" value="reference" /> + <meta name="book_path" value="/ndk/reference/_book.yaml" /> + <title>$title</title> + <link rel="stylesheet" type="text/css" href="doxygen-dac.css"> +</head> +<body> +<div id="top"><!-- we must have this tag, it's closed by doxygen. ¯\_(ツ)_/¯ --> diff --git a/docs/header.jd b/docs/header.jd deleted file mode 100644 index e50f41b03b..0000000000 --- a/docs/header.jd +++ /dev/null @@ -1,3 +0,0 @@ -page.title=$title -page.customHeadTag=<link rel="stylesheet" type="text/css" href="doxygen-dac.css"> -@jd:body diff --git a/include/android/keycodes.h b/include/android/keycodes.h index 67e28da815..e202060f16 100644 --- a/include/android/keycodes.h +++ b/include/android/keycodes.h @@ -757,7 +757,15 @@ enum { /** Copy key. */ AKEYCODE_COPY = 278, /** Paste key. */ - AKEYCODE_PASTE = 279 + AKEYCODE_PASTE = 279, + /** fingerprint navigation key, up. */ + AKEYCODE_SYSTEM_NAVIGATION_UP = 280, + /** fingerprint navigation key, down. */ + AKEYCODE_SYSTEM_NAVIGATION_DOWN = 281, + /** fingerprint navigation key, left. */ + AKEYCODE_SYSTEM_NAVIGATION_LEFT = 282, + /** fingerprint navigation key, right. */ + AKEYCODE_SYSTEM_NAVIGATION_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 a23ed57266..266f0aa501 100644 --- a/include/gui/BufferQueue.h +++ b/include/gui/BufferQueue.h @@ -66,6 +66,8 @@ public: 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 c655f2af70..e2bafec4a9 100644 --- a/include/gui/BufferQueueConsumer.h +++ b/include/gui/BufferQueueConsumer.h @@ -136,6 +136,13 @@ public: // 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; + + // See IGraphicBufferConsumer::discardFreeBuffers + virtual status_t discardFreeBuffers() override; + // dump our state in a String virtual void dumpState(String8& result, const char* prefix) const; diff --git a/include/gui/BufferQueueCore.h b/include/gui/BufferQueueCore.h index 4f1e09c728..b1c730a587 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> @@ -124,6 +125,11 @@ private: // all slots, even if they're currently dequeued, queued, or acquired. void freeAllBuffersLocked(); + // discardFreeBuffersLocked releases all currently-free buffers held by the + // queue, in order to reduce the memory consumption of the queue to the + // minimum possible without discarding data. + void discardFreeBuffersLocked(); + // If delta is positive, makes more slots available. If negative, takes // away slots. Returns false if the request can't be met. bool adjustAvailableSlotsLocked(int delta); @@ -176,9 +182,15 @@ private: // to this BufferQueue. It defaults to NO_CONNECTED_API, and gets updated // by the connect and disconnect methods. int mConnectedApi; + // PID of the process which last successfully called connect(...) + pid_t mConnectedPid; - // mConnectedProducerToken is used to set a binder death notification on + // mLinkedToDeath is used to set a binder death notification on // the producer. + sp<IProducerListener> mLinkedToDeath; + + // mConnectedProducerListener is used to handle the onBufferReleased + // notification. sp<IProducerListener> mConnectedProducerListener; // mSlots is an array of buffer slots that must be mirrored on the producer @@ -322,6 +334,10 @@ private: // The slot of the last queued buffer int mLastQueuedSlot; + OccupancyTracker mOccupancyTracker; + + const uint64_t mUniqueId; + }; // class BufferQueueCore } // namespace android diff --git a/include/gui/BufferQueueProducer.h b/include/gui/BufferQueueProducer.h index a48f45e0d9..79e7af2117 100644 --- a/include/gui/BufferQueueProducer.h +++ b/include/gui/BufferQueueProducer.h @@ -135,15 +135,8 @@ public: virtual status_t connect(const sp<IProducerListener>& listener, int api, bool producerControlledByApp, QueueBufferOutput* output); - // disconnect attempts to disconnect a producer API from the BufferQueue. - // Calling this method will cause any subsequent calls to other - // IGraphicBufferProducer methods to fail except for getAllocator and connect. - // Successfully calling connect after this will allow the other methods to - // succeed again. - // - // This method will fail if the the BufferQueue is not currently - // connected to the specified producer API. - virtual status_t disconnect(int api); + // See IGraphicBufferProducer::disconnect + virtual status_t disconnect(int api, DisconnectMode mode = DisconnectMode::Api); // Attaches a sideband buffer stream to the IGraphicBufferProducer. // @@ -170,9 +163,6 @@ public: // See IGraphicBufferProducer::getConsumerName virtual String8 getConsumerName() const override; - // See IGraphicBufferProducer::getNextFrameNumber - virtual uint64_t getNextFrameNumber() const override; - // See IGraphicBufferProducer::setSharedBufferMode virtual status_t setSharedBufferMode(bool sharedBufferMode) override; @@ -186,6 +176,13 @@ public: 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; + + // See IGraphicBufferProducer::getUniqueId + virtual status_t getUniqueId(uint64_t* outId) 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 19fdcc3f61..9f8b638a3a 100644 --- a/include/gui/ConsumerBase.h +++ b/include/gui/ConsumerBase.h @@ -85,6 +85,13 @@ public: // See IGraphicBufferConsumer::setDefaultBufferDataSpace status_t setDefaultBufferDataSpace(android_dataspace defaultDataSpace); + // See IGraphicBufferConsumer::getOccupancyHistory + status_t getOccupancyHistory(bool forceFlush, + std::vector<OccupancyTracker::Segment>* outHistory); + + // See IGraphicBufferConsumer::discardFreeBuffers + status_t discardFreeBuffers(); + 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 0000000000..4dc7467029 --- /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 aef076cbd2..460a03d91a 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 @@ public: // 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 210fbc8115..dcddca46e8 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,17 @@ public: // 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; + + // discardFreeBuffers releases all currently-free buffers held by the queue, + // in order to reduce the memory consumption of the queue to the minimum + // possible without discarding data. + virtual status_t discardFreeBuffers() = 0; + // dump state into a string virtual void dumpState(String8& result, const char* prefix) const = 0; diff --git a/include/gui/IGraphicBufferProducer.h b/include/gui/IGraphicBufferProducer.h index d74f1f652e..c2dba50361 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 { // ---------------------------------------------------------------------------- @@ -360,24 +362,29 @@ public: inline void deflate(uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransformHint, - uint32_t* outNumPendingBuffers) const { + uint32_t* outNumPendingBuffers, + uint64_t* outNextFrameNumber) const { *outWidth = width; *outHeight = height; *outTransformHint = transformHint; *outNumPendingBuffers = numPendingBuffers; + *outNextFrameNumber = nextFrameNumber; } inline void inflate(uint32_t inWidth, uint32_t inHeight, - uint32_t inTransformHint, uint32_t inNumPendingBuffers) { + uint32_t inTransformHint, uint32_t inNumPendingBuffers, + uint64_t inNextFrameNumber) { width = inWidth; height = inHeight; transformHint = inTransformHint; numPendingBuffers = inNumPendingBuffers; + nextFrameNumber = inNextFrameNumber; } private: uint32_t width; uint32_t height; uint32_t transformHint; uint32_t numPendingBuffers; + uint64_t nextFrameNumber{0}; }; virtual status_t queueBuffer(int slot, const QueueBufferInput& input, @@ -452,17 +459,24 @@ public: virtual status_t connect(const sp<IProducerListener>& listener, int api, bool producerControlledByApp, QueueBufferOutput* output) = 0; + enum class DisconnectMode { + // Disconnect only the specified API. + Api, + // Disconnect any API originally connected from the process calling disconnect. + AllLocal + }; + // disconnect attempts to disconnect a client API from the // IGraphicBufferProducer. Calling this method will cause any subsequent // calls to other IGraphicBufferProducer methods to fail except for // getAllocator and connect. Successfully calling connect after this will // allow the other methods to succeed again. // - // This method will fail if the the IGraphicBufferProducer is not currently - // connected to the specified client API. - // // The api should be one of the NATIVE_WINDOW_API_* values in <window.h> // + // Alternatively if mode is AllLocal, then the API value is ignored, and any API + // connected from the same PID calling disconnect will be disconnected. + // // Disconnecting from an abandoned IGraphicBufferProducer is legal and // is considered a no-op. // @@ -471,7 +485,7 @@ public: // * the api specified does not match the one that was connected // * api was out of range (see above). // * DEAD_OBJECT - the token is hosted by an already-dead process - virtual status_t disconnect(int api) = 0; + virtual status_t disconnect(int api, DisconnectMode mode = DisconnectMode::Api) = 0; // Attaches a sideband buffer stream to the IGraphicBufferProducer. // @@ -522,9 +536,6 @@ public: // Returns the name of the connected consumer. virtual String8 getConsumerName() const = 0; - // Returns the number of the next frame which will be dequeued. - virtual uint64_t getNextFrameNumber() const = 0; - // Used to enable/disable shared buffer mode. // // When shared buffer mode is enabled the first buffer that is queued or @@ -569,6 +580,17 @@ public: // 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; } + + // Returns a unique id for this BufferQueue + virtual status_t getUniqueId(uint64_t* outId) const = 0; }; // ---------------------------------------------------------------------------- diff --git a/include/gui/IProducerListener.h b/include/gui/IProducerListener.h index 4a5ed46891..e808bd3bc3 100644 --- a/include/gui/IProducerListener.h +++ b/include/gui/IProducerListener.h @@ -41,6 +41,7 @@ public: // This is called without any lock held and can be called concurrently by // multiple threads. virtual void onBufferReleased() = 0; // Asynchronous + virtual bool needsReleaseNotify() = 0; }; class IProducerListener : public ProducerListener, public IInterface @@ -54,6 +55,7 @@ class BnProducerListener : public BnInterface<IProducerListener> public: virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); + virtual bool needsReleaseNotify(); }; class DummyProducerListener : public BnProducerListener @@ -61,6 +63,7 @@ class DummyProducerListener : public BnProducerListener public: virtual ~DummyProducerListener(); virtual void onBufferReleased() {} + virtual bool needsReleaseNotify() { return false; } }; } // namespace android diff --git a/include/gui/ISurfaceComposer.h b/include/gui/ISurfaceComposer.h index dbaa5fe90d..555a0cc6ab 100644 --- a/include/gui/ISurfaceComposer.h +++ b/include/gui/ISurfaceComposer.h @@ -137,6 +137,12 @@ public: * should be used */ virtual status_t setActiveConfig(const sp<IBinder>& display, int id) = 0; + virtual status_t getDisplayColorModes(const sp<IBinder>& display, + Vector<android_color_mode_t>* outColorModes) = 0; + virtual android_color_mode_t getActiveColorMode(const sp<IBinder>& display) = 0; + virtual status_t setActiveColorMode(const sp<IBinder>& display, + android_color_mode_t colorMode) = 0; + /* Capture the specified screen. requires READ_FRAME_BUFFER permission * This function will fail if there is a secure window on screen. */ @@ -193,6 +199,9 @@ public: SET_POWER_MODE, GET_DISPLAY_STATS, GET_HDR_CAPABILITIES, + GET_DISPLAY_COLOR_MODES, + GET_ACTIVE_COLOR_MODE, + SET_ACTIVE_COLOR_MODE, }; virtual status_t onTransact(uint32_t code, const Parcel& data, diff --git a/include/gui/ISurfaceComposerClient.h b/include/gui/ISurfaceComposerClient.h index 48bd088168..4a4efb631a 100644 --- a/include/gui/ISurfaceComposerClient.h +++ b/include/gui/ISurfaceComposerClient.h @@ -77,6 +77,9 @@ public: * Requires ACCESS_SURFACE_FLINGER permission */ virtual status_t getLayerFrameStats(const sp<IBinder>& handle, FrameStats* outStats) const = 0; + + virtual status_t getTransformToDisplayInverse(const sp<IBinder>& handle, + bool* outTransformToDisplayInverse) const = 0; }; // ---------------------------------------------------------------------------- diff --git a/include/gui/OccupancyTracker.h b/include/gui/OccupancyTracker.h new file mode 100644 index 0000000000..d4de8f2b14 --- /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 592391f7fb..489d5ea7bf 100644 --- a/include/gui/Surface.h +++ b/include/gui/Surface.h @@ -134,6 +134,14 @@ public: 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); + + status_t getUniqueId(uint64_t* outId) const; + protected: virtual ~Surface(); @@ -183,19 +191,18 @@ private: 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); virtual int cancelBuffer(ANativeWindowBuffer* buffer, int fenceFd); virtual int queueBuffer(ANativeWindowBuffer* buffer, int fenceFd); virtual int perform(int operation, va_list args); - virtual int query(int what, int* value) const; virtual int setSwapInterval(int interval); virtual int lockBuffer_DEPRECATED(ANativeWindowBuffer* buffer); virtual int connect(int api); - virtual int disconnect(int api); virtual int setBufferCount(int bufferCount); virtual int setBuffersDimensions(uint32_t width, uint32_t height); virtual int setBuffersUserDimensions(uint32_t width, uint32_t height); @@ -209,12 +216,17 @@ protected: virtual void setSurfaceDamage(android_native_rect_t* rects, size_t numRects); public: + virtual int disconnect(int api, + IGraphicBufferProducer::DisconnectMode mode = + IGraphicBufferProducer::DisconnectMode::Api); + virtual int setMaxDequeuedBufferCount(int maxDequeuedBuffers); virtual int setAsyncMode(bool async); virtual int setSharedBufferMode(bool sharedBufferMode); virtual int setAutoRefresh(bool autoRefresh); virtual int lock(ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds); virtual int unlockAndPost(); + virtual int query(int what, int* value) const; virtual int connect(int api, const sp<IProducerListener>& listener); virtual int detachNextBuffer(sp<GraphicBuffer>* outBuffer, @@ -361,7 +373,13 @@ private: // used to prevent a mismatch between the number of queue/dequeue calls. bool mSharedBufferHasBeenQueued; + // These are used to satisfy the NATIVE_WINDOW_LAST_*_DURATION queries + nsecs_t mLastDequeueDuration = 0; + nsecs_t mLastQueueDuration = 0; + Condition mQueueBufferCondition; + + uint64_t mNextFrameNumber; }; namespace view { diff --git a/include/gui/SurfaceComposerClient.h b/include/gui/SurfaceComposerClient.h index 6c621ee8eb..f2932f2c08 100644 --- a/include/gui/SurfaceComposerClient.h +++ b/include/gui/SurfaceComposerClient.h @@ -83,6 +83,16 @@ public: // returned by getDisplayInfo static status_t setActiveConfig(const sp<IBinder>& display, int id); + // Gets the list of supported color modes for the given display + static status_t getDisplayColorModes(const sp<IBinder>& display, + Vector<android_color_mode_t>* outColorModes); + + // Gets the active color mode for the given display + static android_color_mode_t getActiveColorMode(const sp<IBinder>& display); + + // Sets the active color mode for the given display + static status_t setActiveColorMode(const sp<IBinder>& display, android_color_mode_t colorMode); + /* Triggers screen on/off or low power mode and waits for it to complete */ static void setDisplayPowerMode(const sp<IBinder>& display, int mode); @@ -140,21 +150,24 @@ public: const sp<IBinder>& handle, uint64_t frameNumber); status_t setOverrideScalingMode(const sp<IBinder>& id, int32_t overrideScalingMode); - status_t setPositionAppliesWithResize(const sp<IBinder>& id); + status_t setGeometryAppliesWithResize(const sp<IBinder>& id); status_t destroySurface(const sp<IBinder>& id); status_t clearLayerFrameStats(const sp<IBinder>& token) const; status_t getLayerFrameStats(const sp<IBinder>& token, FrameStats* outStats) const; + status_t getTransformToDisplayInverse(const sp<IBinder>& token, + bool* outTransformToDisplayInverse) const; + static status_t clearAnimationFrameStats(); static status_t getAnimationFrameStats(FrameStats* outStats); static status_t getHdrCapabilities(const sp<IBinder>& display, HdrCapabilities* outCapabilities); - static void setDisplaySurface(const sp<IBinder>& token, - const sp<IGraphicBufferProducer>& bufferProducer); + static status_t setDisplaySurface(const sp<IBinder>& token, + sp<IGraphicBufferProducer> bufferProducer); static void setDisplayLayerStack(const sp<IBinder>& token, uint32_t layerStack); static void setDisplaySize(const sp<IBinder>& token, uint32_t width, uint32_t height); diff --git a/include/gui/SurfaceControl.h b/include/gui/SurfaceControl.h index fafd1948fe..5e731c3964 100644 --- a/include/gui/SurfaceControl.h +++ b/include/gui/SurfaceControl.h @@ -73,10 +73,11 @@ public: status_t setCrop(const Rect& crop); status_t setFinalCrop(const Rect& crop); - // If the size changes in this transaction, position updates specified + // If the size changes in this transaction, all geometry updates specified // in this transaction will not complete until a buffer of the new size - // arrives. - status_t setPositionAppliesWithResize(); + // arrives. As some elements normally apply immediately, this enables + // freezing the total geometry of a surface until a resize is completed. + status_t setGeometryAppliesWithResize(); // Defers applying any changes made in this transaction until the Layer // identified by handle reaches the given frameNumber @@ -96,6 +97,8 @@ public: status_t clearLayerFrameStats() const; status_t getLayerFrameStats(FrameStats* outStats) const; + status_t getTransformToDisplayInverse(bool* outTransformToDisplayInverse) const; + private: // can't be copied SurfaceControl& operator = (SurfaceControl& rhs); diff --git a/include/input/InputEventLabels.h b/include/input/InputEventLabels.h index b59728ac39..20154eb10e 100644 --- a/include/input/InputEventLabels.h +++ b/include/input/InputEventLabels.h @@ -319,6 +319,10 @@ static const InputEventLabel KEYCODES[] = { DEFINE_KEYCODE(CUT), DEFINE_KEYCODE(COPY), DEFINE_KEYCODE(PASTE), + DEFINE_KEYCODE(SYSTEM_NAVIGATION_UP), + DEFINE_KEYCODE(SYSTEM_NAVIGATION_DOWN), + DEFINE_KEYCODE(SYSTEM_NAVIGATION_LEFT), + DEFINE_KEYCODE(SYSTEM_NAVIGATION_RIGHT), { NULL, 0 } }; diff --git a/include/media/openmax/OMX_AsString.h b/include/media/openmax/OMX_AsString.h index 03801cafe1..7ae07ad71e 100644 --- a/include/media/openmax/OMX_AsString.h +++ b/include/media/openmax/OMX_AsString.h @@ -543,6 +543,8 @@ inline static const char *asString(OMX_INDEXEXTTYPE i, const char *def = "??") { case OMX_IndexParamVideoHevc: return "ParamVideoHevc"; // case OMX_IndexParamSliceSegments: return "ParamSliceSegments"; case OMX_IndexConfigAndroidIntraRefresh: return "ConfigAndroidIntraRefresh"; + case OMX_IndexParamAndroidVideoTemporalLayering: return "ParamAndroidVideoTemporalLayering"; + case OMX_IndexConfigAndroidVideoTemporalLayering: return "ConfigAndroidVideoTemporalLayering"; case OMX_IndexConfigAutoFramerateConversion: return "ConfigAutoFramerateConversion"; case OMX_IndexConfigPriority: return "ConfigPriority"; case OMX_IndexConfigOperatingRate: return "ConfigOperatingRate"; @@ -973,8 +975,8 @@ inline static const char *asString(OMX_VIDEO_VP9LEVELTYPE i, const char *def = " inline static const char *asString( OMX_VIDEO_ANDROID_VPXTEMPORALLAYERPATTERNTYPE i, const char *def = "??") { switch (i) { - case OMX_VIDEO_VPXTemporalLayerPatternNone: return "VPXTemporalLayerPatternNone"; - case OMX_VIDEO_VPXTemporalLayerPatternWebRTC: return "VPXTemporalLayerPatternWebRTC"; + case OMX_VIDEO_VPXTemporalLayerPatternNone: return "None"; + case OMX_VIDEO_VPXTemporalLayerPatternWebRTC: return "WebRTC"; default: return def; } } @@ -1022,6 +1024,16 @@ inline static const char *asString(OMX_VIDEO_HEVCLEVELTYPE i, const char *def = } } +inline static const char *asString( + OMX_VIDEO_ANDROID_TEMPORALLAYERINGPATTERNTYPE i, const char *def = "??") { + switch (i) { + case OMX_VIDEO_AndroidTemporalLayeringPatternNone: return "None"; + case OMX_VIDEO_AndroidTemporalLayeringPatternWebRTC: return "WebRTC"; + case OMX_VIDEO_AndroidTemporalLayeringPatternAndroid: return "Android"; + default: return def; + } +} + #endif // AS_STRING_FOR_OMX_VIDEOEXT_H #endif // OMX_VideoExt_h diff --git a/include/media/openmax/OMX_Core.h b/include/media/openmax/OMX_Core.h index eeacf43569..bb974b3f88 100644 --- a/include/media/openmax/OMX_Core.h +++ b/include/media/openmax/OMX_Core.h @@ -516,6 +516,9 @@ typedef enum OMX_EVENTTYPE * but must signal the event no more than 40ms after the first frame in the batch. The frames * must be ordered by system timestamp inside and across batches. * + * The component shall signal the render-timestamp of the very first frame (as well as the + * first frame after each flush) unbatched (with nData1 set to 1) within 5 msec. + * * If component is doing frame-rate conversion, it must signal the render time of each * converted frame, and must interpolate media timestamps for in-between frames. * @@ -753,15 +756,21 @@ typedef struct OMX_TUNNELSETUPTYPE When the command is "OMX_CommandStateSet" the component will queue a state transition to the new state idenfied in nParam. + The component shall transition from executing to loaded state within 500 msec. + When the command is "OMX_CommandFlush", to flush a port's buffer queues, the command will force the component to return all buffers NOT CURRENTLY BEING PROCESSED to the application, in the order in which the buffers were received. + The component shall finish flusing each port within 5 msec. + When the command is "OMX_CommandPortDisable" or "OMX_CommandPortEnable", the component's port (given by the value of nParam) will be stopped or restarted. + The component shall finish disabling/reenabling each port within 5 msec. + When the command "OMX_CommandMarkBuffer" is used to mark a buffer, the pCmdData will point to a OMX_MARKTYPE structure containing the component handle of the component to examine the buffer chain for the mark. nParam1 diff --git a/include/media/openmax/OMX_IndexExt.h b/include/media/openmax/OMX_IndexExt.h index 1724576ad5..b688d1d949 100644 --- a/include/media/openmax/OMX_IndexExt.h +++ b/include/media/openmax/OMX_IndexExt.h @@ -78,6 +78,8 @@ typedef enum OMX_INDEXEXTTYPE { OMX_IndexParamVideoHevc, /**< reference: OMX_VIDEO_PARAM_HEVCTYPE */ OMX_IndexParamSliceSegments, /**< reference: OMX_VIDEO_SLICESEGMENTSTYPE */ OMX_IndexConfigAndroidIntraRefresh, /**< reference: OMX_VIDEO_CONFIG_ANDROID_INTRAREFRESHTYPE */ + OMX_IndexParamAndroidVideoTemporalLayering, /**< reference: OMX_VIDEO_PARAM_ANDROID_TEMPORALLAYERINGTYPE */ + OMX_IndexConfigAndroidVideoTemporalLayering, /**< reference: OMX_VIDEO_CONFIG_ANDROID_TEMPORALLAYERINGTYPE */ /* Image & Video common configurations */ OMX_IndexExtCommonStartUnused = OMX_IndexKhronosExtensions + 0x00700000, diff --git a/include/media/openmax/OMX_VideoExt.h b/include/media/openmax/OMX_VideoExt.h index bf15ee4a5e..2c02431730 100644 --- a/include/media/openmax/OMX_VideoExt.h +++ b/include/media/openmax/OMX_VideoExt.h @@ -170,7 +170,11 @@ typedef struct OMX_VIDEO_PARAM_ANDROID_VP8ENCODERTYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; - OMX_U32 nKeyFrameInterval; + OMX_U32 nKeyFrameInterval; // distance between consecutive key_frames (including one + // of the key_frames). 0 means interval is unspecified and + // can be freely chosen by the codec. 1 means a stream of + // only key_frames. + OMX_VIDEO_ANDROID_VPXTEMPORALLAYERPATTERNTYPE eTemporalPattern; OMX_U32 nTemporalLayerCount; OMX_U32 nTemporalLayerBitrateRatio[OMX_VIDEO_ANDROID_MAXVP8TEMPORALLAYERS]; @@ -227,7 +231,10 @@ typedef struct OMX_VIDEO_PARAM_HEVCTYPE { OMX_U32 nPortIndex; OMX_VIDEO_HEVCPROFILETYPE eProfile; OMX_VIDEO_HEVCLEVELTYPE eLevel; - OMX_U32 nKeyFrameInterval; + OMX_U32 nKeyFrameInterval; // distance between consecutive I-frames (including one + // of the I frames). 0 means interval is unspecified and + // can be freely chosen by the codec. 1 means a stream of + // only I frames. } OMX_VIDEO_PARAM_HEVCTYPE; /** Structure to define if dependent slice segments should be used */ @@ -289,7 +296,7 @@ typedef enum OMX_VIDEO_DOLBYVISIONLEVELTYPE { * nVersion : OMX specification version information * nPortIndex : Port that this structure applies to * nRefreshPeriod : Intra refreh period in frames. Value 0 means disable intra refresh -*/ + */ typedef struct OMX_VIDEO_CONFIG_ANDROID_INTRAREFRESHTYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; @@ -297,6 +304,95 @@ typedef struct OMX_VIDEO_CONFIG_ANDROID_INTRAREFRESHTYPE { OMX_U32 nRefreshPeriod; } OMX_VIDEO_CONFIG_ANDROID_INTRAREFRESHTYPE; +/** Maximum number of temporal layers supported by AVC/HEVC */ +#define OMX_VIDEO_ANDROID_MAXTEMPORALLAYERS 8 + +/** temporal layer patterns */ +typedef enum OMX_VIDEO_ANDROID_TEMPORALLAYERINGPATTERNTYPE { + OMX_VIDEO_AndroidTemporalLayeringPatternNone = 0, + // pattern as defined by WebRTC + OMX_VIDEO_AndroidTemporalLayeringPatternWebRTC = 1 << 0, + // pattern where frames in any layer other than the base layer only depend on at most the very + // last frame from each preceding layer (other than the base layer.) + OMX_VIDEO_AndroidTemporalLayeringPatternAndroid = 1 << 1, +} OMX_VIDEO_ANDROID_TEMPORALLAYERINGPATTERNTYPE; + +/** + * Android specific param for configuration of temporal layering. + * Android only supports temporal layering where successive layers each double the + * previous layer's framerate. + * NOTE: Reading this parameter at run-time SHALL return actual run-time values. + * + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to (output port for encoders) + * eSupportedPatterns : A bitmask of supported layering patterns + * nLayerCountMax : Max number of temporal coding layers supported + * by the encoder (must be at least 1, 1 meaning temporal layering + * is NOT supported) + * nBLayerCountMax : Max number of layers that can contain B frames + * (0) to (nLayerCountMax - 1) + * ePattern : Layering pattern. + * nPLayerCountActual : Number of temporal layers to be coded with non-B frames, + * starting from and including the base-layer. + * (1 to nLayerCountMax - nBLayerCountActual) + * If nPLayerCountActual is 1 and nBLayerCountActual is 0, temporal + * layering is disabled. Otherwise, it is enabled. + * nBLayerCountActual : Number of temporal layers to be coded with B frames, + * starting after non-B layers. + * (0 to nBLayerCountMax) + * bBitrateRatiosSpecified : Flag to indicate if layer-wise bitrate + * distribution is specified. + * nBitrateRatios : Bitrate ratio (100 based) per layer (index 0 is base layer). + * Honored if bBitrateRatiosSpecified is set. + * i.e for 4 layers with desired distribution (25% 25% 25% 25%), + * nBitrateRatio = {25, 50, 75, 100, ... } + * Values in indices not less than 'the actual number of layers + * minus 1' MAY be ignored and assumed to be 100. + */ +typedef struct OMX_VIDEO_PARAM_ANDROID_TEMPORALLAYERINGTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_VIDEO_ANDROID_TEMPORALLAYERINGPATTERNTYPE eSupportedPatterns; + OMX_U32 nLayerCountMax; + OMX_U32 nBLayerCountMax; + OMX_VIDEO_ANDROID_TEMPORALLAYERINGPATTERNTYPE ePattern; + OMX_U32 nPLayerCountActual; + OMX_U32 nBLayerCountActual; + OMX_BOOL bBitrateRatiosSpecified; + OMX_U32 nBitrateRatios[OMX_VIDEO_ANDROID_MAXTEMPORALLAYERS]; +} OMX_VIDEO_PARAM_ANDROID_TEMPORALLAYERINGTYPE; + +/** + * Android specific config for changing the temporal-layer count or + * bitrate-distribution at run-time. + * + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to (output port for encoders) + * ePattern : Layering pattern. + * nPLayerCountActual : Number of temporal layers to be coded with non-B frames. + * (same OMX_VIDEO_PARAM_ANDROID_TEMPORALLAYERINGTYPE limits apply.) + * nBLayerCountActual : Number of temporal layers to be coded with B frames. + * (same OMX_VIDEO_PARAM_ANDROID_TEMPORALLAYERINGTYPE limits apply.) + * bBitrateRatiosSpecified : Flag to indicate if layer-wise bitrate + * distribution is specified. + * nBitrateRatios : Bitrate ratio (100 based, Q16 values) per layer (0 is base layer). + * Honored if bBitrateRatiosSpecified is set. + * (same OMX_VIDEO_PARAM_ANDROID_TEMPORALLAYERINGTYPE limits apply.) + */ +typedef struct OMX_VIDEO_CONFIG_ANDROID_TEMPORALLAYERINGTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_VIDEO_ANDROID_TEMPORALLAYERINGPATTERNTYPE ePattern; + OMX_U32 nPLayerCountActual; + OMX_U32 nBLayerCountActual; + OMX_BOOL bBitrateRatiosSpecified; + OMX_U32 nBitrateRatios[OMX_VIDEO_ANDROID_MAXTEMPORALLAYERS]; +} OMX_VIDEO_CONFIG_ANDROID_TEMPORALLAYERINGTYPE; + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/include/private/gui/LayerState.h b/include/private/gui/LayerState.h index 4885e05d04..4b3fcc6e2b 100644 --- a/include/private/gui/LayerState.h +++ b/include/private/gui/LayerState.h @@ -55,7 +55,7 @@ struct layer_state_t { eDeferTransaction = 0x00000200, eFinalCropChanged = 0x00000400, eOverrideScalingModeChanged = 0x00000800, - ePositionAppliesWithResize = 0x00001000, + eGeometryAppliesWithResize = 0x00001000, }; layer_state_t() diff --git a/include/ui/DisplayInfo.h b/include/ui/DisplayInfo.h index ad73ee72f9..799944f3ee 100644 --- a/include/ui/DisplayInfo.h +++ b/include/ui/DisplayInfo.h @@ -36,7 +36,6 @@ struct DisplayInfo { bool secure; nsecs_t appVsyncOffset; nsecs_t presentationDeadline; - int colorTransform; }; /* Display orientations as defined in Surface.java and ISurfaceComposer.h. */ diff --git a/include/ui/Fence.h b/include/ui/Fence.h index a4c1df72bc..2fbc9efb45 100644 --- a/include/ui/Fence.h +++ b/include/ui/Fence.h @@ -79,6 +79,9 @@ public: // becomes signaled when both f1 and f2 are signaled (even if f1 or f2 is // destroyed before it becomes signaled). The name argument specifies the // human-readable name to associated with the new Fence object. + static sp<Fence> merge(const char* name, const sp<Fence>& f1, + const sp<Fence>& f2); + static sp<Fence> merge(const String8& name, const sp<Fence>& f1, const sp<Fence>& f2); @@ -93,6 +96,17 @@ public: // occurs then -1 is returned. nsecs_t getSignalTime() const; + // hasSignaled returns whether the fence has signaled yet. Prefer this to + // getSignalTime() or wait() if all you care about is whether the fence has + // signaled. + inline bool hasSignaled() { + // The sync_wait call underlying wait() has been measured to be + // significantly faster than the sync_fence_info call underlying + // getSignalTime(), which might otherwise appear to be the more obvious + // way to check whether a fence has signaled. + return wait(0) == NO_ERROR; + } + // Flattenable interface size_t getFlattenedSize() const; size_t getFdCount() const; diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index b558ec9d87..8e8bb802b3 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -83,6 +83,7 @@ cc_library_shared { "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 ccbb5a25f3..6de98f5a25 100644 --- a/libs/gui/BufferQueue.cpp +++ b/libs/gui/BufferQueue.cpp @@ -61,6 +61,15 @@ void BufferQueue::ProxyConsumerListener::onSidebandStreamChanged() { } } +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 856115dc74..7fbf312727 100644 --- a/libs/gui/BufferQueueConsumer.cpp +++ b/libs/gui/BufferQueueConsumer.cpp @@ -261,6 +261,7 @@ status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer, ATRACE_INT(mCore->mConsumerName.string(), static_cast<int32_t>(mCore->mQueue.size())); + mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size()); VALIDATE_CONSISTENCY(); } @@ -718,6 +719,19 @@ sp<NativeHandle> BufferQueueConsumer::getSidebandStream() const { 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; +} + +status_t BufferQueueConsumer::discardFreeBuffers() { + Mutex::Autolock lock(mCore->mMutex); + mCore->discardFreeBuffersLocked(); + return NO_ERROR; +} + void BufferQueueConsumer::dumpState(String8& result, const char* prefix) const { const IPCThreadState* ipc = IPCThreadState::self(); const pid_t pid = ipc->getCallingPid(); diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp index 032779dc3a..d74d32c06d 100644 --- a/libs/gui/BufferQueueCore.cpp +++ b/libs/gui/BufferQueueCore.cpp @@ -47,6 +47,12 @@ static String8 getUniqueName() { android_atomic_inc(&counter)); } +static uint64_t getUniqueId() { + static std::atomic<uint32_t> counter{0}; + static uint64_t id = static_cast<uint64_t>(getpid()) << 32; + return id | counter++; +} + BufferQueueCore::BufferQueueCore(const sp<IGraphicBufferAlloc>& allocator) : mAllocator(allocator), mMutex(), @@ -56,6 +62,7 @@ BufferQueueCore::BufferQueueCore(const sp<IGraphicBufferAlloc>& allocator) : mConsumerListener(), mConsumerUsageBits(0), mConnectedApi(NO_CONNECTED_API), + mLinkedToDeath(), mConnectedProducerListener(), mSlots(), mQueue(), @@ -85,7 +92,8 @@ BufferQueueCore::BufferQueueCore(const sp<IGraphicBufferAlloc>& allocator) : mAutoRefresh(false), mSharedBufferSlot(INVALID_BUFFER_SLOT), mSharedBufferCache(Rect::INVALID_RECT, 0, NATIVE_WINDOW_SCALING_MODE_FREEZE, - HAL_DATASPACE_UNKNOWN) + HAL_DATASPACE_UNKNOWN), + mUniqueId(getUniqueId()) { if (allocator == NULL) { @@ -256,6 +264,16 @@ void BufferQueueCore::freeAllBuffersLocked() { VALIDATE_CONSISTENCY(); } +void BufferQueueCore::discardFreeBuffersLocked() { + for (int s : mFreeBuffers) { + mFreeSlots.insert(s); + clearBufferSlotLocked(s); + } + mFreeBuffers.clear(); + + VALIDATE_CONSISTENCY(); +} + bool BufferQueueCore::adjustAvailableSlotsLocked(int delta) { if (delta >= 0) { // If we're going to fail, do so before modifying anything diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp index e853dfb551..f8f38725b5 100644 --- a/libs/gui/BufferQueueProducer.cpp +++ b/libs/gui/BufferQueueProducer.cpp @@ -28,6 +28,7 @@ #define EGL_EGLEXT_PROTOTYPES +#include <binder/IPCThreadState.h> #include <gui/BufferItem.h> #include <gui/BufferQueueCore.h> #include <gui/BufferQueueProducer.h> @@ -510,11 +511,15 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot, mCore->mIsAllocatingCondition.broadcast(); if (graphicBuffer == NULL) { + mCore->mFreeSlots.insert(*outSlot); + mCore->clearBufferSlotLocked(*outSlot); BQ_LOGE("dequeueBuffer: createGraphicBuffer failed"); return error; } if (mCore->mIsAbandoned) { + mCore->mFreeSlots.insert(*outSlot); + mCore->clearBufferSlotLocked(*outSlot); BQ_LOGE("dequeueBuffer: BufferQueue has been abandoned"); return NO_INIT; } @@ -897,10 +902,12 @@ status_t BufferQueueProducer::queueBuffer(int slot, output->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight, mCore->mTransformHint, - static_cast<uint32_t>(mCore->mQueue.size())); + static_cast<uint32_t>(mCore->mQueue.size()), + mCore->mFrameCounter + 1); ATRACE_INT(mCore->mConsumerName.string(), static_cast<int32_t>(mCore->mQueue.size())); + mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size()); // Take a ticket for the callback functions callbackTicket = mNextCallbackTicket++; @@ -1104,27 +1111,32 @@ status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener, mCore->mConnectedApi = api; output->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight, mCore->mTransformHint, - static_cast<uint32_t>(mCore->mQueue.size())); - - // Set up a death notification so that we can disconnect - // automatically if the remote producer dies - if (listener != NULL && - IInterface::asBinder(listener)->remoteBinder() != NULL) { - status = IInterface::asBinder(listener)->linkToDeath( - static_cast<IBinder::DeathRecipient*>(this)); - if (status != NO_ERROR) { - BQ_LOGE("connect: linkToDeath failed: %s (%d)", - strerror(-status), status); + static_cast<uint32_t>(mCore->mQueue.size()), + mCore->mFrameCounter + 1); + + if (listener != NULL) { + // Set up a death notification so that we can disconnect + // automatically if the remote producer dies + if (IInterface::asBinder(listener)->remoteBinder() != NULL) { + status = IInterface::asBinder(listener)->linkToDeath( + static_cast<IBinder::DeathRecipient*>(this)); + if (status != NO_ERROR) { + BQ_LOGE("connect: linkToDeath failed: %s (%d)", + strerror(-status), status); + } + mCore->mLinkedToDeath = listener; + } + if (listener->needsReleaseNotify()) { + mCore->mConnectedProducerListener = listener; } } - mCore->mConnectedProducerListener = listener; break; default: BQ_LOGE("connect: unknown API %d", api); status = BAD_VALUE; break; } - + mCore->mConnectedPid = IPCThreadState::self()->getCallingPid(); mCore->mBufferHasBeenQueued = false; mCore->mDequeueBufferCannotBlock = false; if (mDequeueTimeout < 0) { @@ -1137,7 +1149,7 @@ status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener, return status; } -status_t BufferQueueProducer::disconnect(int api) { +status_t BufferQueueProducer::disconnect(int api, DisconnectMode mode) { ATRACE_CALL(); BQ_LOGV("disconnect: api %d", api); @@ -1145,6 +1157,14 @@ status_t BufferQueueProducer::disconnect(int api) { sp<IConsumerListener> listener; { // Autolock scope Mutex::Autolock lock(mCore->mMutex); + + if (mode == DisconnectMode::AllLocal) { + if (IPCThreadState::self()->getCallingPid() != mCore->mConnectedPid) { + return NO_ERROR; + } + api = BufferQueueCore::CURRENTLY_CONNECTED_API; + } + mCore->waitWhileAllocatingLocked(); if (mCore->mIsAbandoned) { @@ -1171,9 +1191,9 @@ status_t BufferQueueProducer::disconnect(int api) { mCore->freeAllBuffersLocked(); // Remove our death notification callback if we have one - if (mCore->mConnectedProducerListener != NULL) { + if (mCore->mLinkedToDeath != NULL) { sp<IBinder> token = - IInterface::asBinder(mCore->mConnectedProducerListener); + IInterface::asBinder(mCore->mLinkedToDeath); // This can fail if we're here because of the death // notification, but we just ignore it token->unlinkToDeath( @@ -1181,8 +1201,10 @@ status_t BufferQueueProducer::disconnect(int api) { } mCore->mSharedBufferSlot = BufferQueueCore::INVALID_BUFFER_SLOT; + mCore->mLinkedToDeath = NULL; mCore->mConnectedProducerListener = NULL; mCore->mConnectedApi = BufferQueueCore::NO_CONNECTED_API; + mCore->mConnectedPid = -1; mCore->mSidebandStream.clear(); mCore->mDequeueCondition.broadcast(); listener = mCore->mConsumerListener; @@ -1340,14 +1362,6 @@ String8 BufferQueueProducer::getConsumerName() const { return mConsumerName; } -uint64_t BufferQueueProducer::getNextFrameNumber() const { - ATRACE_CALL(); - - Mutex::Autolock lock(mCore->mMutex); - uint64_t nextFrameNumber = mCore->mFrameCounter + 1; - return nextFrameNumber; -} - status_t BufferQueueProducer::setSharedBufferMode(bool sharedBufferMode) { ATRACE_CALL(); BQ_LOGV("setSharedBufferMode: %d", sharedBufferMode); @@ -1416,6 +1430,22 @@ status_t BufferQueueProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, 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 @@ -1425,4 +1455,11 @@ void BufferQueueProducer::binderDied(const wp<android::IBinder>& /* who */) { disconnect(api); } +status_t BufferQueueProducer::getUniqueId(uint64_t* outId) const { + BQ_LOGV("getUniqueId"); + + *outId = mCore->mUniqueId; + return NO_ERROR; +} + } // namespace android diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp index 5d2e5cffd8..5546d5416c 100644 --- a/libs/gui/ConsumerBase.cpp +++ b/libs/gui/ConsumerBase.cpp @@ -235,6 +235,25 @@ status_t ConsumerBase::setDefaultBufferDataSpace( 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); +} + +status_t ConsumerBase::discardFreeBuffers() { + Mutex::Autolock _l(mMutex); + if (mAbandoned) { + CB_LOGE("discardFreeBuffers: ConsumerBase is abandoned!"); + return NO_INIT; + } + return mConsumer->discardFreeBuffers(); +} + void ConsumerBase::dumpState(String8& result) const { dumpState(result, ""); } @@ -296,9 +315,10 @@ status_t ConsumerBase::addReleaseFenceLocked(int slot, if (!mSlots[slot].mFence.get()) { mSlots[slot].mFence = fence; } else { + char fenceName[32] = {}; + snprintf(fenceName, 32, "%.28s:%d", mName.string(), slot); sp<Fence> mergedFence = Fence::merge( - String8::format("%.28s:%d", mName.string(), slot), - mSlots[slot].mFence, fence); + fenceName, mSlots[slot].mFence, fence); if (!mergedFence.get()) { CB_LOGE("failed to merge release fences"); // synchronization is broken, the best we can do is hope fences diff --git a/libs/gui/IConsumerListener.cpp b/libs/gui/IConsumerListener.cpp index 4382ee8b26..ff7b83a7d6 100644 --- a/libs/gui/IConsumerListener.cpp +++ b/libs/gui/IConsumerListener.cpp @@ -31,6 +31,7 @@ enum { 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 @@ public: 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 @@ status_t BnConsumerListener::onTransact( 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 6d33a10ab1..240146455e 100644 --- a/libs/gui/IGraphicBufferConsumer.cpp +++ b/libs/gui/IGraphicBufferConsumer.cpp @@ -51,6 +51,8 @@ enum { SET_CONSUMER_USAGE_BITS, SET_TRANSFORM_HINT, GET_SIDEBAND_STREAM, + GET_OCCUPANCY_HISTORY, + DISCARD_FREE_BUFFERS, DUMP, }; @@ -260,6 +262,46 @@ public: 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 status_t discardFreeBuffers() { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); + status_t error = remote()->transact(DISCARD_FREE_BUFFERS, data, &reply); + if (error != NO_ERROR) { + return error; + } + int32_t result = NO_ERROR; + error = reply.readInt32(&result); + if (error != NO_ERROR) { + return error; + } + return result; + } + virtual void dumpState(String8& result, const char* prefix) const { Parcel data, reply; data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); @@ -409,6 +451,31 @@ status_t BnGraphicBufferConsumer::onTransact( } 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 DISCARD_FREE_BUFFERS: { + CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); + status_t result = discardFreeBuffers(); + status_t error = reply->writeInt32(result); + return 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 81e9407f11..846c205c00 100644 --- a/libs/gui/IGraphicBufferProducer.cpp +++ b/libs/gui/IGraphicBufferProducer.cpp @@ -50,11 +50,12 @@ enum { GET_CONSUMER_NAME, SET_MAX_DEQUEUED_BUFFER_COUNT, SET_ASYNC_MODE, - GET_NEXT_FRAME_NUMBER, SET_SHARED_BUFFER_MODE, SET_AUTO_REFRESH, SET_DEQUEUE_TIMEOUT, GET_LAST_QUEUED_BUFFER, + GET_FRAME_TIMESTAMPS, + GET_UNIQUE_ID }; class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer> @@ -132,7 +133,11 @@ public: bool nonNull = reply.readInt32(); if (nonNull) { *fence = new Fence(); - reply.read(**fence); + result = reply.read(**fence); + if (result != NO_ERROR) { + fence->clear(); + return result; + } } result = reply.readInt32(); return result; @@ -170,12 +175,21 @@ public: bool nonNull = reply.readInt32(); if (nonNull) { *outBuffer = new GraphicBuffer; - reply.read(**outBuffer); + result = reply.read(**outBuffer); + if (result != NO_ERROR) { + outBuffer->clear(); + return result; + } } nonNull = reply.readInt32(); if (nonNull) { *outFence = new Fence; - reply.read(**outFence); + result = reply.read(**outFence); + if (result != NO_ERROR) { + outBuffer->clear(); + outFence->clear(); + return result; + } } } return result; @@ -256,10 +270,11 @@ public: return result; } - virtual status_t disconnect(int api) { + virtual status_t disconnect(int api, DisconnectMode mode) { Parcel data, reply; data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); data.writeInt32(api); + data.writeInt32(static_cast<int32_t>(mode)); status_t result =remote()->transact(DISCONNECT, data, &reply); if (result != NO_ERROR) { return result; @@ -332,18 +347,6 @@ public: return reply.readString8(); } - virtual uint64_t getNextFrameNumber() const { - Parcel data, reply; - data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); - status_t result = remote()->transact(GET_NEXT_FRAME_NUMBER, data, &reply); - if (result != NO_ERROR) { - ALOGE("getNextFrameNumber failed to transact: %d", result); - return 0; - } - uint64_t frameNumber = reply.readUint64(); - return frameNumber; - } - virtual status_t setSharedBufferMode(bool sharedBufferMode) { Parcel data, reply; data.writeInterfaceToken( @@ -418,6 +421,61 @@ public: *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; + } + + virtual status_t getUniqueId(uint64_t* outId) const { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); + status_t result = remote()->transact(GET_UNIQUE_ID, data, &reply); + if (result != NO_ERROR) { + ALOGE("getUniqueId failed to transact: %d", result); + } + status_t actualResult = NO_ERROR; + result = reply.readInt32(&actualResult); + if (result != NO_ERROR) { + return result; + } + result = reply.readUint64(outId); + if (result != NO_ERROR) { + return result; + } + return actualResult; + } }; // Out-of-line virtual method definition to trigger vtable emission in this @@ -504,9 +562,11 @@ status_t BnGraphicBufferProducer::onTransact( case ATTACH_BUFFER: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); sp<GraphicBuffer> buffer = new GraphicBuffer(); - data.read(*buffer.get()); + status_t result = data.read(*buffer.get()); int slot = 0; - int result = attachBuffer(&slot, buffer); + if (result == NO_ERROR) { + result = attachBuffer(&slot, buffer); + } reply->writeInt32(slot); reply->writeInt32(result); return NO_ERROR; @@ -527,8 +587,10 @@ status_t BnGraphicBufferProducer::onTransact( CHECK_INTERFACE(IGraphicBufferProducer, data, reply); int buf = data.readInt32(); sp<Fence> fence = new Fence(); - data.read(*fence.get()); - status_t result = cancelBuffer(buf, fence); + status_t result = data.read(*fence.get()); + if (result == NO_ERROR) { + result = cancelBuffer(buf, fence); + } reply->writeInt32(result); return NO_ERROR; } @@ -560,7 +622,8 @@ status_t BnGraphicBufferProducer::onTransact( case DISCONNECT: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); int api = data.readInt32(); - status_t res = disconnect(api); + DisconnectMode mode = static_cast<DisconnectMode>(data.readInt32()); + status_t res = disconnect(api, mode); reply->writeInt32(res); return NO_ERROR; } @@ -602,12 +665,6 @@ status_t BnGraphicBufferProducer::onTransact( reply->writeString8(getConsumerName()); return NO_ERROR; } - case GET_NEXT_FRAME_NUMBER: { - CHECK_INTERFACE(IGraphicBufferProducer, data, reply); - uint64_t frameNumber = getNextFrameNumber(); - reply->writeUint64(frameNumber); - return NO_ERROR; - } case SET_SHARED_BUFFER_MODE: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); bool sharedBufferMode = data.readInt32(); @@ -659,6 +716,44 @@ status_t BnGraphicBufferProducer::onTransact( } 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; + } + case GET_UNIQUE_ID: { + CHECK_INTERFACE(IGraphicBufferProducer, data, reply); + uint64_t outId = 0; + status_t actualResult = getUniqueId(&outId); + status_t result = reply->writeInt32(actualResult); + if (result != NO_ERROR) { + return result; + } + result = reply->writeUint64(outId); + if (result != NO_ERROR) { + return result; + } + return NO_ERROR; + } } return BBinder::onTransact(code, data, reply, flags); } diff --git a/libs/gui/IProducerListener.cpp b/libs/gui/IProducerListener.cpp index 855a4885cf..62abfa81c4 100644 --- a/libs/gui/IProducerListener.cpp +++ b/libs/gui/IProducerListener.cpp @@ -22,6 +22,7 @@ namespace android { enum { ON_BUFFER_RELEASED = IBinder::FIRST_CALL_TRANSACTION, + NEEDS_RELEASE_NOTIFY, }; class BpProducerListener : public BpInterface<IProducerListener> @@ -37,6 +38,23 @@ public: data.writeInterfaceToken(IProducerListener::getInterfaceDescriptor()); remote()->transact(ON_BUFFER_RELEASED, data, &reply, IBinder::FLAG_ONEWAY); } + + virtual bool needsReleaseNotify() { + bool result; + Parcel data, reply; + data.writeInterfaceToken(IProducerListener::getInterfaceDescriptor()); + status_t err = remote()->transact(NEEDS_RELEASE_NOTIFY, data, &reply); + if (err != NO_ERROR) { + ALOGE("IProducerListener: binder call \'needsReleaseNotify\' failed"); + return true; + } + err = reply.readBool(&result); + if (err != NO_ERROR) { + ALOGE("IProducerListener: malformed binder reply"); + return true; + } + return result; + } }; // Out-of-line virtual method definition to trigger vtable emission in this @@ -52,6 +70,10 @@ status_t BnProducerListener::onTransact(uint32_t code, const Parcel& data, CHECK_INTERFACE(IProducerListener, data, reply); onBufferReleased(); return NO_ERROR; + case NEEDS_RELEASE_NOTIFY: + CHECK_INTERFACE(IProducerListener, data, reply); + reply->writeBool(needsReleaseNotify()); + return NO_ERROR; } return BBinder::onTransact(code, data, reply, flags); } @@ -60,4 +82,8 @@ ProducerListener::~ProducerListener() = default; DummyProducerListener::~DummyProducerListener() = default; +bool BnProducerListener::needsReleaseNotify() { + return true; +} + } // namespace android diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index 015945a4fc..0a8e6a555e 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -32,6 +32,8 @@ #include <private/gui/LayerState.h> +#include <system/graphics.h> + #include <ui/DisplayInfo.h> #include <ui/DisplayStatInfo.h> #include <ui/HdrCapabilities.h> @@ -269,6 +271,82 @@ public: return reply.readInt32(); } + virtual status_t getDisplayColorModes(const sp<IBinder>& display, + Vector<android_color_mode_t>* outColorModes) { + Parcel data, reply; + status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + if (result != NO_ERROR) { + ALOGE("getDisplayColorModes failed to writeInterfaceToken: %d", result); + return result; + } + result = data.writeStrongBinder(display); + if (result != NO_ERROR) { + ALOGE("getDisplayColorModes failed to writeStrongBinder: %d", result); + return result; + } + result = remote()->transact(BnSurfaceComposer::GET_DISPLAY_COLOR_MODES, data, &reply); + if (result != NO_ERROR) { + ALOGE("getDisplayColorModes failed to transact: %d", result); + return result; + } + result = static_cast<status_t>(reply.readInt32()); + if (result == NO_ERROR) { + size_t numModes = reply.readUint32(); + outColorModes->clear(); + outColorModes->resize(numModes); + for (size_t i = 0; i < numModes; ++i) { + outColorModes->replaceAt(static_cast<android_color_mode_t>(reply.readInt32()), i); + } + } + return result; + } + + virtual android_color_mode_t getActiveColorMode(const sp<IBinder>& display) { + Parcel data, reply; + status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + if (result != NO_ERROR) { + ALOGE("getActiveColorMode failed to writeInterfaceToken: %d", result); + return static_cast<android_color_mode_t>(result); + } + result = data.writeStrongBinder(display); + if (result != NO_ERROR) { + ALOGE("getActiveColorMode failed to writeStrongBinder: %d", result); + return static_cast<android_color_mode_t>(result); + } + result = remote()->transact(BnSurfaceComposer::GET_ACTIVE_COLOR_MODE, data, &reply); + if (result != NO_ERROR) { + ALOGE("getActiveColorMode failed to transact: %d", result); + return static_cast<android_color_mode_t>(result); + } + return static_cast<android_color_mode_t>(reply.readInt32()); + } + + virtual status_t setActiveColorMode(const sp<IBinder>& display, + android_color_mode_t colorMode) { + Parcel data, reply; + status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + if (result != NO_ERROR) { + ALOGE("setActiveColorMode failed to writeInterfaceToken: %d", result); + return result; + } + result = data.writeStrongBinder(display); + if (result != NO_ERROR) { + ALOGE("setActiveColorMode failed to writeStrongBinder: %d", result); + return result; + } + result = data.writeInt32(colorMode); + if (result != NO_ERROR) { + ALOGE("setActiveColorMode failed to writeInt32: %d", result); + return result; + } + result = remote()->transact(BnSurfaceComposer::SET_ACTIVE_COLOR_MODE, data, &reply); + if (result != NO_ERROR) { + ALOGE("setActiveColorMode failed to transact: %d", result); + return result; + } + return static_cast<status_t>(reply.readInt32()); + } + virtual status_t clearAnimationFrameStats() { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); @@ -469,6 +547,56 @@ status_t BnSurfaceComposer::onTransact( reply->writeInt32(result); return NO_ERROR; } + case GET_DISPLAY_COLOR_MODES: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + Vector<android_color_mode_t> colorModes; + sp<IBinder> display = nullptr; + status_t result = data.readStrongBinder(&display); + if (result != NO_ERROR) { + ALOGE("getDisplayColorModes failed to readStrongBinder: %d", result); + return result; + } + result = getDisplayColorModes(display, &colorModes); + reply->writeInt32(result); + if (result == NO_ERROR) { + reply->writeUint32(static_cast<uint32_t>(colorModes.size())); + for (size_t i = 0; i < colorModes.size(); ++i) { + reply->writeInt32(colorModes[i]); + } + } + return NO_ERROR; + } + case GET_ACTIVE_COLOR_MODE: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + sp<IBinder> display = nullptr; + status_t result = data.readStrongBinder(&display); + if (result != NO_ERROR) { + ALOGE("getActiveColorMode failed to readStrongBinder: %d", result); + return result; + } + android_color_mode_t colorMode = getActiveColorMode(display); + result = reply->writeInt32(static_cast<int32_t>(colorMode)); + return result; + } + case SET_ACTIVE_COLOR_MODE: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + sp<IBinder> display = nullptr; + status_t result = data.readStrongBinder(&display); + if (result != NO_ERROR) { + ALOGE("getActiveColorMode failed to readStrongBinder: %d", result); + return result; + } + int32_t colorModeInt = 0; + result = data.readInt32(&colorModeInt); + if (result != NO_ERROR) { + ALOGE("setActiveColorMode failed to readInt32: %d", result); + return result; + } + result = setActiveColorMode(display, + static_cast<android_color_mode_t>(colorModeInt)); + result = reply->writeInt32(result); + return result; + } case CLEAR_ANIMATION_FRAME_STATS: { CHECK_INTERFACE(ISurfaceComposer, data, reply); status_t result = clearAnimationFrameStats(); diff --git a/libs/gui/ISurfaceComposerClient.cpp b/libs/gui/ISurfaceComposerClient.cpp index decffbf9ba..47cb0473e8 100644 --- a/libs/gui/ISurfaceComposerClient.cpp +++ b/libs/gui/ISurfaceComposerClient.cpp @@ -41,7 +41,8 @@ enum { CREATE_SURFACE = IBinder::FIRST_CALL_TRANSACTION, DESTROY_SURFACE, CLEAR_LAYER_FRAME_STATS, - GET_LAYER_FRAME_STATS + GET_LAYER_FRAME_STATS, + GET_TRANSFORM_TO_DISPLAY_INVERSE }; class BpSurfaceComposerClient : public BpInterface<ISurfaceComposerClient> @@ -94,6 +95,35 @@ public: reply.read(*outStats); return reply.readInt32(); } + + virtual status_t getTransformToDisplayInverse(const sp<IBinder>& handle, + bool* outTransformToDisplayInverse) const { + Parcel data, reply; + status_t result = + data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor()); + if (result != NO_ERROR) { + return result; + } + result = data.writeStrongBinder(handle); + if (result != NO_ERROR) { + return result; + } + result = remote()->transact(GET_TRANSFORM_TO_DISPLAY_INVERSE, data, &reply); + if (result != NO_ERROR) { + return result; + } + int transformInverse; + result = reply.readInt32(&transformInverse); + if (result != NO_ERROR) { + return result; + } + *outTransformToDisplayInverse = transformInverse != 0 ? true : false; + status_t result2 = reply.readInt32(&result); + if (result2 != NO_ERROR) { + return result2; + } + return result; + } }; // Out-of-line virtual method definition to trigger vtable emission in this @@ -145,6 +175,25 @@ status_t BnSurfaceComposerClient::onTransact( reply->writeInt32(result); return NO_ERROR; } + case GET_TRANSFORM_TO_DISPLAY_INVERSE: { + CHECK_INTERFACE(ISurfaceComposerClient, data, reply); + sp<IBinder> handle; + status_t result = data.readStrongBinder(&handle); + if (result != NO_ERROR) { + return result; + } + bool transformInverse = false; + result = getTransformToDisplayInverse(handle, &transformInverse); + if (result != NO_ERROR) { + return result; + } + result = reply->writeInt32(transformInverse ? 1 : 0); + if (result != NO_ERROR) { + return result; + } + result = reply->writeInt32(NO_ERROR); + return result; + } default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/libs/gui/OccupancyTracker.cpp b/libs/gui/OccupancyTracker.cpp new file mode 100644 index 0000000000..9687aaf744 --- /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 1879f8a099..8e6ab1c73b 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -48,7 +48,8 @@ Surface::Surface( mSharedBufferMode(false), mAutoRefresh(false), mSharedBufferSlot(BufferItem::INVALID_BUFFER_SLOT), - mSharedBufferHasBeenQueued(false) + mSharedBufferHasBeenQueued(false), + mNextFrameNumber(1) { // Initialize the ANativeWindow function pointers. ANativeWindow::setSwapInterval = hook_setSwapInterval; @@ -116,7 +117,8 @@ status_t Surface::setGenerationNumber(uint32_t generation) { } uint64_t Surface::getNextFrameNumber() const { - return mGraphicBufferProducer->getNextFrameNumber(); + Mutex::Autolock lock(mMutex); + return mNextFrameNumber; } String8 Surface::getConsumerName() const { @@ -133,6 +135,39 @@ status_t Surface::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, 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); @@ -259,8 +294,10 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) { int buf = -1; sp<Fence> fence; + nsecs_t now = systemTime(); status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, reqWidth, reqHeight, reqFormat, reqUsage); + mLastDequeueDuration = systemTime() - now; if (result < 0) { ALOGV("dequeueBuffer: IGraphicBufferProducer::dequeueBuffer" @@ -463,7 +500,9 @@ int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) { input.setSurfaceDamage(flippedRegion); } + nsecs_t now = systemTime(); status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output); + mLastQueueDuration = systemTime() - now; if (err != OK) { ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err); } @@ -471,7 +510,7 @@ int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) { uint32_t numPendingBuffers = 0; uint32_t hint = 0; output.deflate(&mDefaultWidth, &mDefaultHeight, &hint, - &numPendingBuffers); + &numPendingBuffers, &mNextFrameNumber); // Disable transform hint if sticky transform is set. if (mStickyTransform == 0) { @@ -542,6 +581,20 @@ int Surface::query(int what, int* value) const { } return err; } + case NATIVE_WINDOW_LAST_DEQUEUE_DURATION: { + int64_t durationUs = mLastDequeueDuration / 1000; + *value = durationUs > std::numeric_limits<int>::max() ? + std::numeric_limits<int>::max() : + static_cast<int>(durationUs); + return NO_ERROR; + } + case NATIVE_WINDOW_LAST_QUEUE_DURATION: { + int64_t durationUs = mLastQueueDuration / 1000; + *value = durationUs > std::numeric_limits<int>::max() ? + std::numeric_limits<int>::max() : + static_cast<int>(durationUs); + return NO_ERROR; + } } } return mGraphicBufferProducer->query(what, value); @@ -617,6 +670,9 @@ int Surface::perform(int operation, va_list args) 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 +793,20 @@ int Surface::dispatchSetAutoRefresh(va_list args) { 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); @@ -752,7 +822,7 @@ int Surface::connect(int api, const sp<IProducerListener>& listener) { uint32_t numPendingBuffers = 0; uint32_t hint = 0; output.deflate(&mDefaultWidth, &mDefaultHeight, &hint, - &numPendingBuffers); + &numPendingBuffers, &mNextFrameNumber); // Disable transform hint if sticky transform is set. if (mStickyTransform == 0) { @@ -774,14 +844,14 @@ int Surface::connect(int api, const sp<IProducerListener>& listener) { } -int Surface::disconnect(int api) { +int Surface::disconnect(int api, IGraphicBufferProducer::DisconnectMode mode) { ATRACE_CALL(); ALOGV("Surface::disconnect"); Mutex::Autolock lock(mMutex); mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT; mSharedBufferHasBeenQueued = false; freeAllBuffers(); - int err = mGraphicBufferProducer->disconnect(api); + int err = mGraphicBufferProducer->disconnect(api, mode); if (!err) { mReqFormat = 0; mReqWidth = 0; @@ -1272,13 +1342,17 @@ status_t Surface::unlockAndPost() bool Surface::waitForNextFrame(uint64_t lastFrame, nsecs_t timeout) { Mutex::Autolock lock(mMutex); - uint64_t currentFrame = mGraphicBufferProducer->getNextFrameNumber(); - if (currentFrame > lastFrame) { + if (mNextFrameNumber > lastFrame) { return true; } return mQueueBufferCondition.waitRelative(mMutex, timeout) == OK; } +status_t Surface::getUniqueId(uint64_t* outId) const { + Mutex::Autolock lock(mMutex); + return mGraphicBufferProducer->getUniqueId(outId); +} + namespace view { status_t Surface::writeToParcel(Parcel* parcel) const { @@ -1290,12 +1364,18 @@ status_t Surface::writeToParcel(Parcel* parcel, bool nameAlreadyWritten) const { status_t res = OK; - if (!nameAlreadyWritten) res = parcel->writeString16(name); + if (!nameAlreadyWritten) { + res = parcel->writeString16(name); + if (res != OK) return res; - if (res == OK) { - res = parcel->writeStrongBinder( - IGraphicBufferProducer::asBinder(graphicBufferProducer)); + /* isSingleBuffered defaults to no */ + res = parcel->writeInt32(0); + if (res != OK) return res; } + + res = parcel->writeStrongBinder( + IGraphicBufferProducer::asBinder(graphicBufferProducer)); + return res; } @@ -1306,13 +1386,20 @@ status_t Surface::readFromParcel(const Parcel* parcel) { status_t Surface::readFromParcel(const Parcel* parcel, bool nameAlreadyRead) { if (parcel == nullptr) return BAD_VALUE; + status_t res = OK; if (!nameAlreadyRead) { name = readMaybeEmptyString16(parcel); + // Discard this for now + int isSingleBuffered; + res = parcel->readInt32(&isSingleBuffered); + if (res != OK) { + return res; + } } sp<IBinder> binder; - status_t res = parcel->readStrongBinder(&binder); + res = parcel->readStrongBinder(&binder); if (res != OK) return res; graphicBufferProducer = interface_cast<IGraphicBufferProducer>(binder); diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 2189047519..43506e9191 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -29,6 +29,8 @@ #include <binder/IMemory.h> #include <binder/IServiceManager.h> +#include <system/graphics.h> + #include <ui/DisplayInfo.h> #include <gui/CpuConsumer.h> @@ -165,11 +167,11 @@ public: uint64_t frameNumber); status_t setOverrideScalingMode(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id, int32_t overrideScalingMode); - status_t setPositionAppliesWithResize(const sp<SurfaceComposerClient>& client, + status_t setGeometryAppliesWithResize(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id); - void setDisplaySurface(const sp<IBinder>& token, - const sp<IGraphicBufferProducer>& bufferProducer); + status_t setDisplaySurface(const sp<IBinder>& token, + sp<IGraphicBufferProducer> bufferProducer); void setDisplayLayerStack(const sp<IBinder>& token, uint32_t layerStack); void setDisplayProjection(const sp<IBinder>& token, uint32_t orientation, @@ -445,7 +447,7 @@ status_t Composer::setOverrideScalingMode( return NO_ERROR; } -status_t Composer::setPositionAppliesWithResize( +status_t Composer::setGeometryAppliesWithResize( const sp<SurfaceComposerClient>& client, const sp<IBinder>& id) { Mutex::Autolock lock(mLock); @@ -453,7 +455,7 @@ status_t Composer::setPositionAppliesWithResize( if (!s) { return BAD_INDEX; } - s->what |= layer_state_t::ePositionAppliesWithResize; + s->what |= layer_state_t::eGeometryAppliesWithResize; return NO_ERROR; } @@ -471,12 +473,24 @@ DisplayState& Composer::getDisplayStateLocked(const sp<IBinder>& token) { return mDisplayStates.editItemAt(static_cast<size_t>(index)); } -void Composer::setDisplaySurface(const sp<IBinder>& token, - const sp<IGraphicBufferProducer>& bufferProducer) { +status_t Composer::setDisplaySurface(const sp<IBinder>& token, + sp<IGraphicBufferProducer> bufferProducer) { + if (bufferProducer.get() != nullptr) { + // Make sure that composition can never be stalled by a virtual display + // consumer that isn't processing buffers fast enough. + status_t err = bufferProducer->setAsyncMode(true); + if (err != NO_ERROR) { + ALOGE("Composer::setDisplaySurface Failed to enable async mode on the " + "BufferQueue. This BufferQueue cannot be used for virtual " + "display. (%d)", err); + return err; + } + } Mutex::Autolock _l(mLock); DisplayState& s(getDisplayStateLocked(token)); s.surface = bufferProducer; s.what |= DisplayState::eSurfaceChanged; + return NO_ERROR; } void Composer::setDisplayLayerStack(const sp<IBinder>& token, @@ -612,6 +626,14 @@ status_t SurfaceComposerClient::getLayerFrameStats(const sp<IBinder>& token, return mClient->getLayerFrameStats(token, outStats); } +status_t SurfaceComposerClient::getTransformToDisplayInverse(const sp<IBinder>& token, + bool* outTransformToDisplayInverse) const { + if (mStatus != NO_ERROR) { + return mStatus; + } + return mClient->getTransformToDisplayInverse(token, outTransformToDisplayInverse); +} + inline Composer& SurfaceComposerClient::getComposer() { return mComposer; } @@ -699,16 +721,16 @@ status_t SurfaceComposerClient::setOverrideScalingMode( this, id, overrideScalingMode); } -status_t SurfaceComposerClient::setPositionAppliesWithResize( +status_t SurfaceComposerClient::setGeometryAppliesWithResize( const sp<IBinder>& id) { - return getComposer().setPositionAppliesWithResize(this, id); + return getComposer().setGeometryAppliesWithResize(this, id); } // ---------------------------------------------------------------------------- -void SurfaceComposerClient::setDisplaySurface(const sp<IBinder>& token, - const sp<IGraphicBufferProducer>& bufferProducer) { - Composer::getInstance().setDisplaySurface(token, bufferProducer); +status_t SurfaceComposerClient::setDisplaySurface(const sp<IBinder>& token, + sp<IGraphicBufferProducer> bufferProducer) { + return Composer::getInstance().setDisplaySurface(token, bufferProducer); } void SurfaceComposerClient::setDisplayLayerStack(const sp<IBinder>& token, @@ -763,6 +785,20 @@ status_t SurfaceComposerClient::setActiveConfig(const sp<IBinder>& display, int return ComposerService::getComposerService()->setActiveConfig(display, id); } +status_t SurfaceComposerClient::getDisplayColorModes(const sp<IBinder>& display, + Vector<android_color_mode_t>* outColorModes) { + return ComposerService::getComposerService()->getDisplayColorModes(display, outColorModes); +} + +android_color_mode_t SurfaceComposerClient::getActiveColorMode(const sp<IBinder>& display) { + return ComposerService::getComposerService()->getActiveColorMode(display); +} + +status_t SurfaceComposerClient::setActiveColorMode(const sp<IBinder>& display, + android_color_mode_t colorMode) { + return ComposerService::getComposerService()->setActiveColorMode(display, colorMode); +} + void SurfaceComposerClient::setDisplayPowerMode(const sp<IBinder>& token, int mode) { ComposerService::getComposerService()->setPowerMode(token, mode); diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp index 4671e505aa..33c1d906e6 100644 --- a/libs/gui/SurfaceControl.cpp +++ b/libs/gui/SurfaceControl.cpp @@ -112,10 +112,10 @@ status_t SurfaceControl::setPosition(float x, float y) { if (err < 0) return err; return mClient->setPosition(mHandle, x, y); } -status_t SurfaceControl::setPositionAppliesWithResize() { +status_t SurfaceControl::setGeometryAppliesWithResize() { status_t err = validate(); if (err < 0) return err; - return mClient->setPositionAppliesWithResize(mHandle); + return mClient->setGeometryAppliesWithResize(mHandle); } status_t SurfaceControl::setSize(uint32_t w, uint32_t h) { status_t err = validate(); @@ -190,6 +190,13 @@ status_t SurfaceControl::getLayerFrameStats(FrameStats* outStats) const { return client->getLayerFrameStats(mHandle, outStats); } +status_t SurfaceControl::getTransformToDisplayInverse(bool* outTransformToDisplayInverse) const { + status_t err = validate(); + if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); + return client->getTransformToDisplayInverse(mHandle, outTransformToDisplayInverse); +} + status_t SurfaceControl::validate() const { if (mHandle==0 || mClient==0) { diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp index 85d63b4ba2..65df7dc991 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,217 @@ TEST_F(BufferQueueTest, CanRetrieveLastQueuedBuffer) { 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); +} + +TEST_F(BufferQueueTest, TestDiscardFreeBuffers) { + 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 4 buffers so we don't get + // BUFFER_NEEDS_REALLOCATION below + int slots[4] = {}; + mProducer->setMaxDequeuedBufferCount(4); + for (size_t i = 0; i < 4; ++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 < 4; ++i) { + ASSERT_EQ(OK, mProducer->cancelBuffer(slots[i], Fence::NO_FENCE)); + } + + // Get buffers in all states: dequeued, filled, acquired, free + + // Fill 3 buffers + 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, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0)); + ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); + // Dequeue 1 buffer + ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0)); + + // Acquire and free 1 buffer + 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)); + // Acquire 1 buffer, leaving 1 filled buffer in queue + ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); + + // Now discard the free buffers + ASSERT_EQ(OK, mConsumer->discardFreeBuffers()); + + // Check no free buffers in dump + String8 dumpString; + mConsumer->dumpState(dumpString, nullptr); + + // Parse the dump to ensure that all buffer slots that are FREE also + // have a null GraphicBuffer + // Fragile - assumes the following format for the dump for a buffer entry: + // ":%p\][^:]*state=FREE" where %p is the buffer pointer in hex. + ssize_t idx = dumpString.find("state=FREE"); + while (idx != -1) { + ssize_t bufferPtrIdx = idx - 1; + while (bufferPtrIdx > 0) { + if (dumpString[bufferPtrIdx] == ':') { + bufferPtrIdx++; + break; + } + bufferPtrIdx--; + } + ASSERT_GT(bufferPtrIdx, 0) << "Can't parse queue dump to validate"; + ssize_t nullPtrIdx = dumpString.find("0x0]", bufferPtrIdx); + ASSERT_EQ(bufferPtrIdx, nullPtrIdx) << "Free buffer not discarded"; + idx = dumpString.find("FREE", idx + 1); + } +} + } // namespace android diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp index 45b64639d2..9f3304731e 100644 --- a/libs/gui/tests/IGraphicBufferProducer_test.cpp +++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp @@ -370,13 +370,16 @@ TEST_F(IGraphicBufferProducerTest, Queue_Succeeds) { uint32_t height; uint32_t transformHint; uint32_t numPendingBuffers; + uint64_t nextFrameNumber; - output.deflate(&width, &height, &transformHint, &numPendingBuffers); + output.deflate(&width, &height, &transformHint, &numPendingBuffers, + &nextFrameNumber); EXPECT_EQ(DEFAULT_WIDTH, width); EXPECT_EQ(DEFAULT_HEIGHT, height); EXPECT_EQ(DEFAULT_TRANSFORM_HINT, transformHint); EXPECT_EQ(1u, numPendingBuffers); // since queueBuffer was called exactly once + EXPECT_EQ(2u, nextFrameNumber); } // Buffer was not in the dequeued state diff --git a/libs/gui/tests/SurfaceTextureGL_test.cpp b/libs/gui/tests/SurfaceTextureGL_test.cpp index 81d5a57eb0..308bd7d69c 100644 --- a/libs/gui/tests/SurfaceTextureGL_test.cpp +++ b/libs/gui/tests/SurfaceTextureGL_test.cpp @@ -115,13 +115,13 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferPow2) { EXPECT_TRUE(checkPixel(63, 63, 0, 133, 0, 255)); EXPECT_TRUE(checkPixel( 0, 63, 255, 127, 255, 255)); - EXPECT_TRUE(checkPixel(22, 19, 100, 255, 74, 255)); - EXPECT_TRUE(checkPixel(45, 11, 100, 255, 74, 255)); - EXPECT_TRUE(checkPixel(52, 12, 155, 0, 181, 255)); - EXPECT_TRUE(checkPixel( 7, 32, 150, 237, 170, 255)); - EXPECT_TRUE(checkPixel(31, 54, 0, 71, 117, 255)); - EXPECT_TRUE(checkPixel(29, 28, 0, 133, 0, 255)); - EXPECT_TRUE(checkPixel(36, 41, 100, 232, 255, 255)); + EXPECT_TRUE(checkPixel(22, 19, 100, 255, 74, 255, 3)); + EXPECT_TRUE(checkPixel(45, 11, 100, 255, 74, 255, 3)); + EXPECT_TRUE(checkPixel(52, 12, 155, 0, 181, 255, 3)); + EXPECT_TRUE(checkPixel( 7, 32, 150, 237, 170, 255, 3)); + EXPECT_TRUE(checkPixel(31, 54, 0, 71, 117, 255, 3)); + EXPECT_TRUE(checkPixel(29, 28, 0, 133, 0, 255, 3)); + EXPECT_TRUE(checkPixel(36, 41, 100, 232, 255, 255, 3)); } TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferWithCrop) { diff --git a/libs/ui/Fence.cpp b/libs/ui/Fence.cpp index bf24ffb7e0..7cf8233820 100644 --- a/libs/ui/Fence.cpp +++ b/libs/ui/Fence.cpp @@ -72,7 +72,7 @@ status_t Fence::waitForever(const char* logname) { return err < 0 ? -errno : status_t(NO_ERROR); } -sp<Fence> Fence::merge(const String8& name, const sp<Fence>& f1, +sp<Fence> Fence::merge(const char* name, const sp<Fence>& f1, const sp<Fence>& f2) { ATRACE_CALL(); int result; @@ -80,24 +80,29 @@ sp<Fence> Fence::merge(const String8& name, const sp<Fence>& f1, // valid fence (e.g. NO_FENCE) we merge the one valid fence with itself so // that a new fence with the given name is created. if (f1->isValid() && f2->isValid()) { - result = sync_merge(name.string(), f1->mFenceFd, f2->mFenceFd); + result = sync_merge(name, f1->mFenceFd, f2->mFenceFd); } else if (f1->isValid()) { - result = sync_merge(name.string(), f1->mFenceFd, f1->mFenceFd); + result = sync_merge(name, f1->mFenceFd, f1->mFenceFd); } else if (f2->isValid()) { - result = sync_merge(name.string(), f2->mFenceFd, f2->mFenceFd); + result = sync_merge(name, f2->mFenceFd, f2->mFenceFd); } else { return NO_FENCE; } if (result == -1) { status_t err = -errno; ALOGE("merge: sync_merge(\"%s\", %d, %d) returned an error: %s (%d)", - name.string(), f1->mFenceFd, f2->mFenceFd, + name, f1->mFenceFd, f2->mFenceFd, strerror(-err), err); return NO_FENCE; } return sp<Fence>(new Fence(result)); } +sp<Fence> Fence::merge(const String8& name, const sp<Fence>& f1, + const sp<Fence>& f2) { + return merge(name.string(), f1, f2); +} + int Fence::dup() const { return ::dup(mFenceFd); } diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp index a430a31058..b53c563624 100644 --- a/libs/ui/Region.cpp +++ b/libs/ui/Region.cpp @@ -796,6 +796,11 @@ status_t Region::unflatten(void const* buffer, size_t size) { return NO_MEMORY; } + if (numRects > (UINT32_MAX / sizeof(Rect))) { + android_errorWriteWithInfoLog(0x534e4554, "29983260", -1, NULL, 0); + return NO_MEMORY; + } + Region result; result.mStorage.clear(); for (size_t r = 0; r < numRects; ++r) { diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h index 0a141388e8..c7bdadb85a 100644 --- a/opengl/include/EGL/eglext.h +++ b/opengl/include/EGL/eglext.h @@ -628,6 +628,24 @@ typedef EGLClientBuffer (EGLAPIENTRYP PFNEGLCREATENATIVECLIENTBUFFERANDROID) (co #define EGL_MUTABLE_RENDER_BUFFER_BIT_KHR 0x1000 #endif +#ifndef EGL_ANDROID_get_frame_timestamps +#define EGL_ANDROID_get_frame_timestamps 1 +#define EGL_TIMESTAMPS_ANDROID 0x314D +#define EGL_QUEUE_TIME_ANDROID 0x314E +#define EGL_RENDERING_COMPLETE_TIME_ANDROID 0x314F +#define EGL_COMPOSITION_START_TIME_ANDROID 0x3430 +#define EGL_COMPOSITION_FINISHED_TIME_ANDROID 0x3431 +#define EGL_DISPLAY_RETIRE_TIME_ANDROID 0x3432 +#define EGL_READS_DONE_TIME_ANDROID 0x3433 +#ifdef EGL_EGLEXT_PROTOTYPES +EGLAPI EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface, EGLint framesAgo, EGLint numTimestamps, const EGLint *timestamps, EGLnsecsANDROID *values); +EGLAPI EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface surface, EGLint timestamp); +#else +typedef EGLAPI EGLBoolean (EGLAPIENTRYP PFNEGLGETFRAMETIMESTAMPSANDROID) (EGLDisplay dpy, EGLSurface surface, EGLint framesAgo, EGLint numTimestamps, const EGLint *timestamps, EGLnsecsANDROID *values); +typedef EGLAPI EGLBoolean (EGLAPIENTRYP PFNEGLQUERYTIMESTAMPSUPPORTEDANDROID) (EGLDisplay dpy, EGLSurface surface, EGLint timestamp); +#endif +#endif + #ifdef __cplusplus } #endif diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp index 0c4b9e9898..5e727942ac 100644 --- a/opengl/libs/EGL/eglApi.cpp +++ b/opengl/libs/EGL/eglApi.cpp @@ -56,6 +56,8 @@ using namespace android; +#define ENABLE_EGL_ANDROID_GET_FRAME_TIMESTAMPS 0 + // ---------------------------------------------------------------------------- namespace android { @@ -86,6 +88,9 @@ extern char const * const gBuiltinExtensionString = "EGL_KHR_swap_buffers_with_damage " "EGL_ANDROID_create_native_client_buffer " "EGL_ANDROID_front_buffer_auto_refresh " +#if ENABLE_EGL_ANDROID_GET_FRAME_TIMESTAMPS + "EGL_ANDROID_get_frame_timestamps " +#endif ; extern char const * const gExtensionString = "EGL_KHR_image " // mandatory @@ -207,6 +212,12 @@ static const extention_map_t sExtensionMap[] = { (__eglMustCastToProperFunctionPointerType)&eglGetStreamFileDescriptorKHR }, { "eglCreateStreamFromFileDescriptorKHR", (__eglMustCastToProperFunctionPointerType)&eglCreateStreamFromFileDescriptorKHR }, + + // EGL_ANDROID_get_frame_timestamps + { "eglGetFrameTimestampsANDROID", + (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampsANDROID }, + { "eglQueryTimestampSupportedANDROID", + (__eglMustCastToProperFunctionPointerType)&eglQueryTimestampSupportedANDROID }, }; /* @@ -1180,7 +1191,7 @@ EGLBoolean eglSurfaceAttrib( if (!_s.get()) return setError(EGL_BAD_SURFACE, EGL_FALSE); - egl_surface_t const * const s = get_surface(surface); + egl_surface_t * const s = get_surface(surface); if (attribute == EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID) { int err = native_window_set_auto_refresh(s->win.get(), @@ -1189,6 +1200,13 @@ EGLBoolean eglSurfaceAttrib( setError(EGL_BAD_SURFACE, EGL_FALSE); } +#if ENABLE_EGL_ANDROID_GET_FRAME_TIMESTAMPS + if (attribute == EGL_TIMESTAMPS_ANDROID) { + s->enableTimestamps = value; + return EGL_TRUE; + } +#endif + if (s->cnx->egl.eglSurfaceAttrib) { return s->cnx->egl.eglSurfaceAttrib( dp->disp.dpy, s->surface, attribute, value); @@ -1979,3 +1997,105 @@ EGLBoolean eglSetDamageRegionKHR(EGLDisplay dpy, EGLSurface surface, return EGL_FALSE; } + +EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface, + EGLint framesAgo, EGLint numTimestamps, const EGLint *timestamps, + EGLnsecsANDROID *values) +{ + clearError(); + + const egl_display_ptr dp = validate_display(dpy); + if (!dp) { + setError(EGL_BAD_DISPLAY, EGL_FALSE); + return EGL_FALSE; + } + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) { + setError(EGL_BAD_SURFACE, EGL_FALSE); + return EGL_FALSE; + } + + egl_surface_t const * const s = get_surface(surface); + + if (!s->enableTimestamps) { + setError(EGL_BAD_SURFACE, EGL_FALSE); + return EGL_FALSE; + } + + nsecs_t* postedTime = nullptr; + nsecs_t* acquireTime = nullptr; + nsecs_t* refreshStartTime = nullptr; + nsecs_t* GLCompositionDoneTime = nullptr; + nsecs_t* displayRetireTime = nullptr; + nsecs_t* releaseTime = nullptr; + + for (int i = 0; i < numTimestamps; i++) { + switch (timestamps[i]) { + case EGL_QUEUE_TIME_ANDROID: + postedTime = &values[i]; + break; + case EGL_RENDERING_COMPLETE_TIME_ANDROID: + acquireTime = &values[i]; + break; + case EGL_COMPOSITION_START_TIME_ANDROID: + refreshStartTime = &values[i]; + break; + case EGL_COMPOSITION_FINISHED_TIME_ANDROID: + GLCompositionDoneTime = &values[i]; + break; + case EGL_DISPLAY_RETIRE_TIME_ANDROID: + displayRetireTime = &values[i]; + break; + case EGL_READS_DONE_TIME_ANDROID: + releaseTime = &values[i]; + break; + default: + setError(EGL_BAD_PARAMETER, EGL_FALSE); + return EGL_FALSE; + } + } + + status_t ret = native_window_get_frame_timestamps(s->win.get(), framesAgo, + postedTime, acquireTime, refreshStartTime, GLCompositionDoneTime, + displayRetireTime, releaseTime); + + if (ret != NO_ERROR) { + setError(EGL_BAD_ACCESS, EGL_FALSE); + return EGL_FALSE; + } + + return EGL_TRUE; +} + +EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface surface, + EGLint timestamp) +{ + clearError(); + + const egl_display_ptr dp = validate_display(dpy); + if (!dp) { + setError(EGL_BAD_DISPLAY, EGL_FALSE); + return EGL_FALSE; + } + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) { + setError(EGL_BAD_SURFACE, EGL_FALSE); + return EGL_FALSE; + } + + switch (timestamp) { +#if ENABLE_EGL_ANDROID_GET_FRAME_TIMESTAMPS + case EGL_QUEUE_TIME_ANDROID: + case EGL_RENDERING_COMPLETE_TIME_ANDROID: + case EGL_COMPOSITION_START_TIME_ANDROID: + case EGL_COMPOSITION_FINISHED_TIME_ANDROID: + case EGL_DISPLAY_RETIRE_TIME_ANDROID: + case EGL_READS_DONE_TIME_ANDROID: + return EGL_TRUE; +#endif + default: + return EGL_FALSE; + } +} diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp index 10523f50a7..a32f0378be 100644 --- a/opengl/libs/EGL/egl_display.cpp +++ b/opengl/libs/EGL/egl_display.cpp @@ -66,7 +66,10 @@ egl_display_t::~egl_display_t() { egl_display_t* egl_display_t::get(EGLDisplay dpy) { uintptr_t index = uintptr_t(dpy)-1U; - return (index >= NUM_DISPLAYS) ? NULL : &sDisplay[index]; + if (index >= NUM_DISPLAYS || !sDisplay[index].isValid()) { + return nullptr; + } + return &sDisplay[index]; } void egl_display_t::addObject(egl_object_t* object) { diff --git a/opengl/libs/EGL/egl_object.cpp b/opengl/libs/EGL/egl_object.cpp index 7fc56094bf..6a76737f70 100644 --- a/opengl/libs/EGL/egl_object.cpp +++ b/opengl/libs/EGL/egl_object.cpp @@ -68,7 +68,7 @@ egl_surface_t::egl_surface_t(egl_display_t* dpy, EGLConfig config, EGLNativeWindowType win, EGLSurface surface, egl_connection_t const* cnx) : egl_object_t(dpy), surface(surface), config(config), win(win), cnx(cnx), - connected(true) + enableTimestamps(false), connected(true) {} egl_surface_t::~egl_surface_t() { diff --git a/opengl/libs/EGL/egl_object.h b/opengl/libs/EGL/egl_object.h index 8ceba1d892..3150ba6918 100644 --- a/opengl/libs/EGL/egl_object.h +++ b/opengl/libs/EGL/egl_object.h @@ -139,6 +139,7 @@ public: EGLConfig config; sp<ANativeWindow> win; egl_connection_t const* cnx; + bool enableTimestamps; private: bool connected; void disconnect(); diff --git a/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt b/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt new file mode 100644 index 0000000000..30337ad7a9 --- /dev/null +++ b/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt @@ -0,0 +1,145 @@ +Name + + ANDROID_get_frame_timestamps + +Name Strings + + EGL_ANDROID_get_frame_timestamps + +Contributors + + Pablo Ceballos + +Contact + + Pablo Ceballos, Google Inc. (pceballos 'at' google.com) + +Status + + Draft + +Version + + Version 1, May 31, 2016 + +Number + + EGL Extension #XXX + +Dependencies + + Requires EGL 1.2 + + This extension is written against the wording of the EGL 1.5 Specification + +Overview + + This extension allows querying various timestamps related to the composition + and display of window surfaces. + + Some examples of how this might be used: + - The display retire time can be used to calculate end-to-end latency of + the entire graphics pipeline. + - The queue time and rendering complete time can be used to determine + how long the application's rendering took to complete. Likewise, the + composition start time and finish time can be used to determine how + long the compositor's rendering work took. In combination these can be + used to help determine if the system is GPU or CPU bound. + +New Types + + /* + * EGLnsecsANDROID is a signed integer type for representing a time in + * nanoseconds. + */ + #include <khrplatform.h> + typedef khronos_stime_nanoseconds_t EGLnsecsANDROID; + +New Procedures and Functions + + EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface, + EGLint framesAgo, EGLint numTimestamps, const EGLint *timestamps, + EGLnsecsANDROID *values); + + EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface + surface, EGLint timestamp); + +New Tokens + + EGL_TIMESTAMPS_ANDROID 0x314D + EGL_QUEUE_TIME_ANDROID 0x314E + EGL_RENDERING_COMPLETE_TIME_ANDROID 0x314F + EGL_COMPOSITION_START_TIME_ANDROID 0x3430 + EGL_COMPOSITION_FINISHED_TIME_ANDROID 0x3431 + EGL_DISPLAY_RETIRE_TIME_ANDROID 0x3432 + EGL_READS_DONE_TIME_ANDROID 0x3433 + +Add to the list of supported tokens for eglSurfaceAttrib in section 3.5.6 +"Surface Attributes", page 43: + + If attribute is EGL_TIMESTAMPS_ANDROID, then values specifies whether to + enable/disable timestamp collection for this surface. A value of EGL_TRUE + enables timestamp collection, while a value of EGL_FALSE disables it. The + initial value is false. If surface is not a window surface this has no + effect. + +Changes to Chapter 3 of the EGL 1.5 Specification (EGL Functions and Errors) + + Add a new subsection under Section 3, + + "3.13 Composition and Display Timestamps + + The function + + EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface + surface, EGLint framesAgo, EGLint numTimestamps, + const EGLint *timestamps, EGLnsecsANDROID *values); + + allows querying various timestamps related to the composition and display of + a window surface. + + The framesAgo parameter indicates how many frames before the last posted + frame to query. So a value of zero would indicate that the query is for the + last posted frame. Note that the implementation maintains a limited history + of timestamp data. If a query is made for a frame whose timestamp history + no longer exists then EGL_BAD_ACCESS is generated. If timestamp collection + has not been enabled for the surface then EGL_BAD_SURFACE is generated. + Timestamps for events that will not occur or have not yet occurred will be + zero. Timestamp queries that are not supported will generate an + EGL_BAD_PARAMETER error. If any error is generated the function will return + EGL_FALSE. + + The eglGetFrameTimestampsANDROID function takes an array of timestamps to + query and returns timestamps in the corresponding indices of the values + array. The possible timestamps that can be queried are: + - EGL_QUEUE_TIME_ANDROID - The time this frame was queued by the + application. + - EGL_RENDERING_COMPLETE_TIME_ANDROID - The time when all of the + application's rendering to the surface was completed. + - EGL_COMPOSITION_START_TIME_ANDROID - The time at which the compositor + began preparing composition for this frame. + - EGL_COMPOSITION_FINISHED_TIME_ANDROID - The time at which the + compositor's rendering work for this frame finished. This will be zero + if composition was handled by the display and the compositor didn't do + any rendering. + - EGL_DISPLAY_RETIRE_TIME_ANDROID - The time at which this frame was + replaced by the next frame on-screen. + - EGL_READS_DONE_TIME_ANDROID - The time at which all reads for the + purpose of display/composition were completed for this frame. + + Not all implementations may support all off the above timestamp queries. The + function + + EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface + surface, EGLint timestamp); + + allows querying which timestamps are supported on the implementation." + +Issues + + None + +Revision History + +#1 (Pablo Ceballos, May 31, 2016) + - Initial draft. diff --git a/opengl/specs/README b/opengl/specs/README index 8f1eaf3cf6..f0c024ee66 100644 --- a/opengl/specs/README +++ b/opengl/specs/README @@ -19,4 +19,11 @@ for use by Android extensions. 0x314A EGL_IMAGE_CROP_RIGHT_ANDROID (EGL_ANDROID_image_crop) 0x314B EGL_IMAGE_CROP_BOTTOM_ANDROID (EGL_ANDROID_image_crop) 0x314C EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID (EGL_ANDROID_front_buffer_auto_refresh) -0x314D - 0x314F (unused) +0x314D EGL_TIMESTAMPS_ANDROID (EGL_ANDROID_get_frame_timestamps) +0x314E EGL_QUEUE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) +0x314F EGL_RENDERING_COMPLETE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) +0x3430 EGL_COMPOSITION_START_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) +0x3431 EGL_COMPOSITION_FINISHED_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) +0x3432 EGL_DISPLAY_RETIRE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) +0x3433 EGL_READS_DONE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) +0x3434 - 0x343F (unused) diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp index 170d91e86b..4dec34bc93 100644 --- a/services/inputflinger/InputReader.cpp +++ b/services/inputflinger/InputReader.cpp @@ -134,6 +134,14 @@ static const int32_t keyCodeRotationMap[][4] = { { 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_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT, + AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT }, + { AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP, + AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN }, + { AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT, + AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT }, + { AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN, + AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP }, }; static const size_t keyCodeRotationMapSize = sizeof(keyCodeRotationMap) / sizeof(keyCodeRotationMap[0]); diff --git a/services/sensorservice/RecentEventLogger.cpp b/services/sensorservice/RecentEventLogger.cpp index 754b60393d..62e9ce0616 100644 --- a/services/sensorservice/RecentEventLogger.cpp +++ b/services/sensorservice/RecentEventLogger.cpp @@ -31,7 +31,7 @@ namespace { RecentEventLogger::RecentEventLogger(int sensorType) : mSensorType(sensorType), mEventSize(eventSizeBySensorType(mSensorType)), - mRecentEvents(logSizeBySensorType(sensorType)) { + mRecentEvents(logSizeBySensorType(sensorType)), mMaskData(false) { // blank } @@ -60,18 +60,30 @@ std::string RecentEventLogger::dump() const { (int) ns2ms(ev.mWallTime.tv_nsec)); // data - if (mSensorType == SENSOR_TYPE_STEP_COUNTER) { - buffer.appendFormat("%" PRIu64 ", ", ev.mEvent.u64.step_counter); - } else { - for (size_t k = 0; k < mEventSize; ++k) { - buffer.appendFormat("%.2f, ", ev.mEvent.data[k]); + if (!mMaskData) { + if (mSensorType == SENSOR_TYPE_STEP_COUNTER) { + buffer.appendFormat("%" PRIu64 ", ", ev.mEvent.u64.step_counter); + } else { + for (size_t k = 0; k < mEventSize; ++k) { + buffer.appendFormat("%.2f, ", ev.mEvent.data[k]); + } } + } else { + buffer.append("[value masked]"); } buffer.append("\n"); } return std::string(buffer.string()); } +void RecentEventLogger::setFormat(std::string format) { + if (format == "mask_data" ) { + mMaskData = true; + } else { + mMaskData = false; + } +} + bool RecentEventLogger::populateLastEvent(sensors_event_t *event) const { std::lock_guard<std::mutex> lk(mLock); diff --git a/services/sensorservice/RecentEventLogger.h b/services/sensorservice/RecentEventLogger.h index 973a247a63..bf1f655704 100644 --- a/services/sensorservice/RecentEventLogger.h +++ b/services/sensorservice/RecentEventLogger.h @@ -43,6 +43,7 @@ public: // Dumpable interface virtual std::string dump() const override; + virtual void setFormat(std::string format) override; protected: struct SensorEventLog { @@ -57,6 +58,8 @@ protected: mutable std::mutex mLock; RingBuffer<SensorEventLog> mRecentEvents; + bool mMaskData; + private: static size_t logSizeBySensorType(int sensorType); }; diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp index 4fbaa502d2..ac0374232e 100644 --- a/services/sensorservice/SensorDevice.cpp +++ b/services/sensorservice/SensorDevice.cpp @@ -475,6 +475,11 @@ ssize_t SensorDevice::Info::removeBatchParamsForIdent(void* ident) { return idx; } +void SensorDevice::notifyConnectionDestroyed(void* ident) { + Mutex::Autolock _l(mLock); + mDisabledClients.remove(ident); +} + // --------------------------------------------------------------------------- }; // namespace android diff --git a/services/sensorservice/SensorDevice.h b/services/sensorservice/SensorDevice.h index 68bb853ac9..d340da31c1 100644 --- a/services/sensorservice/SensorDevice.h +++ b/services/sensorservice/SensorDevice.h @@ -51,6 +51,7 @@ public: void enableAllSensors(); void autoDisable(void *ident, int handle); status_t injectSensorData(const sensors_event_t *event); + void notifyConnectionDestroyed(void *ident); // Dumpable virtual std::string dump() const; diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp index c1e1badf32..f2f1444125 100644 --- a/services/sensorservice/SensorEventConnection.cpp +++ b/services/sensorservice/SensorEventConnection.cpp @@ -206,7 +206,7 @@ void SensorService::SensorEventConnection::incrementPendingFlushCount(int32_t ha status_t SensorService::SensorEventConnection::sendEvents( sensors_event_t const* buffer, size_t numEvents, sensors_event_t* scratch, - SensorEventConnection const * const * mapFlushEventsToConnections) { + wp<const SensorEventConnection> const * mapFlushEventsToConnections) { // filter out events not for this connection int count = 0; Mutex::Autolock _l(mConnectionLock); @@ -234,7 +234,7 @@ status_t SensorService::SensorEventConnection::sendEvents( FlushInfo& flushInfo = mSensorInfo.editValueAt(index); // Check if there is a pending flush_complete event for this sensor on this connection. if (buffer[i].type == SENSOR_TYPE_META_DATA && flushInfo.mFirstFlushPending == true && - this == mapFlushEventsToConnections[i]) { + mapFlushEventsToConnections[i] == this) { flushInfo.mFirstFlushPending = false; ALOGD_IF(DEBUG_CONNECTIONS, "First flush event for sensor==%d ", buffer[i].meta_data.sensor); @@ -255,7 +255,7 @@ status_t SensorService::SensorEventConnection::sendEvents( // from the same sensor_handle AND the current connection is mapped to the // corresponding flush_complete_event. if (buffer[i].type == SENSOR_TYPE_META_DATA) { - if (this == mapFlushEventsToConnections[i]) { + if (mapFlushEventsToConnections[i] == this) { scratch[count++] = buffer[i]; } ++i; diff --git a/services/sensorservice/SensorEventConnection.h b/services/sensorservice/SensorEventConnection.h index b796cc05a1..883c16e23b 100644 --- a/services/sensorservice/SensorEventConnection.h +++ b/services/sensorservice/SensorEventConnection.h @@ -52,7 +52,7 @@ public: bool isDataInjectionMode, const String16& opPackageName); status_t sendEvents(sensors_event_t const* buffer, size_t count, sensors_event_t* scratch, - SensorEventConnection const * const * mapFlushEventsToConnections = NULL); + wp<const SensorEventConnection> const * mapFlushEventsToConnections = NULL); bool hasSensor(int32_t handle) const; bool hasAnySensor() const; bool hasOneShotSensors() const; diff --git a/services/sensorservice/SensorRecord.cpp b/services/sensorservice/SensorRecord.cpp index 644cfb0de3..53fb9de230 100644 --- a/services/sensorservice/SensorRecord.cpp +++ b/services/sensorservice/SensorRecord.cpp @@ -21,13 +21,13 @@ namespace android { SensorService::SensorRecord::SensorRecord( - const sp<SensorEventConnection>& connection) + const sp<const SensorEventConnection>& connection) { mConnections.add(connection); } bool SensorService::SensorRecord::addConnection( - const sp<SensorEventConnection>& connection) + const sp<const SensorEventConnection>& connection) { if (mConnections.indexOf(connection) < 0) { mConnections.add(connection); @@ -37,16 +37,16 @@ bool SensorService::SensorRecord::addConnection( } bool SensorService::SensorRecord::removeConnection( - const wp<SensorEventConnection>& connection) + const wp<const SensorEventConnection>& connection) { ssize_t index = mConnections.indexOf(connection); if (index >= 0) { mConnections.removeItemsAt(index, 1); } // Remove this connections from the queue of flush() calls made on this sensor. - for (Vector< wp<SensorEventConnection> >::iterator it = mPendingFlushConnections.begin(); + for (Vector< wp<const SensorEventConnection> >::iterator it = mPendingFlushConnections.begin(); it != mPendingFlushConnections.end(); ) { - if (it->unsafe_get() == connection.unsafe_get()) { + if (*it == connection) { it = mPendingFlushConnections.erase(it); } else { ++it; @@ -56,7 +56,7 @@ bool SensorService::SensorRecord::removeConnection( } void SensorService::SensorRecord::addPendingFlushConnection( - const sp<SensorEventConnection>& connection) { + const sp<const SensorEventConnection>& connection) { mPendingFlushConnections.add(connection); } @@ -66,10 +66,10 @@ void SensorService::SensorRecord::removeFirstPendingFlushConnection() { } } -SensorService::SensorEventConnection * +wp<const SensorService::SensorEventConnection> SensorService::SensorRecord::getFirstPendingFlushConnection() { if (mPendingFlushConnections.size() > 0) { - return mPendingFlushConnections[0].unsafe_get(); + return mPendingFlushConnections[0]; } return NULL; } diff --git a/services/sensorservice/SensorRecord.h b/services/sensorservice/SensorRecord.h index 29b970dc1f..5a35410572 100644 --- a/services/sensorservice/SensorRecord.h +++ b/services/sensorservice/SensorRecord.h @@ -25,20 +25,20 @@ class SensorService; class SensorService::SensorRecord { public: - SensorRecord(const sp<SensorEventConnection>& connection); - bool addConnection(const sp<SensorEventConnection>& connection); - bool removeConnection(const wp<SensorEventConnection>& connection); + SensorRecord(const sp<const SensorEventConnection>& connection); + bool addConnection(const sp<const SensorEventConnection>& connection); + bool removeConnection(const wp<const SensorEventConnection>& connection); size_t getNumConnections() const { return mConnections.size(); } - void addPendingFlushConnection(const sp<SensorEventConnection>& connection); + void addPendingFlushConnection(const sp<const SensorEventConnection>& connection); void removeFirstPendingFlushConnection(); - SensorEventConnection * getFirstPendingFlushConnection(); + wp<const SensorEventConnection> getFirstPendingFlushConnection(); void clearAllPendingFlushConnections(); private: - SortedVector< wp<SensorEventConnection> > mConnections; + SortedVector< wp<const SensorEventConnection> > mConnections; // A queue of all flush() calls made on this sensor. Flush complete events // will be sent in this order. - Vector< wp<SensorEventConnection> > mPendingFlushConnections; + Vector< wp<const SensorEventConnection> > mPendingFlushConnections; }; } diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp index 319f4d4ff8..7b47709a82 100644 --- a/services/sensorservice/SensorService.cpp +++ b/services/sensorservice/SensorService.cpp @@ -47,6 +47,7 @@ #include <inttypes.h> #include <math.h> +#include <sched.h> #include <stdint.h> #include <sys/socket.h> #include <sys/stat.h> @@ -71,6 +72,7 @@ bool SensorService::sHmacGlobalKeyIsValid = false; #define SENSOR_SERVICE_DIR "/data/system/sensor_service" #define SENSOR_SERVICE_HMAC_KEY_FILE SENSOR_SERVICE_DIR "/hmac_key" +#define SENSOR_SERVICE_SCHED_FIFO_PRIORITY 10 // Permissions. static const String16 sDump("android.permission.DUMP"); @@ -117,6 +119,15 @@ bool SensorService::initializeHmacKey() { return true; } +// Set main thread to SCHED_FIFO to lower sensor event latency when system is under load +void SensorService::enableSchedFifoMode() { + struct sched_param param = {0}; + param.sched_priority = SENSOR_SERVICE_SCHED_FIFO_PRIORITY; + if (sched_setscheduler(getTid(), SCHED_FIFO | SCHED_RESET_ON_FORK, ¶m) != 0) { + ALOGE("Couldn't set SCHED_FIFO for SensorService thread"); + } +} + void SensorService::onFirstRef() { ALOGD("nuSensorService starting..."); SensorDevice& dev(SensorDevice::getInstance()); @@ -249,7 +260,7 @@ void SensorService::onFirstRef() { const size_t minBufferSize = SensorEventQueue::MAX_RECEIVE_BUFFER_EVENT_COUNT; mSensorEventBuffer = new sensors_event_t[minBufferSize]; mSensorEventScratch = new sensors_event_t[minBufferSize]; - mMapFlushEventsToConnections = new SensorEventConnection const * [minBufferSize]; + mMapFlushEventsToConnections = new wp<const SensorEventConnection> [minBufferSize]; mCurrentOperatingMode = NORMAL; mNextSensorRegIndex = 0; @@ -261,6 +272,9 @@ void SensorService::onFirstRef() { mAckReceiver = new SensorEventAckReceiver(this); mAckReceiver->run("SensorEventAckReceiver", PRIORITY_URGENT_DISPLAY); run("SensorService", PRIORITY_URGENT_DISPLAY); + + // priority can only be changed after run + enableSchedFifoMode(); } } } @@ -308,6 +322,7 @@ status_t SensorService::dump(int fd, const Vector<String16>& args) { IPCThreadState::self()->getCallingPid(), IPCThreadState::self()->getCallingUid()); } else { + bool privileged = IPCThreadState::self()->getCallingUid() == 0; if (args.size() > 2) { return INVALID_OPERATION; } @@ -379,8 +394,12 @@ status_t SensorService::dump(int fd, const Vector<String16>& args) { result.append("Recent Sensor events:\n"); for (auto&& i : mRecentEvent) { sp<SensorInterface> s = mSensors.getInterface(i.first); - if (!i.second->isEmpty() && - s->getSensor().getRequiredPermission().isEmpty()) { + if (!i.second->isEmpty()) { + if (privileged || s->getSensor().getRequiredPermission().isEmpty()) { + i.second->setFormat("normal"); + } else { + i.second->setFormat("mask_data"); + } // if there is events and sensor does not need special permission. result.appendFormat("%s: ", s->getSensor().getName().string()); result.append(i.second->dump().c_str()); @@ -435,15 +454,15 @@ status_t SensorService::dump(int fd, const Vector<String16>& args) { continue; } if (reg_info.mActivated) { - result.appendFormat("%02d:%02d:%02d activated package=%s handle=0x%08x " - "samplingRate=%dus maxReportLatency=%dus\n", - reg_info.mHour, reg_info.mMin, reg_info.mSec, - reg_info.mPackageName.string(), reg_info.mSensorHandle, - reg_info.mSamplingRateUs, reg_info.mMaxReportLatencyUs); + result.appendFormat("%02d:%02d:%02d activated handle=0x%08x " + "samplingRate=%dus maxReportLatency=%dus package=%s\n", + reg_info.mHour, reg_info.mMin, reg_info.mSec, reg_info.mSensorHandle, + reg_info.mSamplingRateUs, reg_info.mMaxReportLatencyUs, + reg_info.mPackageName.string()); } else { - result.appendFormat("%02d:%02d:%02d de-activated package=%s handle=0x%08x\n", + result.appendFormat("%02d:%02d:%02d de-activated handle=0x%08x package=%s\n", reg_info.mHour, reg_info.mMin, reg_info.mSec, - reg_info.mPackageName.string(), reg_info.mSensorHandle); + reg_info.mSensorHandle, reg_info.mPackageName.string()); } currentIndex = (currentIndex - 1 + SENSOR_REGISTRATIONS_BUF_SIZE) % SENSOR_REGISTRATIONS_BUF_SIZE; @@ -978,6 +997,9 @@ void SensorService::cleanupConnection(SensorEventConnection* c) { if (c->needsWakeLock()) { checkWakeLockStateLocked(); } + + SensorDevice& dev(SensorDevice::getInstance()); + dev.notifyConnectionDestroyed(c); } sp<SensorInterface> SensorService::getSensorInterfaceFromHandle(int handle) const { diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h index dc49b06c07..e969d8a9c0 100644 --- a/services/sensorservice/SensorService.h +++ b/services/sensorservice/SensorService.h @@ -53,7 +53,7 @@ // For older HALs which don't support batching, use a smaller socket buffer size. #define SOCKET_BUFFER_SIZE_NON_BATCHED (4 * 1024) -#define SENSOR_REGISTRATIONS_BUF_SIZE 20 +#define SENSOR_REGISTRATIONS_BUF_SIZE 200 namespace android { // --------------------------------------------------------------------------- @@ -215,6 +215,8 @@ private: // Either read from storage or create a new one. static bool initializeHmacKey(); + // Enable SCHED_FIFO priority for thread + void enableSchedFifoMode(); static uint8_t sHmacGlobalKey[128]; static bool sHmacGlobalKeyIsValid; @@ -235,7 +237,7 @@ private: SortedVector< wp<SensorEventConnection> > mActiveConnections; bool mWakeLockAcquired; sensors_event_t *mSensorEventBuffer, *mSensorEventScratch; - SensorEventConnection const **mMapFlushEventsToConnections; + wp<const SensorEventConnection> * mMapFlushEventsToConnections; std::unordered_map<int, RecentEventLogger*> mRecentEvent; Mode mCurrentOperatingMode; diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk index 90c12accaf..170faa8139 100644 --- a/services/surfaceflinger/Android.mk +++ b/services/surfaceflinger/Android.mk @@ -44,7 +44,6 @@ LOCAL_C_INCLUDES := \ LOCAL_CFLAGS := -DLOG_TAG=\"SurfaceFlinger\" LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES -#LOCAL_CFLAGS += -DENABLE_FENCE_TRACKING ifeq ($(TARGET_USES_HWC2),true) LOCAL_CFLAGS += -DUSE_HWC2 @@ -80,18 +79,36 @@ ifeq ($(TARGET_RUNNING_WITHOUT_SYNC_FRAMEWORK),true) LOCAL_CFLAGS += -DRUNNING_WITHOUT_SYNC_FRAMEWORK endif -# See build/target/board/generic/BoardConfig.mk for a description of this setting. +# The following two BoardConfig variables define (respectively): +# +# - The phase offset between hardware vsync and when apps are woken up by the +# Choreographer callback +# - The phase offset between hardware vsync and when SurfaceFlinger wakes up +# to consume input +# +# Their values can be tuned to trade off between display pipeline latency (both +# overall latency and the lengths of the app --> SF and SF --> display phases) +# and frame delivery jitter (which typically manifests as "jank" or "jerkiness" +# while interacting with the device). The default values should produce a +# relatively low amount of jitter at the expense of roughly two frames of +# app --> display latency, and unless significant testing is performed to avoid +# increased display jitter (both manual investigation using systrace [1] and +# automated testing using dumpsys gfxinfo [2] are recommended), they should not +# be modified. +# +# [1] https://developer.android.com/studio/profile/systrace.html +# [2] https://developer.android.com/training/testing/performance.html + ifneq ($(VSYNC_EVENT_PHASE_OFFSET_NS),) LOCAL_CFLAGS += -DVSYNC_EVENT_PHASE_OFFSET_NS=$(VSYNC_EVENT_PHASE_OFFSET_NS) else - LOCAL_CFLAGS += -DVSYNC_EVENT_PHASE_OFFSET_NS=0 + LOCAL_CFLAGS += -DVSYNC_EVENT_PHASE_OFFSET_NS=1000000 endif -# See build/target/board/generic/BoardConfig.mk for a description of this setting. ifneq ($(SF_VSYNC_EVENT_PHASE_OFFSET_NS),) LOCAL_CFLAGS += -DSF_VSYNC_EVENT_PHASE_OFFSET_NS=$(SF_VSYNC_EVENT_PHASE_OFFSET_NS) else - LOCAL_CFLAGS += -DSF_VSYNC_EVENT_PHASE_OFFSET_NS=0 + LOCAL_CFLAGS += -DSF_VSYNC_EVENT_PHASE_OFFSET_NS=1000000 endif ifneq ($(PRESENT_TIME_OFFSET_FROM_VSYNC_NS),) diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp index 3e48cd2fa0..e14a59b46d 100644 --- a/services/surfaceflinger/Client.cpp +++ b/services/surfaceflinger/Client.cpp @@ -170,5 +170,15 @@ status_t Client::getLayerFrameStats(const sp<IBinder>& handle, FrameStats* outSt return NO_ERROR; } +status_t Client::getTransformToDisplayInverse(const sp<IBinder>& handle, + bool* outTransformToDisplayInverse) const { + sp<Layer> layer = getLayerUser(handle); + if (layer == NULL) { + return NAME_NOT_FOUND; + } + *outTransformToDisplayInverse = layer->getTransformToDisplayInverse(); + return NO_ERROR; +} + // --------------------------------------------------------------------------- }; // namespace android diff --git a/services/surfaceflinger/Client.h b/services/surfaceflinger/Client.h index c77651fa43..9c7d05042a 100644 --- a/services/surfaceflinger/Client.h +++ b/services/surfaceflinger/Client.h @@ -63,6 +63,8 @@ private: virtual status_t clearLayerFrameStats(const sp<IBinder>& handle) const; virtual status_t getLayerFrameStats(const sp<IBinder>& handle, FrameStats* outStats) const; + virtual status_t getTransformToDisplayInverse( + const sp<IBinder>& handle, bool* outTransformToDisplayInverse) const; virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags); diff --git a/services/surfaceflinger/DispSync.cpp b/services/surfaceflinger/DispSync.cpp index 37b642015f..1a9820d95e 100644 --- a/services/surfaceflinger/DispSync.cpp +++ b/services/surfaceflinger/DispSync.cpp @@ -383,6 +383,13 @@ DispSync::DispSync(const char* name) : mThread(new DispSyncThread(name)) { mThread->run("DispSync", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE); + // set DispSync to SCHED_FIFO to minimize jitter + struct sched_param param = {0}; + param.sched_priority = 2; + if (sched_setscheduler(mThread->getTid(), SCHED_FIFO, ¶m) != 0) { + ALOGE("Couldn't set SCHED_FIFO for DispSyncThread"); + } + reset(); beginResync(); diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index a67b3ffa22..5c2c0adf3a 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -415,6 +415,17 @@ int DisplayDevice::getActiveConfig() const { } // ---------------------------------------------------------------------------- +#ifdef USE_HWC2 +void DisplayDevice::setActiveColorMode(android_color_mode_t mode) { + mActiveColorMode = mode; +} + +android_color_mode_t DisplayDevice::getActiveColorMode() const { + return mActiveColorMode; +} +#endif + +// ---------------------------------------------------------------------------- void DisplayDevice::setLayerStack(uint32_t stack) { mLayerStack = stack; diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h index dd9b104f4e..105e980ab4 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -182,6 +182,11 @@ public: void setPowerMode(int mode); bool isDisplayOn() const; +#ifdef USE_HWC2 + android_color_mode_t getActiveColorMode() const; + void setActiveColorMode(android_color_mode_t mode); +#endif + /* ------------------------------------------------------------------------ * Display active config management. */ @@ -252,6 +257,10 @@ private: int mPowerMode; // Current active config int mActiveConfig; +#ifdef USE_HWC2 + // current active color mode + android_color_mode_t mActiveColorMode; +#endif }; }; // namespace android diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp index ae6ba98bd5..4fe3cfd03e 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.cpp +++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp @@ -317,9 +317,12 @@ void Device::loadCapabilities() "Capability size has changed"); uint32_t numCapabilities = 0; mHwcDevice->getCapabilities(mHwcDevice, &numCapabilities, nullptr); - mCapabilities.resize(numCapabilities); - auto asInt = reinterpret_cast<int32_t*>(mCapabilities.data()); + std::vector<Capability> capabilities(numCapabilities); + auto asInt = reinterpret_cast<int32_t*>(capabilities.data()); mHwcDevice->getCapabilities(mHwcDevice, &numCapabilities, asInt); + for (auto capability : capabilities) { + mCapabilities.emplace(capability); + } } bool Device::hasCapability(HWC2::Capability capability) const @@ -578,7 +581,7 @@ Error Display::getChangedCompositionTypes( return Error::None; } -Error Display::getColorModes(std::vector<int32_t>* outModes) const +Error Display::getColorModes(std::vector<android_color_mode_t>* outModes) const { uint32_t numModes = 0; int32_t intError = mDevice.mGetColorModes(mDevice.mHwcDevice, mId, @@ -596,7 +599,10 @@ Error Display::getColorModes(std::vector<int32_t>* outModes) const return error; } - std::swap(*outModes, modes); + outModes->resize(numModes); + for (size_t i = 0; i < numModes; i++) { + (*outModes)[i] = static_cast<android_color_mode_t>(modes[i]); + } return Error::None; } @@ -802,7 +808,7 @@ Error Display::setClientTarget(buffer_handle_t target, return static_cast<Error>(intError); } -Error Display::setColorMode(int32_t mode) +Error Display::setColorMode(android_color_mode_t mode) { int32_t intError = mDevice.mSetColorMode(mDevice.mHwcDevice, mId, mode); return static_cast<Error>(intError); diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h index 25a7d89c8c..32a9de0018 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.h +++ b/services/surfaceflinger/DisplayHardware/HWC2.h @@ -33,6 +33,7 @@ #include <functional> #include <string> #include <unordered_map> +#include <unordered_set> #include <vector> namespace android { @@ -66,7 +67,7 @@ public: std::string dump() const; - const std::vector<Capability>& getCapabilities() const { + const std::unordered_set<Capability>& getCapabilities() const { return mCapabilities; }; @@ -181,7 +182,7 @@ private: HWC2_PFN_SET_LAYER_VISIBLE_REGION mSetLayerVisibleRegion; HWC2_PFN_SET_LAYER_Z_ORDER mSetLayerZOrder; - std::vector<Capability> mCapabilities; + std::unordered_set<Capability> mCapabilities; std::unordered_map<hwc2_display_t, std::weak_ptr<Display>> mDisplays; HotplugCallback mHotplug; @@ -281,7 +282,7 @@ public: [[clang::warn_unused_result]] Error getChangedCompositionTypes( std::unordered_map<std::shared_ptr<Layer>, Composition>* outTypes); [[clang::warn_unused_result]] Error getColorModes( - std::vector<int32_t>* outModes) const; + std::vector<android_color_mode_t>* outModes) const; // Doesn't call into the HWC2 device, so no errors are possible std::vector<std::shared_ptr<const Config>> getConfigs() const; @@ -306,7 +307,7 @@ public: buffer_handle_t target, const android::sp<android::Fence>& acquireFence, android_dataspace_t dataspace); - [[clang::warn_unused_result]] Error setColorMode(int32_t mode); + [[clang::warn_unused_result]] Error setColorMode(android_color_mode_t mode); [[clang::warn_unused_result]] Error setColorTransform( const android::mat4& matrix, android_color_transform_t hint); [[clang::warn_unused_result]] Error setOutputBuffer( diff --git a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp index 2641ee6261..cc0dfb0945 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp +++ b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp @@ -75,7 +75,7 @@ static hwc2_function_pointer_t asFP(T function) using namespace HWC2; -static constexpr Attribute ColorTransform = static_cast<Attribute>(6); +static constexpr Attribute ColorMode = static_cast<Attribute>(6); namespace android { @@ -268,9 +268,7 @@ hwc2_function_pointer_t HWC2On1Adapter::doGetFunction( &Display::setClientTarget, buffer_handle_t, int32_t, int32_t, hwc_region_t>); case FunctionDescriptor::SetColorMode: - return asFP<HWC2_PFN_SET_COLOR_MODE>( - displayHook<decltype(&Display::setColorMode), - &Display::setColorMode, int32_t>); + return asFP<HWC2_PFN_SET_COLOR_MODE>(setColorModeHook); case FunctionDescriptor::SetColorTransform: return asFP<HWC2_PFN_SET_COLOR_TRANSFORM>(setColorTransformHook); case FunctionDescriptor::SetOutputBuffer: @@ -894,7 +892,7 @@ Error HWC2On1Adapter::Display::setClientTarget(buffer_handle_t target, return Error::None; } -Error HWC2On1Adapter::Display::setColorMode(int32_t mode) +Error HWC2On1Adapter::Display::setColorMode(android_color_mode_t mode) { std::unique_lock<std::recursive_mutex> lock (mStateMutex); @@ -1198,11 +1196,14 @@ void HWC2On1Adapter::Display::populateConfigs() newConfig->setAttribute(Attribute::DpiY, values[attributeMap[HWC_DISPLAY_DPI_Y]]); if (hasColor) { - newConfig->setAttribute(ColorTransform, + // In HWC1, color modes are referred to as color transforms. To avoid confusion with + // the HWC2 concept of color transforms, we internally refer to them as color modes for + // both HWC1 and 2. + newConfig->setAttribute(ColorMode, values[attributeMap[HWC_DISPLAY_COLOR_TRANSFORM]]); } - // We can only do this after attempting to read the color transform + // We can only do this after attempting to read the color mode newConfig->setHwc1Id(hwc1ConfigId); for (auto& existingConfig : mConfigs) { @@ -1678,8 +1679,8 @@ int32_t HWC2On1Adapter::Display::Config::getAttribute(Attribute attribute) const void HWC2On1Adapter::Display::Config::setHwc1Id(uint32_t id) { - int32_t colorTransform = getAttribute(ColorTransform); - mHwc1Ids.emplace(colorTransform, id); + android_color_mode_t colorMode = static_cast<android_color_mode_t>(getAttribute(ColorMode)); + mHwc1Ids.emplace(colorMode, id); } bool HWC2On1Adapter::Display::Config::hasHwc1Id(uint32_t id) const @@ -1692,18 +1693,20 @@ bool HWC2On1Adapter::Display::Config::hasHwc1Id(uint32_t id) const return false; } -int32_t HWC2On1Adapter::Display::Config::getColorModeForHwc1Id( - uint32_t id) const +Error HWC2On1Adapter::Display::Config::getColorModeForHwc1Id( + uint32_t id, android_color_mode_t* outMode) const { for (const auto& idPair : mHwc1Ids) { if (id == idPair.second) { - return idPair.first; + *outMode = idPair.first; + return Error::None; } } - return -1; + ALOGE("Unable to find color mode for HWC ID %" PRIu32 " on config %u", id, mId); + return Error::BadParameter; } -Error HWC2On1Adapter::Display::Config::getHwc1IdForColorMode(int32_t mode, +Error HWC2On1Adapter::Display::Config::getHwc1IdForColorMode(android_color_mode_t mode, uint32_t* outId) const { for (const auto& idPair : mHwc1Ids) { @@ -1726,25 +1729,26 @@ bool HWC2On1Adapter::Display::Config::merge(const Config& other) return false; } } - int32_t otherColorTransform = other.getAttribute(ColorTransform); - if (mHwc1Ids.count(otherColorTransform) != 0) { + android_color_mode_t otherColorMode = + static_cast<android_color_mode_t>(other.getAttribute(ColorMode)); + if (mHwc1Ids.count(otherColorMode) != 0) { ALOGE("Attempted to merge two configs (%u and %u) which appear to be " - "identical", mHwc1Ids.at(otherColorTransform), - other.mHwc1Ids.at(otherColorTransform)); + "identical", mHwc1Ids.at(otherColorMode), + other.mHwc1Ids.at(otherColorMode)); return false; } - mHwc1Ids.emplace(otherColorTransform, - other.mHwc1Ids.at(otherColorTransform)); + mHwc1Ids.emplace(otherColorMode, + other.mHwc1Ids.at(otherColorMode)); return true; } -std::set<int32_t> HWC2On1Adapter::Display::Config::getColorTransforms() const +std::set<android_color_mode_t> HWC2On1Adapter::Display::Config::getColorModes() const { - std::set<int32_t> colorTransforms; + std::set<android_color_mode_t> colorModes; for (const auto& idPair : mHwc1Ids) { - colorTransforms.emplace(idPair.first); + colorModes.emplace(idPair.first); } - return colorTransforms; + return colorModes; } std::string HWC2On1Adapter::Display::Config::toString(bool splitLine) const @@ -1787,15 +1791,15 @@ std::string HWC2On1Adapter::Display::Config::toString(bool splitLine) const for (const auto& id : mHwc1Ids) { - int32_t colorTransform = id.first; + android_color_mode_t colorMode = id.first; uint32_t hwc1Id = id.second; std::memset(buffer, 0, BUFFER_SIZE); - if (colorTransform == mDisplay.mActiveColorMode) { + if (colorMode == mDisplay.mActiveColorMode) { writtenBytes = snprintf(buffer, BUFFER_SIZE, " [%u/%d]", hwc1Id, - colorTransform); + colorMode); } else { writtenBytes = snprintf(buffer, BUFFER_SIZE, " %u/%d", hwc1Id, - colorTransform); + colorMode); } output.append(buffer, writtenBytes); } @@ -1814,10 +1818,10 @@ std::shared_ptr<const HWC2On1Adapter::Display::Config> void HWC2On1Adapter::Display::populateColorModes() { - mColorModes = mConfigs[0]->getColorTransforms(); + mColorModes = mConfigs[0]->getColorModes(); for (const auto& config : mConfigs) { - std::set<int32_t> intersection; - auto configModes = config->getColorTransforms(); + std::set<android_color_mode_t> intersection; + auto configModes = config->getColorModes(); std::set_intersection(mColorModes.cbegin(), mColorModes.cend(), configModes.cbegin(), configModes.cend(), std::inserter(intersection, intersection.begin())); @@ -1830,7 +1834,7 @@ void HWC2On1Adapter::Display::initializeActiveConfig() if (mDevice.mHwc1Device->getActiveConfig == nullptr) { ALOGV("getActiveConfig is null, choosing config 0"); mActiveConfig = mConfigs[0]; - mActiveColorMode = -1; + mActiveColorMode = HAL_COLOR_MODE_NATIVE; return; } @@ -1842,7 +1846,13 @@ void HWC2On1Adapter::Display::initializeActiveConfig() ALOGV("Setting active config to %d for HWC1 config %u", config->getId(), activeConfig); mActiveConfig = config; - mActiveColorMode = config->getColorModeForHwc1Id(activeConfig); + if (config->getColorModeForHwc1Id(activeConfig, &mActiveColorMode) != Error::None) { + // This should never happen since we checked for the config's presence before + // setting it as active. + ALOGE("Unable to find color mode for active HWC1 config %d", + config->getId()); + mActiveColorMode = HAL_COLOR_MODE_NATIVE; + } break; } } @@ -1850,7 +1860,7 @@ void HWC2On1Adapter::Display::initializeActiveConfig() ALOGV("Unable to find active HWC1 config %u, defaulting to " "config 0", activeConfig); mActiveConfig = mConfigs[0]; - mActiveColorMode = -1; + mActiveColorMode = HAL_COLOR_MODE_NATIVE; } } } @@ -2295,7 +2305,14 @@ void HWC2On1Adapter::Layer::applyCompositionType(hwc_layer_1_t& hwc1Layer, hwc1Layer.compositionType = HWC_FRAMEBUFFER; break; case Composition::SolidColor: - hwc1Layer.compositionType = HWC_BACKGROUND; + // In theory the following line should work, but since the HWC1 + // version of SurfaceFlinger never used HWC_BACKGROUND, HWC1 + // devices may not work correctly. To be on the safe side, we + // fall back to client composition. + // + // hwc1Layer.compositionType = HWC_BACKGROUND; + hwc1Layer.compositionType = HWC_FRAMEBUFFER; + hwc1Layer.flags |= HWC_SKIP_LAYER; break; case Composition::Cursor: hwc1Layer.compositionType = HWC_FRAMEBUFFER; @@ -2333,7 +2350,7 @@ void HWC2On1Adapter::populateCapabilities() int supportedTypes = 0; auto result = mHwc1Device->query(mHwc1Device, HWC_DISPLAY_TYPES_SUPPORTED, &supportedTypes); - if ((result == 0) && ((supportedTypes & HWC_DISPLAY_VIRTUAL) != 0)) { + if ((result == 0) && ((supportedTypes & HWC_DISPLAY_VIRTUAL_BIT) != 0)) { ALOGI("Found support for HWC virtual displays"); mHwc1SupportsVirtualDisplays = true; } diff --git a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h index 5debb9ad3a..962361ec6e 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h +++ b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h @@ -213,7 +213,7 @@ private: HWC2::Error setClientTarget(buffer_handle_t target, int32_t acquireFence, int32_t dataspace, hwc_region_t damage); - HWC2::Error setColorMode(int32_t mode); + HWC2::Error setColorMode(android_color_mode_t mode); HWC2::Error setColorTransform(android_color_transform_t hint); HWC2::Error setOutputBuffer(buffer_handle_t buffer, int32_t releaseFence); @@ -258,8 +258,9 @@ private: void setHwc1Id(uint32_t id); bool hasHwc1Id(uint32_t id) const; - int32_t getColorModeForHwc1Id(uint32_t id) const; - HWC2::Error getHwc1IdForColorMode(int32_t mode, + HWC2::Error getColorModeForHwc1Id(uint32_t id, + android_color_mode_t *outMode) const; + HWC2::Error getHwc1IdForColorMode(android_color_mode_t mode, uint32_t* outId) const; void setId(hwc2_config_t id) { mId = id; } @@ -269,7 +270,7 @@ private: // mode. Returns whether the merge was successful bool merge(const Config& other); - std::set<int32_t> getColorTransforms() const; + std::set<android_color_mode_t> getColorModes() const; // splitLine divides the output into two lines suitable for // dumpsys SurfaceFlinger @@ -281,7 +282,7 @@ private: std::unordered_map<HWC2::Attribute, int32_t> mAttributes; // Maps from color transform to HWC1 config ID - std::unordered_map<int32_t, uint32_t> mHwc1Ids; + std::unordered_map<android_color_mode_t, uint32_t> mHwc1Ids; }; class Changes { @@ -378,8 +379,8 @@ private: std::vector<std::shared_ptr<Config>> mConfigs; std::shared_ptr<const Config> mActiveConfig; - std::set<int32_t> mColorModes; - int32_t mActiveColorMode; + std::set<android_color_mode_t> mColorModes; + android_color_mode_t mActiveColorMode; std::string mName; HWC2::DisplayType mType; HWC2::PowerMode mPowerMode; @@ -432,6 +433,12 @@ private: hint); } + static int32_t setColorModeHook(hwc2_device_t* device, + hwc2_display_t display, int32_t /*android_color_mode_t*/ intMode) { + auto mode = static_cast<android_color_mode_t>(intMode); + return callDisplayFunction(device, display, &Display::setColorMode, mode); + } + static int32_t setPowerModeHook(hwc2_device_t* device, hwc2_display_t display, int32_t intMode) { auto mode = static_cast<HWC2::PowerMode>(intMode); diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index 216bbc9e55..c87ba7233e 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -144,6 +144,11 @@ void HWComposer::loadHwcModule() mRemainingHwcVirtualDisplays = mHwcDevice->getMaxVirtualDisplayCount(); } +bool HWComposer::hasCapability(HWC2::Capability capability) const +{ + return mHwcDevice->getCapabilities().count(capability) > 0; +} + bool HWComposer::isValidDisplay(int32_t displayId) const { return static_cast<size_t>(displayId) < mDisplayData.size() && mDisplayData[displayId].hwcDisplay; @@ -359,6 +364,46 @@ std::shared_ptr<const HWC2::Display::Config> return config; } +std::vector<android_color_mode_t> HWComposer::getColorModes(int32_t displayId) const { + std::vector<android_color_mode_t> modes; + + if (!isValidDisplay(displayId)) { + ALOGE("getColorModes: Attempted to access invalid display %d", + displayId); + return modes; + } + const std::shared_ptr<HWC2::Display>& hwcDisplay = + mDisplayData[displayId].hwcDisplay; + + auto error = hwcDisplay->getColorModes(&modes); + if (error != HWC2::Error::None) { + ALOGE("getColorModes failed for display %d: %s (%d)", displayId, + to_string(error).c_str(), static_cast<int32_t>(error)); + return std::vector<android_color_mode_t>(); + } + + return modes; +} + +status_t HWComposer::setActiveColorMode(int32_t displayId, android_color_mode_t mode) { + if (!isValidDisplay(displayId)) { + ALOGE("setActiveColorMode: Display %d is not valid", displayId); + return BAD_INDEX; + } + + auto& displayData = mDisplayData[displayId]; + auto error = displayData.hwcDisplay->setColorMode(mode); + if (error != HWC2::Error::None) { + ALOGE("setActiveConfig: Failed to set color mode %d on display %d: " + "%s (%d)", mode, displayId, to_string(error).c_str(), + static_cast<int32_t>(error)); + return UNKNOWN_ERROR; + } + + return NO_ERROR; +} + + void HWComposer::setVsyncEnabled(int32_t disp, HWC2::Vsync enabled) { if (disp < 0 || disp >= HWC_DISPLAY_VIRTUAL) { ALOGD("setVsyncEnabled: Ignoring for virtual display %d", disp); @@ -685,6 +730,28 @@ status_t HWComposer::setActiveConfig(int32_t displayId, size_t configId) { return NO_ERROR; } +status_t HWComposer::setColorTransform(int32_t displayId, + const mat4& transform) { + if (!isValidDisplay(displayId)) { + ALOGE("setColorTransform: Display %d is not valid", displayId); + return BAD_INDEX; + } + + auto& displayData = mDisplayData[displayId]; + bool isIdentity = transform == mat4(); + auto error = displayData.hwcDisplay->setColorTransform(transform, + isIdentity ? HAL_COLOR_TRANSFORM_IDENTITY : + HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX); + if (error != HWC2::Error::None) { + ALOGE("setColorTransform: Failed to set transform on display %d: " + "%s (%d)", displayId, to_string(error).c_str(), + static_cast<int32_t>(error)); + return UNKNOWN_ERROR; + } + + return NO_ERROR; +} + void HWComposer::disconnectDisplay(int displayId) { LOG_ALWAYS_FATAL_IF(displayId < 0); auto& displayData = mDisplayData[displayId]; diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index b88e2501fe..41671f62fd 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -81,6 +81,8 @@ public: void setEventHandler(EventHandler* handler); + bool hasCapability(HWC2::Capability capability) const; + // Attempts to allocate a virtual display. If the virtual display is created // on the HWC device, outId will contain its HWC ID. status_t allocateVirtualDisplay(uint32_t width, uint32_t height, @@ -104,6 +106,9 @@ public: // set active config status_t setActiveConfig(int32_t displayId, size_t configId); + // Sets a color transform to be applied to the result of composition + status_t setColorTransform(int32_t displayId, const mat4& transform); + // reset state when an external, non-virtual display is disconnected void disconnectDisplay(int32_t displayId); @@ -137,15 +142,6 @@ public: void setVsyncEnabled(int32_t disp, HWC2::Vsync enabled); - struct DisplayConfig { - uint32_t width; - uint32_t height; - float xdpi; - float ydpi; - nsecs_t refresh; - int colorTransform; - }; - // Query display parameters. Pass in a display index (e.g. // HWC_DISPLAY_PRIMARY). nsecs_t getRefreshTimestamp(int32_t disp) const; @@ -158,6 +154,10 @@ public: std::shared_ptr<const HWC2::Display::Config> getActiveConfig(int32_t displayId) const; + std::vector<android_color_mode_t> getColorModes(int32_t displayId) const; + + status_t setActiveColorMode(int32_t displayId, android_color_mode_t mode); + // for debugging ---------------------------------------------------------- void dump(String8& out) const; diff --git a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp index 4afd8a2dce..ef416581a2 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp @@ -42,6 +42,8 @@ #include <cutils/log.h> #include <cutils/properties.h> +#include <system/graphics.h> + #include "HWComposer.h" #include "../Layer.h" // needed only for debugging @@ -403,7 +405,7 @@ status_t HWComposer::queryDisplayProperties(int disp) { config.ydpi = values[i] / 1000.0f; break; case HWC_DISPLAY_COLOR_TRANSFORM: - config.colorTransform = values[i]; + config.colorMode = static_cast<android_color_mode_t>(values[i]); break; default: ALOG_ASSERT(false, "unknown display attribute[%zu] %#x", @@ -519,6 +521,11 @@ nsecs_t HWComposer::getRefreshPeriod(int disp) const { return mDisplayData[disp].configs[currentConfig].refresh; } +android_color_mode_t HWComposer::getColorMode(int disp) const { + size_t currentConfig = mDisplayData[disp].currentConfig; + return mDisplayData[disp].configs[currentConfig].colorMode; +} + const Vector<HWComposer::DisplayConfig>& HWComposer::getConfigs(int disp) const { return mDisplayData[disp].configs; } @@ -1182,10 +1189,10 @@ void HWComposer::dump(String8& result) const { for (size_t c = 0; c < disp.configs.size(); ++c) { const DisplayConfig& config(disp.configs[c]); result.appendFormat(" %s%zd: %ux%u, xdpi=%f, ydpi=%f" - ", refresh=%" PRId64 ", colorTransform=%d\n", + ", refresh=%" PRId64 ", colorMode=%d\n", c == disp.currentConfig ? "* " : "", c, config.width, config.height, config.xdpi, config.ydpi, - config.refresh, config.colorTransform); + config.refresh, config.colorMode); } if (disp.list) { diff --git a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h index c86181781b..170e382330 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h @@ -22,6 +22,8 @@ #include <hardware/hwcomposer_defs.h> +#include <system/graphics.h> + #include <ui/Fence.h> #include <utils/BitSet.h> @@ -257,7 +259,15 @@ public: float xdpi; float ydpi; nsecs_t refresh; - int colorTransform; + android_color_mode_t colorMode; + bool operator==(const DisplayConfig& rhs) const { + return width == rhs.width && + height == rhs.height && + xdpi == rhs.xdpi && + ydpi == rhs.ydpi && + refresh == rhs.refresh && + colorMode == rhs.colorMode; + } }; // Query display parameters. Pass in a display index (e.g. @@ -274,6 +284,7 @@ public: float getDpiX(int disp) const; float getDpiY(int disp) const; nsecs_t getRefreshPeriod(int disp) const; + android_color_mode_t getColorMode(int disp) const; const Vector<DisplayConfig>& getConfigs(int disp) const; size_t getCurrentConfig(int disp) const; diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp index bc8dfbb5b6..2190466ad9 100644 --- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp +++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp @@ -304,8 +304,11 @@ void VirtualDisplaySurface::dumpAsString(String8& /* result */) const { void VirtualDisplaySurface::resizeBuffers(const uint32_t w, const uint32_t h) { uint32_t tmpW, tmpH, transformHint, numPendingBuffers; - mQueueBufferOutput.deflate(&tmpW, &tmpH, &transformHint, &numPendingBuffers); - mQueueBufferOutput.inflate(w, h, transformHint, numPendingBuffers); + uint64_t nextFrameNumber; + mQueueBufferOutput.deflate(&tmpW, &tmpH, &transformHint, &numPendingBuffers, + &nextFrameNumber); + mQueueBufferOutput.inflate(w, h, transformHint, numPendingBuffers, + nextFrameNumber); mSinkBufferWidth = w; mSinkBufferHeight = h; @@ -560,8 +563,8 @@ status_t VirtualDisplaySurface::connect(const sp<IProducerListener>& listener, return result; } -status_t VirtualDisplaySurface::disconnect(int api) { - return mSource[SOURCE_SINK]->disconnect(api); +status_t VirtualDisplaySurface::disconnect(int api, DisconnectMode mode) { + return mSource[SOURCE_SINK]->disconnect(api, mode); } status_t VirtualDisplaySurface::setSidebandStream(const sp<NativeHandle>& /*stream*/) { @@ -586,10 +589,6 @@ String8 VirtualDisplaySurface::getConsumerName() const { return String8("VirtualDisplaySurface"); } -uint64_t VirtualDisplaySurface::getNextFrameNumber() const { - return 0; -} - status_t VirtualDisplaySurface::setSharedBufferMode(bool /*sharedBufferMode*/) { ALOGE("setSharedBufferMode not supported on VirtualDisplaySurface"); return INVALID_OPERATION; @@ -612,11 +611,17 @@ status_t VirtualDisplaySurface::getLastQueuedBuffer( return INVALID_OPERATION; } +status_t VirtualDisplaySurface::getUniqueId(uint64_t* /*outId*/) const { + ALOGE("getUniqueId not supported on VirtualDisplaySurface"); + return INVALID_OPERATION; +} + void VirtualDisplaySurface::updateQueueBufferOutput( const QueueBufferOutput& qbo) { uint32_t w, h, transformHint, numPendingBuffers; - qbo.deflate(&w, &h, &transformHint, &numPendingBuffers); - mQueueBufferOutput.inflate(w, h, 0, numPendingBuffers); + uint64_t nextFrameNumber; + qbo.deflate(&w, &h, &transformHint, &numPendingBuffers, &nextFrameNumber); + mQueueBufferOutput.inflate(w, h, 0, numPendingBuffers, nextFrameNumber); } void VirtualDisplaySurface::resetPerFrameState() { diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h index 29563b6a06..70f717f8c2 100644 --- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h +++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h @@ -115,19 +115,19 @@ private: virtual int query(int what, int* value); virtual status_t connect(const sp<IProducerListener>& listener, int api, bool producerControlledByApp, QueueBufferOutput* output); - virtual status_t disconnect(int api); + virtual status_t disconnect(int api, DisconnectMode mode); virtual status_t setSidebandStream(const sp<NativeHandle>& stream); virtual void allocateBuffers(uint32_t width, uint32_t height, PixelFormat format, uint32_t usage); virtual status_t allowAllocation(bool allow); virtual status_t setGenerationNumber(uint32_t generationNumber); virtual String8 getConsumerName() const override; - virtual uint64_t getNextFrameNumber() const override; virtual status_t setSharedBufferMode(bool sharedBufferMode) override; virtual status_t setAutoRefresh(bool autoRefresh) override; virtual status_t setDequeueTimeout(nsecs_t timeout) override; virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence, float outTransformMatrix[16]) override; + virtual status_t getUniqueId(uint64_t* outId) const override; // // Utility methods diff --git a/services/surfaceflinger/Effects/Daltonizer.cpp b/services/surfaceflinger/Effects/Daltonizer.cpp index feb8936cdf..a104e8f780 100644 --- a/services/surfaceflinger/Effects/Daltonizer.cpp +++ b/services/surfaceflinger/Effects/Daltonizer.cpp @@ -19,21 +19,14 @@ namespace android { -Daltonizer::Daltonizer() : - mType(deuteranomaly), mMode(simulation), mDirty(true) { -} - -Daltonizer::~Daltonizer() { -} - -void Daltonizer::setType(Daltonizer::ColorBlindnessTypes type) { +void Daltonizer::setType(ColorBlindnessType type) { if (type != mType) { mDirty = true; mType = type; } } -void Daltonizer::setMode(Daltonizer::Mode mode) { +void Daltonizer::setMode(ColorBlindnessMode mode) { if (mode != mMode) { mDirty = true; mMode = mode; @@ -49,6 +42,11 @@ const mat4& Daltonizer::operator()() { } void Daltonizer::update() { + if (mType == ColorBlindnessType::None) { + mColorTransform = mat4(); + return; + } + // converts a linear RGB color to the XYZ space const mat4 rgb2xyz( 0.4124, 0.2126, 0.0193, 0, 0.3576, 0.7152, 0.1192, 0, @@ -149,24 +147,25 @@ void Daltonizer::update() { mat4 correction(0); switch (mType) { - case protanopia: - case protanomaly: + case ColorBlindnessType::Protanomaly: simulation = lms2lmsp; - if (mMode == Daltonizer::correction) + if (mMode == ColorBlindnessMode::Correction) correction = errp; break; - case deuteranopia: - case deuteranomaly: + case ColorBlindnessType::Deuteranomaly: simulation = lms2lmsd; - if (mMode == Daltonizer::correction) + if (mMode == ColorBlindnessMode::Correction) correction = errd; break; - case tritanopia: - case tritanomaly: + case ColorBlindnessType::Tritanomaly: simulation = lms2lmst; - if (mMode == Daltonizer::correction) + if (mMode == ColorBlindnessMode::Correction) correction = errt; break; + case ColorBlindnessType::None: + // We already caught this at the beginning of the method, but the + // compiler doesn't know that + break; } mColorTransform = lms2rgb * diff --git a/services/surfaceflinger/Effects/Daltonizer.h b/services/surfaceflinger/Effects/Daltonizer.h index e81643747d..d21b155f85 100644 --- a/services/surfaceflinger/Effects/Daltonizer.h +++ b/services/surfaceflinger/Effects/Daltonizer.h @@ -21,27 +21,22 @@ namespace android { +enum class ColorBlindnessType { + None, // Disables the Daltonizer + Protanomaly, // L (red) cone deficient + Deuteranomaly, // M (green) cone deficient (most common) + Tritanomaly // S (blue) cone deficient +}; + +enum class ColorBlindnessMode { + Simulation, + Correction +}; + class Daltonizer { public: - enum ColorBlindnessTypes { - protanopia, // L (red) cone missing - deuteranopia, // M (green) cone missing - tritanopia, // S (blue) cone missing - protanomaly, // L (red) cone deficient - deuteranomaly, // M (green) cone deficient (most common) - tritanomaly // S (blue) cone deficient - }; - - enum Mode { - simulation, - correction - }; - - Daltonizer(); - ~Daltonizer(); - - void setType(ColorBlindnessTypes type); - void setMode(Mode mode); + void setType(ColorBlindnessType type); + void setMode(ColorBlindnessMode mode); // returns the color transform to apply in the shader const mat4& operator()(); @@ -49,9 +44,9 @@ public: private: void update(); - ColorBlindnessTypes mType; - Mode mMode; - bool mDirty; + ColorBlindnessType mType = ColorBlindnessType::None; + ColorBlindnessMode mMode = ColorBlindnessMode::Simulation; + bool mDirty = true; mat4 mColorTransform; }; diff --git a/services/surfaceflinger/FenceTracker.cpp b/services/surfaceflinger/FenceTracker.cpp index 885d712530..0e18a937e4 100644 --- a/services/surfaceflinger/FenceTracker.cpp +++ b/services/surfaceflinger/FenceTracker.cpp @@ -26,7 +26,9 @@ namespace android { FenceTracker::FenceTracker() : mFrameCounter(0), mOffset(0), - mFrames() {} + mFrames(), + mMutex() { +} void FenceTracker::dump(String8* outString) { Mutex::Autolock lock(mMutex); @@ -135,31 +137,30 @@ void FenceTracker::addFrame(nsecs_t refreshStartTime, sp<Fence> retireFence, 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); #ifdef USE_HWC2 if (glesComposition) { 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)); wasGlesCompositionDone = true; } 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, Fence::NO_FENCE)); - - auto prevLayer = prevFrame.layers.find(key); + auto prevLayer = prevFrame.layers.find(layerId); if (prevLayer != prevFrame.layers.end()) { prevLayer->second.releaseFence = prevReleaseFence; } } #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 +169,7 @@ void FenceTracker::addFrame(nsecs_t refreshStartTime, sp<Fence> retireFence, } #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 +185,35 @@ void FenceTracker::addFrame(nsecs_t refreshStartTime, sp<Fence> retireFence, 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 de99820370..4cb14a539d 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 @@ public: 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 89e37b2f37..d13b6dbe19 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -51,6 +51,8 @@ #include "RenderEngine/RenderEngine.h" +#include <mutex> + #define DEBUG_RESIZE 0 namespace android { @@ -155,7 +157,8 @@ void Layer::onFirstRef() { 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); @@ -554,7 +557,7 @@ void Layer::setGeometry( #endif activeCrop.clear(); } - activeCrop = s.active.transform.inverse().transform(activeCrop); + activeCrop = s.active.transform.inverse().transform(activeCrop, true); // This needs to be here as transform.transform(Rect) computes the // transformed rect and then takes the bounding box of the result before // returning. This means @@ -586,19 +589,25 @@ void Layer::setGeometry( const Transform& tr(displayDevice->getTransform()); Rect transformedFrame = tr.transform(frame); auto error = hwcLayer->setDisplayFrame(transformedFrame); - ALOGE_IF(error != HWC2::Error::None, "[%s] Failed to set display frame " - "[%d, %d, %d, %d]: %s (%d)", mName.string(), transformedFrame.left, - transformedFrame.top, transformedFrame.right, - transformedFrame.bottom, to_string(error).c_str(), - static_cast<int32_t>(error)); + if (error != HWC2::Error::None) { + ALOGE("[%s] Failed to set display frame [%d, %d, %d, %d]: %s (%d)", + mName.string(), transformedFrame.left, transformedFrame.top, + transformedFrame.right, transformedFrame.bottom, + to_string(error).c_str(), static_cast<int32_t>(error)); + } else { + hwcInfo.displayFrame = transformedFrame; + } FloatRect sourceCrop = computeCrop(displayDevice); error = hwcLayer->setSourceCrop(sourceCrop); - ALOGE_IF(error != HWC2::Error::None, "[%s] Failed to set source crop " - "[%.3f, %.3f, %.3f, %.3f]: %s (%d)", mName.string(), - sourceCrop.left, sourceCrop.top, sourceCrop.right, - sourceCrop.bottom, to_string(error).c_str(), - static_cast<int32_t>(error)); + if (error != HWC2::Error::None) { + ALOGE("[%s] Failed to set source crop [%.3f, %.3f, %.3f, %.3f]: " + "%s (%d)", mName.string(), sourceCrop.left, sourceCrop.top, + sourceCrop.right, sourceCrop.bottom, to_string(error).c_str(), + static_cast<int32_t>(error)); + } else { + hwcInfo.sourceCrop = sourceCrop; + } error = hwcLayer->setPlaneAlpha(s.alpha); ALOGE_IF(error != HWC2::Error::None, "[%s] Failed to set plane alpha %.3f: " @@ -1113,6 +1122,20 @@ uint32_t Layer::getProducerStickyTransform() const { return static_cast<uint32_t>(producerStickyTransform); } +bool Layer::latchUnsignaledBuffers() { + static bool propertyLoaded = false; + static bool latch = false; + static std::mutex mutex; + std::lock_guard<std::mutex> lock(mutex); + if (!propertyLoaded) { + char value[PROPERTY_VALUE_MAX] = {}; + property_get("debug.sf.latch_unsignaled", value, "0"); + latch = atoi(value); + propertyLoaded = true; + } + return latch; +} + uint64_t Layer::getHeadFrameNumber() const { Mutex::Autolock lock(mQueueItemLock); if (!mQueueItems.empty()) { @@ -1122,6 +1145,29 @@ uint64_t Layer::getHeadFrameNumber() const { } } +bool Layer::headFenceHasSignaled() const { +#ifdef USE_HWC2 + if (latchUnsignaledBuffers()) { + return true; + } + + Mutex::Autolock lock(mQueueItemLock); + if (mQueueItems.empty()) { + return true; + } + if (mQueueItems[0].mIsDroppable) { + // Even though this buffer's fence may not have signaled yet, it could + // be replaced by another buffer before it has a chance to, which means + // that it's possible to get into a situation where a buffer is never + // able to be latched. To avoid this, grab this buffer anyway. + return true; + } + return mQueueItems[0].mFence->getSignalTime() != INT64_MAX; +#else + return true; +#endif +} + bool Layer::addSyncPoint(const std::shared_ptr<SyncPoint>& point) { if (point->getFrameNumber() <= mCurrentFrameNumber) { // Don't bother with a SyncPoint, since we've already latched the @@ -1384,9 +1430,10 @@ bool Layer::applyPendingStates(State* stateToCommit) { void Layer::notifyAvailableFrames() { auto headFrameNumber = getHeadFrameNumber(); + bool headFenceSignaled = headFenceHasSignaled(); Mutex::Autolock lock(mLocalSyncPointMutex); for (auto& point : mLocalSyncPoints) { - if (headFrameNumber >= point->getFrameNumber()) { + if (headFrameNumber >= point->getFrameNumber() && headFenceSignaled) { point->setFrameAvailable(); } } @@ -1596,11 +1643,15 @@ bool Layer::setFlags(uint8_t flags, uint8_t mask) { setTransactionFlags(eTransactionNeeded); return true; } -bool Layer::setCrop(const Rect& crop) { + +bool Layer::setCrop(const Rect& crop, bool immediate) { if (mCurrentState.crop == crop) return false; mCurrentState.sequence++; - mCurrentState.crop = crop; + mCurrentState.requestedCrop = crop; + if (immediate) { + mCurrentState.crop = crop; + } mCurrentState.modified = true; setTransactionFlags(eTransactionNeeded); return true; @@ -1697,7 +1748,8 @@ bool Layer::onPreComposition() { return mQueuedFrames > 0 || mSidebandStreamChanged || mAutoRefresh; } -void Layer::onPostComposition() { +bool Layer::onPostComposition() { + bool frameLatencyNeeded = mFrameLatencyNeeded; if (mFrameLatencyNeeded) { nsecs_t desiredPresentTime = mSurfaceFlingerConsumer->getTimestamp(); mFrameTracker.setDesiredPresentTime(desiredPresentTime); @@ -1729,6 +1781,7 @@ void Layer::onPostComposition() { mFrameTracker.advanceFrame(); mFrameLatencyNeeded = false; } + return frameLatencyNeeded; } #ifdef USE_HWC2 @@ -1777,6 +1830,13 @@ Region Layer::latchBuffer(bool& recomputeVisibleRegions) return outDirtyRegion; } + // If the head buffer's acquire fence hasn't signaled yet, return and + // try again later + if (!headFenceHasSignaled()) { + mFlinger->signalLayerUpdate(); + return outDirtyRegion; + } + // Capture the old state of the layer for comparisons later const State& s(getDrawingState()); const bool oldOpacity = isOpaque(s); @@ -1789,16 +1849,19 @@ Region Layer::latchBuffer(bool& recomputeVisibleRegions) bool stickyTransformSet; const char* name; int32_t overrideScalingMode; + bool& freezePositionUpdates; Reject(Layer::State& front, Layer::State& current, bool& recomputeVisibleRegions, bool stickySet, const char* name, - int32_t overrideScalingMode) + int32_t overrideScalingMode, + bool& freezePositionUpdates) : front(front), current(current), recomputeVisibleRegions(recomputeVisibleRegions), stickyTransformSet(stickySet), name(name), - overrideScalingMode(overrideScalingMode) { + overrideScalingMode(overrideScalingMode), + freezePositionUpdates(freezePositionUpdates) { } virtual bool reject(const sp<GraphicBuffer>& buf, @@ -1890,13 +1953,20 @@ Region Layer::latchBuffer(bool& recomputeVisibleRegions) recomputeVisibleRegions = true; } + if (front.crop != front.requestedCrop) { + front.crop = front.requestedCrop; + current.crop = front.requestedCrop; + recomputeVisibleRegions = true; + } + freezePositionUpdates = false; + return false; } }; Reject r(mDrawingState, getCurrentState(), recomputeVisibleRegions, getProducerStickyTransform() != 0, mName.string(), - mOverrideScalingMode); + mOverrideScalingMode, mFreezePositionUpdates); // Check all of our local sync points to ensure that all transactions @@ -2039,7 +2109,6 @@ Region Layer::latchBuffer(bool& recomputeVisibleRegions) if (bufWidth != uint32_t(oldActiveBuffer->width) || bufHeight != uint32_t(oldActiveBuffer->height)) { recomputeVisibleRegions = true; - mFreezePositionUpdates = false; } } @@ -2170,6 +2239,54 @@ void Layer::dump(String8& result, Colorizer& colorizer) const } } +#ifdef USE_HWC2 +void Layer::miniDumpHeader(String8& result) { + result.append("----------------------------------------"); + result.append("---------------------------------------\n"); + result.append(" Layer name\n"); + result.append(" Z | "); + result.append(" Comp Type | "); + result.append(" Disp Frame (LTRB) | "); + result.append(" Source Crop (LTRB)\n"); + result.append("----------------------------------------"); + result.append("---------------------------------------\n"); +} + +void Layer::miniDump(String8& result, int32_t hwcId) const { + if (mHwcLayers.count(hwcId) == 0) { + return; + } + + String8 name; + if (mName.length() > 77) { + std::string shortened; + shortened.append(mName.string(), 36); + shortened.append("[...]"); + shortened.append(mName.string() + (mName.length() - 36), 36); + name = shortened.c_str(); + } else { + name = mName; + } + + result.appendFormat(" %s\n", name.string()); + + const Layer::State& layerState(getDrawingState()); + const HWCInfo& hwcInfo = mHwcLayers.at(hwcId); + result.appendFormat(" %10u | ", layerState.z); + result.appendFormat("%10s | ", + to_string(getCompositionType(hwcId)).c_str()); + const Rect& frame = hwcInfo.displayFrame; + result.appendFormat("%4d %4d %4d %4d | ", frame.left, frame.top, + frame.right, frame.bottom); + const FloatRect& crop = hwcInfo.sourceCrop; + result.appendFormat("%6.1f %6.1f %6.1f %6.1f\n", crop.left, crop.top, + crop.right, crop.bottom); + + result.append("- - - - - - - - - - - - - - - - - - - - "); + result.append("- - - - - - - - - - - - - - - - - - - -\n"); +} +#endif + void Layer::dumpFrameStats(String8& result) const { mFrameTracker.dumpStats(result); } @@ -2203,6 +2320,24 @@ void Layer::getFenceData(String8* outName, uint64_t* outFrameNumber, *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; +} + +bool Layer::getTransformToDisplayInverse() const { + return mSurfaceFlingerConsumer->getTransformToDisplayInverse(); +} + // --------------------------------------------------------------------------- Layer::LayerCleaner::LayerCleaner(const sp<SurfaceFlinger>& flinger, diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index c96e7d5ee9..24de87ee9b 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -96,7 +96,9 @@ public: Transform transform; inline bool operator ==(const Geometry& rhs) const { - return (w == rhs.w && h == rhs.h); + return (w == rhs.w && h == rhs.h) && + (transform.tx() == rhs.transform.tx()) && + (transform.ty() == rhs.transform.ty()); } inline bool operator !=(const Geometry& rhs) const { return !operator ==(rhs); @@ -120,6 +122,8 @@ public: bool modified; Rect crop; + Rect requestedCrop; + Rect finalCrop; // If set, defers this state update until the Layer identified by handle @@ -156,7 +160,7 @@ public: bool setMatrix(const layer_state_t::matrix22_t& matrix); bool setTransparentRegionHint(const Region& transparent); bool setFlags(uint8_t flags, uint8_t mask); - bool setCrop(const Rect& crop); + bool setCrop(const Rect& crop, bool immediate); bool setFinalCrop(const Rect& crop); bool setLayerStack(uint32_t layerStack); void deferTransactionUntil(const sp<IBinder>& handle, uint64_t frameNumber); @@ -274,9 +278,10 @@ public: 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 @@ -397,6 +402,10 @@ public: /* always call base class first */ void dump(String8& result, Colorizer& colorizer) const; +#ifdef USE_HWC2 + static void miniDumpHeader(String8& result); + void miniDump(String8& result, int32_t hwcId) const; +#endif void dumpFrameStats(String8& result) const; void clearFrameStats(); void logFrameStats(); @@ -406,6 +415,15 @@ public: 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); + } + + bool getTransformToDisplayInverse() const; + protected: // constant sp<SurfaceFlinger> mFlinger; @@ -451,6 +469,9 @@ private: // Temporary - Used only for LEGACY camera mode. uint32_t getProducerStickyTransform() const; + // Loads the corresponding system property once per process + static bool latchUnsignaledBuffers(); + // ----------------------------------------------------------------------- class SyncPoint @@ -496,6 +517,7 @@ private: std::list<std::shared_ptr<SyncPoint>> mRemoteSyncPoints; uint64_t getHeadFrameNumber() const; + bool headFenceHasSignaled() const; // Returns false if the relevant frame has already been latched bool addSyncPoint(const std::shared_ptr<SyncPoint>& point); @@ -570,6 +592,8 @@ private: bool forceClientComposition; HWC2::Composition compositionType; bool clearClientTarget; + Rect displayFrame; + FloatRect sourceCrop; }; std::unordered_map<int32_t, HWCInfo> mHwcLayers; #else diff --git a/services/surfaceflinger/MessageQueue.cpp b/services/surfaceflinger/MessageQueue.cpp index 34dc24b1f3..974c7a340e 100644 --- a/services/surfaceflinger/MessageQueue.cpp +++ b/services/surfaceflinger/MessageQueue.cpp @@ -134,31 +134,12 @@ status_t MessageQueue::postMessage( } -/* when INVALIDATE_ON_VSYNC is set SF only processes - * buffer updates on VSYNC and performs a refresh immediately - * after. - * - * when INVALIDATE_ON_VSYNC is set to false, SF will instead - * perform the buffer updates immediately, but the refresh only - * at the next VSYNC. - * THIS MODE IS BUGGY ON GALAXY NEXUS AND WILL CAUSE HANGS - */ -#define INVALIDATE_ON_VSYNC 1 - void MessageQueue::invalidate() { -#if INVALIDATE_ON_VSYNC mEvents->requestNextVsync(); -#else - mHandler->dispatchInvalidate(); -#endif } void MessageQueue::refresh() { -#if INVALIDATE_ON_VSYNC mHandler->dispatchRefresh(); -#else - mEvents->requestNextVsync(); -#endif } int MessageQueue::cb_eventReceiver(int fd, int events, void* data) { @@ -172,11 +153,7 @@ int MessageQueue::eventReceiver(int /*fd*/, int /*events*/) { while ((n = DisplayEventReceiver::getEvents(mEventTube, buffer, 8)) > 0) { for (int i=0 ; i<n ; i++) { if (buffer[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) { -#if INVALIDATE_ON_VSYNC mHandler->dispatchInvalidate(); -#else - mHandler->dispatchRefresh(); -#endif break; } } diff --git a/services/surfaceflinger/MonitoredProducer.cpp b/services/surfaceflinger/MonitoredProducer.cpp index faab62cb4e..ffaee7a107 100644 --- a/services/surfaceflinger/MonitoredProducer.cpp +++ b/services/surfaceflinger/MonitoredProducer.cpp @@ -102,8 +102,8 @@ status_t MonitoredProducer::connect(const sp<IProducerListener>& listener, return mProducer->connect(listener, api, producerControlledByApp, output); } -status_t MonitoredProducer::disconnect(int api) { - return mProducer->disconnect(api); +status_t MonitoredProducer::disconnect(int api, DisconnectMode mode) { + return mProducer->disconnect(api, mode); } status_t MonitoredProducer::setSidebandStream(const sp<NativeHandle>& stream) { @@ -127,10 +127,6 @@ String8 MonitoredProducer::getConsumerName() const { return mProducer->getConsumerName(); } -uint64_t MonitoredProducer::getNextFrameNumber() const { - return mProducer->getNextFrameNumber(); -} - status_t MonitoredProducer::setSharedBufferMode(bool sharedBufferMode) { return mProducer->setSharedBufferMode(sharedBufferMode); } @@ -149,6 +145,10 @@ status_t MonitoredProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, outTransformMatrix); } +status_t MonitoredProducer::getUniqueId(uint64_t* outId) const { + return mProducer->getUniqueId(outId); +} + IBinder* MonitoredProducer::onAsBinder() { return IInterface::asBinder(mProducer).get(); } diff --git a/services/surfaceflinger/MonitoredProducer.h b/services/surfaceflinger/MonitoredProducer.h index ce756dc822..66f6cf0bdb 100644 --- a/services/surfaceflinger/MonitoredProducer.h +++ b/services/surfaceflinger/MonitoredProducer.h @@ -50,20 +50,20 @@ public: virtual int query(int what, int* value); virtual status_t connect(const sp<IProducerListener>& token, int api, bool producerControlledByApp, QueueBufferOutput* output); - virtual status_t disconnect(int api); + virtual status_t disconnect(int api, DisconnectMode mode); virtual status_t setSidebandStream(const sp<NativeHandle>& stream); virtual void allocateBuffers(uint32_t width, uint32_t height, PixelFormat format, uint32_t usage); virtual status_t allowAllocation(bool allow); virtual status_t setGenerationNumber(uint32_t generationNumber); virtual String8 getConsumerName() const override; - virtual uint64_t getNextFrameNumber() const override; virtual status_t setDequeueTimeout(nsecs_t timeout) override; virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence, float outTransformMatrix[16]) override; virtual IBinder* onAsBinder(); virtual status_t setSharedBufferMode(bool sharedBufferMode) override; virtual status_t setAutoRefresh(bool autoRefresh) override; + virtual status_t getUniqueId(uint64_t* outId) const override; private: sp<IGraphicBufferProducer> mProducer; diff --git a/services/surfaceflinger/RenderEngine/RenderEngine.cpp b/services/surfaceflinger/RenderEngine/RenderEngine.cpp index 27357b91cc..d6a032fb35 100644 --- a/services/surfaceflinger/RenderEngine/RenderEngine.cpp +++ b/services/surfaceflinger/RenderEngine/RenderEngine.cpp @@ -436,6 +436,13 @@ EGLConfig RenderEngine::chooseEglConfig(EGLDisplay display, int format) { return config; } + +void RenderEngine::primeCache() const { + // Getting the ProgramCache instance causes it to prime its shader cache, + // which is performed in its constructor + ProgramCache::getInstance(); +} + // --------------------------------------------------------------------------- }; // namespace android // --------------------------------------------------------------------------- diff --git a/services/surfaceflinger/RenderEngine/RenderEngine.h b/services/surfaceflinger/RenderEngine/RenderEngine.h index 9cc1ed7a09..0259881eee 100644 --- a/services/surfaceflinger/RenderEngine/RenderEngine.h +++ b/services/surfaceflinger/RenderEngine/RenderEngine.h @@ -63,6 +63,8 @@ public: static EGLConfig chooseEglConfig(EGLDisplay display, int format); + void primeCache() const; + // dump the extension strings. always call the base class. virtual void dump(String8& result); diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 45fe6a76b6..b4538b33a5 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -152,7 +152,6 @@ SurfaceFlinger::SurfaceFlinger() mPrimaryDispSync("PrimaryDispSync"), mPrimaryHWVsyncEnabled(false), mHWVsyncAvailable(false), - mDaltonize(false), mHasColorMatrix(false), mHasPoweredOff(false), mFrameBuckets(), @@ -167,9 +166,6 @@ SurfaceFlinger::SurfaceFlinger() property_get("ro.bq.gpu_to_cpu_unsupported", value, "0"); mGpuToCpuSupported = !atoi(value); - property_get("debug.sf.drop_missed_frames", value, "0"); - mDropMissedFrames = atoi(value); - property_get("debug.sf.showupdates", value, "0"); mDebugRegion = atoi(value); @@ -183,6 +179,14 @@ SurfaceFlinger::SurfaceFlinger() } ALOGI_IF(mDebugRegion, "showupdates enabled"); ALOGI_IF(mDebugDDMS, "DDMS debugging enabled"); + + property_get("debug.sf.disable_backpressure", value, "0"); + mPropagateBackpressure = !atoi(value); + ALOGI_IF(!mPropagateBackpressure, "Disabling backpressure propagation"); + + property_get("debug.sf.disable_hwc_vds", value, "0"); + mUseHwcVirtualDisplays = !atoi(value); + ALOGI_IF(!mUseHwcVirtualDisplays, "Disabling HWC virtual displays"); } void SurfaceFlinger::onFirstRef() @@ -465,6 +469,13 @@ void SurfaceFlinger::init() { mSFEventThread = new EventThread(sfVsyncSrc, *this); mEventQueue.setEventThread(mSFEventThread); + // set SFEventThread to SCHED_FIFO to minimize jitter + struct sched_param param = {0}; + param.sched_priority = 2; + if (sched_setscheduler(mSFEventThread->getTid(), SCHED_FIFO, ¶m) != 0) { + ALOGE("Couldn't set SCHED_FIFO for SFEventThread"); + } + // Get a RenderEngine for the given display / config (can't fail) mRenderEngine = RenderEngine::create(mEGLDisplay, HAL_PIXEL_FORMAT_RGBA_8888); @@ -497,6 +508,8 @@ void SurfaceFlinger::init() { // set initial conditions (e.g. unblank default device) initializeDisplays(); + mRenderEngine->primeCache(); + // start boot animation startBootAnim(); @@ -605,9 +618,6 @@ status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& display, info.fps = 1e9 / hwConfig->getVsyncPeriod(); info.appVsyncOffset = VSYNC_EVENT_PHASE_OFFSET_NS; - // TODO: Hook this back up - info.colorTransform = 0; - // This is how far in advance a buffer must be queued for // presentation at a given time. If you want a buffer to appear // on the screen at time N, you must submit the buffer before @@ -692,6 +702,7 @@ status_t SurfaceFlinger::setActiveConfig(const sp<IBinder>& display, int mode) { if (mMode < 0 || mMode >= static_cast<int>(configs.size())) { ALOGE("Attempt to set active config = %d for display with %zu configs", mMode, configs.size()); + return true; } sp<DisplayDevice> hw(mFlinger.getDisplayDevice(mDisplay)); if (hw == NULL) { @@ -710,6 +721,101 @@ status_t SurfaceFlinger::setActiveConfig(const sp<IBinder>& display, int mode) { postMessageSync(msg); return NO_ERROR; } +status_t SurfaceFlinger::getDisplayColorModes(const sp<IBinder>& display, + Vector<android_color_mode_t>* outColorModes) { + if ((outColorModes == nullptr) || (display.get() == nullptr)) { + return BAD_VALUE; + } + + if (!display.get()) { + return NAME_NOT_FOUND; + } + + int32_t type = NAME_NOT_FOUND; + for (int i=0 ; i<DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) { + if (display == mBuiltinDisplays[i]) { + type = i; + break; + } + } + + if (type < 0) { + return type; + } + + std::vector<android_color_mode_t> modes = getHwComposer().getColorModes(type); + outColorModes->clear(); + std::copy(modes.cbegin(), modes.cend(), std::back_inserter(*outColorModes)); + + return NO_ERROR; +} + +android_color_mode_t SurfaceFlinger::getActiveColorMode(const sp<IBinder>& display) { + sp<DisplayDevice> device(getDisplayDevice(display)); + if (device != nullptr) { + return device->getActiveColorMode(); + } + return static_cast<android_color_mode_t>(BAD_VALUE); +} + +void SurfaceFlinger::setActiveColorModeInternal(const sp<DisplayDevice>& hw, + android_color_mode_t mode) { + ALOGD("Set active color mode=%d, type=%d flinger=%p", mode, hw->getDisplayType(), + this); + int32_t type = hw->getDisplayType(); + android_color_mode_t currentMode = hw->getActiveColorMode(); + + if (mode == currentMode) { + ALOGD("Screen type=%d is already in color mode=%d", hw->getDisplayType(), mode); + return; + } + + if (type >= DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) { + ALOGW("Trying to set config for virtual display"); + return; + } + + hw->setActiveColorMode(mode); + getHwComposer().setActiveColorMode(type, mode); +} + + +status_t SurfaceFlinger::setActiveColorMode(const sp<IBinder>& display, + android_color_mode_t colorMode) { + class MessageSetActiveColorMode: public MessageBase { + SurfaceFlinger& mFlinger; + sp<IBinder> mDisplay; + android_color_mode_t mMode; + public: + MessageSetActiveColorMode(SurfaceFlinger& flinger, const sp<IBinder>& disp, + android_color_mode_t mode) : + mFlinger(flinger), mDisplay(disp) { mMode = mode; } + virtual bool handler() { + Vector<android_color_mode_t> modes; + mFlinger.getDisplayColorModes(mDisplay, &modes); + bool exists = std::find(std::begin(modes), std::end(modes), mMode) != std::end(modes); + if (mMode < 0 || !exists) { + ALOGE("Attempt to set invalid active color mode = %d for display %p", mMode, + mDisplay.get()); + return true; + } + sp<DisplayDevice> hw(mFlinger.getDisplayDevice(mDisplay)); + if (hw == nullptr) { + ALOGE("Attempt to set active color mode = %d for null display %p", + mMode, mDisplay.get()); + } else if (hw->getDisplayType() >= DisplayDevice::DISPLAY_VIRTUAL) { + ALOGW("Attempt to set active color mode= %d for virtual display", + mMode); + } else { + mFlinger.setActiveColorModeInternal(hw, mMode); + } + return true; + } + }; + sp<MessageBase> msg = new MessageSetActiveColorMode(*this, display, colorMode); + postMessageSync(msg); + return NO_ERROR; +} status_t SurfaceFlinger::clearAnimationFrameStats() { Mutex::Autolock _l(mStateLock); @@ -908,6 +1014,15 @@ void SurfaceFlinger::onMessageReceived(int32_t what) { ATRACE_CALL(); switch (what) { case MessageQueue::INVALIDATE: { + bool frameMissed = !mHadClientComposition && + mPreviousPresentFence != Fence::NO_FENCE && + mPreviousPresentFence->getSignalTime() == INT64_MAX; + ATRACE_INT("FrameMissed", static_cast<int>(frameMissed)); + if (mPropagateBackpressure && frameMissed) { + signalLayerUpdate(); + break; + } + bool refreshNeeded = handleMessageTransaction(); refreshNeeded |= handleMessageInvalidate(); refreshNeeded |= mRepaintEverything; @@ -943,32 +1058,22 @@ bool SurfaceFlinger::handleMessageInvalidate() { 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; - bool frameMissed = (expectedPresent == previousExpectedPresent); - if (frameMissed != previousFrameMissed) { - ATRACE_INT("FrameMissed", static_cast<int>(frameMissed)); - } - previousFrameMissed = frameMissed; - - if (CC_UNLIKELY(mDropMissedFrames && frameMissed)) { - // Latch buffers, but don't send anything to HWC, then signal another - // wakeup for the next vsync - preComposition(); - repaintEverything(); - } else { - preComposition(); - rebuildLayerStacks(); - setUpHWComposer(); - doDebugFlashRegions(); - doComposition(); - postComposition(refreshStartTime); + + preComposition(); + rebuildLayerStacks(); + setUpHWComposer(); + doDebugFlashRegions(); + doComposition(); + postComposition(refreshStartTime); + + mPreviousPresentFence = mHwc->getRetireFence(HWC_DISPLAY_PRIMARY); + + mHadClientComposition = false; + for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) { + const sp<DisplayDevice>& displayDevice = mDisplays[displayId]; + mHadClientComposition = mHadClientComposition || + mHwc->hasClientComposition(displayDevice->getHwcDisplayId()); } // Release any buffers which were replaced this frame @@ -976,8 +1081,6 @@ void SurfaceFlinger::handleMessageRefresh() { layer->releasePendingBuffer(); } mLayersWithQueuedFrames.clear(); - - previousExpectedPresent = mPrimaryDispSync.computeNextRefresh(0); } void SurfaceFlinger::doDebugFlashRegions() @@ -1042,11 +1145,7 @@ void SurfaceFlinger::preComposition() } } -#ifdef ENABLE_FENCE_TRACKING void SurfaceFlinger::postComposition(nsecs_t refreshStartTime) -#else -void SurfaceFlinger::postComposition(nsecs_t /*refreshStartTime*/) -#endif { ATRACE_CALL(); ALOGV("postComposition"); @@ -1054,7 +1153,11 @@ void SurfaceFlinger::postComposition(nsecs_t /*refreshStartTime*/) 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); @@ -1074,10 +1177,8 @@ void SurfaceFlinger::postComposition(nsecs_t /*refreshStartTime*/) } } -#ifdef ENABLE_FENCE_TRACKING mFenceTracker.addFrame(refreshStartTime, presentFence, hw->getVisibleLayersSortedByZ(), hw->getClientTargetAcquireFence()); -#endif if (mAnimCompositionPending) { mAnimCompositionPending = false; @@ -1222,8 +1323,7 @@ void SurfaceFlinger::setUpHWComposer() { } layer->setGeometry(displayDevice); - if (mDebugDisableHWC || mDebugRegion || mDaltonize || - mHasColorMatrix) { + if (mDebugDisableHWC || mDebugRegion) { layer->forceClientComposition(hwcId); } } @@ -1231,6 +1331,9 @@ void SurfaceFlinger::setUpHWComposer() { } } + + mat4 colorMatrix = mColorMatrix * mDaltonizer(); + // Set the per-frame data for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) { auto& displayDevice = mDisplays[displayId]; @@ -1238,11 +1341,18 @@ void SurfaceFlinger::setUpHWComposer() { if (hwcId < 0) { continue; } + if (colorMatrix != mPreviousColorMatrix) { + status_t result = mHwc->setColorTransform(hwcId, colorMatrix); + ALOGE_IF(result != NO_ERROR, "Failed to set color transform on " + "display %zd: %d", displayId, result); + } for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) { layer->setPerFrameData(displayDevice); } } + mPreviousColorMatrix = colorMatrix; + for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) { auto& displayDevice = mDisplays[displayId]; if (!displayDevice->isDisplayOn()) { @@ -1480,26 +1590,28 @@ void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) // etc.) but no internal state (i.e. a DisplayDevice). if (state.surface != NULL) { - int width = 0; - int status = state.surface->query( - NATIVE_WINDOW_WIDTH, &width); - ALOGE_IF(status != NO_ERROR, - "Unable to query width (%d)", status); - int height = 0; - status = state.surface->query( - NATIVE_WINDOW_HEIGHT, &height); - ALOGE_IF(status != NO_ERROR, - "Unable to query height (%d)", status); - int intFormat = 0; - status = state.surface->query( - NATIVE_WINDOW_FORMAT, &intFormat); - ALOGE_IF(status != NO_ERROR, - "Unable to query format (%d)", status); - auto format = static_cast<android_pixel_format_t>( - intFormat); - - mHwc->allocateVirtualDisplay(width, height, &format, - &hwcId); + if (mUseHwcVirtualDisplays) { + int width = 0; + int status = state.surface->query( + NATIVE_WINDOW_WIDTH, &width); + ALOGE_IF(status != NO_ERROR, + "Unable to query width (%d)", status); + int height = 0; + status = state.surface->query( + NATIVE_WINDOW_HEIGHT, &height); + ALOGE_IF(status != NO_ERROR, + "Unable to query height (%d)", status); + int intFormat = 0; + status = state.surface->query( + NATIVE_WINDOW_FORMAT, &intFormat); + ALOGE_IF(status != NO_ERROR, + "Unable to query format (%d)", status); + auto format = static_cast<android_pixel_format_t>( + intFormat); + + mHwc->allocateVirtualDisplay(width, height, &format, + &hwcId); + } // TODO: Plumb requested format back up to consumer @@ -1663,6 +1775,8 @@ void SurfaceFlinger::commitTransaction() 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(); @@ -1922,18 +2036,7 @@ void SurfaceFlinger::doDisplayComposition(const sp<const DisplayDevice>& hw, } } - if (CC_LIKELY(!mDaltonize && !mHasColorMatrix)) { - if (!doComposeSurfaces(hw, dirtyRegion)) return; - } else { - RenderEngine& engine(getRenderEngine()); - mat4 colorMatrix = mColorMatrix; - if (mDaltonize) { - colorMatrix = colorMatrix * mDaltonizer(); - } - mat4 oldMatrix = engine.setupColorTransform(colorMatrix); - doComposeSurfaces(hw, dirtyRegion); - engine.setupColorTransform(oldMatrix); - } + if (!doComposeSurfaces(hw, dirtyRegion)) return; // update the swap region and clear the dirty region hw->swapRegion.orSelf(dirtyRegion); @@ -1948,6 +2051,15 @@ bool SurfaceFlinger::doComposeSurfaces( ALOGV("doComposeSurfaces"); const auto hwcId = displayDevice->getHwcDisplayId(); + + mat4 oldColorMatrix; + const bool applyColorMatrix = !mHwc->hasDeviceComposition(hwcId) && + !mHwc->hasCapability(HWC2::Capability::SkipClientColorTransform); + if (applyColorMatrix) { + mat4 colorMatrix = mColorMatrix * mDaltonizer(); + oldColorMatrix = getRenderEngine().setupColorTransform(colorMatrix); + } + bool hasClientComposition = mHwc->hasClientComposition(hwcId); if (hasClientComposition) { ALOGV("hasClientComposition"); @@ -2065,6 +2177,10 @@ bool SurfaceFlinger::doComposeSurfaces( } } + if (applyColorMatrix) { + getRenderEngine().setupColorTransform(oldColorMatrix); + } + // disable scissor at the end of the frame mRenderEngine->disableScissor(); return true; @@ -2274,10 +2390,10 @@ uint32_t SurfaceFlinger::setClientStateLocked( sp<Layer> layer(client->getLayerUser(s.surface)); if (layer != 0) { const uint32_t what = s.what; - bool positionAppliesWithResize = - what & layer_state_t::ePositionAppliesWithResize; + bool geometryAppliesWithResize = + what & layer_state_t::eGeometryAppliesWithResize; if (what & layer_state_t::ePositionChanged) { - if (layer->setPosition(s.x, s.y, !positionAppliesWithResize)) { + if (layer->setPosition(s.x, s.y, !geometryAppliesWithResize)) { flags |= eTraversalNeeded; } } @@ -2314,7 +2430,7 @@ uint32_t SurfaceFlinger::setClientStateLocked( flags |= eTraversalNeeded; } if (what & layer_state_t::eCropChanged) { - if (layer->setCrop(s.crop)) + if (layer->setCrop(s.crop, !geometryAppliesWithResize)) flags |= eTraversalNeeded; } if (what & layer_state_t::eFinalCropChanged) { @@ -2506,6 +2622,7 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& hw, } if (currentMode == HWC_POWER_MODE_OFF) { + // Turn on the display getHwComposer().setPowerMode(type, mode); if (type == DisplayDevice::DISPLAY_PRIMARY) { // FIXME: eventthread only knows about the main display right now @@ -2516,7 +2633,19 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& hw, mVisibleRegionsDirty = true; mHasPoweredOff = true; repaintEverything(); + + struct sched_param param = {0}; + param.sched_priority = 1; + if (sched_setscheduler(0, SCHED_FIFO, ¶m) != 0) { + ALOGW("Couldn't set SCHED_FIFO on display on"); + } } else if (mode == HWC_POWER_MODE_OFF) { + // Turn off the display + struct sched_param param = {0}; + if (sched_setscheduler(0, SCHED_OTHER, ¶m) != 0) { + ALOGW("Couldn't set SCHED_OTHER on display off"); + } + if (type == DisplayDevice::DISPLAY_PRIMARY) { disableHardwareVsync(true); // also cancels any in-progress resync @@ -2623,14 +2752,12 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args) dumpAll = false; } -#ifdef ENABLE_FENCE_TRACKING if ((index < numArgs) && (args[index] == String16("--fences"))) { index++; mFenceTracker.dump(&result); dumpAll = false; } -#endif } if (dumpAll) { @@ -2751,6 +2878,59 @@ void SurfaceFlinger::dumpStaticScreenStats(String8& result) const 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 { @@ -2804,6 +2984,8 @@ void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index, dumpStaticScreenStats(result); result.append("\n"); + dumpBufferingStats(result); + /* * Dump the visible layer list */ @@ -2879,6 +3061,26 @@ void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index, * VSYNC state */ mEventThread->dump(result); + result.append("\n"); + + /* + * HWC layer minidump + */ + for (size_t d = 0; d < mDisplays.size(); d++) { + const sp<const DisplayDevice>& displayDevice(mDisplays[d]); + int32_t hwcId = displayDevice->getHwcDisplayId(); + if (hwcId == DisplayDevice::DISPLAY_ID_INVALID) { + continue; + } + + result.appendFormat("Display %d HWC layers:\n", hwcId); + Layer::miniDumpHeader(result); + for (size_t l = 0; l < count; l++) { + const sp<Layer>& layer(currentLayers[l]); + layer->miniDump(result, hwcId); + } + result.append("\n"); + } /* * Dump HWComposer state @@ -2886,8 +3088,7 @@ void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index, colorizer.bold(result); result.append("h/w composer state:\n"); colorizer.reset(result); - bool hwcDisabled = mDebugDisableHWC || mDebugRegion || mDaltonize || - mHasColorMatrix; + bool hwcDisabled = mDebugDisableHWC || mDebugRegion; result.appendFormat(" h/w composer %s\n", hwcDisabled ? "disabled" : "enabled"); hwc.dump(result); @@ -3042,16 +3243,24 @@ status_t SurfaceFlinger::onTransact( // daltonize n = data.readInt32(); switch (n % 10) { - case 1: mDaltonizer.setType(Daltonizer::protanomaly); break; - case 2: mDaltonizer.setType(Daltonizer::deuteranomaly); break; - case 3: mDaltonizer.setType(Daltonizer::tritanomaly); break; + case 1: + mDaltonizer.setType(ColorBlindnessType::Protanomaly); + break; + case 2: + mDaltonizer.setType(ColorBlindnessType::Deuteranomaly); + break; + case 3: + mDaltonizer.setType(ColorBlindnessType::Tritanomaly); + break; + default: + mDaltonizer.setType(ColorBlindnessType::None); + break; } if (n >= 10) { - mDaltonizer.setMode(Daltonizer::correction); + mDaltonizer.setMode(ColorBlindnessMode::Correction); } else { - mDaltonizer.setMode(Daltonizer::simulation); + mDaltonizer.setMode(ColorBlindnessMode::Simulation); } - mDaltonize = n > 0; invalidateHwcGeometry(); repaintEverything(); return NO_ERROR; @@ -3059,15 +3268,14 @@ status_t SurfaceFlinger::onTransact( case 1015: { // apply a color matrix n = data.readInt32(); - mHasColorMatrix = n ? 1 : 0; if (n) { // color matrix is sent as mat3 matrix followed by vec3 // offset, then packed into a mat4 where the last row is // the offset and extra values are 0 for (size_t i = 0 ; i < 4; i++) { - for (size_t j = 0; j < 4; j++) { - mColorMatrix[i][j] = data.readFloat(); - } + for (size_t j = 0; j < 4; j++) { + mColorMatrix[i][j] = data.readFloat(); + } } } else { mColorMatrix = mat4(); @@ -3098,6 +3306,11 @@ status_t SurfaceFlinger::onTransact( mSFEventThread->setPhaseOffset(static_cast<nsecs_t>(n)); return NO_ERROR; } + case 1021: { // Disable HWC virtual displays + n = data.readInt32(); + mUseHwcVirtualDisplays = !n; + return NO_ERROR; + } } } return err; @@ -3440,6 +3653,14 @@ status_t SurfaceFlinger::captureScreenImplLocked( // create a surface (because we're a producer, and we need to // dequeue/queue a buffer) sp<Surface> sur = new Surface(producer, false); + + // Put the screenshot Surface into async mode so that + // Layer::headFenceHasSignaled will always return true and we'll latch the + // first buffer regardless of whether or not its acquire fence has + // signaled. This is needed to avoid a race condition in the rotation + // animation. See b/30209608 + sur->setAsyncMode(true); + ANativeWindow* window = sur.get(); status_t result = native_window_api_connect(window, NATIVE_WINDOW_API_EGL); @@ -3576,6 +3797,11 @@ void SurfaceFlinger::checkScreenshot(size_t w, size_t s, size_t h, void const* v } } +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 8279a85b45..b98924bb25 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -42,9 +42,12 @@ #include <gui/ISurfaceComposer.h> #include <gui/ISurfaceComposerClient.h> +#include <gui/OccupancyTracker.h> #include <hardware/hwcomposer_defs.h> +#include <system/graphics.h> + #include <private/gui/LayerState.h> #include "Barrier.h" @@ -57,6 +60,9 @@ #include "DisplayHardware/HWComposer.h" #include "Effects/Daltonizer.h" +#include <map> +#include <string> + namespace android { // --------------------------------------------------------------------------- @@ -218,6 +224,10 @@ private: virtual status_t getDisplayConfigs(const sp<IBinder>& display, Vector<DisplayInfo>* configs); virtual int getActiveConfig(const sp<IBinder>& display); + virtual status_t getDisplayColorModes(const sp<IBinder>& display, + Vector<android_color_mode_t>* configs); + virtual android_color_mode_t getActiveColorMode(const sp<IBinder>& display); + virtual status_t setActiveColorMode(const sp<IBinder>& display, android_color_mode_t colorMode); virtual void setPowerMode(const sp<IBinder>& display, int mode); virtual status_t setActiveConfig(const sp<IBinder>& display, int id); virtual status_t clearAnimationFrameStats(); @@ -256,6 +266,9 @@ private: // called on the main thread in response to setPowerMode() void setPowerModeInternal(const sp<DisplayDevice>& hw, int mode); + // Called on the main thread in response to setActiveColorMode() + void setActiveColorModeInternal(const sp<DisplayDevice>& hw, android_color_mode_t colorMode); + // Returns whether the transaction actually modified any state bool handleMessageTransaction(); @@ -364,6 +377,16 @@ private: return mDisplays.valueFor(dpy); } + int32_t getDisplayType(const sp<IBinder>& display) { + if (!display.get()) return NAME_NOT_FOUND; + for (int i = 0; i < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES; ++i) { + if (display == mBuiltinDisplays[i]) { + return i; + } + } + return NAME_NOT_FOUND; + } + // mark a region of a layer stack dirty. this updates the dirty // region of all screens presenting this layer stack. void invalidateLayerStack(uint32_t layerStack, const Region& dirty); @@ -432,6 +455,13 @@ private: 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 */ @@ -457,7 +487,6 @@ private: RenderEngine* mRenderEngine; nsecs_t mBootTime; bool mGpuToCpuSupported; - bool mDropMissedFrames; sp<EventThread> mEventThread; sp<EventThread> mSFEventThread; sp<EventControlThread> mEventControlThread; @@ -477,6 +506,8 @@ private: bool mAnimCompositionPending; #ifdef USE_HWC2 std::vector<sp<Layer>> mLayersWithQueuedFrames; + sp<Fence> mPreviousPresentFence = Fence::NO_FENCE; + bool mHadClientComposition = false; #endif // this may only be written from the main thread with mStateLock held @@ -495,6 +526,10 @@ private: bool mBootFinished; bool mForceFullDamage; FenceTracker mFenceTracker; +#ifdef USE_HWC2 + bool mPropagateBackpressure = true; +#endif + bool mUseHwcVirtualDisplays = true; // these are thread safe mutable MessageQueue mEventQueue; @@ -515,8 +550,11 @@ private: */ Daltonizer mDaltonizer; +#ifndef USE_HWC2 bool mDaltonize; +#endif + mat4 mPreviousColorMatrix; mat4 mColorMatrix; bool mHasColorMatrix; @@ -526,6 +564,29 @@ private: 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 edd53a36ee..6f2520be23 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> @@ -128,6 +129,7 @@ status_t SurfaceFlingerConsumer::acquireBufferLocked(BufferItem* item, } bool SurfaceFlingerConsumer::getTransformToDisplayInverse() const { + Mutex::Autolock lock(mMutex); return mTransformToDisplayInverse; } @@ -251,6 +253,12 @@ void SurfaceFlingerConsumer::onSidebandStreamChanged() { } } +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 51b002f991..4271039d25 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 @@ public: }; 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 { @@ -64,8 +66,9 @@ public: // See GLConsumer::bindTextureImageLocked(). status_t bindTextureImage(); - // must be called from SF main thread bool getTransformToDisplayInverse() const; + + // must be called from SF main thread const Region& getSurfaceDamage() const; // Sets the contents changed listener. This should be used instead of @@ -82,6 +85,9 @@ public: void releasePendingBuffer(); #endif + virtual bool getFrameTimestamps(uint64_t frameNumber, + FrameTimestamps* outTimestamps) const override; + private: virtual void onSidebandStreamChanged(); @@ -103,6 +109,9 @@ private: // 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 072de81773..b0f418c4fe 100644 --- a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp +++ b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp @@ -59,6 +59,8 @@ #include <private/android_filesystem_config.h> #include <private/gui/SyncFeatures.h> +#include <set> + #include "Client.h" #include "clz.h" #include "Colorizer.h" @@ -166,9 +168,6 @@ SurfaceFlinger::SurfaceFlinger() property_get("ro.bq.gpu_to_cpu_unsupported", value, "0"); mGpuToCpuSupported = !atoi(value); - property_get("debug.sf.drop_missed_frames", value, "0"); - mDropMissedFrames = atoi(value); - property_get("debug.sf.showupdates", value, "0"); mDebugRegion = atoi(value); @@ -182,6 +181,10 @@ SurfaceFlinger::SurfaceFlinger() } ALOGI_IF(mDebugRegion, "showupdates enabled"); ALOGI_IF(mDebugDDMS, "DDMS debugging enabled"); + + property_get("debug.sf.disable_hwc_vds", value, "0"); + mUseHwcVirtualDisplays = !atoi(value); + ALOGI_IF(!mUseHwcVirtualDisplays, "Disabling HWC virtual displays"); } void SurfaceFlinger::onFirstRef() @@ -462,6 +465,14 @@ void SurfaceFlinger::init() { mSFEventThread = new EventThread(sfVsyncSrc, *this); mEventQueue.setEventThread(mSFEventThread); + // set SFEventThread to SCHED_FIFO to minimize jitter + struct sched_param param = {0}; + param.sched_priority = 2; + if (sched_setscheduler(mSFEventThread->getTid(), SCHED_FIFO, ¶m) != 0) { + ALOGE("Couldn't set SCHED_FIFO for SFEventThread"); + } + + // Initialize the H/W composer object. There may or may not be an // actual hardware composer underneath. mHwc = new HWComposer(this, @@ -527,6 +538,8 @@ void SurfaceFlinger::init() { // set initial conditions (e.g. unblank default device) initializeDisplays(); + mRenderEngine->primeCache(); + // start boot animation startBootAnim(); } @@ -565,20 +578,8 @@ status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& display, return BAD_VALUE; } - if (!display.get()) - return NAME_NOT_FOUND; - - int32_t type = NAME_NOT_FOUND; - for (int i=0 ; i<DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) { - if (display == mBuiltinDisplays[i]) { - type = i; - break; - } - } - - if (type < 0) { - return type; - } + int32_t type = getDisplayType(display); + if (type < 0) return type; // TODO: Not sure if display density should handled by SF any longer class Density { @@ -640,7 +641,6 @@ status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& display, info.ydpi = ydpi; info.fps = float(1e9 / hwConfig.refresh); info.appVsyncOffset = VSYNC_EVENT_PHASE_OFFSET_NS; - info.colorTransform = hwConfig.colorTransform; // This is how far in advance a buffer must be queued for // presentation at a given time. If you want a buffer to appear @@ -741,6 +741,55 @@ status_t SurfaceFlinger::setActiveConfig(const sp<IBinder>& display, int mode) { return NO_ERROR; } +status_t SurfaceFlinger::getDisplayColorModes(const sp<IBinder>& display, + Vector<android_color_mode_t>* outColorModes) { + if (outColorModes == nullptr || display.get() == nullptr) { + return BAD_VALUE; + } + + int32_t type = getDisplayType(display); + if (type < 0) return type; + + std::set<android_color_mode_t> colorModes; + for (const HWComposer::DisplayConfig& hwConfig : getHwComposer().getConfigs(type)) { + colorModes.insert(hwConfig.colorMode); + } + + outColorModes->clear(); + std::copy(colorModes.cbegin(), colorModes.cend(), std::back_inserter(*outColorModes)); + + return NO_ERROR; +} + +android_color_mode_t SurfaceFlinger::getActiveColorMode(const sp<IBinder>& display) { + if (display.get() == nullptr) return static_cast<android_color_mode_t>(BAD_VALUE); + + int32_t type = getDisplayType(display); + if (type < 0) return static_cast<android_color_mode_t>(type); + + return getHwComposer().getColorMode(type); +} + +status_t SurfaceFlinger::setActiveColorMode(const sp<IBinder>& display, + android_color_mode_t colorMode) { + if (display.get() == nullptr || colorMode < 0) { + return BAD_VALUE; + } + + int32_t type = getDisplayType(display); + if (type < 0) return type; + const Vector<HWComposer::DisplayConfig>& hwConfigs = getHwComposer().getConfigs(type); + HWComposer::DisplayConfig desiredConfig = hwConfigs[getHwComposer().getCurrentConfig(type)]; + desiredConfig.colorMode = colorMode; + for (size_t c = 0; c < hwConfigs.size(); ++c) { + const HWComposer::DisplayConfig config = hwConfigs[c]; + if (config == desiredConfig) { + return setActiveConfig(display, c); + } + } + return BAD_VALUE; +} + status_t SurfaceFlinger::clearAnimationFrameStats() { Mutex::Autolock _l(mStateLock); mAnimFrameTracker.clearStats(); @@ -943,35 +992,14 @@ bool SurfaceFlinger::handleMessageInvalidate() { 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; - bool frameMissed = (expectedPresent == previousExpectedPresent); - if (frameMissed != previousFrameMissed) { - ATRACE_INT("FrameMissed", static_cast<int>(frameMissed)); - } - previousFrameMissed = frameMissed; - - if (CC_UNLIKELY(mDropMissedFrames && frameMissed)) { - // Latch buffers, but don't send anything to HWC, then signal another - // wakeup for the next vsync - preComposition(); - repaintEverything(); - } else { - preComposition(); - rebuildLayerStacks(); - setUpHWComposer(); - doDebugFlashRegions(); - doComposition(); - postComposition(refreshStartTime); - } - previousExpectedPresent = mPrimaryDispSync.computeNextRefresh(0); + preComposition(); + rebuildLayerStacks(); + setUpHWComposer(); + doDebugFlashRegions(); + doComposition(); + postComposition(refreshStartTime); } void SurfaceFlinger::doDebugFlashRegions() @@ -1029,16 +1057,16 @@ void SurfaceFlinger::preComposition() } } -#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 +1087,8 @@ void SurfaceFlinger::postComposition(nsecs_t /*refreshStartTime*/) } } -#ifdef ENABLE_FENCE_TRACKING mFenceTracker.addFrame(refreshStartTime, presentFence, hw->getVisibleLayersSortedByZ(), hw->getClientTargetAcquireFence()); -#endif if (mAnimCompositionPending) { mAnimCompositionPending = false; @@ -1486,9 +1512,10 @@ void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) NATIVE_WINDOW_HEIGHT, &height); ALOGE_IF(status != NO_ERROR, "Unable to query height (%d)", status); - if (MAX_VIRTUAL_DISPLAY_DIMENSION == 0 || + if (mUseHwcVirtualDisplays && + (MAX_VIRTUAL_DISPLAY_DIMENSION == 0 || (width <= MAX_VIRTUAL_DISPLAY_DIMENSION && - height <= MAX_VIRTUAL_DISPLAY_DIMENSION)) { + height <= MAX_VIRTUAL_DISPLAY_DIMENSION))) { hwcDisplayId = allocateHwcDisplayId(state.type); } @@ -1666,6 +1693,8 @@ void SurfaceFlinger::commitTransaction() 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(); @@ -2273,10 +2302,10 @@ uint32_t SurfaceFlinger::setClientStateLocked( sp<Layer> layer(client->getLayerUser(s.surface)); if (layer != 0) { const uint32_t what = s.what; - bool positionAppliesWithResize = - what & layer_state_t::ePositionAppliesWithResize; + bool geometryAppliesWithResize = + what & layer_state_t::eGeometryAppliesWithResize; if (what & layer_state_t::ePositionChanged) { - if (layer->setPosition(s.x, s.y, !positionAppliesWithResize)) { + if (layer->setPosition(s.x, s.y, !geometryAppliesWithResize)) { flags |= eTraversalNeeded; } } @@ -2313,7 +2342,7 @@ uint32_t SurfaceFlinger::setClientStateLocked( flags |= eTraversalNeeded; } if (what & layer_state_t::eCropChanged) { - if (layer->setCrop(s.crop)) + if (layer->setCrop(s.crop, !geometryAppliesWithResize)) flags |= eTraversalNeeded; } if (what & layer_state_t::eFinalCropChanged) { @@ -2505,6 +2534,7 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& hw, } if (currentMode == HWC_POWER_MODE_OFF) { + // Turn on the display getHwComposer().setPowerMode(type, mode); if (type == DisplayDevice::DISPLAY_PRIMARY) { // FIXME: eventthread only knows about the main display right now @@ -2515,7 +2545,19 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& hw, mVisibleRegionsDirty = true; mHasPoweredOff = true; repaintEverything(); + + struct sched_param param = {0}; + param.sched_priority = 1; + if (sched_setscheduler(0, SCHED_FIFO, ¶m) != 0) { + ALOGW("Couldn't set SCHED_FIFO on display on"); + } } else if (mode == HWC_POWER_MODE_OFF) { + // Turn off the display + struct sched_param param = {0}; + if (sched_setscheduler(0, SCHED_OTHER, ¶m) != 0) { + ALOGW("Couldn't set SCHED_OTHER on display off"); + } + if (type == DisplayDevice::DISPLAY_PRIMARY) { disableHardwareVsync(true); // also cancels any in-progress resync @@ -2622,14 +2664,12 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args) dumpAll = false; } -#ifdef ENABLE_FENCE_TRACKING if ((index < numArgs) && (args[index] == String16("--fences"))) { index++; mFenceTracker.dump(&result); dumpAll = false; } -#endif } if (dumpAll) { @@ -2750,6 +2790,58 @@ void SurfaceFlinger::dumpStaticScreenStats(String8& result) const 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 { @@ -2801,6 +2893,8 @@ void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index, dumpStaticScreenStats(result); result.append("\n"); + dumpBufferingStats(result); + /* * Dump the visible layer list */ @@ -3039,14 +3133,20 @@ status_t SurfaceFlinger::onTransact( // daltonize n = data.readInt32(); switch (n % 10) { - case 1: mDaltonizer.setType(Daltonizer::protanomaly); break; - case 2: mDaltonizer.setType(Daltonizer::deuteranomaly); break; - case 3: mDaltonizer.setType(Daltonizer::tritanomaly); break; + case 1: + mDaltonizer.setType(ColorBlindnessType::Protanomaly); + break; + case 2: + mDaltonizer.setType(ColorBlindnessType::Deuteranomaly); + break; + case 3: + mDaltonizer.setType(ColorBlindnessType::Tritanomaly); + break; } if (n >= 10) { - mDaltonizer.setMode(Daltonizer::correction); + mDaltonizer.setMode(ColorBlindnessMode::Correction); } else { - mDaltonizer.setMode(Daltonizer::simulation); + mDaltonizer.setMode(ColorBlindnessMode::Simulation); } mDaltonize = n > 0; invalidateHwcGeometry(); @@ -3095,6 +3195,11 @@ status_t SurfaceFlinger::onTransact( mSFEventThread->setPhaseOffset(static_cast<nsecs_t>(n)); return NO_ERROR; } + case 1021: { // Disable HWC virtual displays + n = data.readInt32(); + mUseHwcVirtualDisplays = !n; + return NO_ERROR; + } } } return err; @@ -3547,6 +3652,11 @@ status_t SurfaceFlinger::captureScreenImplLocked( 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/Transform.cpp b/services/surfaceflinger/Transform.cpp index c2be91df24..6be9ae2c5f 100644 --- a/services/surfaceflinger/Transform.cpp +++ b/services/surfaceflinger/Transform.cpp @@ -196,7 +196,7 @@ Rect Transform::makeBounds(int w, int h) const return transform( Rect(w, h) ); } -Rect Transform::transform(const Rect& bounds) const +Rect Transform::transform(const Rect& bounds, bool roundOutwards) const { Rect r; vec2 lt( bounds.left, bounds.top ); @@ -209,10 +209,17 @@ Rect Transform::transform(const Rect& bounds) const lb = transform(lb); rb = transform(rb); - r.left = floorf(min(lt[0], rt[0], lb[0], rb[0]) + 0.5f); - r.top = floorf(min(lt[1], rt[1], lb[1], rb[1]) + 0.5f); - r.right = floorf(max(lt[0], rt[0], lb[0], rb[0]) + 0.5f); - r.bottom = floorf(max(lt[1], rt[1], lb[1], rb[1]) + 0.5f); + if (roundOutwards) { + r.left = floorf(min(lt[0], rt[0], lb[0], rb[0])); + r.top = floorf(min(lt[1], rt[1], lb[1], rb[1])); + r.right = ceilf(max(lt[0], rt[0], lb[0], rb[0])); + r.bottom = ceilf(max(lt[1], rt[1], lb[1], rb[1])); + } else { + r.left = floorf(min(lt[0], rt[0], lb[0], rb[0]) + 0.5f); + r.top = floorf(min(lt[1], rt[1], lb[1], rb[1]) + 0.5f); + r.right = floorf(max(lt[0], rt[0], lb[0], rb[0]) + 0.5f); + r.bottom = floorf(max(lt[1], rt[1], lb[1], rb[1]) + 0.5f); + } return r; } diff --git a/services/surfaceflinger/Transform.h b/services/surfaceflinger/Transform.h index 90855da04d..66463a02e3 100644 --- a/services/surfaceflinger/Transform.h +++ b/services/surfaceflinger/Transform.h @@ -78,7 +78,8 @@ public: Rect makeBounds(int w, int h) const; vec2 transform(int x, int y) const; Region transform(const Region& reg) const; - Rect transform(const Rect& bounds) const; + Rect transform(const Rect& bounds, + bool roundOutwards = false) const; Transform operator * (const Transform& rhs) const; // assumes the last row is < 0 , 0 , 1 > vec2 transform(const vec2& v) const; diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp index 97a1e8b62b..53a63bdd9b 100644 --- a/services/surfaceflinger/main_surfaceflinger.cpp +++ b/services/surfaceflinger/main_surfaceflinger.cpp @@ -16,6 +16,8 @@ #include <sys/resource.h> +#include <sched.h> + #include <cutils/sched_policy.h> #include <binder/IServiceManager.h> #include <binder/IPCThreadState.h> @@ -61,6 +63,12 @@ int main(int, char**) { sp<GpuService> gpuservice = new GpuService(); sm->addService(String16(GpuService::SERVICE_NAME), gpuservice, false); + struct sched_param param = {0}; + param.sched_priority = 2; + if (sched_setscheduler(0, SCHED_FIFO, ¶m) != 0) { + ALOGE("Couldn't set SCHED_FIFO"); + } + // run surface flinger in this thread flinger->run(); diff --git a/services/surfaceflinger/surfaceflinger.rc b/services/surfaceflinger/surfaceflinger.rc index 2b4ea2ae1c..435aa0cce7 100644 --- a/services/surfaceflinger/surfaceflinger.rc +++ b/services/surfaceflinger/surfaceflinger.rc @@ -3,4 +3,4 @@ service surfaceflinger /system/bin/surfaceflinger user system group graphics drmrpc readproc onrestart restart zygote - writepid /sys/fs/cgroup/stune/foreground/tasks + writepid /dev/stune/foreground/tasks |